| | |
| | | throw new CoolException("幂等键不能为空(eventId/taskNo/reportNo)"); |
| | | } |
| | | |
| | | String orderNo = pickString(root, "orderNo", "OrderNo", "WMSNO", "wmsno"); |
| | | if (StringUtils.isBlank(orderNo)) { |
| | | return CommonResponse.error("orderNo不能为空"); |
| | | } |
| | | |
| | | if (existsReportEvent(eventId, taskNo, reportNo)) { |
| | | return CommonResponse.ok("重复回传已忽略"); |
| | | } |
| | | OpenApiOrderReportEvent reportEvent = saveReportEvent(eventId, taskNo, reportNo, pickString(root, "orderNo", "WMSNO"), root.toJSONString()); |
| | | OpenApiOrderReportEvent reportEvent = saveReportEvent(eventId, taskNo, reportNo, orderNo, root.toJSONString()); |
| | | |
| | | JSONArray dataArray = pickArray(root, "Data", "data"); |
| | | // 解析明细:优先取 orderItems(Order格式),兼容 Data/data(ReportParams格式) |
| | | JSONArray dataArray = pickArray(root, "orderItems", "orderItems"); |
| | | if (Objects.isNull(dataArray) || dataArray.isEmpty()) { |
| | | dataArray = pickArray(root, "Data", "data"); |
| | | } |
| | | if (Objects.isNull(dataArray) || dataArray.isEmpty()) { |
| | | return CommonResponse.error("回传明细为空"); |
| | | } |
| | | |
| | | int allocateCount = 0; |
| | | int erpReportCount = 0; |
| | | List<String> errors = new ArrayList<>(); |
| | | // 对回传数据做反映射(WMS字段名 → ERP字段名),兼容旧格式 |
| | | JSONObject mappedRoot = ParamsMapUtils.reverseApiMaps("erp", "orderId", root); |
| | | JSONArray mappedDataArray = pickArray(mappedRoot, "orderItems", "orderItems"); |
| | | if (Objects.isNull(mappedDataArray) || mappedDataArray.isEmpty()) { |
| | | mappedDataArray = pickArray(mappedRoot, "Data", "data"); |
| | | } |
| | | if (Objects.isNull(mappedDataArray) || mappedDataArray.isEmpty()) { |
| | | mappedDataArray = dataArray; |
| | | } |
| | | |
| | | for (int i = 0; i < dataArray.size(); i++) { |
| | | JSONObject row = dataArray.getJSONObject(i); |
| | | String orderNo = pickString(row, "WMSNO", "wmsNo", "orderNo", "OrderNo"); |
| | | String matnrCode = pickString(row, "ItemCode", "itemCode", "matNr", "MatNr"); |
| | | String batch = pickString(row, "Batch", "batch", "GoodsNO", "goodsNo"); |
| | | BigDecimal doneQty = pickDecimal(row, "qty", "doneQty", "InQty", "OutQty"); |
| | | if (StringUtils.isBlank(orderNo) || StringUtils.isBlank(matnrCode) || doneQty.compareTo(ZERO) <= 0) { |
| | | errors.add("第" + (i + 1) + "行缺少必要字段(WMSNO/ItemCode/数量)"); |
| | | // 主单级信息(Order格式上报时携带) |
| | | String orderType = pickString(root, "type", "Type"); |
| | | String wkType = pickString(root, "wkType", "WkType"); |
| | | String poCode = pickString(root, "poCode", "PoCode", "PONO"); |
| | | String editUser = pickString(root, "editUser", "EditUser"); |
| | | Date editDate = null; |
| | | Object editDateObj = root.get("editDate"); |
| | | if (editDateObj instanceof Number) { |
| | | editDate = toDate(((Number) editDateObj).longValue()); |
| | | } |
| | | |
| | | int allocateCount = 0; |
| | | List<String> errors = new ArrayList<>(); |
| | | Set<String> affectedOrderNos = new HashSet<>(); |
| | | Map<String, OpenApiOrder> orderCache = new HashMap<>(); |
| | | |
| | | for (int i = 0; i < mappedDataArray.size(); i++) { |
| | | JSONObject row = mappedDataArray.getJSONObject(i); |
| | | // Order格式:matNr/anfme/batch/doneQty/lineId |
| | | // ReportParams格式兼容:ItemCode/InQty/OutQty/GoodsNO |
| | | String matnrCode = pickString(row, "matNr", "MatNr", "ItemCode", "itemCode"); |
| | | String batch = pickString(row, "batch", "Batch", "GoodsNO", "goodsNo"); |
| | | String lineId = pickString(row, "lineId", "LineId", "sourceLineId"); |
| | | BigDecimal doneQty = pickDecimal(row, "doneQty", "anfme", "qty", "InQty", "inQty", "OutQty", "outQty", "pdqty", "PDQty"); |
| | | if (StringUtils.isBlank(matnrCode) || doneQty.compareTo(ZERO) <= 0) { |
| | | errors.add("第" + (i + 1) + "行缺少必要字段(matNr/数量)"); |
| | | continue; |
| | | } |
| | | doneQty = doneQty.setScale(2, RoundingMode.HALF_UP); |
| | | |
| | | try { |
| | | int singleAllocated = allocateMergedDoneQty(orderNo, matnrCode, batch, doneQty); |
| | | int singleAllocated = allocateMergedDoneQty(orderNo, matnrCode, batch, lineId, doneQty, orderCache); |
| | | allocateCount += singleAllocated; |
| | | erpReportCount += reportReadyLinesToErp(orderNo); |
| | | refreshOrderFinishStatus(orderNo); |
| | | affectedOrderNos.add(orderNo); |
| | | } catch (Exception e) { |
| | | log.error("处理回传失败,orderNo={}, matnr={}, batch={}", orderNo, matnrCode, batch, e); |
| | | log.error("处理回传失败,orderNo={}, matnr={}, batch={}, lineId={}", orderNo, matnrCode, batch, lineId, e); |
| | | errors.add("第" + (i + 1) + "行处理失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | // 循环结束后,每个受影响订单只刷新一次状态 |
| | | for (String no : affectedOrderNos) { |
| | | refreshOrderFinishStatus(no); |
| | | } |
| | | |
| | | // 组装ReportParams上报ERP(一次性整单上报) |
| | | try { |
| | | ReportParams erpReportParams = buildReportParamsFromOrderData(orderNo, poCode, orderType, wkType, editUser, editDate, mappedDataArray); |
| | | CommonResponse erpResp = wmsErpService.reportOrders(erpReportParams); |
| | | if (Objects.nonNull(erpResp) && Objects.equals(erpResp.getCode(), 200)) { |
| | | log.info("上报ERP成功,orderNo={}", orderNo); |
| | | } else { |
| | | String msg = Objects.isNull(erpResp) ? "ERP响应为空" : erpResp.getMsg(); |
| | | errors.add("上报ERP失败:" + msg); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("上报ERP异常", e); |
| | | errors.add("上报ERP异常:" + e.getMessage()); |
| | | } |
| | | |
| | | if (!errors.isEmpty()) { |
| | |
| | | |
| | | Map<String, Object> result = new HashMap<>(); |
| | | result.put("allocatedCount", allocateCount); |
| | | result.put("erpReportCount", erpReportCount); |
| | | return CommonResponse.ok(result); |
| | | } |
| | | |
| | | /** |
| | | * 根据Order格式数据组装ReportParams,参照旧链路wmsErpService.reportOrders格式上报ERP |
| | | */ |
| | | private ReportParams buildReportParamsFromOrderData(String orderNo, String poCode, String type, String wkType, |
| | | String editUser, Date editDate, JSONArray dataArray) { |
| | | // 根据wkType/type确定ERP上报的OrderType |
| | | String erpOrderType = resolveErpOrderType(type, wkType); |
| | | |
| | | List<ReportDataParam> reportDataList = new ArrayList<>(); |
| | | for (int i = 0; i < dataArray.size(); i++) { |
| | | JSONObject row = dataArray.getJSONObject(i); |
| | | String matnrCode = pickString(row, "matNr", "MatNr", "ItemCode", "itemCode"); |
| | | String batch = pickString(row, "batch", "Batch", "GoodsNO", "goodsNo"); |
| | | BigDecimal doneQty = pickDecimal(row, "doneQty", "anfme", "qty", "InQty", "inQty", "OutQty", "outQty"); |
| | | |
| | | ReportDataParam dataParam = new ReportDataParam() |
| | | .setWMSNO(orderNo) |
| | | .setPONO(poCode) |
| | | .setOrderNO(orderNo) |
| | | .setGoodsNO(batch) |
| | | .setItemCode(matnrCode) |
| | | .setEditUser(editUser) |
| | | .setEditDate(editDate) |
| | | .setMemoDtl(pickString(row, "memo", "MemoDtl")); |
| | | |
| | | // 入库类型设InQty,出库类型设OutQty |
| | | if ("in".equalsIgnoreCase(type)) { |
| | | if (doneQty.compareTo(ZERO) > 0) { |
| | | dataParam.setInQty(doneQty.doubleValue()); |
| | | } |
| | | } else { |
| | | if (doneQty.compareTo(ZERO) > 0) { |
| | | dataParam.setOutQty(doneQty.doubleValue()); |
| | | } |
| | | } |
| | | |
| | | reportDataList.add(dataParam); |
| | | } |
| | | |
| | | return new ReportParams() |
| | | .setOrderType(erpOrderType) |
| | | .setAction("Update") |
| | | .setData(reportDataList); |
| | | } |
| | | |
| | | /** |
| | | * 根据type和wkType解析ERP上报的订单类型 |
| | | */ |
| | | private String resolveErpOrderType(String type, String wkType) { |
| | | if (StringUtils.isBlank(wkType)) { |
| | | return "in".equalsIgnoreCase(type) ? "PO_Instock" : "WO_Outstock"; |
| | | } |
| | | switch (wkType) { |
| | | case "PUR_ReceiveBill": return "PO_Instock"; |
| | | case "PUR_MRB": return "PR_Outstock"; |
| | | case "PRD_ReturnMtrl": return "WR_Instock"; |
| | | case "PRD_PickMtrl": return "WO_Outstock"; |
| | | case "PRD_FeedMtrl": return "WR_Instock_BL"; |
| | | case "PRD_INSTOCK": |
| | | case "PRD_MORPT": return "WO_Outstock"; |
| | | case "STK_MISCELLANEOUS": return "In_Instock"; |
| | | case "STK_MisDelivery": return "Io_Outstock"; |
| | | default: |
| | | return "in".equalsIgnoreCase(type) ? "PO_Instock" : "WO_Outstock"; |
| | | } |
| | | } |
| | | |
| | | private OpenApiOrder saveOpenApiOrder(Order order) { |
| | |
| | | return event; |
| | | } |
| | | |
| | | private int allocateMergedDoneQty(String orderNo, String matnrCode, String batch, BigDecimal mergedDoneQty) { |
| | | OpenApiOrder order = openApiOrderMapper.selectOne(new LambdaQueryWrapper<OpenApiOrder>() |
| | | .eq(OpenApiOrder::getCode, orderNo) |
| | | .last("limit 1")); |
| | | private int allocateMergedDoneQty(String orderNo, String matnrCode, String batch, String lineId, BigDecimal mergedDoneQty, Map<String, OpenApiOrder> orderCache) { |
| | | OpenApiOrder order = orderCache.computeIfAbsent(orderNo, k -> |
| | | openApiOrderMapper.selectOne(new LambdaQueryWrapper<OpenApiOrder>() |
| | | .eq(OpenApiOrder::getCode, k) |
| | | .last("limit 1"))); |
| | | if (Objects.isNull(order)) { |
| | | throw new CoolException("未找到订单:" + orderNo); |
| | | } |
| | | |
| | | LambdaQueryWrapper<OpenApiOrderItemMap> queryWrapper = new LambdaQueryWrapper<OpenApiOrderItemMap>() |
| | | .eq(OpenApiOrderItemMap::getOrderId, order.getId()) |
| | | .eq(OpenApiOrderItemMap::getMergeMatnrCode, defaultString(matnrCode)) |
| | | .orderByAsc(OpenApiOrderItemMap::getSeqNo) |
| | | .orderByAsc(OpenApiOrderItemMap::getId); |
| | | if (StringUtils.isNotBlank(batch)) { |
| | | queryWrapper.eq(OpenApiOrderItemMap::getMergeBatch, batch); |
| | | |
| | | // 优先用lineId精确匹配原始行 |
| | | if (StringUtils.isNotBlank(lineId)) { |
| | | queryWrapper.eq(OpenApiOrderItemMap::getSourceLineId, lineId); |
| | | } else { |
| | | queryWrapper.eq(OpenApiOrderItemMap::getMergeMatnrCode, defaultString(matnrCode)); |
| | | if (StringUtils.isNotBlank(batch)) { |
| | | queryWrapper.eq(OpenApiOrderItemMap::getMergeBatch, batch); |
| | | } |
| | | } |
| | | |
| | | List<OpenApiOrderItemMap> mappingRows = openApiOrderItemMapMapper.selectList(queryWrapper); |
| | | if (mappingRows.isEmpty() && StringUtils.isNotBlank(lineId)) { |
| | | // lineId匹配不到时,回退到matNr+batch匹配 |
| | | queryWrapper = new LambdaQueryWrapper<OpenApiOrderItemMap>() |
| | | .eq(OpenApiOrderItemMap::getOrderId, order.getId()) |
| | | .eq(OpenApiOrderItemMap::getMergeMatnrCode, defaultString(matnrCode)) |
| | | .orderByAsc(OpenApiOrderItemMap::getSeqNo) |
| | | .orderByAsc(OpenApiOrderItemMap::getId); |
| | | if (StringUtils.isNotBlank(batch)) { |
| | | queryWrapper.eq(OpenApiOrderItemMap::getMergeBatch, batch); |
| | | } |
| | | mappingRows = openApiOrderItemMapMapper.selectList(queryWrapper); |
| | | } |
| | | if (mappingRows.isEmpty()) { |
| | | throw new CoolException("未找到映射关系,订单:" + orderNo + ",物料:" + matnrCode); |
| | | } |
| | |
| | | openApiOrderItemMapMapper.updateById(row); |
| | | openApiOrderItemMapper.update(null, new LambdaUpdateWrapper<OpenApiOrderItem>() |
| | | .eq(OpenApiOrderItem::getId, row.getSourceItemId()) |
| | | .setSql("qty = IFNULL(qty,0) + " + toAllocate)); |
| | | .setSql("qty = IFNULL(qty,0) + " + toAllocate.toPlainString())); |
| | | remaining = remaining.subtract(toAllocate).setScale(2, RoundingMode.HALF_UP); |
| | | allocateRows++; |
| | | } |
| | |
| | | .eq(OpenApiOrderItemMap::getOrderId, order.getId()) |
| | | .orderByAsc(OpenApiOrderItemMap::getSeqNo) |
| | | .orderByAsc(OpenApiOrderItemMap::getId)); |
| | | int successCount = 0; |
| | | |
| | | // 批量查询所有 sourceItemId 对应的 OpenApiOrderItem,消除 N+1 |
| | | List<Long> itemIds = mappings.stream() |
| | | .map(OpenApiOrderItemMap::getSourceItemId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .toList(); |
| | | Map<Long, OpenApiOrderItem> itemMap = new HashMap<>(); |
| | | if (!itemIds.isEmpty()) { |
| | | List<OpenApiOrderItem> items = openApiOrderItemMapper.selectBatchIds(itemIds); |
| | | for (OpenApiOrderItem item : items) { |
| | | itemMap.put(item.getId(), item); |
| | | } |
| | | } |
| | | |
| | | int successCount = 0; |
| | | for (OpenApiOrderItemMap mapping : mappings) { |
| | | BigDecimal allocatedQty = defaultQty(mapping.getAllocatedQty()); |
| | | BigDecimal reportedQty = defaultQty(mapping.getReportedQty()); |
| | |
| | | continue; |
| | | } |
| | | |
| | | OpenApiOrderItem item = openApiOrderItemMapper.selectById(mapping.getSourceItemId()); |
| | | OpenApiOrderItem item = itemMap.get(mapping.getSourceItemId()); |
| | | if (Objects.isNull(item)) { |
| | | continue; |
| | | } |