|
|
@@ -93,6 +93,25 @@ public class ShipmentServiceImpl implements ShipmentService {
|
|
|
master.setCreateTime(LocalDateTime.now());
|
|
|
master.setStatus("O"); // 默认状态为Open
|
|
|
|
|
|
+ // 设置ShipTime默认值(数据库int类型,不能为空)
|
|
|
+ if (master.getShipTime() == null) {
|
|
|
+ master.setShipTime(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置数值字段默认值(数据库NOT NULL)
|
|
|
+ if (master.getGrossWeight() == null) {
|
|
|
+ master.setGrossWeight(java.math.BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+ if (master.getNetWeight() == null) {
|
|
|
+ master.setNetWeight(java.math.BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+ if (master.getVolume() == null) {
|
|
|
+ master.setVolume(java.math.BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+ if (master.getQtyToShip() == null) {
|
|
|
+ master.setQtyToShip(java.math.BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
// 设置必填字段默认值
|
|
|
if (StrUtil.isBlank(master.getShipFromId())) {
|
|
|
master.setShipFromId(domain); // 默认使用domain作为发货地
|
|
|
@@ -135,6 +154,36 @@ public class ShipmentServiceImpl implements ShipmentService {
|
|
|
throw exception(SHIPMENT_STATUS_CLOSED);
|
|
|
}
|
|
|
|
|
|
+ // 查询旧的明细数据,用于日志对比
|
|
|
+ List<ShipmentDetailDO> oldDetails = shipmentDetailMapper.selectDetailsByMasterId(exists.getId(), exists.getDomain());
|
|
|
+ // 旧数据(不含items,避免框架自动对比列表)
|
|
|
+ ShipmentSaveReqVO oldData = BeanUtils.toBean(exists, ShipmentSaveReqVO.class);
|
|
|
+ // 统一日期格式:将LocalDateTime转为yyyy-MM-dd字符串
|
|
|
+ if (exists.getShipDate() != null) {
|
|
|
+ oldData.setShipDate(exists.getShipDate().toLocalDate().toString());
|
|
|
+ }
|
|
|
+ // 清除不需要比较的字段(避免格式差异导致误判)
|
|
|
+ oldData.setShipTime(null);
|
|
|
+ oldData.setGrossWeight(null);
|
|
|
+ oldData.setNetWeight(null);
|
|
|
+ oldData.setVolume(null);
|
|
|
+ oldData.setQtyToShip(null);
|
|
|
+ oldData.setItems(null);
|
|
|
+
|
|
|
+ // 新数据副本(不含items,用于主表DIFF对比)
|
|
|
+ ShipmentSaveReqVO newDataForDiff = BeanUtils.toBean(updateReqVO, ShipmentSaveReqVO.class);
|
|
|
+ // 统一日期格式:只保留yyyy-MM-dd部分
|
|
|
+ if (newDataForDiff.getShipDate() != null && newDataForDiff.getShipDate().length() > 10) {
|
|
|
+ newDataForDiff.setShipDate(newDataForDiff.getShipDate().substring(0, 10));
|
|
|
+ }
|
|
|
+ // 清除不需要比较的字段
|
|
|
+ newDataForDiff.setShipTime(null);
|
|
|
+ newDataForDiff.setGrossWeight(null);
|
|
|
+ newDataForDiff.setNetWeight(null);
|
|
|
+ newDataForDiff.setVolume(null);
|
|
|
+ newDataForDiff.setQtyToShip(null);
|
|
|
+ newDataForDiff.setItems(null);
|
|
|
+
|
|
|
// 更新主表
|
|
|
ShipmentMasterDO update = convertToMasterDO(updateReqVO);
|
|
|
update.setRecId(exists.getRecId());
|
|
|
@@ -147,9 +196,125 @@ public class ShipmentServiceImpl implements ShipmentService {
|
|
|
saveDetails(exists, updateReqVO.getItems());
|
|
|
}
|
|
|
|
|
|
- // 记录操作日志上下文
|
|
|
- LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(exists, ShipmentSaveReqVO.class));
|
|
|
+ // 手动生成明细变更日志(按recId匹配)
|
|
|
+ String itemsChangeLog = buildShipmentItemsChangeLog(oldDetails, updateReqVO.getItems());
|
|
|
+
|
|
|
+ // 记录操作日志上下文(使用不含items的副本进行DIFF)
|
|
|
+ LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, oldData);
|
|
|
+ LogRecordContext.putVariable("updateReqVO", newDataForDiff);
|
|
|
LogRecordContext.putVariable("shipment", exists);
|
|
|
+ LogRecordContext.putVariable("itemsChangeLog", itemsChangeLog);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建发货单明细变更日志(按recId匹配新旧数据)
|
|
|
+ */
|
|
|
+ private String buildShipmentItemsChangeLog(List<ShipmentDetailDO> oldDetails,
|
|
|
+ List<ShipmentSaveReqVO.ShipmentDetailSaveReqVO> newItems) {
|
|
|
+ StringBuilder log = new StringBuilder();
|
|
|
+ if (CollUtil.isEmpty(newItems)) {
|
|
|
+ newItems = List.of();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建旧数据Map(按recId)
|
|
|
+ Map<Long, ShipmentDetailDO> oldMap = oldDetails.stream()
|
|
|
+ .filter(e -> e.getRecId() != null)
|
|
|
+ .collect(Collectors.toMap(ShipmentDetailDO::getRecId, e -> e));
|
|
|
+
|
|
|
+ // 构建新数据Map(按recId)
|
|
|
+ Map<Long, ShipmentSaveReqVO.ShipmentDetailSaveReqVO> newMap = newItems.stream()
|
|
|
+ .filter(e -> e.getRecId() != null)
|
|
|
+ .collect(Collectors.toMap(ShipmentSaveReqVO.ShipmentDetailSaveReqVO::getRecId, e -> e));
|
|
|
+
|
|
|
+ // 1. 检查更新的明细(recId相同)
|
|
|
+ for (ShipmentSaveReqVO.ShipmentDetailSaveReqVO newItem : newItems) {
|
|
|
+ if (newItem.getRecId() != null && oldMap.containsKey(newItem.getRecId())) {
|
|
|
+ ShipmentDetailDO oldItem = oldMap.get(newItem.getRecId());
|
|
|
+ String changes = compareShipmentDetail(oldItem, newItem);
|
|
|
+ if (!changes.isEmpty()) {
|
|
|
+ log.append(";更新了明细行【").append(oldItem.getLine()).append("】: ").append(changes);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 检查新增的明细(recId为空或recId不在旧数据中)
|
|
|
+ for (ShipmentSaveReqVO.ShipmentDetailSaveReqVO newItem : newItems) {
|
|
|
+ if (newItem.getRecId() == null || !oldMap.containsKey(newItem.getRecId())) {
|
|
|
+ log.append(";新增了明细行【").append(newItem.getContainerItem()).append("】");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 检查删除的明细(旧recId不在新数据中)
|
|
|
+ for (ShipmentDetailDO oldItem : oldDetails) {
|
|
|
+ if (oldItem.getRecId() != null && !newMap.containsKey(oldItem.getRecId())) {
|
|
|
+ log.append(";删除了明细行【").append(oldItem.getLine()).append("】物料【").append(oldItem.getContainerItem()).append("】");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return log.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 对比单条发货单明细的变更
|
|
|
+ */
|
|
|
+ private String compareShipmentDetail(ShipmentDetailDO oldItem,
|
|
|
+ ShipmentSaveReqVO.ShipmentDetailSaveReqVO newItem) {
|
|
|
+ StringBuilder changes = new StringBuilder();
|
|
|
+
|
|
|
+ // 使用compareTo比较BigDecimal,避免精度差异导致误判
|
|
|
+ if (!compareBigDecimal(oldItem.getQtyToShip(), newItem.getQtyToShip())) {
|
|
|
+ changes.append("【发运数量】从【").append(formatBigDecimal(oldItem.getQtyToShip())).append("】修改为【").append(formatBigDecimal(newItem.getQtyToShip())).append("】;");
|
|
|
+ }
|
|
|
+ if (!compareBigDecimal(oldItem.getPickingQty(), newItem.getPickingQty())) {
|
|
|
+ changes.append("【挑选数量】从【").append(formatBigDecimal(oldItem.getPickingQty())).append("】修改为【").append(formatBigDecimal(newItem.getPickingQty())).append("】;");
|
|
|
+ }
|
|
|
+ if (!compareBigDecimal(oldItem.getRealQty(), newItem.getRealQty())) {
|
|
|
+ changes.append("【实际数量】从【").append(formatBigDecimal(oldItem.getRealQty())).append("】修改为【").append(formatBigDecimal(newItem.getRealQty())).append("】;");
|
|
|
+ }
|
|
|
+ if (!java.util.Objects.equals(oldItem.getContainerItem(), newItem.getContainerItem())) {
|
|
|
+ changes.append("【物料编号】从【").append(oldItem.getContainerItem()).append("】修改为【").append(newItem.getContainerItem()).append("】;");
|
|
|
+ }
|
|
|
+ if (!java.util.Objects.equals(oldItem.getDescr(), newItem.getDescr())) {
|
|
|
+ changes.append("【描述】从【").append(oldItem.getDescr()).append("】修改为【").append(newItem.getDescr()).append("】;");
|
|
|
+ }
|
|
|
+ if (!java.util.Objects.equals(nullToEmpty(oldItem.getLocation()), nullToEmpty(newItem.getLocation()))) {
|
|
|
+ changes.append("【库位】从【").append(nullToEmpty(oldItem.getLocation())).append("】修改为【").append(nullToEmpty(newItem.getLocation())).append("】;");
|
|
|
+ }
|
|
|
+ if (!java.util.Objects.equals(nullToEmpty(oldItem.getLotSerial()), nullToEmpty(newItem.getLotSerial()))) {
|
|
|
+ changes.append("【批次号】从【").append(nullToEmpty(oldItem.getLotSerial())).append("】修改为【").append(nullToEmpty(newItem.getLotSerial())).append("】;");
|
|
|
+ }
|
|
|
+ if (!java.util.Objects.equals(oldItem.getRemark(), newItem.getRemark())) {
|
|
|
+ changes.append("【备注】从【").append(oldItem.getRemark()).append("】修改为【").append(newItem.getRemark()).append("】;");
|
|
|
+ }
|
|
|
+ if (!java.util.Objects.equals(oldItem.getStatus(), newItem.getStatus())) {
|
|
|
+ changes.append("【状态】从【").append(oldItem.getStatus()).append("】修改为【").append(newItem.getStatus()).append("】;");
|
|
|
+ }
|
|
|
+
|
|
|
+ return changes.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 比较两个BigDecimal是否相等(忽略精度差异)
|
|
|
+ */
|
|
|
+ private boolean compareBigDecimal(java.math.BigDecimal a, java.math.BigDecimal b) {
|
|
|
+ if (a == null && b == null) return true;
|
|
|
+ if (a == null || b == null) return false;
|
|
|
+ return a.compareTo(b) == 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 格式化BigDecimal,去除尾部多余的0
|
|
|
+ */
|
|
|
+ private String formatBigDecimal(java.math.BigDecimal value) {
|
|
|
+ if (value == null) return "空";
|
|
|
+ return value.stripTrailingZeros().toPlainString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将null转为空字符串
|
|
|
+ */
|
|
|
+ private String nullToEmpty(String value) {
|
|
|
+ return value == null ? "" : value;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -203,9 +368,9 @@ public class ShipmentServiceImpl implements ShipmentService {
|
|
|
detail.setUm(item.getUm());
|
|
|
detail.setLocation(item.getLocation());
|
|
|
detail.setLotSerial(item.getLotSerial());
|
|
|
- detail.setQtyToShip(item.getQtyToShip());
|
|
|
- detail.setPickingQty(item.getPickingQty());
|
|
|
- detail.setRealQty(item.getRealQty());
|
|
|
+ detail.setQtyToShip(item.getQtyToShip() != null ? item.getQtyToShip() : java.math.BigDecimal.ZERO);
|
|
|
+ detail.setPickingQty(item.getPickingQty() != null ? item.getPickingQty() : java.math.BigDecimal.ZERO);
|
|
|
+ detail.setRealQty(item.getRealQty() != null ? item.getRealQty() : java.math.BigDecimal.ZERO);
|
|
|
detail.setStatus(item.getStatus());
|
|
|
detail.setRemark(item.getRemark());
|
|
|
detail.setShType("SH");
|