|
|
|
@ -2920,140 +2920,67 @@ public class SupplierServiceImpl implements SupplierService { |
|
|
|
|
|
|
|
// 2025-05-27
|
|
|
|
List<SupplierState> stateList = supplierStateMapper.listByNumber(invoiceNumber); |
|
|
|
List<CorporateInvoiceItems> stateItems = new ArrayList<>();//supplierInvoiceMatchOrderVo
|
|
|
|
if (!stateList.isEmpty()) { |
|
|
|
// 对账单逻辑 supplier_state 优先匹配原则
|
|
|
|
List<CorporateInvoiceItems> itemList = corporateInvoiceItemsMapper.selectItemsByPid(invoiceId); |
|
|
|
List<SupplierOrderItemMatching> stateInsertList = new ArrayList<>(); |
|
|
|
// 需要重算差异的发票明细行(按 id 去重,避免同一行被反复计算)
|
|
|
|
Map<String, CorporateInvoiceItems> stateDirtyItems = new LinkedHashMap<>(); |
|
|
|
for (SupplierState supplierState : stateList) { |
|
|
|
String receiveNo = supplierState.getExt01(); |
|
|
|
String orderNo = supplierState.getPo(); |
|
|
|
String stateItemNo = supplierState.getItemNo(); |
|
|
|
String desc = supplierState.getDesc(); |
|
|
|
BigDecimal amount = new BigDecimal(supplierState.getAmount()); |
|
|
|
BigDecimal quantity = new BigDecimal(supplierState.getQuantity()); |
|
|
|
BigDecimal price = amount.divide(quantity).setScale(2, BigDecimal.ROUND_HALF_UP); |
|
|
|
if (org.apache.commons.lang3.StringUtils.isNotEmpty(receiveNo)) { |
|
|
|
//4. 取这个发票的state第一行,如果有receive number(放在ext01上),
|
|
|
|
// 则取这个receive number的值去查收货表,获取相关信息,只有数量从state上取,金额计算。
|
|
|
|
SupplierOrder stateOrder = supplierOrderMapper.selectByState(receiveNo, orderNo, stateItemNo); |
|
|
|
if (null != stateOrder) { |
|
|
|
// 可以匹配 查找items的name TODO 目前简单点是否包含 state的des描述
|
|
|
|
boolean matchedFlag = false; |
|
|
|
for (CorporateInvoiceItems item : itemList) { |
|
|
|
BigDecimal itemTotal = new BigDecimal(item.getTotal()); |
|
|
|
String name = item.getName(); |
|
|
|
if (name.indexOf(desc) > -1) { |
|
|
|
stateItems.add(item); |
|
|
|
matchedFlag = true; |
|
|
|
// 简单的表示匹配成功
|
|
|
|
//插入supplier_order_item_matching
|
|
|
|
SupplierOrderItemMatching matching = new SupplierOrderItemMatching(); |
|
|
|
matching.setInvoiceId(invoiceId); |
|
|
|
matching.setItemId(item.getId()); |
|
|
|
matching.setOrderId(stateOrder.getId()); |
|
|
|
matching.setName(desc); |
|
|
|
matching.setSpecification(item.getSpecification()); |
|
|
|
matching.setUnit(supplierState.getUom()); |
|
|
|
// 20220510price取值修改
|
|
|
|
matching.setPrice(price); |
|
|
|
// 20220508price取值修改
|
|
|
|
//matching.setPrice(price.setScale(6, BigDecimal.ROUND_HALF_UP));
|
|
|
|
matching.setQuantity(supplierState.getQuantity()); |
|
|
|
log.info(matching.getQuantity()); |
|
|
|
matching.setTotal(amount); |
|
|
|
matching.setDeductionAmount(BigDecimal.ZERO); |
|
|
|
matching.setDeductionTax(BigDecimal.ZERO); |
|
|
|
matching.setDeductionTaxRate(item.getDeductionTaxRate()); |
|
|
|
// tax 不是空并且 tax和deduction_tax
|
|
|
|
if (StringUtil.isNotBlank(item.getTax())) { |
|
|
|
if (itemTotal.add(new BigDecimal(item.getDeductionAmount() == null ? "0" : item.getDeductionAmount())).compareTo(BigDecimal.ZERO) == 1) { |
|
|
|
BigDecimal percent = matching.getTotal().divide(itemTotal.add(new BigDecimal(item.getDeductionAmount() == null ? "0" : item.getDeductionAmount())), 10, BigDecimal.ROUND_HALF_UP); |
|
|
|
matching.setTax(new BigDecimal(item.getTax()).add(new BigDecimal(item.getDeductionTax() == null ? "0" : item.getDeductionTax())).multiply(percent)); |
|
|
|
} else { |
|
|
|
matching.setTax(BigDecimal.ZERO); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
matching.setReceivedTotal(amount); |
|
|
|
matching.setTaxRate(item.getTaxRate()); |
|
|
|
matching.setMatchingStatus(0); |
|
|
|
matching.setReceiveNo(supplierState.getExt01()); |
|
|
|
matching.setOrderNo(supplierState.getPo()); |
|
|
|
matching.setItemNo(supplierState.getItemNo()); |
|
|
|
if (new BigDecimal(matching.getQuantity()).compareTo(BigDecimal.ZERO) == 0) { |
|
|
|
break; |
|
|
|
} |
|
|
|
// 插入匹配数据
|
|
|
|
supplierOrderItemMatchingMapper.insert(matching); |
|
|
|
if (org.apache.commons.lang3.StringUtils.isEmpty(receiveNo)) { |
|
|
|
//如果没有receive,先根据PO订单号 + 料号获取收货表的可匹配数量,先找下是否有同样的数量,
|
|
|
|
// 如果有就匹配这一行,没有就按照先进先出来匹配,插入supplier order item matching
|
|
|
|
continue; |
|
|
|
} |
|
|
|
// 数量为 0 时不能作为单价分母,直接跳过
|
|
|
|
BigDecimal quantity = dec(supplierState.getQuantity()); |
|
|
|
if (quantity.signum() == 0) { |
|
|
|
log.warn("对账单数量为0,跳过:invoiceId={}, po={}, itemNo={}", invoiceId, |
|
|
|
supplierState.getPo(), supplierState.getItemNo()); |
|
|
|
continue; |
|
|
|
} |
|
|
|
if (!matchedFlag) { |
|
|
|
CorporateInvoiceItems item = itemList.get(0); |
|
|
|
stateItems.add(item); |
|
|
|
BigDecimal itemTotal = new BigDecimal(item.getTotal()); |
|
|
|
String name = item.getName(); |
|
|
|
matchedFlag = true; |
|
|
|
// 简单的表示匹配成功
|
|
|
|
//插入supplier_order_item_matching
|
|
|
|
SupplierOrderItemMatching matching = new SupplierOrderItemMatching(); |
|
|
|
matching.setInvoiceId(invoiceId); |
|
|
|
matching.setItemId(item.getId()); |
|
|
|
matching.setOrderId(stateOrder.getId()); |
|
|
|
matching.setName(desc); |
|
|
|
matching.setSpecification(item.getSpecification()); |
|
|
|
matching.setUnit(supplierState.getUom()); |
|
|
|
// 20220510price取值修改
|
|
|
|
matching.setPrice(price); |
|
|
|
// 20220508price取值修改
|
|
|
|
//matching.setPrice(price.setScale(6, BigDecimal.ROUND_HALF_UP));
|
|
|
|
matching.setQuantity(supplierState.getQuantity()); |
|
|
|
log.info(matching.getQuantity()); |
|
|
|
matching.setTotal(amount); |
|
|
|
matching.setDeductionAmount(BigDecimal.ZERO); |
|
|
|
matching.setDeductionTax(BigDecimal.ZERO); |
|
|
|
matching.setDeductionTaxRate(item.getDeductionTaxRate()); |
|
|
|
// tax 不是空并且 tax和deduction_tax
|
|
|
|
if (StringUtil.isNotBlank(item.getTax())) { |
|
|
|
if (itemTotal.add(new BigDecimal(item.getDeductionAmount() == null ? "0" : item.getDeductionAmount())).compareTo(BigDecimal.ZERO) == 1) { |
|
|
|
BigDecimal percent = matching.getTotal().divide(itemTotal.add(new BigDecimal(item.getDeductionAmount() == null ? "0" : item.getDeductionAmount())), 10, BigDecimal.ROUND_HALF_UP); |
|
|
|
matching.setTax(new BigDecimal(item.getTax()).add(new BigDecimal(item.getDeductionTax() == null ? "0" : item.getDeductionTax())).multiply(percent)); |
|
|
|
} else { |
|
|
|
matching.setTax(BigDecimal.ZERO); |
|
|
|
// 金额为空时不写 0 值数据,直接跳过
|
|
|
|
if (StringUtil.isBlank(supplierState.getAmount())) { |
|
|
|
log.warn("对账单金额为空,跳过:invoiceId={}, po={}, itemNo={}", invoiceId, |
|
|
|
supplierState.getPo(), supplierState.getItemNo()); |
|
|
|
continue; |
|
|
|
} |
|
|
|
BigDecimal amount = dec(supplierState.getAmount()); |
|
|
|
BigDecimal price = amount.divide(quantity, 2, RoundingMode.HALF_UP); |
|
|
|
//4. 取这个发票的state第一行,如果有receive number(放在ext01上),
|
|
|
|
// 则取这个receive number的值去查收货表,获取相关信息,只有数量从state上取,金额计算。
|
|
|
|
SupplierOrder stateOrder = supplierOrderMapper.selectByState(receiveNo, supplierState.getPo(), supplierState.getItemNo()); |
|
|
|
if (null == stateOrder) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
matching.setReceivedTotal(amount); |
|
|
|
matching.setTaxRate(item.getTaxRate()); |
|
|
|
matching.setMatchingStatus(0); |
|
|
|
matching.setReceiveNo(supplierState.getExt01()); |
|
|
|
matching.setOrderNo(supplierState.getPo()); |
|
|
|
matching.setItemNo(supplierState.getItemNo()); |
|
|
|
if (new BigDecimal(matching.getQuantity()).compareTo(BigDecimal.ZERO) == 0) { |
|
|
|
break; |
|
|
|
// 可以匹配 查找items的name :优先名称包含 state 描述的行,未命中则兜底第一行;命中即停
|
|
|
|
CorporateInvoiceItems item = findStateItem(itemList, supplierState.getDesc()); |
|
|
|
if (null == item) { |
|
|
|
log.warn("对账单未匹配到发票明细行:invoiceId={}, po={}, itemNo={}", invoiceId, |
|
|
|
supplierState.getPo(), supplierState.getItemNo()); |
|
|
|
continue; |
|
|
|
} |
|
|
|
// 插入匹配数据
|
|
|
|
supplierOrderItemMatchingMapper.insert(matching); |
|
|
|
// 简单的表示匹配成功,插入 supplier_order_item_matching
|
|
|
|
stateInsertList.add(buildStateMatching(invoiceId, item, supplierState, stateOrder, price, amount)); |
|
|
|
stateDirtyItems.put(item.getId(), item); |
|
|
|
} |
|
|
|
// 批量插入匹配数据
|
|
|
|
if (!stateInsertList.isEmpty()) { |
|
|
|
supplierOrderItemMatchingMapper.insertBatch(stateInsertList); |
|
|
|
} |
|
|
|
// 20220430更新diff
|
|
|
|
for (CorporateInvoiceItems tmpItem : stateItems) { |
|
|
|
for (CorporateInvoiceItems tmpItem : stateDirtyItems.values()) { |
|
|
|
updateDifference(corporateInvoice, tmpItem); |
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
//如果没有receive,先根据PO订单号 + 料号获取收货表的可匹配数量,先找下是否有同样的数量,
|
|
|
|
// 如果有就匹配这一行,没有就按照先进先出来匹配,插入supplier order item matching
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
// 2025-05-29 如果是服务类行 按金额匹配 根据po查询永远匹配到第一个item上面。
|
|
|
|
// 根据PO 查询 supplierOrder
|
|
|
|
if (0 == matchType && corporateInvoice.getInvoiceType() == 1) { |
|
|
|
List<SupplierOrderItemMatching> insertList2 = new ArrayList<>(); |
|
|
|
// 没有PO时 split 和 IN 查询都没有意义,直接跳过
|
|
|
|
if (StringUtil.isBlank(supplierMatching.getOrderNo())) { |
|
|
|
log.warn("服务类匹配跳过:未匹配到PO,invoiceId={}", invoiceId); |
|
|
|
} else { |
|
|
|
Map<String, Object> mapParams = new HashMap<>(); |
|
|
|
if (!supplier.getSupplierSpecial()) { |
|
|
|
mapParams.put("supplierCode", supplierCode); |
|
|
|
@ -3066,9 +2993,14 @@ public class SupplierServiceImpl implements SupplierService { |
|
|
|
List<CorporateInvoiceItems> items = corporateInvoiceItemsMapper.selectItemsByPid(invoiceId); |
|
|
|
if (!items.isEmpty()) { |
|
|
|
CorporateInvoiceItems item = items.get(0); |
|
|
|
BigDecimal itemTotal = new BigDecimal(item.getTotal()); |
|
|
|
BigDecimal itemTotal = dec(item.getTotal()); |
|
|
|
// 已分摊金额,防止超出明细金额继续匹配
|
|
|
|
BigDecimal allocated = BigDecimal.ZERO; |
|
|
|
for (SupplierOrder order : orderList) { |
|
|
|
log.info("order.getUnusedDquantity:{}", order.getUnUsedQuantity()); |
|
|
|
if (allocated.compareTo(itemTotal) >= 0) { |
|
|
|
log.warn("服务类匹配已分摊满,剩余订单跳过:invoiceId={}, itemId={}", invoiceId, item.getId()); |
|
|
|
break; |
|
|
|
} |
|
|
|
order.setUsedAmount(supplierOrderItemMatchingMapper.getUsedAmountByOrderId(order.getId())); |
|
|
|
order.setUsedQuantity(supplierOrderItemMatchingMapper.getUsedQuantityByOrderId(order.getId())); |
|
|
|
//插入supplier_order_item_matching
|
|
|
|
@ -3079,215 +3011,162 @@ public class SupplierServiceImpl implements SupplierService { |
|
|
|
matching.setName(order.getGoods()); |
|
|
|
matching.setSpecification(item.getSpecification()); |
|
|
|
matching.setUnit(order.getUom()); |
|
|
|
BigDecimal price = order.getUnitPrice(); |
|
|
|
// 20220510price取值修改
|
|
|
|
matching.setPrice(order.getUnitPrice()); |
|
|
|
if (0 == corporateInvoice.getMatchType()) { |
|
|
|
BigDecimal percent = order.getUnUsedAmount().divide(itemTotal, 5, BigDecimal.ROUND_HALF_UP); |
|
|
|
matching.setTotal(order.getUnUsedAmount() == null ? BigDecimal.ZERO : order.getUnUsedAmount()); |
|
|
|
BigDecimal unUsedAmount = nvl(order.getUnUsedAmount()); |
|
|
|
BigDecimal percent = div(unUsedAmount, itemTotal, 5); |
|
|
|
matching.setTotal(unUsedAmount); |
|
|
|
if (StringUtil.isNotBlank(item.getDeductionAmount())) { |
|
|
|
matching.setDeductionAmount(new BigDecimal(item.getDeductionAmount()).multiply(percent)); |
|
|
|
matching.setDeductionAmount(dec(item.getDeductionAmount()).multiply(percent)); |
|
|
|
if (StringUtil.isNotBlank(item.getDeductionTax())) { |
|
|
|
matching.setDeductionTax(new BigDecimal(item.getDeductionTax()).multiply(percent)); |
|
|
|
matching.setDeductionTax(dec(item.getDeductionTax()).multiply(percent)); |
|
|
|
} |
|
|
|
matching.setDeductionTaxRate(item.getDeductionTaxRate()); |
|
|
|
} |
|
|
|
// 20220510单价修改
|
|
|
|
//matching.setQuantity(matching.getTotal().divide(price, 2, BigDecimal.ROUND_HALF_UP).intValue());
|
|
|
|
matching.setQuantity(matching.getTotal().divide(order.getUnitPrice(), 2, BigDecimal.ROUND_HALF_UP).toString()); |
|
|
|
matching.setQuantity(div(unUsedAmount, order.getUnitPrice(), 2).toString()); |
|
|
|
} |
|
|
|
// 数量没算出来(matchType 非 0)或数量为 0 的行不能入库
|
|
|
|
if (StringUtil.isBlank(matching.getQuantity()) || dec(matching.getQuantity()).signum() == 0) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
// tax 不是空并且 tax和deduction_tax
|
|
|
|
if (1 == priceType) { |
|
|
|
matching.setTax(BigDecimal.ZERO); |
|
|
|
} else { |
|
|
|
if (StringUtil.isNotBlank(item.getTax())) { |
|
|
|
if (itemTotal.add(new BigDecimal(item.getDeductionAmount() == null ? "0" : item.getDeductionAmount())).compareTo(BigDecimal.ZERO) == 1) { |
|
|
|
BigDecimal percent = matching.getTotal().divide(itemTotal.add(new BigDecimal(item.getDeductionAmount() == null ? "0" : item.getDeductionAmount())), 10, BigDecimal.ROUND_HALF_UP); |
|
|
|
matching.setTax(new BigDecimal(item.getTax()).add(new BigDecimal(item.getDeductionTax() == null ? "0" : item.getDeductionTax())).multiply(percent)); |
|
|
|
} else if (StringUtil.isNotBlank(item.getTax())) { |
|
|
|
BigDecimal baseAmount = itemTotal.add(dec(item.getDeductionAmount())); |
|
|
|
if (baseAmount.signum() > 0) { |
|
|
|
matching.setTax(dec(item.getTax()).add(dec(item.getDeductionTax())) |
|
|
|
.multiply(div(matching.getTotal(), baseAmount, 10))); |
|
|
|
} else { |
|
|
|
matching.setTax(BigDecimal.ZERO); |
|
|
|
} |
|
|
|
} else { |
|
|
|
matching.setTax(BigDecimal.ZERO); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
matching.setReceivedTotal(order.getUnitPrice().multiply(new BigDecimal(matching.getQuantity()))); |
|
|
|
matching.setReceivedTotal(nvl(matching.getPrice()).multiply(dec(matching.getQuantity()))); |
|
|
|
matching.setTaxRate(item.getTaxRate()); |
|
|
|
matching.setMatchingStatus(0); |
|
|
|
matching.setReceiveNo(order.getReceiveNo()); |
|
|
|
matching.setOrderNo(order.getOrderNo()); |
|
|
|
matching.setItemNo(order.getItemNo()); |
|
|
|
if (new BigDecimal(matching.getQuantity()).compareTo(BigDecimal.ZERO) == 0) { |
|
|
|
break; |
|
|
|
} |
|
|
|
insertList2.add(matching); |
|
|
|
allocated = allocated.add(nvl(matching.getTotal())); |
|
|
|
// 插入匹配数据
|
|
|
|
supplierOrderItemMatchingMapper.insert(matching); |
|
|
|
} |
|
|
|
updateDifference(corporateInvoice, item); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
// 待插入数据
|
|
|
|
List<SupplierOrderItemMatching> insertList = new ArrayList<>(); |
|
|
|
Map<String, SupplierInvoiceMatchOrderVo> matchMap = new HashMap<>(); |
|
|
|
// 已使用数据
|
|
|
|
Map<String, BigDecimal> matchMapNum = new HashMap<>(); |
|
|
|
// 数据分组
|
|
|
|
Map<String, List<SupplierInvoiceMatchOrderVo>> matchList = new HashMap<>(); |
|
|
|
StringBuffer poStr = new StringBuffer(); |
|
|
|
for (SupplierInvoiceMatchOrderVo supplierInvoiceMatchOrderVo : result) { |
|
|
|
// 判断itemNo是否已出现过
|
|
|
|
String tmpKey = supplierInvoiceMatchOrderVo.getItemNo(); |
|
|
|
int temId = supplierInvoiceMatchOrderVo.getId(); |
|
|
|
if (matchList.get(tmpKey) != null) { |
|
|
|
List<SupplierInvoiceMatchOrderVo> tmp = matchList.get(tmpKey); |
|
|
|
boolean existFlag1 = true; |
|
|
|
for (SupplierInvoiceMatchOrderVo invoiceMatchOrderVo : tmp) { |
|
|
|
if (invoiceMatchOrderVo.getId() == temId) { |
|
|
|
existFlag1 = false; |
|
|
|
} |
|
|
|
} |
|
|
|
if (existFlag1) { |
|
|
|
tmp.add(supplierInvoiceMatchOrderVo); |
|
|
|
matchList.put(tmpKey, tmp); |
|
|
|
} |
|
|
|
} else { |
|
|
|
List<SupplierInvoiceMatchOrderVo> tmp = new ArrayList<>(); |
|
|
|
tmp.add(supplierInvoiceMatchOrderVo); |
|
|
|
matchList.put(tmpKey, tmp); |
|
|
|
} |
|
|
|
// 按 itemNo 分组,同一 itemNo 下按 id 去重并保持出现顺序
|
|
|
|
Map<String, LinkedHashMap<Integer, SupplierInvoiceMatchOrderVo>> grouped = new LinkedHashMap<>(); |
|
|
|
for (SupplierInvoiceMatchOrderVo vo : result) { |
|
|
|
grouped.computeIfAbsent(vo.getItemNo(), key -> new LinkedHashMap<>()).putIfAbsent(vo.getId(), vo); |
|
|
|
} |
|
|
|
// 新增物料和收货单价是否需要匹配
|
|
|
|
SysConfig AUTO_MATCHING_NEED_PRICE_EQUAL_CONIG = sysConfigMapper.selectByCode("AUTO_MATCHING_NEED_PRICE_EQUAL"); |
|
|
|
log.info("matchList=>{}", JSONObject.toJSONString(matchList)); |
|
|
|
List<CorporateInvoiceItems> items = new ArrayList<>();//supplierInvoiceMatchOrderVo
|
|
|
|
for (String itemNo : matchList.keySet()) { |
|
|
|
List<CorporateInvoiceItems> items2 = null; |
|
|
|
String unitPrice = null; |
|
|
|
List<SupplierInvoiceMatchOrderVo> simovs = matchList.get(itemNo); |
|
|
|
if (simovs.isEmpty()) { |
|
|
|
|
|
|
|
} else { |
|
|
|
unitPrice = simovs.get(0).getPrice().toString(); |
|
|
|
} |
|
|
|
// String needPriceRight = supplierMaterialMapper.selectItemTypeFromMaterialDefine(itemNo);
|
|
|
|
SupplierMaterialDefineVo supplierMaterialDefineVo = supplierMaterialMapper.getSimilarMaterialNo(itemNo, supplierCode, unitPrice); |
|
|
|
log.info("supplierMaterialDefineVo=>{}", JSONObject.toJSONString(supplierMaterialDefineVo)); |
|
|
|
String needPriceRight = null; |
|
|
|
|
|
|
|
if (null != supplierMaterialDefineVo) { |
|
|
|
// needPriceRight = supplierMaterialDefineVo.getItemType();
|
|
|
|
items2 = corporateInvoiceItemsMapper.selectForAutoSmartMatchingMulti3(invoiceId, supplierMaterialDefineVo.getSimilarItemNo()); |
|
|
|
} else { |
|
|
|
items2 = corporateInvoiceItemsMapper.selectForAutoSmartMatchingMulti2(invoiceId, itemNo); |
|
|
|
boolean needPriceEqual = null != AUTO_MATCHING_NEED_PRICE_EQUAL_CONIG |
|
|
|
&& "1".equals(AUTO_MATCHING_NEED_PRICE_EQUAL_CONIG.getValue()); |
|
|
|
// 需要重算差异的发票明细行,按 id 去重,避免同一行反复计算
|
|
|
|
Map<String, CorporateInvoiceItems> dirtyItems = new LinkedHashMap<>(); |
|
|
|
// 已匹配订单号,去重且保持顺序
|
|
|
|
LinkedHashSet<String> matchedOrderNos = new LinkedHashSet<>(); |
|
|
|
// 收货行剩余可匹配数量:内存记账,不再依赖同一次事务里刚插入的数据
|
|
|
|
Map<Integer, BigDecimal> orderRemainQty = new HashMap<>(); |
|
|
|
// 收货行缓存,避免同一行反复查询
|
|
|
|
Map<Integer, SupplierOrder> orderCache = new HashMap<>(); |
|
|
|
// 发票明细行已匹配数量,避免每一行都回查一次
|
|
|
|
Map<String, BigDecimal> itemMatchedQty = new HashMap<>(); |
|
|
|
|
|
|
|
for (Map.Entry<String, LinkedHashMap<Integer, SupplierInvoiceMatchOrderVo>> groupEntry : grouped.entrySet()) { |
|
|
|
String itemNo = groupEntry.getKey(); |
|
|
|
// 候选收货行:在副本上消耗,不再改动共享集合;按 id 去重
|
|
|
|
LinkedHashMap<Integer, SupplierInvoiceMatchOrderVo> available = new LinkedHashMap<>(groupEntry.getValue()); |
|
|
|
SupplierInvoiceMatchOrderVo firstVo = available.isEmpty() ? null : available.values().iterator().next(); |
|
|
|
String unitPrice = null == firstVo || null == firstVo.getPrice() ? null : firstVo.getPrice().toString(); |
|
|
|
SupplierMaterialDefineVo supplierMaterialDefineVo = |
|
|
|
supplierMaterialMapper.getSimilarMaterialNo(itemNo, supplierCode, unitPrice); |
|
|
|
List<CorporateInvoiceItems> items2 = null == supplierMaterialDefineVo |
|
|
|
? corporateInvoiceItemsMapper.selectForAutoSmartMatchingMulti2(invoiceId, itemNo) |
|
|
|
: corporateInvoiceItemsMapper.selectForAutoSmartMatchingMulti3(invoiceId, supplierMaterialDefineVo.getSimilarItemNo()); |
|
|
|
// 相似料号对应的收货行一并作为候选
|
|
|
|
if (null != supplierMaterialDefineVo && StringUtil.isNotBlank(supplierMaterialDefineVo.getSimilarItemNo())) { |
|
|
|
for (String no : supplierMaterialDefineVo.getSimilarItemNo().split(",")) { |
|
|
|
if (no.equals(itemNo)) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
int startIndex = 0; |
|
|
|
BigDecimal useQty = BigDecimal.ZERO; |
|
|
|
List<SupplierInvoiceMatchOrderVo> lsMa = matchList.get(itemNo); |
|
|
|
if (null != supplierMaterialDefineVo) { |
|
|
|
if (!org.apache.commons.lang3.StringUtils.isEmpty(supplierMaterialDefineVo.getSimilarItemNo())) { |
|
|
|
String[] similarItemNos = supplierMaterialDefineVo.getSimilarItemNo().split(","); |
|
|
|
for (String no : similarItemNos) { |
|
|
|
if (no.equals(itemNo)) continue; |
|
|
|
if (null != matchList.get(no)) { |
|
|
|
lsMa.addAll(matchList.get(no)); |
|
|
|
LinkedHashMap<Integer, SupplierInvoiceMatchOrderVo> similarGroup = grouped.get(no); |
|
|
|
if (null != similarGroup) { |
|
|
|
available.putAll(similarGroup); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (null == items2 || items2.isEmpty()) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
int temp = 0; |
|
|
|
for (CorporateInvoiceItems item : items2) { |
|
|
|
if (item.getName().equals("*金属制品*直角接头")) { |
|
|
|
log.info("123"); |
|
|
|
} |
|
|
|
temp++; |
|
|
|
// 发票信息
|
|
|
|
BigDecimal itemTotal = new BigDecimal(item.getTotal()); |
|
|
|
BigDecimal itemQty = item.getUnUsedQuantity(); |
|
|
|
BigDecimal itemQty2 = item.getUnUsedQuantity(); |
|
|
|
BigDecimal lineQty = new BigDecimal("0"); |
|
|
|
boolean isEnough = false; |
|
|
|
// 2025 04 28 这里会存在多个物料匹配同一个行数据的情况。 这就使得这2个物料都匹配了同一个名称的明细行
|
|
|
|
// 判断 是否有无剩余 / 或者超配 也同样需要从 items这里确保。
|
|
|
|
String qtt = item.getQuantity(); |
|
|
|
BigDecimal qt = new BigDecimal(qtt); |
|
|
|
// 通过itemId去 supplier_order_item_matching查询匹配的数量。
|
|
|
|
String itemId = item.getId(); |
|
|
|
|
|
|
|
BigDecimal matchedQt = supplierOrderItemMatchingMapper.getUsedQuantityByItemId(itemId); |
|
|
|
if (matchedQt.compareTo(qt) >= 0) continue; |
|
|
|
// 是否有完全匹配数据
|
|
|
|
BigDecimal itemTotal = dec(item.getTotal()); |
|
|
|
BigDecimal itemQty = nvl(item.getUnUsedQuantity()); |
|
|
|
if (itemQty.signum() <= 0) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
// 该明细行已匹配的数量(含本次匹配过程中已插入的行)
|
|
|
|
BigDecimal matchedQty = itemMatchedQty.computeIfAbsent(item.getId(), |
|
|
|
id -> nvl(supplierOrderItemMatchingMapper.getUsedQuantityByItemId(id))); |
|
|
|
if (matchedQty.compareTo(dec(item.getQuantity())) >= 0) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
// 是否存在剩余数量完全相等的收货行:有则只匹配这一行,取收货时间最早的一行
|
|
|
|
boolean isOk = false; |
|
|
|
Integer isId = 0; |
|
|
|
int isId = 0; |
|
|
|
SupplierInvoiceMatchOrderVo isOkInvoice = null; |
|
|
|
startIndex = 0; |
|
|
|
for (int i = startIndex; i < lsMa.size(); i++) { |
|
|
|
SupplierInvoiceMatchOrderVo so = lsMa.get(i); |
|
|
|
if (null != AUTO_MATCHING_NEED_PRICE_EQUAL_CONIG && "1".equals(AUTO_MATCHING_NEED_PRICE_EQUAL_CONIG.getValue())) { |
|
|
|
// 判断单价是否相等
|
|
|
|
String price1 = item.getPrice(); |
|
|
|
BigDecimal priceOne = new BigDecimal(price1).setScale(2, BigDecimal.ROUND_HALF_UP); |
|
|
|
BigDecimal priceTwo = so.getPrice().setScale(2, BigDecimal.ROUND_HALF_UP); |
|
|
|
if (priceOne.compareTo(priceTwo) != 0) { |
|
|
|
for (SupplierInvoiceMatchOrderVo so : available.values()) { |
|
|
|
if (needPriceEqual && !isSamePrice(item.getPrice(), so.getPrice())) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
if (useQty.compareTo(BigDecimal.ZERO) > 0) continue; |
|
|
|
if (itemQty.compareTo(so.getUsedQuantity()) == 0) { |
|
|
|
if (itemQty.compareTo(nvl(so.getUsedQuantity())) == 0) { |
|
|
|
isOk = true; |
|
|
|
if (isOkInvoice != null && isId > 0) { |
|
|
|
if (isOkInvoice.getReceiveDate().after(so.getReceiveDate())) { |
|
|
|
isOkInvoice = so; |
|
|
|
isId = isOkInvoice.getId(); |
|
|
|
} |
|
|
|
} else { |
|
|
|
if (null == isOkInvoice || isEarlier(so.getReceiveDate(), isOkInvoice.getReceiveDate())) { |
|
|
|
isOkInvoice = so; |
|
|
|
isId = so.getId(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
int tryed = 0; |
|
|
|
int maxTry = 50; |
|
|
|
while (!lsMa.isEmpty() && tryed <= maxTry) { |
|
|
|
tryed++; |
|
|
|
int j = startIndex; |
|
|
|
if (startIndex >= lsMa.size()) { |
|
|
|
break; |
|
|
|
} |
|
|
|
// for (int j=startIndex;j<lsMa.size(); j++) {
|
|
|
|
SupplierInvoiceMatchOrderVo supplierInvoiceMatchOrderVo = lsMa.get(j); |
|
|
|
if (null != AUTO_MATCHING_NEED_PRICE_EQUAL_CONIG && "1".equals(AUTO_MATCHING_NEED_PRICE_EQUAL_CONIG.getValue())) { |
|
|
|
// 判断单价是否相等
|
|
|
|
String price1 = item.getPrice(); |
|
|
|
BigDecimal priceOne = new BigDecimal(price1).setScale(2, BigDecimal.ROUND_HALF_UP); |
|
|
|
BigDecimal priceTwo = supplierInvoiceMatchOrderVo.getPrice().setScale(2, BigDecimal.ROUND_HALF_UP); |
|
|
|
if (priceOne.compareTo(priceTwo) != 0) { |
|
|
|
startIndex++; |
|
|
|
// 按顺序消耗收货行的剩余数量
|
|
|
|
BigDecimal remaining = itemQty; |
|
|
|
Iterator<SupplierInvoiceMatchOrderVo> iterator = available.values().iterator(); |
|
|
|
while (iterator.hasNext() && remaining.signum() > 0) { |
|
|
|
SupplierInvoiceMatchOrderVo so = iterator.next(); |
|
|
|
if (needPriceEqual && !isSamePrice(item.getPrice(), so.getPrice())) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
if (isOk && isId != supplierInvoiceMatchOrderVo.getId()) { |
|
|
|
// lsMa.remove(supplierInvoiceMatchOrderVo);
|
|
|
|
startIndex++; |
|
|
|
if (isOk && isId != so.getId()) { |
|
|
|
// 已定位到完全匹配的收货行,其它行不再参与匹配
|
|
|
|
continue; |
|
|
|
} |
|
|
|
SupplierOrder order = supplierOrderMapper.selectForAutoSmartMatching(supplierInvoiceMatchOrderVo.getId()); |
|
|
|
log.info("order.getUnusedDquantity:{}", order.getUnUsedQuantity()); |
|
|
|
if (order.getId() == 910187) { |
|
|
|
log.info("====="); |
|
|
|
} |
|
|
|
if (null != item && null != order) { |
|
|
|
items.add(item); |
|
|
|
// 大于可用数量
|
|
|
|
boolean qtyFlag = false; |
|
|
|
if (supplierInvoiceMatchOrderVo.getUsedQuantity().subtract(useQty).compareTo(itemQty2) > 0) { |
|
|
|
qtyFlag = true; |
|
|
|
SupplierOrder order = orderCache.computeIfAbsent(so.getId(), supplierOrderMapper::selectForAutoSmartMatching); |
|
|
|
if (null == order) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
BigDecimal orderAvail = orderRemainQty.get(order.getId()); |
|
|
|
if (null == orderAvail) { |
|
|
|
order.setUsedAmount(supplierOrderItemMatchingMapper.getUsedAmountByOrderId(order.getId())); |
|
|
|
order.setUsedQuantity(supplierOrderItemMatchingMapper.getUsedQuantityByOrderId(order.getId())); |
|
|
|
orderAvail = dec(order.getUnUsedQuantity()); |
|
|
|
orderRemainQty.put(order.getId(), orderAvail); |
|
|
|
} |
|
|
|
if (orderAvail.signum() <= 0) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
// 收货行剩余 大于 发票行剩余:本次只匹配发票需要的部分,剩余留给后面的明细行
|
|
|
|
boolean qtyFlag = orderAvail.compareTo(remaining) > 0; |
|
|
|
BigDecimal matchQty = qtyFlag ? remaining : orderAvail; |
|
|
|
|
|
|
|
//插入supplier_order_item_matching
|
|
|
|
SupplierOrderItemMatching matching = new SupplierOrderItemMatching(); |
|
|
|
matching.setInvoiceId(invoiceId); |
|
|
|
matching.setItemId(item.getId()); |
|
|
|
@ -3295,122 +3174,81 @@ public class SupplierServiceImpl implements SupplierService { |
|
|
|
matching.setName(order.getGoods()); |
|
|
|
matching.setSpecification(item.getSpecification()); |
|
|
|
matching.setUnit(order.getUom()); |
|
|
|
BigDecimal price = order.getUnitPrice(); |
|
|
|
// 20220510price取值修改
|
|
|
|
matching.setPrice(order.getUnitPrice()); |
|
|
|
if (0 == corporateInvoice.getMatchType()) { |
|
|
|
BigDecimal percent = order.getUnUsedAmount().divide(itemTotal, 5, BigDecimal.ROUND_HALF_UP); |
|
|
|
matching.setTotal(order.getUnUsedAmount() == null ? BigDecimal.ZERO : order.getUnUsedAmount()); |
|
|
|
BigDecimal unUsedAmount = nvl(order.getUnUsedAmount()); |
|
|
|
BigDecimal percent = div(unUsedAmount, itemTotal, 5); |
|
|
|
BigDecimal matchAmount = unUsedAmount; |
|
|
|
if (qtyFlag) { |
|
|
|
percent = itemQty2.divide(itemQty, 5, BigDecimal.ROUND_HALF_UP); |
|
|
|
matching.setTotal(order.getUnUsedAmount() == null ? BigDecimal.ZERO : order.getUnUsedAmount().multiply(percent)); |
|
|
|
percent = div(remaining, itemQty, 5); |
|
|
|
matchAmount = unUsedAmount.multiply(percent); |
|
|
|
} |
|
|
|
matching.setTotal(matchAmount); |
|
|
|
if (StringUtil.isNotBlank(item.getDeductionAmount())) { |
|
|
|
matching.setDeductionAmount(new BigDecimal(item.getDeductionAmount()).multiply(percent)); |
|
|
|
matching.setDeductionAmount(dec(item.getDeductionAmount()).multiply(percent)); |
|
|
|
if (StringUtil.isNotBlank(item.getDeductionTax())) { |
|
|
|
matching.setDeductionTax(new BigDecimal(item.getDeductionTax()).multiply(percent)); |
|
|
|
matching.setDeductionTax(dec(item.getDeductionTax()).multiply(percent)); |
|
|
|
} |
|
|
|
matching.setDeductionTaxRate(item.getDeductionTaxRate()); |
|
|
|
} |
|
|
|
// 20220510单价修改
|
|
|
|
//matching.setQuantity(matching.getTotal().divide(price, 2, BigDecimal.ROUND_HALF_UP).intValue());
|
|
|
|
matching.setQuantity(matching.getTotal().divide(order.getUnitPrice(), 2, BigDecimal.ROUND_HALF_UP).toString()); |
|
|
|
matching.setQuantity(div(matchAmount, order.getUnitPrice(), 2).toString()); |
|
|
|
} else { |
|
|
|
// 20220508pricsee取值修改
|
|
|
|
//matching.setPrice(price.setScale(6, BigDecimal.ROUND_HALF_UP));
|
|
|
|
if (new BigDecimal(order.getUnUsedQuantity()).compareTo(itemQty2) > 0) { |
|
|
|
matching.setQuantity(itemQty2.toString()); |
|
|
|
} else { |
|
|
|
matching.setQuantity(order.getUnUsedQuantity()); |
|
|
|
} |
|
|
|
if (matching.getQuantity().indexOf("-") > -1) { |
|
|
|
log.info(matching.getQuantity()); |
|
|
|
} |
|
|
|
|
|
|
|
if (qtyFlag) { |
|
|
|
matching.setQuantity(itemQty2.setScale(2, BigDecimal.ROUND_HALF_UP).toString()); |
|
|
|
} |
|
|
|
matching.setTotal(price.multiply(new BigDecimal(matching.getQuantity()))); |
|
|
|
matching.setQuantity(matchQty.setScale(2, RoundingMode.HALF_UP).toString()); |
|
|
|
matching.setTotal(nvl(order.getUnitPrice()).multiply(matchQty)); |
|
|
|
matching.setDeductionAmount(BigDecimal.ZERO); |
|
|
|
matching.setDeductionTax(BigDecimal.ZERO); |
|
|
|
matching.setDeductionTaxRate(item.getDeductionTaxRate()); |
|
|
|
} |
|
|
|
if (dec(matching.getQuantity()).signum() == 0) { |
|
|
|
// 数量为 0 的行不能入库,继续尝试下一行
|
|
|
|
continue; |
|
|
|
} |
|
|
|
// tax 不是空并且 tax和deduction_tax
|
|
|
|
if (StringUtil.isNotBlank(item.getTax())) { |
|
|
|
if (itemTotal.add(new BigDecimal(item.getDeductionAmount() == null ? "0" : item.getDeductionAmount())).compareTo(BigDecimal.ZERO) == 1) { |
|
|
|
BigDecimal percent = matching.getTotal().divide(itemTotal.add(new BigDecimal(item.getDeductionAmount() == null ? "0" : item.getDeductionAmount())), 10, BigDecimal.ROUND_HALF_UP); |
|
|
|
matching.setTax(new BigDecimal(item.getTax()).add(new BigDecimal(item.getDeductionTax() == null ? "0" : item.getDeductionTax())).multiply(percent)); |
|
|
|
BigDecimal baseAmount = itemTotal.add(dec(item.getDeductionAmount())); |
|
|
|
if (baseAmount.signum() > 0) { |
|
|
|
matching.setTax(dec(item.getTax()).add(dec(item.getDeductionTax())) |
|
|
|
.multiply(div(matching.getTotal(), baseAmount, 10))); |
|
|
|
} else { |
|
|
|
matching.setTax(BigDecimal.ZERO); |
|
|
|
} |
|
|
|
} else { |
|
|
|
matching.setTax(BigDecimal.ZERO); |
|
|
|
} |
|
|
|
|
|
|
|
matching.setReceivedTotal(order.getUnitPrice().multiply(new BigDecimal(matching.getQuantity()))); |
|
|
|
|
|
|
|
matching.setReceivedTotal(nvl(order.getUnitPrice()).multiply(dec(matching.getQuantity()))); |
|
|
|
matching.setTaxRate(item.getTaxRate()); |
|
|
|
matching.setMatchingStatus(0); |
|
|
|
matching.setReceiveNo(order.getReceiveNo()); |
|
|
|
matching.setOrderNo(order.getOrderNo()); |
|
|
|
matching.setItemNo(order.getItemNo()); |
|
|
|
if (new BigDecimal(matching.getQuantity()).compareTo(BigDecimal.ZERO) == 0) { |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
insertList.add(matching); |
|
|
|
// 插入匹配数据
|
|
|
|
if (matching.getQuantity().indexOf("-") > -1) { |
|
|
|
log.error("================="); |
|
|
|
} |
|
|
|
supplierOrderItemMatchingMapper.insert(matching); |
|
|
|
// 拼接orderNo
|
|
|
|
if (poStr.toString().indexOf(matching.getOrderNo()) == -1) { |
|
|
|
if (poStr.length() > 0) { |
|
|
|
poStr.append(","); |
|
|
|
if (StringUtil.isNotBlank(matching.getOrderNo())) { |
|
|
|
matchedOrderNos.add(matching.getOrderNo()); |
|
|
|
} |
|
|
|
poStr.append(matching.getOrderNo()); |
|
|
|
// 内存记账:扣减收货行和发票行的剩余数量
|
|
|
|
orderRemainQty.put(order.getId(), orderAvail.subtract(matchQty)); |
|
|
|
itemMatchedQty.put(item.getId(), matchedQty.add(matchQty)); |
|
|
|
dirtyItems.putIfAbsent(item.getId(), item); |
|
|
|
remaining = remaining.subtract(matchQty); |
|
|
|
if (!qtyFlag) { |
|
|
|
// 该收货行已耗尽,不再参与后续明细行的匹配
|
|
|
|
iterator.remove(); |
|
|
|
} |
|
|
|
// 判断数据是否足够
|
|
|
|
lineQty = lineQty.add(supplierInvoiceMatchOrderVo.getUsedQuantity().subtract(useQty)); |
|
|
|
itemQty2 = itemQty2.subtract(supplierInvoiceMatchOrderVo.getUsedQuantity().subtract(useQty)); |
|
|
|
// 足够则跳出
|
|
|
|
startIndex = 0; |
|
|
|
|
|
|
|
if (lineQty.compareTo(itemQty) > 0) { |
|
|
|
useQty = useQty.add(new BigDecimal(matching.getQuantity())); |
|
|
|
// isEnough = true;
|
|
|
|
break; |
|
|
|
} else if (lineQty.compareTo(itemQty) == 0) { |
|
|
|
// startIndex++;
|
|
|
|
useQty = BigDecimal.ZERO; |
|
|
|
lsMa.remove(supplierInvoiceMatchOrderVo); |
|
|
|
// isEnough = true;
|
|
|
|
break; |
|
|
|
} |
|
|
|
// startIndex--;
|
|
|
|
useQty = BigDecimal.ZERO; |
|
|
|
lsMa.remove(supplierInvoiceMatchOrderVo); |
|
|
|
continue; |
|
|
|
// log.info("==:"+j);
|
|
|
|
// log.info("==:"+startIndex);
|
|
|
|
// log.info("==:{}",j<lsMa.size());
|
|
|
|
// log.info("==:"+j<lsMa.size());
|
|
|
|
} |
|
|
|
// startIndex++;
|
|
|
|
} |
|
|
|
// if(isEnough){
|
|
|
|
// break;
|
|
|
|
// }
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (!insertList.isEmpty()) { |
|
|
|
if (supplierMatching.getOrderNo() == null || supplierMatching.getOrderNo().length() == 0) { |
|
|
|
supplierMatching.setOrderNo(poStr.toString()); |
|
|
|
if (!insertList.isEmpty() && StringUtil.isBlank(supplierMatching.getOrderNo())) { |
|
|
|
supplierMatching.setOrderNo(String.join(",", matchedOrderNos)); |
|
|
|
supplierMatchingMapper.updateByPrimaryKey(supplierMatching); |
|
|
|
} |
|
|
|
} |
|
|
|
for (CorporateInvoiceItems tmpItem : items) { |
|
|
|
for (CorporateInvoiceItems tmpItem : dirtyItems.values()) { |
|
|
|
updateDifference(corporateInvoice, tmpItem); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -4091,6 +3929,121 @@ public class SupplierServiceImpl implements SupplierService { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 数字/金额字符串安全解析:null、空串、千分位、非数字 一律按 0 处理,避免 new BigDecimal 抛异常 |
|
|
|
*/ |
|
|
|
private static BigDecimal dec(String value) { |
|
|
|
if (value == null) { |
|
|
|
return BigDecimal.ZERO; |
|
|
|
} |
|
|
|
String text = value.trim().replace(",", ""); |
|
|
|
if (text.isEmpty()) { |
|
|
|
return BigDecimal.ZERO; |
|
|
|
} |
|
|
|
try { |
|
|
|
return new BigDecimal(text); |
|
|
|
} catch (NumberFormatException e) { |
|
|
|
log.warn("无法解析为数字,按 0 处理:{}", value); |
|
|
|
return BigDecimal.ZERO; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 数字/金额字符串安全解析并按指定精度取值 |
|
|
|
*/ |
|
|
|
private static BigDecimal dec(String value, int scale) { |
|
|
|
return dec(value).setScale(scale, RoundingMode.HALF_UP); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 安全除法:被除数或除数为 null、除数为 0 时返回 0,避免 ArithmeticException |
|
|
|
*/ |
|
|
|
private static BigDecimal div(BigDecimal dividend, BigDecimal divisor, int scale) { |
|
|
|
if (dividend == null || divisor == null || divisor.signum() == 0) { |
|
|
|
return BigDecimal.ZERO; |
|
|
|
} |
|
|
|
return dividend.divide(divisor, scale, RoundingMode.HALF_UP); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 金额取 0 值兜底 |
|
|
|
*/ |
|
|
|
private static BigDecimal nvl(BigDecimal value) { |
|
|
|
return value == null ? BigDecimal.ZERO : value; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 收货单价与发票单价是否相等(都按 2 位小数比较) |
|
|
|
*/ |
|
|
|
private static boolean isSamePrice(String invoicePrice, BigDecimal orderPrice) { |
|
|
|
return null != orderPrice && dec(invoicePrice, 2).compareTo(orderPrice.setScale(2, RoundingMode.HALF_UP)) == 0; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* candidate 是否比 current 更早,null 视为最晚 |
|
|
|
*/ |
|
|
|
private static boolean isEarlier(Date candidate, Date current) { |
|
|
|
if (null == candidate) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
return null == current || candidate.before(current); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 对账单明细匹配发票明细行:优先取名称包含对账单描述的行,未命中则兜底第一行 |
|
|
|
*/ |
|
|
|
private CorporateInvoiceItems findStateItem(List<CorporateInvoiceItems> itemList, String desc) { |
|
|
|
if (itemList == null || itemList.isEmpty()) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
if (StringUtil.isNotBlank(desc)) { |
|
|
|
for (CorporateInvoiceItems item : itemList) { |
|
|
|
if (null != item.getName() && item.getName().contains(desc)) { |
|
|
|
return item; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return itemList.get(0); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 构建对账单(supplier_state)与发票明细行的匹配数据 |
|
|
|
*/ |
|
|
|
private SupplierOrderItemMatching buildStateMatching(String invoiceId, CorporateInvoiceItems item, SupplierState supplierState, |
|
|
|
SupplierOrder stateOrder, BigDecimal price, BigDecimal amount) { |
|
|
|
// 折前金额,作为税额分摊的分母
|
|
|
|
BigDecimal baseAmount = dec(item.getTotal()).add(dec(item.getDeductionAmount())); |
|
|
|
SupplierOrderItemMatching matching = new SupplierOrderItemMatching(); |
|
|
|
matching.setInvoiceId(invoiceId); |
|
|
|
matching.setItemId(item.getId()); |
|
|
|
matching.setOrderId(stateOrder.getId()); |
|
|
|
matching.setName(supplierState.getDesc()); |
|
|
|
matching.setSpecification(item.getSpecification()); |
|
|
|
matching.setUnit(supplierState.getUom()); |
|
|
|
// 20220510price取值修改
|
|
|
|
matching.setPrice(price); |
|
|
|
matching.setQuantity(supplierState.getQuantity()); |
|
|
|
matching.setTotal(amount); |
|
|
|
matching.setDeductionAmount(BigDecimal.ZERO); |
|
|
|
matching.setDeductionTax(BigDecimal.ZERO); |
|
|
|
matching.setDeductionTaxRate(item.getDeductionTaxRate()); |
|
|
|
// tax 不是空并且 tax和deduction_tax
|
|
|
|
if (StringUtil.isNotBlank(item.getTax()) && baseAmount.signum() > 0) { |
|
|
|
BigDecimal percent = div(matching.getTotal(), baseAmount, 10); |
|
|
|
matching.setTax(dec(item.getTax()).add(dec(item.getDeductionTax())).multiply(percent)); |
|
|
|
} else { |
|
|
|
// 置 0 而非 null,避免后续 updateDifference 累加税额时空指针
|
|
|
|
matching.setTax(BigDecimal.ZERO); |
|
|
|
} |
|
|
|
matching.setReceivedTotal(amount); |
|
|
|
matching.setTaxRate(item.getTaxRate()); |
|
|
|
matching.setMatchingStatus(0); |
|
|
|
matching.setReceiveNo(supplierState.getExt01()); |
|
|
|
matching.setOrderNo(supplierState.getPo()); |
|
|
|
matching.setItemNo(supplierState.getItemNo()); |
|
|
|
return matching; |
|
|
|
} |
|
|
|
|
|
|
|
public void updateDifference(CorporateInvoice corporateInvoice, CorporateInvoiceItems item) { |
|
|
|
log.info("supplierOrderItemMatching item.getId()=>{}", item.getId()); |
|
|
|
List<SupplierOrderItemMatching> supplierOrderItemMatchings = supplierOrderItemMatchingMapper.selectListByItemId(item.getId()); |
|
|
|
@ -4098,7 +4051,7 @@ public class SupplierServiceImpl implements SupplierService { |
|
|
|
BigDecimal orderTotal = BigDecimal.ZERO; |
|
|
|
// ReceivedTotal合计
|
|
|
|
for (SupplierOrderItemMatching supplierOrderItemMatching : supplierOrderItemMatchings) { |
|
|
|
orderTotal = orderTotal.add(supplierOrderItemMatching.getTotal()).add(supplierOrderItemMatching.getTax()); |
|
|
|
orderTotal = orderTotal.add(nvl(supplierOrderItemMatching.getTotal())).add(nvl(supplierOrderItemMatching.getTax())); |
|
|
|
/* // 折前+数量
|
|
|
|
if (corporateInvoice.getPriceType().equals(1) && corporateInvoice.getMatchType().equals(1)&& null != supplierOrderItemMatching.getDeductionAmount()) { |
|
|
|
orderTotal = orderTotal.add(supplierOrderItemMatching.getDeductionAmount()); |
|
|
|
@ -4115,16 +4068,10 @@ public class SupplierServiceImpl implements SupplierService { |
|
|
|
}*/ |
|
|
|
} |
|
|
|
// 发票金额
|
|
|
|
BigDecimal invoiceMoney = new BigDecimal(item.getTotal()); |
|
|
|
if (StringUtil.isNotEmpty(item.getDeductionAmount())) { |
|
|
|
invoiceMoney = invoiceMoney.add(new BigDecimal(item.getDeductionAmount())); |
|
|
|
} |
|
|
|
if (StringUtil.isNotEmpty(item.getTax())) { |
|
|
|
invoiceMoney = invoiceMoney.add(new BigDecimal(item.getTax())); |
|
|
|
} |
|
|
|
if (StringUtil.isNotEmpty(item.getDeductionTax())) { |
|
|
|
invoiceMoney = invoiceMoney.add(new BigDecimal(item.getDeductionTax())); |
|
|
|
} |
|
|
|
BigDecimal invoiceMoney = dec(item.getTotal()) |
|
|
|
.add(dec(item.getDeductionAmount())) |
|
|
|
.add(dec(item.getTax())) |
|
|
|
.add(dec(item.getDeductionTax())); |
|
|
|
|
|
|
|
BigDecimal difference = invoiceMoney.subtract(orderTotal).setScale(2, BigDecimal.ROUND_HALF_UP); |
|
|
|
log.info("invoiceMoney=>{} ,difference=>{}", invoiceMoney.toString(), difference.toString()); |
|
|
|
|