From 3046b5d6dc6863e6da855d53f11637e13223b8f9 Mon Sep 17 00:00:00 2001 From: mzr Date: Mon, 28 Jul 2025 12:37:34 +0800 Subject: [PATCH] =?UTF-8?q?refactor(so):=20=E4=BC=98=E5=8C=96=E9=94=80?= =?UTF-8?q?=E5=94=AE=E8=AE=A2=E5=8D=95=E4=BF=9D=E5=AD=98=E9=80=BB=E8=BE=91?= =?UTF-8?q?=20-=20=E6=B7=BB=E5=8A=A0=E9=94=80=E5=94=AE=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E5=8F=98=E6=9B=B4=E6=A3=80=E6=9F=A5=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E5=8C=85=E6=8B=AC=EF=BC=9A=20-=20=E9=94=80=E5=94=AE=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E6=80=BB=E9=87=91=E9=A2=9D=E4=B8=8E=E5=AE=9E=E9=99=85?= =?UTF-8?q?=E6=94=B6=E6=AC=BE=E9=87=91=E9=A2=9D=E7=9A=84=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=20-=20=E8=A1=8C=E6=95=B0=E9=87=8F=E4=B8=8E=E9=87=91=E9=A2=9D?= =?UTF-8?q?=E7=9A=84=E6=A0=A1=E9=AA=8C=20=20=20-=20=E7=89=A9=E6=96=99?= =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E7=9A=84=E6=A0=A1=E9=AA=8C=20=20=20-=20?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E8=A1=8C=E7=9A=84=E6=A0=A1=E9=AA=8C=20-=20?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=85=B3=E8=81=94=E7=9A=84=E4=B8=8B=E6=B8=B8?= =?UTF-8?q?=E5=8D=95=E6=8D=AE=EF=BC=8C=E5=8C=85=E6=8B=AC=EF=BC=9A=20=20=20?= =?UTF-8?q?-=20=E6=B5=81=E7=A8=8B=E7=94=9F=E4=BA=A7=E8=AE=A2=E5=8D=95=20?= =?UTF-8?q?=20=20-=20=E6=94=B6=E6=AC=BE=E5=8D=95=20=20=20-=E9=94=80?= =?UTF-8?q?=E5=94=AE=E5=8F=91=E7=A5=A8=20-=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=BB=93=E6=9E=84=EF=BC=8C=E6=8F=90=E9=AB=98=E5=8F=AF?= =?UTF-8?q?=E8=AF=BB=E6=80=A7=E5=92=8C=E5=8F=AF=E7=BB=B4=E6=8A=A4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/so/m30/APISaleOrderMaitainImpl.java | 275 +++++++++++++++++- 1 file changed, 268 insertions(+), 7 deletions(-) diff --git a/so/src/private/nccloud/api/impl/so/m30/APISaleOrderMaitainImpl.java b/so/src/private/nccloud/api/impl/so/m30/APISaleOrderMaitainImpl.java index 290e3c1..439cd76 100644 --- a/so/src/private/nccloud/api/impl/so/m30/APISaleOrderMaitainImpl.java +++ b/so/src/private/nccloud/api/impl/so/m30/APISaleOrderMaitainImpl.java @@ -7,6 +7,7 @@ import nc.bs.dao.BaseDAO; import nc.bs.dao.DAOException; import nc.bs.framework.common.NCLocator; import nc.bs.logging.Logger; +import nc.bs.trade.business.HYPubBO; import nc.bs.trade.business.HYSuperDMO; import nc.impl.pubapp.pattern.data.bill.BillQuery; import nc.itf.scmpub.reference.uap.bd.customer.CustomerPubService; @@ -15,10 +16,12 @@ import nc.itf.so.m30.self.ISaleOrderMaintain; import nc.itf.so.m30.self.ISaleOrderScriptMaintain; import nc.itf.uap.IUAPQueryBS; import nc.itf.uap.pf.IPFBusiAction; +import nc.jdbc.framework.SQLParameter; import nc.jdbc.framework.processor.ColumnProcessor; import nc.jdbc.framework.processor.MapProcessor; import nc.pubimpl.so.m30.pub.SaleOrderSaveUtil; import nc.pubitf.so.m30.api.ISaleOrderQueryAPI; +import nc.vo.arap.gathering.GatheringBillVO; import nc.vo.bd.defdoc.DefdocVO; import nc.vo.ml.NCLangRes4VoTransl; import nc.vo.pub.BusinessException; @@ -30,6 +33,7 @@ import nc.vo.pub.lang.UFDouble; import nc.vo.pubapp.AppContext; import nc.vo.pubapp.calculator.HslParseUtil; import nc.vo.pubapp.pattern.exception.ExceptionUtils; +import nc.vo.pubapp.pattern.pub.SqlBuilder; import nc.vo.scmpub.check.billvalidate.BillVOsCheckRule; import nc.vo.scmpub.fill.pricemny.INumPriceMnyCalculator; import nc.vo.scmpub.res.billtype.SOBillType; @@ -38,6 +42,7 @@ import nc.vo.scmpub.util.StringUtil; import nc.vo.so.m30.entity.SaleOrderBVO; import nc.vo.so.m30.entity.SaleOrderHVO; import nc.vo.so.m30.entity.SaleOrderVO; +import nc.vo.so.m32.entity.SaleInvoiceHVO; import nc.vo.so.pub.SOConstant; import nc.vo.so.pub.enumeration.BillStatus; import nc.vo.so.pub.keyvalue.IKeyValue; @@ -417,7 +422,7 @@ public class APISaleOrderMaitainImpl implements IAPISaleOrderMaitain { // 判断是否存在新增的子表 boolean hasNewStatus = Arrays.stream(bvos).anyMatch(bvo -> bvo.getStatus() == VOStatus.NEW); // 新增子表或改订单类型的情况下不校验是否存在下游 - if (!hasNewStatus && !isChangedTranType) { + /*if (!hasNewStatus && !isChangedTranType) { String countSql = "SELECT count(1) FROM so_saleinvoice_b a" + " LEFT JOIN so_saleinvoice b ON a.csaleinvoiceid = b.csaleinvoiceid" + " WHERE b.fopposeflag = 0 AND nvl(b.dr, 0) = 0 and csrcid = '[csrcid]' "; @@ -428,17 +433,17 @@ public class APISaleOrderMaitainImpl implements IAPISaleOrderMaitain { ExceptionUtils.wrappBusinessException("下游存在未红冲完成的销售发票"); return null; } - } + }*/ } fillcustomervidbyoid(combinBillVOs); - + checkSaleOrderChange(combinBillVOs[0], originVos[0]); // 保存 ISaleOrderScriptMaintain maintainsrv = NCLocator.getInstance().lookup(ISaleOrderScriptMaintain.class); SaleOrderVO[] retvos = maintainsrv.saleOrderUpdate(combinBillVOs, null, originVos); if (retvos != null) { // 同步修改流程生产订单的国网行项目号、国内采购订单号 - updatePmoBill(retvos); + updateRelatedBill(retvos); } return retvos; } @@ -1594,10 +1599,184 @@ public class APISaleOrderMaitainImpl implements IAPISaleOrderMaitain { } } - private void updatePmoBill(SaleOrderVO[] vos) { - // 同步修改流程生产订单的国网行项目号、国内采购订单号 + /** + * 检查销售订单数量金额字段的变化 + * + * @param newVO + * @param oldVO + * @throws BusinessException + */ + private void checkSaleOrderChange(SaleOrderVO newVO, SaleOrderVO oldVO) throws BusinessException { + /** + * 1.实际收款金额 >0 + * 销售订单行金额减少,校验 表头的销售订单总金额不得小于销售订单实际收款金额 + * 2.累计开票主数量 & 累计确认应收金额 >0 + * 修改行金额和行数量时,不得小于行累计确认应收金额和行累计开票主数量; + * 行数量、金额减少时,销售订单行金额不得小于行累计确认应收金额,销售订单数量不得小于销售订单行累计开票主数量;不允许删除已开票的物料行(判断累计开票主数量是否大于0); + * 3.实际收款和累计开票主数量、累计确认应收金额 >0 + * 变更行金额和行数量时,销售订单行金额不得小于行累计确认应收金额,销售订单行数量不得小于销售订单行累计开票主数量; + * 4.累计出库主数量 or 累计安排生产订单主数量 or 累计发货主数量 or 累计排产主数量 >0 + * 不可替换物料,不可删除订单明细行(删除就是把子表VO的dr赋值1,0表示未删除); + * 行数量减少后的值不可小于 累计出库主数量,累计安排生产订单主数量,累计发货主数量,累计排产主数量 + * 行金额不得小于行累计确认应收金额与收款金额的最小值 + * 5.累计安排生产订单主数量 & 累计排产主数量 >0 + * 行数量减少值数量减少后的值不可小于 累计排产主数量 + */ + /** + * 字段解释: + * 行号 crowno + * 物料id cmaterialvid + * 行数量 nnum + * 行金额 norigtaxmny + * 销售订单总金额 ntotalorigmny + * 实际收款/销售订单实际收款金额 nreceivedmny + * 累计开票主数量 ntotalinvoicenum + * 累计确认应收金额 ntotalarmny + * 累计出库主数量 ntotaloutnum + * 累计安排生产订单主数量 narrangemonum + * 累计发货主数量 ntotalsendnum + * 累计排产主数量 vbdef12 + * 实体的增删改 status VOStatus.NEW VOStatus.UPDATED + */ + if (newVO == null || oldVO == null) return; + + SaleOrderHVO newHead = newVO.getParentVO(); + SaleOrderHVO oldHead = oldVO.getParentVO(); + SaleOrderBVO[] newBodies = newVO.getChildrenVO(); + SaleOrderBVO[] oldBodies = oldVO.getChildrenVO(); + + // 1. 表头金额校验 + UFDouble newNtotalorigmny = newHead.getNtotalorigmny(); + UFDouble newNreceivedmny = newHead.getNreceivedmny(); + if (newNreceivedmny != null && newNreceivedmny.doubleValue() > 0) { + if (newNtotalorigmny == null || newNtotalorigmny.doubleValue() < newNreceivedmny.doubleValue()) { + throw new BusinessException("销售订单总金额不得小于销售订单实际收款金额"); + } + } + + // 新旧表体映射 + Map oldBodyMap = new HashMap<>(); + for (SaleOrderBVO oldBody : oldBodies) { + oldBodyMap.put(oldBody.getCsaleorderbid(), oldBody); + } + + for (SaleOrderBVO newBody : newBodies) { + String bid = newBody.getCsaleorderbid(); + SaleOrderBVO oldBody = oldBodyMap.get(bid); + if (oldBody == null) continue; // 新增行不校验 + + // 校验用到的字段 + String newCmaterialvid = newBody.getCmaterialvid(); + String oldCmaterialvid = oldBody.getCmaterialvid(); + UFDouble newNnum = newBody.getNnum(); + UFDouble oldNnum = oldBody.getNnum(); + UFDouble newNorigtaxmny = newBody.getNorigtaxmny(); + UFDouble oldNorigtaxmny = oldBody.getNorigtaxmny(); + + UFDouble ntotalinvoicenum = getUFDouble_NullAsOne(newBody.getNtotalinvoicenum()); + UFDouble ntotalarmny = getUFDouble_NullAsOne(newBody.getNtotalarmny()); + UFDouble ntotaloutnum = getUFDouble_NullAsOne(newBody.getNtotaloutnum()); + UFDouble narrangemonum = getUFDouble_NullAsOne(newBody.getNarrangemonum()); + UFDouble ntotalsendnum = getUFDouble_NullAsOne(newBody.getNtotalsendnum()); + UFDouble vbdef12 = getUFDouble_NullAsOne(newBody.getVbdef12()); + String crowno = newBody.getCrowno(); + + // 1. 修改行校验 + if (VOStatus.UPDATED == newBody.getStatus()) { + // 1.1 累计开票主数量 & 累计确认应收金额 >0 + if ((ntotalinvoicenum != null && ntotalinvoicenum.doubleValue() > 0) || + (ntotalarmny != null && ntotalarmny.doubleValue() > 0)) { + if (ntotalinvoicenum != null && ntotalinvoicenum.doubleValue() > 0) { + if (newNnum == null || newNnum.doubleValue() < ntotalinvoicenum.doubleValue()) { + throw new BusinessException("行号:" + crowno + ", 销售订单行数量(" + newNnum + ")不得小于行累计开票主数量(" + ntotalinvoicenum + ")"); + } + } + if (ntotalarmny != null && ntotalarmny.doubleValue() > 0) { + if (newNorigtaxmny == null || newNorigtaxmny.doubleValue() < ntotalarmny.doubleValue()) { + throw new BusinessException("行号:" + crowno + ", 销售订单行金额(" + newNorigtaxmny + ")不得小于行累计确认应收金额(" + ntotalarmny + ")"); + } + } + } + + // 1.2 累计出库/安排生产/发货/排产 + boolean hasOutOrArrange = (ntotaloutnum != null && ntotaloutnum.doubleValue() > 0) || + (narrangemonum != null && narrangemonum.doubleValue() > 0) || + (ntotalsendnum != null && ntotalsendnum.doubleValue() > 0) || + (vbdef12 != null && vbdef12.doubleValue() > 0); + if (hasOutOrArrange) { + if (newNnum != null && oldNnum != null && newNnum.doubleValue() < oldNnum.doubleValue()) { + if (ntotaloutnum != null && ntotaloutnum.doubleValue() > 0 && newNnum.doubleValue() < ntotaloutnum.doubleValue()) { + throw new BusinessException("行号:" + crowno + ", 行数量(" + newNnum + ")不可小于累计出库主数量(" + ntotaloutnum + ")"); + } + if (narrangemonum != null && narrangemonum.doubleValue() > 0 && newNnum.doubleValue() < narrangemonum.doubleValue()) { + throw new BusinessException("行号:" + crowno + ", 行数量(" + newNnum + ")不可小于累计安排生产订单主数量(" + narrangemonum + ")"); + } + if (ntotalsendnum != null && ntotalsendnum.doubleValue() > 0 && newNnum.doubleValue() < ntotalsendnum.doubleValue()) { + throw new BusinessException("行号:" + crowno + ", 行数量(" + newNnum + ")不可小于累计发货主数量(" + ntotalsendnum + ")"); + } + if (vbdef12 != null && vbdef12.doubleValue() > 0 && newNnum.doubleValue() < vbdef12.doubleValue()) { + throw new BusinessException("行号:" + crowno + ", 行数量(" + newNnum + ")不可小于累计排产主数量(" + vbdef12 + ")"); + } + } + // 金额校验 + if (ntotalarmny != null && ntotalarmny.doubleValue() > 0) { + UFDouble minAmount = ntotalarmny; + if (newNreceivedmny != null && newNreceivedmny.doubleValue() > 0) { + minAmount = ntotalarmny.doubleValue() < newNreceivedmny.doubleValue() ? ntotalarmny : newNreceivedmny; + } + if (newNorigtaxmny == null || newNorigtaxmny.doubleValue() < minAmount.doubleValue()) { + throw new BusinessException("行号:" + crowno + ", 行金额(" + newNorigtaxmny + ")不得小于行累计确认应收金额与收款金额的最小值(" + minAmount + ")"); + } + } + // 1.4 物料替换校验 + if (StringUtils.isNotEmpty(oldCmaterialvid) && !newCmaterialvid.equals(oldCmaterialvid)) { + throw new BusinessException("行号:" + crowno + ",存在累计出库/安排生产订单/发货/排产主数量,请勿修改物料"); + } + } + // 1.3 累计安排生产订单主数量 & 累计排产主数量 >0 + if ((narrangemonum != null && narrangemonum.doubleValue() > 0) && + (vbdef12 != null && vbdef12.doubleValue() > 0)) { + if (newNnum != null && oldNnum != null && newNnum.doubleValue() < oldNnum.doubleValue()) { + if (newNnum.doubleValue() < vbdef12.doubleValue()) { + throw new BusinessException("行号:" + crowno + ", 行数量减少后的值(" + newNnum + ")不可小于累计排产主数量(" + vbdef12 + ")"); + } + } + } + + } + + // 2. 删除行校验 + if (VOStatus.DELETED == newBody.getStatus()) { + if (ntotalinvoicenum != null && ntotalinvoicenum.doubleValue() > 0) { + throw new BusinessException("行号:" + crowno + ", 不允许删除已开票的物料行"); + } + if ((ntotaloutnum != null && ntotaloutnum.doubleValue() > 0) || + (narrangemonum != null && narrangemonum.doubleValue() > 0) || + (ntotalsendnum != null && ntotalsendnum.doubleValue() > 0) || + (vbdef12 != null && vbdef12.doubleValue() > 0)) { + throw new BusinessException("行号:" + crowno + ", 不可删除已有累计出库、安排生产、发货或排产的订单明细行"); + } + } + // 3. 新增行校验 + if (VOStatus.NEW == newBody.getStatus()) { + + } + } + + } + + /** + * 更新关联的下游单据 + * + * @param vos 销售订单集合 + */ + private void updateRelatedBill(SaleOrderVO[] vos) throws BusinessException { + HYPubBO hypub = new HYPubBO(); for (SaleOrderVO vo : vos) { + SaleOrderHVO hvo = vo.getParentVO(); + String csaleorderid = hvo.getCsaleorderid();// 销售订单ID SaleOrderBVO[] bvos = vo.getChildrenVO(); + // 同步修改流程生产订单的国网行项目号、国内采购订单号 for (SaleOrderBVO bvo : bvos) { String csaleorderbid = bvo.getCsaleorderbid(); // 源头单据明细ID(vfirstbid) @@ -1620,10 +1799,92 @@ public class APISaleOrderMaitainImpl implements IAPISaleOrderMaitain { try { getDao().executeUpdate(updateSql); } catch (Exception e) { - ExceptionUtils.wrappBusinessException("so-updatePmoBill-exp: " + e.getMessage()); + ExceptionUtils.wrappBusinessException("so-updateRelatedBill-exp: " + e.getMessage()); + } + } + + /** + * 收款单未生成凭证时,同步修改下游收款单的部门、人员、客户 + * 首先根据源头单据id查询关联的收款单,如果有则循环判断是否生成凭证,未生成则更新字段值为销售订单同字段的值 + * 收款单和凭证的关系查询: + * 单据和凭证的关联 查询`fip_relation`表可知,src_relationid 存的是关联单据的主键 + */ + SqlBuilder strWhere = new SqlBuilder(); + strWhere.append("dr = 0 and "); + strWhere.append("src_billid", csaleorderid); + GatheringBillVO[] payBillVOs = (GatheringBillVO[]) hypub.queryByCondition(GatheringBillVO.class, strWhere.toString()); + String ccustomerid = hvo.getCcustomerid(); + String ccustomervid = hvo.getCcustomervid(); + String cemployeeid = hvo.getCemployeeid(); + String cdeptid = hvo.getCdeptid(); + String cdeptvid = hvo.getCdeptvid(); + if (null != payBillVOs) { + for (GatheringBillVO payBillVO : payBillVOs) { + // 查询收款单是否已生成凭证 + String countSql = "SELECT count(1) FROM fip_relation " + + " WHERE dr = 0 and src_relationid = '[billId]' "; + countSql = countSql.replace("[billId]", payBillVO.getPk_gatherbill()); + Integer num = (Integer) getDao().executeQuery(countSql, new ColumnProcessor()); + if (num > 0) { + continue; + } + // 修改收款单 + payBillVO.setPk_psndoc(cemployeeid); + payBillVO.setCustomer(ccustomerid); + payBillVO.setPk_deptid(cdeptid); + payBillVO.setPk_deptid_v(cdeptvid); + payBillVO.setStatus(VOStatus.UPDATED); + hypub.update(payBillVO); + // 修改收款单子表 + String updateSql = "update ar_gatheritem set pk_psndoc=?,customer=?,pk_deptid=?,pk_deptid_v=? where pk_gatherbill=?"; + SQLParameter parameter = new SQLParameter(); + parameter.addParam(cemployeeid); + parameter.addParam(ccustomerid); + parameter.addParam(cdeptid); + parameter.addParam(cdeptvid); + int num1 = getDao().executeUpdate(updateSql); + } + } + + /** + * 应收单未生效时,同步修改下游销售发票的部门(销售和生产)、销售业务员、客户、开票客户 + * 首先根据源头单据id查询关联的销售发票,如果有则循环判断应收单是否生效,未生效则更新字段值为销售订单同字段的值 + */ + SqlBuilder strWhereInv = new SqlBuilder(); + strWhereInv.append("dr = 0 and "); + strWhereInv.append("cfirstid", csaleorderid); + SaleInvoiceHVO[] invoiceHvos = (SaleInvoiceHVO[]) hypub.queryByCondition(SaleInvoiceHVO.class, strWhereInv.toString()); + if (null != invoiceHvos) { + for (SaleInvoiceHVO invoiceHVO : invoiceHvos) { + // 查询应收单是否生效 + String countSql = "SELECT count(1) FROM ar_recitem b " + + " left join ar_recbill a on a.pk_recbill = b.pk_recbill " + + " WHERE b.dr = 0 and a.effectstatus = 10 and b.src_billid = '[billId]' "; + countSql = countSql.replace("[billId]", csaleorderid); + Integer num = (Integer) getDao().executeQuery(countSql, new ColumnProcessor()); + if (num > 0) { + continue; + } + invoiceHVO.setCinvoicecustid(ccustomerid); + invoiceHVO.setCinvoicecustvid(ccustomervid); + invoiceHVO.setStatus(VOStatus.UPDATED); + hypub.update(invoiceHVO); + // 修改销售发票子表 + String updateSql = "update so_saleinvoice_b set " + + "cordercustid=?,cordercustvid=?,cdeptid=?,cdeptvid=?,cemployeeid=?, " + + "where csaleinvoiceid=?"; + SQLParameter parameter = new SQLParameter(); + parameter.addParam(ccustomerid); + parameter.addParam(ccustomerid); + parameter.addParam(cdeptid); + parameter.addParam(cdeptvid); + parameter.addParam(cemployeeid); + parameter.addParam(invoiceHVO.getPrimaryKey()); + int num1 = getDao().executeUpdate(updateSql); } } } } + }