From b0877a3275ed5bc96fb80f84949904e149946cf2 Mon Sep 17 00:00:00 2001
From: zwl <1051256694@qq.com>
Date: 星期三, 29 四月 2026 19:37:51 +0800
Subject: [PATCH] 将erp下发直接生成任务改成先生成订单再出库
---
src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java | 1632 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 1,582 insertions(+), 50 deletions(-)
diff --git a/src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java b/src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
index 9aa7192..4ccb39e 100644
--- a/src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
+++ b/src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
@@ -14,11 +14,14 @@
import com.zy.asrs.mapper.TagMapper;
import com.zy.asrs.service.*;
import com.zy.asrs.task.core.ReturnT;
+import com.zy.asrs.task.support.OutboundBatchSeqReleaseGuard;
+import com.zy.asrs.task.support.WorkPublishLockKeys;
import com.zy.asrs.utils.MatUtils;
import com.zy.asrs.utils.OrderInAndOutUtil;
import com.zy.asrs.utils.Utils;
import com.zy.common.constant.AgvConstant;
import com.zy.common.constant.ArmConstant;
+import com.zy.common.entity.Parameter;
import com.zy.common.model.DetlDto;
import com.zy.common.model.LocDetlDto;
import com.zy.common.model.LocDto;
@@ -27,13 +30,15 @@
import com.zy.common.service.CommonService;
import com.zy.common.utils.HttpHandler;
import com.zy.common.utils.NodeUtils;
+import com.zy.common.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import javax.rmi.CORBA.Util;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
@@ -44,10 +49,29 @@
@Service
public class OpenServiceImpl implements OpenService {
+ private static final Map<Integer, BigDecimal> INBOUND_WEIGHT_FACTOR_BY_SOURCE_STA;
+ private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
+ private static final int DEFAULT_OUT_ORDER_BATCH_PRIORITY = 100;
+ private static final int DEFAULT_OUT_ORDER_BATCH_PRIORITY_THRESHOLD = 1;
+ // ERP 鍑哄簱鍙eぇ浜庤闃堝�兼椂锛�/outOrder 鍙惤鍑哄簱璁㈠崟锛岀敱瀹氭椂鍣ㄥ悗缁敓鎴愪换鍔°��
+ private static final int PENDING_OUT_ORDER_STATION_THRESHOLD = 600;
+ // 寤惰繜鍑哄簱璁㈠崟浣跨敤鐙珛鍗曟嵁绫诲瀷锛屼究浜庡拰浜哄伐/椤甸潰鍒涘缓鐨勫嚭搴撳崟鍖哄垎鏉ユ簮銆�
+ private static final String OUT_ORDER_PENDING_DOC_TYPE = "鎺ュ彛鍑哄簱鍗�";
+
+ static {
+ Map<Integer, BigDecimal> factorMap = new HashMap<>();
+ factorMap.put(112, new BigDecimal("0.98"));
+ INBOUND_WEIGHT_FACTOR_BY_SOURCE_STA = Collections.unmodifiableMap(factorMap);
+ }
+
@Autowired
private OrderService orderService;
@Autowired
private OrderDetlService orderDetlService;
+ @Autowired
+ private OrderPakoutService orderPakoutService;
+ @Autowired
+ private OrderDetlPakoutService orderDetlPakoutService;
@Autowired
private SnowflakeIdWorker snowflakeIdWorker;
@Autowired
@@ -88,16 +112,36 @@
private String mesUrl;
@Value("${mes.stationaddress}")
private String stationAddress;
+ @Value("${erp.address.URL:}")
+ private String erpUrl;
+ @Value("${erp.switch.ErpReportOld}")
+ private boolean erpReportOld;
+ @Value("${erp.address.Inaddress:}")
+ private String erpInAddress;
+ @Value("${erp.address.OutErroraddress:}")
+ private String erpOutErrorAddress;
@Autowired
private WaitPakinService waitPakinService;
@Autowired
+ private WaitPakinLogService waitPakinLogService;
+ @Autowired
private WrkMastService wrkMastService;
+ @Autowired
+ private WrkMastLogService wrkMastLogService;
+ @Autowired
+ private WrkDetlLogService wrkDetlLogService;
@Autowired
private WcsApiService wcsApiService;
@Autowired
private WorkService workService;
@Autowired
+ private RedisUtil redisUtil;
+ @Autowired
private BasCrnpService basCrnpService;
+ @Autowired
+ private ApiLogService apiLogService;
+ @Autowired
+ private OutboundBatchSeqReleaseGuard outboundBatchSeqReleaseGuard;
@Override
@Transactional
@@ -378,67 +422,230 @@
throw new CoolException("orderNo涓嶈兘涓虹┖");
}
if (param.getExecute() == null) {
- throw new CoolException("reason涓嶈兘涓虹┖");
+ throw new CoolException("execute涓嶈兘涓虹┖");
+ }
+ // 涓鏃跺厛閬垮紑姝e湪涓嬪彂缁� WCS 鐨勭獥鍙o紝闃叉鏈湴鎶婅鍗曠疆鍋滃悗锛�
+ // WCS 渚т粛鏀跺埌鍚屼竴鎵逛换鍔★紝閫犳垚璁㈠崟鐘舵�佸拰璁惧鎵ц鐘舵�佸垎鍙夈��
+ if (Objects.equals(param.getExecute(), 2)
+ && redisUtil.hasKey(WorkPublishLockKeys.outboundUserNoLock(param.getOrderId()))) {
+ return R.error("姝e湪涓嬪彂浠诲姟缁橶CS锛屾棤娉曚腑姝�");
}
- List<WrkMast> activeTasks = findActiveOutboundTasks(param.getOrderId());
if (Objects.equals(param.getExecute(), 1)) {
- // ERP纭绔嬪嵆鎵ц锛屼粎澶勭悊寰呬笅鍙戠殑鍑哄簱浠诲姟銆�
- List<WrkMast> pendingTasks = activeTasks.stream()
- .filter(wrkMast -> wrkMast != null && Objects.equals(wrkMast.getWrkSts(), 11L))
- .collect(Collectors.toList());
- Map<String, Object> result = new HashMap<>();
- result.put("orderNo", param.getOrderId());
- result.put("taskCount", pendingTasks.size());
- if (pendingTasks.isEmpty()) {
- result.put("confirmedCount", 0);
- return R.ok("鏃犳湁鏁堝嚭搴撲换鍔�").add(result);
- }
-
- Date now = new Date();
- int confirmedCount = 0;
- for (WrkMast wrkMast : pendingTasks) {
- if (wrkMast == null || "Y".equalsIgnoreCase(wrkMast.getPdcType())) {
- continue;
- }
- wrkMast.setPdcType("Y");
-// wrkMast.setUpdMk("ERP_CONFIRMED");
-// wrkMast.setManuType("ERP_CONFIRM_OUT");
- wrkMast.setModiTime(now);
- wrkMast.setModiUser(9527L);
- if (!wrkMastService.updateById(wrkMast)) {
- throw new CoolException("纭鎵ц鍑哄簱浠诲姟澶辫触: " + wrkMast.getWrkNo());
- }
- confirmedCount++;
- }
-
- result.put("confirmedCount", confirmedCount);
- return R.ok(confirmedCount == 0 ? "浠诲姟宸茬‘璁ゆ墽琛�" : "ERP纭鎵ц鍑哄簱鎴愬姛").add(result);
+ // execute=1 鐨勫惈涔夊凡缁忎粠鈥滀粎纭宸茬敓鎴愪换鍔♀�濇墿灞曚负鈥滃惎鍔�/鎭㈠璁㈠崟鈥濓細
+ // 1. 璁㈠崟 status=0 鏃舵仮澶嶄负 1锛�
+ // 2. 宸叉湁 wrk_sts=11 鐨勪换鍔¤缃� pdcType=Y锛�
+ // 3. 鏈敓鎴愮殑璁㈠崟鏄庣粏绔嬪嵆灏濊瘯鐢熸垚褰撳墠鍙斁琛屾壒娆°��
+ // 鍥犳杩欓噷鐩存帴澶嶇敤鍏紑鎵ц鎺ュ彛鐨勭粺涓�瀹炵幇锛岄伩鍏� pause 鍜� execute 涓ゅ閫昏緫鍒嗗弶銆�
+ return executePakoutOrder(param.getOrderId());
}
+
+ OrderPakout orderPakout = orderPakoutService.selectByNo(param.getOrderId());
+ List<WrkMast> activeTasks = findActiveOutboundTasks(param.getOrderId());
if (Objects.equals(param.getExecute(), 2)) {
- // ERP璇锋眰鍙栨秷浠诲姟锛氱洿鎺ユ敹闆嗕换鍔″彿锛屾寜 taskList 鏍煎紡鍙戦�佺粰 WCS銆�
+ // execute=2 鍏堝叧闂鍗曞紑鍏筹紝闃绘瀹氭椂鍣ㄧ户缁负鏈敓鎴愪换鍔$殑鏄庣粏寤� WrkMast銆�
+ // 宸茬粡鐢熸垚鐨勪换鍔℃寜鐘舵�佸垎娴侊細11 灏氭湭涓嬪彂锛岃蛋鏈湴鍙栨秷锛�12/13 宸蹭笅鍙戞垨鎵ц涓紝闇�瑕侀�氱煡 WCS 鍙栨秷銆�
Map<String, Object> result = new HashMap<>();
result.put("orderNo", param.getOrderId());
result.put("execute", param.getExecute());
result.put("taskCount", activeTasks.size());
+ boolean orderStatusUpdated = false;
+ if (orderPakout != null && !Objects.equals(orderPakout.getStatus(), 0)) {
+ // status=0 鏄鍗曠骇涓寮�鍏炽�傚厛鍏宠鍗曪紝鍐嶅彇娑堜换鍔★紝
+ // 鍙互闃绘瀹氭椂鍣ㄥ湪鍙栨秷杩囩▼涓張鐢熸垚涓嬩竴鎵规湭涓嬪彂浠诲姟銆�
+ orderPakout.setStatus(0);
+ orderPakout.setUpdateBy(9527L);
+ orderPakout.setUpdateTime(new Date());
+ if (!orderPakoutService.updateById(orderPakout)) {
+ throw new CoolException("涓鍑哄簱璁㈠崟澶辫触锛�" + param.getOrderId());
+ }
+ orderStatusUpdated = true;
+ }
+ result.put("orderStatusUpdated", orderStatusUpdated);
if (activeTasks.isEmpty()) {
- return R.ok("鏃犳湁鏁堝嚭搴撲换鍔�").add(result);
+ result.put("cancelledLocalTaskCount", 0);
+ result.put("pausedWcsTaskCount", 0);
+ return R.ok("鍑哄簱璁㈠崟宸蹭腑姝�").add(result);
}
List<HashMap<String,Object>> taskList = new ArrayList<>();
+ List<WrkMast> wcsCancelTasks = new ArrayList<>();
+ int cancelledLocalTaskCount = 0;
for (WrkMast wrkMast : activeTasks) {
HashMap<String,Object> hashMap = new HashMap<>();
hashMap.put("taskNo", wrkMast.getWrkNo());
+ if (!Cools.isEmpty(wrkMast) && Objects.equals(wrkMast.getWrkSts(), 11L)) {
+ // wrk_sts=11 杩樻病鍙戝埌 WCS锛屾湰鍦板彇娑堝嵆鍙紝涓嶉渶瑕佷骇鐢� WCS 鏆傚仠鎸囦护銆�
+ // cancelWrkMast 浼氭妸宸ヤ綔妗h浆鍘嗗彶銆侀噴鏀惧簱浣嶏紝骞跺洖婊氳鍗曟槑缁� work_qty銆�
+ workService.cancelWrkMast(wrkMast.getWrkNo()+"", 9955L);
+ cancelledLocalTaskCount++;
+ continue;
+ }
taskList.add(hashMap);
+ wcsCancelTasks.add(wrkMast);
}
- wcsApiService.pauseOutTasks(taskList);
- return R.ok("鍙栨秷浠诲姟宸插彂閫佽嚦WCS").add(result);
+ int cancelledWcsTaskCount = 0;
+ if (!taskList.isEmpty()) {
+ // wrk_sts=12/13 绛夊凡杩涘叆 WCS 渚х殑浠诲姟蹇呴』鏍¢獙 WCS 杩斿洖锛屽け璐ュ垯浜嬪姟鍥炴粴锛岄伩鍏嶆湰鍦拌鎶ュ凡鍙栨秷銆�
+ R wcsR = wcsApiService.pauseOutTasks(taskList);
+ requireWcsPauseOk(wcsR);
+ // WCS 宸茬‘璁ゅ彇娑堝悗锛屾湰鍦颁篃瑕佸彇娑堜换鍔″苟鍥炴粴璁㈠崟鏄庣粏 work_qty銆�
+ // 鍥炴粴閫昏緫鍦� WorkService.cancelWrkMast 涓粺涓�澶勭悊锛岀‘淇� WCS 鍥炶皟 task_cancel 璧板悓涓�濂楀彛寰勩��
+ for (WrkMast wrkMast : wcsCancelTasks) {
+ workService.cancelWrkMast(wrkMast.getWrkNo()+"", 9955L);
+ cancelledWcsTaskCount++;
+ }
+ }
+ result.put("cancelledLocalTaskCount", cancelledLocalTaskCount);
+ result.put("pausedWcsTaskCount", taskList.size());
+ result.put("cancelledWcsTaskCount", cancelledWcsTaskCount);
+ return R.ok("鍑哄簱璁㈠崟宸蹭腑姝�").add(result);
}
throw new CoolException("reason浠呮敮鎸�1鎴�2");
}
+ /**
+ * 灏嗗凡鐢熸垚浣嗚繕鏈厑璁镐笅鍙戠殑鍑哄簱浠诲姟缃负鍙笅鍙戙��
+ *
+ * pdcType 鏄幇鏈� WorkMastScheduler 鐨勪笅鍙戝紑鍏筹細
+ * - wrk_sts=11 涓� pdcType=Y 鏃讹紝璋冨害鍣ㄥ彲浠ョ户缁笅鍙戠粰 WCS銆�
+ * - 宸茬粡鏄� Y 鐨勪换鍔′繚鎸佸箓绛夛紝涓嶉噸澶嶆洿鏂般��
+ *
+ * 涓轰粈涔堜笉鍦� outOrderBatch 閲岀洿鎺ュ己鍒� Y锛�
+ * - outOrderBatch 浠嶈鍏朵粬鍏ュ彛澶嶇敤锛�
+ * - MQTT/IoT autoConfirm=false 鐨勮鍗曢渶瑕佸厛寤哄崟浣嗕笉涓嬪彂锛�
+ * - 鎵�浠ユ斁琛屽繀椤荤敱鈥滆鍗� status=1 鐨勫畾鏃剁敓鎴愨�濇垨鈥滄墽琛屾帴鍙b�濇樉寮忓畬鎴愩��
+ */
+ private int confirmPendingOutboundTasks(List<WrkMast> activeTasks) {
+ if (Cools.isEmpty(activeTasks)) {
+ return 0;
+ }
+ Date now = new Date();
+ int confirmedCount = 0;
+ for (WrkMast wrkMast : activeTasks) {
+ if (wrkMast == null || !Objects.equals(wrkMast.getWrkSts(), 11L) || "Y".equalsIgnoreCase(wrkMast.getPdcType())) {
+ continue;
+ }
+ wrkMast.setPdcType("Y");
+ wrkMast.setModiTime(now);
+ wrkMast.setModiUser(9527L);
+ if (!wrkMastService.updateById(wrkMast)) {
+ throw new CoolException("纭鎵ц鍑哄簱浠诲姟澶辫触: " + wrkMast.getWrkNo());
+ }
+ confirmedCount++;
+ }
+ return confirmedCount;
+ }
+
+ /** WCS 杩斿洖闈炴垚鍔熺爜鏃舵姏閿� */
+ private void requireWcsPauseOk(R wcsR) {
+ if (wcsR == null) {
+ throw new CoolException("WCS鍙栨秷鍑哄簱浠诲姟鏃犺繑鍥�");
+ }
+ Object codeObj = wcsR.get("code");
+ int code = codeObj instanceof Number ? ((Number) codeObj).intValue() : -1;
+ if (code != 200) {
+ Object msgObj = wcsR.get("msg");
+ throw new CoolException(msgObj == null ? "WCS鍙栨秷鍑哄簱浠诲姟澶辫触" : String.valueOf(msgObj));
+ }
+ }
+
@Override
+ @Transactional
public R pakoutOrderExecute(OpenOrderPakoutExecuteParam param) {
- return null;
+ if (param == null || Cools.isEmpty(param.getOrderId())) {
+ throw new CoolException("orderNo涓嶈兘涓虹┖");
+ }
+ if (param.getExecute() == null) {
+ throw new CoolException("execute涓嶈兘涓虹┖");
+ }
+ if (!Objects.equals(param.getExecute(), 1)) {
+ throw new CoolException("execute浠呮敮鎸�1");
+ }
+ return executePakoutOrder(param.getOrderId());
+ }
+
+ /**
+ * 鍑哄簱璁㈠崟鎵ц/鎭㈠鐨勭粺涓�鍏ュ彛銆�
+ *
+ * 璇ユ柟娉曞悓鏃舵湇鍔★細
+ * - 鏂板鍏紑鎵ц鎺ュ彛锛�
+ * - pakoutOrderPause(execute=1) 鐨勬仮澶嶉�昏緫锛�
+ * - IoT/MQTT autoConfirm=true 鐨勫嵆鏃舵墽琛岄�昏緫銆�
+ *
+ * 鎵ц璇箟鍒嗕笁姝ワ細
+ * 1. 鎶婅鍗� status 鎭㈠涓� 1锛岃瀹氭椂鍣ㄥ拰褰撳墠璋冪敤閮藉彲浠ョ敓鎴愪换鍔★紱
+ * 2. 瀵瑰凡缁忓瓨鍦ㄤ笖 wrk_sts=11 鐨勪换鍔¤缃� pdcType=Y锛�
+ * 3. 绔嬪嵆灏濊瘯鐢熸垚褰撳墠鍏佽鐨勪竴涓壒娆★紝骞跺啀娆$‘璁ゆ柊鐢熸垚浠诲姟鍙笅鍙戙��
+ *
+ * 骞傜瓑鎬э細
+ * - 璁㈠崟宸叉槸 status=1 鏃朵笉浼氶噸澶嶆洿鏂帮紱
+ * - 宸茬粡 pdcType=Y 鐨勪换鍔′笉浼氶噸澶嶇‘璁わ紱
+ * - 宸茬粡 work_qty 瑕嗙洊鐨勬槑缁嗕笉浼氬啀娆$敓鎴愪换鍔°��
+ */
+ private R executePakoutOrder(String orderNo) {
+ if (Cools.isEmpty(orderNo)) {
+ throw new CoolException("orderNo涓嶈兘涓虹┖");
+ }
+ OrderPakout orderPakout = orderPakoutService.selectByNo(orderNo);
+ List<WrkMast> activeTasks = findActiveOutboundTasks(orderNo);
+
+ Map<String, Object> result = new LinkedHashMap<>();
+ result.put("orderNo", orderNo);
+ result.put("execute", 1);
+ boolean orderStatusUpdated = false;
+ if (orderPakout != null && !Objects.equals(orderPakout.getStatus(), 1)) {
+ orderPakout.setStatus(1);
+ orderPakout.setUpdateBy(9527L);
+ orderPakout.setUpdateTime(new Date());
+ if (!orderPakoutService.updateById(orderPakout)) {
+ throw new CoolException("鍚姩鍑哄簱璁㈠崟澶辫触锛�" + orderNo);
+ }
+ orderStatusUpdated = true;
+ }
+
+ int confirmedCount = confirmPendingOutboundTasks(activeTasks);
+ R generateResult = null;
+ int generatedConfirmedCount = 0;
+ if (orderPakout != null) {
+ // 涓诲姩璋冪敤鎵ц鎺ュ彛鏃讹紝涓嶇瓑涓嬩竴杞畾鏃跺櫒锛岀洿鎺ョ敓鎴愬綋鍓嶅彲鏀捐鐨勪竴涓壒娆°��
+ // 濡傛灉褰撳墠鎵规琚� OutboundBatchSeqReleaseGuard 闃诲锛実enerateResult 浼氳繑鍥為樆濉炲師鍥狅紝
+ // 涓嶄細鎶涢敊锛屼篃涓嶄細鎻愬墠鐢熸垚鍚庣画鎵规銆�
+ generateResult = generatePendingPakoutOrderTasks(orderNo);
+ // 鏂扮敓鎴愮殑浠诲姟鍒濆鐘舵�佷负 11锛岃繖閲屽啀娆$‘璁わ紝璁╂墽琛屾帴鍙h繑鍥炲悗浠诲姟鍗冲彲琚皟搴﹀櫒涓嬪彂銆�
+ generatedConfirmedCount = confirmPendingOutboundTasks(findActiveOutboundTasks(orderNo));
+ }
+
+ List<Integer> wrkNos = collectActiveOutboundWrkNos(orderNo);
+ result.put("orderStatusUpdated", orderStatusUpdated);
+ result.put("confirmedCount", confirmedCount + generatedConfirmedCount);
+ result.put("generatedConfirmedCount", generatedConfirmedCount);
+ result.put("generateResult", generateResult);
+ result.put("wrkNos", wrkNos);
+ if (!wrkNos.isEmpty()) {
+ // IoT 鍗曟墭鐩樿皟鐢ㄤ緷璧栭《灞� wrkNo 鍥炲啓浠诲姟鍙凤紱澶氭墭鐩樻椂杩斿洖褰撳墠璁㈠崟娲诲姩浠诲姟鐨勭涓�鏉°��
+ result.put("wrkNo", wrkNos.get(0));
+ }
+ R response = R.ok("鍑哄簱璁㈠崟鍚姩鎴愬姛").add(result);
+ if (!wrkNos.isEmpty()) {
+ // add(Map) 鍦ㄩ儴鍒嗚皟鐢ㄦ柟浼氭寜 data 璇诲彇锛岃繖閲屽啀鏄惧紡鍐欓《灞傚瓧娈碉紝鍏煎 IoT 鐨� resolveWrkNo銆�
+ response.put("wrkNo", wrkNos.get(0));
+ response.put("wrkNos", wrkNos);
+ }
+ return response;
+ }
+
+ private List<Integer> collectActiveOutboundWrkNos(String orderNo) {
+ List<WrkMast> activeTasks = findActiveOutboundTasks(orderNo);
+ if (Cools.isEmpty(activeTasks)) {
+ return Collections.emptyList();
+ }
+ List<Integer> wrkNos = new ArrayList<>();
+ for (WrkMast wrkMast : activeTasks) {
+ if (wrkMast != null && wrkMast.getWrkNo() != null) {
+ wrkNos.add(wrkMast.getWrkNo());
+ }
+ }
+ return wrkNos;
}
private List<WrkMast> findActiveOutboundTasks(String orderNo) {
@@ -637,6 +844,117 @@
@Transactional
public List<StockVo> queryStock() {
return locDetlService.queryStockTotal();
+ }
+
+ @Override
+ public R reportPakinHistoryToErp(List<String> barcodes) {
+ List<String> normalizedBarcodes = normalizeBarcodes(barcodes);
+ if (normalizedBarcodes.isEmpty()) {
+ return R.error("鎵樼洏鐮侀泦鍚堜笉鑳戒负绌�");
+ }
+ if (!isErpReportEnabled()) {
+ return R.error("ERP reporting is disabled");
+ }
+ if (Cools.isEmpty(erpInAddress)) {
+ return R.error("ERP鍏ュ簱涓婃姤鍦板潃鏈厤缃�");
+ }
+
+ Map<String, WrkMastLog> latestInboundLogMap = loadLatestInboundHistoryLogMap(normalizedBarcodes);
+ Map<Integer, List<WrkDetlLog>> wrkDetlLogMap = loadWrkDetlLogMap(latestInboundLogMap.values());
+ Map<String, WaitPakinLog> waitPakinLogMap = loadWaitPakinLogMap(normalizedBarcodes);
+ String requestUrl = buildErpInboundRequestUrl();
+
+ List<Map<String, Object>> rows = new ArrayList<>();
+ int successCount = 0;
+ int failCount = 0;
+
+ for (String barcode : normalizedBarcodes) {
+ Map<String, Object> row = new LinkedHashMap<>();
+ row.put("barcode", barcode);
+
+ WrkMastLog wrkMastLog = latestInboundLogMap.get(barcode);
+ if (wrkMastLog == null) {
+ row.put("success", false);
+ row.put("message", "鏈壘鍒版渶鏂板叆搴撳巻鍙茶褰�");
+ rows.add(row);
+ failCount++;
+ continue;
+ }
+
+ WaitPakinLog waitPakinLog = waitPakinLogMap.get(barcode);
+ List<WrkDetlLog> wrkDetlLogs = wrkDetlLogMap.getOrDefault(wrkMastLog.getWrkNo(), Collections.emptyList());
+ ErpPakinReportParam param = buildInboundErpParam(barcode, wrkMastLog, wrkDetlLogs, waitPakinLog);
+ if (Cools.isEmpty(param.getPalletId())) {
+ row.put("success", false);
+ row.put("message", "鎵樼洏鐮佺己灏戜笂鎶ュ瓧娈礫palletId]");
+ rows.add(row);
+ failCount++;
+ continue;
+ }
+ if (Cools.isEmpty(param.getLocId())) {
+ row.put("success", false);
+ row.put("message", "鎵樼洏鐮佺己灏戜笂鎶ュ瓧娈礫locId]");
+ rows.add(row);
+ failCount++;
+ continue;
+ }
+
+ String request = JSON.toJSONString(param);
+ String response = "";
+ boolean success = false;
+ String errorMsg = null;
+ try {
+ response = new HttpHandler.Builder()
+ .setUri(erpUrl)
+ .setPath(erpInAddress)
+ .setJson(request)
+ .build()
+ .doPost();
+ success = isErpCallSuccess(response);
+ if (!success) {
+ errorMsg = extractErpCallError(response);
+ }
+ } catch (Exception e) {
+ errorMsg = e.getMessage();
+ } finally {
+ try {
+ apiLogService.save(
+ "Inbound ERP Report",
+ requestUrl,
+ null,
+ "127.0.0.1",
+ request,
+ response,
+ success,
+ "barcode=" + barcode + ",workNo=" + wrkMastLog.getWrkNo()
+ );
+ } catch (Exception logEx) {
+ log.error("save inbound erp api log failed", logEx);
+ }
+ }
+
+ row.put("workNo", wrkMastLog.getWrkNo());
+ Integer sourceStaNo = resolveInboundSourceStaNo(wrkMastLog);
+ row.put("sourceStaNo", sourceStaNo);
+ row.put("weightFactor", resolveInboundWeightFactor(sourceStaNo));
+ row.put("erpWeight", param.getWeight());
+ row.put("success", success);
+ row.put("message", success ? "OK" : errorMsg);
+ rows.add(row);
+
+ if (success) {
+ successCount++;
+ } else {
+ failCount++;
+ }
+ }
+
+ Map<String, Object> result = new LinkedHashMap<>();
+ result.put("total", normalizedBarcodes.size());
+ result.put("successCount", successCount);
+ result.put("failCount", failCount);
+ result.put("rows", rows);
+ return R.ok().add(result);
}
@Override
@@ -1221,7 +1539,10 @@
stationParams.add(stationParam);
}
}
+ String requestJson = JSON.toJSONString(stationParams);
String response = "";
+ boolean pushOk = false;
+ String pushUrl = buildMesStationRequestUrl();
try {
//鑾峰彇Cookie鍊�
HashMap<String, Object> headers = new HashMap<>();
@@ -1231,21 +1552,55 @@
.setHeaders(headers)
.setUri(mesUrl)
.setPath(stationAddress)
- .setJson(JSON.toJSONString(stationParams))
+ .setJson(requestJson)
.build()
.doPost();
JSONObject jsonResponse = JSON.parseObject(response);
- if (jsonResponse.getInteger("code") == 200) {
-
+ if (jsonResponse != null && Integer.valueOf(200).equals(jsonResponse.getInteger("code"))) {
+ pushOk = true;
} else {
return R.error();
}
} catch (Exception e) {
e.printStackTrace();
+ } finally {
+ try {
+ apiLogService.save(
+ "鎺‥RP-绔欑偣鍚屾",
+ pushUrl,
+ null,
+ "127.0.0.1",
+ requestJson,
+ response,
+ pushOk,
+ "绔欑偣鍚屾鎺ㄥ鏂�"
+ );
+ } catch (Exception logEx) {
+ log.error("save station sync api log failed", logEx);
+ }
}
return R.ok();
}
+ private String buildMesStationRequestUrl() {
+ if (Cools.isEmpty(mesUrl)) {
+ return stationAddress;
+ }
+ if (stationAddress == null) {
+ return mesUrl;
+ }
+ if (mesUrl.endsWith("/") && stationAddress.startsWith("/")) {
+ return mesUrl + stationAddress.substring(1);
+ }
+ if (!mesUrl.endsWith("/") && !stationAddress.startsWith("/")) {
+ return mesUrl + "/" + stationAddress;
+ }
+ return mesUrl + stationAddress;
+ }
+
+ /**
+ * 7.3 缁勬墭淇℃伅涓嬪彂銆�
+ */
@Override
public R mesToComb(MesToCombParam param) {
if (Cools.isEmpty(param.getPalletId())) {
@@ -1258,7 +1613,12 @@
if (param.getFull() == 1) {
//婊℃墭鐩�
- mat = matService.selectByMatnr("emptyPallet");
+ if(param.getBoxType1().equals("aws")){
+ mat = matService.selectByMatnr("amazon");
+ }else {
+ mat = matService.selectByMatnr("cloudWarehouse");
+ }
+
} else if (param.getFull() == 0) {
//绌烘墭鐩�
mat = matService.selectByMatnr("emptyPallet");
@@ -1273,31 +1633,70 @@
waitPakin.setIoStatus("N"); // 鍏ュ嚭鐘舵��
waitPakin.setAnfme(param.getAnfme()); // 鏁伴噺
+ waitPakin.setFreqType(param.getFreqType());
+ waitPakin.setContainerNo(param.getContainerNo());
+ waitPakin.setTeu(param.getTeu());
+ waitPakin.setPlateNo(param.getPlateNo());
+ waitPakin.setTrainNo(param.getTrainNo());
+ waitPakin.setCubeNumber(param.getCubeNumber());
waitPakin.setStatus("Y"); // 鐘舵��
waitPakin.setAppeUser(9995L);
waitPakin.setAppeTime(now);
waitPakin.setModiUser(9995L);
waitPakin.setModiTime(now);
- waitPakin.setOrderNo(String.valueOf(param.getOrderId()));
+ waitPakin.setOrderNo(String.valueOf(param.getBizNo()));
waitPakin.setOrigin(String.valueOf(param.getStorageArea()));//寤鸿鍏ュ簱鍖哄煙
waitPakin.setManu(String.valueOf(param.getLocId()));//mes鍏蜂綋搴撲綅缂栧彿
waitPakin.setThreeCode(param.getBizNo());
+ // 7.3 鏂板瀛楁澶嶇敤鐜版湁缁勬墭妗f壙杞戒綅锛屼笉鏂板鍒椼��
+ waitPakin.setSuppCode(param.getCustomerId());
+ waitPakin.setSupp(param.getCustomerName());
+ waitPakin.setStandby3(param.getItem());
+ waitPakin.setStandby1(param.getEntryWmsCode());
+ waitPakin.setStandby2(param.getOutDoorNo());
waitPakin.setBeBatch(param.getPackage1());//鏄惁鏁h揣锛�0 闈炴暎璐э紱1 鏁h揣锛涗负浜嗙鎺у嚭璐ч�熺巼锛屾暎璐у彲浠ュ嚭鎱㈢偣銆�
+ // ERP 鍏ュ彛榛樿鎵� erp锛孧QTT 缁勬墭浼氬湪鍙傛暟閲屾樉寮忎紶 aws銆�
+ waitPakin.setBoxType1(Cools.isEmpty(param.getBoxType1()) ? "erp" : param.getBoxType1());
if (!waitPakinService.insert(waitPakin)) {
throw new CoolException("淇濆瓨鍏ュ簱閫氱煡妗eけ璐�");
}
- return null;
+ return R.ok().add(Cools.add("palletId", param.getPalletId()).add("orderId", param.getOrderId()));
}
+ /**
+ * 7.11 鍑哄簱閫氱煡鍗曪紙浼犻�掓湁搴忔棤搴忚鍒欙級鍗曟潯寤哄崟銆�
+ */
@Override
- public R outOrder(OutTaskParam param) {
+ public R outOrder(OutTaskParam param,int count,int i) {
+ return outOrder(param, count, 0 ,i);
+ }
+
+ private R outOrder(OutTaskParam param, int count, int teu , int i) {
LocMast locMast = locMastService.selectOne(new EntityWrapper<LocMast>().eq("loc_sts", "F").eq("barcode", param.getPalletId()));
if (locMast == null) {
- return R.error("娌℃湁鎵惧埌鎵樼洏鐮�=" + param.getPalletId() + "瀵瑰簲鐨勫簱浣�");
+ throw new CoolException("娌℃湁鎵惧埌鎵樼洏鐮�=" + param.getPalletId() + "瀵瑰簲鐨勫簱浣�");
+ }
+ if (Cools.isEmpty(param.getStationId())) {
+ throw new CoolException("鍑哄簱鍙g紪鐮佷笉鑳戒负绌�");
+ }
+ if (Cools.isEmpty(param.getBatchSeq())) {
+ throw new CoolException("鎵规鏍囪瘑涓嶈兘涓虹┖");
+ }
+ if (param.getSeq() == null || param.getSeq() < 0) {
+ throw new CoolException("鍑哄簱椤哄簭涓嶈兘涓虹┖涓斾笉鑳藉皬浜�0");
}
Integer ioType = 101;
// 鑾峰彇璺緞
- StaDesc staDesc = staDescService.queryCrnStn(ioType, locMast.getCrnNo(), Integer.valueOf(param.getStationId()));
+ Integer stationId;
+ try {
+ stationId = Integer.valueOf(param.getStationId());
+ } catch (NumberFormatException ex) {
+ throw new CoolException("鍑哄簱鍙g紪鐮佹牸寮忛敊璇細" + param.getStationId());
+ }
+ StaDesc staDesc = staDescService.queryCrnStn(ioType, locMast.getCrnNo(), stationId);
+ if (staDesc == null) {
+ throw new CoolException("鏈壘鍒板嚭搴撳彛=" + param.getStationId() + "瀵瑰簲璺緞");
+ }
Date now = new Date();
// 鐢熸垚宸ヤ綔鍙�
int workNo = commonService.getWorkNo(WorkNoType.getWorkNoType(ioType));
@@ -1307,7 +1706,7 @@
wrkMast.setIoTime(now);
wrkMast.setWrkSts(11L); // 宸ヤ綔鐘舵�侊細11.鐢熸垚鍑哄簱ID
wrkMast.setIoType(ioType); // 鍏ュ嚭搴撶姸鎬�
- wrkMast.setIoPri(13D); // 浼樺厛绾э細13
+ wrkMast.setIoPri(Double.valueOf(i)); // 浼樺厛绾�
wrkMast.setCrnNo(locMast.getCrnNo());
wrkMast.setSourceStaNo(staDesc.getCrnStn()); // 婧愮珯
wrkMast.setStaNo(staDesc.getStnNo()); // 鐩爣绔�
@@ -1318,8 +1717,16 @@
wrkMast.setExitMk("N"); // 閫�鍑�
wrkMast.setEmptyMk("N"); // 绌烘澘
wrkMast.setLinkMis("N");
- wrkMast.setPdcType("N");
+ wrkMast.setContainerNo(param.getContainerNo());
+ wrkMast.setTeu(teu);
+ wrkMast.setPdcType(locMast.getCrnNo()>=19?"Y":"N");//鑷姩浠诲姟涓嬪彂鏍囪
+ wrkMast.setPlateNo(param.getPlateNo());
+ wrkMast.setTrainNo(param.getTrainNo());
+ wrkMast.setFreqType(param.getFreqType());
+ wrkMast.setCubeNumber(param.getCubeNumber());
+ // 7.11锛歰rderId 瀛� userNo锛宐atchSeq 瀛樻壒娆℃爣璇嗭紝seq 瀛樻壒娆″唴椤哄簭銆�
wrkMast.setUserNo(param.getOrderId());//璁㈠崟鍙�
+ wrkMast.setBatchSeq(param.getBatchSeq());//璁㈠崟鍐呮壒娆℃爣璇�
wrkMast.setPltType(param.getSeq());//鍑哄簱椤哄簭锛屼粠1寮�濮�
wrkMast.setTakeNone("0"); //0闈炶嚜鍔�
wrkMast.setAppeUser(9995L); // 鎿嶄綔浜哄憳鏁版嵁
@@ -1346,6 +1753,17 @@
wrkDetl.setAppeUser(9995L);
wrkDetl.setModiTime(now);
wrkDetl.setModiUser(9995L);
+ wrkDetl.setTeu(teu);
+ wrkDetl.setContainerNo(param.getContainerNo());
+ wrkDetl.setPlateNo(param.getPlateNo());
+ wrkDetl.setTrainNo(param.getTrainNo());
+ wrkDetl.setFreqType(param.getFreqType());
+ wrkDetl.setCubeNumber(param.getCubeNumber());
+ // 7.11锛歟ntryWmsCode銆乷utDoorNo 澶嶇敤鏄庣粏澶囩敤瀛楁銆�
+ wrkDetl.setStandby1(param.getEntryWmsCode());
+ wrkDetl.setStandby2(param.getOutDoorNo());
+ wrkDetl.setSupp(count+"");
+ wrkDetl.setTeu(param.getTeu());
if (!wrkDetlService.insert(wrkDetl)) {
throw new CoolException("淇濆瓨宸ヤ綔妗f槑缁嗗け璐�");
@@ -1364,7 +1782,1121 @@
log.error(locMast.getLocNo() + "搴撲綅涓嶆槸鍦ㄥ簱鐘舵��");
throw new CoolException(locMast.getLocNo() + "搴撲綅涓嶆槸鍦ㄥ簱鐘舵��");
}
+ return R.ok().add(Cools.add("wrkNo", workNo).add("orderId", param.getOrderId()));
+ }
+
+ /**
+ * 7.11 鍑哄簱閫氱煡鍗曪紙浼犻�掓湁搴忔棤搴忚鍒欙級鎵归噺寤哄崟銆�
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public R outOrderBatch(Map<String, List<OutTaskParam>> linesByBatchSeq,int count) {
+
+ for (Map.Entry<String, List<OutTaskParam>> entry : linesByBatchSeq.entrySet()) {
+ int i = DEFAULT_OUT_ORDER_BATCH_PRIORITY;
+ int j = 0;
+ int priorityThreshold = getOutOrderBatchPriorityThreshold();
+ for (OutTaskParam outTaskParam : entry.getValue()) {
+ int teu = Cools.isEmpty(outTaskParam.getTeu())?0:outTaskParam.getTeu();
+ R r = outOrder(outTaskParam, count, teu ,i);
+ if (!Objects.equals(r.get("code"), 200)) {
+ throw new CoolException("鍑哄簱寤哄崟澶辫触");
+ }
+ j++;
+ if (j >= priorityThreshold) {
+ i--;
+ j = 0;
+ }
+ }
+
+ }
return R.ok();
}
+
+ private int getOutOrderBatchPriorityThreshold() {
+ Parameter parameter = Parameter.get();
+ if (parameter == null || Cools.isEmpty(parameter.getOutOrderBatchPriorityThreshold())) {
+ return DEFAULT_OUT_ORDER_BATCH_PRIORITY_THRESHOLD;
+ }
+ try {
+ int threshold = Integer.parseInt(parameter.getOutOrderBatchPriorityThreshold().trim());
+ return threshold <= 0 ? DEFAULT_OUT_ORDER_BATCH_PRIORITY_THRESHOLD : threshold;
+ } catch (NumberFormatException ignored) {
+ return DEFAULT_OUT_ORDER_BATCH_PRIORITY_THRESHOLD;
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public R outOrderCreatePakoutOrder(List<OutTaskParam> params) {
+ return outOrderCreatePakoutOrder(params, true);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public R outOrderCreatePakoutOrder(List<OutTaskParam> params, boolean executable) {
+ if (Cools.isEmpty(params)) {
+ return R.ok();
+ }
+
+ // 涓�涓� /outOrder 璇锋眰鍙兘鍖呭惈澶氫釜 orderId銆傛寜璁㈠崟鍙峰垎缁勫悗鍒嗗埆鍒涘缓璁㈠崟澶达紝
+ // 淇濊瘉 orderId 濮嬬粓瀵瑰簲 man_order_pakout.order_no锛屾槑缁嗘寕鍦ㄥ搴旇鍗曚笅銆�
+ //
+ // 杩欓噷涓嶆寜鎵规鍒嗙粍钀借鍗曞ご锛氬悓涓�涓� ERP 鍗曞彿涓嬪彲鑳藉悓鏃跺寘鍚涓� entryWmsCode锛�
+ // 瀹冧滑搴旇鏄悓涓�寮犲嚭搴撹鍗曠殑澶氫釜鎵规锛岃�屼笉鏄寮犺鍗曘��
+ Map<String, List<OutTaskParam>> paramsByOrderNo = new LinkedHashMap<>();
+ for (OutTaskParam param : params) {
+ validatePendingOutOrderParam(param);
+ paramsByOrderNo.computeIfAbsent(param.getOrderId(), key -> new ArrayList<>()).add(param);
+ }
+
+ Date now = new Date();
+ int orderCount = 0;
+ int detailCount = 0;
+ int removedUndispatchedDetailCount = 0;
+ List<String> orderNos = new ArrayList<>();
+ for (Map.Entry<String, List<OutTaskParam>> entry : paramsByOrderNo.entrySet()) {
+ String orderNo = entry.getKey();
+ // 寤惰繜寤哄崟鍏佽鍚屼竴涓� orderNo 鍐嶆涓嬪彂锛�
+ // 宸蹭笅鍙�/宸插畬鎴愭槑缁嗕繚鐣欒拷婧紱鏈笅鍙戞槑缁嗗垹闄ゅ悗锛屼娇鐢ㄦ湰娆℃帴鍙e弬鏁伴噸鏂版彃鍏ャ��
+ // 濡傛灉褰撳墠璁㈠崟杩樻湁娲诲姩 WrkMast锛屽垯涓嶅厑璁歌鐩栵紝閬垮厤鍚屼竴鎵樼洏鍚屾椂瀛樺湪涓ゆ潯鎵ц閾俱��
+ assertPendingPakoutOrderCanReplace(orderNo);
+
+ OrderPakout order = orderPakoutService.selectByNo(orderNo);
+ if (order == null) {
+ order = createPendingPakoutOrderHeader(orderNo, now, executable);
+ } else {
+ assertNoNonReplaceablePendingDetailConflict(order, entry.getValue());
+ removedUndispatchedDetailCount += removeUndispatchedPendingDetails(order.getId());
+ refreshPendingPakoutOrderForResubmit(order, now, executable);
+ }
+
+ for (OutTaskParam param : entry.getValue()) {
+ // 鏄庣粏瀹屾暣淇濆瓨鎺ュ彛瀛楁锛屽悗缁畾鏃跺櫒鍙互鏃犳崯杩樺師 OutTaskParam 鍐嶅鐢� outOrderBatch銆�
+ // 璁㈠崟鍖栫殑鏍稿績鏄�滃弬鏁板厛鎸佷箙鍖栥�佷换鍔″悗鐢熸垚鈥濓細鍗充娇鏈嶅姟閲嶅惎锛屽畾鏃跺櫒浠嶈兘缁х画澶勭悊鏈敓鎴愭槑缁嗐��
+ OrderDetlPakout detail = buildPendingPakoutOrderDetl(order, param, now);
+ if (!orderDetlPakoutService.insert(detail)) {
+ throw new CoolException("鐢熸垚鍑哄簱璁㈠崟鏄庣粏澶辫触锛�" + orderNo);
+ }
+ detailCount++;
+ }
+ orderCount++;
+ orderNos.add(orderNo);
+ }
+
+ Map<String, Object> result = new LinkedHashMap<>();
+ result.put("orderCount", orderCount);
+ result.put("detailCount", detailCount);
+ result.put("removedUndispatchedDetailCount", removedUndispatchedDetailCount);
+ result.put("orderNos", orderNos);
+ result.put("executable", executable);
+ return R.ok("鍑哄簱璁㈠崟鐢熸垚鎴愬姛").add(result);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public R generatePendingPakoutOrderTasks() {
+ // 瀹氭椂鍏ュ彛鍙壂鎻忓惎鐢ㄤ腑鐨勫嚭搴撹鍗曪細
+ // status=1 琛ㄧず鏈 pakoutOrderPause 涓锛�
+ // settle in (1,2) 琛ㄧず鏈畬鎴愬叏閮ㄤ换鍔$敓鎴愶紱
+ // pakin_pakout_status=2 闄愬畾鍑哄簱鍗曪紝閬垮厤璇壂鍏ュ簱/鍏朵粬鏂瑰悜鍗曟嵁銆�
+ //
+ // status=0 鐨勮鍗曚笉浼氳杩欓噷鎵弿锛屽吀鍨嬫潵婧愭槸锛�
+ // - IoT/MQTT autoConfirm=false 棰勫垱寤鸿鍗曪紱
+ // - pakoutOrderPause execute=2 涓鍚庣殑璁㈠崟銆�
+ List<OrderPakout> orders = orderPakoutService.selectList(new EntityWrapper<OrderPakout>()
+ .eq("status", 1)
+ .in("settle", Arrays.asList(1L, 2L))
+ .eq("pakin_pakout_status", 2)
+ .orderBy("create_time", true));
+ Map<String, Object> result = new LinkedHashMap<>();
+ int scannedOrderCount = 0;
+ int generatedOrderCount = 0;
+ int generatedTaskCount = 0;
+ List<Object> details = new ArrayList<>();
+ if (!Cools.isEmpty(orders)) {
+ for (OrderPakout order : orders) {
+ if (order == null || Cools.isEmpty(order.getOrderNo())) {
+ continue;
+ }
+ scannedOrderCount++;
+ // 姣忎釜璁㈠崟鍗曠嫭璧扮敓鎴愰�昏緫銆傚崟璁㈠崟鏂规硶鍐呴儴鍙細鐢熸垚褰撳墠鍏佽鐨勪竴涓壒娆★細
+ // - 楂樼珯鐐癸細涓�涓� entryWmsCode 鎵规锛�
+ // - 浣庣珯鐐癸細涓�涓� orderNo 鎵规銆�
+ // 鍥犳瀹氭椂鍣ㄩ噸澶嶆墽琛屼篃涓嶄細涓�娆℃�ф妸鍚庣画鎵�鏈夎繘浠撶紪鍙峰叏閮ㄩ噴鏀俱��
+ R r = generatePendingPakoutOrderTasks(order.getOrderNo());
+ details.add(r);
+ int taskCount = extractGeneratedTaskCount(r);
+ if (taskCount > 0) {
+ generatedOrderCount++;
+ generatedTaskCount += taskCount;
+ }
+ }
+ }
+ result.put("scannedOrderCount", scannedOrderCount);
+ result.put("generatedOrderCount", generatedOrderCount);
+ result.put("generatedTaskCount", generatedTaskCount);
+ result.put("details", details);
+ return R.ok().add(result);
+ }
+
+ private int extractGeneratedTaskCount(R r) {
+ if (r == null) {
+ return 0;
+ }
+ Object generatedObj = r.get("generatedTaskCount");
+ if (generatedObj instanceof Number) {
+ return ((Number) generatedObj).intValue();
+ }
+ Object dataObj = r.get("data");
+ if (dataObj instanceof Map) {
+ Object dataGeneratedObj = ((Map) dataObj).get("generatedTaskCount");
+ if (dataGeneratedObj instanceof Number) {
+ return ((Number) dataGeneratedObj).intValue();
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public R generatePendingPakoutOrderTasks(String orderNo) {
+ if (Cools.isEmpty(orderNo)) {
+ throw new CoolException("orderNo涓嶈兘涓虹┖");
+ }
+ OrderPakout order = orderPakoutService.selectByNo(orderNo);
+ if (order == null) {
+ return R.ok("鍑哄簱璁㈠崟涓嶅瓨鍦�").add(buildGeneratePendingResult(orderNo, null, 0, 0, "order not found"));
+ }
+ if (!Objects.equals(order.getStatus(), 1)) {
+ // 璁㈠崟 status=0 鏃跺彧杩斿洖鈥滆鏆傚仠/涓鈥濓紝涓嶆姏寮傚父銆�
+ // 杩欐牱瀹氭椂鍣ㄦ壂鎻忓拰浜哄伐鎵ц鎺ュ彛閮藉彲浠ュ箓绛夎皟鐢紝璋冪敤鏂规牴鎹� reason 鍒ゆ柇鍗冲彲銆�
+ return R.ok("鍑哄簱璁㈠崟宸蹭腑姝�").add(buildGeneratePendingResult(orderNo, null, 0, 0, "order status disabled"));
+ }
+ if (!Objects.equals(order.getSettle(), 1L) && !Objects.equals(order.getSettle(), 2L)) {
+ return R.ok("鍑哄簱璁㈠崟宸插鐞�").add(buildGeneratePendingResult(orderNo, null, 0, 0, "order settle not pending"));
+ }
+
+ // 鎸夊師鏄庣粏 id 椤哄簭璇诲彇锛孡inkedHashMap 浼氫繚鎸侀娆″嚭鐜扮殑鎵规閿『搴忋��
+ // 楂樼珯鐐规壒娆¢敭涓� entryWmsCode锛屽彲缁х画瀹炵幇鈥滆繘浠撶紪鍙� A 鍋氬畬鍚庡啀鐢熸垚 B鈥濓紱
+ // 浣庣珯鐐规壒娆¢敭涓� orderNo锛屽悓涓�璁㈠崟鍙舰鎴愪竴涓壒娆°��
+ List<OrderDetlPakout> details = orderDetlPakoutService.selectList(new EntityWrapper<OrderDetlPakout>()
+ .eq("order_id", order.getId())
+ .eq("status", 1)
+ .orderBy("id", true));
+ LinkedHashMap<String, List<OrderDetlPakout>> detailsByTaskBatchSeq = new LinkedHashMap<>();
+ for (OrderDetlPakout detail : details) {
+ if (!isPendingPakoutDetail(detail)) {
+ continue;
+ }
+ String taskBatchSeq = resolvePakoutTaskBatchSeq(orderNo, detail);
+ // LinkedHashMap 淇濇寔棣栨鍑虹幇椤哄簭锛屼繚璇佸悓涓�璁㈠崟鍐呯殑鎵规鐢熸垚椤哄簭鍜� ERP 鏄庣粏椤哄簭涓�鑷淬��
+ // 杩欏楂樼珯鐐瑰挨鍏跺叧閿細绗簩涓� entryWmsCode 鍙湁鍦ㄧ涓�涓壒娆℃弧瓒抽噴鏀炬潯浠跺悗鎵嶄細琚鐞嗐��
+ detailsByTaskBatchSeq
+ .computeIfAbsent(taskBatchSeq, key -> new ArrayList<>())
+ .add(detail);
+ }
+ if (detailsByTaskBatchSeq.isEmpty()) {
+ return R.ok("鏃犲緟鐢熸垚鍑哄簱浠诲姟").add(buildGeneratePendingResult(orderNo, null, 0, 0, "no pending detail"));
+ }
+
+ // 涓�娆″彧鍙栧綋鍓嶆渶闈犲墠鐨勬壒娆°�傞珮绔欑偣鐨勫悗缁繘浠撶紪鍙锋槸鍚﹀厑璁哥敓鎴愶紝
+ // 鐢� OutboundBatchSeqReleaseGuard 鏍规嵁宸叉湁 WrkMast.batchSeq銆亀rk_sts=25 鏁伴噺闃堝�肩瓑鍒ゆ柇銆�
+ Map.Entry<String, List<OrderDetlPakout>> candidate = detailsByTaskBatchSeq.entrySet().iterator().next();
+ String taskBatchSeq = candidate.getKey();
+ // 鎵规瀹堝崼璇诲彇 WrkMast.userNo/orderNo + WrkMast.batchSeq锛�
+ // - 濡傛灉鍓嶅簭鎵规鏈畬鎴愶紝闃诲锛�
+ // - 濡傛灉褰撳墠鎵规 wrk_sts=25 鏁伴噺杈惧埌閰嶇疆闃堝�硷紝闃诲锛�
+ // - 鍚﹀垯鍏佽鐢熸垚褰撳墠鎵规鐨勪笅涓�缁勪换鍔°��
+ String blockMsg = outboundBatchSeqReleaseGuard.validateReady(orderNo, taskBatchSeq);
+ if (!Cools.isEmpty(blockMsg)) {
+ return R.ok("鍑哄簱鎵规鏆備笉婊¤冻鐢熸垚鏉′欢").add(buildGeneratePendingResult(orderNo, taskBatchSeq, 0, candidate.getValue().size(), blockMsg));
+ }
+
+ List<OutTaskParam> outTaskParams = new ArrayList<>();
+ for (OrderDetlPakout detail : candidate.getValue()) {
+ outTaskParams.add(buildOutTaskParam(orderNo, taskBatchSeq, detail));
+ }
+ Map<String, List<OutTaskParam>> linesByBatchSeq = new LinkedHashMap<>();
+ // outOrderBatch 鐨勫垎缁� key 灏辨槸浠诲姟 batchSeq銆傝繖閲屽己鍒朵娇鐢ㄨ绠楀悗鐨勪换鍔℃壒娆¢敭锛�
+ // 纭繚 WrkMast.userNo=orderId 涓� WrkMast.batchSeq 涓庢壒娆″畧鍗彛寰勪竴鑷淬��
+ //
+ // 涓嶇洿鎺ヤ娇鐢ㄨ鍗曟槑缁嗛噷鐨� batchSeq锛屽洜涓轰綆绔欑偣瑕佹眰鍥哄畾 orderId锛�
+ // 楂樼珯鐐硅姹傚浐瀹� entryWmsCode锛孍RP 鍘熷 batchSeq 鍙綔涓鸿拷婧瓧娈典繚瀛樸��
+ linesByBatchSeq.put(taskBatchSeq, outTaskParams);
+ R r = outOrderBatch(linesByBatchSeq, outTaskParams.size());
+ if (!Objects.equals(r.get("code"), 200)) {
+ throw new CoolException("鍑哄簱璁㈠崟鐢熸垚浠诲姟澶辫触锛�" + orderNo);
+ }
+
+ for (OrderDetlPakout detail : candidate.getValue()) {
+ double remaining = getPendingDetailQty(detail);
+ // work_qty 琛ㄧず鈥滃凡鐢熸垚浠诲姟鏁伴噺鈥濓紝涓嶆槸瀹屾垚鏁伴噺銆�
+ // 浠诲姟瀹屾垚鍚庣敱 WorkMastHandler 閫掑 qty锛屽洜姝ら噸澶嶅畾鏃舵壂鎻忎笉浼氫负鍚屼竴鎵樼洏閲嶅寤轰换鍔°��
+ //
+ // 涓鍙栨秷鏃� WorkService.cancelWrkMast 浼氭妸杩欓儴鍒� work_qty 鍥炴粴锛�
+ // 鍥炴粴鍚庡啀娆℃墽琛岃鍗曪紝鏈笅鍙�/宸插彇娑堢殑鏄庣粏鍙互閲嶆柊鐢熸垚浠诲姟銆�
+ detail.setWorkQty(safeDouble(detail.getWorkQty()) + remaining);
+ detail.setUpdateBy(9527L);
+ detail.setUpdateTime(new Date());
+ if (!orderDetlPakoutService.updateById(detail)) {
+ throw new CoolException("鏇存柊鍑哄簱璁㈠崟鏄庣粏浣滀笟鏁伴噺澶辫触锛�" + orderNo);
+ }
+ }
+ if (Objects.equals(order.getSettle(), 1L)) {
+ // 璁㈠崟涓�鏃︾敓鎴愯繃浠诲姟鍗崇疆涓� 2銆傛槸鍚﹁繕鏈夋槑缁嗘湭鐢熸垚锛屼粛浠ユ槑缁� anfme - work_qty 鍒ゆ柇銆�
+ order.setSettle(2L);
+ order.setUpdateBy(9527L);
+ order.setUpdateTime(new Date());
+ if (!orderPakoutService.updateById(order)) {
+ throw new CoolException("鏇存柊鍑哄簱璁㈠崟鐘舵�佸け璐ワ細" + orderNo);
+ }
+ }
+
+ // status=1 鐨勮鍗曚唬琛ㄥ綋鍓嶅厑璁告墽琛屻�傛棤璁烘潵鑷� ERP 鑷姩璁㈠崟杩樻槸鎵ц鎺ュ彛鎭㈠鍚庣殑璁㈠崟锛�
+ // 鐢熸垚浠诲姟鍚庨兘绔嬪嵆璁剧疆 pdcType=Y锛屼繚璇� WorkMastScheduler 鍙互缁х画涓嬪彂銆�
+ int confirmedCount = confirmPendingOutboundTasks(findActiveOutboundTasks(orderNo));
+ Map<String, Object> result = buildGeneratePendingResult(orderNo, taskBatchSeq, outTaskParams.size(), candidate.getValue().size(), null);
+ result.put("confirmedCount", confirmedCount);
+ return R.ok("鍑哄簱璁㈠崟鐢熸垚浠诲姟鎴愬姛").add(result);
+ }
+
+ private void validatePendingOutOrderParam(OutTaskParam param) {
+ if (param == null || Cools.isEmpty(param.getOrderId())) {
+ throw new CoolException("鍑哄簱鍗曞彿涓嶈兘涓虹┖");
+ }
+ if (Cools.isEmpty(param.getPalletId())) {
+ throw new CoolException("鎵樼洏鍙蜂笉鑳戒负绌�");
+ }
+ if (Cools.isEmpty(param.getStationId())) {
+ throw new CoolException("鎵樼洏銆�" + param.getPalletId() + "銆嶅嚭搴撳彛缂栫爜涓嶈兘涓虹┖");
+ }
+ try {
+ Integer.valueOf(param.getStationId());
+ } catch (NumberFormatException ex) {
+ throw new CoolException("鎵樼洏銆�" + param.getPalletId() + "銆嶅嚭搴撳彛缂栫爜鏍煎紡閿欒锛�" + param.getStationId());
+ }
+ if (isPendingOutOrderStation(param.getStationId()) && Cools.isEmpty(param.getEntryWmsCode())) {
+ throw new CoolException("鎵樼洏銆�" + param.getPalletId() + "銆嶈繘浠撶紪鍙蜂笉鑳戒负绌�");
+ }
+ if (Cools.isEmpty(param.getBatchSeq())) {
+ // batchSeq 鏄帴鍙e師瀛楁锛屼繚瀛樺湪璁㈠崟鏄庣粏鐢ㄤ簬杩芥函锛涘疄闄呬换鍔℃壒娆¢敭鐢� resolvePakoutTaskBatchSeq 鍐冲畾銆�
+ param.setBatchSeq(param.getOrderId());
+ }
+ }
+
+ private OrderPakout createPendingPakoutOrderHeader(String orderNo, Date now, boolean executable) {
+ DocType docType = docTypeService.selectOrAdd(OUT_ORDER_PENDING_DOC_TYPE, Boolean.FALSE);
+ OrderPakout order = new OrderPakout();
+ order.setUuid(String.valueOf(snowflakeIdWorker.nextId()));
+ order.setOrderNo(orderNo);
+ order.setOrderTime(DateUtils.convert(now));
+ order.setDocType(docType.getDocId());
+ // settle 浠嶆部鐢ㄧ幇鏈夎鍗曡繘搴﹁涔夛細
+ // - 1锛氳鍗曞凡鍒涘缓锛岃繕娌℃湁鐢熸垚杩囦换鍔★紱
+ // - 2锛氳嚦灏戠敓鎴愯繃涓�涓换鍔℃壒娆★紝鍚庣画鏄惁杩樻湁寰呯敓鎴愭槑缁嗙户缁湅 anfme - work_qty锛�
+ // - 澶т簬 2锛氬叾浠栦笟鍔℃祦绋嬪凡澶勭悊/瀹屾垚锛屼笉鍏佽鏈帴鍙h鐩栥��
+ // status 鏄惎鍔ㄥ紑鍏筹細
+ // - ERP /outOrder 鍒涘缓 executable=true锛宻tatus=1锛屽畾鏃跺櫒鍙嚜鍔ㄧ敓鎴愶紱
+ // - IoT/MQTT 鍒涘缓 executable=false锛宻tatus=0锛屽繀椤昏皟鐢ㄦ墽琛屾帴鍙e悗鎵嶇敓鎴愩��
+ order.setSettle(1L);
+ order.setStatus(executable ? 1 : 0);
+ order.setCreateBy(9527L);
+ order.setCreateTime(now);
+ order.setUpdateBy(9527L);
+ order.setUpdateTime(now);
+ // moveStatus 淇濈暀鐜版湁鈥滃璐�/绉诲簱鈥濊涔夛紝杩欓噷涓嶅鐢ㄥ畠鍋氭殏鍋滃紑鍏炽��
+ order.setMoveStatus(0);
+ // 2 琛ㄧず鍑哄簱鏂瑰悜锛屽拰 man_order_detl_pakout 鏄庣粏淇濇寔涓�鑷淬��
+ order.setPakinPakoutStatus(2);
+ if (!orderPakoutService.insert(order)) {
+ throw new CoolException("鐢熸垚鍑哄簱璁㈠崟澶辫触锛�" + orderNo);
+ }
+ return order;
+ }
+
+ private void refreshPendingPakoutOrderForResubmit(OrderPakout order, Date now, boolean executable) {
+ if (order == null) {
+ return;
+ }
+ boolean hasDispatchedDetail = hasDispatchedPendingDetail(order.getId());
+ // 閲嶅涓嬪彂鏃舵寜鏈鍏ュ彛閲嶆柊鍐冲畾鏄惁绔嬪嵆鍙墽琛岋細
+ // ERP 閲嶅彂缁х画 status=1锛汭oT/MQTT 棰勫垱寤虹户缁� status=0锛岀瓑寰呮墽琛屾帴鍙c��
+ //
+ // settle 鐨勬仮澶嶅彛寰勶細
+ // - 濡傛灉宸叉湁宸蹭笅鍙�/宸插畬鎴愭槑缁嗭紝淇濇寔 2锛岃鏄庤璁㈠崟鏇捐繘鍏ョ敓鎴愪换鍔¢樁娈碉紱
+ // - 濡傛灉鍙墿鏈笅鍙戞槑缁嗭紝閲嶇疆涓� 1锛岃〃绀哄彲浠ヤ綔涓烘柊璁㈠崟鎵规閲嶆柊鐢熸垚銆�
+ order.setStatus(executable ? 1 : 0);
+ order.setSettle(hasDispatchedDetail ? 2L : 1L);
+ order.setUpdateBy(9527L);
+ order.setUpdateTime(now);
+ order.setPakinPakoutStatus(2);
+ if (!orderPakoutService.updateById(order)) {
+ throw new CoolException("鏇存柊鍑哄簱璁㈠崟澶辫触锛�" + order.getOrderNo());
+ }
+ }
+
+ private boolean hasDispatchedPendingDetail(Long orderId) {
+ // 鍙瀛樺湪 work_qty>0 鎴� qty>0 鐨勬槑缁嗭紝灏辫涓鸿璁㈠崟宸茬粡杩涘叆杩囨墽琛岄摼璺��
+ // 杩欑被鏄庣粏涓嶈兘鍦ㄩ噸鍙戞椂鐩存帴鍒犻櫎锛屽惁鍒欎細涓㈠け浠诲姟杩芥函鎴栧畬鎴愭暟閲忋��
+ List<OrderDetlPakout> details = orderDetlPakoutService.selectList(new EntityWrapper<OrderDetlPakout>()
+ .eq("order_id", orderId)
+ .eq("status", 1));
+ if (Cools.isEmpty(details)) {
+ return false;
+ }
+ for (OrderDetlPakout detail : details) {
+ if (!isUndispatchedPendingDetail(detail)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 鍒犻櫎鍚岃鍗曚腑灏氭湭涓嬪彂鐨勬槑缁嗐��
+ *
+ * 鍒ゆ柇鍙e緞锛�
+ * - work_qty <= 0锛氬皻鏈敓鎴愭湁鏁堜换鍔★紝鎴栦腑姝㈠悗浠诲姟宸茶鍙栨秷骞跺畬鎴愬洖婊氥��
+ * - qty <= 0锛氭病鏈変笟鍔″畬鎴愰噺锛屽垹闄ゅ悗涓嶄細涓㈠け宸插畬鎴愬嚭搴撹褰曘��
+ */
+ private int removeUndispatchedPendingDetails(Long orderId) {
+ List<OrderDetlPakout> details = orderDetlPakoutService.selectList(new EntityWrapper<OrderDetlPakout>()
+ .eq("order_id", orderId)
+ .eq("status", 1));
+ if (Cools.isEmpty(details)) {
+ return 0;
+ }
+ int removed = 0;
+ for (OrderDetlPakout detail : details) {
+ if (!isUndispatchedPendingDetail(detail)) {
+ continue;
+ }
+ if (!orderDetlPakoutService.deleteById(detail.getId())) {
+ throw new CoolException("鍒犻櫎鏈笅鍙戝嚭搴撹鍗曟槑缁嗗け璐ワ細" + detail.getOrderNo());
+ }
+ removed++;
+ }
+ return removed;
+ }
+
+ private void assertNoNonReplaceablePendingDetailConflict(OrderPakout order, List<OutTaskParam> params) {
+ if (order == null || Cools.isEmpty(params)) {
+ return;
+ }
+ Set<String> newPalletIds = params.stream()
+ .filter(Objects::nonNull)
+ .map(OutTaskParam::getPalletId)
+ .filter(palletId -> !Cools.isEmpty(palletId))
+ .collect(Collectors.toCollection(LinkedHashSet::new));
+ if (newPalletIds.isEmpty()) {
+ return;
+ }
+ List<OrderDetlPakout> details = orderDetlPakoutService.selectList(new EntityWrapper<OrderDetlPakout>()
+ .eq("order_id", order.getId())
+ .eq("status", 1));
+ if (Cools.isEmpty(details)) {
+ return;
+ }
+ for (OrderDetlPakout detail : details) {
+ // 鏈閲嶅彂濡傛灉鍖呭惈浜嗗凡缁忎笅鍙�/瀹屾垚鐨勫悓鎵樼洏鏄庣粏锛岀洿鎺ユ嫆缁濄��
+ // 鏈笅鍙戞槑缁嗗彲浠ュ垹闄ら噸寤猴紱宸蹭笅鍙�/瀹屾垚鏄庣粏蹇呴』淇濈暀锛屼互鍏嶇牬鍧忓彇娑堝洖婊氬拰瀹屾垚鍥炲啓銆�
+ if (detail == null || !newPalletIds.contains(detail.getPalletId()) || isUndispatchedPendingDetail(detail)) {
+ continue;
+ }
+ throw new CoolException("鎵樼洏銆�" + detail.getPalletId() + "銆嶅凡瀛樺湪宸蹭笅鍙戞垨宸插畬鎴愬嚭搴撴槑缁嗭紝鏃犳硶瑕嗙洊");
+ }
+ }
+
+ private boolean isUndispatchedPendingDetail(OrderDetlPakout detail) {
+ // 鈥滄湭涓嬪彂鈥濅笉鏄湅璁㈠崟鐘舵�侊紝鑰屾槸鐪嬫槑缁嗚繘搴︼細
+ // work_qty=0 涓� qty=0 鎵嶈鏄庢棦娌℃湁鐢熸垚浠诲姟锛屼篃娌℃湁瀹屾垚閲忥紝鍙互瀹夊叏鍒犻櫎閲嶅缓銆�
+ return detail != null
+ && safeDouble(detail.getWorkQty()) <= 0.0D
+ && safeDouble(detail.getQty()) <= 0.0D;
+ }
+
+ /**
+ * 鍒ゆ柇寤惰繜鍑哄簱璁㈠崟鏄惁杩樿兘琚綋鍓嶆帴鍙h姹傝鐩栥��
+ *
+ * 娲诲姩浠诲姟浠嶇劧瀛樺湪鏃朵笉鍏佽瑕嗙洊锛屽洜涓轰换鍔°�佸簱浣嶃�乄CS 鎸囦护杩樺湪鎵ц閾捐矾涓娿��
+ * 宸茬粡鍙栨秷骞跺綊妗e埌 WrkMastLog 鐨勪换鍔″厑璁搁噸涓嬪彂锛涘叾璁㈠崟鏄庣粏 work_qty 宸插湪鍙栨秷鏃跺洖婊氾紝
+ * 鏈鎺ュ彛浼氬垹闄ゆ湭涓嬪彂鏄庣粏骞舵彃鍏ユ柊鏄庣粏銆�
+ */
+ private void assertPendingPakoutOrderCanReplace(String orderNo) {
+ List<WrkMast> activeTasks = findActiveOutboundTasks(orderNo);
+ if (!Cools.isEmpty(activeTasks)) {
+ throw new CoolException("鍑哄簱璁㈠崟宸插瓨鍦ㄦ椿鍔ㄤ换鍔★紝鏃犳硶瑕嗙洊锛�" + orderNo);
+ }
+ int activeWrkCount = wrkMastService.selectCount(new EntityWrapper<WrkMast>()
+ .eq("io_type", 101)
+ .eq("user_no", orderNo));
+ if (activeWrkCount > 0) {
+ throw new CoolException("鍑哄簱璁㈠崟宸插瓨鍦ㄤ换鍔℃。锛屾棤娉曡鐩栵細" + orderNo);
+ }
+ OrderPakout order = orderPakoutService.selectByNo(orderNo);
+ if (order != null && order.getSettle() != null && order.getSettle() > 2L) {
+ throw new CoolException(orderNo + "姝e湪鍑哄簱锛屾棤娉曚慨鏀瑰崟鎹�");
+ }
+ }
+
+ /**
+ * 鎶� OutTaskParam 钀芥垚鍑哄簱璁㈠崟鏄庣粏銆�
+ *
+ * detail.sync(locDetl) 璐熻矗澶嶅埗搴撳瓨缁村害瀛楁锛堢墿鏂欍�佹壒娆°�佸搧鐗屻�佹墿灞曞瓧娈电瓑锛夛紱
+ * 鍚庣画 setXxx 淇濆瓨鎺ュ彛鍘熷瀛楁锛屼繚璇佸畾鏃跺櫒鍙互杩樺師瀹屾暣 OutTaskParam銆�
+ */
+ private OrderDetlPakout buildPendingPakoutOrderDetl(OrderPakout order, OutTaskParam param, Date now) {
+ LocDetl locDetl = locDetlService.selectOne(new EntityWrapper<LocDetl>().eq("zpallet", param.getPalletId()));
+ if (locDetl == null) {
+ throw new CoolException("搴撳瓨涓笉瀛樺湪璇ユ墭鐩橈細" + param.getPalletId());
+ }
+ if (!Cools.isEmpty(param.getMatnr()) && !Objects.equals(param.getMatnr(), locDetl.getMatnr())) {
+ throw new CoolException("鎵樼洏銆�" + param.getPalletId() + "銆嶇墿鏂欑紪鐮佷笌搴撳瓨涓嶄竴鑷�");
+ }
+ OrderDetlPakout detail = new OrderDetlPakout();
+ detail.sync(locDetl);
+ // order_id/order_no 浣跨敤 man_order_pakout 鐨勪富妗d俊鎭紝涓嶈兘浣跨敤 ERP 鍘熷瀛楁鐩存帴鍐欏叆 id銆�
+ detail.setOrderId(order.getId());
+ detail.setOrderNo(order.getOrderNo());
+ detail.setAnfme(resolvePendingOrderAnfme(param, locDetl));
+ // work_qty锛氬凡鐢熸垚浠诲姟鏁伴噺锛泀ty锛氬凡瀹屾垚鍑哄簱鏁伴噺銆�
+ // 涓よ�呭垵濮嬮兘涓� 0锛岀敓鎴愪换鍔″彧鍔� work_qty锛屼换鍔″畬鎴愬洖鍐欐墠鍔� qty銆�
+ detail.setWorkQty(0.0D);
+ detail.setQty(0.0D);
+ detail.setStatus(1);
+ detail.setCreateBy(9527L);
+ detail.setCreateTime(now);
+ detail.setUpdateBy(9527L);
+ detail.setUpdateTime(now);
+ detail.setPakinPakoutStatus(2);
+ // 浠ヤ笅瀛楁瀹屾暣淇濆瓨 OutTaskParam 鍘熷�硷紝淇濊瘉鍚庣画瀹氭椂鍣ㄥ彲浠ヨ繕鍘熷嚭 OutTaskParam銆�
+ // 娉ㄦ剰锛歞etail.batchSeq 鍙槸 ERP 鍘熷鎵规瀛楁锛涚湡姝g敓鎴� WrkMast.batchSeq 鏃朵細閲嶆柊璁$畻銆�
+ detail.setBatchSeq(param.getBatchSeq());
+ detail.setSeq(param.getSeq());
+ detail.setPalletId(param.getPalletId());
+ detail.setStationId(param.getStationId());
+ detail.setEntryWmsCode(param.getEntryWmsCode());
+ detail.setContainerNo(param.getContainerNo());
+ detail.setOutDoorNo(param.getOutDoorNo());
+ detail.setPlateNo(param.getPlateNo());
+ detail.setTrainNo(param.getTrainNo());
+ detail.setFreqType(param.getFreqType());
+ detail.setCubeNumber(param.getCubeNumber());
+ detail.setTeu(param.getTeu());
+ // standby1/standby2 缁х画闀滃儚杩涗粨缂栧彿鍜屽嚭搴撻棬鍙凤紝鍏煎鐜版湁瀹屾垚鍥炲啓鐨� selectItem 鍖归厤閫昏緫銆�
+ // 浣庣珯鐐瑰厑璁� entryWmsCode 涓虹┖锛屼篃瑕佹樉寮忚惤绌哄�硷紝淇濊瘉璁㈠崟鏄庣粏鍜� WrkDetl 缁村害涓�鑷淬��
+ detail.setStandby1(param.getEntryWmsCode());
+ detail.setStandby2(param.getOutDoorNo());
+ return detail;
+ }
+
+ /**
+ * 鏄庣粏鏁伴噺浼樺厛浣跨敤 ERP 鍙傛暟涓殑 anfme銆�
+ * 濡傛灉 ERP 鏈紶鏁伴噺锛屽垯浣跨敤褰撳墠搴撳瓨鏁伴噺锛涗袱鑰呴兘涓嶅彲鐢ㄦ椂鎸夋暣鎵� 1 澶勭悊锛岄伩鍏嶇敓鎴� 0 鏁伴噺浠诲姟銆�
+ */
+ private double resolvePendingOrderAnfme(OutTaskParam param, LocDetl locDetl) {
+ if (param.getAnfme() > 0.0D) {
+ return param.getAnfme();
+ }
+ if (locDetl != null && locDetl.getAnfme() != null && locDetl.getAnfme() > 0.0D) {
+ return locDetl.getAnfme();
+ }
+ return 1.0D;
+ }
+
+ /**
+ * 鍒ゆ柇璁㈠崟鏄庣粏鏄惁杩橀渶瑕佺敓鎴愪换鍔°��
+ *
+ * work_qty 鏄换鍔$敓鎴愯繘搴︼紝qty 鏄换鍔″畬鎴愯繘搴︺��
+ * 杩欓噷鍙湅 anfme - work_qty锛岄伩鍏嶅畾鏃跺櫒鍥犱负浠诲姟鏈畬鎴愯�岄噸澶嶇敓鎴愬悓涓�鎵樼洏浠诲姟銆�
+ */
+ private boolean isPendingPakoutDetail(OrderDetlPakout detail) {
+ // 鍙杩樺瓨鍦ㄥ緟鐢熸垚鏁伴噺锛屽苟涓旇兘璁$畻鍑轰换鍔℃壒娆¢敭锛屽氨璁や负璇ユ槑缁嗗彲杩涘叆鐢熸垚娴佺▼銆�
+ // 鏄惁鐪熺殑鐢熸垚鐢卞悗缁� OutboundBatchSeqReleaseGuard 鍐冲畾銆�
+ return detail != null
+ && !Cools.isEmpty(resolvePakoutTaskBatchSeq(detail.getOrderNo(), detail))
+ && getPendingDetailQty(detail) > 0.0D;
+ }
+
+ /**
+ * 璁㈠崟鏄庣粏杞换鍔℃椂鐨勬壒娆¢敭銆�
+ *
+ * entryWmsCode 瀛樺湪鏃朵紭鍏堜綔涓鸿繘浠撶紪鍙锋壒娆★紝鍏煎 stationId > 600 鐨勫垎鎵规斁琛岋紱
+ * entryWmsCode 涓虹┖鏃朵娇鐢ㄨ鍗曞彿锛屾弧瓒充綆绔欑偣 ERP 鍑哄簱缁熶竴璁㈠崟鍖栧悗鐨勬壒娆¤竟鐣屻��
+ *
+ * stationId <= 600 鍗充娇浼犱簡 entryWmsCode锛屼篃涓嶅弬涓庢壒娆¢敭璁$畻锛�
+ * 杩欐槸涓轰簡婊¤冻鈥滀綆绔欑偣鎵规杈圭晫鎸� orderId 澶勭悊鈥濈殑鎺ュ彛绾﹀畾銆�
+ */
+ private String resolvePakoutTaskBatchSeq(String orderNo, OrderDetlPakout detail) {
+ if (detail != null && isPendingOutOrderStation(detail.getStationId()) && !Cools.isEmpty(detail.getEntryWmsCode())) {
+ return detail.getEntryWmsCode();
+ }
+ return orderNo;
+ }
+
+ /**
+ * 浠庤鍗曟槑缁嗚繕鍘� outOrderBatch 闇�瑕佺殑鍙傛暟銆�
+ *
+ * batchSeq 蹇呴』寮哄埗鏀逛负浠诲姟鎵规閿細
+ * - 楂樼珯鐐癸細entryWmsCode锛學CS 涓嬪彂瀹堝崼浠ヨ繘浠撶紪鍙蜂负椤哄簭杈圭晫锛�
+ * - 浣庣珯鐐癸細orderNo锛屾暣寮� ERP 璁㈠崟浣滀负涓�涓壒娆°��
+ */
+ private OutTaskParam buildOutTaskParam(String orderNo, String taskBatchSeq, OrderDetlPakout detail) {
+ OutTaskParam param = new OutTaskParam();
+ param.setOrderId(orderNo);
+ param.setBatchSeq(taskBatchSeq);
+ param.setSeq(detail.getSeq());
+ param.setPalletId(detail.getPalletId());
+ param.setStationId(detail.getStationId());
+ param.setMatnr(detail.getMatnr());
+ param.setAnfme(getPendingDetailQty(detail));
+ param.setEntryWmsCode(detail.getEntryWmsCode());
+ param.setContainerNo(detail.getContainerNo());
+ param.setOutDoorNo(detail.getOutDoorNo());
+ param.setPlateNo(detail.getPlateNo());
+ param.setTrainNo(detail.getTrainNo());
+ param.setFreqType(detail.getFreqType());
+ param.setCubeNumber(detail.getCubeNumber());
+ param.setTeu(detail.getTeu());
+ return param;
+ }
+
+ private Map<String, Object> buildGeneratePendingResult(String orderNo, String taskBatchSeq, int generatedTaskCount, int pendingDetailCount, String reason) {
+ Map<String, Object> result = new LinkedHashMap<>();
+ result.put("orderNo", orderNo);
+ result.put("batchSeq", taskBatchSeq);
+ // 淇濈暀鏃� key锛岄伩鍏嶅閮ㄦ棩蹇楁垨椤甸潰浠嶆寜 entryWmsCode 璇诲彇楂樼珯鐐规壒娆°��
+ result.put("entryWmsCode", taskBatchSeq);
+ result.put("generatedTaskCount", generatedTaskCount);
+ result.put("pendingDetailCount", pendingDetailCount);
+ if (!Cools.isEmpty(reason)) {
+ result.put("reason", reason);
+ }
+ return result;
+ }
+
+ private boolean isPendingOutOrderStation(String stationId) {
+ if (Cools.isEmpty(stationId)) {
+ return false;
+ }
+ try {
+ return Integer.valueOf(stationId) > PENDING_OUT_ORDER_STATION_THRESHOLD;
+ } catch (NumberFormatException ignored) {
+ return false;
+ }
+ }
+
+ private double getPendingDetailQty(OrderDetlPakout detail) {
+ return safeDouble(detail.getAnfme()) - safeDouble(detail.getWorkQty());
+ }
+
+ private double safeDouble(Double value) {
+ return value == null ? 0.0D : value;
+ }
+
+ /**
+ * 7.9 鍑哄簱寮傚父鍙樺姩涓婃姤銆�
+ * WCS 鍙紶 palletId銆乪rrorMsg锛學MS 瑙f瀽 orderId 鍚庤浆鍙� ERP銆�
+ */
+ @Override
+ public R outOrderAbnormalReport(OutOrderAbnormalReportParam param) {
+ if (param == null || Cools.isEmpty(param.getPalletId())) {
+ return R.error("palletId涓嶈兘涓虹┖");
+ }
+ String orderId = resolveOutboundOrderId(param.getPalletId());
+ if (Cools.isEmpty(orderId)) {
+ return R.error("鏈壘鍒版墭鐩樺搴斿嚭搴撳崟鍙凤細" + param.getPalletId());
+ }
+ if (Cools.isEmpty(erpOutErrorAddress)) {
+ return R.error("ERP鍑哄簱寮傚父涓婃姤鍦板潃鏈厤缃�");
+ }
+
+ ErpOutOrderAbnormalReportParam erpParam = new ErpOutOrderAbnormalReportParam();
+ erpParam.setPalletId(param.getPalletId());
+ erpParam.setErrorMsg(param.getErrorMsg());
+ erpParam.setOrderId(orderId);
+
+ String requestJson = JSON.toJSONString(erpParam);
+ String response = "";
+ boolean pushOk = false;
+ String errorMsg = null;
+ String pushUrl = buildErpOutErrorRequestUrl();
+ try {
+ response = new HttpHandler.Builder()
+ .setUri(erpUrl)
+ .setPath(erpOutErrorAddress)
+ .setJson(requestJson)
+ .build()
+ .doPost();
+ pushOk = isErpCallSuccess(response);
+ if (!pushOk) {
+ errorMsg = extractErpCallError(response);
+ }
+ } catch (Exception e) {
+ errorMsg = e.getMessage();
+ log.error("鎺‥RP鍑哄簱寮傚父涓婃姤澶辫触, palletId={}, orderId={}", param.getPalletId(), orderId, e);
+ } finally {
+ try {
+ apiLogService.save(
+ "鎺‥RP-鍑哄簱寮傚父涓婃姤",
+ pushUrl,
+ null,
+ "127.0.0.1",
+ requestJson,
+ response,
+ pushOk,
+ "palletId=" + param.getPalletId() + ",orderId=" + orderId
+ );
+ } catch (Exception logEx) {
+ log.error("save out abnormal api log failed", logEx);
+ }
+ }
+ if (!pushOk) {
+ return R.error(Cools.isEmpty(errorMsg) ? "ERP鍑哄簱寮傚父涓婃姤澶辫触" : errorMsg);
+ }
+ return R.ok().add(Cools.add("palletId", param.getPalletId()).add("orderId", orderId));
+ }
+
+ /**
+ * 7.10 鍑哄簱寮傚父鍙樺姩澶勭悊銆�
+ * 鏈湡鍙帴鏀躲�佹牎楠屽拰璁板綍锛屼笉鏀规湰鍦颁换鍔★紝涔熶笉杞彂 WCS銆�
+ */
+ @Override
+ public R outOrderAbnormalHandle(OutOrderAbnormalHandleParam param) {
+ if (param == null || Cools.isEmpty(param.getPalletId())) {
+ return R.error("palletId涓嶈兘涓虹┖");
+ }
+ if (param.getOperateType() == null || !(Objects.equals(param.getOperateType(), 0) || Objects.equals(param.getOperateType(), 2))) {
+ return R.error("operateType鍙厑璁�0鎴�2");
+ }
+ if (!existsOutboundPallet(param.getPalletId())) {
+ return R.error("鏈壘鍒版墭鐩橈細" + param.getPalletId());
+ }
+ log.info("鎺ユ敹鍑哄簱寮傚父澶勭悊鎸囦护, palletId={}, operateType={}", param.getPalletId(), param.getOperateType());
+ return R.ok().add(Cools.add("palletId", param.getPalletId()).add("operateType", param.getOperateType()));
+ }
+
+ private String buildOutOrderBatchKey(OutTaskParam param) {
+ return param.getOrderId() + "#" + param.getBatchSeq();
+ }
+
+ private Map<String, Integer> buildOutOrderBatchTeuCounts(List<OutTaskParam> params) {
+ Map<String, Set<String>> batchOrderIds = new HashMap<>();
+ for (OutTaskParam param : params) {
+ batchOrderIds.computeIfAbsent(param.getBatchSeq(), k -> new LinkedHashSet<>()).add(param.getOrderId());
+ }
+ Map<String, Integer> batchTeuCounts = new HashMap<>();
+ for (Map.Entry<String, Set<String>> entry : batchOrderIds.entrySet()) {
+ batchTeuCounts.put(entry.getKey(), entry.getValue().size());
+ }
+ return batchTeuCounts;
+ }
+
+ private String resolveOutboundOrderId(String palletId) {
+ List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>()
+ .eq("io_type", 101)
+ .eq("barcode", palletId)
+ .orderBy("io_time", false)
+ .orderBy("wrk_no", false));
+ if (!Cools.isEmpty(wrkMasts)) {
+ for (WrkMast wrkMast : wrkMasts) {
+ if (!Cools.isEmpty(wrkMast.getUserNo())) {
+ return wrkMast.getUserNo();
+ }
+ }
+ }
+ List<WrkDetl> wrkDetls = wrkDetlService.selectList(new EntityWrapper<WrkDetl>()
+ .eq("zpallet", palletId)
+ .orderBy("io_time", false)
+ .orderBy("wrk_no", false));
+ if (!Cools.isEmpty(wrkDetls)) {
+ for (WrkDetl wrkDetl : wrkDetls) {
+ if (!Cools.isEmpty(wrkDetl.getOrderNo())) {
+ return wrkDetl.getOrderNo();
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean existsOutboundPallet(String palletId) {
+ if (locDetlService.selectCount(new EntityWrapper<LocDetl>().eq("zpallet", palletId)) > 0) {
+ return true;
+ }
+ if (wrkMastService.selectCount(new EntityWrapper<WrkMast>().eq("io_type", 101).eq("barcode", palletId)) > 0) {
+ return true;
+ }
+ return wrkDetlService.selectCount(new EntityWrapper<WrkDetl>().eq("zpallet", palletId)) > 0;
+ }
+
+ private boolean isErpCallSuccess(String response) {
+ if (Cools.isEmpty(response)) {
+ return false;
+ }
+ try {
+ JSONObject jsonObject = JSON.parseObject(response);
+ if (jsonObject == null) {
+ return false;
+ }
+ Boolean success = jsonObject.getBoolean("success");
+ if (success != null) {
+ return success;
+ }
+ Integer code = jsonObject.getInteger("code");
+ if (code != null) {
+ return code == 200 || code == 0;
+ }
+ Integer status = jsonObject.getInteger("status");
+ if (status != null) {
+ return status == 200 || status == 0;
+ }
+ String result = jsonObject.getString("result");
+ if (!Cools.isEmpty(result)) {
+ return "success".equalsIgnoreCase(result) || "ok".equalsIgnoreCase(result) || "true".equalsIgnoreCase(result);
+ }
+ } catch (Exception ignore) {
+ return response.toLowerCase().contains("success") && response.toLowerCase().contains("true");
+ }
+ return false;
+ }
+
+ private String extractErpCallError(String response) {
+ if (Cools.isEmpty(response)) {
+ return "empty erp response";
+ }
+ try {
+ JSONObject jsonObject = JSON.parseObject(response);
+ if (jsonObject == null) {
+ return response;
+ }
+ String[] keys = new String[]{"msg", "message", "error", "errMsg"};
+ for (String key : keys) {
+ String value = jsonObject.getString(key);
+ if (!Cools.isEmpty(value)) {
+ return value;
+ }
+ }
+ } catch (Exception ignore) {
+ }
+ return response;
+ }
+
+ private boolean isErpReportEnabled() {
+ if (!erpReportOld) {
+ return false;
+ }
+ String erpReport = Parameter.get().getErpReport();
+ return Cools.isEmpty(erpReport) || "true".equalsIgnoreCase(erpReport);
+ }
+
+ private List<String> normalizeBarcodes(List<String> barcodes) {
+ if (barcodes == null || barcodes.isEmpty()) {
+ return Collections.emptyList();
+ }
+ LinkedHashSet<String> normalized = new LinkedHashSet<>();
+ for (String barcode : barcodes) {
+ if (barcode == null) {
+ continue;
+ }
+ String value = barcode.trim();
+ if (!value.isEmpty()) {
+ normalized.add(value);
+ }
+ }
+ return new ArrayList<>(normalized);
+ }
+
+ private Map<String, WrkMastLog> loadLatestInboundHistoryLogMap(List<String> barcodes) {
+ Map<String, WrkMastLog> latestInboundLogMap = new LinkedHashMap<>();
+ if (barcodes == null || barcodes.isEmpty()) {
+ return latestInboundLogMap;
+ }
+ List<WrkMastLog> wrkMastLogs = wrkMastLogService.selectList(new EntityWrapper<WrkMastLog>()
+ .in("barcode", barcodes)
+ .orderBy("barcode", true)
+ .orderBy("io_time", false)
+ .orderBy("wrk_no", false));
+ if (Cools.isEmpty(wrkMastLogs)) {
+ return latestInboundLogMap;
+ }
+ for (WrkMastLog wrkMastLog : wrkMastLogs) {
+ if (wrkMastLog == null || Cools.isEmpty(wrkMastLog.getBarcode())) {
+ continue;
+ }
+ if (!isInboundHistoryLog(wrkMastLog.getIoType())) {
+ continue;
+ }
+ latestInboundLogMap.putIfAbsent(wrkMastLog.getBarcode(), wrkMastLog);
+ }
+ return latestInboundLogMap;
+ }
+
+ private boolean isInboundHistoryLog(Integer ioType) {
+ if (ioType == null) {
+ return false;
+ }
+ // 鍘嗗彶琛ㄩ噷鏃㈡湁鍏ュ簱瀹屾垚锛屼篃鏈夊簱瀛樿皟鏁达紱杩欓噷鍙彇鐪熸鐨勫叆搴撶被璁板綍銆�
+ return ioType < 19 || ioType == 53 || ioType == 54 || ioType == 57;
+ }
+
+ private Map<Integer, List<WrkDetlLog>> loadWrkDetlLogMap(Collection<WrkMastLog> wrkMastLogs) {
+ Map<Integer, List<WrkDetlLog>> wrkDetlLogMap = new HashMap<>();
+ if (wrkMastLogs == null || wrkMastLogs.isEmpty()) {
+ return wrkDetlLogMap;
+ }
+ LinkedHashSet<Integer> wrkNos = new LinkedHashSet<>();
+ for (WrkMastLog wrkMastLog : wrkMastLogs) {
+ if (wrkMastLog != null && wrkMastLog.getWrkNo() != null) {
+ wrkNos.add(wrkMastLog.getWrkNo());
+ }
+ }
+ if (wrkNos.isEmpty()) {
+ return wrkDetlLogMap;
+ }
+ List<WrkDetlLog> wrkDetlLogs = wrkDetlLogService.selectList(new EntityWrapper<WrkDetlLog>().in("wrk_no", wrkNos));
+ if (Cools.isEmpty(wrkDetlLogs)) {
+ return wrkDetlLogMap;
+ }
+ for (WrkDetlLog wrkDetlLog : wrkDetlLogs) {
+ if (wrkDetlLog == null || wrkDetlLog.getWrkNo() == null) {
+ continue;
+ }
+ wrkDetlLogMap.computeIfAbsent(wrkDetlLog.getWrkNo(), k -> new ArrayList<>()).add(wrkDetlLog);
+ }
+ return wrkDetlLogMap;
+ }
+
+ private Map<String, WaitPakinLog> loadWaitPakinLogMap(List<String> barcodes) {
+ Map<String, WaitPakinLog> waitPakinLogMap = new LinkedHashMap<>();
+ if (barcodes == null || barcodes.isEmpty()) {
+ return waitPakinLogMap;
+ }
+ List<WaitPakinLog> waitPakinLogs = waitPakinLogService.selectList(new EntityWrapper<WaitPakinLog>()
+ .in("zpallet", barcodes)
+ .orderBy("zpallet", true)
+ .orderBy("appe_time", false)
+ .orderBy("modi_time", false));
+ if (Cools.isEmpty(waitPakinLogs)) {
+ return waitPakinLogMap;
+ }
+ for (WaitPakinLog waitPakinLog : waitPakinLogs) {
+ if (waitPakinLog == null || Cools.isEmpty(waitPakinLog.getZpallet())) {
+ continue;
+ }
+ waitPakinLogMap.putIfAbsent(waitPakinLog.getZpallet(), waitPakinLog);
+ }
+ return waitPakinLogMap;
+ }
+
+ private ErpPakinReportParam buildInboundErpParam(String barcode,
+ WrkMastLog wrkMastLog,
+ List<WrkDetlLog> wrkDetlLogs,
+ WaitPakinLog waitPakinLog) {
+ ErpPakinReportParam param = new ErpPakinReportParam();
+ param.setPalletId(resolvePalletId(barcode, wrkMastLog, wrkDetlLogs, waitPakinLog));
+ param.setAnfme(resolveInboundAnfme(wrkDetlLogs, waitPakinLog));
+ param.setLocId(transformInboundLocId(resolveInboundLocNo(wrkMastLog, waitPakinLog)));
+ param.setWeight(resolveInboundWeight(wrkMastLog, waitPakinLog));
+ param.setCreateTime(formatDate(resolveInboundCreateTime(wrkMastLog)));
+ param.setBizNo(resolveInboundBizNo(wrkDetlLogs, waitPakinLog));
+ param.setStartTime(formatDate(resolveInboundStartTime(wrkMastLog, waitPakinLog)));
+ return param;
+ }
+
+ private String resolvePalletId(String barcode,
+ WrkMastLog wrkMastLog,
+ List<WrkDetlLog> wrkDetlLogs,
+ WaitPakinLog waitPakinLog) {
+ if (wrkMastLog != null && !Cools.isEmpty(wrkMastLog.getBarcode())) {
+ return wrkMastLog.getBarcode();
+ }
+ if (wrkDetlLogs != null) {
+ for (WrkDetlLog wrkDetlLog : wrkDetlLogs) {
+ if (wrkDetlLog != null && !Cools.isEmpty(wrkDetlLog.getZpallet())) {
+ return wrkDetlLog.getZpallet();
+ }
+ }
+ }
+ if (waitPakinLog != null && !Cools.isEmpty(waitPakinLog.getZpallet())) {
+ return waitPakinLog.getZpallet();
+ }
+ return barcode;
+ }
+
+ private Double resolveInboundAnfme(List<WrkDetlLog> wrkDetlLogs, WaitPakinLog waitPakinLog) {
+ double total = 0D;
+ boolean hasDetail = false;
+ if (wrkDetlLogs != null) {
+ for (WrkDetlLog wrkDetlLog : wrkDetlLogs) {
+ if (wrkDetlLog == null || wrkDetlLog.getAnfme() == null) {
+ continue;
+ }
+ total += wrkDetlLog.getAnfme();
+ hasDetail = true;
+ }
+ }
+ if (hasDetail) {
+ return total;
+ }
+ if (waitPakinLog != null && waitPakinLog.getAnfme() != null) {
+ return waitPakinLog.getAnfme();
+ }
+ return total;
+ }
+
+ private BigDecimal resolveInboundWeight(WrkMastLog wrkMastLog, WaitPakinLog waitPakinLog) {
+ BigDecimal baseWeight = BigDecimal.ZERO;
+ if (wrkMastLog != null && wrkMastLog.getScWeight() != null) {
+ baseWeight = BigDecimal.valueOf(wrkMastLog.getScWeight());
+ } else if (waitPakinLog != null && waitPakinLog.getWeight() != null) {
+ baseWeight = BigDecimal.valueOf(waitPakinLog.getWeight());
+ }
+ Integer sourceStaNo = resolveInboundSourceStaNo(wrkMastLog);
+ return baseWeight.multiply(resolveInboundWeightFactor(sourceStaNo));
+ }
+
+ private Integer resolveInboundSourceStaNo(WrkMastLog wrkMastLog) {
+ if (wrkMastLog == null) {
+ return null;
+ }
+ if (wrkMastLog.getSourceStaNo() != null) {
+ return wrkMastLog.getSourceStaNo();
+ }
+ return wrkMastLog.getStaNo();
+ }
+
+ private BigDecimal resolveInboundWeightFactor(Integer sourceStaNo) {
+ if (sourceStaNo == null) {
+ return BigDecimal.ONE;
+ }
+ BigDecimal factor = INBOUND_WEIGHT_FACTOR_BY_SOURCE_STA.get(sourceStaNo);
+ return factor == null ? BigDecimal.ONE : factor;
+ }
+
+ private String resolveInboundLocNo(WrkMastLog wrkMastLog, WaitPakinLog waitPakinLog) {
+ if (wrkMastLog != null && !Cools.isEmpty(wrkMastLog.getLocNo())) {
+ return wrkMastLog.getLocNo();
+ }
+ if (waitPakinLog != null && !Cools.isEmpty(waitPakinLog.getLocNo())) {
+ return waitPakinLog.getLocNo();
+ }
+ return null;
+ }
+
+ private Date resolveInboundCreateTime(WrkMastLog wrkMastLog) {
+ if (wrkMastLog == null) {
+ return new Date();
+ }
+ if (wrkMastLog.getCrnEndTime() != null) {
+ return wrkMastLog.getCrnEndTime();
+ }
+ if (wrkMastLog.getModiTime() != null) {
+ return wrkMastLog.getModiTime();
+ }
+ if (wrkMastLog.getIoTime() != null) {
+ return wrkMastLog.getIoTime();
+ }
+ return new Date();
+ }
+
+ private Date resolveInboundStartTime(WrkMastLog wrkMastLog, WaitPakinLog waitPakinLog) {
+ if (waitPakinLog != null && waitPakinLog.getAppeTime() != null) {
+ return waitPakinLog.getAppeTime();
+ }
+ if (wrkMastLog == null) {
+ return new Date();
+ }
+ if (wrkMastLog.getAppeTime() != null) {
+ return wrkMastLog.getAppeTime();
+ }
+ if (wrkMastLog.getIoTime() != null) {
+ return wrkMastLog.getIoTime();
+ }
+ if (wrkMastLog.getModiTime() != null) {
+ return wrkMastLog.getModiTime();
+ }
+ return new Date();
+ }
+
+ private String resolveInboundBizNo(List<WrkDetlLog> wrkDetlLogs, WaitPakinLog waitPakinLog) {
+ if (wrkDetlLogs != null) {
+ for (WrkDetlLog wrkDetlLog : wrkDetlLogs) {
+ if (wrkDetlLog != null && !Cools.isEmpty(wrkDetlLog.getThreeCode())) {
+ return wrkDetlLog.getThreeCode();
+ }
+ }
+ }
+ if (waitPakinLog != null && !Cools.isEmpty(waitPakinLog.getThreeCode())) {
+ return waitPakinLog.getThreeCode();
+ }
+ return null;
+ }
+
+ private String transformInboundLocId(String locId) {
+ if (Cools.isEmpty(locId)) {
+ return null;
+ }
+ String trimmed = locId.trim();
+ if (trimmed.length() < 7) {
+ return trimmed;
+ }
+ String row = trimmed.substring(0, 2);
+ String col = trimmed.substring(2, 5);
+ String lev = trimmed.substring(5, 7);
+ try {
+ int rowNo = Integer.parseInt(row);
+ if (rowNo >= 37) {
+ row = "C" + row;
+ } else if (rowNo >= 13) {
+ row = "B" + row;
+ } else {
+ row = "A" + row;
+ }
+ } catch (Exception ignore) {
+ return trimmed;
+ }
+ return row + "-" + col + "-" + lev;
+ }
+
+ private String formatDate(Date date) {
+ if (date == null) {
+ return null;
+ }
+ return new SimpleDateFormat(DATE_TIME_PATTERN).format(date);
+ }
+
+ private String buildErpInboundRequestUrl() {
+ if (Cools.isEmpty(erpUrl)) {
+ return erpInAddress;
+ }
+ if (erpInAddress == null) {
+ return erpUrl;
+ }
+ if (erpUrl.endsWith("/") && erpInAddress.startsWith("/")) {
+ return erpUrl + erpInAddress.substring(1);
+ }
+ if (!erpUrl.endsWith("/") && !erpInAddress.startsWith("/")) {
+ return erpUrl + "/" + erpInAddress;
+ }
+ return erpUrl + erpInAddress;
+ }
+
+ private String buildErpOutErrorRequestUrl() {
+ if (Cools.isEmpty(erpUrl)) {
+ return erpOutErrorAddress;
+ }
+ if (erpOutErrorAddress == null) {
+ return erpUrl;
+ }
+ if (erpUrl.endsWith("/") && erpOutErrorAddress.startsWith("/")) {
+ return erpUrl + erpOutErrorAddress.substring(1);
+ }
+ if (!erpUrl.endsWith("/") && !erpOutErrorAddress.startsWith("/")) {
+ return erpUrl + "/" + erpOutErrorAddress;
+ }
+ return erpUrl + erpOutErrorAddress;
+ }
}
+
--
Gitblit v1.9.1