| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- package com.jeesite.modules.mes.erp;
- import com.alibaba.fastjson.JSONArray;
- import com.alibaba.fastjson.JSONObject;
- import com.jeesite.common.lang.StringUtils;
- import com.jeesite.modules.mes.entity.MesBatchSn;
- import com.jeesite.modules.mes.entity.MesErpPrdInstockLog;
- import com.jeesite.modules.mes.service.MesBatchSnService;
- import com.jeesite.modules.mes.service.MesErpPrdInstockLogService;
- 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.time.LocalDate;
- import java.time.format.DateTimeFormatter;
- import java.util.ArrayList;
- import java.util.Date;
- import java.util.LinkedHashSet;
- import java.util.List;
- import java.util.Set;
- import java.util.stream.Collectors;
- /**
- * ERP 生产入库单 Save(PRD_INSTOCK)。
- * 数量由 MES 批次码 mes_batch_sn.nums 汇总;ERP 批号 FLot 由 ERP 系统内部生成,Save 后回查。
- */
- @Service
- public class ErpPrdInStockSaveService {
- private static final Logger logger = LoggerFactory.getLogger(ErpPrdInStockSaveService.class);
- private static final String FORM_ID = "PRD_INSTOCK";
- private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- private final KingdeeErpClient erpClient;
- private final KingdeeErpProperties erpProperties;
- private final ErpProductionOrderService erpProductionOrderService;
- private final ErpProductionMaterialService erpProductionMaterialService;
- private final ErpPrdInStockService erpPrdInStockService;
- private final ErpStockLocService erpStockLocService;
- private final MesBatchSnService mesBatchSnService;
- private final MesErpPrdInstockLogService mesErpPrdInstockLogService;
- public ErpPrdInStockSaveService(KingdeeErpClient erpClient,
- KingdeeErpProperties erpProperties,
- ErpProductionOrderService erpProductionOrderService,
- ErpProductionMaterialService erpProductionMaterialService,
- ErpPrdInStockService erpPrdInStockService,
- ErpStockLocService erpStockLocService,
- MesBatchSnService mesBatchSnService,
- MesErpPrdInstockLogService mesErpPrdInstockLogService) {
- this.erpClient = erpClient;
- this.erpProperties = erpProperties;
- this.erpProductionOrderService = erpProductionOrderService;
- this.erpProductionMaterialService = erpProductionMaterialService;
- this.erpPrdInStockService = erpPrdInStockService;
- this.erpStockLocService = erpStockLocService;
- this.mesBatchSnService = mesBatchSnService;
- this.mesErpPrdInstockLogService = mesErpPrdInstockLogService;
- }
- @Transactional
- public ErpPrdInStockResult submit(ErpPrdInStockSubmitRequest request) {
- if (request == null || StringUtils.isBlank(request.getMoBillNo())) {
- return ErpPrdInStockResult.fail("生产订单号不能为空");
- }
- if (StringUtils.isBlank(request.getEntryId())) {
- return ErpPrdInStockResult.fail("生产订单分录不能为空");
- }
- if (request.getBatchIds() == null || request.getBatchIds().isEmpty()) {
- return ErpPrdInStockResult.fail("请至少选择一个 MES 批次码");
- }
- ErpProductionOrder moEntry = erpProductionOrderService.findInstockMoEntry(
- request.getMoBillNo().trim(), request.getEntryId().trim());
- if (moEntry == null) {
- return ErpPrdInStockResult.fail("未找到对应的生产订单分录");
- }
- if (!erpProperties.isAllowedPrdInStockMaterial(moEntry.getMaterialNumber())) {
- return ErpPrdInStockResult.fail("该订单分录物料不在 HEV/115 入库范围内");
- }
- List<MesBatchSn> batches = loadAndValidateBatches(request.getBatchIds(), moEntry.getMaterialNumber());
- if (batches.isEmpty()) {
- return ErpPrdInStockResult.fail("未找到有效的待入库批次码");
- }
- BigDecimal totalQty = sumBatchQty(batches);
- if (totalQty.compareTo(BigDecimal.ZERO) <= 0) {
- return ErpPrdInStockResult.fail("所选批次码合计数量必须大于 0");
- }
- String stockNumber = StringUtils.trim(request.getStockNumber());
- if (StringUtils.isBlank(stockNumber)) {
- stockNumber = firstNonBlank(moEntry.getStockNumber(), erpProperties.getPrdInStockDefaultStockNumber(),
- erpProperties.getInStockStockNumber());
- }
- if (StringUtils.isBlank(stockNumber)) {
- return ErpPrdInStockResult.fail("请选择入库仓库");
- }
- String stockLocNumber = StringUtils.trim(request.getStockLocNumber());
- if (erpStockLocService.requiresStockLocation(stockNumber) && StringUtils.isBlank(stockLocNumber)) {
- return ErpPrdInStockResult.fail("所选仓库需要选择仓位");
- }
- String unitNumber = resolveUnitNumber(moEntry);
- if (StringUtils.isBlank(unitNumber)) {
- return ErpPrdInStockResult.fail("物料单位编码为空,无法入库");
- }
- JSONObject saveData = buildSaveData(moEntry, totalQty, unitNumber, stockNumber, stockLocNumber);
- logger.info("ERP production in-stock save request: {}", saveData.toJSONString());
- JSONObject response = erpClient.save(FORM_ID, saveData);
- ErpPrdInStockResult result = parseSaveResponse(response, totalQty, batches.size());
- if (!result.isSuccess()) {
- return result;
- }
- String erpLotNumber = resolveErpLotNumber(result.getBillNo());
- if (StringUtils.isNotBlank(erpLotNumber)) {
- result = ErpPrdInStockResult.success(result.getBillNo(), result.getBillId(), erpLotNumber,
- totalQty, batches.size());
- }
- Date now = new Date();
- mesBatchSnService.markInstocked(batches, moEntry.getBillNo(), moEntry.getEntryId(),
- result.getBillNo(), result.getLotNumber(), now);
- saveInstockLog(moEntry, batches, result, stockNumber, stockLocNumber, totalQty, now);
- return result;
- }
- /** 加载并校验 MES 批次码:nums>0、未入库、产线与物料匹配 */
- private List<MesBatchSn> loadAndValidateBatches(List<String> batchIds, String materialNumber) {
- List<MesBatchSn> batches = mesBatchSnService.findByIds(batchIds);
- List<MesBatchSn> valid = new ArrayList<>();
- for (MesBatchSn batch : batches) {
- if (batch == null) {
- continue;
- }
- if (batch.getNums() == null || batch.getNums() <= 0) {
- continue;
- }
- if ("1".equals(StringUtils.trim(batch.getInstockStatus()))) {
- continue;
- }
- if (!MesErpLineMaterialMapping.isLineAllowedForMaterial(batch.getLineSn(), materialNumber)) {
- continue;
- }
- valid.add(batch);
- }
- return valid;
- }
- private BigDecimal sumBatchQty(List<MesBatchSn> batches) {
- BigDecimal total = BigDecimal.ZERO;
- for (MesBatchSn batch : batches) {
- if (batch.getNums() != null) {
- total = total.add(BigDecimal.valueOf(batch.getNums()));
- }
- }
- return ErpKingdeeParseUtils.normalizeQty(total);
- }
- private String resolveUnitNumber(ErpProductionOrder moEntry) {
- if (StringUtils.isNotBlank(moEntry.getUnitNumber())) {
- return moEntry.getUnitNumber().trim();
- }
- return erpProductionMaterialService.findMaterialUnitNumber(moEntry.getMaterialNumber());
- }
- private JSONObject buildSaveData(ErpProductionOrder moEntry, BigDecimal totalQty, String unitNumber,
- String stockNumber, String stockLocNumber) {
- String orgNumber = firstNonBlank(moEntry.getPrdOrgNumber(), erpProperties.getOrgNumber(), "110");
- String ownerType = erpProperties.getPrdInStockOwnerType();
- String keeperType = erpProperties.getPrdInStockKeeperType();
- String stockStatus = erpProperties.getPrdInStockStockStatus();
- Object erpQty = ErpKingdeeParseUtils.toErpQty(totalQty);
- JSONObject model = new JSONObject(true);
- model.put("FID", 0);
- model.put("FBillType", numberObject(erpProperties.getPrdInStockBillType(), true));
- model.put("FDate", LocalDate.now().format(DATE_FORMAT));
- model.put("FPrdOrgId", numberObject(orgNumber, false));
- model.put("FStockOrgId", numberObject(orgNumber, false));
- model.put("FOwnerTypeId0", ownerType);
- model.put("FOwnerId0", numberObject(orgNumber, false));
- if (StringUtils.isNotBlank(moEntry.getWorkshopNumber())) {
- model.put("FWorkShopId", numberObject(moEntry.getWorkshopNumber(), false));
- }
- JSONObject entity = new JSONObject(true);
- entity.put("FEntryID", 0);
- entity.put("FMaterialId", numberObject(moEntry.getMaterialNumber(), false));
- entity.put("FInStockType", erpProperties.getPrdInStockInStockType());
- entity.put("FUnitID", numberObject(unitNumber, false));
- entity.put("FBaseUnitId", numberObject(unitNumber, false));
- entity.put("FMustQty", erpQty);
- entity.put("FRealQty", erpQty);
- entity.put("FBaseMustQty", erpQty);
- entity.put("FBaseRealQty", erpQty);
- entity.put("FStockRealQty", erpQty);
- entity.put("FMoBillNo", moEntry.getBillNo());
- entity.put("FMoId", parseLong(moEntry.getFid()));
- entity.put("FMoEntryId", parseLong(moEntry.getEntryId()));
- if (StringUtils.isNotBlank(moEntry.getSeq())) {
- entity.put("FMoEntrySeq", parseInt(moEntry.getSeq()));
- }
- entity.put("FOwnerTypeId", ownerType);
- entity.put("FOwnerId", numberObject(orgNumber, false));
- entity.put("FKeeperTypeId", keeperType);
- entity.put("FKeeperId", numberObject(orgNumber, false));
- entity.put("FStockId", numberObject(stockNumber, false));
- entity.put("FStockStatusId", numberObject(stockStatus, false));
- putStockLoc(entity, stockNumber, stockLocNumber);
- // 不传 FLot,由 ERP 系统内部生成批号
- JSONArray entityList = new JSONArray();
- entityList.add(entity);
- model.put("FEntity", entityList);
- JSONObject saveData = new JSONObject(true);
- saveData.put("NeedUpDateFields", new JSONArray());
- saveData.put("NeedReturnFields", JSONArray.parseArray("[\"FBillNo\"]"));
- saveData.put("IsDeleteEntry", "true");
- saveData.put("IsVerifyBaseDataField", "false");
- saveData.put("IsEntryBatchFill", "true");
- saveData.put("ValidateFlag", "true");
- saveData.put("NumberSearch", "true");
- saveData.put("IsAutoAdjustField", "true");
- saveData.put("IgnoreInterationFlag", "true");
- saveData.put("Model", model);
- return saveData;
- }
- private void putStockLoc(JSONObject entry, String stockNumber, String stockLocNumber) {
- if (!erpStockLocService.requiresStockLocation(stockNumber)) {
- return;
- }
- String locNumber = StringUtils.trim(stockLocNumber);
- if (StringUtils.isBlank(locNumber)) {
- return;
- }
- String locKey = erpStockLocService.resolveLocKeyForStock(stockNumber);
- String saveKey = erpProperties.toStockLocSaveKey(locKey);
- JSONObject flexValue = numberObject(locNumber, false);
- JSONObject stockLoc = new JSONObject(true);
- stockLoc.put(saveKey, flexValue);
- entry.put("FStockLocId", stockLoc);
- }
- /** Save 成功后 BillQuery 回查 ERP 内部批号 */
- private String resolveErpLotNumber(String billNo) {
- if (StringUtils.isBlank(billNo)) {
- return "";
- }
- List<ErpPrdInStock> rows = erpPrdInStockService.findDetailList(billNo.trim());
- for (ErpPrdInStock row : rows) {
- if (StringUtils.isNotBlank(row.getLotNumber())) {
- return row.getLotNumber().trim();
- }
- }
- return "";
- }
- private void saveInstockLog(ErpProductionOrder moEntry, List<MesBatchSn> batches, ErpPrdInStockResult result,
- String stockNumber, String stockLocNumber, BigDecimal totalQty, Date now) {
- Set<String> batchSnSet = new LinkedHashSet<>();
- for (MesBatchSn batch : batches) {
- if (batch != null && StringUtils.isNotBlank(batch.getBatchSn())) {
- batchSnSet.add(batch.getBatchSn().trim());
- }
- }
- MesErpPrdInstockLog log = new MesErpPrdInstockLog();
- log.setIsNewRecord(true);
- log.setMoBillNo(moEntry.getBillNo());
- log.setMoEntryId(moEntry.getEntryId());
- log.setMaterialNumber(moEntry.getMaterialNumber());
- log.setErpBillNo(result.getBillNo());
- log.setErpLotNumber(result.getLotNumber());
- log.setTotalQty(totalQty);
- log.setBatchCount(batches.size());
- log.setStockNumber(stockNumber);
- log.setStockLocNumber(stockLocNumber);
- log.setBatchSnList(batchSnSet.stream().collect(Collectors.joining(",")));
- log.setStatus("0");
- log.setCreateDate(now);
- log.setUpdateDate(now);
- mesErpPrdInstockLogService.saveLog(log);
- }
- private JSONObject numberObject(String number, boolean useFNumberKey) {
- JSONObject object = new JSONObject();
- object.put(useFNumberKey ? "FNUMBER" : "FNumber", number);
- return object;
- }
- private long parseLong(String value) {
- if (StringUtils.isBlank(value)) {
- return 0L;
- }
- return Long.parseLong(value.trim());
- }
- private int parseInt(String value) {
- if (StringUtils.isBlank(value)) {
- return 0;
- }
- return Integer.parseInt(value.trim());
- }
- private String firstNonBlank(String... values) {
- for (String value : values) {
- if (StringUtils.isNotBlank(value)) {
- return value.trim();
- }
- }
- return "";
- }
- private ErpPrdInStockResult parseSaveResponse(JSONObject response, BigDecimal totalQty, int batchCount) {
- if (response == null) {
- return ErpPrdInStockResult.fail("ERP 返回为空");
- }
- JSONObject resultWrapper = response.getJSONObject("Result");
- JSONObject status = resultWrapper == null ? null : resultWrapper.getJSONObject("ResponseStatus");
- if (status != null && Boolean.TRUE.equals(status.getBoolean("IsSuccess"))) {
- JSONObject result = resultWrapper.getJSONObject("Result");
- String billNo = result == null ? "" : result.getString("Number");
- if (StringUtils.isBlank(billNo)) {
- billNo = resultWrapper.getString("Number");
- }
- String billId = result == null ? "" : result.getString("Id");
- if (StringUtils.isBlank(billId)) {
- billId = resultWrapper.getString("Id");
- }
- return ErpPrdInStockResult.success(billNo, billId, "", totalQty, batchCount);
- }
- return ErpPrdInStockResult.fail(extractErrorMessage(status, response));
- }
- private String extractErrorMessage(JSONObject status, JSONObject response) {
- if (status != null) {
- JSONArray errors = status.getJSONArray("Errors");
- if (errors != null && !errors.isEmpty()) {
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < errors.size(); i++) {
- JSONObject error = errors.getJSONObject(i);
- if (builder.length() > 0) {
- builder.append(";");
- }
- builder.append(error.getString("Message"));
- }
- if (builder.length() > 0) {
- return builder.toString();
- }
- }
- }
- return "ERP 生产入库单保存失败:" + response.toJSONString();
- }
- }
|