package com.jeesite.modules.mes.erp; import com.jeesite.common.lang.StringUtils; import com.jeesite.modules.mes.entity.MesMaterial; import com.jeesite.modules.mes.entity.MesMaterialCk; import com.jeesite.modules.mes.entity.MesMaterialKcLog; import com.jeesite.modules.mes.entity.MesMaterialKw; import com.jeesite.modules.mes.service.MesMaterialCkService; import com.jeesite.modules.mes.service.MesMaterialKcLogService; import com.jeesite.modules.mes.service.MesMaterialKwService; import com.jeesite.modules.mes.service.MesMaterialService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; /** * ERP 生产领料成功后,同步 MES 物料主数据并增加线边库存(mes_material_kc_log)。 */ @Service public class MesErpPickKcService { private static final Logger logger = LoggerFactory.getLogger(MesErpPickKcService.class); private static final String SOURCE_TYPE_ERP_PICK = "ERP_PICK"; private static final String DEFAULT_EXPIRE_DATE = "2099-01-01 00:00:00"; private final KingdeeErpProperties erpProperties; private final MesMaterialService mesMaterialService; private final MesMaterialKcLogService mesMaterialKcLogService; private final MesMaterialKwService mesMaterialKwService; private final MesMaterialCkService mesMaterialCkService; public MesErpPickKcService(KingdeeErpProperties erpProperties, MesMaterialService mesMaterialService, MesMaterialKcLogService mesMaterialKcLogService, MesMaterialKwService mesMaterialKwService, MesMaterialCkService mesMaterialCkService) { this.erpProperties = erpProperties; this.mesMaterialService = mesMaterialService; this.mesMaterialKcLogService = mesMaterialKcLogService; this.mesMaterialKwService = mesMaterialKwService; this.mesMaterialCkService = mesMaterialCkService; } /** * MES 提交 ERP 领料 Save 成功后调用。 */ @Transactional public MesErpPickKcSyncResult increaseFromSubmit(String pickBillNo, List lines) { MesErpPickKcSyncResult result = new MesErpPickKcSyncResult(); if (StringUtils.isBlank(pickBillNo) || lines == null || lines.isEmpty()) { return result; } String kwId = resolveMesKwId(); for (SubmitLine line : lines) { if (line == null || line.getMaterial() == null) { continue; } ErpProductionMaterial material = line.getMaterial(); String lineKey = firstNonBlank(material.getId(), material.getSeq(), material.getMaterialNumber()); increaseOne(result, kwId, pickBillNo.trim(), lineKey, material.getBillNo(), material.getMaterialNumber(), material.getMaterialName(), material.getSpecification(), line.getPickQty()); } logger.info("ERP pick submit synced to MES kc, pickBillNo={}, materialCreated={}, kcCreated={}", pickBillNo, result.getMaterialCreatedCount(), result.getKcCreatedCount()); return result; } /** * 从 ERP 领料单明细补同步(幂等,已同步分录跳过)。 */ @Transactional public MesErpPickKcSyncResult increaseFromBillDetails(String pickBillNo, List details) { MesErpPickKcSyncResult result = new MesErpPickKcSyncResult(); if (StringUtils.isBlank(pickBillNo) || details == null || details.isEmpty()) { return result; } String kwId = resolveMesKwId(); for (ErpPickMaterialBill detail : details) { if (detail == null || StringUtils.isBlank(detail.getMaterialNumber())) { continue; } BigDecimal qty = detail.getActualQty() != null ? detail.getActualQty() : detail.getAppQty(); if (qty == null || qty.compareTo(BigDecimal.ZERO) <= 0) { continue; } String lineKey = firstNonBlank(detail.getEntryId(), detail.getSeq(), detail.getMaterialNumber()); increaseOne(result, kwId, pickBillNo.trim(), lineKey, detail.getMoBillNo(), detail.getMaterialNumber(), detail.getMaterialName(), "", qty); } if (result.getKcCreatedCount() > 0) { logger.info("ERP pick bill detail synced to MES kc, pickBillNo={}, kcCreated={}", pickBillNo, result.getKcCreatedCount()); } return result; } private void increaseOne(MesErpPickKcSyncResult result, String kwId, String pickBillNo, String lineKey, String moBillNo, String materialNumber, String materialName, String specification, BigDecimal qty) { if (StringUtils.isBlank(materialNumber) || qty == null || qty.compareTo(BigDecimal.ZERO) <= 0) { return; } String erpSn = materialNumber.trim(); String pickLineKey = StringUtils.isNotBlank(lineKey) ? lineKey.trim() : erpSn; if (mesMaterialKcLogService.findByErpPickLine(pickBillNo, pickLineKey) != null) { return; } MesMaterial existing = mesMaterialService.findByErpSn(erpSn); MesMaterial mesMaterial = mesMaterialService.ensureFromErp(erpSn, materialName, specification); if (existing == null && mesMaterial != null) { result.addMaterialCreated(); } if (mesMaterial == null || StringUtils.isBlank(mesMaterial.getId())) { logger.warn("ERP pick kc skip: mes material missing, erpSn={}", erpSn); return; } long nums = qty.setScale(0, RoundingMode.HALF_UP).longValue(); if (nums <= 0) { return; } MesMaterialKcLog kcLog = new MesMaterialKcLog(); kcLog.setMaterialId(mesMaterial.getId()); kcLog.setKwId(kwId); kcLog.setBatchSn(pickBillNo + "-" + pickLineKey); kcLog.setNums(nums); kcLog.setExpireDate(DEFAULT_EXPIRE_DATE); kcLog.setSourceType(SOURCE_TYPE_ERP_PICK); kcLog.setErpPickBillNo(pickBillNo); kcLog.setErpPickLineKey(pickLineKey); kcLog.setErpMoBillNo(StringUtils.trim(moBillNo)); kcLog.setRemark("ERP生产领料入库"); mesMaterialKcLogService.save(kcLog); result.addKcCreated(); } private String resolveMesKwId() { if (StringUtils.isNotBlank(erpProperties.getPickMtrlMesKwId())) { MesMaterialKw kw = mesMaterialKwService.get(new MesMaterialKw(erpProperties.getPickMtrlMesKwId().trim())); if (kw != null && StringUtils.isNotBlank(kw.getId())) { return kw.getId(); } logger.warn("ERP pick kc: configured mes-kw-id not found, id={}", erpProperties.getPickMtrlMesKwId()); } String kwTitle = StringUtils.defaultIfBlank(erpProperties.getPickMtrlMesKwTitle(), "ERP线边库").trim(); MesMaterialKw query = new MesMaterialKw(); query.setTitle(kwTitle); List kwList = mesMaterialKwService.findList(query); if (kwList != null && !kwList.isEmpty() && StringUtils.isNotBlank(kwList.get(0).getId())) { return kwList.get(0).getId(); } return createDefaultKw(kwTitle); } private String createDefaultKw(String kwTitle) { String ckName = StringUtils.defaultIfBlank(erpProperties.getPickMtrlMesCkName(), "ERP线边仓").trim(); MesMaterialCk ckQuery = new MesMaterialCk(); ckQuery.setCkName(ckName); List ckList = mesMaterialCkService.findList(ckQuery); String ckId; if (ckList != null && !ckList.isEmpty() && StringUtils.isNotBlank(ckList.get(0).getId())) { ckId = ckList.get(0).getId(); } else { MesMaterialCk ck = new MesMaterialCk(); ck.setCkName(ckName); mesMaterialCkService.save(ck); ckId = ck.getId(); } MesMaterialKw kw = new MesMaterialKw(); kw.setTitle(kwTitle); kw.setCkId(ckId); mesMaterialKwService.save(kw); logger.info("ERP pick kc: auto created kw={}, ck={}", kwTitle, ckName); return kw.getId(); } private String firstNonBlank(String... values) { for (String value : values) { if (StringUtils.isNotBlank(value)) { return value.trim(); } } return ""; } /** 提交领料时的分录上下文 */ public static class SubmitLine { private final ErpProductionMaterial material; private final BigDecimal pickQty; public SubmitLine(ErpProductionMaterial material, BigDecimal pickQty) { this.material = material; this.pickQty = pickQty; } public ErpProductionMaterial getMaterial() { return material; } public BigDecimal getPickQty() { return pickQty; } } }