refactor(so): 优化销售订单保存逻辑
- 添加销售订单变更检查逻辑,包括: - 销售订单总金额与实际收款金额的校验 - 行数量与金额的校验 - 物料替换的校验 - 删除行的校验 - 更新关联的下游单据,包括: - 流程生产订单 - 收款单 -销售发票 - 优化代码结构,提高可读性和可维护性
This commit is contained in:
parent
5898e36b89
commit
3046b5d6dc
|
@ -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<String, SaleOrderBVO> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue