红冲发票生成开票申请逻辑调整

This commit is contained in:
mzr 2025-04-25 18:08:35 +08:00
parent e64926cd6e
commit 677f7f9e56
2 changed files with 245 additions and 18 deletions

View File

@ -0,0 +1,190 @@
package nccloud.api.so.saleinvoice.operator;
import nc.vo.pub.BusinessException;
import nc.vo.pub.lang.UFDouble;
import nc.vo.pubapp.pattern.log.Log;
import nc.vo.pubapp.pattern.pub.MathTool;
import nc.vo.pubapp.scale.ScaleUtils;
import nc.vo.scmpub.util.ValueCheckUtil;
import nc.vo.so.m32.entity.SaleInvoiceBVO;
import nc.vo.so.m32.entity.SaleInvoiceVO;
import nc.vo.so.m32trantype.entity.M32TranTypeVO;
import nc.vo.so.m32trantype.enumeration.Finvoicetime;
import nc.vo.so.pub.enumeration.BillStatus;
import nc.vo.so.pub.util.AggVOUtil;
import nccloud.framework.core.exception.ExceptionUtils;
import nccloud.framework.service.ServiceLocator;
import nccloud.itf.sscivm.ivsale.service.ExchangeToIvAppService;
import nccloud.pubitf.so.saleinvoice.service.IM32TransTypeService;
import nccloud.web.scmpub.pub.action.group.SysInitGroupQueryNcc;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class ApplicationRedInvoiceService {
private static final String REDINVOICEMNYDEALFORSSC = "redinvoicemnydealforssc";// 红字发票传开票申请数据处理
private static final String SL = "sl"; // 税率
private static final String SESUM = "sesum"; // 税额合计
private static final String JSHJSUM = "jshjsum"; // 价税合计
private static final String JESUM = "jesum"; // 无税金额合计
/**
* 销售发票判断是否可以开红字发票
*
* @param vos
* @return
*/
public List<Map<String, String>> getData(SaleInvoiceVO[] vos) {
Map<String, List<Map<String, String>>> dealMnyMap = new HashMap<String, List<Map<String, String>>>();
try {
if (!SysInitGroupQueryNcc.isEndable("1058")) {
ExceptionUtils.wrapBusinessException(
nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("4006013_0", "04006013-0038")/* @res "请启用发票管理模块!" */);
}
Map<String, Boolean> isCanGenerateMap = this.checkInvoiceStatus(AggVOUtil.getPrimaryKeys(vos));
Map<String, M32TranTypeVO> invoicetime = this.qryinvoicetime(vos);
for (SaleInvoiceVO vo : vos) {
Boolean isCan = isCanGenerateMap.get(vo.getParentVO().getPrimaryKey());
if (isCan == null || !isCan) {
ExceptionUtils.wrapBusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("4006013_0",
"04006013-0039", null, new String[]{vo.getParentVO().getVbillcode()})/* 发票{0}已经开票,不能再进行开票! */);
}
Integer fstatusflag = vo.getParentVO().getFstatusflag();
if (null != invoicetime && Finvoicetime.FINVOICETIEMAPPROVEAFTER
.equalsValue(invoicetime.get(vo.getParentVO().getCtrantypeid()).getFinvoicetime())) {
if (!BillStatus.AUDIT.getIntegerValue().equals(fstatusflag)) {
ExceptionUtils.wrapBusinessException(nc.vo.ml.NCLangRes4VoTransl
.getNCLangRes().getStrByID("4006013_0", "04006013-0141", null, new String[]{vo.getParentVO().getVbillcode()})/* @res "发票{0}的状态不是审批通过状态,不能进行红字发票处理!" */);
}
} else if (null != invoicetime && Finvoicetime.FINVOICETIEMAPPROVEBEFORE
.equalsValue(invoicetime.get(vo.getParentVO().getCtrantypeid()).getFinvoicetime())) {
if (BillStatus.AUDIT.getIntegerValue().equals(fstatusflag)) {
if (isCan == null || isCan) {
ExceptionUtils
.wrapBusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes()
.getStrByID("4006013_0", "04006013-0146", null, new String[]{vo.getParentVO().getVbillcode()})/* @res "发票{0}的交易类型属性税务发票开票时机为先开票后审批通过,已审批通过不允许开票!" */);
}
}
}
}
// 根据税率分组Map<税率,Map<价税合计,税额>>
dealMnyMap = this.dealMnyForSSC(vos);
} catch (Exception e) {
ExceptionUtils.wrapException(e);
}
return dealMnyMap.get(REDINVOICEMNYDEALFORSSC);
}
/**
* 检查是否存在已生成发票未作废的开票申请;能否继续操作
*
* @param billIds
* @return (true / 未不包含有效下游单据 可继续开票 或业务单据逆向操作 false / 为包含下游单据 业务领域控制不允许继续开票 或业务单据逆向操作 )
* @throws BusinessException
*/
private Map<String, Boolean> checkInvoiceStatus(String[] billIds) throws BusinessException {
if (ValueCheckUtil.isNullORZeroLength(billIds)) {
return new HashMap<String, Boolean>();
}
if (!SysInitGroupQueryNcc.isEndable("1058")) {
return new HashMap<String, Boolean>();
}
return ServiceLocator.find(ExchangeToIvAppService.class).checkApplicationStatus(billIds);
}
/**
* 查询交易类型开票时机
*
* @param vos
* @throws BusinessException
*/
private Map<String, M32TranTypeVO> qryinvoicetime(SaleInvoiceVO[] vos) throws BusinessException {
Map<String, M32TranTypeVO> tranttypeMap = new HashMap<String, M32TranTypeVO>();
List<String> trantypeids = new ArrayList<String>();
// 查询发票交易类型的税务发票开票时机
for (SaleInvoiceVO vo : vos) {
trantypeids.add(vo.getParentVO().getCtrantypeid());
}
// 默认先审批通过后开票
if (null != trantypeids && !trantypeids.isEmpty()) {
try {
IM32TransTypeService service = ServiceLocator.find(IM32TransTypeService.class);
tranttypeMap = service.queryM32TranType(trantypeids.toArray(new String[0]));
} catch (Exception e) {
Log.info(e);
ExceptionUtils.wrapException(e);
}
}
if (null != tranttypeMap && !tranttypeMap.isEmpty()) {
return tranttypeMap;
}
return null;
}
/**
* 处理表体发票数据根据税率分组
*
* @param vos
* @return Map<String, List < Map < String, UFDouble>>>:Map<REDINVOICEMNYDEALFORSSC,List<Map<税率/税额/价税合计,UFDouble>>>
* @throws BusinessException
*/
private Map<String, List<Map<String, String>>> dealMnyForSSC(SaleInvoiceVO[] vos) {
Map<String, List<Map<String, String>>> mnyDealForSSCMap = new HashMap<String, List<Map<String, String>>>();
Map<UFDouble, String> ntaxrateGroupMap = new HashMap<UFDouble, String>();
for (SaleInvoiceVO vo : vos) {
SaleInvoiceBVO[] bvos = vo.getChildrenVO();
for (SaleInvoiceBVO bvo : bvos) {
// 税率
UFDouble ntaxrate = bvo.getNtaxrate();
// 税额
UFDouble ntax = bvo.getNtax();
// 价税合计
UFDouble norigtaxmny = bvo.getNorigtaxmny();
// 无税金额
UFDouble norigmny = bvo.getNorigmny();
if (ntaxrateGroupMap.containsKey(bvo.getNtaxrate())) {
String dealmny = ntaxrateGroupMap.get(ntaxrate);
String[] splitmny = dealmny.split(",");
UFDouble oldntax = new UFDouble(Double.valueOf(splitmny[0]));
UFDouble oldnorigtaxmny = new UFDouble(Double.valueOf(splitmny[1]));
UFDouble oldnorigmny = new UFDouble(Double.valueOf(splitmny[2]));
UFDouble ntaxsum = MathTool.add(ntax, oldntax);
UFDouble norigtaxmnysum = MathTool.add(norigtaxmny, oldnorigtaxmny);
UFDouble norigmnysum = MathTool.add(norigmny, oldnorigmny);
ntaxrateGroupMap.put(ntaxrate, ntaxsum + "," + norigtaxmnysum + "," + norigmnysum);
} else {
ntaxrateGroupMap.put(ntaxrate, ntax + "," + norigtaxmny + "," + norigmny);
}
}
List<Map<String, String>> mnyDealForSSCList = new ArrayList<Map<String, String>>();
for (Entry<UFDouble, String> dealmny : ntaxrateGroupMap.entrySet()) {
Map<String, String> mnyDealforSSCMap = new HashMap<String, String>();
UFDouble dealtaxrate = dealmny.getKey();
String[] splitdealmny = dealmny.getValue().split(",");
UFDouble dealnatx = new UFDouble(Double.valueOf(splitdealmny[0]));
dealnatx = ScaleUtils.adjustScale(dealnatx, 2);
UFDouble dealnorigtaxmny = new UFDouble(Double.valueOf(splitdealmny[1]));
dealnorigtaxmny = ScaleUtils.adjustScale(dealnorigtaxmny, 2);
UFDouble dealnorigmny = new UFDouble(Double.valueOf(splitdealmny[2]));
dealnorigmny = ScaleUtils.adjustScale(dealnorigmny, 2);
// 税率
mnyDealforSSCMap.put(SL, dealtaxrate.toString());
// 税额合计
mnyDealforSSCMap.put(SESUM, dealnatx.toString());
// 价税合计
mnyDealforSSCMap.put(JSHJSUM, dealnorigtaxmny.toString());
// 无税金额合计
mnyDealforSSCMap.put(JESUM, dealnorigmny.toString());
mnyDealForSSCList.add(mnyDealforSSCMap);
}
mnyDealForSSCMap.put(REDINVOICEMNYDEALFORSSC, mnyDealForSSCList);
}
return mnyDealForSSCMap;
}
}

