|
|
|
@ -264,21 +264,21 @@ public class CoDelExcelServiceImpl implements CoDelExcelService { |
|
|
|
String cnativeKey = "cnative_" + cmcInvoice; |
|
|
|
String salesAreaKey = "salesArea_" + cmcInvoice; |
|
|
|
|
|
|
|
// 从请求参数中获取按发票号设置的客户信息 |
|
|
|
String customerName = request.getParameter(customerNameKey); |
|
|
|
String localShipAddress = request.getParameter(localShipAddressKey); |
|
|
|
String overseasShipper = request.getParameter(overseasShipperKey); |
|
|
|
String overseasAddress = request.getParameter(overseasAddressKey); |
|
|
|
String cnative = request.getParameter(cnativeKey); |
|
|
|
String salesArea = request.getParameter(salesAreaKey); |
|
|
|
// 从请求参数中获取按发票号设置的客户信息,并进行 HTML 反转义处理 |
|
|
|
String customerName = unescapeHtml(request.getParameter(customerNameKey)); |
|
|
|
String localShipAddress = unescapeHtml(request.getParameter(localShipAddressKey)); |
|
|
|
String overseasShipper = unescapeHtml(request.getParameter(overseasShipperKey)); |
|
|
|
String overseasAddress = unescapeHtml(request.getParameter(overseasAddressKey)); |
|
|
|
String cnative = unescapeHtml(request.getParameter(cnativeKey)); |
|
|
|
String salesArea = unescapeHtml(request.getParameter(salesAreaKey)); |
|
|
|
|
|
|
|
// 如果按发票号的信息不存在,则使用全局信息 |
|
|
|
headerList.setCustomerName(customerName != null ? customerName : inData.getCustomerName()); |
|
|
|
headerList.setLocalShipAddress(localShipAddress != null ? localShipAddress : inData.getLocalShipAddress()); |
|
|
|
headerList.setOverseasShipper(overseasShipper != null ? overseasShipper : inData.getOverseasShipper()); |
|
|
|
headerList.setOverseasAddress(overseasAddress != null ? overseasAddress : inData.getOverseasAddress()); |
|
|
|
headerList.setCnative(cnative != null ? cnative : inData.getCnative()); |
|
|
|
headerList.setSalesArea(salesArea != null ? salesArea : inData.getSalesArea()); |
|
|
|
headerList.setCustomerName(customerName != null ? customerName : unescapeHtml(inData.getCustomerName())); |
|
|
|
headerList.setLocalShipAddress(localShipAddress != null ? localShipAddress : unescapeHtml(inData.getLocalShipAddress())); |
|
|
|
headerList.setOverseasShipper(overseasShipper != null ? overseasShipper : unescapeHtml(inData.getOverseasShipper())); |
|
|
|
headerList.setOverseasAddress(overseasAddress != null ? overseasAddress : unescapeHtml(inData.getOverseasAddress())); |
|
|
|
headerList.setCnative(cnative != null ? cnative : unescapeHtml(inData.getCnative())); |
|
|
|
headerList.setSalesArea(salesArea != null ? salesArea : unescapeHtml(inData.getSalesArea())); |
|
|
|
headerList.setCmcInvoice(cmcInvoice); |
|
|
|
headerList.setCreateBy(inData.getUsername()); |
|
|
|
coDelMapper.saveEcssCoDelNotifyHeader(headerList); |
|
|
|
@ -3446,40 +3446,77 @@ public class CoDelExcelServiceImpl implements CoDelExcelService { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 根据装箱明细的物料计算体积 |
|
|
|
* 每种物料(每个pn是一种物料)维护的箱子的长*宽*高*箱数 |
|
|
|
* 根据装箱明细计算总体积 |
|
|
|
* |
|
|
|
* @return 总体积 |
|
|
|
* <p><b>计算逻辑:</b></p> |
|
|
|
* <ul> |
|
|
|
* <li>1. 查询所有装箱明细(不按pn分组,避免箱数统计错误)</li> |
|
|
|
* <li>2. 按箱子(seq_no)分组,每个箱子只计算一次体积</li> |
|
|
|
* <li>3. 根据箱子里的主要物料获取包装信息(长宽高)</li> |
|
|
|
* <li>4. 单箱体积 = 长 * 宽 * 高,总体积 = Σ(单箱体积)</li> |
|
|
|
* </ul> |
|
|
|
* |
|
|
|
* <p><b>修复说明:</b></p> |
|
|
|
* 原方法使用 SQL 的 GROUP BY pn + SUM(DISTINCT box_qty), |
|
|
|
* 导致10个相同pn的箱子被统计为1个箱子(DISTINCT去重)。 |
|
|
|
* 现改为按箱号遍历,确保每个箱子都被正确计算。 |
|
|
|
* |
|
|
|
* @param notifyHeader 发货通知单头信息 |
|
|
|
* @return 总体积(立方米,保留2位小数) |
|
|
|
*/ |
|
|
|
private BigDecimal calculateVolumeByMaterials(EcssCoDelNotifyHeaderData notifyHeader) { |
|
|
|
List<Map> palletDetailList = coDelMapper.getCoDelPalletDetailGroupByPn(notifyHeader); |
|
|
|
try { |
|
|
|
// 1. 查询箱子列表(每个箱子包含box_qty、长宽高等信息) |
|
|
|
List<Map> boxList = coDelMapper.selectBoxList(notifyHeader); |
|
|
|
|
|
|
|
if (palletDetailList == null || palletDetailList.isEmpty()) { |
|
|
|
return BigDecimal.ZERO; |
|
|
|
} |
|
|
|
if (boxList == null || boxList.isEmpty()) { |
|
|
|
log.debug("未找到装箱数据,发货单号: {}", notifyHeader.getDelNo()); |
|
|
|
return BigDecimal.ZERO; |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
// 获取所有物料编号 |
|
|
|
List<String> pns = palletDetailList.stream() |
|
|
|
.map(detail -> (String) detail.get("pn")) |
|
|
|
.filter(Objects::nonNull) |
|
|
|
.distinct() |
|
|
|
.collect(Collectors.toList()); |
|
|
|
// 2. 查询所有装箱明细(获取每个箱子里的物料信息) |
|
|
|
EcssCoDelPalletHeaderData queryParam = new EcssCoDelPalletHeaderData(); |
|
|
|
queryParam.setSite(notifyHeader.getSite()); |
|
|
|
queryParam.setBuNo(notifyHeader.getBuNo()); |
|
|
|
queryParam.setDelNo(notifyHeader.getDelNo()); |
|
|
|
|
|
|
|
// 获取所有明细中的物料编号 |
|
|
|
Set<String> allPns = new HashSet<>(); |
|
|
|
for (Map box : boxList) { |
|
|
|
Object seqNoObj = box.get("item_no"); |
|
|
|
if (seqNoObj == null) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (pns.isEmpty()) { |
|
|
|
queryParam.setSeqNo(Integer.valueOf(seqNoObj.toString())); |
|
|
|
List<Map> detailList = coDelMapper.selectPalletDetailList(queryParam); |
|
|
|
|
|
|
|
if (detailList != null && !detailList.isEmpty()) { |
|
|
|
for (Map detail : detailList) { |
|
|
|
String pn = (String) detail.get("pn"); |
|
|
|
if (pn != null) { |
|
|
|
allPns.add(pn); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (allPns.isEmpty()) { |
|
|
|
log.warn("装箱明细中未找到物料信息,发货单号: {}", notifyHeader.getDelNo()); |
|
|
|
return BigDecimal.ZERO; |
|
|
|
} |
|
|
|
|
|
|
|
// 根据物料编号获取packageNo |
|
|
|
List<Map> packageNoList = coDelMapper.getPackageNoByPn(notifyHeader.getSite(), pns); |
|
|
|
// 3. 获取物料对应的包装信息 |
|
|
|
List<String> pnList = new ArrayList<>(allPns); |
|
|
|
List<Map> packageNoList = coDelMapper.getPackageNoByPn(notifyHeader.getSite(), pnList); |
|
|
|
Map<String, String> partNoToPackageNoMap = packageNoList.stream() |
|
|
|
.collect(Collectors.toMap( |
|
|
|
map -> (String) map.get("pn"), |
|
|
|
map -> (String) map.get("packageNo"), |
|
|
|
(existing, replacement) -> existing // 处理重复key的情况 |
|
|
|
(existing, replacement) -> existing |
|
|
|
)); |
|
|
|
|
|
|
|
// 获取所有packageNo对应的包装信息 |
|
|
|
// 4. 获取包装的长宽高信息 |
|
|
|
List<String> packageNos = packageNoList.stream() |
|
|
|
.map(map -> (String) map.get("packageNo")) |
|
|
|
.filter(Objects::nonNull) |
|
|
|
@ -3487,11 +3524,10 @@ public class CoDelExcelServiceImpl implements CoDelExcelService { |
|
|
|
.collect(Collectors.toList()); |
|
|
|
|
|
|
|
if (packageNos.isEmpty()) { |
|
|
|
log.warn("未找到物料对应的包装信息,物料编号: {}", pns); |
|
|
|
log.warn("未找到物料对应的包装信息,发货单号: {}", notifyHeader.getDelNo()); |
|
|
|
return BigDecimal.ZERO; |
|
|
|
} |
|
|
|
|
|
|
|
// 获取包装信息(长宽高) |
|
|
|
List<EcssPackageData> packageDataList = coDelMapper.getPackageList(notifyHeader.getSite(), notifyHeader.getBuNo(), packageNos); |
|
|
|
Map<String, EcssPackageData> packageNoToDataMap = packageDataList.stream() |
|
|
|
.collect(Collectors.toMap( |
|
|
|
@ -3500,61 +3536,67 @@ public class CoDelExcelServiceImpl implements CoDelExcelService { |
|
|
|
(existing, replacement) -> existing |
|
|
|
)); |
|
|
|
|
|
|
|
// 计算总体积 |
|
|
|
// 5. 遍历每个箱子,计算体积 |
|
|
|
BigDecimal totalVolume = BigDecimal.ZERO; |
|
|
|
for (Map detail : palletDetailList) { |
|
|
|
String pn = (String) detail.get("pn"); |
|
|
|
Object boxQtyObj = detail.get("box_qty"); |
|
|
|
int processedBoxCount = 0; |
|
|
|
|
|
|
|
if (pn == null || boxQtyObj == null) { |
|
|
|
for (Map box : boxList) { |
|
|
|
Object seqNoObj = box.get("item_no"); |
|
|
|
if (seqNoObj == null) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// 获取箱数 |
|
|
|
BigDecimal boxQty; |
|
|
|
if (boxQtyObj instanceof BigDecimal) { |
|
|
|
boxQty = (BigDecimal) boxQtyObj; |
|
|
|
} else { |
|
|
|
boxQty = new BigDecimal(boxQtyObj.toString()); |
|
|
|
// 查询该箱子的明细,获取主要物料(取第一个物料) |
|
|
|
queryParam.setSeqNo(Integer.valueOf(seqNoObj.toString())); |
|
|
|
List<Map> detailList = coDelMapper.selectPalletDetailList(queryParam); |
|
|
|
|
|
|
|
if (detailList == null || detailList.isEmpty()) { |
|
|
|
log.warn("箱号 {} 没有装箱明细", seqNoObj); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// 获取包装信息 |
|
|
|
String packageNo = partNoToPackageNoMap.get(pn); |
|
|
|
if (packageNo == null) { |
|
|
|
log.warn("物料 {} 未维护packageNo", pn); |
|
|
|
// 获取该箱子的第一个物料作为代表物料 |
|
|
|
String mainPn = (String) detailList.get(0).get("pn"); |
|
|
|
if (mainPn == null) { |
|
|
|
log.warn("箱号 {} 的物料编号为空", seqNoObj); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
EcssPackageData packageData = packageNoToDataMap.get(packageNo); |
|
|
|
if (packageData == null) { |
|
|
|
log.warn("包装编号 {} 未找到对应的包装信息", packageNo); |
|
|
|
// 获取包装信息 |
|
|
|
String packageNo = partNoToPackageNoMap.get(mainPn); |
|
|
|
if (packageNo == null) { |
|
|
|
log.warn("箱号 {} 的物料 {} 未维护packageNo", seqNoObj, mainPn); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// 检查长宽高是否都有值 |
|
|
|
if (packageData.getLength() == null || packageData.getWidth() == null || packageData.getHeight() == null) { |
|
|
|
log.warn("包装编号 {} 的长宽高信息不完整", packageNo); |
|
|
|
EcssPackageData packageData = packageNoToDataMap.get(packageNo); |
|
|
|
if (packageData == null || packageData.getLength() == null |
|
|
|
|| packageData.getWidth() == null || packageData.getHeight() == null) { |
|
|
|
log.warn("箱号 {} 的包装 {} 信息不完整", seqNoObj, packageNo); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// 计算该物料的体积:长*宽*高*箱数 |
|
|
|
BigDecimal materialVolume = packageData.getLength() |
|
|
|
// 计算单箱体积:长 * 宽 * 高 |
|
|
|
BigDecimal singleBoxVolume = packageData.getLength() |
|
|
|
.multiply(packageData.getWidth()) |
|
|
|
.multiply(packageData.getHeight()) |
|
|
|
.multiply(boxQty); |
|
|
|
.multiply(packageData.getHeight()); |
|
|
|
|
|
|
|
totalVolume = totalVolume.add(materialVolume); |
|
|
|
// 累加到总体积 |
|
|
|
totalVolume = totalVolume.add(singleBoxVolume); |
|
|
|
processedBoxCount++; |
|
|
|
|
|
|
|
log.debug("物料 {} 包装 {} 箱数 {} 单箱体积 {} 总体积 {}", |
|
|
|
pn, packageNo, boxQty, |
|
|
|
packageData.getLength().multiply(packageData.getWidth()).multiply(packageData.getHeight()), |
|
|
|
materialVolume); |
|
|
|
log.debug("箱号 {} 物料 {} 包装 {} 单箱体积 {} m³", |
|
|
|
seqNoObj, mainPn, packageNo, singleBoxVolume.setScale(4, RoundingMode.HALF_UP)); |
|
|
|
} |
|
|
|
|
|
|
|
log.info("体积计算完成,发货单号: {}, 处理箱数: {}/{}, 总体积: {} m³", |
|
|
|
notifyHeader.getDelNo(), processedBoxCount, boxList.size(), |
|
|
|
totalVolume.setScale(2, RoundingMode.HALF_UP)); |
|
|
|
|
|
|
|
return totalVolume.setScale(2, RoundingMode.HALF_UP); |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
log.error("计算物料体积时发生异常", e); |
|
|
|
log.error("计算物料体积时发生异常,发货单号: {}", notifyHeader.getDelNo(), e); |
|
|
|
return BigDecimal.ZERO; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -4919,4 +4961,34 @@ public class CoDelExcelServiceImpl implements CoDelExcelService { |
|
|
|
return lineCount; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* HTML 反转义处理 |
|
|
|
* 将 HTML 实体字符转换回原始字符 |
|
|
|
* |
|
|
|
* |
|
|
|
* @param text 需要反转义的文本 |
|
|
|
* @return 反转义后的文本 |
|
|
|
*/ |
|
|
|
private String unescapeHtml(String text) { |
|
|
|
if (text == null || text.isEmpty()) { |
|
|
|
return text; |
|
|
|
} |
|
|
|
|
|
|
|
// 当前临时方案:处理常见的 HTML 实体 |
|
|
|
String result = text; |
|
|
|
result = result.replace("&", "&"); // & 符号 |
|
|
|
result = result.replace("<", "<"); // < 符号 |
|
|
|
result = result.replace(">", ">"); // > 符号 |
|
|
|
result = result.replace(""", "\""); // " 双引号 |
|
|
|
result = result.replace("'", "'"); // ' 单引号 |
|
|
|
result = result.replace("'", "'"); // ' 单引号(XML风格) |
|
|
|
result = result.replace(" ", " "); // 空格 |
|
|
|
result = result.replace("©", "©"); // 版权符号 |
|
|
|
result = result.replace("®", "®"); // 注册商标 |
|
|
|
result = result.replace("×", "×"); // 乘号 |
|
|
|
result = result.replace("÷", "÷"); // 除号 |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
} |