diff --git a/src/main/java/com/gaotao/modules/po/controller/PoController.java b/src/main/java/com/gaotao/modules/po/controller/PoController.java index 14654943..4773f52f 100644 --- a/src/main/java/com/gaotao/modules/po/controller/PoController.java +++ b/src/main/java/com/gaotao/modules/po/controller/PoController.java @@ -37,6 +37,12 @@ public class PoController extends AbstractController { return R.ok().put("rows",list); } + @PostMapping("getPartShelfLife") + public R getPartShelfLife(@RequestBody Map params){ + Integer shelfLife = poService.getPartShelfLife(params.get("partNo"), params.get("site")); + return R.ok().put("data", shelfLife); + } + @SysLog("采购订单-接收") @PostMapping("receivePo") public R receivePo(@RequestBody TransDetailDto inData){ diff --git a/src/main/java/com/gaotao/modules/po/service/PoService.java b/src/main/java/com/gaotao/modules/po/service/PoService.java index ecba3f17..8d3bb35e 100644 --- a/src/main/java/com/gaotao/modules/po/service/PoService.java +++ b/src/main/java/com/gaotao/modules/po/service/PoService.java @@ -12,6 +12,11 @@ public interface PoService extends IService { List> getPoList(PurchaseOrderDto accessSite); + /** + * 查询物料有效期天数。页面用它按供应商生产日期或到达日期预填失效日期。 + */ + Integer getPartShelfLife(String partNo, String site); + public String saveTrans(TransDetailDto indata); /** diff --git a/src/main/java/com/gaotao/modules/po/service/impl/PoServiceImpl.java b/src/main/java/com/gaotao/modules/po/service/impl/PoServiceImpl.java index 3ed91ce3..a1227992 100644 --- a/src/main/java/com/gaotao/modules/po/service/impl/PoServiceImpl.java +++ b/src/main/java/com/gaotao/modules/po/service/impl/PoServiceImpl.java @@ -8,7 +8,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.gaotao.common.exception.XJException; import com.gaotao.common.utils.*; import com.gaotao.modules.common.i18n.AppI18n; -import com.gaotao.modules.api.entity.dto.IfsChangeExpiryDateDto; import com.gaotao.modules.api.entity.issueAndReturnVo.InventoryPartVo; import com.gaotao.modules.api.service.IfsApiIssueAndReturnService; import com.gaotao.modules.api.service.IfsApiService; @@ -16,7 +15,6 @@ import com.gaotao.modules.factory.dao.AccessSiteMapper; import com.gaotao.modules.factory.dao.PartAttributeMapper; import com.gaotao.modules.factory.entity.PartAttribute; import com.gaotao.modules.factory.entity.Site; -import com.gaotao.modules.warehouse.mapper.HandlingUnitSpecialMapper; import lombok.extern.slf4j.Slf4j; import java.math.RoundingMode; @@ -93,9 +91,6 @@ public class PoServiceImpl extends ServiceImpl implemen @Autowired private IfsApiService ifsApiService; - @Autowired - private HandlingUnitSpecialMapper handlingUnitSpecialMapper; - @Autowired private AppI18n i18n; @@ -183,15 +178,23 @@ public class PoServiceImpl extends ServiceImpl implemen @Transactional public String saveTrans(TransDetailDto inData) { try { - List parts = ifsApiIssueAndReturnService.getInventoryPart(inData.getPartNo(),inData.getSite()); - Integer shelfLife = null; // 有效期 - if (parts!=null && !parts.isEmpty()) { - shelfLife = parts.getFirst().getDurabilityDays()!=0?parts.getFirst().getDurabilityDays():null; - } - // 如果物料没有设置shelfLife,报错提示:请联系工程维护产品有效期! - if (shelfLife == null) { + // 有效期仍要有:页面按同一口径预填失效日期,没维护时不能入库 + if (resolveShelfLife(inData.getPartNo(), inData.getSite()) == null) { throw new XJException(i18n.text("purchase.po.shelfLifeMissing")); } + // 失效日期以前端提交为准,允许在计算结果上改,但不能空、不能早于今天 + if (inData.getExpiredDate() == null) { + throw new XJException(i18n.text("purchase.po.expiredDateRequired")); + } + if (isBeforeToday(inData.getExpiredDate())) { + String expiredDateText = DateUtils.format(inData.getExpiredDate(), DateUtils.DATE_PATTERN); + log.warn("采购入库失效日期早于当前日期,拒绝入库,poNo={}, partNo={}, expiredDate={}", + inData.getPoNo(), inData.getPartNo(), expiredDateText); + throw new XJException(i18n.text("purchase.po.expiredDateBeforeToday", expiredDateText)); + } + log.info("采购入库使用前端失效日期,poNo={}, partNo={}, expiredDate={}", + inData.getPoNo(), inData.getPartNo(), + DateUtils.format(inData.getExpiredDate(), DateUtils.DATE_PATTERN)); // 根据库位获取仓库ID String warehouseId = null; Location location = locationService.getByLocationIdAndSite(inData.getFuSite(), inData.getLocationNo()); @@ -199,7 +202,7 @@ public class PoServiceImpl extends ServiceImpl implemen warehouseId = location.getWarehouseId(); } // 创建采购接收记录 - 在库存更新之前创建 - PoReceiptDetail receiptDetail = createPoReceiptRecords(inData,shelfLife,warehouseId); + PoReceiptDetail receiptDetail = createPoReceiptRecords(inData, warehouseId); String receiptNo = receiptDetail.getReceiptNo(); SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); String transType = "CRT"; @@ -237,10 +240,9 @@ public class PoServiceImpl extends ServiceImpl implemen } else { inData.setNeedHandlingUnit("Y"); } - // 创建Handling Unit相关数据(仅收集本次操作生成的HU) - List currentHandlingUnits = new ArrayList<>(); + // 创建Handling Unit相关数据 for (int i = 0; i < inData.getHandlingUnitList().size(); i++) { - currentHandlingUnits.addAll(handlingUnitPart(inData, i, transHeader, currentUser,shelfLife,receiptDetail)); + handlingUnitPart(inData, i, transHeader, currentUser, receiptDetail); } // 免检更新库存 待检验的出入库记录状态为待检验 /*if ("N".equals(inData.getNeedCheck())) { @@ -248,24 +250,8 @@ public class PoServiceImpl extends ServiceImpl implemen }*/ // 处理料件属性(创建或更新) handlePartAttribute(inData); - // RegisterArrival 的 expirationDate 以本次生成 HU 的失效日期为准。 - // 同一批标签按同一基准日和有效期计算,取第一条非空日期即可,避免和标签不一致。 - for (HandlingUnit handlingUnit : currentHandlingUnits) { - if (handlingUnit.getExpiredDate() != null) { - inData.setExpiredDate(handlingUnit.getExpiredDate()); - break; - } - } - // 同步到IFS + // 同步到IFS,expirationDate 使用前端传入的失效日期 ifsApiService.registerArrivalToIFS(inData); - // 当用户选择供应商生产日期时,失效日期需要按供应商生产日期+有效期重新同步到IFS - /*if (inData.getSupplierManufactureDate() != null) { - Date supplierExpiryDate = calculateExpiryDate(inData.getSupplierManufactureDate(), shelfLife); - if (supplierExpiryDate != null) { - inData.setExpiredDate(supplierExpiryDate); - changeExpiryDate(currentHandlingUnits, inData); - } - }*/ return receiptNo; } catch (Exception e) { log.error("采购入库失败,PO号: {}, 错误: {}", inData.getPoNo(), e.getMessage()); @@ -274,9 +260,8 @@ public class PoServiceImpl extends ServiceImpl implemen } } - private List handlingUnitPart(TransDetailDto inData, int i, TransHeader transHeader, - SysUserEntity currentUser, Integer shelfLife,PoReceiptDetail receiptDetail) { - List createdHandlingUnits = new ArrayList<>(); + private void handlingUnitPart(TransDetailDto inData, int i, TransHeader transHeader, + SysUserEntity currentUser, PoReceiptDetail receiptDetail) { TransDetailSubDto huInfo = inData.getHandlingUnitList().get(i); Date supplierManufactureDate = inData.getSupplierManufactureDate(); for (int j = 0; j < huInfo.getPackageQty(); j++) { @@ -330,37 +315,9 @@ public class PoServiceImpl extends ServiceImpl implemen handlingUnit.setBatchNo(inData.getBatchNo()); handlingUnit.setLocationId(inData.getLocationNo()); handlingUnit.setWarehouseId(transHeader.getWarehouseId()); - // 记下参与计算的基准日,过期时报错要告诉用户是供应商生产日期还是到达日期导致的 - String expiryBaseName = null; - Date expiryBaseDate = null; - if (shelfLife==null || shelfLife==0) { - handlingUnit.setExpiredDate(null); - } else if (inData.getSupplierManufactureDate() != null) { - // 有供应商生产日期时优先用它推算,页面字段同名 - expiryBaseName = i18n.text("purchase.po.expiryBaseSupplierManufactureDate"); - expiryBaseDate = inData.getSupplierManufactureDate(); - java.util.Calendar calendar = java.util.Calendar.getInstance(); - calendar.setTime(expiryBaseDate); - calendar.add(java.util.Calendar.DAY_OF_YEAR, shelfLife); - handlingUnit.setExpiredDate(calendar.getTime()); - } else if (inData.getArrivalDate() != null) { - // 没有供应商生产日期才退回到达日期,避免两套基准日同时参与计算 - expiryBaseName = i18n.text("purchase.po.expiryBaseArrivalDate"); - expiryBaseDate = inData.getArrivalDate(); - java.util.Calendar calendar = java.util.Calendar.getInstance(); - calendar.setTime(expiryBaseDate); - calendar.add(java.util.Calendar.DAY_OF_YEAR, shelfLife); - handlingUnit.setExpiredDate(calendar.getTime()); - } - // 按自然日比较:当天仍可入库,早于今天说明物料已过期,必须拦住,避免过期料进入库存 - if (isBeforeToday(handlingUnit.getExpiredDate())) { - String expiredDateText = DateUtils.format(handlingUnit.getExpiredDate(), DateUtils.DATE_PATTERN); - String baseDateText = DateUtils.format(expiryBaseDate, DateUtils.DATE_PATTERN); - log.warn("采购入库失效日期早于当前日期,拒绝入库,poNo={}, partNo={}, baseDateName={}, baseDate={}, shelfLife={}, expiredDate={}", - inData.getPoNo(), inData.getPartNo(), expiryBaseName, baseDateText, shelfLife, expiredDateText); - throw new XJException(i18n.text("purchase.po.expiredDateBeforeToday", - expiryBaseName, baseDateText, shelfLife, expiredDateText)); - } + // 失效日期以前端提交为准。页面按供应商生产日期优先、否则到达日期,再加有效期预填,用户可改。 + // 标签必须和接收明细、IFS 用同一天,否则手改后会对不上。 + handlingUnit.setExpiredDate(inData.getExpiredDate()); handlingUnit.setWdr(inData.getWdr()!=null?inData.getWdr():"*"); handlingUnit.setStatus("ACTIVE"); handlingUnit.setStatusDb("ACTIVE"); @@ -428,9 +385,7 @@ public class PoServiceImpl extends ServiceImpl implemen huInfo.getPerQty(), currentUser.getUserDisplay(), "Y", "保存成功" ); - createdHandlingUnits.add(handlingUnit); } - return createdHandlingUnits; } private void genInventoryStock(TransDetailDto inData, TransHeader transHeader, PoReceiptDetail receiptDetail) { @@ -534,80 +489,6 @@ public class PoServiceImpl extends ServiceImpl implemen } } - private void changeExpiryDate(List handlingUnits, TransDetailDto inData) { - if (handlingUnits == null || handlingUnits.isEmpty()) { - log.warn("供应商生产日期模式下未查询到标签信息,跳过失效日期同步,transNo={}", inData.getTransNo()); - return; - } - if (inData.getExpiredDate() == null) { - log.warn("供应商生产日期模式下失效日期为空,跳过失效日期同步,transNo={}", inData.getTransNo()); - return; - } - - String ifsExpiryDate = DateUtils.format(inData.getExpiredDate(), DateUtils.DATE_PATTERN); - String modifiedBy = "SYSTEM"; - try { - SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); - if (currentUser != null && StringUtils.isNotBlank(currentUser.getUserDisplay())) { - modifiedBy = currentUser.getUserDisplay(); - } - } catch (Exception e) { - log.warn("获取当前用户失败,失效日期同步将使用默认操作人"); - } - - // 参考标签特殊信息批量修改逻辑,按库存关键字去重后调用IFS - Map inventoryKeyMap = new LinkedHashMap<>(); - for (HandlingUnit item : handlingUnits) { - String key = buildInventoryKey(item); - if (!inventoryKeyMap.containsKey(key)) { - inventoryKeyMap.put(key, item); - } - } - - log.info("供应商生产日期模式开始同步失效日期,标签数:{},去重后调用次数:{},新失效日期:{}", - handlingUnits.size(), inventoryKeyMap.size(), ifsExpiryDate); - - for (HandlingUnit item : inventoryKeyMap.values()) { - try { - IfsChangeExpiryDateDto dto = new IfsChangeExpiryDateDto(); - dto.setIfsSiteID(item.getSite()); - dto.setIfsPartNo(item.getPartNo()); - dto.setIfsLocationNo(item.getLocationId()); - dto.setIfsLotBatchNo(item.getBatchNo()); - dto.setIfsWdrNo(StringUtils.isNotBlank(item.getWdr()) ? item.getWdr() : "*"); - dto.setIfsEngChgLevel(StringUtils.isNotBlank(item.getEngChgLevel()) ? item.getEngChgLevel() : "1"); - dto.setIfsExpiryDate(ifsExpiryDate); - - ifsApiService.changeExpiryDate(dto); - - int updatedRows = handlingUnitSpecialMapper.updateExpiryDateByInventoryKey( - item.getSite(), - item.getPartNo(), - item.getLocationId(), - item.getBatchNo(), - StringUtils.isNotBlank(item.getWdr()) ? item.getWdr() : "*", - StringUtils.isNotBlank(item.getEngChgLevel()) ? item.getEngChgLevel() : "1", - ifsExpiryDate, - modifiedBy - ); - log.info("失效日期同步成功,unitId={},影响行数={}", item.getUnitId(), updatedRows); - } catch (Exception e) { - log.error("失效日期同步失败,unitId={},error={}", item.getUnitId(), e.getMessage(), e); - } - } - } - - private String buildInventoryKey(HandlingUnit item) { - return String.join("|", - item.getSite() != null ? item.getSite() : "", - item.getPartNo() != null ? item.getPartNo() : "", - item.getLocationId() != null ? item.getLocationId() : "", - item.getBatchNo() != null ? item.getBatchNo() : "", - item.getWdr() != null ? item.getWdr() : "", - item.getEngChgLevel() != null ? item.getEngChgLevel() : "" - ); - } - /** * 失效日期早于今天则视为已过期。只比较自然日,当天不算过期。 */ @@ -630,20 +511,30 @@ public class PoServiceImpl extends ServiceImpl implemen return expired.before(today); } - private Date calculateExpiryDate(Date baseDate, Integer shelfLife) { - if (baseDate == null || shelfLife == null || shelfLife <= 0) { - return null; + /** + * 取 IFS 物料有效期天数。0 视为未维护,和入库校验口径一致。 + */ + private Integer resolveShelfLife(String partNo, String site) { + List parts = ifsApiIssueAndReturnService.getInventoryPart(partNo, site); + if (parts != null && !parts.isEmpty() && parts.getFirst().getDurabilityDays() != 0) { + return parts.getFirst().getDurabilityDays(); + } + return null; + } + + @Override + public Integer getPartShelfLife(String partNo, String site) { + Integer shelfLife = resolveShelfLife(partNo, site); + if (shelfLife == null) { + throw new XJException(i18n.text("purchase.po.shelfLifeMissing")); } - Calendar calendar = Calendar.getInstance(); - calendar.setTime(baseDate); - calendar.add(Calendar.DAY_OF_YEAR, shelfLife); - return calendar.getTime(); + return shelfLife; } /** * 创建采购接收记录 */ - private PoReceiptDetail createPoReceiptRecords(TransDetailDto inData,Integer shelfLife,String warehouseId) { + private PoReceiptDetail createPoReceiptRecords(TransDetailDto inData, String warehouseId) { // 生成接收单号 TransNoControl receiptNoControl = transNoService.getTransNo(inData.getSite(), "PR", 10); String receiptNo = receiptNoControl.getNewTransNo(); @@ -777,22 +668,8 @@ public class PoServiceImpl extends ServiceImpl implemen if (inData.getManufactureDate() != null) { poReceiptDetail.setManuFactureDate(inData.getManufactureDate()); } - // 失效日期是根据生产日期ManufactureDate+有效期计算,如果生产日期为空,再用接收日期arrivalDate+有效期,如果有效期为空,失效日期就为空 - if (shelfLife==null || shelfLife==0) { - poReceiptDetail.setExpiredDate(null); - } else if (inData.getManufactureDate() != null) { - // 如果有制造日期,计算失效日期 - java.util.Calendar calendar = java.util.Calendar.getInstance(); - calendar.setTime(inData.getManufactureDate()); - calendar.add(java.util.Calendar.DAY_OF_YEAR, shelfLife); - poReceiptDetail.setExpiredDate(calendar.getTime()); - } else if (inData.getArrivalDate() != null) { - // 如果制造日期,但有接收日期,尝试用接收日期计算 - java.util.Calendar calendar = java.util.Calendar.getInstance(); - calendar.setTime(inData.getArrivalDate()); - calendar.add(java.util.Calendar.DAY_OF_YEAR, shelfLife); - poReceiptDetail.setExpiredDate(calendar.getTime()); - } + // 接收明细与标签、IFS 共用前端传入的失效日期,不再按制造日期重算 + poReceiptDetail.setExpiredDate(inData.getExpiredDate()); poReceiptDetail.setDeliveryDate(inData.getDeliveryDate()); poReceiptDetail.setArrivalDate(inData.getArrivalDate()); poReceiptDetail.setSupplierBatchNo(inData.getSupplierBatchNo()); diff --git a/src/main/resources/i18n/messages_en_US.properties b/src/main/resources/i18n/messages_en_US.properties index a1038447..e72ca283 100644 --- a/src/main/resources/i18n/messages_en_US.properties +++ b/src/main/resources/i18n/messages_en_US.properties @@ -216,9 +216,8 @@ purchase.po.orderNotFound=No matching purchase order was found purchase.po.orderLineNotFound=No matching purchase order lines were found purchase.po.getPoListFailedWithDetail=Failed to get PO list: {0} purchase.po.shelfLifeMissing=Please contact engineering to maintain product shelf life -purchase.po.expiryBaseSupplierManufactureDate=supplier manufacture date -purchase.po.expiryBaseArrivalDate=arrival date -purchase.po.expiredDateBeforeToday=Expiry date {3}, calculated from {0} {1} plus a shelf life of {2} days, is earlier than today and cannot be received +purchase.po.expiredDateRequired=Expiration date is required +purchase.po.expiredDateBeforeToday=Expiration date {0} is earlier than today and cannot be received purchase.po.receiptFailedWithDetail=Purchase receipt failed: {0} purchase.po.labelSaveReturnedFalseWithUnitId=Label save failed (save returned false): {0} purchase.po.labelSaveValidationFailedWithUnitId=Label save failed (validation check failed): {0} diff --git a/src/main/resources/i18n/messages_zh_CN.properties b/src/main/resources/i18n/messages_zh_CN.properties index 9f507465..f88c7fa8 100644 --- a/src/main/resources/i18n/messages_zh_CN.properties +++ b/src/main/resources/i18n/messages_zh_CN.properties @@ -216,9 +216,8 @@ purchase.po.orderNotFound=未找到对应的采购订单 purchase.po.orderLineNotFound=未找到对应的采购订单行 purchase.po.getPoListFailedWithDetail=获取PO列表失败: {0} purchase.po.shelfLifeMissing=请联系工程维护产品有效期! -purchase.po.expiryBaseSupplierManufactureDate=供应商生产日期 -purchase.po.expiryBaseArrivalDate=到达日期 -purchase.po.expiredDateBeforeToday=根据{0}{1}加有效期{2}天计算出的失效日期{3}早于当前日期,不能入库 +purchase.po.expiredDateRequired=失效日期不能为空 +purchase.po.expiredDateBeforeToday=失效日期{0}早于当前日期,不能入库 purchase.po.receiptFailedWithDetail=采购入库失败: {0} purchase.po.labelSaveReturnedFalseWithUnitId=标签保存失败(save返回false): {0} purchase.po.labelSaveValidationFailedWithUnitId=标签保存失败(验证未通过): {0}