View File

@ -5,7 +5,6 @@ import com.alibaba.fastjson.JSONObject;
import nc.bs.framework.common.InvocationInfoProxy; import nc.bs.framework.common.InvocationInfoProxy;
import nc.bs.framework.common.NCLocator; import nc.bs.framework.common.NCLocator;
import nc.bs.logging.Logger; import nc.bs.logging.Logger;
import nc.bs.trade.business.HYSuperDMO;
import nc.itf.uap.IUAPQueryBS; import nc.itf.uap.IUAPQueryBS;
import nc.jdbc.framework.processor.ColumnProcessor; import nc.jdbc.framework.processor.ColumnProcessor;
import nc.jdbc.framework.processor.MapProcessor; import nc.jdbc.framework.processor.MapProcessor;
@ -30,7 +29,9 @@ import nccloud.api.baseapp.exchange.convert.OpenApiConvertDataResult;
import nccloud.api.rest.utils.ResultMessageUtil; import nccloud.api.rest.utils.ResultMessageUtil;
import nccloud.commons.lang.StringUtils; import nccloud.commons.lang.StringUtils;
import nccloud.dto.scmpub.script.entity.SCMScriptResultDTO; import nccloud.dto.scmpub.script.entity.SCMScriptResultDTO;
import nccloud.itf.sscivm.ivsale.impl.IVApplicationServiceImpl; import nccloud.framework.core.exception.ExceptionUtils;
import nccloud.framework.service.ServiceLocator;
import nccloud.itf.sscivm.ivsale.service.IVApplicationInvoiceService;
import nccloud.pubitf.riart.pflow.CloudPFlowContext; import nccloud.pubitf.riart.pflow.CloudPFlowContext;
import nccloud.pubitf.riart.pflow.ICloudScriptPFlowService; import nccloud.pubitf.riart.pflow.ICloudScriptPFlowService;
import nccloud.pubitf.scmpub.commit.service.IBatchRunScriptService; import nccloud.pubitf.scmpub.commit.service.IBatchRunScriptService;
@ -40,7 +41,6 @@ import nccloud.pubitf.ssctp.sscbd.lientage.ISSClientageMatchService;
import org.json.JSONString; import org.json.JSONString;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -210,22 +210,12 @@ public class IAPISaleInvMaitainImpl {
/** /**
* 开票申请红冲逻辑 * 开票申请红冲逻辑
* 1.根据销售发票号查询下游开票申请 * 生成开票申请的旧逻辑走不通已废弃 1.根据销售发票号查询下游开票申请 2.根据原开票申请生成新红冲
* 2.根据原开票申请生成新红冲 * 新逻辑根据税务云的发票号查询蓝字发票根据蓝字和已生成的红字发票共同生成红字的开票申请
*/ */
String vBillcode = saleInvoiceVOs[0].getParentVO().getVbillcode(); IVApplicationAggVO returnSaveIVApplicationAggVO = saveIVApplication(bject, redVos);
HYSuperDMO dmo = new HYSuperDMO();
// 开票申请单主表
IVApplicationHeadVO[] iVApplicationHeadVO = (IVApplicationHeadVO[]) dmo.queryByWhereClause(IVApplicationHeadVO.class, "src_billno='" + vBillcode + "' and dr=0 ");
String pk_ivapplication = iVApplicationHeadVO[0].getPk_ivapplication(); // 开票申请id
// 开票申请单子表
IVApplicationBodyVO[] iVApplicationBodyVOs = (IVApplicationBodyVO[]) dmo.queryByWhereClause(IVApplicationBodyVO.class, "pk_ivapplication='" + pk_ivapplication + "' and dr=0 ");
// 调用函数封装开票申请红冲VO
IVApplicationAggVO iVApplicationAggVO = makeNewRedRushIVApplicationAggVO(iVApplicationHeadVO[0], iVApplicationBodyVOs, bject, redVos);
// 生成红冲的开票申请
IVApplicationServiceImpl serviceImpl = new IVApplicationServiceImpl();
IVApplicationAggVO returnSaveIVApplicationAggVO = serviceImpl.save(iVApplicationAggVO);
if (returnSaveIVApplicationAggVO != null) { if (returnSaveIVApplicationAggVO != null) {
Logger.error("inv-redApply = " + JSONArray.toJSONString(returnSaveIVApplicationAggVO));
// 查询红冲发票信息返回给调用方 // 查询红冲发票信息返回给调用方
return ResultMessageUtil.toJSON(redVos, "接口调用成功"); return ResultMessageUtil.toJSON(redVos, "接口调用成功");
} else { } else {
@ -661,7 +651,8 @@ public class IAPISaleInvMaitainImpl {
newivApplicationHeadVO.setSrc_tradetype(rpSaleInvoiceHVO.getCtrantypeid()); // 来源交易类型 newivApplicationHeadVO.setSrc_tradetype(rpSaleInvoiceHVO.getCtrantypeid()); // 来源交易类型
newivApplicationHeadVO.setSrc_pkbusibill(rpSaleInvoiceHVO.getCsaleinvoiceid()); // 来源单据id newivApplicationHeadVO.setSrc_pkbusibill(rpSaleInvoiceHVO.getCsaleinvoiceid()); // 来源单据id
newivApplicationHeadVO.setSrc_billno(rpSaleInvoiceHVO.getVbillcode()); // 来源单据编号 newivApplicationHeadVO.setSrc_billno(rpSaleInvoiceHVO.getVbillcode()); // 来源单据编号
newivApplicationHeadVO.setHcyy("2"); // 红冲原因2开票有误 // 红冲原因1=销货退回; 2=开票有误; 3=服务中止; 4=销售折让;
newivApplicationHeadVO.setHcyy("1");
// 组装VO // 组装VO
applicationAggVO.setParentVO(newivApplicationHeadVO); applicationAggVO.setParentVO(newivApplicationHeadVO);
@ -672,6 +663,36 @@ public class IAPISaleInvMaitainImpl {
} }
} }
public IVApplicationAggVO saveIVApplication(JSONObject bject, SaleInvoiceVO[] redVos) throws Exception {
// 获取 红字发票传开票申请的数据
ApplicationRedInvoiceService applicationRedInvoiceService = new ApplicationRedInvoiceService();
List<Map<String, String>> jeMap = applicationRedInvoiceService.getData(redVos);
IVApplicationAggVO applyVo = null;
String invId = bject.getOrDefault("vdef40", "") + "";
// 查询蓝字发票号对应的税务云开出的发票号
String invCode = getSaleInvCode(invId);
if (StringUtils.isBlank(invCode) || "null".equals(invCode)) {
ExceptionUtils.wrapBusinessException("税务云的发票号不能为空");
}
IVApplicationInvoiceService ivService = ServiceLocator.find(IVApplicationInvoiceService.class);
JSONObject jsonObject1 = new JSONObject();
// 查询对应的蓝字发票
IVApplicationAggVO[] blueVos = ivService.getRedInvoice("", invCode, jsonObject1);
Logger.error("saveIVApplication-jsonObject1 = " + jsonObject1);
JSONObject jsonObject = new JSONObject();
// 红字发票生成开票申请动作
IVApplicationAggVO[] ivApplicationVOs = ivService.makeRedInvoice(blueVos, jsonObject, jeMap);
if (ivApplicationVOs != null && ivApplicationVOs.length > 0) {
applyVo = ivApplicationVOs[0];
}
Logger.error("saveIVApplication-msg = " + jsonObject);
return applyVo;
}
private Map<String, Object> getSaleOrderInfo(String csaleorderbid) throws BusinessException { private Map<String, Object> getSaleOrderInfo(String csaleorderbid) throws BusinessException {
IUAPQueryBS queryBS = NCLocator.getInstance().lookup(IUAPQueryBS.class); IUAPQueryBS queryBS = NCLocator.getInstance().lookup(IUAPQueryBS.class);
String sql = " select s.vbillcode,s.csaleorderid, s.corigcurrencyid," + String sql = " select s.vbillcode,s.csaleorderid, s.corigcurrencyid," +
@ -684,6 +705,22 @@ public class IAPISaleInvMaitainImpl {
return (Map<String, Object>) queryBS.executeQuery(sql, new MapProcessor()); return (Map<String, Object>) queryBS.executeQuery(sql, new MapProcessor());
} }
/**
* 根据税务云单号查询发票的单号
*
* @param invId
* @return
* @throws BusinessException
*/
private String getSaleInvCode(String invId) throws BusinessException {
IUAPQueryBS queryBS = NCLocator.getInstance().lookup(IUAPQueryBS.class);
String sql = " select nvl(vdef13, '') vdef13" +
" from so_saleinvoice" +
" where nvl(dr,0) = 0 and csaleinvoiceid = '" + invId + "' ";
Map<String, String> resMap = (Map<String, String>) queryBS.executeQuery(sql, new MapProcessor());
return resMap.getOrDefault("vbillcode", "");
}
private String getString_TrimAsNull(Object value) { private String getString_TrimAsNull(Object value) {
if ((value == null) || (value.toString().trim().isEmpty())) { if ((value == null) || (value.toString().trim().isEmpty())) {
return ""; return "";