Compare commits
46
Commits
dev-bms-260826
...
test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab26e6f130 | ||
|
|
134ab4116c | ||
|
|
29cbf80b7b | ||
|
|
0314d80bca | ||
|
|
e59173b3f9 | ||
|
|
b1d92fdda8 | ||
|
|
74dbf674b0 | ||
|
|
ec5afcc5bd | ||
|
|
888a293ebd | ||
|
|
8324d8dc31 | ||
|
|
6ce35b58e4 | ||
|
|
700556ea63 | ||
|
|
100a8d87ac | ||
|
|
b0c9a41f07 | ||
|
|
82d5eda139 | ||
|
|
07393e43ff | ||
|
|
08858f24d6 | ||
|
|
a994241400 | ||
|
|
a9be03961a | ||
|
|
e60f22979c | ||
|
|
84ff45b43d | ||
|
|
892cc30999 | ||
|
|
da76816533 | ||
|
|
b86ab75dcf | ||
|
|
796bb99a8b | ||
|
|
f9a50ae353 | ||
|
|
2094ea31cd | ||
|
|
788bae9a16 | ||
|
|
a65cca1cfa | ||
|
|
cae62e83a5 | ||
|
|
e2da7177f0 | ||
|
|
0b45ad181f | ||
|
|
260ec1f369 | ||
|
|
b28575c34d | ||
|
|
8af8197901 | ||
|
|
12f1d99e02 | ||
|
|
244fbf9759 | ||
|
|
e3826d6333 | ||
|
|
f217d6ec5c | ||
|
|
e58b6cb9ce | ||
|
|
b7cacaeb8d | ||
|
|
715883f1d4 | ||
|
|
139a3a82da | ||
|
|
fcc8102d9e | ||
|
|
7649585715 | ||
|
|
7511d211c6 |
@@ -20,7 +20,7 @@ import java.util.Set;
|
|||||||
* bms财务结算系统feign调用接口
|
* bms财务结算系统feign调用接口
|
||||||
*/
|
*/
|
||||||
// 目标地址由 nacos 配置项 feign.url.mhd-bms-service 指定,未配置时按服务名走服务发现
|
// 目标地址由 nacos 配置项 feign.url.mhd-bms-service 指定,未配置时按服务名走服务发现
|
||||||
@FeignClient(contextId = "BmsService", value = ServiceNameConstants.BMS_SERVICE, url = "http://10.102.192.32:8019",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
@FeignClient(contextId = "BmsService", value = ServiceNameConstants.BMS_SERVICE, url = "http://10.33.0.109:8019",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||||
public interface BmsServiceFeign {
|
public interface BmsServiceFeign {
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import java.util.List;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
// 目标地址由 nacos 配置项 feign.url.mhd-oms-service 指定,未配置时按服务名走服务发现
|
// 目标地址由 nacos 配置项 feign.url.mhd-oms-service 指定,未配置时按服务名走服务发现
|
||||||
@FeignClient(contextId = "OmsService", value = ServiceNameConstants.OMS_SERVICE,url = "http://10.102.192.33:8017",fallbackFactory = RemoteOmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
@FeignClient(contextId = "OmsService", value = ServiceNameConstants.OMS_SERVICE,url = "http://10.33.0.99:8017",fallbackFactory = RemoteOmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||||
public interface OmsServiceFeign {
|
public interface OmsServiceFeign {
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
// 目标地址由 nacos 配置项 feign.url.mhd-wlhy-service 指定,未配置时按服务名走服务发现
|
// 目标地址由 nacos 配置项 feign.url.mhd-wlhy-service 指定,未配置时按服务名走服务发现
|
||||||
@FeignClient(contextId = "commonWlhyServiceFeign",value = ServiceNameConstants.WLHY_SERVICE,url = "http://10.102.192.31:8014",configuration = FeignAutoConfiguration.class)
|
@FeignClient(contextId = "commonWlhyServiceFeign",value = ServiceNameConstants.WLHY_SERVICE,url = "http://10.33.0.129:8014",configuration = FeignAutoConfiguration.class)
|
||||||
public interface WlhyServiceFeign {
|
public interface WlhyServiceFeign {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -545,6 +545,13 @@ public interface WlhyServiceFeign {
|
|||||||
@GetMapping("/feign/order/cancelIssueDocuments")
|
@GetMapping("/feign/order/cancelIssueDocuments")
|
||||||
public AjaxResult cancelIssueDocuments(@RequestParam(name="executeOrderId") String executeOrderId);
|
public AjaxResult cancelIssueDocuments(@RequestParam(name="executeOrderId") String executeOrderId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OMS出库业务单反审联动: 按执行单货源编号软删TMS整条链路(货源单->运单->运输作业单, is_delete=1), 幂等,
|
||||||
|
* 与 cancelIssueDocuments(仅关闭货源单且有有效运单即拒绝)不同, 反审口径不留TMS数据
|
||||||
|
*/
|
||||||
|
@PostMapping("/feign/order/deleteChainByExecuteOrderNumber")
|
||||||
|
public AjaxResult deleteChainByExecuteOrderNumber(@RequestParam(name="executeOrderNumber") String executeOrderNumber);
|
||||||
|
|
||||||
@GetMapping(value = "/ntocc/tmsSettlementExchangeRate/queryList")
|
@GetMapping(value = "/ntocc/tmsSettlementExchangeRate/queryList")
|
||||||
public AjaxResult<?> queryList(@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
|
public AjaxResult<?> queryList(@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
|
||||||
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
|
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ import java.util.Map;
|
|||||||
* @description: TODO
|
* @description: TODO
|
||||||
* @date 2024/5/10 8:58
|
* @date 2024/5/10 8:58
|
||||||
**/
|
**/
|
||||||
// 目标地址由 nacos 配置项 feign.url.mhd-wms-service 指定,未配置时按服务名走服务发现
|
@FeignClient(contextId = "remoteWmsServiceFeign",value = ServiceNameConstants.WMS_SERVICE,url = "http://10.33.0.109:8016",fallbackFactory = RemoteWmsFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||||
@FeignClient(contextId = "remoteWmsServiceFeign",value = ServiceNameConstants.WMS_SERVICE,url = "http://10.102.192.32:8016",fallbackFactory = RemoteWmsFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
|
||||||
public interface WmsServiceFeign {
|
public interface WmsServiceFeign {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,21 +74,21 @@ public interface WmsServiceFeign {
|
|||||||
*/
|
*/
|
||||||
@ApiOperation("oms下发单保存")
|
@ApiOperation("oms下发单保存")
|
||||||
@PostMapping(value = "/stockOutOrderApi/omsOutOrderAdd")
|
@PostMapping(value = "/stockOutOrderApi/omsOutOrderAdd")
|
||||||
public void omsOutOrderAdd(@RequestBody StockOutOrder stockOutOrder);
|
public AjaxResult omsOutOrderAdd(@RequestBody StockOutOrder stockOutOrder);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* oms下发单明细保存
|
* oms下发单明细保存
|
||||||
*/
|
*/
|
||||||
@ApiOperation("oms下发单明细保存")
|
@ApiOperation("oms下发单明细保存")
|
||||||
@PostMapping(value = "/stockOutOrderApi/omsOutOrderAddDetail")
|
@PostMapping(value = "/stockOutOrderApi/omsOutOrderAddDetail")
|
||||||
public void omsOutOrderAddDetail(@RequestBody List<OutMaterialDetail> outMaterialDetails);
|
public AjaxResult omsOutOrderAddDetail(@RequestBody List<OutMaterialDetail> outMaterialDetails);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* oms下发单明细保存
|
* oms下发单明细保存
|
||||||
*/
|
*/
|
||||||
@ApiOperation("oms下发单明细保存")
|
@ApiOperation("oms下发单明细保存")
|
||||||
@PostMapping(value = "/stockOutOrderApi/omsOutOrderAddDeliveryDetailsLink")
|
@PostMapping(value = "/stockOutOrderApi/omsOutOrderAddDeliveryDetailsLink")
|
||||||
public void omsOutOrderAddDeliveryDetailsLink(@RequestBody List<DeliveryDetailsLink> deliveryDetailsLinks);
|
public AjaxResult omsOutOrderAddDeliveryDetailsLink(@RequestBody List<DeliveryDetailsLink> deliveryDetailsLinks);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,11 +112,46 @@ public interface WmsServiceFeign {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* oms下发前库存校验:可用库存不足时返回错误,OMS据此在推送前拦截下发,
|
* oms下发前库存校验:可用库存不足时返回错误,OMS据此在推送前拦截下发,
|
||||||
* 避免单据先落库、分配才失败而在WMS残留"已审核"状态的垃圾单
|
* 避免单据先落库、分配才失败而在WMS残留"已审核"状态的垃圾单。
|
||||||
|
* businessOrderNumber(可空):OMS出库业务单号,下单冻结模式下校验可用量=库存可用+本单冻结记录量
|
||||||
*/
|
*/
|
||||||
@ApiOperation("oms下发前库存校验")
|
@ApiOperation("oms下发前库存校验")
|
||||||
@PostMapping(value = "/stockOutOrderApi/checkOmsAssignInventory")
|
@PostMapping(value = "/stockOutOrderApi/checkOmsAssignInventory")
|
||||||
public AjaxResult checkOmsAssignInventory(@RequestBody List<OutMaterialDetail> outMaterialDetails);
|
public AjaxResult checkOmsAssignInventory(@RequestBody List<OutMaterialDetail> outMaterialDetails,
|
||||||
|
@RequestParam(value = "businessOrderNumber", required = false) String businessOrderNumber);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* oms出库业务单下单冻结库存(防超卖):WMS按明细冻结库存(可用-、冻结+,库存数量不变),
|
||||||
|
* 条件更新防并发超卖,任一行可用不足整体失败并返回错误;同业务单号幂等。
|
||||||
|
* 冻结失败必须阻断OMS下单(返回非200由OMS整体回滚)。
|
||||||
|
*/
|
||||||
|
@ApiOperation("oms出库业务单下单冻结库存")
|
||||||
|
@PostMapping(value = "/stockOutOrderApi/omsFreezeInventory")
|
||||||
|
public AjaxResult omsFreezeInventory(@RequestBody OmsFreezeInventoryParam param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* oms出库业务单取消/删除-释放冻结库存:可用+、冻结-(库存数量不变);按业务单号幂等;
|
||||||
|
* 冻结已被执行单承接时拒绝(须先取消下发)
|
||||||
|
*/
|
||||||
|
@ApiOperation("oms出库业务单释放冻结库存")
|
||||||
|
@PostMapping(value = "/stockOutOrderApi/omsUnfreezeInventory")
|
||||||
|
public AjaxResult omsUnfreezeInventory(@RequestBody OmsFreezeInventoryParam param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* oms出库业务单编辑-原子重锁:WMS同一事务内先释放原冻结再按新明细重新冻结,
|
||||||
|
* 任一新明细可用不足整体回滚(旧冻结同样还原),满足"先释放再重新锁定"原子性要求
|
||||||
|
*/
|
||||||
|
@ApiOperation("oms出库业务单编辑重锁库存")
|
||||||
|
@PostMapping(value = "/stockOutOrderApi/omsRelockInventory")
|
||||||
|
public AjaxResult omsRelockInventory(@RequestBody OmsFreezeInventoryParam param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仓库是否存在活跃冻结记录(status in 1-业务单冻结 2-执行单承接):
|
||||||
|
* 仓库"库存锁定模式"(stock_lock_mode)切换守卫,存在在途冻结单时禁止切换
|
||||||
|
*/
|
||||||
|
@ApiOperation("仓库是否存在活跃冻结记录")
|
||||||
|
@GetMapping(value = "/stockOutOrderApi/hasActiveFreezeByWarehouse/{warehouseId}")
|
||||||
|
public AjaxResult hasActiveFreezeByWarehouse(@PathVariable("warehouseId") Long warehouseId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* oms下发单明细保存(返回执行结果,明细保存含自动审核链路,失败时OMS据此回滚)
|
* oms下发单明细保存(返回执行结果,明细保存含自动审核链路,失败时OMS据此回滚)
|
||||||
@@ -134,6 +168,27 @@ public interface WmsServiceFeign {
|
|||||||
@PostMapping(value = "/stockInOrderApi/omsCancelInOrder")
|
@PostMapping(value = "/stockInOrderApi/omsCancelInOrder")
|
||||||
public AjaxResult omsCancelInOrder(@RequestBody StockInOrder stockInOrder);
|
public AjaxResult omsCancelInOrder(@RequestBody StockInOrder stockInOrder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* oms出库执行单反审(已出库/已复核单据客户通知回退场景):WMS侧还原实物库存并推送OMS库存、
|
||||||
|
* 逻辑删除出库链路单据、生成1条出库异常【反审记录】;反审原因经 remark 透传。
|
||||||
|
* 已产生下游不可逆数据时返回错误,OMS据此整体回滚。
|
||||||
|
*/
|
||||||
|
@ApiOperation("oms出库执行单反审WMS回退")
|
||||||
|
@PostMapping(value = "/stockOutOrderApi/omsReverseExecutionOrder")
|
||||||
|
public AjaxResult omsReverseExecutionOrder(@RequestBody StockOutOrder stockOutOrder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* oms入库业务单反审:WMS侧一个事务内回退已入库/收货中/上架中单据——
|
||||||
|
* 校验未产生出库(按入库单号+动作"分配库存/出库"查库存调整记录>0即有出库) ->
|
||||||
|
* 按库存行整行回退库存扣减 -> 软删W入库单/W收货单/W上架单全链并打已反审标记
|
||||||
|
* (各页面【已反审】tab按"已软删+已反审"查询归档数据)-> 写1条入库异常【反审记录】(type=3)。
|
||||||
|
* 入参: inOrderNumber=执行入库单号, remark=业务单号+反审原因, updateBy/updateByName=反审人;
|
||||||
|
* WMS无该单数据时幂等返回成功(未下发单反审不产生WMS数据)。
|
||||||
|
*/
|
||||||
|
@ApiOperation("oms入库单反审WMS回退")
|
||||||
|
@PostMapping(value = "/stockInOrderApi/omsReverseInOrder")
|
||||||
|
public AjaxResult omsReverseInOrder(@RequestBody StockInOrder stockInOrder);
|
||||||
|
|
||||||
@ApiOperation("oms查询物料列表")
|
@ApiOperation("oms查询物料列表")
|
||||||
@GetMapping("/materialBaseInfoApi/queryList")
|
@GetMapping("/materialBaseInfoApi/queryList")
|
||||||
public TableDataInfo getMaterialBaseInfoList(MaterialInfoDTO materialInfoDTO);
|
public TableDataInfo getMaterialBaseInfoList(MaterialInfoDTO materialInfoDTO);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
// 目标地址由 nacos 配置项 feign.url.xxl-job-service 指定,未配置时按服务名走服务发现
|
// 目标地址由 nacos 配置项 feign.url.xxl-job-service 指定,未配置时按服务名走服务发现
|
||||||
@FeignClient(contextId = "XxlJobService", value = ServiceNameConstants.XXL_JOB_SERVICE,url = "http://127.0.0.1:8020",fallbackFactory = RemoteXxlJobFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
@FeignClient(contextId = "XxlJobService", value = ServiceNameConstants.XXL_JOB_SERVICE,url = "http://10.33.0.99:8020",fallbackFactory = RemoteXxlJobFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||||
public interface XxlJobServiceFeign {
|
public interface XxlJobServiceFeign {
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import org.springframework.cloud.openfeign.FeignClient;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
// 目标地址由 nacos 配置项 feign.url.mhd-yms-service 指定,未配置时按服务名走服务发现
|
// 目标地址由 nacos 配置项 feign.url.mhd-yms-service 指定,未配置时按服务名走服务发现
|
||||||
@FeignClient(contextId = "YmsService", value = ServiceNameConstants.YMS_SERVICE,url = "http://10.102.192.33:8018",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
@FeignClient(contextId = "YmsService", value = ServiceNameConstants.YMS_SERVICE,url = "http://10.33.0.99:8018",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||||
public interface YmsServiceFeign {
|
public interface YmsServiceFeign {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
package com.mhd.system.api.domain;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OMS出库业务单冻结库存请求参数
|
||||||
|
*
|
||||||
|
* @description OMS下单冻结库存模式(防超卖):OMS出库业务单「下单」即冻结WMS库存
|
||||||
|
* (可用-、冻结+,库存数量不变,库存数量=可用数量+冻结数量);
|
||||||
|
* 「编辑」原子释放旧冻结+按新明细重锁;「取消」释放冻结。
|
||||||
|
* 冻结/解冻以业务单号为幂等键。
|
||||||
|
* @date 2026-09-12
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class OmsFreezeInventoryParam implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty("OMS出库业务单号(幂等键)")
|
||||||
|
private String outOrderNumber;
|
||||||
|
|
||||||
|
@ApiModelProperty("执行出库单号(预留,冻结/解冻/重锁时不使用)")
|
||||||
|
private String executionOrderNumber;
|
||||||
|
|
||||||
|
@ApiModelProperty("库存行ID集合(预留)")
|
||||||
|
private List<Long> materialInventoryIds;
|
||||||
|
|
||||||
|
@ApiModelProperty("出库明细列表(须携带 materialInventoryId 定位库存行)")
|
||||||
|
private List<OutMaterialDetail> details;
|
||||||
|
|
||||||
|
@ApiModelProperty("操作人ID(Feign调用可能无登录上下文时的兜底)")
|
||||||
|
private Long operatorId;
|
||||||
|
|
||||||
|
@ApiModelProperty("操作人姓名(Feign调用可能无登录上下文时的兜底)")
|
||||||
|
private String operatorName;
|
||||||
|
|
||||||
|
@ApiModelProperty("备注")
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -124,6 +124,10 @@ public class WarehouseFeignPO extends BaseVOEntity{
|
|||||||
@Excel(name = "作业模式")
|
@Excel(name = "作业模式")
|
||||||
private String workMode;
|
private String workMode;
|
||||||
|
|
||||||
|
@ApiModelProperty("库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||||
|
@Excel(name = "库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||||
|
private Integer stockLockMode;
|
||||||
|
|
||||||
@ApiModelProperty("订单号生成规则(0-日期累加 1-历史累加)")
|
@ApiModelProperty("订单号生成规则(0-日期累加 1-历史累加)")
|
||||||
@Excel(name = "单号生成规则")
|
@Excel(name = "单号生成规则")
|
||||||
private String orderNoGenerateRule;
|
private String orderNoGenerateRule;
|
||||||
|
|||||||
+6
@@ -556,6 +556,12 @@ public class RemoteWlhyFallbackFactory implements FallbackFactory<WlhyServiceFei
|
|||||||
return AjaxResult.error(cause.getMessage());
|
return AjaxResult.error(cause.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AjaxResult deleteChainByExecuteOrderNumber(String executeOrderNumber) {
|
||||||
|
//返回错误而非静默成功, 让OMS反审事务能感知失败并整体回滚
|
||||||
|
return AjaxResult.error("TMS反审联动软删链路失败(服务降级):" + cause.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AjaxResult<?> queryList(Integer pageNo, Integer pageSize, String pushStatus, String organizationId) {
|
public AjaxResult<?> queryList(Integer pageNo, Integer pageSize, String pushStatus, String organizationId) {
|
||||||
return AjaxResult.error(cause.getMessage());
|
return AjaxResult.error(cause.getMessage());
|
||||||
|
|||||||
+44
-5
@@ -57,16 +57,19 @@ public class RemoteWmsFallbackFactory implements FallbackFactory<WmsServiceFeign
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void omsOutOrderAdd(StockOutOrder stockOutOrder) {
|
public AjaxResult omsOutOrderAdd(StockOutOrder stockOutOrder) {
|
||||||
|
//返回错误而非静默成功: 降级吞掉会让OMS继续走分配, 残留"有单无明细"的垃圾单
|
||||||
|
return AjaxResult.error("WMS下发单保存失败(服务降级):" + cause.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void omsOutOrderAddDetail(List<OutMaterialDetail> outMaterialDetails) {
|
public AjaxResult omsOutOrderAddDetail(List<OutMaterialDetail> outMaterialDetails) {
|
||||||
|
return AjaxResult.error("WMS下发单明细保存失败(服务降级):" + cause.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void omsOutOrderAddDeliveryDetailsLink(List<DeliveryDetailsLink> deliveryDetailsLinks) {
|
public AjaxResult omsOutOrderAddDeliveryDetailsLink(List<DeliveryDetailsLink> deliveryDetailsLinks) {
|
||||||
|
return AjaxResult.error("WMS下发单配送明细保存失败(服务降级):" + cause.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -82,6 +85,12 @@ public class RemoteWmsFallbackFactory implements FallbackFactory<WmsServiceFeign
|
|||||||
return AjaxResult.error("WMS取消下发清理失败:" + cause.getMessage());
|
return AjaxResult.error("WMS取消下发清理失败:" + cause.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AjaxResult omsReverseExecutionOrder(StockOutOrder stockOutOrder) {
|
||||||
|
//返回错误而非静默成功,让OMS反审事务能感知失败并整体回滚
|
||||||
|
return AjaxResult.error("WMS出库执行单反审失败(服务降级):" + cause.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void omsAutoAssign(StockOutOrderDTO stockOutOrderDTO) {
|
public void omsAutoAssign(StockOutOrderDTO stockOutOrderDTO) {
|
||||||
|
|
||||||
@@ -93,11 +102,35 @@ public class RemoteWmsFallbackFactory implements FallbackFactory<WmsServiceFeign
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AjaxResult checkOmsAssignInventory(List<OutMaterialDetail> outMaterialDetails) {
|
public AjaxResult checkOmsAssignInventory(List<OutMaterialDetail> outMaterialDetails, String businessOrderNumber) {
|
||||||
//校验接口本身不可用时返回错误,OMS会阻止下发(不能在WMS状态未知时盲目推送)
|
//校验接口本身不可用时返回错误,OMS会阻止下发(不能在WMS状态未知时盲目推送)
|
||||||
return AjaxResult.error("WMS库存校验服务不可用:" + cause.getMessage());
|
return AjaxResult.error("WMS库存校验服务不可用:" + cause.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AjaxResult omsFreezeInventory(OmsFreezeInventoryParam param) {
|
||||||
|
//返回错误而非静默成功:下单冻结失败必须阻断OMS下单落库(冻结成功与否状态未知时不允许继续)
|
||||||
|
return AjaxResult.error("WMS下单冻结库存失败(服务降级):" + cause.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AjaxResult omsUnfreezeInventory(OmsFreezeInventoryParam param) {
|
||||||
|
//返回错误而非静默成功:解冻失败让OMS取消/删除感知(解冻可重试,WMS解冻幂等)
|
||||||
|
return AjaxResult.error("WMS释放冻结库存失败(服务降级):" + cause.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AjaxResult omsRelockInventory(OmsFreezeInventoryParam param) {
|
||||||
|
//返回错误而非静默成功:编辑重锁是原子操作,失败必须让OMS编辑整体回滚
|
||||||
|
return AjaxResult.error("WMS编辑重锁库存失败(服务降级):" + cause.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AjaxResult hasActiveFreezeByWarehouse(Long warehouseId) {
|
||||||
|
//返回错误(fail-closed):模式切换守卫校验不可用时拒绝切换,避免在途冻结单语义错乱
|
||||||
|
return AjaxResult.error("WMS冻结记录校验服务不可用:" + cause.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AjaxResult omsInOrderAddDetail(List<InMaterialDetail> inMaterialDetails) {
|
public AjaxResult omsInOrderAddDetail(List<InMaterialDetail> inMaterialDetails) {
|
||||||
//返回错误而非静默成功:明细保存(含自动审核)失败必须让OMS感知并回滚下发标记
|
//返回错误而非静默成功:明细保存(含自动审核)失败必须让OMS感知并回滚下发标记
|
||||||
@@ -110,6 +143,12 @@ public class RemoteWmsFallbackFactory implements FallbackFactory<WmsServiceFeign
|
|||||||
return AjaxResult.error("WMS取消下发清理失败:" + cause.getMessage());
|
return AjaxResult.error("WMS取消下发清理失败:" + cause.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AjaxResult omsReverseInOrder(StockInOrder stockInOrder) {
|
||||||
|
//返回错误而非静默成功,让OMS侧反审能感知回退失败并整体回滚(反审可重试,WMS回退幂等)
|
||||||
|
return AjaxResult.error("WMS入库单反审回退失败(服务降级):" + cause.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TableDataInfo getMaterialBaseInfoList(MaterialInfoDTO materialInfoDTO) {
|
public TableDataInfo getMaterialBaseInfoList(MaterialInfoDTO materialInfoDTO) {
|
||||||
TableDataInfo tableDataInfo = new TableDataInfo();
|
TableDataInfo tableDataInfo = new TableDataInfo();
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ public interface BiPlatformRealDataMapper {
|
|||||||
/** 今日作业任务数 */
|
/** 今日作业任务数 */
|
||||||
Long countTodayTasks();
|
Long countTodayTasks();
|
||||||
|
|
||||||
/** 车辆状态统计(今日) */
|
/** 车辆状态统计(今日,含叫号状态合计 callStatusTotal = 已签到+已叫号+已过号) */
|
||||||
RealVehicleStatusPO queryVehicleStatusToday();
|
RealVehicleStatusPO queryVehicleStatusToday();
|
||||||
|
|
||||||
/** 本月任务量 */
|
/** 本月任务量 */
|
||||||
|
|||||||
+3
@@ -19,6 +19,9 @@ public class RealVehicleStatusPO implements Serializable {
|
|||||||
/** 当前等待作业(FLOW_STATUS IN '1','2','3' 已预约/已签到/已叫号) */
|
/** 当前等待作业(FLOW_STATUS IN '1','2','3' 已预约/已签到/已叫号) */
|
||||||
private Long waiting;
|
private Long waiting;
|
||||||
|
|
||||||
|
/** 叫号状态合计:已签到 + 已叫号 + 已过号(FLOW_STATUS IN '2','3','4',预约日期为今天) */
|
||||||
|
private Long callStatusTotal;
|
||||||
|
|
||||||
/** 当前作业中 */
|
/** 当前作业中 */
|
||||||
private Long operatingVehicles;
|
private Long operatingVehicles;
|
||||||
|
|
||||||
|
|||||||
+15
@@ -16,7 +16,9 @@ import com.mhd.bi.domain.biPlatformSurveillance.repository.mapper.BiPlatformSurv
|
|||||||
import com.mhd.bi.domain.biSnapshotSync.repository.mapper.BiSnapshotWriteMapper;
|
import com.mhd.bi.domain.biSnapshotSync.repository.mapper.BiSnapshotWriteMapper;
|
||||||
import com.mhd.bi.domain.biSnapshotSync.support.BiSnapshotWeeklyRandomDataBuilder;
|
import com.mhd.bi.domain.biSnapshotSync.support.BiSnapshotWeeklyRandomDataBuilder;
|
||||||
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.PlatformSurveillanceVO;
|
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.PlatformSurveillanceVO;
|
||||||
|
|
||||||
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.PlatformVehicleItemVO;
|
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.PlatformVehicleItemVO;
|
||||||
|
|
||||||
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.TaskLoadItemVO;
|
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.TaskLoadItemVO;
|
||||||
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.TaskTypeStatisticsVO;
|
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.TaskTypeStatisticsVO;
|
||||||
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.TaskTypeCountItemVO;
|
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.TaskTypeCountItemVO;
|
||||||
@@ -106,9 +108,13 @@ public class BiPlatformRealDataService {
|
|||||||
fp.put("todayReservedVehicles", vo.getVehicleStatus().getTodayReservedVehicles());
|
fp.put("todayReservedVehicles", vo.getVehicleStatus().getTodayReservedVehicles());
|
||||||
fp.put("notCheckedIn", vo.getVehicleStatus().getNotCheckedIn());
|
fp.put("notCheckedIn", vo.getVehicleStatus().getNotCheckedIn());
|
||||||
fp.put("waiting", vo.getVehicleStatus().getWaiting());
|
fp.put("waiting", vo.getVehicleStatus().getWaiting());
|
||||||
|
fp.put("callStatusTotal", vo.getVehicleStatus().getCallStatusTotal());
|
||||||
fp.put("operatingVehicles", vo.getVehicleStatus().getOperatingVehicles());
|
fp.put("operatingVehicles", vo.getVehicleStatus().getOperatingVehicles());
|
||||||
|
|
||||||
|
|
||||||
fp.put("completedVehicles", vo.getVehicleStatus().getCompletedVehicles());
|
fp.put("completedVehicles", vo.getVehicleStatus().getCompletedVehicles());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vo.getMonthlyTaskStatistics() != null) {
|
if (vo.getMonthlyTaskStatistics() != null) {
|
||||||
fp.put("taskTotal", vo.getMonthlyTaskStatistics().getTaskTotal());
|
fp.put("taskTotal", vo.getMonthlyTaskStatistics().getTaskTotal());
|
||||||
fp.put("avgTaskDuration", vo.getMonthlyTaskStatistics().getAvgTaskDuration());
|
fp.put("avgTaskDuration", vo.getMonthlyTaskStatistics().getAvgTaskDuration());
|
||||||
@@ -226,6 +232,7 @@ public class BiPlatformRealDataService {
|
|||||||
if (po.getTodayReservedVehicles() == null) po.setTodayReservedVehicles(0L);
|
if (po.getTodayReservedVehicles() == null) po.setTodayReservedVehicles(0L);
|
||||||
if (po.getNotCheckedIn() == null) po.setNotCheckedIn(0L);
|
if (po.getNotCheckedIn() == null) po.setNotCheckedIn(0L);
|
||||||
if (po.getWaiting() == null) po.setWaiting(0L);
|
if (po.getWaiting() == null) po.setWaiting(0L);
|
||||||
|
if (po.getCallStatusTotal() == null) po.setCallStatusTotal(0L);
|
||||||
if (po.getOperatingVehicles() == null) po.setOperatingVehicles(0L);
|
if (po.getOperatingVehicles() == null) po.setOperatingVehicles(0L);
|
||||||
if (po.getCompletedVehicles() == null) po.setCompletedVehicles(0L);
|
if (po.getCompletedVehicles() == null) po.setCompletedVehicles(0L);
|
||||||
return po;
|
return po;
|
||||||
@@ -235,10 +242,13 @@ public class BiPlatformRealDataService {
|
|||||||
po.setTodayReservedVehicles(0L);
|
po.setTodayReservedVehicles(0L);
|
||||||
po.setNotCheckedIn(0L);
|
po.setNotCheckedIn(0L);
|
||||||
po.setWaiting(0L);
|
po.setWaiting(0L);
|
||||||
|
po.setCallStatusTotal(0L);
|
||||||
po.setOperatingVehicles(0L);
|
po.setOperatingVehicles(0L);
|
||||||
po.setCompletedVehicles(0L);
|
po.setCompletedVehicles(0L);
|
||||||
return po;
|
return po;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long countMonthTasks() {
|
public long countMonthTasks() {
|
||||||
@@ -404,10 +414,15 @@ public class BiPlatformRealDataService {
|
|||||||
template.getVehicleStatus().setTodayReservedVehicles(vs.getTodayReservedVehicles().intValue());
|
template.getVehicleStatus().setTodayReservedVehicles(vs.getTodayReservedVehicles().intValue());
|
||||||
template.getVehicleStatus().setNotCheckedIn(vs.getNotCheckedIn().intValue());
|
template.getVehicleStatus().setNotCheckedIn(vs.getNotCheckedIn().intValue());
|
||||||
template.getVehicleStatus().setWaiting(vs.getWaiting().intValue());
|
template.getVehicleStatus().setWaiting(vs.getWaiting().intValue());
|
||||||
|
// 叫号状态合计:已签到 + 已叫号 + 已过号(一个字段表示三个状态的总数量)
|
||||||
|
template.getVehicleStatus().setCallStatusTotal(vs.getCallStatusTotal().intValue());
|
||||||
template.getVehicleStatus().setOperatingVehicles(vs.getOperatingVehicles().intValue());
|
template.getVehicleStatus().setOperatingVehicles(vs.getOperatingVehicles().intValue());
|
||||||
|
|
||||||
|
|
||||||
template.getVehicleStatus().setCompletedVehicles(vs.getCompletedVehicles().intValue());
|
template.getVehicleStatus().setCompletedVehicles(vs.getCompletedVehicles().intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 本月任务统计:taskTotal / avgTaskDuration / taskCompletionRate 均有真实数据源,无条件覆盖;YoY 保留模板写死值
|
// 本月任务统计:taskTotal / avgTaskDuration / taskCompletionRate 均有真实数据源,无条件覆盖;YoY 保留模板写死值
|
||||||
if (template.getMonthlyTaskStatistics() != null) {
|
if (template.getMonthlyTaskStatistics() != null) {
|
||||||
template.getMonthlyTaskStatistics().setTaskTotal((int) countMonthTasks());
|
template.getMonthlyTaskStatistics().setTaskTotal((int) countMonthTasks());
|
||||||
|
|||||||
+3
-1
@@ -87,7 +87,9 @@ public final class BiReportSnapshotMapperSupport {
|
|||||||
.platformOverview(PlatformOverviewVO.builder()
|
.platformOverview(PlatformOverviewVO.builder()
|
||||||
.totalPlatforms(0).currentOccupancyRate(0d).operatingPlatforms(0).todayTasks(0).build())
|
.totalPlatforms(0).currentOccupancyRate(0d).operatingPlatforms(0).todayTasks(0).build())
|
||||||
.vehicleStatus(VehicleStatusVO.builder()
|
.vehicleStatus(VehicleStatusVO.builder()
|
||||||
.todayReservedVehicles(0).notCheckedIn(0).waiting(0).operatingVehicles(0).completedVehicles(0).build())
|
.todayReservedVehicles(0).notCheckedIn(0).waiting(0)
|
||||||
|
.callStatusTotal(0)
|
||||||
|
.operatingVehicles(0).completedVehicles(0).build())
|
||||||
.avgOperationDurationTrend(Collections.emptyList())
|
.avgOperationDurationTrend(Collections.emptyList())
|
||||||
.monthlyTaskStatistics(MonthlyTaskStatisticsVO.builder()
|
.monthlyTaskStatistics(MonthlyTaskStatisticsVO.builder()
|
||||||
.taskTotal(0).taskTotalYoY(0).avgTaskDuration(0d).avgDurationYoY(0)
|
.taskTotal(0).taskTotalYoY(0).avgTaskDuration(0d).avgDurationYoY(0)
|
||||||
|
|||||||
+5
@@ -206,8 +206,13 @@ public class BiReportSnapshotSyncService {
|
|||||||
template.getVehicleStatus().setTodayReservedVehicles(vs.getTodayReservedVehicles().intValue());
|
template.getVehicleStatus().setTodayReservedVehicles(vs.getTodayReservedVehicles().intValue());
|
||||||
template.getVehicleStatus().setNotCheckedIn(vs.getNotCheckedIn().intValue());
|
template.getVehicleStatus().setNotCheckedIn(vs.getNotCheckedIn().intValue());
|
||||||
template.getVehicleStatus().setWaiting(vs.getWaiting().intValue());
|
template.getVehicleStatus().setWaiting(vs.getWaiting().intValue());
|
||||||
|
// 叫号状态合计:已签到 + 已叫号 + 已过号(一个字段表示三个状态的总数量)
|
||||||
|
template.getVehicleStatus().setCallStatusTotal(vs.getCallStatusTotal() == null ? 0 : vs.getCallStatusTotal().intValue());
|
||||||
template.getVehicleStatus().setOperatingVehicles(vs.getOperatingVehicles().intValue());
|
template.getVehicleStatus().setOperatingVehicles(vs.getOperatingVehicles().intValue());
|
||||||
|
|
||||||
|
|
||||||
template.getVehicleStatus().setCompletedVehicles(vs.getCompletedVehicles().intValue());
|
template.getVehicleStatus().setCompletedVehicles(vs.getCompletedVehicles().intValue());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====== 真实数据:本月任务统计 taskTotal / avgTaskDuration / taskCompletionRate ======
|
// ====== 真实数据:本月任务统计 taskTotal / avgTaskDuration / taskCompletionRate ======
|
||||||
|
|||||||
+4
@@ -155,10 +155,13 @@ public final class BiSnapshotWeeklyRandomDataBuilder {
|
|||||||
VehicleStatusVO vs = VehicleStatusVO.builder()
|
VehicleStatusVO vs = VehicleStatusVO.builder()
|
||||||
.todayReservedVehicles(Math.max(0, jitterInt(Baseline.P_RESV, r)))
|
.todayReservedVehicles(Math.max(0, jitterInt(Baseline.P_RESV, r)))
|
||||||
.notCheckedIn(Math.max(0, jitterInt(Baseline.P_NC, r)))
|
.notCheckedIn(Math.max(0, jitterInt(Baseline.P_NC, r)))
|
||||||
|
.waiting(Math.max(0, jitterInt(Baseline.P_WAIT, r)))
|
||||||
|
.callStatusTotal(Math.max(0, jitterInt(Baseline.P_CALL_STATUS_TOTAL, r)))
|
||||||
.operatingVehicles(Math.max(0, jitterInt(Baseline.P_OPV, r)))
|
.operatingVehicles(Math.max(0, jitterInt(Baseline.P_OPV, r)))
|
||||||
.completedVehicles(Math.max(0, jitterInt(Baseline.P_DONE, r)))
|
.completedVehicles(Math.max(0, jitterInt(Baseline.P_DONE, r)))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
List<AvgOperationDurationTrendItemVO> avgTrend = new ArrayList<>();
|
List<AvgOperationDurationTrendItemVO> avgTrend = new ArrayList<>();
|
||||||
for (int i = 0; i < Baseline.P_MO_LABELS.length; i++) {
|
for (int i = 0; i < Baseline.P_MO_LABELS.length; i++) {
|
||||||
avgTrend.add(AvgOperationDurationTrendItemVO.builder()
|
avgTrend.add(AvgOperationDurationTrendItemVO.builder()
|
||||||
@@ -404,6 +407,7 @@ public final class BiSnapshotWeeklyRandomDataBuilder {
|
|||||||
static final int P_RESV = 2_850;
|
static final int P_RESV = 2_850;
|
||||||
static final int P_NC = 920;
|
static final int P_NC = 920;
|
||||||
static final int P_WAIT = 680;
|
static final int P_WAIT = 680;
|
||||||
|
static final int P_CALL_STATUS_TOTAL = 680;
|
||||||
static final int P_OPV = 1_180;
|
static final int P_OPV = 1_180;
|
||||||
static final int P_DONE = 1_420;
|
static final int P_DONE = 1_420;
|
||||||
static final String[] P_MO_LABELS = {"5月", "6月", "7月"};
|
static final String[] P_MO_LABELS = {"5月", "6月", "7月"};
|
||||||
|
|||||||
+3
@@ -23,6 +23,9 @@ public class VehicleStatusVO {
|
|||||||
@ApiModelProperty("当前等待作业(已预约/已签到/已叫号),单位:辆")
|
@ApiModelProperty("当前等待作业(已预约/已签到/已叫号),单位:辆")
|
||||||
private Integer waiting;
|
private Integer waiting;
|
||||||
|
|
||||||
|
@ApiModelProperty("已签到+已叫号+已过号(FLOW_STATUS IN 2/3/4),单位:辆")
|
||||||
|
private Integer callStatusTotal;
|
||||||
|
|
||||||
@ApiModelProperty("当前作业中,单位:辆")
|
@ApiModelProperty("当前作业中,单位:辆")
|
||||||
private Integer operatingVehicles;
|
private Integer operatingVehicles;
|
||||||
|
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ spring:
|
|||||||
# username: nacos
|
# username: nacos
|
||||||
# password: manhuoda@2023
|
# password: manhuoda@2023
|
||||||
#线上正式环境
|
#线上正式环境
|
||||||
server-addr: 10.102.192.30:6848
|
server-addr: 10.102.192.31:6848
|
||||||
username: nacos
|
username: nacos
|
||||||
password: manhuoda@2023
|
password: manhuoda@2023
|
||||||
config:
|
config:
|
||||||
# server-addr: 127.0.0.1:8848
|
# server-addr: 127.0.0.1:8848
|
||||||
# 线上测试环境配置 容器名+端口号
|
# 线上测试环境配置 容器名+端口号
|
||||||
# server-addr: 10.33.0.129:6010
|
# server-addr: 10.33.0.129:6010
|
||||||
server-addr: 10.102.192.30:6848
|
server-addr: 10.102.192.31:6848
|
||||||
username: nacos
|
username: nacos
|
||||||
password: manhuoda@2023
|
password: manhuoda@2023
|
||||||
#线上正式环境
|
#线上正式环境
|
||||||
|
|||||||
@@ -70,17 +70,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<!-- 车辆状态统计:今日的统计(按创建时间 CREATE_TIME / 流程状态)
|
<!-- 车辆状态统计:今日的统计(按创建时间 CREATE_TIME / 流程状态)
|
||||||
todayReservedVehicles 改为按预约日期 TARGET_DATE(其它 3 项保持 CREATE_TIME)
|
todayReservedVehicles 改为按预约日期 TARGET_DATE(其它 3 项保持 CREATE_TIME)
|
||||||
waiting 等待作业:FLOW_STATUS IN ('1','2','3') 已预约/已签到/已叫号 且 预约日期 = 今天
|
waiting 等待作业:FLOW_STATUS IN ('1','2','3') 已预约/已签到/已叫号 且 预约日期 = 今天
|
||||||
|
callStatusTotal 叫号状态合计:已签到 + 已叫号 + 已过号 = FLOW_STATUS IN ('2','3','4') 且 预约日期 = 今天
|
||||||
FLOW_STATUS: 1已预约 2已签到 3已叫号 4已过号 5已进场 6已称重 7已装车 8二次称重 9已结算 10已还卡 11已出场 12已取消 -->
|
FLOW_STATUS: 1已预约 2已签到 3已叫号 4已过号 5已进场 6已称重 7已装车 8二次称重 9已结算 10已还卡 11已出场 12已取消 -->
|
||||||
<select id="queryVehicleStatusToday" resultType="com.mhd.bi.domain.biPlatformRealData.repository.po.RealVehicleStatusPO">
|
<select id="queryVehicleStatusToday" resultType="com.mhd.bi.domain.biPlatformRealData.repository.po.RealVehicleStatusPO">
|
||||||
SELECT
|
SELECT
|
||||||
SUM(CASE WHEN TRUNC(TARGET_DATE) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS todayReservedVehicles,
|
SUM(CASE WHEN TRUNC(TARGET_DATE) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS todayReservedVehicles,
|
||||||
SUM(CASE WHEN FLOW_STATUS = '1' AND TRUNC(CREATE_TIME) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS notCheckedIn,
|
SUM(CASE WHEN FLOW_STATUS = '1' AND TRUNC(CREATE_TIME) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS notCheckedIn,
|
||||||
SUM(CASE WHEN FLOW_STATUS IN ('1','2','3') AND TRUNC(TARGET_DATE) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS waiting,
|
SUM(CASE WHEN FLOW_STATUS IN ('1','2','3') AND TRUNC(TARGET_DATE) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS waiting,
|
||||||
|
SUM(CASE WHEN FLOW_STATUS IN ('2','3','4') AND TRUNC(TARGET_DATE) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS callStatusTotal,
|
||||||
SUM(CASE WHEN FLOW_STATUS IN ('5','6','7','8','9','10') AND TRUNC(CREATE_TIME) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS operatingVehicles,
|
SUM(CASE WHEN FLOW_STATUS IN ('5','6','7','8','9','10') AND TRUNC(CREATE_TIME) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS operatingVehicles,
|
||||||
SUM(CASE WHEN FLOW_STATUS = '11' AND TRUNC(CREATE_TIME) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS completedVehicles
|
SUM(CASE WHEN FLOW_STATUS = '11' AND TRUNC(CREATE_TIME) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS completedVehicles
|
||||||
FROM NGWL_TEST_YMS.QMS_MAIN_BUSINESS
|
FROM NGWL_TEST_YMS.QMS_MAIN_BUSINESS
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- 本月任务量:按预约日期 TARGET_DATE(Y预约日期)统计本月预约总数量 -->
|
<!-- 本月任务量:按预约日期 TARGET_DATE(Y预约日期)统计本月预约总数量 -->
|
||||||
<select id="countMonthTasks" resultType="java.lang.Long">
|
<select id="countMonthTasks" resultType="java.lang.Long">
|
||||||
SELECT COUNT(1)
|
SELECT COUNT(1)
|
||||||
|
|||||||
+3
@@ -35,4 +35,7 @@ public class WarehousePO implements Serializable {
|
|||||||
|
|
||||||
@Excel(name = "是否启用: 1-否 2-是")
|
@Excel(name = "是否启用: 1-否 2-是")
|
||||||
private Integer isEnable;
|
private Integer isEnable;
|
||||||
|
|
||||||
|
@ApiModelProperty("库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||||
|
private Integer stockLockMode;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
// 目标地址由 nacos 配置项 feign.url.mhd-third-party-service 指定,未配置时按服务名走服务发现
|
// 目标地址由 nacos 配置项 feign.url.mhd-third-party-service 指定,未配置时按服务名走服务发现
|
||||||
@FeignClient(value = "mhd-third-party-service",url = "http://10.102.192.32:8012", configuration = FeignAutoConfiguration.class)
|
@FeignClient(value = "mhd-third-party-service",url = "http://10.33.0.129:8012", configuration = FeignAutoConfiguration.class)
|
||||||
public interface ThirdPartyServiceFeign {
|
public interface ThirdPartyServiceFeign {
|
||||||
|
|
||||||
//查询当前组织所有三方接口信息
|
//查询当前组织所有三方接口信息
|
||||||
|
|||||||
+70
@@ -12,7 +12,9 @@ import com.mhd.basic.domain.warehouse.repository.todo.WarehouseDO;
|
|||||||
import com.mhd.basic.domain.warehouse.service.WarehouseDomainService;
|
import com.mhd.basic.domain.warehouse.service.WarehouseDomainService;
|
||||||
import com.mhd.common.core.enums.DictCode;
|
import com.mhd.common.core.enums.DictCode;
|
||||||
import com.mhd.common.core.exception.ServiceException;
|
import com.mhd.common.core.exception.ServiceException;
|
||||||
|
import com.mhd.common.core.web.domain.AjaxResult;
|
||||||
import com.mhd.common.security.utils.SecurityUtils;
|
import com.mhd.common.security.utils.SecurityUtils;
|
||||||
|
import com.mhd.system.api.WmsServiceFeign;
|
||||||
import com.mhd.system.api.domain.SysDictData;
|
import com.mhd.system.api.domain.SysDictData;
|
||||||
import com.mhd.system.api.model.LoginUser;
|
import com.mhd.system.api.model.LoginUser;
|
||||||
import com.mhd.system.service.ISysDictTypeService;
|
import com.mhd.system.service.ISysDictTypeService;
|
||||||
@@ -43,6 +45,8 @@ public class WarehouseApplicationService {
|
|||||||
private ISysDictTypeService dictTypeService;
|
private ISysDictTypeService dictTypeService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private IAssociationWarehouseService associationWarehouseService;
|
private IAssociationWarehouseService associationWarehouseService;
|
||||||
|
@Autowired
|
||||||
|
private WmsServiceFeign wmsServiceFeign;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -239,12 +243,78 @@ public class WarehouseApplicationService {
|
|||||||
/**
|
/**
|
||||||
* 修改仓库
|
* 修改仓库
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* @description 查询仓库库存锁定模式(OMS下单冻结库存/防超卖功能的仓库级开关)
|
||||||
|
* 1-下单冻结:OMS出库业务单「下单」即冻结WMS库存(可用-、冻结+,库存数量不变)
|
||||||
|
* 2-分配库存冻结:现有模式,WMS「分配库存」时才冻结
|
||||||
|
* 仓库不存在或未配置(null)时返回默认值 2(与代码侧降级口径一致)
|
||||||
|
* @author gen
|
||||||
|
* @date 2026-09-12
|
||||||
|
*/
|
||||||
|
public Integer getStockLockMode(Long warehouseId) {
|
||||||
|
WarehousePO warehousePO = warehouseDomainService.getInfo(warehouseId);
|
||||||
|
return warehousePO == null ? 2
|
||||||
|
: cn.hutool.core.util.ObjectUtil.defaultIfNull(warehousePO.getStockLockMode(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean update(WarehouseDO warehouseDO) {
|
public Boolean update(WarehouseDO warehouseDO) {
|
||||||
|
// 库存锁定模式切换守卫:模式发生变化时,存在在途冻结单(OMS下单冻结未释放)则禁止切换,
|
||||||
|
// 否则在途单的解冻/承接逻辑会按新模式错乱。fail-closed:校验服务不可用时拒绝切换
|
||||||
|
if (warehouseDO.getWarehouseId() != null && warehouseDO.getStockLockMode() != null) {
|
||||||
|
WarehousePO existingPO = warehouseDomainService.getInfo(warehouseDO.getWarehouseId());
|
||||||
|
if (existingPO != null) {
|
||||||
|
checkStockLockModeSwitchable(warehouseDO.getWarehouseId(), warehouseDO.getStockLockMode(), existingPO);
|
||||||
|
}
|
||||||
|
}
|
||||||
//设置数据字典键值
|
//设置数据字典键值
|
||||||
setDataDict(warehouseDO);
|
setDataDict(warehouseDO);
|
||||||
return warehouseDomainService.update(warehouseDO);
|
return warehouseDomainService.update(warehouseDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 单独切换仓库库存锁定模式(不改动仓库其他信息)
|
||||||
|
* 1-下单冻结:OMS出库业务单「下单」即冻结WMS库存
|
||||||
|
* 2-分配库存冻结(默认):WMS「分配库存」时才冻结
|
||||||
|
* 切换守卫与通用编辑一致:模式变化且存在在途冻结单时禁止切换,fail-closed
|
||||||
|
* @author gen
|
||||||
|
* @date 2026-09-14
|
||||||
|
*/
|
||||||
|
public Boolean switchStockLockMode(Long warehouseId, Integer stockLockMode) {
|
||||||
|
if (warehouseId == null) {
|
||||||
|
throw new ServiceException("仓库ID不能为空");
|
||||||
|
}
|
||||||
|
if (stockLockMode == null || (stockLockMode.intValue() != 1 && stockLockMode.intValue() != 2)) {
|
||||||
|
throw new ServiceException("库存锁定模式不合法,仅支持 1-下单冻结 2-分配库存冻结");
|
||||||
|
}
|
||||||
|
WarehousePO existingPO = warehouseDomainService.getInfo(warehouseId);
|
||||||
|
if (existingPO == null) {
|
||||||
|
throw new ServiceException("仓库不存在");
|
||||||
|
}
|
||||||
|
checkStockLockModeSwitchable(warehouseId, stockLockMode, existingPO);
|
||||||
|
return warehouseDomainService.switchStockLockMode(warehouseId, stockLockMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 库存锁定模式切换守卫:模式未变化直接放行;模式变化时查询WMS是否存在
|
||||||
|
* 在途下单冻结单(freeze_status 1/2),存在则禁止切换。
|
||||||
|
* fail-closed:WMS服务不可用/返回异常时一律拒绝切换,防止切换后冻结语义错乱
|
||||||
|
* @author gen
|
||||||
|
* @date 2026-09-14
|
||||||
|
*/
|
||||||
|
private void checkStockLockModeSwitchable(Long warehouseId, Integer newMode, WarehousePO existingPO) {
|
||||||
|
if (newMode.compareTo(cn.hutool.core.util.ObjectUtil.defaultIfNull(existingPO.getStockLockMode(), 2)) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AjaxResult freezeCheck = wmsServiceFeign.hasActiveFreezeByWarehouse(warehouseId);
|
||||||
|
if (freezeCheck == null || !"200".equals(String.valueOf(freezeCheck.get("code")))) {
|
||||||
|
throw new ServiceException("库存锁定模式切换校验失败,请稍后重试:"
|
||||||
|
+ (freezeCheck == null ? "WMS服务不可用" : freezeCheck.get("msg")));
|
||||||
|
}
|
||||||
|
if (Boolean.TRUE.equals(freezeCheck.get("data"))) {
|
||||||
|
throw new ServiceException("该仓库存在下单冻结中的出库单,不可切换库存锁定模式");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 设置数据字典键值
|
* @description 设置数据字典键值
|
||||||
* @author ZhouGY
|
* @author ZhouGY
|
||||||
|
|||||||
+4
@@ -130,6 +130,10 @@ public class Warehouse extends BaseVOEntity{
|
|||||||
@Excel(name = "作业模式")
|
@Excel(name = "作业模式")
|
||||||
private String workMode;
|
private String workMode;
|
||||||
|
|
||||||
|
@ApiModelProperty("库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||||
|
@Excel(name = "库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||||
|
private Integer stockLockMode;
|
||||||
|
|
||||||
@ApiModelProperty("订单号生成规则0日期累加1历史累加")
|
@ApiModelProperty("订单号生成规则0日期累加1历史累加")
|
||||||
@Excel(name = "单号生成规则")
|
@Excel(name = "单号生成规则")
|
||||||
private String orderNoGenerateRule;
|
private String orderNoGenerateRule;
|
||||||
|
|||||||
+4
@@ -132,6 +132,10 @@ public class WarehousePO extends BaseVOEntity{
|
|||||||
@Excel(name = "作业模式")
|
@Excel(name = "作业模式")
|
||||||
private String workMode;
|
private String workMode;
|
||||||
|
|
||||||
|
@ApiModelProperty("库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||||
|
@Excel(name = "库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||||
|
private Integer stockLockMode;
|
||||||
|
|
||||||
|
|
||||||
@ApiModelProperty("订单号生成规则")
|
@ApiModelProperty("订单号生成规则")
|
||||||
@Excel(name = "单号生成规则")
|
@Excel(name = "单号生成规则")
|
||||||
|
|||||||
+4
@@ -140,6 +140,10 @@ public class WarehouseDO extends BaseVOEntity{
|
|||||||
@Excel(name = "作业模式")
|
@Excel(name = "作业模式")
|
||||||
private String workMode;
|
private String workMode;
|
||||||
|
|
||||||
|
@ApiModelProperty("库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||||
|
@Excel(name = "库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||||
|
private Integer stockLockMode;
|
||||||
|
|
||||||
|
|
||||||
@ApiModelProperty("订单号生成规则")
|
@ApiModelProperty("订单号生成规则")
|
||||||
@Excel(name = "单号生成规则")
|
@Excel(name = "单号生成规则")
|
||||||
|
|||||||
+23
@@ -142,6 +142,29 @@ public class WarehouseDomainService {
|
|||||||
.eq(Warehouse::getDelFlag, 1));
|
.eq(Warehouse::getDelFlag, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 单独切换仓库库存锁定模式(仅更新 STOCK_LOCK_MODE 与审计字段,不触碰仓库其他数据)
|
||||||
|
* 1-下单冻结:OMS出库业务单「下单」即冻结WMS库存
|
||||||
|
* 2-分配库存冻结(默认):WMS「分配库存」时才冻结
|
||||||
|
* @author gen
|
||||||
|
* @date 2026-09-14
|
||||||
|
*/
|
||||||
|
public Boolean switchStockLockMode(Long warehouseId, Integer stockLockMode) {
|
||||||
|
Long updateBy = null;
|
||||||
|
try {
|
||||||
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
|
updateBy = loginUser == null ? null : loginUser.getUserid();
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
Long finalUpdateBy = updateBy;
|
||||||
|
return warehouseService.update(new UpdateWrapper<Warehouse>().lambda()
|
||||||
|
.set(Warehouse::getStockLockMode, stockLockMode)
|
||||||
|
.set(finalUpdateBy != null, Warehouse::getUpdateBy, finalUpdateBy)
|
||||||
|
.set(Warehouse::getUpdateTime, new Date())
|
||||||
|
.eq(Warehouse::getWarehouseId, warehouseId)
|
||||||
|
.eq(Warehouse::getDelFlag, 1));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 获取指定仓库下的所有库区及库区下所有库位
|
* @description 获取指定仓库下的所有库区及库区下所有库位
|
||||||
* @author ZhouGY
|
* @author ZhouGY
|
||||||
|
|||||||
+4
@@ -145,6 +145,10 @@ public class WarehouseDTO extends BaseVOEntity{
|
|||||||
@Excel(name = "作业模式")
|
@Excel(name = "作业模式")
|
||||||
private String workMode;
|
private String workMode;
|
||||||
|
|
||||||
|
@ApiModelProperty("库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||||
|
@Excel(name = "库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||||
|
private Integer stockLockMode;
|
||||||
|
|
||||||
|
|
||||||
@ApiModelProperty("订单号生成规则")
|
@ApiModelProperty("订单号生成规则")
|
||||||
@Excel(name = "单号生成规则")
|
@Excel(name = "单号生成规则")
|
||||||
|
|||||||
+27
@@ -104,6 +104,33 @@ public class WarehouseApi extends BaseController{
|
|||||||
return AjaxResult.success(warehouseApplicationService.getInfo(warehouseId));
|
return AjaxResult.success(warehouseApplicationService.getInfo(warehouseId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询仓库库存锁定模式(OMS下单冻结库存/防超卖功能的仓库级开关):
|
||||||
|
* 1-下单冻结(OMS出库业务单「下单」即冻结WMS库存:可用-、冻结+,库存数量不变;
|
||||||
|
* 分配不动数量;编辑=原子重锁;取消=释放;交接=库存-冻结-)
|
||||||
|
* 2-分配库存冻结(默认,现有行为:WMS「分配库存」时才 可用-冻结+)
|
||||||
|
* 仓库不存在或未配置时返回 2(与 OMS/WMS 代码侧降级口径一致)
|
||||||
|
*/
|
||||||
|
@ApiOperation("查询仓库库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||||
|
@GetMapping(value = "/getStockLockMode/{warehouseId}")
|
||||||
|
public AjaxResult getStockLockMode(@PathVariable("warehouseId") Long warehouseId)
|
||||||
|
{
|
||||||
|
return AjaxResult.success(warehouseApplicationService.getStockLockMode(warehouseId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单独切换仓库库存锁定模式(不改动仓库其他信息):
|
||||||
|
* 模式变化且该仓库存在在途下单冻结出库单(freeze_status 1/2)时拒绝切换;
|
||||||
|
* WMS校验服务不可用时同样拒绝(fail-closed)
|
||||||
|
*/
|
||||||
|
@ApiOperation("切换仓库库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||||
|
@PostMapping(value = "/switchStockLockMode/{warehouseId}/{stockLockMode}")
|
||||||
|
public AjaxResult switchStockLockMode(@PathVariable("warehouseId") Long warehouseId,
|
||||||
|
@PathVariable("stockLockMode") Integer stockLockMode)
|
||||||
|
{
|
||||||
|
return toAjax(warehouseApplicationService.switchStockLockMode(warehouseId, stockLockMode));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@ApiOperation("批量作废仓库")
|
@ApiOperation("批量作废仓库")
|
||||||
@GetMapping("/nullifyByIds/{warehouseIds}")
|
@GetMapping("/nullifyByIds/{warehouseIds}")
|
||||||
|
|||||||
@@ -13,16 +13,16 @@ spring:
|
|||||||
cloud:
|
cloud:
|
||||||
nacos:
|
nacos:
|
||||||
discovery:
|
discovery:
|
||||||
server-addr: 127.0.0.1:8848
|
# server-addr: 127.0.0.1:8848
|
||||||
# server-addr: 10.33.0.129:6010
|
server-addr: 10.33.0.129:6010
|
||||||
# server-addr: 10.102.192.31:6848
|
# server-addr: 10.102.192.31:6848
|
||||||
username: nacos
|
username: nacos
|
||||||
password: manhuoda@2023
|
password: manhuoda@2023
|
||||||
#线上正式环境
|
#线上正式环境
|
||||||
# server-addr: 10.102.192.105:6848
|
# server-addr: 10.102.192.105:6848
|
||||||
config:
|
config:
|
||||||
server-addr: 127.0.0.1:8848
|
# server-addr: 127.0.0.1:8848
|
||||||
# server-addr: 10.33.0.129:6010
|
server-addr: 10.33.0.129:6010
|
||||||
# server-addr: 10.102.192.31:6848
|
# server-addr: 10.102.192.31:6848
|
||||||
username: nacos
|
username: nacos
|
||||||
password: manhuoda@2023
|
password: manhuoda@2023
|
||||||
|
|||||||
+2
-1
@@ -43,6 +43,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<result property="orderNoGenerateRule" column="ORDER_NO_GENERATE_RULE" />
|
<result property="orderNoGenerateRule" column="ORDER_NO_GENERATE_RULE" />
|
||||||
<result property="inOrderNoPrefix" column="IN_ORDER_NO_PREFIX" />
|
<result property="inOrderNoPrefix" column="IN_ORDER_NO_PREFIX" />
|
||||||
<result property="outOrderNoPrefix" column="OUT_ORDER_NO_PREFIX" />
|
<result property="outOrderNoPrefix" column="OUT_ORDER_NO_PREFIX" />
|
||||||
|
<result property="stockLockMode" column="STOCK_LOCK_MODE" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
warehouse_type, warehouse_type_name, warehouse_structure, warehouse_nature, warm_layer_code, warm_layer_name,
|
warehouse_type, warehouse_type_name, warehouse_structure, warehouse_nature, warm_layer_code, warm_layer_name,
|
||||||
director_name, director_phone, region_code, region_name, detail_address, longitude, latitude, building_region,
|
director_name, director_phone, region_code, region_name, detail_address, longitude, latitude, building_region,
|
||||||
usage_region, remark, opening_up, nullify, create_time, create_by, create_by_name, update_time, update_by,
|
usage_region, remark, opening_up, nullify, create_time, create_by, create_by_name, update_time, update_by,
|
||||||
update_by_name, del_flag, IS_ENABLE ,WORK_MODE,TASK_MODE,SAMPLE_OUT_ORDER_NO_PREFIX,ORDER_NO_GENERATE_RULE,IN_ORDER_NO_PREFIX,
|
update_by_name, del_flag, IS_ENABLE ,WORK_MODE,TASK_MODE,STOCK_LOCK_MODE,SAMPLE_OUT_ORDER_NO_PREFIX,ORDER_NO_GENERATE_RULE,IN_ORDER_NO_PREFIX,
|
||||||
OUT_ORDER_NO_PREFIX
|
OUT_ORDER_NO_PREFIX
|
||||||
from warehouse
|
from warehouse
|
||||||
</sql>
|
</sql>
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ spring:
|
|||||||
cloud:
|
cloud:
|
||||||
nacos:
|
nacos:
|
||||||
discovery:
|
discovery:
|
||||||
server-addr: 127.0.0.1:8848
|
# server-addr: 127.0.0.1:8848
|
||||||
# server-addr: 10.33.0.129:6010
|
server-addr: 10.33.0.129:6010
|
||||||
# server-addr: 10.102.192.31:6848
|
# server-addr: 10.102.192.31:6848
|
||||||
username: nacos
|
username: nacos
|
||||||
password: manhuoda@2023
|
password: manhuoda@2023
|
||||||
config:
|
config:
|
||||||
server-addr: 127.0.0.1:8848
|
# server-addr: 127.0.0.1:8848
|
||||||
# server-addr: 10.33.0.129:6010
|
server-addr: 10.33.0.129:6010
|
||||||
# server-addr: 10.102.192.31:6848
|
# server-addr: 10.102.192.31:6848
|
||||||
username: nacos
|
username: nacos
|
||||||
password: manhuoda@2023
|
password: manhuoda@2023
|
||||||
|
|||||||
+2
@@ -318,6 +318,7 @@ public class SettlementCustomersApplicationService {
|
|||||||
contractManageDTO.setBillDays(10);
|
contractManageDTO.setBillDays(10);
|
||||||
contractManageDTO.setContractType(1);
|
contractManageDTO.setContractType(1);
|
||||||
AjaxResult ajaxResult = systemServiceFeign.feignSave(contractManageDTO);
|
AjaxResult ajaxResult = systemServiceFeign.feignSave(contractManageDTO);
|
||||||
|
System.out.println(ajaxResult+"==================contractManageDTO");
|
||||||
if (ajaxResult != null && "200".equals(String.valueOf(ajaxResult.get("code")))) {
|
if (ajaxResult != null && "200".equals(String.valueOf(ajaxResult.get("code")))) {
|
||||||
ContractManageFeignDTO contractManageFeignDTO = com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson2.JSONObject.toJSONString(ajaxResult.get("data")), ContractManageFeignDTO.class);
|
ContractManageFeignDTO contractManageFeignDTO = com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson2.JSONObject.toJSONString(ajaxResult.get("data")), ContractManageFeignDTO.class);
|
||||||
Long contractManageId = contractManageFeignDTO.getContractManageId();
|
Long contractManageId = contractManageFeignDTO.getContractManageId();
|
||||||
@@ -333,6 +334,7 @@ public class SettlementCustomersApplicationService {
|
|||||||
contractManageParametersFeign.setOrganizationName(settlementCustomersDO.getOrganizationName());
|
contractManageParametersFeign.setOrganizationName(settlementCustomersDO.getOrganizationName());
|
||||||
contractManageParametersFeign.setTopOrganizationId(settlementCustomersDO.getTopOrganizationId());
|
contractManageParametersFeign.setTopOrganizationId(settlementCustomersDO.getTopOrganizationId());
|
||||||
AjaxResult paramResult = systemServiceFeign.feignSaveContractParameters(contractManageParametersFeign);
|
AjaxResult paramResult = systemServiceFeign.feignSaveContractParameters(contractManageParametersFeign);
|
||||||
|
System.out.println(paramResult+"==================contractManageParametersFeign");
|
||||||
if (paramResult == null || !"200".equals(String.valueOf(paramResult.get("code")))) {
|
if (paramResult == null || !"200".equals(String.valueOf(paramResult.get("code")))) {
|
||||||
throw new ServiceException("保存合同参数失败");
|
throw new ServiceException("保存合同参数失败");
|
||||||
}
|
}
|
||||||
|
|||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package com.mhd.oms.application.service;
|
||||||
|
|
||||||
|
import com.mhd.common.core.exception.ServiceException;
|
||||||
|
import com.mhd.common.core.web.domain.AjaxResult;
|
||||||
|
import com.mhd.oms.domain.reservationStockInOrder.repository.mapper.ReservationStockInOrderMapper;
|
||||||
|
import com.mhd.system.api.SystemServiceFeign;
|
||||||
|
import com.mhd.system.api.domain.WarehouseFeignPO;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仓库信息获取(带本地兜底): 下发/下单冻结/保存等流程原先直接 Feign 调 system 服务取仓库信息,
|
||||||
|
* system 服务未启动/降级/返回非200时整个流程被"获取仓库信息失败"阻断。
|
||||||
|
* 本组件统一收口: 优先 Feign, 失败时回退本地跨库直查 "NGWL_TEST_SYSTEM".WAREHOUSE(与 Feign 同库同表, OMS 库账号可读),
|
||||||
|
* 两侧都取不到才抛错并带出 Feign 真实返回原因。
|
||||||
|
*
|
||||||
|
* @date 2026/9/11
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class WarehouseInfoApplicationService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SystemServiceFeign systemServiceFeign;
|
||||||
|
@Autowired
|
||||||
|
private ReservationStockInOrderMapper reservationStockInOrderMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取仓库信息: Feign 优先, 失败走本地兜底; 两边都取不到抛 ServiceException
|
||||||
|
*
|
||||||
|
* @param warehouseId 仓库id
|
||||||
|
* @return 仓库信息(含编码/名称/单号生成规则/库存锁定模式等)
|
||||||
|
*/
|
||||||
|
public WarehouseFeignPO getWarehouseInfo(Long warehouseId) {
|
||||||
|
if (warehouseId == null) {
|
||||||
|
throw new ServiceException("仓库ID不能为空");
|
||||||
|
}
|
||||||
|
AjaxResult ajaxResult = null;
|
||||||
|
try {
|
||||||
|
ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(warehouseId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("获取仓库信息Feign调用异常, 走本地兜底查询, warehouseId={}, error={}", warehouseId, e.getMessage());
|
||||||
|
}
|
||||||
|
if (ajaxResult != null && "200".equals(String.valueOf(ajaxResult.get("code"))) && ajaxResult.get("data") != null) {
|
||||||
|
return com.alibaba.fastjson.JSON.parseObject(
|
||||||
|
com.alibaba.fastjson2.JSONObject.toJSONString(ajaxResult.get("data")), WarehouseFeignPO.class);
|
||||||
|
}
|
||||||
|
String remoteMsg = ajaxResult == null ? "Feign调用异常" : String.valueOf(ajaxResult.get("msg"));
|
||||||
|
log.warn("获取仓库信息Feign失败(返回: {}), 走本地兜底查询, warehouseId={}", remoteMsg, warehouseId);
|
||||||
|
WarehouseFeignPO local = reservationStockInOrderMapper.getWarehouseInfoFallback(warehouseId);
|
||||||
|
if (local == null) {
|
||||||
|
throw new ServiceException("获取仓库信息失败(Feign返回:" + remoteMsg + "), 且本地未查到仓库[warehouseId="
|
||||||
|
+ warehouseId + "], 请检查仓库是否存在/已启用");
|
||||||
|
}
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
}
|
||||||
+50
-6
@@ -11,6 +11,7 @@ import com.mhd.oms.domain.outEntrustOrder.repository.po.OutEntrustOrderPO;
|
|||||||
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDO;
|
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDO;
|
||||||
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDetailDO;
|
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDetailDO;
|
||||||
import com.mhd.oms.domain.outEntrustOrder.service.OutEntrustOrderDomainService;
|
import com.mhd.oms.domain.outEntrustOrder.service.OutEntrustOrderDomainService;
|
||||||
|
import com.mhd.oms.domain.outEntrustOrder.repository.listener.OutEntrustExcelParseService;
|
||||||
import com.mhd.system.api.SystemServiceFeign;
|
import com.mhd.system.api.SystemServiceFeign;
|
||||||
import com.mhd.system.api.WmsServiceFeign;
|
import com.mhd.system.api.WmsServiceFeign;
|
||||||
import com.mhd.system.api.domain.NoticeMaterialDetailDTO;
|
import com.mhd.system.api.domain.NoticeMaterialDetailDTO;
|
||||||
@@ -20,8 +21,10 @@ import com.mhd.system.api.model.LoginUser;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@@ -46,9 +49,15 @@ public class OutEntrustOrderApplicationService {
|
|||||||
@Resource
|
@Resource
|
||||||
private SystemServiceFeign systemServiceFeign;
|
private SystemServiceFeign systemServiceFeign;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private com.mhd.oms.application.service.WarehouseInfoApplicationService warehouseInfoApplicationService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private WmsServiceFeign wmsServiceFeign;
|
private WmsServiceFeign wmsServiceFeign;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private OutEntrustExcelParseService outEntrustExcelParseService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询出仓委托单列表(不做默认状态过滤,显示全部数据)
|
* 分页查询出仓委托单列表(不做默认状态过滤,显示全部数据)
|
||||||
*/
|
*/
|
||||||
@@ -105,6 +114,45 @@ public class OutEntrustOrderApplicationService {
|
|||||||
return outEntrustOrderDomainService.insert(outEntrustOrderDO);
|
return outEntrustOrderDomainService.insert(outEntrustOrderDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入出仓委托单(模板与WMS出库管理导入一致:前11行表头+第12行起明细)
|
||||||
|
* 解析校验通过后复用新增链路落库:生成CKWT委托单号,状态待审核/未下发
|
||||||
|
*
|
||||||
|
* @return 校验错误信息;空串表示导入成功
|
||||||
|
*/
|
||||||
|
public StringBuilder importOutEntrustOrder(MultipartFile file, Long warehouseId, String warehouseCode, String warehouseName) throws IOException {
|
||||||
|
//解析+校验(客户解析/物料匹配/字典转换/必填项)
|
||||||
|
OutEntrustExcelParseService.ParseResult parseResult =
|
||||||
|
outEntrustExcelParseService.parseOutEntrustOrderExcel(file, warehouseName, warehouseCode, warehouseId);
|
||||||
|
if (parseResult.getErrorMsg().length() > 0) {
|
||||||
|
return parseResult.getErrorMsg();
|
||||||
|
}
|
||||||
|
OutEntrustOrderDO outEntrustOrderDO = parseResult.getOrder();
|
||||||
|
//审计信息(与WMS出库导入一致,导入人=当前登录人)
|
||||||
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
|
if (loginUser != null) {
|
||||||
|
String userRealName;
|
||||||
|
if (loginUser.getUserPo() != null && loginUser.getUserPo().getUserName() != null
|
||||||
|
&& !loginUser.getUserPo().getUserName().trim().isEmpty()) {
|
||||||
|
userRealName = loginUser.getUserPo().getUserName();
|
||||||
|
} else if (loginUser.getRealname() != null && !loginUser.getRealname().trim().isEmpty()) {
|
||||||
|
userRealName = loginUser.getRealname();
|
||||||
|
} else {
|
||||||
|
userRealName = loginUser.getUsername();
|
||||||
|
}
|
||||||
|
Date now = new Date();
|
||||||
|
outEntrustOrderDO.setCreateBy(loginUser.getUserid());
|
||||||
|
outEntrustOrderDO.setCreateByName(userRealName);
|
||||||
|
outEntrustOrderDO.setCreateTime(now);
|
||||||
|
outEntrustOrderDO.setUpdateBy(loginUser.getUserid());
|
||||||
|
outEntrustOrderDO.setUpdateByName(userRealName);
|
||||||
|
outEntrustOrderDO.setUpdateTime(now);
|
||||||
|
}
|
||||||
|
//复用新增链路:必填校验→默认普通出库→仓库校验→组织信息→汇总申报数量→生成CKWT委托单号(待审核/未下发)
|
||||||
|
insert(outEntrustOrderDO);
|
||||||
|
return new StringBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改出仓委托单(仅待审核/已驳回且未下发可修改;已驳回单编辑后状态重置为待审核)
|
* 修改出仓委托单(仅待审核/已驳回且未下发可修改;已驳回单编辑后状态重置为待审核)
|
||||||
*/
|
*/
|
||||||
@@ -352,14 +400,10 @@ public class OutEntrustOrderApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 仓库信息校验并回填编码/名称
|
* 仓库信息校验并回填编码/名称 (Feign失败走本地跨库兜底)
|
||||||
*/
|
*/
|
||||||
private void setWarehouseInfo(OutEntrustOrderDO outEntrustOrderDO) {
|
private void setWarehouseInfo(OutEntrustOrderDO outEntrustOrderDO) {
|
||||||
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(outEntrustOrderDO.getWarehouseId());
|
WarehouseFeignPO warehouseFeignPO = warehouseInfoApplicationService.getWarehouseInfo(outEntrustOrderDO.getWarehouseId());
|
||||||
if (ajaxResult == null || !"200".equals(String.valueOf(ajaxResult.get("code")))) {
|
|
||||||
throw new ServiceException("获取仓库信息失败,请检查仓库是否有效");
|
|
||||||
}
|
|
||||||
WarehouseFeignPO warehouseFeignPO = JSONUtil.toBean(JSONUtil.toJsonStr(ajaxResult.get("data")), WarehouseFeignPO.class);
|
|
||||||
if (warehouseFeignPO == null || Objects.isNull(warehouseFeignPO.getWarehouseId())) {
|
if (warehouseFeignPO == null || Objects.isNull(warehouseFeignPO.getWarehouseId())) {
|
||||||
throw new ServiceException("仓库不存在,请重新选择");
|
throw new ServiceException("仓库不存在,请重新选择");
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -39,8 +39,8 @@ public interface BusinessDocumentOrderMapper extends BaseMapper<BusinessDocument
|
|||||||
String getPushVerifyByOrgId(@Param("organizationId") Long organizationId);
|
String getPushVerifyByOrgId(@Param("organizationId") Long organizationId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统计指定日期前缀(yyMMdd)下已生成的新格式业务单号数量
|
* 查询指定日期前缀(yyMMdd)下已生成的新格式业务单号列表
|
||||||
* 仅统计新格式(长度<=12),过滤历史16位旧数据,保证序号每日从001正确累计
|
* 仅查新格式(长度<=12),过滤历史16位旧数据,供取号器解析当日最大序号
|
||||||
*/
|
*/
|
||||||
int countByGoodsNumberPrefix(@Param("prefix") String prefix);
|
List<String> selectGoodsNumbersByPrefix(@Param("prefix") String prefix);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-5
@@ -237,11 +237,8 @@ public class BusinessDocumentOrderImpl extends ServiceImpl<BusinessDocumentOrder
|
|||||||
subOrder.setRecipientName(user.getUserName());
|
subOrder.setRecipientName(user.getUserName());
|
||||||
|
|
||||||
|
|
||||||
//生成子单货源单号(yyMMdd + 当日顺序号)
|
//生成子单货源单号(yyMMdd + 当日顺序号)并落库, 撞唯一索引自动重新取号重试
|
||||||
String orderCode = goodsNumberGenerator.generateUniqueGoodsNumber(subOrder.getDocumentDate());
|
goodsNumberGenerator.insertWithUniqueGoodsNumber(subOrder);
|
||||||
subOrder.setGoodsNumber(orderCode);
|
|
||||||
|
|
||||||
businessDocumentOrderMapper.insert(subOrder);
|
|
||||||
subOrderCount++; // 增加子单计数
|
subOrderCount++; // 增加子单计数
|
||||||
|
|
||||||
// 保存子单的地址信息到BUSINESS_DOCUMENT_ADDRESS_MULTI表
|
// 保存子单的地址信息到BUSINESS_DOCUMENT_ADDRESS_MULTI表
|
||||||
|
|||||||
+21
-19
@@ -285,24 +285,9 @@ public class BusinessDocumentOrderDomainService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//生成货源单号(yyMMdd + 当日顺序号,每日重置)
|
//生成货源单号并落库见下方 insertWithUniqueGoodsNumber:
|
||||||
String orderCode = goodsNumberGenerator.generateUniqueGoodsNumber(tmsOrder.getDocumentDate());
|
//取号与插单合并为原子重试(撞goods_number唯一索引自动重新取号),
|
||||||
tmsOrder.setGoodsNumber(orderCode);
|
//出库/入库业务单的单号回填也移至插单成功之后,避免插单失败留下脏回填
|
||||||
|
|
||||||
ReservationStockOutOrder reservationStockOutOrder = reservationStockOutOrderMapper.selectOne(new LambdaQueryWrapper<ReservationStockOutOrder>()
|
|
||||||
.eq(ReservationStockOutOrder::getOutOrderNumber, tmsOrder.getInOrderNumber())
|
|
||||||
.eq(ReservationStockOutOrder::getDelFlag, 1));
|
|
||||||
if (reservationStockOutOrder != null){
|
|
||||||
reservationStockOutOrder.setBusinessNumber(tmsOrder.getGoodsNumber());
|
|
||||||
reservationStockOutOrderMapper.updateById(reservationStockOutOrder);
|
|
||||||
}
|
|
||||||
ReservationStockInOrder reservationStockInOrder = reservationStockInOrderMapper.selectOne(new LambdaQueryWrapper<ReservationStockInOrder>()
|
|
||||||
.eq(ReservationStockInOrder::getInOrderNumber, tmsOrder.getInOrderNumber())
|
|
||||||
.eq(ReservationStockInOrder::getDelFlag, 1));
|
|
||||||
if (reservationStockInOrder != null){
|
|
||||||
reservationStockInOrder.setBusinessNumber(tmsOrder.getGoodsNumber());
|
|
||||||
reservationStockInOrderMapper.updateById(reservationStockInOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<BusinessDocumentAddressMultiDTO> loadingAddressList = businessDocumentOrderDO.getLoadingAddressList();//装货地信息集合
|
List<BusinessDocumentAddressMultiDTO> loadingAddressList = businessDocumentOrderDO.getLoadingAddressList();//装货地信息集合
|
||||||
List<BusinessDocumentAddressMultiDTO> unloadAddressList = businessDocumentOrderDO.getUnloadAddressList();//卸货地信息集合
|
List<BusinessDocumentAddressMultiDTO> unloadAddressList = businessDocumentOrderDO.getUnloadAddressList();//卸货地信息集合
|
||||||
@@ -426,7 +411,24 @@ public class BusinessDocumentOrderDomainService {
|
|||||||
//一天后
|
//一天后
|
||||||
tmsOrder.setDocumentDate(businessDocumentOrderDO.getDocumentDate());
|
tmsOrder.setDocumentDate(businessDocumentOrderDO.getDocumentDate());
|
||||||
|
|
||||||
boolean istrue = businessDocumentOrderMapper.insert(tmsOrder) > 0;
|
//生成货源单号(yyMMdd + 当日顺序号,每日重置)并落库, 撞唯一索引自动重新取号重试
|
||||||
|
boolean istrue = goodsNumberGenerator.insertWithUniqueGoodsNumber(tmsOrder);
|
||||||
|
|
||||||
|
//落库成功后将运输业务单号回填至关联的出库/入库业务单
|
||||||
|
ReservationStockOutOrder reservationStockOutOrder = reservationStockOutOrderMapper.selectOne(new LambdaQueryWrapper<ReservationStockOutOrder>()
|
||||||
|
.eq(ReservationStockOutOrder::getOutOrderNumber, tmsOrder.getInOrderNumber())
|
||||||
|
.eq(ReservationStockOutOrder::getDelFlag, 1));
|
||||||
|
if (reservationStockOutOrder != null){
|
||||||
|
reservationStockOutOrder.setBusinessNumber(tmsOrder.getGoodsNumber());
|
||||||
|
reservationStockOutOrderMapper.updateById(reservationStockOutOrder);
|
||||||
|
}
|
||||||
|
ReservationStockInOrder reservationStockInOrder = reservationStockInOrderMapper.selectOne(new LambdaQueryWrapper<ReservationStockInOrder>()
|
||||||
|
.eq(ReservationStockInOrder::getInOrderNumber, tmsOrder.getInOrderNumber())
|
||||||
|
.eq(ReservationStockInOrder::getDelFlag, 1));
|
||||||
|
if (reservationStockInOrder != null){
|
||||||
|
reservationStockInOrder.setBusinessNumber(tmsOrder.getGoodsNumber());
|
||||||
|
reservationStockInOrderMapper.updateById(reservationStockInOrder);
|
||||||
|
}
|
||||||
|
|
||||||
if (hasDetails) {
|
if (hasDetails) {
|
||||||
for (BusinessDeliveryDetailsLinkDTO businessDeliveryDetailsLinkDTO : businessDeliveryDetailsLinkDTOList) {
|
for (BusinessDeliveryDetailsLinkDTO businessDeliveryDetailsLinkDTO : businessDeliveryDetailsLinkDTOList) {
|
||||||
|
|||||||
+89
-3
@@ -5,23 +5,44 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|||||||
import com.mhd.common.core.exception.ServiceException;
|
import com.mhd.common.core.exception.ServiceException;
|
||||||
import com.mhd.oms.domain.businessDocumentOrder.entity.BusinessDocumentOrder;
|
import com.mhd.oms.domain.businessDocumentOrder.entity.BusinessDocumentOrder;
|
||||||
import com.mhd.oms.domain.businessDocumentOrder.repository.mapper.BusinessDocumentOrderMapper;
|
import com.mhd.oms.domain.businessDocumentOrder.repository.mapper.BusinessDocumentOrderMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 业务单据订单(goodsNumber)单号生成器
|
* 业务单据订单(goodsNumber)单号生成器
|
||||||
* 规则: yyMMdd(取自单据日期) + 当日顺序号(每日重置, 3位起步, 超999自动扩位)
|
* 规则: yyMMdd(取自单据日期) + 当日顺序号(每日重置, 3位起步, 超999自动扩位)
|
||||||
* 示例: 260804001、260804999、2608041000
|
* 示例: 260804001、260804999、2608041000
|
||||||
|
*
|
||||||
|
* 唯一性保障(不依赖唯一索引, 三层防线):
|
||||||
|
* 1) 取号: 当日已生成单号 MAX+1 作为基线 + 候选号存在性校验后移收敛
|
||||||
|
* (对齐入库单号修复先例 ReservationStockInOrderDomainService#generateReserveOrderNumber)
|
||||||
|
* 2) 落库: 按日期前缀分段的JVM锁串行化"取号+插单", 消除本实例内并发审核/
|
||||||
|
* 批量取号的check-then-act竞态(多实例部署时本层只覆盖各自实例)
|
||||||
|
* 3) 自检自愈: 插单后立即校验同号数量, >1 说明与并发插入撞号, 给本单重新
|
||||||
|
* 取号改号, 兜住跨实例残余竞态
|
||||||
|
* 若日后补建goods_number唯一索引(见 sql/20260906_oms_business_goods_number_unique.sql),
|
||||||
|
* 撞号将抛DuplicateKeyException由重试逻辑接住, 与上述防线叠加互不冲突
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class GoodsNumberGenerator {
|
public class GoodsNumberGenerator {
|
||||||
|
|
||||||
private static final int MAX_RETRY = 100;
|
private static final int MAX_RETRY = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取号+插单分段锁: 按单号日期前缀(yyMMdd)分段, 同日串行、跨日并行,
|
||||||
|
* 锁对象常驻(每日新增1个, 增量可忽略)
|
||||||
|
*/
|
||||||
|
private static final Map<String, Object> LOCK_STRIPES = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private BusinessDocumentOrderMapper businessDocumentOrderMapper;
|
private BusinessDocumentOrderMapper businessDocumentOrderMapper;
|
||||||
|
|
||||||
@@ -33,8 +54,21 @@ public class GoodsNumberGenerator {
|
|||||||
Date date = (documentDate == null) ? new Date() : documentDate;
|
Date date = (documentDate == null) ? new Date() : documentDate;
|
||||||
String datePart = DateUtil.format(date, "yyMMdd");
|
String datePart = DateUtil.format(date, "yyMMdd");
|
||||||
|
|
||||||
// count 只查一次作为序号基线
|
// 当日已生成单号的最大序号作为基线(MAX+1而非COUNT+1, 防脏数据/序号错位导致回退复用)
|
||||||
long seq = (long) businessDocumentOrderMapper.countByGoodsNumberPrefix(datePart) + 1;
|
List<String> existNumbers = businessDocumentOrderMapper.selectGoodsNumbersByPrefix(datePart);
|
||||||
|
long seq = 0;
|
||||||
|
for (String existNumber : existNumbers) {
|
||||||
|
if (existNumber == null || existNumber.length() <= datePart.length()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String numStr = existNumber.substring(datePart.length());
|
||||||
|
if (!numStr.matches("\\d+")) {
|
||||||
|
continue; // 防脏数据(非纯数字后缀)导致解析失败
|
||||||
|
}
|
||||||
|
seq = Math.max(seq, Long.parseLong(numStr));
|
||||||
|
}
|
||||||
|
seq = seq + 1;
|
||||||
|
|
||||||
int retryCount = 0;
|
int retryCount = 0;
|
||||||
while (retryCount < MAX_RETRY) {
|
while (retryCount < MAX_RETRY) {
|
||||||
String orderCode = datePart + String.format("%03d", seq);
|
String orderCode = datePart + String.format("%03d", seq);
|
||||||
@@ -46,8 +80,60 @@ public class GoodsNumberGenerator {
|
|||||||
if (CollectionUtils.isEmpty(exist)) {
|
if (CollectionUtils.isEmpty(exist)) {
|
||||||
return orderCode;
|
return orderCode;
|
||||||
}
|
}
|
||||||
seq++; // ← 关键:查重失败,序号往后递增找空位,而不是重新count
|
seq++; // ← 关键:查重失败,序号往后递增找空位,而不是重新取基线
|
||||||
}
|
}
|
||||||
throw new ServiceException("生成唯一业务单号失败,已达到最大重试次数:" + MAX_RETRY);
|
throw new ServiceException("生成唯一业务单号失败,已达到最大重试次数:" + MAX_RETRY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成唯一业务单号并落库: JVM分段锁内完成"取号+插单",
|
||||||
|
* 插入撞goods_number唯一索引时自动重新取号重试(索引为可选兜底),
|
||||||
|
* 插单成功后自检同号并自愈改号。
|
||||||
|
* 供业务单创建入口(手动/审核新增、审核拆单、预约确认)统一使用。
|
||||||
|
* @return 是否插入成功(mapper.insert结果)
|
||||||
|
*/
|
||||||
|
public boolean insertWithUniqueGoodsNumber(BusinessDocumentOrder order) {
|
||||||
|
Date date = (order.getDocumentDate() == null) ? new Date() : order.getDocumentDate();
|
||||||
|
String lockKey = DateUtil.format(date, "yyMMdd");
|
||||||
|
synchronized (LOCK_STRIPES.computeIfAbsent(lockKey, k -> new Object())) {
|
||||||
|
int retryCount = 0;
|
||||||
|
while (retryCount < MAX_RETRY) {
|
||||||
|
retryCount++;
|
||||||
|
order.setGoodsNumber(generateUniqueGoodsNumber(order.getDocumentDate()));
|
||||||
|
try {
|
||||||
|
boolean inserted = businessDocumentOrderMapper.insert(order) > 0;
|
||||||
|
// 插单后自检自愈: 无唯一索引时跨实例并发窗口内的撞号双方都能落库,
|
||||||
|
// 同号数量>1说明本单撞号, 给本单重新取号改号, 防止单号重复扩散
|
||||||
|
healIfDuplicated(order);
|
||||||
|
return inserted;
|
||||||
|
} catch (DuplicateKeyException e) {
|
||||||
|
// 已建唯一索引时的兜底: 仅业务单号撞唯一索引时重试, 其他唯一约束冲突原样抛出
|
||||||
|
String message = e.getMessage();
|
||||||
|
if (message == null || !message.toLowerCase().contains("goods_number")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
log.warn("业务单号[{}]撞号, 自动重新取号重试(第{}次): {}", order.getGoodsNumber(), retryCount, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ServiceException("生成唯一业务单号失败,已达到最大重试次数:" + MAX_RETRY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插单后自检自愈: 查同号数量, >1 说明有并发插入撞号(本单为后插入方),
|
||||||
|
* 保持先落库单据单号不变, 重新取号给本单改号
|
||||||
|
*/
|
||||||
|
private void healIfDuplicated(BusinessDocumentOrder order) {
|
||||||
|
List<BusinessDocumentOrder> sameNumber = businessDocumentOrderMapper.selectList(
|
||||||
|
new QueryWrapper<BusinessDocumentOrder>().eq("goods_number", order.getGoodsNumber())
|
||||||
|
);
|
||||||
|
if (sameNumber != null && sameNumber.size() > 1) {
|
||||||
|
String oldNumber = order.getGoodsNumber();
|
||||||
|
String newNumber = generateUniqueGoodsNumber(order.getDocumentDate());
|
||||||
|
order.setGoodsNumber(newNumber);
|
||||||
|
businessDocumentOrderMapper.updateById(order);
|
||||||
|
log.warn("业务单号[{}]与并发插入撞号(无唯一索引兜底), 已自愈改号为[{}], orderId={}",
|
||||||
|
oldNumber, newNumber, order.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -501,6 +501,6 @@ public class ExecutionInMaterialDetailPO extends ExecutionStockInOrderBasePO {
|
|||||||
private BigDecimal insureAmount;
|
private BigDecimal insureAmount;
|
||||||
|
|
||||||
@ApiModelProperty("货物类型:0-物料 1-半成品 2-成品")
|
@ApiModelProperty("货物类型:0-物料 1-半成品 2-成品")
|
||||||
@Excel(name = "货物类型")
|
@Excel(name = "货物类型", readConverterExp = "0=物料,1=半成品,2=成品")
|
||||||
private Integer goodsType;
|
private Integer goodsType;
|
||||||
}
|
}
|
||||||
+16
-1
@@ -1,5 +1,7 @@
|
|||||||
package com.mhd.oms.domain.executionInMaterialDetail.repository.po;
|
package com.mhd.oms.domain.executionInMaterialDetail.repository.po;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.mhd.common.core.annotation.Excel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -15,21 +17,34 @@ import java.util.Date;
|
|||||||
public class ExecutionInMaterialDetailWithOrderPO extends ExecutionInMaterialDetailPO {
|
public class ExecutionInMaterialDetailWithOrderPO extends ExecutionInMaterialDetailPO {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@ApiModelProperty("入库单状态: 1-已创建 2-已审核 3-收货中 4-上架中 5-已入库 6-已取消 7-已关闭")
|
@ApiModelProperty("入库单流转状态(参考): 1-已创建 2-已审核 3-收货中 4-上架中 5-已入库 6-已取消 7-已关闭")
|
||||||
private Integer inOrderStatus;
|
private Integer inOrderStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("入库单展示状态: 0-已创建 2-已审核 3-已驳回 4-已入库(WMS回写status=5时同步/推送创建时置4,前端状态列绑定此字段)")
|
||||||
|
@Excel(name = "单据状态", readConverterExp = "0=已创建,2=已审核,3=已驳回,4=已入库")
|
||||||
|
private Integer issueStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("货物种类(物料分类名称,多个逗号分隔)")
|
||||||
|
@Excel(name = "货物种类")
|
||||||
|
private String materialClassifyName;
|
||||||
|
|
||||||
@ApiModelProperty("入库单审核状态: 1-未审核 2-审核不通过 3-审核通过")
|
@ApiModelProperty("入库单审核状态: 1-未审核 2-审核不通过 3-审核通过")
|
||||||
|
@Excel(name = "入库单审核状态: 1-未审核 2-审核不通过 3-审核通过")
|
||||||
private Integer inOrderAuditStatus;
|
private Integer inOrderAuditStatus;
|
||||||
|
|
||||||
@ApiModelProperty("货主ID")
|
@ApiModelProperty("货主ID")
|
||||||
private Long orderShipperId;
|
private Long orderShipperId;
|
||||||
|
|
||||||
@ApiModelProperty("货主编码")
|
@ApiModelProperty("货主编码")
|
||||||
|
@Excel(name = "货主编码")
|
||||||
private String orderShipperCode;
|
private String orderShipperCode;
|
||||||
|
|
||||||
@ApiModelProperty("货主名称")
|
@ApiModelProperty("货主名称")
|
||||||
|
@Excel(name = "货主名称")
|
||||||
private String orderShipperName;
|
private String orderShipperName;
|
||||||
|
|
||||||
@ApiModelProperty("入库日期")
|
@ApiModelProperty("入库日期")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||||
|
@Excel(name = "入仓日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||||
private Date warehouseDate;
|
private Date warehouseDate;
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -45,6 +45,12 @@ public class ExecutionInMaterialDetailQueryDTO extends BaseVOEntity {
|
|||||||
@ApiModelProperty("入库单类型编码")
|
@ApiModelProperty("入库单类型编码")
|
||||||
private String inOrderTypeCode;
|
private String inOrderTypeCode;
|
||||||
|
|
||||||
|
@ApiModelProperty("入库单类型编码(与inOrderTypeCode同义)")
|
||||||
|
private String orderTypeCode;
|
||||||
|
|
||||||
|
@ApiModelProperty("入库单类型名称(模糊查询)")
|
||||||
|
private String orderTypeName;
|
||||||
|
|
||||||
@ApiModelProperty("入库状态: 1-已创建 2-已审核 3-收货中 4-上架中 5-已入库 6-已取消 7-已关闭")
|
@ApiModelProperty("入库状态: 1-已创建 2-已审核 3-收货中 4-上架中 5-已入库 6-已取消 7-已关闭")
|
||||||
private Integer inStatus;
|
private Integer inStatus;
|
||||||
|
|
||||||
@@ -66,4 +72,10 @@ public class ExecutionInMaterialDetailQueryDTO extends BaseVOEntity {
|
|||||||
|
|
||||||
@ApiModelProperty("物料名称")
|
@ApiModelProperty("物料名称")
|
||||||
private String materialName;
|
private String materialName;
|
||||||
|
|
||||||
|
@ApiModelProperty("实际入仓日期 开始(yyyy-MM-dd,为空按建档时间兜底)")
|
||||||
|
private String warehouseDateStart;
|
||||||
|
|
||||||
|
@ApiModelProperty("实际入仓日期 结束(yyyy-MM-dd,为空按建档时间兜底)")
|
||||||
|
private String warehouseDateEnd;
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -78,6 +78,12 @@ public class QueryOrderOverStockDO extends BaseVOEntity {
|
|||||||
@Excel(name = "入库单类型编码")
|
@Excel(name = "入库单类型编码")
|
||||||
private String inOrderTypeCode;
|
private String inOrderTypeCode;
|
||||||
|
|
||||||
|
@ApiModelProperty("入库单类型编码(与inOrderTypeCode同义)")
|
||||||
|
private String orderTypeCode;
|
||||||
|
|
||||||
|
@ApiModelProperty("入库单类型名称")
|
||||||
|
private String orderTypeName;
|
||||||
|
|
||||||
@ApiModelProperty("入库单类型名称")
|
@ApiModelProperty("入库单类型名称")
|
||||||
@Excel(name = "入库单类型名称")
|
@Excel(name = "入库单类型名称")
|
||||||
private String inOrderTypeName;
|
private String inOrderTypeName;
|
||||||
@@ -137,4 +143,10 @@ public class QueryOrderOverStockDO extends BaseVOEntity {
|
|||||||
@ApiModelProperty("越库单状态 0:无状态 1:处理中 2:越库完成")
|
@ApiModelProperty("越库单状态 0:无状态 1:处理中 2:越库完成")
|
||||||
@Excel(name = "越库单状态 0:无状态 1:处理中 2:越库完成")
|
@Excel(name = "越库单状态 0:无状态 1:处理中 2:越库完成")
|
||||||
private Integer overStockStatus;
|
private Integer overStockStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("实际入仓日期 开始(yyyy-MM-dd,为空按建档时间兜底)")
|
||||||
|
private String warehouseDateStart;
|
||||||
|
|
||||||
|
@ApiModelProperty("实际入仓日期 结束(yyyy-MM-dd,为空按建档时间兜底)")
|
||||||
|
private String warehouseDateEnd;
|
||||||
}
|
}
|
||||||
+17
@@ -85,4 +85,21 @@ public class ExecutionInMaterialDetailApplicationService {
|
|||||||
return inMaterialDetailDomainService.queryInOrderDetailList(queryOrderOverStockDO);
|
return inMaterialDetailDomainService.queryInOrderDetailList(queryOrderOverStockDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入库明细导出(全字段,口径同 queryInOrderDetailList,不分页)
|
||||||
|
*/
|
||||||
|
public List<ExecutionInMaterialDetailWithOrderPO> exportInOrderDetailList(QueryOrderOverStockDO queryOrderOverStockDO) {
|
||||||
|
List<ExecutionInMaterialDetailWithOrderPO> list = queryInOrderDetailList(queryOrderOverStockDO);
|
||||||
|
// 货物种类=挂载分类的父级分类名称:material_classify_name为分类路径(如"食品,饮料"),取倒数第二段;
|
||||||
|
// 仅一级分类(无父级)时取自身名称
|
||||||
|
for (ExecutionInMaterialDetailWithOrderPO po : list) {
|
||||||
|
String names = po.getMaterialClassifyName();
|
||||||
|
if (names != null && names.contains(",")) {
|
||||||
|
String[] parts = names.split(",");
|
||||||
|
po.setMaterialClassifyName(parts[parts.length - 2].trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+3
@@ -518,4 +518,7 @@ public class ExecutionOutMaterialDetail extends BaseVOEntity {
|
|||||||
private String inLineNumGw;
|
private String inLineNumGw;
|
||||||
private String opWhLocGw;
|
private String opWhLocGw;
|
||||||
private String warehouseGw;
|
private String warehouseGw;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审标记: 0-无 1-已反审(业务单反审时软删del_flag=2并打标记)")
|
||||||
|
private Integer reverseFlag;
|
||||||
}
|
}
|
||||||
+17
-1
@@ -1,6 +1,7 @@
|
|||||||
package com.mhd.oms.domain.executionOutMaterialDetail.repository.po;
|
package com.mhd.oms.domain.executionOutMaterialDetail.repository.po;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.mhd.common.core.annotation.Excel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
@@ -18,34 +19,49 @@ import java.util.Date;
|
|||||||
public class ExecutionOutMaterialDetailWithOrderPO extends ExecutionOutMaterialDetailPO {
|
public class ExecutionOutMaterialDetailWithOrderPO extends ExecutionOutMaterialDetailPO {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@ApiModelProperty("出库单状态: 1-已创建 2-已审核 3-部分分配 4-已分配 5-拣货中 6-波次中 7-拣货完成 8-复核中 9-已出库 10-已取消 11-已关闭")
|
@ApiModelProperty("出库单流转状态(参考): 1-已创建 2-已审核 3-部分分配 4-已分配 5-拣货中 6-波次中 7-拣货完成 8-复核中 9-已出库 10-已取消 11-已关闭 12-已复核 13-已交接;WMS出库完成不回写此字段")
|
||||||
private Integer outOrderStatus;
|
private Integer outOrderStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("出库单展示状态: 0-已创建 2-已审核 3-已驳回 4-已出库(WMS出库完成/推送创建时回写,前端状态列绑定此字段)")
|
||||||
|
@Excel(name = "单据状态", readConverterExp = "0=已创建,2=已审核,3=已驳回,4=已出库")
|
||||||
|
private Integer issueStatus;
|
||||||
|
|
||||||
@ApiModelProperty("出库单审核状态: 1-未审核 2-审核不通过 3-审核通过")
|
@ApiModelProperty("出库单审核状态: 1-未审核 2-审核不通过 3-审核通过")
|
||||||
|
@Excel(name = "出库单审核状态: 1-未审核 2-审核不通过 3-审核通过")
|
||||||
private Integer outOrderAuditStatus;
|
private Integer outOrderAuditStatus;
|
||||||
|
|
||||||
@ApiModelProperty("货主ID")
|
@ApiModelProperty("货主ID")
|
||||||
private Long orderShipperId;
|
private Long orderShipperId;
|
||||||
|
|
||||||
@ApiModelProperty("货主编码")
|
@ApiModelProperty("货主编码")
|
||||||
|
@Excel(name = "货主编码")
|
||||||
private String orderShipperCode;
|
private String orderShipperCode;
|
||||||
|
|
||||||
@ApiModelProperty("货主名称")
|
@ApiModelProperty("货主名称")
|
||||||
|
@Excel(name = "货主名称")
|
||||||
private String orderShipperName;
|
private String orderShipperName;
|
||||||
|
|
||||||
@ApiModelProperty("出仓日期")
|
@ApiModelProperty("出仓日期")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@Excel(name = "出仓日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
private Date orderOutboundDate;
|
private Date orderOutboundDate;
|
||||||
|
|
||||||
@ApiModelProperty("出库时间")
|
@ApiModelProperty("出库时间")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@Excel(name = "出库时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
private Date orderOutboundTime;
|
private Date orderOutboundTime;
|
||||||
|
|
||||||
@ApiModelProperty("保额")
|
@ApiModelProperty("保额")
|
||||||
|
@Excel(name = "保额")
|
||||||
private BigDecimal insureAmount;
|
private BigDecimal insureAmount;
|
||||||
|
|
||||||
@ApiModelProperty("货物类型:0-物料 1-半成品 2-成品")
|
@ApiModelProperty("货物类型:0-物料 1-半成品 2-成品")
|
||||||
|
@Excel(name = "货物类型", readConverterExp = "0=物料,1=半成品,2=成品")
|
||||||
private Integer goodsType;
|
private Integer goodsType;
|
||||||
|
|
||||||
|
@ApiModelProperty("货物种类(物料分类名称,多个逗号分隔)")
|
||||||
|
@Excel(name = "货物种类")
|
||||||
|
private String materialClassifyName;
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -45,6 +45,12 @@ public class ExecutionOutMaterialDetailQueryDTO extends BaseVOEntity {
|
|||||||
@ApiModelProperty("出库单类型编码")
|
@ApiModelProperty("出库单类型编码")
|
||||||
private String outOrderTypeCode;
|
private String outOrderTypeCode;
|
||||||
|
|
||||||
|
@ApiModelProperty("出库单类型编码(与outOrderTypeCode同义)")
|
||||||
|
private String orderTypeCode;
|
||||||
|
|
||||||
|
@ApiModelProperty("出库单类型名称(模糊查询)")
|
||||||
|
private String orderTypeName;
|
||||||
|
|
||||||
@ApiModelProperty("出库状态: 1-已创建 2-已审核 3-部分分配 4-已分配 5-拣货中 6-波次中 7-拣货完成 8-复核中 9-已出库 10-已取消 11-已关闭")
|
@ApiModelProperty("出库状态: 1-已创建 2-已审核 3-部分分配 4-已分配 5-拣货中 6-波次中 7-拣货完成 8-复核中 9-已出库 10-已取消 11-已关闭")
|
||||||
private Integer outStatus;
|
private Integer outStatus;
|
||||||
|
|
||||||
@@ -66,4 +72,10 @@ public class ExecutionOutMaterialDetailQueryDTO extends BaseVOEntity {
|
|||||||
|
|
||||||
@ApiModelProperty("物料名称")
|
@ApiModelProperty("物料名称")
|
||||||
private String materialName;
|
private String materialName;
|
||||||
|
|
||||||
|
@ApiModelProperty("实际出仓日期 开始(yyyy-MM-dd,为空按建档时间兜底)")
|
||||||
|
private String outboundDateStart;
|
||||||
|
|
||||||
|
@ApiModelProperty("实际出仓日期 结束(yyyy-MM-dd,为空按建档时间兜底)")
|
||||||
|
private String outboundDateEnd;
|
||||||
}
|
}
|
||||||
|
|||||||
+17
@@ -459,4 +459,21 @@ public class ExecutionStockInOrder extends BaseVOEntity {
|
|||||||
|
|
||||||
@ApiModelProperty("通知单备注")
|
@ApiModelProperty("通知单备注")
|
||||||
private String noticeRemark;
|
private String noticeRemark;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审状态: 0-无 1-已反审")
|
||||||
|
private Integer reverseAuditStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审原因")
|
||||||
|
private String reverseAuditRemark;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人id")
|
||||||
|
private Long reverseAuditBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人姓名")
|
||||||
|
private String reverseAuditByName;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审时间")
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date reverseAuditTime;
|
||||||
}
|
}
|
||||||
+17
@@ -482,4 +482,21 @@ public class ExecutionStockInOrderPO extends BaseVOEntity {
|
|||||||
private String khdm;
|
private String khdm;
|
||||||
@ApiModelProperty("通知单备注")
|
@ApiModelProperty("通知单备注")
|
||||||
private String noticeRemark;
|
private String noticeRemark;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审状态: 0-无 1-已反审")
|
||||||
|
private Integer reverseAuditStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审原因")
|
||||||
|
private String reverseAuditRemark;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人id")
|
||||||
|
private Long reverseAuditBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人姓名")
|
||||||
|
private String reverseAuditByName;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审时间")
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date reverseAuditTime;
|
||||||
}
|
}
|
||||||
+17
@@ -513,4 +513,21 @@ public class ExecutionStockInOrderDO extends BaseVOEntity {
|
|||||||
private String businessPriceWay;
|
private String businessPriceWay;
|
||||||
@ApiModelProperty("通知单备注")
|
@ApiModelProperty("通知单备注")
|
||||||
private String noticeRemark;
|
private String noticeRemark;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审状态: 0-无 1-已反审")
|
||||||
|
private Integer reverseAuditStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审原因")
|
||||||
|
private String reverseAuditRemark;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人id")
|
||||||
|
private Long reverseAuditBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人姓名")
|
||||||
|
private String reverseAuditByName;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审时间")
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date reverseAuditTime;
|
||||||
}
|
}
|
||||||
+17
@@ -510,4 +510,21 @@ public class ExecutionStockInOrderDTO extends ExecutionStockInOrderBaseDTO {
|
|||||||
private String businessPriceWay;
|
private String businessPriceWay;
|
||||||
@ApiModelProperty("通知单备注")
|
@ApiModelProperty("通知单备注")
|
||||||
private String noticeRemark;
|
private String noticeRemark;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审状态: 0-无 1-已反审")
|
||||||
|
private Integer reverseAuditStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审原因")
|
||||||
|
private String reverseAuditRemark;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人id")
|
||||||
|
private Long reverseAuditBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人姓名")
|
||||||
|
private String reverseAuditByName;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审时间")
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date reverseAuditTime;
|
||||||
}
|
}
|
||||||
+18
-1
@@ -263,7 +263,16 @@ public class ExecutionStockInOrderApplicationService {
|
|||||||
*/
|
*/
|
||||||
public ExecutionStockInOrderPO getInfo(Long inOrderId)
|
public ExecutionStockInOrderPO getInfo(Long inOrderId)
|
||||||
{
|
{
|
||||||
ExecutionStockInOrderPO info = stockInOrderDomainService.getInfo(inOrderId);
|
return getInfo(inOrderId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取入库执行单详情(getReverseAuditInfo 归档详情复用)
|
||||||
|
* @param archive true-查询【已反审】tab 归档数据
|
||||||
|
*/
|
||||||
|
public ExecutionStockInOrderPO getInfo(Long inOrderId, boolean archive)
|
||||||
|
{
|
||||||
|
ExecutionStockInOrderPO info = stockInOrderDomainService.getInfo(inOrderId, archive);
|
||||||
String userNcCode = reservationStockInOrderMapper.getUserNcCode(info.getShipperId());
|
String userNcCode = reservationStockInOrderMapper.getUserNcCode(info.getShipperId());
|
||||||
info.setKhdm(userNcCode);
|
info.setKhdm(userNcCode);
|
||||||
List<ExecutionInMaterialDetailPO> materialDetailList = info.getMaterialDetailList();
|
List<ExecutionInMaterialDetailPO> materialDetailList = info.getMaterialDetailList();
|
||||||
@@ -276,6 +285,14 @@ public class ExecutionStockInOrderApplicationService {
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询入库执行单【已反审】tab 归档详情(已软删+已反审标记的数据,含软删明细)
|
||||||
|
*/
|
||||||
|
public ExecutionStockInOrderPO getReverseAuditInfo(Long inOrderId)
|
||||||
|
{
|
||||||
|
return getInfo(inOrderId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+17
@@ -314,14 +314,31 @@ public class ExecutionStockInOrderDomainService {
|
|||||||
* 获取入库单详细信息
|
* 获取入库单详细信息
|
||||||
*/
|
*/
|
||||||
public ExecutionStockInOrderPO getInfo(Long inOrderId)
|
public ExecutionStockInOrderPO getInfo(Long inOrderId)
|
||||||
|
{
|
||||||
|
return getInfo(inOrderId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取入库执行单详情
|
||||||
|
* @param archive true-查询【已反审】tab 归档数据(主单须为已软删+已反审标记,明细放行软删行)
|
||||||
|
*/
|
||||||
|
public ExecutionStockInOrderPO getInfo(Long inOrderId, boolean archive)
|
||||||
{
|
{
|
||||||
ExecutionStockInOrderPO stockInOrderPO = stockInOrderService.getInfo(inOrderId);
|
ExecutionStockInOrderPO stockInOrderPO = stockInOrderService.getInfo(inOrderId);
|
||||||
|
if (archive && (stockInOrderPO.getDelFlag() == null || stockInOrderPO.getDelFlag() != 2
|
||||||
|
|| !Integer.valueOf(1).equals(stockInOrderPO.getReverseAuditStatus()))) {
|
||||||
|
throw new ServiceException("入库执行单非已反审归档数据,无法查看归档详情");
|
||||||
|
}
|
||||||
ExecutionInMaterialDetailDO inMaterialDetailDO = new ExecutionInMaterialDetailDO();
|
ExecutionInMaterialDetailDO inMaterialDetailDO = new ExecutionInMaterialDetailDO();
|
||||||
String inOrderNumber = stockInOrderPO.getInOrderNumber();
|
String inOrderNumber = stockInOrderPO.getInOrderNumber();
|
||||||
List<BusinessDocumentOrder> businessDocumentOrders = businessDocumentOrderMapper.selectList(new LambdaQueryWrapper<BusinessDocumentOrder>().eq(BusinessDocumentOrder::getInOrderNumber, inOrderNumber).notIn(BusinessDocumentOrder::getOrderStatus,4,5));
|
List<BusinessDocumentOrder> businessDocumentOrders = businessDocumentOrderMapper.selectList(new LambdaQueryWrapper<BusinessDocumentOrder>().eq(BusinessDocumentOrder::getInOrderNumber, inOrderNumber).notIn(BusinessDocumentOrder::getOrderStatus,4,5));
|
||||||
stockInOrderPO.setBusinessDocumentOrderDOList(businessDocumentOrders);
|
stockInOrderPO.setBusinessDocumentOrderDOList(businessDocumentOrders);
|
||||||
inMaterialDetailDO.setInOrderNumber(inOrderNumber);
|
inMaterialDetailDO.setInOrderNumber(inOrderNumber);
|
||||||
inMaterialDetailDO.setLevel(1);
|
inMaterialDetailDO.setLevel(1);
|
||||||
|
if (archive) {
|
||||||
|
//归档明细已随主单软删(del_flag=2),放行软删明细行
|
||||||
|
inMaterialDetailDO.setDelFlag(2);
|
||||||
|
}
|
||||||
List<ExecutionInMaterialDetailPO> inMaterialDetailPOList = materialDetailService.queryList(inMaterialDetailDO);
|
List<ExecutionInMaterialDetailPO> inMaterialDetailPOList = materialDetailService.queryList(inMaterialDetailDO);
|
||||||
|
|
||||||
// 先通过入库单号查询收货单列表
|
// 先通过入库单号查询收货单列表
|
||||||
|
|||||||
+3
@@ -516,4 +516,7 @@ public class ExecutionStockOutOrder extends BaseVOEntity {
|
|||||||
@ApiModelProperty("折扣")
|
@ApiModelProperty("折扣")
|
||||||
private BigDecimal discount;
|
private BigDecimal discount;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiModelProperty("反审标记: 0-无 1-已反审(业务单反审时软删del_flag=2并打标记)")
|
||||||
|
private Integer reverseFlag;
|
||||||
}
|
}
|
||||||
+6
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
|||||||
import com.mhd.oms.domain.executionOutMaterialDetail.repository.po.ExecutionOutMaterialDetailPO;
|
import com.mhd.oms.domain.executionOutMaterialDetail.repository.po.ExecutionOutMaterialDetailPO;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.entity.ExecutionStockOutOrder;
|
import com.mhd.oms.domain.executionStockOutOrder.entity.ExecutionStockOutOrder;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderPO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderPO;
|
||||||
|
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderTotalPO;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDO;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -21,6 +22,11 @@ public interface IExecutionStockOutOrderService extends IService<ExecutionStockO
|
|||||||
*/
|
*/
|
||||||
public List<ExecutionStockOutOrderPO> queryList(ExecutionStockOutOrderDO stockOutOrderDO);
|
public List<ExecutionStockOutOrderPO> queryList(ExecutionStockOutOrderDO stockOutOrderDO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表合计:按查询条件全量(不分页)汇总一级明细的计划数量与出库数量
|
||||||
|
*/
|
||||||
|
public ExecutionStockOutOrderTotalPO queryListTotal(ExecutionStockOutOrderDO stockOutOrderDO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增出库单
|
* 新增出库单
|
||||||
*/
|
*/
|
||||||
|
|||||||
+6
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|||||||
import com.mhd.oms.domain.executionOutMaterialDetail.repository.po.ExecutionOutMaterialDetailPO;
|
import com.mhd.oms.domain.executionOutMaterialDetail.repository.po.ExecutionOutMaterialDetailPO;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.entity.ExecutionStockOutOrder;
|
import com.mhd.oms.domain.executionStockOutOrder.entity.ExecutionStockOutOrder;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderPO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderPO;
|
||||||
|
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderTotalPO;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDO;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
//import com.mhd.oms.domain.reservationOutMaterialDetail.repository.po.ExecutionOutMaterialDetailPO;
|
//import com.mhd.oms.domain.reservationOutMaterialDetail.repository.po.ExecutionOutMaterialDetailPO;
|
||||||
@@ -23,6 +24,11 @@ public interface ExecutionStockOutOrderMapper extends BaseMapper<ExecutionStockO
|
|||||||
*/
|
*/
|
||||||
public List<ExecutionStockOutOrderPO> queryList(ExecutionStockOutOrderDO stockOutOrderDO);
|
public List<ExecutionStockOutOrderPO> queryList(ExecutionStockOutOrderDO stockOutOrderDO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表合计:按查询条件全量(不分页)汇总一级明细的计划数量与出库数量
|
||||||
|
*/
|
||||||
|
public ExecutionStockOutOrderTotalPO queryListTotal(ExecutionStockOutOrderDO stockOutOrderDO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 出库单提货单打印-表头
|
* 出库单提货单打印-表头
|
||||||
*/
|
*/
|
||||||
|
|||||||
+10
@@ -6,6 +6,7 @@ import com.mhd.oms.domain.executionStockOutOrder.entity.ExecutionStockOutOrder;
|
|||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.facade.IExecutionStockOutOrderService;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.facade.IExecutionStockOutOrderService;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.mapper.ExecutionStockOutOrderMapper;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.mapper.ExecutionStockOutOrderMapper;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderPO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderPO;
|
||||||
|
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderTotalPO;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDO;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -34,6 +35,15 @@ public class ExecutionStockOutOrderImpl extends ServiceImpl<ExecutionStockOutOrd
|
|||||||
return reservationStockOutOrderMapper.queryList(stockOutOrderDO);
|
return reservationStockOutOrderMapper.queryList(stockOutOrderDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表合计:按查询条件全量(不分页)汇总一级明细的计划数量与出库数量
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ExecutionStockOutOrderTotalPO queryListTotal(ExecutionStockOutOrderDO stockOutOrderDO)
|
||||||
|
{
|
||||||
|
return reservationStockOutOrderMapper.queryListTotal(stockOutOrderDO);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增出库单
|
* 新增出库单
|
||||||
*/
|
*/
|
||||||
|
|||||||
+2
@@ -29,6 +29,8 @@ public class ExecutionStockOutOrderPO extends BaseVOEntity {
|
|||||||
|
|
||||||
@ApiModelProperty("出库单id")
|
@ApiModelProperty("出库单id")
|
||||||
private Long outOrderId;
|
private Long outOrderId;
|
||||||
|
@ApiModelProperty("反审标记: 1-已反审(反审软删归档, 【已反审】tab按 delFlag=2+reverseFlag=1 查询)")
|
||||||
|
private Integer reverseFlag;
|
||||||
@ApiModelProperty("总价")
|
@ApiModelProperty("总价")
|
||||||
@Excel(name = "总价")
|
@Excel(name = "总价")
|
||||||
private BigDecimal amount;
|
private BigDecimal amount;
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package com.mhd.oms.domain.executionStockOutOrder.repository.po;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出库执行单列表合计PO
|
||||||
|
*
|
||||||
|
* <p>当前筛选条件下不分页的全量合计,随分页列表一起返回
|
||||||
|
* (totalPlanQuantity/totalOutboundQuantity),计划数量与出库数量分开计算</p>
|
||||||
|
*
|
||||||
|
* @author rcx
|
||||||
|
* @date 2026-09-14
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value = "出库执行单列表合计对象", description = "出库执行单列表合计响应对象")
|
||||||
|
public class ExecutionStockOutOrderTotalPO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty("计划数量总数(一级明细计划数量之和)")
|
||||||
|
private BigDecimal totalPlanQuantity;
|
||||||
|
|
||||||
|
@ApiModelProperty("出库数量总数(一级明细出库数量之和)")
|
||||||
|
private BigDecimal totalOutboundQuantity;
|
||||||
|
}
|
||||||
+3
@@ -29,6 +29,9 @@ public class ExecutionStockOutOrderDO extends BaseVOEntity {
|
|||||||
@ApiModelProperty("出库单id")
|
@ApiModelProperty("出库单id")
|
||||||
private Long outOrderId;
|
private Long outOrderId;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审标记查询: 1-已反审(与delFlag=2成对传, 查【已反审】tab归档数据)")
|
||||||
|
private Integer reverseFlag;
|
||||||
|
|
||||||
@ApiModelProperty("组织表ID")
|
@ApiModelProperty("组织表ID")
|
||||||
@Excel(name = "组织表ID")
|
@Excel(name = "组织表ID")
|
||||||
private Long organizationId;
|
private Long organizationId;
|
||||||
|
|||||||
+3
@@ -27,6 +27,9 @@ public class ExecutionStockOutOrderDTO extends ExecutionStockOutOrderBaseDTO {
|
|||||||
@ApiModelProperty("出库单id")
|
@ApiModelProperty("出库单id")
|
||||||
private Long outOrderId;
|
private Long outOrderId;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审标记查询: 1-已反审(与delFlag=2成对传, 查【已反审】tab归档数据)")
|
||||||
|
private Integer reverseFlag;
|
||||||
|
|
||||||
@ApiModelProperty("组织表ID")
|
@ApiModelProperty("组织表ID")
|
||||||
@Excel(name = "组织表ID")
|
@Excel(name = "组织表ID")
|
||||||
private Long organizationId;
|
private Long organizationId;
|
||||||
|
|||||||
+14
-5
@@ -26,6 +26,7 @@ import com.mhd.oms.domain.executionStockOutOrder.entity.ExecutionStockOutOrder;
|
|||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.facade.IExecutionStockOutOrderService;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.facade.IExecutionStockOutOrderService;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.mapper.ExecutionStockOutOrderMapper;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.mapper.ExecutionStockOutOrderMapper;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderPO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderPO;
|
||||||
|
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderTotalPO;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDO;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDTO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDTO;
|
||||||
import com.mhd.oms.domain.gwLog.entity.GwLog;
|
import com.mhd.oms.domain.gwLog.entity.GwLog;
|
||||||
@@ -66,6 +67,8 @@ import java.util.stream.Collectors;
|
|||||||
public class ExecutionStockOutOrderApplicationService {
|
public class ExecutionStockOutOrderApplicationService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ExecutionStockOutOrderDomainService stockOutOrderDomainService;
|
private ExecutionStockOutOrderDomainService stockOutOrderDomainService;
|
||||||
|
@Autowired
|
||||||
|
private com.mhd.oms.application.service.WarehouseInfoApplicationService warehouseInfoApplicationService;
|
||||||
// @Autowired
|
// @Autowired
|
||||||
// private ExecutionOutMaterialDetailDomainService outMaterialDetailDomainService;
|
// private ExecutionOutMaterialDetailDomainService outMaterialDetailDomainService;
|
||||||
// @Autowired
|
// @Autowired
|
||||||
@@ -223,6 +226,15 @@ public class ExecutionStockOutOrderApplicationService {
|
|||||||
return stockOutOrderPOS;
|
return stockOutOrderPOS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表合计:按查询条件全量(不分页)汇总一级明细的计划数量与出库数量。
|
||||||
|
* 须在 queryList/queryList1 之后用同一个DO调用:数据权限条件由列表查询原地写入DO,
|
||||||
|
* 合计查询才能继承生效后的筛选;且 PageHelper startPage 只拦截下一条SQL,本查询不会被分页。
|
||||||
|
*/
|
||||||
|
public ExecutionStockOutOrderTotalPO queryListTotal(ExecutionStockOutOrderDO stockOutOrderDO) {
|
||||||
|
return stockOutOrderDomainService.queryListTotal(stockOutOrderDO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增出库单
|
* 新增出库单
|
||||||
@@ -862,11 +874,8 @@ public class ExecutionStockOutOrderApplicationService {
|
|||||||
if (stockOutOrderDO.getWarehouseId() == null) {
|
if (stockOutOrderDO.getWarehouseId() == null) {
|
||||||
throw new ServiceException("仓库ID不能为空");
|
throw new ServiceException("仓库ID不能为空");
|
||||||
}
|
}
|
||||||
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(stockOutOrderDO.getWarehouseId());
|
// Feign失败走本地跨库兜底, 避免保存被"获取仓库信息失败"阻断
|
||||||
if(!"200".equals(String.valueOf(ajaxResult.get("code")))){
|
WarehouseFeignPO warehouseFeignPO = warehouseInfoApplicationService.getWarehouseInfo(stockOutOrderDO.getWarehouseId());
|
||||||
throw new ServiceException("获取仓库信息失败");
|
|
||||||
}
|
|
||||||
WarehouseFeignPO warehouseFeignPO = JSON.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), WarehouseFeignPO.class);
|
|
||||||
stockOutOrderDO.setWarehouseCode(warehouseFeignPO.getWarehouseCode());
|
stockOutOrderDO.setWarehouseCode(warehouseFeignPO.getWarehouseCode());
|
||||||
stockOutOrderDO.setWarehouseName(warehouseFeignPO.getWarehouseName());
|
stockOutOrderDO.setWarehouseName(warehouseFeignPO.getWarehouseName());
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-20
@@ -30,6 +30,7 @@ import com.mhd.oms.domain.executionStockOutOrder.entity.ExecutionStockOutOrder;
|
|||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.facade.IExecutionStockOutOrderService;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.facade.IExecutionStockOutOrderService;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.mapper.ExecutionStockOutOrderMapper;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.mapper.ExecutionStockOutOrderMapper;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderPO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderPO;
|
||||||
|
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderTotalPO;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDO;
|
||||||
import com.mhd.oms.domain.materialBaseInfo.service.MaterialBaseInfoResolveService;
|
import com.mhd.oms.domain.materialBaseInfo.service.MaterialBaseInfoResolveService;
|
||||||
import com.mhd.oms.domain.reservationDeliveryDetailsLink.entity.ReservationDeliveryDetailsLink;
|
import com.mhd.oms.domain.reservationDeliveryDetailsLink.entity.ReservationDeliveryDetailsLink;
|
||||||
@@ -71,6 +72,8 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private IExecutionStockOutOrderService stockOutOrderService;
|
private IExecutionStockOutOrderService stockOutOrderService;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
private com.mhd.oms.application.service.WarehouseInfoApplicationService warehouseInfoApplicationService;
|
||||||
|
@Autowired
|
||||||
private IExecutionOutMaterialDetailService outMaterialDetailService;
|
private IExecutionOutMaterialDetailService outMaterialDetailService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private IReservationDeliveryDetailsLinkService deliveryDetailsLinkService;
|
private IReservationDeliveryDetailsLinkService deliveryDetailsLinkService;
|
||||||
@@ -112,6 +115,13 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
return stockOutOrderService.queryList(stockOutOrderDO);
|
return stockOutOrderService.queryList(stockOutOrderDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表合计:按查询条件全量(不分页)汇总一级明细的计划数量与出库数量
|
||||||
|
*/
|
||||||
|
public ExecutionStockOutOrderTotalPO queryListTotal(ExecutionStockOutOrderDO stockOutOrderDO) {
|
||||||
|
return stockOutOrderService.queryListTotal(stockOutOrderDO);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从单号列表中提取最大序号
|
* 从单号列表中提取最大序号
|
||||||
* 单号格式: prefix + 数字序号(如 RK260807001 中 prefix=RK260807, 序号=001)
|
* 单号格式: prefix + 数字序号(如 RK260807001 中 prefix=RK260807, 序号=001)
|
||||||
@@ -148,14 +158,9 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
public Boolean insert(ExecutionStockOutOrderDO stockOutOrderDO) {
|
public Boolean insert(ExecutionStockOutOrderDO stockOutOrderDO) {
|
||||||
//建单校验:明细数量必须大于0(0数量单下发WMS后自动分配必失败,出库单会卡死在已审核状态)
|
//建单校验:明细数量必须大于0(0数量单下发WMS后自动分配必失败,出库单会卡死在已审核状态)
|
||||||
validateMaterialDetailQuantity(stockOutOrderDO.getMaterialDetailList());
|
validateMaterialDetailQuantity(stockOutOrderDO.getMaterialDetailList());
|
||||||
//设置单号
|
//设置单号(仓库信息: Feign优先, system服务失败/降级时本地跨库兜底)
|
||||||
Long warehouseId = stockOutOrderDO.getWarehouseId();
|
Long warehouseId = stockOutOrderDO.getWarehouseId();
|
||||||
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(warehouseId);
|
WarehouseFeignPO warehouseFeignPO = warehouseInfoApplicationService.getWarehouseInfo(warehouseId);
|
||||||
if (!"200".equals(String.valueOf(ajaxResult.get("code")))) {
|
|
||||||
throw new ServiceException("获取仓库信息失败");
|
|
||||||
}
|
|
||||||
//仓库信息
|
|
||||||
WarehouseFeignPO warehouseFeignPO = com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson2.JSONObject.toJSONString(ajaxResult.get("data")), WarehouseFeignPO.class);
|
|
||||||
String orderNoGenerateRule = warehouseFeignPO.getOrderNoGenerateRule();
|
String orderNoGenerateRule = warehouseFeignPO.getOrderNoGenerateRule();
|
||||||
String prefix = warehouseFeignPO.getOutOrderNoPrefix();
|
String prefix = warehouseFeignPO.getOutOrderNoPrefix();
|
||||||
if ("yang_pin_chu_ku".equals(stockOutOrderDO.getBusinessType())) {
|
if ("yang_pin_chu_ku".equals(stockOutOrderDO.getBusinessType())) {
|
||||||
@@ -218,14 +223,9 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Boolean batchInsert(List<ExecutionStockOutOrderDO> stockOutOrderDOList) {
|
public Boolean batchInsert(List<ExecutionStockOutOrderDO> stockOutOrderDOList) {
|
||||||
for (ExecutionStockOutOrderDO stockOutOrderDO : stockOutOrderDOList){
|
for (ExecutionStockOutOrderDO stockOutOrderDO : stockOutOrderDOList){
|
||||||
//设置单号
|
//设置单号(仓库信息: Feign优先, system服务失败/降级时本地跨库兜底)
|
||||||
Long warehouseId = stockOutOrderDO.getWarehouseId();
|
Long warehouseId = stockOutOrderDO.getWarehouseId();
|
||||||
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(warehouseId);
|
WarehouseFeignPO warehouseFeignPO = warehouseInfoApplicationService.getWarehouseInfo(warehouseId);
|
||||||
if (!"200".equals(String.valueOf(ajaxResult.get("code")))) {
|
|
||||||
throw new ServiceException("获取仓库信息失败");
|
|
||||||
}
|
|
||||||
//仓库信息
|
|
||||||
WarehouseFeignPO warehouseFeignPO = com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson2.JSONObject.toJSONString(ajaxResult.get("data")), WarehouseFeignPO.class);
|
|
||||||
String orderNoGenerateRule = warehouseFeignPO.getOrderNoGenerateRule();
|
String orderNoGenerateRule = warehouseFeignPO.getOrderNoGenerateRule();
|
||||||
String prefix = warehouseFeignPO.getOutOrderNoPrefix();
|
String prefix = warehouseFeignPO.getOutOrderNoPrefix();
|
||||||
if ("yang_pin_chu_ku".equals(stockOutOrderDO.getBusinessType())) {
|
if ("yang_pin_chu_ku".equals(stockOutOrderDO.getBusinessType())) {
|
||||||
@@ -448,8 +448,9 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
BigDecimal freeze = freezeAfterQuantity.subtract(freezeBeforeQuantity);
|
BigDecimal freeze = freezeAfterQuantity.subtract(freezeBeforeQuantity);
|
||||||
BigDecimal allocationQuantity = reservationMaterialInventory.getAllocationQuantity();
|
BigDecimal allocationQuantity = reservationMaterialInventory.getAllocationQuantity();
|
||||||
BigDecimal freezeQuantity = reservationMaterialInventory.getFreezeQuantity();
|
BigDecimal freezeQuantity = reservationMaterialInventory.getFreezeQuantity();
|
||||||
allocationQuantity.add(allocation);
|
// 修复:BigDecimal 不可变,add/subtract 返回新对象必须回赋(原实现未回赋,取消下发的镜像库存还原实际不生效)
|
||||||
freezeQuantity.subtract(freeze);
|
allocationQuantity = (allocationQuantity == null ? BigDecimal.ZERO : allocationQuantity).add(allocation);
|
||||||
|
freezeQuantity = (freezeQuantity == null ? BigDecimal.ZERO : freezeQuantity).subtract(freeze);
|
||||||
reservationMaterialInventory.setAllocationQuantity(allocationQuantity);
|
reservationMaterialInventory.setAllocationQuantity(allocationQuantity);
|
||||||
reservationMaterialInventory.setFreezeQuantity(freezeQuantity);
|
reservationMaterialInventory.setFreezeQuantity(freezeQuantity);
|
||||||
reservationMaterialInventory.setUpdateTime(new Date());
|
reservationMaterialInventory.setUpdateTime(new Date());
|
||||||
@@ -513,7 +514,7 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
checkDetail.setMaterialName(detail.getMaterialName());
|
checkDetail.setMaterialName(detail.getMaterialName());
|
||||||
checkList.add(checkDetail);
|
checkList.add(checkDetail);
|
||||||
}
|
}
|
||||||
AjaxResult checkResult = wmsServiceFeign.checkOmsAssignInventory(checkList);
|
AjaxResult checkResult = wmsServiceFeign.checkOmsAssignInventory(checkList, byId.getBusinessOutOrderNumber());
|
||||||
if (checkResult == null || !"200".equals(String.valueOf(checkResult.get("code")))) {
|
if (checkResult == null || !"200".equals(String.valueOf(checkResult.get("code")))) {
|
||||||
validateErrors.add("出库单[" + byId.getOutOrderNumber() + "]库存校验失败:"
|
validateErrors.add("出库单[" + byId.getOutOrderNumber() + "]库存校验失败:"
|
||||||
+ (checkResult == null ? "WMS服务不可用,已阻止下发" : checkResult.get("msg")));
|
+ (checkResult == null ? "WMS服务不可用,已阻止下发" : checkResult.get("msg")));
|
||||||
@@ -535,6 +536,9 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
try {
|
try {
|
||||||
StockOutOrder stockOutOrder = new StockOutOrder();
|
StockOutOrder stockOutOrder = new StockOutOrder();
|
||||||
BeanUtils.copyProperties(byId, stockOutOrder,"outOrderId");
|
BeanUtils.copyProperties(byId, stockOutOrder,"outOrderId");
|
||||||
|
// 下单冻结模式:WMS单头带上业务单号(字段名不同copyProperties不会复制,必须显式set),
|
||||||
|
// 供WMS分配承接/下发校验做【单据级】冻结记录过滤(双单冻结同行防误消费)
|
||||||
|
stockOutOrder.setBusinessOrderNo(byId.getBusinessOutOrderNumber());
|
||||||
Long warehouseId = stockOutOrder.getWarehouseId();
|
Long warehouseId = stockOutOrder.getWarehouseId();
|
||||||
Long topOrgId = stockInOrderMapper.getTopOrgId(warehouseId);
|
Long topOrgId = stockInOrderMapper.getTopOrgId(warehouseId);
|
||||||
Long orgId = stockInOrderMapper.getOrgId(warehouseId);
|
Long orgId = stockInOrderMapper.getOrgId(warehouseId);
|
||||||
@@ -542,7 +546,12 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
stockOutOrder.setOrganizationId(orgId);
|
stockOutOrder.setOrganizationId(orgId);
|
||||||
stockOutOrder.setOrganizationName(orgName);
|
stockOutOrder.setOrganizationName(orgName);
|
||||||
stockOutOrder.setTopOrganizationId(topOrgId);
|
stockOutOrder.setTopOrganizationId(topOrgId);
|
||||||
wmsServiceFeign.omsOutOrderAdd(stockOutOrder);
|
//单头/配送明细推送同样校验返回值: 降级吞掉会残留"有单无明细"等半截单
|
||||||
|
AjaxResult orderAddResult = wmsServiceFeign.omsOutOrderAdd(stockOutOrder);
|
||||||
|
if (orderAddResult == null || !"200".equals(String.valueOf(orderAddResult.get("code")))) {
|
||||||
|
throw new ServiceException("出库单[" + currentOrderNumber + "]WMS出库单推送失败:"
|
||||||
|
+ (orderAddResult == null ? "WMS服务不可用或已降级" : orderAddResult.get("msg")));
|
||||||
|
}
|
||||||
|
|
||||||
List<ReservationDeliveryDetailsLink> list = deliveryDetailsLinkService.list(new QueryWrapper<ReservationDeliveryDetailsLink>().lambda().eq(ReservationDeliveryDetailsLink::getOrderNumber, byId.getOutOrderNumber()));
|
List<ReservationDeliveryDetailsLink> list = deliveryDetailsLinkService.list(new QueryWrapper<ReservationDeliveryDetailsLink>().lambda().eq(ReservationDeliveryDetailsLink::getOrderNumber, byId.getOutOrderNumber()));
|
||||||
List<DeliveryDetailsLink> deliveryDetailsLinkList = new ArrayList<>();
|
List<DeliveryDetailsLink> deliveryDetailsLinkList = new ArrayList<>();
|
||||||
@@ -552,7 +561,11 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
deliveryDetailsLink.setOmsId(reservationDeliveryDetailsLink.getId());
|
deliveryDetailsLink.setOmsId(reservationDeliveryDetailsLink.getId());
|
||||||
deliveryDetailsLinkList.add(deliveryDetailsLink);
|
deliveryDetailsLinkList.add(deliveryDetailsLink);
|
||||||
}
|
}
|
||||||
wmsServiceFeign.omsOutOrderAddDeliveryDetailsLink(deliveryDetailsLinkList);
|
AjaxResult linkAddResult = wmsServiceFeign.omsOutOrderAddDeliveryDetailsLink(deliveryDetailsLinkList);
|
||||||
|
if (linkAddResult == null || !"200".equals(String.valueOf(linkAddResult.get("code")))) {
|
||||||
|
throw new ServiceException("出库单[" + currentOrderNumber + "]WMS配送明细推送失败:"
|
||||||
|
+ (linkAddResult == null ? "WMS服务不可用或已降级" : linkAddResult.get("msg")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
String inOrderNumber = stockOutOrder.getOutOrderNumber();
|
String inOrderNumber = stockOutOrder.getOutOrderNumber();
|
||||||
@@ -566,7 +579,12 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
outMaterialDetail.setTopOrganizationId(topOrgId);
|
outMaterialDetail.setTopOrganizationId(topOrgId);
|
||||||
outMaterialDetailList.add(outMaterialDetail);
|
outMaterialDetailList.add(outMaterialDetail);
|
||||||
}
|
}
|
||||||
wmsServiceFeign.omsOutOrderAddDetail(outMaterialDetailList);
|
//明细推送必须校验返回值: 失败被降级吞掉时WMS会残留"有单无明细"的出库单, 自动分配报"可用库存为0"难定位
|
||||||
|
AjaxResult detailAddResult = wmsServiceFeign.omsOutOrderAddDetail(outMaterialDetailList);
|
||||||
|
if (detailAddResult == null || !"200".equals(String.valueOf(detailAddResult.get("code")))) {
|
||||||
|
throw new ServiceException("出库单[" + currentOrderNumber + "]WMS出库明细推送失败:"
|
||||||
|
+ (detailAddResult == null ? "WMS服务不可用或已降级" : detailAddResult.get("msg")));
|
||||||
|
}
|
||||||
|
|
||||||
String outOrderNumber = byId.getOutOrderNumber();
|
String outOrderNumber = byId.getOutOrderNumber();
|
||||||
com.mhd.system.api.domain.StockOutOrderDTO stockOutOrderDTO = new com.mhd.system.api.domain.StockOutOrderDTO();
|
com.mhd.system.api.domain.StockOutOrderDTO stockOutOrderDTO = new com.mhd.system.api.domain.StockOutOrderDTO();
|
||||||
|
|||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package com.mhd.oms.domain.outEntrustOrder.repository.listener;
|
||||||
|
|
||||||
|
import com.alibaba.excel.converters.Converter;
|
||||||
|
import com.alibaba.excel.enums.CellDataTypeEnum;
|
||||||
|
import com.alibaba.excel.metadata.GlobalConfiguration;
|
||||||
|
import com.alibaba.excel.metadata.data.ReadCellData;
|
||||||
|
import com.alibaba.excel.metadata.property.ExcelContentProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义转换器,不进行任何类型转换,直接返回原始值
|
||||||
|
* 覆盖EasyExcel默认的StringNumberConverter,避免数字格式化错误
|
||||||
|
*
|
||||||
|
* @author rcx
|
||||||
|
* @date 2026-09-14
|
||||||
|
*/
|
||||||
|
public class NoConvertConverter implements Converter<String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> supportJavaTypeKey() {
|
||||||
|
return String.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CellDataTypeEnum supportExcelTypeKey() {
|
||||||
|
// 关键:返回NUMBER类型,覆盖StringNumberConverter
|
||||||
|
return CellDataTypeEnum.NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
|
||||||
|
GlobalConfiguration globalConfiguration) {
|
||||||
|
// 不进行任何格式化,直接返回原始值的字符串表示
|
||||||
|
if (cellData == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据单元格类型获取原始值
|
||||||
|
try {
|
||||||
|
CellDataTypeEnum type = cellData.getType();
|
||||||
|
if (type == CellDataTypeEnum.STRING) {
|
||||||
|
return cellData.getStringValue() != null ? cellData.getStringValue() : "";
|
||||||
|
} else if (type == CellDataTypeEnum.NUMBER) {
|
||||||
|
// 直接转换数字为字符串,不进行格式化
|
||||||
|
return cellData.getNumberValue() != null ? cellData.getNumberValue().toString() : "";
|
||||||
|
} else if (type == CellDataTypeEnum.BOOLEAN) {
|
||||||
|
return cellData.getBooleanValue() != null ? cellData.getBooleanValue().toString() : "";
|
||||||
|
} else if (type == CellDataTypeEnum.EMPTY) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
// 其他类型尝试获取字符串值
|
||||||
|
return cellData.getStringValue() != null ? cellData.getStringValue() : "";
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 如果获取失败,返回空字符串
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+481
@@ -0,0 +1,481 @@
|
|||||||
|
package com.mhd.oms.domain.outEntrustOrder.repository.listener;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.alibaba.excel.EasyExcel;
|
||||||
|
import com.alibaba.excel.ExcelReader;
|
||||||
|
import com.alibaba.excel.read.metadata.ReadSheet;
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.alibaba.fastjson2.TypeReference;
|
||||||
|
import com.mhd.common.core.domain.po.SysDictDataVo;
|
||||||
|
import com.mhd.common.core.domain.po.UserPo;
|
||||||
|
import com.mhd.common.core.exception.ServiceException;
|
||||||
|
import com.mhd.common.core.utils.StringUtils;
|
||||||
|
import com.mhd.common.core.web.domain.AjaxResult;
|
||||||
|
import com.mhd.common.security.utils.SecurityUtils;
|
||||||
|
import com.mhd.oms.domain.outEntrustOrder.repository.mapper.OutEntrustOrderMapper;
|
||||||
|
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDO;
|
||||||
|
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDetailDO;
|
||||||
|
import com.mhd.oms.domain.reservationStockInOrder.repository.po.MaterialBaseInfoPO;
|
||||||
|
import com.mhd.system.api.SystemServiceFeign;
|
||||||
|
import com.mhd.system.api.UserServiceFeign;
|
||||||
|
import com.mhd.system.api.model.LoginUser;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出仓委托单Excel解析服务
|
||||||
|
* 模板与WMS出库管理导入一致:前11行为表头,第12行起为明细
|
||||||
|
*
|
||||||
|
* @author rcx
|
||||||
|
* @date 2026-09-14
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class OutEntrustExcelParseService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SystemServiceFeign systemServiceFeign;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserServiceFeign userServiceFeign;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OutEntrustOrderMapper outEntrustOrderMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析结果:委托单对象 + 校验错误信息(空串表示通过)
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public static class ParseResult {
|
||||||
|
private final OutEntrustOrderDO order;
|
||||||
|
private final StringBuilder errorMsg;
|
||||||
|
|
||||||
|
public ParseResult(OutEntrustOrderDO order, StringBuilder errorMsg) {
|
||||||
|
this.order = order;
|
||||||
|
this.errorMsg = errorMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析出仓委托单Excel(不落库,落库由应用服务走新增链路)
|
||||||
|
*
|
||||||
|
* @param file 上传的Excel文件
|
||||||
|
* @param warehouseName 仓库名称(导入弹窗选择)
|
||||||
|
* @param warehouseCode 仓库编码
|
||||||
|
* @param warehouseId 仓库ID
|
||||||
|
* @return 解析结果:委托单对象 + 校验错误信息;错误信息非空时不应落库
|
||||||
|
*/
|
||||||
|
public ParseResult parseOutEntrustOrderExcel(MultipartFile file, String warehouseName, String warehouseCode, Long warehouseId) throws IOException {
|
||||||
|
|
||||||
|
OutEntrustHeaderListener headerListener = new OutEntrustHeaderListener();
|
||||||
|
|
||||||
|
ReadSheet headerSheet = EasyExcel.readSheet(0)
|
||||||
|
.headRowNumber(0)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ExcelReader headerReader = EasyExcel.read(file.getInputStream())
|
||||||
|
.autoCloseStream(false)
|
||||||
|
.registerReadListener(headerListener)
|
||||||
|
.registerConverter(new NoConvertConverter())
|
||||||
|
.headRowNumber(0)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 通过监听器控制只读取前11行
|
||||||
|
headerReader.read(headerSheet);
|
||||||
|
|
||||||
|
InputStream itemInputStream = file.getInputStream();
|
||||||
|
OutEntrustItemListener itemListener = new OutEntrustItemListener();
|
||||||
|
|
||||||
|
// 商品明细需要跳过前11行,从第12行开始读取
|
||||||
|
ExcelReader itemReader = EasyExcel.read(itemInputStream)
|
||||||
|
.autoCloseStream(false)
|
||||||
|
.registerReadListener(itemListener)
|
||||||
|
.registerConverter(new NoConvertConverter())
|
||||||
|
.headRowNumber(12)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ReadSheet itemSheet = EasyExcel.readSheet(0).build();
|
||||||
|
itemReader.read(itemSheet);
|
||||||
|
|
||||||
|
itemReader.finish();
|
||||||
|
List<OutEntrustOrderDetailDO> itemList = itemListener.getItemList();
|
||||||
|
OutEntrustOrderDO outEntrustOrder = headerListener.getOutEntrustOrder();
|
||||||
|
|
||||||
|
// 提前获取货主ID,用于查询物料基础信息时过滤
|
||||||
|
String customerName = outEntrustOrder.getShipperName();
|
||||||
|
UserPo shipperUser = resolveCustomerShipperUserPoForImport(customerName);
|
||||||
|
Long shipperId = shipperUser != null ? shipperUser.getUserId() : null;
|
||||||
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
|
Long topOrganizationId = loginUser != null && loginUser.getUserPo() != null
|
||||||
|
? loginUser.getUserPo().getTopOrganizationId() : null;
|
||||||
|
initList(itemList, shipperId, topOrganizationId);
|
||||||
|
outEntrustOrder.setMaterialDetailList(itemList);
|
||||||
|
outEntrustOrder.setWarehouseName(warehouseName);
|
||||||
|
outEntrustOrder.setWarehouseCode(warehouseCode);
|
||||||
|
outEntrustOrder.setWarehouseId(warehouseId);
|
||||||
|
|
||||||
|
//业务员默认当前登录人
|
||||||
|
if (loginUser != null) {
|
||||||
|
outEntrustOrder.setSalesmanId(String.valueOf(loginUser.getUserid()));
|
||||||
|
outEntrustOrder.setSalesmanName(loginUser.getUserPo() != null ? loginUser.getUserPo().getUserName() : loginUser.getUsername());
|
||||||
|
}
|
||||||
|
//货主信息回填(名称/编号以用户中心为准)
|
||||||
|
if (shipperUser != null) {
|
||||||
|
outEntrustOrder.setShipperId(shipperUser.getUserId());
|
||||||
|
outEntrustOrder.setShipperCode(shipperUser.getUserMemberCode());
|
||||||
|
outEntrustOrder.setShipperName(shipperUser.getUserName());
|
||||||
|
}
|
||||||
|
initDict(outEntrustOrder);
|
||||||
|
//校验必填字段
|
||||||
|
StringBuilder errorMsg = check(outEntrustOrder);
|
||||||
|
// 日期单元格有值但解析失败:与物料一致性错误同等对待,阻断导入并明确提示,杜绝静默丢日期
|
||||||
|
for (String dateError : headerListener.getDateParseErrors()) {
|
||||||
|
errorMsg.append(dateError).append("; ");
|
||||||
|
}
|
||||||
|
return new ParseResult(outEntrustOrder, errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验导入明细是否已在当前货主下维护物料基础信息
|
||||||
|
*/
|
||||||
|
private StringBuilder validateShipperMaterialDetails(OutEntrustOrderDO outEntrustOrder) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
List<OutEntrustOrderDetailDO> details = outEntrustOrder.getMaterialDetailList();
|
||||||
|
if (details == null || details.isEmpty()) {
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
Long shipperId = outEntrustOrder.getShipperId();
|
||||||
|
for (OutEntrustOrderDetailDO row : details) {
|
||||||
|
if (row.getMaterialBaseInfoId() != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean hasIdentifier = StringUtils.isNotBlank(row.getMaterialNo())
|
||||||
|
|| StringUtils.isNotBlank(row.getSkucode())
|
||||||
|
|| StringUtils.isNotBlank(row.getCommodityName());
|
||||||
|
if (!hasIdentifier) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String desc = buildImportMaterialRowDesc(row);
|
||||||
|
if (ObjectUtil.isEmpty(shipperId)) {
|
||||||
|
sb.append("请先确认客户(货主)信息;无法校验以下物料是否已在货主下维护:").append(desc).append("; ");
|
||||||
|
} else {
|
||||||
|
sb.append("当前货主下未维护以下物料:").append(desc).append("; ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提示文案只强调 Excel 导入列「商品名称」;商品名称为空时用导入料号/SKU 辅助说明。
|
||||||
|
*/
|
||||||
|
private String buildImportMaterialRowDesc(OutEntrustOrderDetailDO row) {
|
||||||
|
String commodityName = StringUtils.trimToEmpty(row.getCommodityName());
|
||||||
|
if (StringUtils.isNotBlank(commodityName)) {
|
||||||
|
return "商品名称「" + commodityName + "」";
|
||||||
|
}
|
||||||
|
String materialNo = StringUtils.trimToEmpty(row.getMaterialNo());
|
||||||
|
String sku = StringUtils.trimToEmpty(row.getSkucode());
|
||||||
|
StringBuilder b = new StringBuilder("导入商品名称为空");
|
||||||
|
if (StringUtils.isNotBlank(materialNo)) {
|
||||||
|
b.append(",料号「").append(materialNo).append("」");
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(sku)) {
|
||||||
|
b.append(",SKU「").append(sku).append("」");
|
||||||
|
}
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private StringBuilder check(OutEntrustOrderDO outEntrustOrder) {
|
||||||
|
StringBuilder errorMsg = new StringBuilder();
|
||||||
|
errorMsg.append(validateShipperMaterialDetails(outEntrustOrder));
|
||||||
|
if (outEntrustOrder.getMaterialDetailList() == null || outEntrustOrder.getMaterialDetailList().isEmpty()) {
|
||||||
|
errorMsg.append("物料明细列表不能为空或匹配不上物料信息; ");
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(outEntrustOrder.getToReceiver())) errorMsg.append("To不能为空; ");
|
||||||
|
if (StringUtils.isEmpty(outEntrustOrder.getCarrier())) errorMsg.append("承运商不能为空; ");
|
||||||
|
if (StringUtils.isEmpty(outEntrustOrder.getTransportModeCode())) errorMsg.append("运输方式不能为空; ");
|
||||||
|
if (StringUtils.isEmpty(outEntrustOrder.getSupervisionModeCode())) errorMsg.append("监督方式不能为空; ");
|
||||||
|
if (StringUtils.isEmpty(outEntrustOrder.getDepartureArrivalCountry())) errorMsg.append("出发/到达国家不能为空; ");
|
||||||
|
if (StringUtils.isEmpty(outEntrustOrder.getContactPerson())) errorMsg.append("联系人不能为空; ");
|
||||||
|
if (ObjectUtil.isEmpty(outEntrustOrder.getShipperId())) errorMsg.append("客户名称不能为空或匹配不上客户信息; ");
|
||||||
|
if (StringUtils.isEmpty(outEntrustOrder.getDeclareCustoms())) errorMsg.append("申报地关区不能为空; ");
|
||||||
|
if (StringUtils.isEmpty(outEntrustOrder.getEntryExitCustoms())) errorMsg.append("进出境关别不能为空; ");
|
||||||
|
return errorMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 初始化物料明细列表,查询物料基础信息并设置物料基础信息ID(与WMS出库管理导入逻辑一致:料号 > SKU码 > 商品名称)
|
||||||
|
* @param itemList 出仓委托单明细列表
|
||||||
|
* @param shipperId 货主ID(用于过滤物料基础信息,确保查询到的是该货主绑定的物料)
|
||||||
|
* @param topOrganizationId 一级组织ID(用于过滤物料基础信息)
|
||||||
|
*/
|
||||||
|
private void initList(List<OutEntrustOrderDetailDO> itemList, Long shipperId, Long topOrganizationId) {
|
||||||
|
for (OutEntrustOrderDetailDO detail : itemList) {
|
||||||
|
// 未识别货主时不查物料,避免匹配到其他货主的物料基础信息
|
||||||
|
if (shipperId == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据明细表中的字段查询物料基础信息(优先:料号 > 商品SKU码 > 商品名称)
|
||||||
|
MaterialBaseInfoPO materialBaseInfoPO = null;
|
||||||
|
|
||||||
|
// 优先使用料号查询(materialNo对应materialCode)
|
||||||
|
String materialNo = detail.getMaterialNo();
|
||||||
|
if (StringUtils.isNotBlank(materialNo)) {
|
||||||
|
List<MaterialBaseInfoPO> materialList = outEntrustOrderMapper.getMaterialByMaterialCode(materialNo, shipperId, topOrganizationId);
|
||||||
|
if (materialList != null && !materialList.isEmpty()) {
|
||||||
|
materialBaseInfoPO = materialList.get(0);
|
||||||
|
// ==== 料号与规格型号/SKU码一致性校验:任何一项不符直接报对应错误 ====
|
||||||
|
// 双方(trim后)必须完全一致:Excel留空而档案有值、或Excel填了值而档案为空,均视为不一致报错
|
||||||
|
String excelSpec = StringUtils.trimToEmpty(detail.getSpecificationModel());
|
||||||
|
String dbSpec = StringUtils.trimToEmpty(materialBaseInfoPO.getSpecificationModel());
|
||||||
|
if (!excelSpec.equals(dbSpec)) {
|
||||||
|
throw new ServiceException(String.format("导入失败:料号「%s」规格型号与物料档案不一致(Excel:%s / 档案:%s)",
|
||||||
|
materialNo, excelSpec.isEmpty() ? "空" : excelSpec, dbSpec.isEmpty() ? "空" : dbSpec));
|
||||||
|
}
|
||||||
|
String excelSku = StringUtils.trimToEmpty(detail.getSkucode());
|
||||||
|
String dbSku = StringUtils.trimToEmpty(materialBaseInfoPO.getBarCode());
|
||||||
|
if (!excelSku.equals(dbSku)) {
|
||||||
|
throw new ServiceException(String.format("导入失败:料号「%s」SKU码与物料档案不一致(Excel:%s / 档案:%s)",
|
||||||
|
materialNo, excelSku.isEmpty() ? "空" : excelSku, dbSku.isEmpty() ? "空" : dbSku));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 料号已填写但在该货主下查不到 → 直接报错(与WMS出库导入一致),不再被SKU码/商品名称静默兜底
|
||||||
|
throw new ServiceException(String.format("导入失败:料号「%s」在当前客户(货主)下不存在,请核对料号或在物料管理中维护", materialNo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果料号查询不到,使用商品SKU码查询(skucode对应barCode)
|
||||||
|
if (materialBaseInfoPO == null && StringUtils.isNotBlank(detail.getSkucode())) {
|
||||||
|
List<MaterialBaseInfoPO> materialList = outEntrustOrderMapper.getMaterialByBarCode(detail.getSkucode(), shipperId, topOrganizationId);
|
||||||
|
if (materialList != null && !materialList.isEmpty()) {
|
||||||
|
materialBaseInfoPO = materialList.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果料号和SKU码都查询不到,使用商品名称查询(commodityName对应materialName)
|
||||||
|
if (materialBaseInfoPO == null && StringUtils.isNotBlank(detail.getCommodityName())) {
|
||||||
|
List<MaterialBaseInfoPO> materialList = outEntrustOrderMapper.getMaterialByMaterialName(detail.getCommodityName(), shipperId, topOrganizationId);
|
||||||
|
if (materialList != null && !materialList.isEmpty()) {
|
||||||
|
materialBaseInfoPO = materialList.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置物料基础信息ID,并从物料基础信息中回填料号、物料编码、物料名称等信息
|
||||||
|
if (materialBaseInfoPO != null) {
|
||||||
|
detail.setMaterialBaseInfoId(materialBaseInfoPO.getMaterialBaseInfoId());
|
||||||
|
// 单价统一从物料档案回填
|
||||||
|
detail.setUnitPrice(materialBaseInfoPO.getUnitPrice());
|
||||||
|
// 如果Excel中没有料号,从物料基础信息中回填
|
||||||
|
if (StringUtils.isBlank(detail.getMaterialNo())) {
|
||||||
|
detail.setMaterialNo(materialBaseInfoPO.getMaterialCode());
|
||||||
|
}
|
||||||
|
// 回填物料编码
|
||||||
|
if (StringUtils.isBlank(detail.getMaterialCode())) {
|
||||||
|
detail.setMaterialCode(materialBaseInfoPO.getMaterialCode());
|
||||||
|
}
|
||||||
|
// 回填商品编号
|
||||||
|
if (StringUtils.isBlank(detail.getCommodityNo())) {
|
||||||
|
detail.setCommodityNo(materialBaseInfoPO.getMaterialCode());
|
||||||
|
}
|
||||||
|
// 回填物料名称(如果Excel中的商品名称为空,使用物料基础信息中的名称)
|
||||||
|
if (StringUtils.isBlank(detail.getCommodityName())) {
|
||||||
|
detail.setCommodityName(materialBaseInfoPO.getMaterialName());
|
||||||
|
}
|
||||||
|
// 回填物料名称到materialName字段
|
||||||
|
detail.setMaterialName(materialBaseInfoPO.getMaterialName());
|
||||||
|
// 回填条形码
|
||||||
|
detail.setBarCode(materialBaseInfoPO.getBarCode());
|
||||||
|
// 如果Excel中没有SKU码,从物料基础信息中回填
|
||||||
|
if (StringUtils.isBlank(detail.getSkucode())) {
|
||||||
|
detail.setSkucode(materialBaseInfoPO.getBarCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 单位:优先使用Excel中的"单位"字段,如果为空则使用"申报单位",如果还为空则从物料主数据获取(与WMS出库导入处理一致)
|
||||||
|
String unit = StringUtils.isNotBlank(detail.getUnit()) ? detail.getUnit() : null;
|
||||||
|
if (StringUtils.isBlank(unit) && StringUtils.isNotBlank(detail.getDeclareUnit())) {
|
||||||
|
unit = detail.getDeclareUnit();
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(unit) && materialBaseInfoPO != null && StringUtils.isNotBlank(materialBaseInfoPO.getUnitName())) {
|
||||||
|
unit = materialBaseInfoPO.getUnitName();
|
||||||
|
}
|
||||||
|
detail.setUnit(unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDict(OutEntrustOrderDO outEntrustOrder) {
|
||||||
|
if (ObjectUtil.isNotNull(outEntrustOrder.getContainerNoName())) {
|
||||||
|
AjaxResult containerNo = systemServiceFeign.selectListByDictType("cabinetNo");
|
||||||
|
if (ObjectUtil.isNotNull(containerNo) && "200".equals(String.valueOf(containerNo.get("code")))) {
|
||||||
|
List<SysDictDataVo> containerNoList = JSON.parseArray(JSONObject.toJSONString(containerNo.get("data")), SysDictDataVo.class);
|
||||||
|
for (SysDictDataVo sysDictDataVo : containerNoList) {
|
||||||
|
if (outEntrustOrder.getContainerNoName().equals(sysDictDataVo.getDictLabel())) {
|
||||||
|
outEntrustOrder.setContainerNo(sysDictDataVo.getDictValue());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ObjectUtil.isNotNull(outEntrustOrder.getTransportModeName())) {
|
||||||
|
AjaxResult transportMode = systemServiceFeign.selectListByDictType("transportMode");
|
||||||
|
if (ObjectUtil.isNotNull(transportMode) && "200".equals(String.valueOf(transportMode.get("code")))) {
|
||||||
|
List<SysDictDataVo> transportModeList = JSON.parseArray(JSONObject.toJSONString(transportMode.get("data")), SysDictDataVo.class);
|
||||||
|
for (SysDictDataVo sysDictDataVo : transportModeList) {
|
||||||
|
if (outEntrustOrder.getTransportModeName().equals(sysDictDataVo.getDictLabel())) {
|
||||||
|
outEntrustOrder.setTransportModeCode(sysDictDataVo.getDictValue());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ObjectUtil.isNotNull(outEntrustOrder.getSupervisionModeName())) {
|
||||||
|
AjaxResult supervisionMode = systemServiceFeign.selectListByDictType("controlType");
|
||||||
|
if (ObjectUtil.isNotNull(supervisionMode) && "200".equals(String.valueOf(supervisionMode.get("code")))) {
|
||||||
|
List<SysDictDataVo> supervisionModeList = JSON.parseArray(JSONObject.toJSONString(supervisionMode.get("data")), SysDictDataVo.class);
|
||||||
|
for (SysDictDataVo sysDictDataVo : supervisionModeList) {
|
||||||
|
if (outEntrustOrder.getSupervisionModeName().equals(sysDictDataVo.getDictLabel())) {
|
||||||
|
outEntrustOrder.setSupervisionModeCode(sysDictDataVo.getDictValue());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入出库单:解析出的货主用户是否属于当前登录用户同一组织(organizationId)。
|
||||||
|
* 仅当登录用户带有 organizationId 时启用过滤;否则保持与旧行为一致。
|
||||||
|
*/
|
||||||
|
private boolean shipperMatchesImporterOrganization(UserPo userPo, Long loginOrganizationId) {
|
||||||
|
if (loginOrganizationId == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (userPo == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Long oid = userPo.getOrganizationId();
|
||||||
|
return oid != null && loginOrganizationId.equals(oid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按客户名称或数字ID解析货主用户,逻辑与WMS出库管理导入一致:getInfoByName → 货主列表兜底,均做 organizationId 校验。
|
||||||
|
*/
|
||||||
|
private UserPo resolveCustomerShipperUserPoForImport(String customerName) {
|
||||||
|
if (StringUtils.isEmpty(customerName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Long loginOrganizationId = null;
|
||||||
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
|
if (loginUser != null && loginUser.getUserPo() != null) {
|
||||||
|
loginOrganizationId = loginUser.getUserPo().getOrganizationId();
|
||||||
|
}
|
||||||
|
String shipperName = customerName.trim();
|
||||||
|
if (StringUtils.isEmpty(shipperName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 纯数字:按用户ID查询
|
||||||
|
if (shipperName.matches("\\d+")) {
|
||||||
|
try {
|
||||||
|
Long possibleId = Long.valueOf(shipperName);
|
||||||
|
AjaxResult ajaxResult = userServiceFeign.getInfo(possibleId);
|
||||||
|
if ("200".equals(String.valueOf(ajaxResult.get("code"))) && ajaxResult.get("data") != null) {
|
||||||
|
UserPo userPo = JSON.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), UserPo.class);
|
||||||
|
if (userPo != null && shipperMatchesImporterOrganization(userPo, loginOrganizationId)) {
|
||||||
|
return userPo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按名称查询(用户中心)
|
||||||
|
try {
|
||||||
|
AjaxResult ajaxResult = userServiceFeign.getInfoByName(shipperName);
|
||||||
|
if ("200".equals(String.valueOf(ajaxResult.get("code"))) && ajaxResult.get("data") != null) {
|
||||||
|
UserPo userPo = JSON.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), UserPo.class);
|
||||||
|
if (userPo != null && shipperMatchesImporterOrganization(userPo, loginOrganizationId)) {
|
||||||
|
return userPo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 继续货主列表兜底
|
||||||
|
}
|
||||||
|
|
||||||
|
// 货主列表兜底(与WMS出库导入一致)
|
||||||
|
try {
|
||||||
|
AjaxResult shipperListResult = userServiceFeign.userShipperListAll();
|
||||||
|
if ("200".equals(String.valueOf(shipperListResult.get("code"))) && shipperListResult.get("data") != null) {
|
||||||
|
Object data = shipperListResult.get("data");
|
||||||
|
List<Map<String, Object>> shipperList = null;
|
||||||
|
if (data instanceof List) {
|
||||||
|
List<Object> rawList = (List<Object>) data;
|
||||||
|
shipperList = new ArrayList<>();
|
||||||
|
for (Object item : rawList) {
|
||||||
|
if (item instanceof Map) {
|
||||||
|
Map<String, Object> mapItem = (Map<String, Object>) item;
|
||||||
|
shipperList.add(mapItem);
|
||||||
|
} else {
|
||||||
|
Map<String, Object> mapItem = JSON.parseObject(JSONObject.toJSONString(item), new TypeReference<Map<String, Object>>() {
|
||||||
|
});
|
||||||
|
shipperList.add(mapItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String jsonStr = JSONObject.toJSONString(data);
|
||||||
|
shipperList = JSON.parseObject(jsonStr, new TypeReference<List<Map<String, Object>>>() {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shipperList != null && !shipperList.isEmpty()) {
|
||||||
|
for (Map<String, Object> shipper : shipperList) {
|
||||||
|
Object userId = shipper.get("userId");
|
||||||
|
Object userName = shipper.get("userName");
|
||||||
|
if (userName == null) {
|
||||||
|
userName = shipper.get("name");
|
||||||
|
}
|
||||||
|
if (userName == null) {
|
||||||
|
userName = shipper.get("shipperName");
|
||||||
|
}
|
||||||
|
boolean matched = false;
|
||||||
|
if (StringUtils.isNotEmpty(shipperName) && userName != null) {
|
||||||
|
String normalizedUserName = String.valueOf(userName).trim();
|
||||||
|
if (shipperName.equals(normalizedUserName) || normalizedUserName.contains(shipperName)) {
|
||||||
|
matched = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matched && userId != null) {
|
||||||
|
Long candidateId = Long.valueOf(String.valueOf(userId));
|
||||||
|
AjaxResult infoRes = userServiceFeign.getInfo(candidateId);
|
||||||
|
if ("200".equals(String.valueOf(infoRes.get("code"))) && infoRes.get("data") != null) {
|
||||||
|
UserPo up = JSON.parseObject(JSONObject.toJSONString(infoRes.get("data")), UserPo.class);
|
||||||
|
if (shipperMatchesImporterOrganization(up, loginOrganizationId)) {
|
||||||
|
return up;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+201
@@ -0,0 +1,201 @@
|
|||||||
|
package com.mhd.oms.domain.outEntrustOrder.repository.listener;
|
||||||
|
|
||||||
|
import com.alibaba.excel.context.AnalysisContext;
|
||||||
|
import com.alibaba.excel.event.AnalysisEventListener;
|
||||||
|
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDO;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.apache.poi.ss.usermodel.DateUtil; // Excel序列号转日期,POI已随EasyExcel引入
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出仓委托单导入-表头信息监听器(处理前11行,与WMS出库管理导入模板一致)
|
||||||
|
*
|
||||||
|
* @author rcx
|
||||||
|
* @date 2026-09-14
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class OutEntrustHeaderListener extends AnalysisEventListener<Map<Integer, Object>> {
|
||||||
|
private final OutEntrustOrderDO outEntrustOrder = new OutEntrustOrderDO();
|
||||||
|
private int rowNum = 0;
|
||||||
|
/** 日期单元格有值但格式无法识别的收集器(类上已有@Getter,自动生成getDateParseErrors()) */
|
||||||
|
private final List<String> dateParseErrors = new ArrayList<>();
|
||||||
|
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(Map<Integer, Object> data, AnalysisContext context) {
|
||||||
|
// 将Map<Integer, Object>转换为Map<Integer, String>
|
||||||
|
Map<Integer, String> stringData = convertToStringMap(data);
|
||||||
|
// 将Map转换为List再转为数组
|
||||||
|
List<String> dataList = mapToList(stringData);
|
||||||
|
Object[] rowData = listToArray(dataList);
|
||||||
|
String stringValue1 = getStringValue(rowData[0]);
|
||||||
|
if (stringValue1 != null && !stringValue1.isEmpty()) {
|
||||||
|
if ("备注:".equals(stringValue1)) {
|
||||||
|
outEntrustOrder.setRemark(getStringValue(rowData[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 根据行号提取对应信息
|
||||||
|
switch (rowNum) {
|
||||||
|
case 4: // 第5行:to、仓库联系人、车型及车牌、委托编码
|
||||||
|
outEntrustOrder.setToReceiver(getStringValue(rowData[2]));
|
||||||
|
outEntrustOrder.setContactPerson(getStringValue(rowData[5]));
|
||||||
|
outEntrustOrder.setVehicleModelPlate(getStringValue(rowData[12]));
|
||||||
|
outEntrustOrder.setEntrustNo(getStringValue(rowData[16]));
|
||||||
|
break;
|
||||||
|
case 5: // 第6行:承运方、tel、柜号、司机签名
|
||||||
|
outEntrustOrder.setCarrier(getStringValue(rowData[2]));
|
||||||
|
outEntrustOrder.setTel(getStringValue(rowData[5]));
|
||||||
|
outEntrustOrder.setContainerNoName(getStringValue(rowData[12]));
|
||||||
|
outEntrustOrder.setDriverSign(getStringValue(rowData[16]));
|
||||||
|
break;
|
||||||
|
case 6: // 第7行:出仓日期、fax、运输方式、申报地关区
|
||||||
|
Date outboundDate = parseDateSmart(getStringValue(rowData[2]), "出仓日期");
|
||||||
|
if (outboundDate != null) {
|
||||||
|
outEntrustOrder.setOutboundDate(outboundDate);
|
||||||
|
}
|
||||||
|
outEntrustOrder.setFax(getStringValue(rowData[5]));
|
||||||
|
outEntrustOrder.setTransportModeName(getStringValue(rowData[12]));
|
||||||
|
outEntrustOrder.setDeclareCustoms(getStringValue(rowData[16]));
|
||||||
|
break;
|
||||||
|
case 7: // 第8行:生产商、客户名称、报关姓名及联系方式、完成时间
|
||||||
|
outEntrustOrder.setManufacturer(getStringValue(rowData[2]));
|
||||||
|
outEntrustOrder.setShipperName(getStringValue(rowData[5]));
|
||||||
|
outEntrustOrder.setCustomsDeclarerInfo(getStringValue(rowData[12]));
|
||||||
|
Date completeTime = parseDateSmart(getStringValue(rowData[16]), "完成时间");
|
||||||
|
if (completeTime != null) {
|
||||||
|
outEntrustOrder.setCompleteTime(completeTime);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 8: // 第9行:监管方式、送货地址、海关是否查验货物、进出境关别
|
||||||
|
outEntrustOrder.setSupervisionModeName(getStringValue(rowData[2]));
|
||||||
|
outEntrustOrder.setDeliveryAddr(getStringValue(rowData[5]));
|
||||||
|
String stringValue = getStringValue(rowData[12]);
|
||||||
|
outEntrustOrder.setCustomsInspectionFlag(stringValue);
|
||||||
|
outEntrustOrder.setEntryExitCustoms(getStringValue(rowData[16]));
|
||||||
|
break;
|
||||||
|
case 9: // 第10行:下单日期、起运国
|
||||||
|
Date orderDate = parseDateSmart(getStringValue(rowData[2]), "下单日期");
|
||||||
|
if (orderDate != null) {
|
||||||
|
outEntrustOrder.setOrderDate(orderDate);
|
||||||
|
}
|
||||||
|
outEntrustOrder.setDepartureArrivalCountry(getStringValue(rowData[5]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rowNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAfterAllAnalysed(AnalysisContext context) {
|
||||||
|
// 解析完成后的处理
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将List转换为数组
|
||||||
|
*/
|
||||||
|
private Object[] listToArray(List<String> list) {
|
||||||
|
// 创建一个足够大的数组来容纳最多30列数据
|
||||||
|
Object[] array = new Object[30];
|
||||||
|
for (int i = 0; i < 30 && i < list.size(); i++) {
|
||||||
|
array[i] = list.get(i);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Object的Map转换为String的Map
|
||||||
|
*/
|
||||||
|
private Map<Integer, String> convertToStringMap(Map<Integer, Object> dataMap) {
|
||||||
|
if (dataMap == null) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Integer, String> stringMap = new HashMap<>();
|
||||||
|
for (Map.Entry<Integer, Object> entry : dataMap.entrySet()) {
|
||||||
|
Object value = entry.getValue();
|
||||||
|
String stringValue = value != null ? value.toString() : "";
|
||||||
|
stringMap.put(entry.getKey(), stringValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Map转换为List
|
||||||
|
*/
|
||||||
|
private List<String> mapToList(Map<Integer, String> map) {
|
||||||
|
if (map == null) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到最大索引以确定列表大小
|
||||||
|
int maxSize = map.keySet().stream().mapToInt(Integer::intValue).max().orElse(-1) + 1;
|
||||||
|
maxSize = Math.max(maxSize, 30); // 至少30个位置
|
||||||
|
|
||||||
|
List<String> list = new ArrayList<>(Collections.nCopies(maxSize, ""));
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, String> entry : map.entrySet()) {
|
||||||
|
if (entry.getKey() >= 0 && entry.getKey() < maxSize) {
|
||||||
|
String value = entry.getValue();
|
||||||
|
list.set(entry.getKey(), value != null ? value : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为字符串,处理null值
|
||||||
|
*/
|
||||||
|
private String getStringValue(Object value) {
|
||||||
|
return value == null ? "" : value.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能解析日期:兼容 Excel 日期单元格序列号 + 常见文本格式。
|
||||||
|
* 解析失败记入 dateParseErrors,由解析服务汇总返回给用户,不再静默丢失。
|
||||||
|
* @param raw 单元格原始字符串
|
||||||
|
* @param fieldLabel 字段中文名(用于错误提示)
|
||||||
|
*/
|
||||||
|
private Date parseDateSmart(String raw, String fieldLabel) {
|
||||||
|
String s = raw == null ? "" : raw.trim();
|
||||||
|
if (s.isEmpty()) {
|
||||||
|
return null; // 单元格为空:保持原行为,不算错误
|
||||||
|
}
|
||||||
|
// 1) Excel 日期序列号:NoConvertConverter 会把真日期单元格转成纯数字字符串(如 46234 = 2026-08-20)
|
||||||
|
// 区间 20000~60000 对应 1954~2064 年,避免把普通数字误判成日期
|
||||||
|
if (s.matches("^\\d{1,5}(\\.\\d+)?$")) {
|
||||||
|
try {
|
||||||
|
double serial = Double.parseDouble(s);
|
||||||
|
if (serial >= 20000 && serial <= 60000) {
|
||||||
|
return DateUtil.getJavaDate(serial);
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2) 常见文本格式逐个尝试
|
||||||
|
String[] patterns = {
|
||||||
|
"yyyy-MM-dd", "yyyy/M/d", "yyyy.M.d", "yyyy年M月d日",
|
||||||
|
"yyyy-MM-dd HH:mm:ss", "yyyy/M/d H:mm:ss", "yyyy-MM-dd H:mm"
|
||||||
|
};
|
||||||
|
for (String p : patterns) {
|
||||||
|
try {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat(p);
|
||||||
|
sdf.setLenient(false);
|
||||||
|
return sdf.parse(s);
|
||||||
|
} catch (ParseException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dateParseErrors.add(fieldLabel + "「" + s + "」格式无法识别,请改为日期格式或 2026-08-20 / 2026/8/20 / 2026年8月20日 等格式");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+282
@@ -0,0 +1,282 @@
|
|||||||
|
package com.mhd.oms.domain.outEntrustOrder.repository.listener;
|
||||||
|
|
||||||
|
import com.alibaba.excel.context.AnalysisContext;
|
||||||
|
import com.alibaba.excel.event.AnalysisEventListener;
|
||||||
|
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDetailDO;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出仓委托单导入-商品明细监听器(从第12行开始,与WMS出库管理导入模板一致)
|
||||||
|
*
|
||||||
|
* @author rcx
|
||||||
|
* @date 2026-09-14
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class OutEntrustItemListener extends AnalysisEventListener<Map<Integer, Object>> {
|
||||||
|
private final List<OutEntrustOrderDetailDO> itemList = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(Map<Integer, Object> data, AnalysisContext context) {
|
||||||
|
// 将Map<Integer, Object>转换为Map<Integer, String>
|
||||||
|
Map<Integer, String> stringData = convertToStringMap(data);
|
||||||
|
// 将Map转换为List再转为数组
|
||||||
|
List<String> dataList = mapToList(stringData);
|
||||||
|
Object[] rowData = listToArray(dataList);
|
||||||
|
|
||||||
|
// 提取序号(第一列),过滤空行和标题行
|
||||||
|
String serialNoStr = getStringValue(rowData[0]);
|
||||||
|
String commodityName = getStringValue(rowData[1]);
|
||||||
|
String materialNo = getStringValue(rowData[2]);
|
||||||
|
|
||||||
|
if (commodityName.isEmpty() && materialNo.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (serialNoStr.isEmpty() || "序号".equals(serialNoStr) || !isSequenceNumber(serialNoStr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OutEntrustOrderDetailDO item = new OutEntrustOrderDetailDO();
|
||||||
|
|
||||||
|
// 填充商品明细数据
|
||||||
|
//商品名称 料号 规格型号/ITEM 商品SKU码 Invoice No. 申报数量 申报单位 升 箱数 件数 尺寸 出库数量 "总净重
|
||||||
|
//(KG)" 总毛重 (KG) "总体积
|
||||||
|
//(CBM)" 总面积(SQM) Batch Ref NO's Sheet Ref NO's 原产国 箱号/卡板号 总价 币制 备注
|
||||||
|
item.setCommodityName(getStringValue(rowData[1]));
|
||||||
|
item.setMaterialNo(getStringValue(rowData[2]));
|
||||||
|
item.setSpecificationModel(getStringValue(rowData[3]));
|
||||||
|
item.setSkucode(getStringValue(rowData[4]));
|
||||||
|
item.setInvoiceNo(getStringValue(rowData[5]));
|
||||||
|
item.setDeclareQuantity(parseBigDecimal(rowData[6]));
|
||||||
|
item.setDeclareUnit(getStringValue(rowData[7]));
|
||||||
|
item.setLiter(normalizeNumeric(getStringValue(rowData[8])));
|
||||||
|
item.setCaseCount(parseBigDecimal(rowData[9]));
|
||||||
|
item.setPieceCount(parseBigDecimal(rowData[10]));
|
||||||
|
item.setSize(getStringValue(rowData[11]));
|
||||||
|
item.setOutboundQuantity(parseBigDecimal(rowData[12]));
|
||||||
|
item.setTotalNetWeight(parseBigDecimal(rowData[13]));
|
||||||
|
item.setTotalGrossWeight(parseBigDecimal(rowData[14]));
|
||||||
|
item.setTotalVolume(parseBigDecimal(rowData[15]));
|
||||||
|
item.setTotalArea(parseBigDecimal(rowData[16]));
|
||||||
|
item.setBatchRefNo(getStringValue(rowData[17]));
|
||||||
|
item.setSheetRefNo(getStringValue(rowData[18]));
|
||||||
|
item.setCountryOfOrigin(getStringValue(rowData[19]));
|
||||||
|
item.setBoxPalletNo(getStringValue(rowData[20]));
|
||||||
|
item.setTotalAmount(parseBigDecimal(rowData[21]));
|
||||||
|
item.setCurrency(getStringValue(rowData[22]));
|
||||||
|
item.setRemark(getStringValue(rowData[23]));
|
||||||
|
|
||||||
|
itemList.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAfterAllAnalysed(AnalysisContext context) {
|
||||||
|
// 解析完成后的处理
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将List转换为数组
|
||||||
|
*/
|
||||||
|
private Object[] listToArray(List<String> list) {
|
||||||
|
// 创建一个足够大的数组来容纳最多30列数据
|
||||||
|
Object[] array = new Object[30];
|
||||||
|
for (int i = 0; i < 30 && i < list.size(); i++) {
|
||||||
|
array[i] = list.get(i);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Object的Map转换为String的Map
|
||||||
|
*/
|
||||||
|
private Map<Integer, String> convertToStringMap(Map<Integer, Object> dataMap) {
|
||||||
|
if (dataMap == null) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Integer, String> stringMap = new HashMap<>();
|
||||||
|
for (Map.Entry<Integer, Object> entry : dataMap.entrySet()) {
|
||||||
|
Object value = entry.getValue();
|
||||||
|
String stringValue = convertToString(value);
|
||||||
|
stringMap.put(entry.getKey(), stringValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Object转换为String,处理BigDecimal等数字类型,去除小数点后的零
|
||||||
|
*/
|
||||||
|
private String convertToString(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是BigDecimal类型,需要特殊处理,去除小数点后的零
|
||||||
|
if (value instanceof BigDecimal) {
|
||||||
|
BigDecimal bd = (BigDecimal) value;
|
||||||
|
// 如果小数部分为0,转换为整数形式
|
||||||
|
if (bd.scale() > 0 && bd.stripTrailingZeros().scale() <= 0) {
|
||||||
|
return String.valueOf(bd.longValue());
|
||||||
|
}
|
||||||
|
// 否则去除尾部零
|
||||||
|
return bd.stripTrailingZeros().toPlainString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是Double或Float类型,也需要处理
|
||||||
|
if (value instanceof Double) {
|
||||||
|
Double d = (Double) value;
|
||||||
|
if (d == d.longValue()) {
|
||||||
|
return String.valueOf(d.longValue());
|
||||||
|
}
|
||||||
|
return d.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Float) {
|
||||||
|
Float f = (Float) value;
|
||||||
|
if (f == f.longValue()) {
|
||||||
|
return String.valueOf(f.longValue());
|
||||||
|
}
|
||||||
|
return f.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof String) {
|
||||||
|
String str = value.toString().trim();
|
||||||
|
if (str.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Map转换为List
|
||||||
|
*/
|
||||||
|
private List<String> mapToList(Map<Integer, String> map) {
|
||||||
|
if (map == null) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到最大索引以确定列表大小
|
||||||
|
int maxSize = map.keySet().stream().mapToInt(Integer::intValue).max().orElse(-1) + 1;
|
||||||
|
maxSize = Math.max(maxSize, 30); // 至少30个位置
|
||||||
|
|
||||||
|
List<String> list = new ArrayList<>(Collections.nCopies(maxSize, ""));
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, String> entry : map.entrySet()) {
|
||||||
|
if (entry.getKey() >= 0 && entry.getKey() < maxSize) {
|
||||||
|
String value = entry.getValue();
|
||||||
|
list.set(entry.getKey(), value != null ? value : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStringValue(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是BigDecimal类型,需要特殊处理,去除小数点后的零
|
||||||
|
if (value instanceof BigDecimal) {
|
||||||
|
BigDecimal bd = (BigDecimal) value;
|
||||||
|
// 如果小数部分为0,转换为整数形式
|
||||||
|
if (bd.scale() > 0 && bd.stripTrailingZeros().scale() <= 0) {
|
||||||
|
return String.valueOf(bd.longValue());
|
||||||
|
}
|
||||||
|
// 否则去除尾部零
|
||||||
|
return bd.stripTrailingZeros().toPlainString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是Double或Float类型,也需要处理
|
||||||
|
if (value instanceof Double) {
|
||||||
|
Double d = (Double) value;
|
||||||
|
if (d == d.longValue()) {
|
||||||
|
return String.valueOf(d.longValue());
|
||||||
|
}
|
||||||
|
return d.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Float) {
|
||||||
|
Float f = (Float) value;
|
||||||
|
if (f == f.longValue()) {
|
||||||
|
return String.valueOf(f.longValue());
|
||||||
|
}
|
||||||
|
return f.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof String) {
|
||||||
|
String str = value.toString().trim();
|
||||||
|
if (str.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
// 尝试解析为数字,如果是整数形式的小数(如 "123.0"),转换为整数
|
||||||
|
try {
|
||||||
|
BigDecimal bd = new BigDecimal(str);
|
||||||
|
// 如果小数部分为0,转换为整数形式
|
||||||
|
if (bd.scale() > 0 && bd.stripTrailingZeros().scale() <= 0) {
|
||||||
|
return String.valueOf(bd.longValue());
|
||||||
|
}
|
||||||
|
// 否则去除尾部零
|
||||||
|
return bd.stripTrailingZeros().toPlainString();
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 不是数字字符串,直接返回
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析BigDecimal,失败返回0
|
||||||
|
*/
|
||||||
|
private BigDecimal parseBigDecimal(Object value) {
|
||||||
|
try {
|
||||||
|
String strValue = getStringValue(value);
|
||||||
|
if (strValue.isEmpty()) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
return new BigDecimal(strValue);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 升列归一化:去千分位逗号、去空白;空值统一存 null(避免前端合计时对空串求和);
|
||||||
|
* 纯数字归一为无多余零的字符串;非数字内容原样保留。
|
||||||
|
*/
|
||||||
|
private String normalizeNumeric(String raw) {
|
||||||
|
if (raw == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String s = raw.trim().replace(",", "");
|
||||||
|
if (s.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new BigDecimal(s).stripTrailingZeros().toPlainString();
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSequenceNumber(String str) {
|
||||||
|
if (str == null || str.trim().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
str = str.trim();
|
||||||
|
|
||||||
|
return str.matches("^[1-9]\\d*$") || str.matches("^[1-9]\\d*\\.\\d+$") || str.matches("^0\\.\\d+$");
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|||||||
import com.mhd.oms.domain.outEntrustOrder.entity.OutEntrustOrder;
|
import com.mhd.oms.domain.outEntrustOrder.entity.OutEntrustOrder;
|
||||||
import com.mhd.oms.domain.outEntrustOrder.repository.po.OutEntrustOrderPO;
|
import com.mhd.oms.domain.outEntrustOrder.repository.po.OutEntrustOrderPO;
|
||||||
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDO;
|
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDO;
|
||||||
|
import com.mhd.oms.domain.reservationStockInOrder.repository.po.MaterialBaseInfoPO;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -25,4 +26,25 @@ public interface OutEntrustOrderMapper extends BaseMapper<OutEntrustOrder> {
|
|||||||
* 根据编号集合查询出仓委托单列表
|
* 根据编号集合查询出仓委托单列表
|
||||||
*/
|
*/
|
||||||
List<OutEntrustOrderPO> getInfoByIds(@Param("idList") List<Long> idList);
|
List<OutEntrustOrderPO> getInfoByIds(@Param("idList") List<Long> idList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入用:按料号查询货主下的物料基础信息(跨库WMS物料表)
|
||||||
|
*/
|
||||||
|
List<MaterialBaseInfoPO> getMaterialByMaterialCode(@Param("materialCode") String materialCode,
|
||||||
|
@Param("shipperId") Long shipperId,
|
||||||
|
@Param("topOrganizationId") Long topOrganizationId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入用:按SKU码(条形码)查询货主下的物料基础信息(跨库WMS物料表)
|
||||||
|
*/
|
||||||
|
List<MaterialBaseInfoPO> getMaterialByBarCode(@Param("barCode") String barCode,
|
||||||
|
@Param("shipperId") Long shipperId,
|
||||||
|
@Param("topOrganizationId") Long topOrganizationId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入用:按物料名称查询货主下的物料基础信息(跨库WMS物料表)
|
||||||
|
*/
|
||||||
|
List<MaterialBaseInfoPO> getMaterialByMaterialName(@Param("materialName") String materialName,
|
||||||
|
@Param("shipperId") Long shipperId,
|
||||||
|
@Param("topOrganizationId") Long topOrganizationId);
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -102,6 +102,12 @@ public class QueryOrderOverStockDO extends BaseVOEntity {
|
|||||||
@Excel(name = "出库单类型编码")
|
@Excel(name = "出库单类型编码")
|
||||||
private String outOrderTypeCode;
|
private String outOrderTypeCode;
|
||||||
|
|
||||||
|
@ApiModelProperty("出库单类型编码(与outOrderTypeCode同义)")
|
||||||
|
private String orderTypeCode;
|
||||||
|
|
||||||
|
@ApiModelProperty("出库单类型名称")
|
||||||
|
private String orderTypeName;
|
||||||
|
|
||||||
@ApiModelProperty("出库单类型名称")
|
@ApiModelProperty("出库单类型名称")
|
||||||
@Excel(name = "出库单类型名称")
|
@Excel(name = "出库单类型名称")
|
||||||
private String outOrderTypeName;
|
private String outOrderTypeName;
|
||||||
@@ -137,4 +143,10 @@ public class QueryOrderOverStockDO extends BaseVOEntity {
|
|||||||
@ApiModelProperty("越库单状态 0:无状态 1:处理中 2:越库完成")
|
@ApiModelProperty("越库单状态 0:无状态 1:处理中 2:越库完成")
|
||||||
@Excel(name = "越库单状态 0:无状态 1:处理中 2:越库完成")
|
@Excel(name = "越库单状态 0:无状态 1:处理中 2:越库完成")
|
||||||
private Integer overStockStatus;
|
private Integer overStockStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("实际出仓日期 开始(yyyy-MM-dd,为空按建档时间兜底)")
|
||||||
|
private String outboundDateStart;
|
||||||
|
|
||||||
|
@ApiModelProperty("实际出仓日期 结束(yyyy-MM-dd,为空按建档时间兜底)")
|
||||||
|
private String outboundDateEnd;
|
||||||
}
|
}
|
||||||
+17
@@ -456,4 +456,21 @@ public class ReservationStockInOrder extends BaseVOEntity {
|
|||||||
|
|
||||||
@ApiModelProperty("业务计价方式")
|
@ApiModelProperty("业务计价方式")
|
||||||
private String businessPriceWay;
|
private String businessPriceWay;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审状态: 0-无 1-已反审")
|
||||||
|
private Integer reverseAuditStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审原因")
|
||||||
|
private String reverseAuditRemark;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人id")
|
||||||
|
private Long reverseAuditBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人姓名")
|
||||||
|
private String reverseAuditByName;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审时间")
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date reverseAuditTime;
|
||||||
}
|
}
|
||||||
+7
@@ -54,6 +54,13 @@ public interface ReservationStockInOrderMapper extends BaseMapper<ReservationSto
|
|||||||
|
|
||||||
public String getWarehouseName(@Param("id") Long id);
|
public String getWarehouseName(@Param("id") Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地跨库直查仓库信息(编码/名称/单号生成规则),
|
||||||
|
* 供下发等流程在 system 服务 Feign 失败/降级时兜底, 与 getWarehouseInfoByWarehouseId Feign 接口同数据源。
|
||||||
|
* 不含 STOCK_LOCK_MODE(冻结功能列, 各环境库结构不一致), 兜底时 stockLockMode 为 null 即默认分配库存冻结模式
|
||||||
|
*/
|
||||||
|
public com.mhd.system.api.domain.WarehouseFeignPO getWarehouseInfoFallback(@Param("id") Long id);
|
||||||
|
|
||||||
List<ReservationStockInOrderPO> queryNoticePrint(ReservationStockInOrderDO stockInOrderDO);
|
List<ReservationStockInOrderPO> queryNoticePrint(ReservationStockInOrderDO stockInOrderDO);
|
||||||
List<ReservationInMaterialDetailPO> queryNoticePrintDetail(ReservationStockInOrderDO stockInOrderDO);
|
List<ReservationInMaterialDetailPO> queryNoticePrintDetail(ReservationStockInOrderDO stockInOrderDO);
|
||||||
public void updateByTransportationNumber(@Param("transportationNumber") String transportationNumber);
|
public void updateByTransportationNumber(@Param("transportationNumber") String transportationNumber);
|
||||||
|
|||||||
+17
@@ -502,4 +502,21 @@ public class ReservationStockInOrderPO extends BaseVOEntity {
|
|||||||
@ApiModelProperty("业务计价方式")
|
@ApiModelProperty("业务计价方式")
|
||||||
private String businessPriceWay;
|
private String businessPriceWay;
|
||||||
private String khdm;
|
private String khdm;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审状态: 0-无 1-已反审")
|
||||||
|
private Integer reverseAuditStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审原因")
|
||||||
|
private String reverseAuditRemark;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人id")
|
||||||
|
private Long reverseAuditBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人姓名")
|
||||||
|
private String reverseAuditByName;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审时间")
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date reverseAuditTime;
|
||||||
}
|
}
|
||||||
+17
@@ -513,4 +513,21 @@ public class ReservationStockInOrderDO extends BaseVOEntity {
|
|||||||
|
|
||||||
@ApiModelProperty("业务计价方式")
|
@ApiModelProperty("业务计价方式")
|
||||||
private String businessPriceWay;
|
private String businessPriceWay;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审状态: 0-无 1-已反审")
|
||||||
|
private Integer reverseAuditStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审原因")
|
||||||
|
private String reverseAuditRemark;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人id")
|
||||||
|
private Long reverseAuditBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人姓名")
|
||||||
|
private String reverseAuditByName;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审时间")
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date reverseAuditTime;
|
||||||
}
|
}
|
||||||
+17
@@ -507,4 +507,21 @@ public class ReservationStockInOrderDTO extends ReservationStockInOrderBaseDTO {
|
|||||||
|
|
||||||
@ApiModelProperty("业务计价方式")
|
@ApiModelProperty("业务计价方式")
|
||||||
private String businessPriceWay;
|
private String businessPriceWay;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审状态: 0-无 1-已反审")
|
||||||
|
private Integer reverseAuditStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审原因")
|
||||||
|
private String reverseAuditRemark;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人id")
|
||||||
|
private Long reverseAuditBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人姓名")
|
||||||
|
private String reverseAuditByName;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审时间")
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date reverseAuditTime;
|
||||||
}
|
}
|
||||||
+643
-2
@@ -10,6 +10,7 @@ import com.mhd.common.core.domain.po.UserPo;
|
|||||||
import com.mhd.common.core.enums.DictCode;
|
import com.mhd.common.core.enums.DictCode;
|
||||||
import com.mhd.common.core.exception.ServiceException;
|
import com.mhd.common.core.exception.ServiceException;
|
||||||
import com.mhd.common.core.utils.StringUtils;
|
import com.mhd.common.core.utils.StringUtils;
|
||||||
|
import com.mhd.common.core.utils.poi.ExcelUtil;
|
||||||
import com.mhd.common.core.web.domain.AjaxResult;
|
import com.mhd.common.core.web.domain.AjaxResult;
|
||||||
import com.mhd.common.security.utils.SecurityUtils;
|
import com.mhd.common.security.utils.SecurityUtils;
|
||||||
import com.mhd.oms.domain.erpCountry.entity.ErpCountry;
|
import com.mhd.oms.domain.erpCountry.entity.ErpCountry;
|
||||||
@@ -18,6 +19,7 @@ import com.mhd.oms.domain.gwLog.repository.mapper.GwLogMapper;
|
|||||||
import com.mhd.oms.domain.reservationInMaterialDetail.entity.ReservationInMaterialDetail;
|
import com.mhd.oms.domain.reservationInMaterialDetail.entity.ReservationInMaterialDetail;
|
||||||
import com.mhd.oms.domain.reservationInMaterialDetail.repository.mapper.ReservationInMaterialDetailMapper;
|
import com.mhd.oms.domain.reservationInMaterialDetail.repository.mapper.ReservationInMaterialDetailMapper;
|
||||||
import com.mhd.oms.domain.reservationInMaterialDetail.repository.po.ReservationInMaterialDetailPO;
|
import com.mhd.oms.domain.reservationInMaterialDetail.repository.po.ReservationInMaterialDetailPO;
|
||||||
|
import com.mhd.oms.domain.reservationInMaterialDetail.repository.todo.ReservationInMaterialDetailDO;
|
||||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.facade.IReservationMaterialInventoryService;
|
import com.mhd.oms.domain.reservationMaterialInventory.repository.facade.IReservationMaterialInventoryService;
|
||||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.mapper.ReservationMaterialInventoryMapper;
|
import com.mhd.oms.domain.reservationMaterialInventory.repository.mapper.ReservationMaterialInventoryMapper;
|
||||||
import com.mhd.oms.domain.reservationStockInOrder.entity.ReservationStockInOrder;
|
import com.mhd.oms.domain.reservationStockInOrder.entity.ReservationStockInOrder;
|
||||||
@@ -26,6 +28,7 @@ import com.mhd.oms.domain.reservationStockInOrder.repository.po.MaterialBaseInfo
|
|||||||
import com.mhd.oms.domain.reservationStockInOrder.repository.po.ReservationStockInOrderPO;
|
import com.mhd.oms.domain.reservationStockInOrder.repository.po.ReservationStockInOrderPO;
|
||||||
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDO;
|
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDO;
|
||||||
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDTO;
|
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDTO;
|
||||||
|
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderImportRowDTO;
|
||||||
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.GwInDetail;
|
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.GwInDetail;
|
||||||
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.GwStockInOrder;
|
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.GwStockInOrder;
|
||||||
import com.mhd.oms.interfaces.dto.erpCountry.ErpCountryDTO;
|
import com.mhd.oms.interfaces.dto.erpCountry.ErpCountryDTO;
|
||||||
@@ -47,6 +50,7 @@ import org.springframework.beans.BeanUtils;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -282,8 +286,7 @@ public class ReservationStockInOrderApplicationService {
|
|||||||
* @return Boolean
|
* @return Boolean
|
||||||
*/
|
*/
|
||||||
public Boolean auditState(ReservationStockInOrderDO stockInOrderDO){
|
public Boolean auditState(ReservationStockInOrderDO stockInOrderDO){
|
||||||
Boolean b = stockInOrderDomainService.auditState(stockInOrderDO);
|
Boolean b = stockInOrderDomainService.auditState(stockInOrderDO);// List<Long> inOrderIds = stockInOrderDO.getInOrderIds();
|
||||||
// List<Long> inOrderIds = stockInOrderDO.getInOrderIds();
|
|
||||||
// if (inOrderIds != null && !inOrderIds.isEmpty()) {
|
// if (inOrderIds != null && !inOrderIds.isEmpty()) {
|
||||||
// ReservationStockInOrder stockInOrder = reservationStockInOrderMapper.selectById(inOrderIds.get(0));
|
// ReservationStockInOrder stockInOrder = reservationStockInOrderMapper.selectById(inOrderIds.get(0));
|
||||||
// Integer needCustomsDeclaration = stockInOrder.getNeedCustomsDeclaration();
|
// Integer needCustomsDeclaration = stockInOrder.getNeedCustomsDeclaration();
|
||||||
@@ -302,6 +305,15 @@ public class ReservationStockInOrderApplicationService {
|
|||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反审入库业务单:业务单状态改为已驳回(issueStatus=3)并打已反审标记落入【已反审】tab,
|
||||||
|
* 入库执行单+明细逻辑删除,WMS侧整链回退(库存扣减+单据清理+反审异常记录),
|
||||||
|
* W入库单有出库记录时WMS侧拦截,已推送关务直接拦截
|
||||||
|
*/
|
||||||
|
public Boolean reverseAudit(ReservationStockInOrderDO stockInOrderDO){
|
||||||
|
return stockInOrderDomainService.reverseAudit(stockInOrderDO);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 下发入库单
|
* @description 下发入库单
|
||||||
* @author ZhouGY
|
* @author ZhouGY
|
||||||
@@ -1582,6 +1594,635 @@ public class ReservationStockInOrderApplicationService {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入入仓委托单(模板与WMS入库管理导入一致:第1-12行表头,第13行列名,第14行起明细)
|
||||||
|
* 以【委托编号NO】作为分组键,同一委托编号的多条明细归为一张入库单
|
||||||
|
*/
|
||||||
|
public Boolean importData(MultipartFile file, Long warehouseId, String warehouseCode, String warehouseName) {
|
||||||
|
if (file == null || file.isEmpty()) {
|
||||||
|
throw new ServiceException("导入文件不能为空");
|
||||||
|
}
|
||||||
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
|
if (loginUser == null) {
|
||||||
|
throw new ServiceException("登录已失效,请重新登录");
|
||||||
|
}
|
||||||
|
Long topOrganizationId = loginUser.getUserPo() != null ? loginUser.getUserPo().getTopOrganizationId() : null;
|
||||||
|
|
||||||
|
try (InputStream is = file.getInputStream()) {
|
||||||
|
// 明细表的列名在第13行(索引12),数据从第14行开始
|
||||||
|
int detailTitleRowIndex = 12;
|
||||||
|
|
||||||
|
// 1. 读取表头信息(第1-12行),按标签扫描提取字段
|
||||||
|
ReservationStockInOrderImportRowDTO headerInfo = new ReservationStockInOrderImportRowDTO();
|
||||||
|
try (InputStream isForHeader = file.getInputStream()) {
|
||||||
|
Workbook wb = WorkbookFactory.create(isForHeader);
|
||||||
|
Sheet sheet = wb.getSheetAt(0);
|
||||||
|
fillHeaderInfoByLabels(sheet, headerInfo);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("读取Excel表头信息失败", e);
|
||||||
|
// 不抛出异常,继续尝试从明细行读取
|
||||||
|
}
|
||||||
|
log.info("表头信息读取完成 - 委托编号NO: {}, 客户名称: {}, To: {}, 联系人: {}, 承运商: {}, 入仓日期: {}, 完成时间: {}, 下单日期: {}",
|
||||||
|
headerInfo.getConsignmentNo(), headerInfo.getShipperName(), headerInfo.getTo(),
|
||||||
|
headerInfo.getContactPerson(), headerInfo.getCarrier(), headerInfo.getWarehouseDate(),
|
||||||
|
headerInfo.getCompletionTime(), headerInfo.getOrderDate());
|
||||||
|
|
||||||
|
// 2. 读取明细数据(ExcelUtil从第13行列名开始)
|
||||||
|
ExcelUtil<ReservationStockInOrderImportRowDTO> util = new ExcelUtil<>(ReservationStockInOrderImportRowDTO.class);
|
||||||
|
List<ReservationStockInOrderImportRowDTO> rows = util.importExcel(is, detailTitleRowIndex);
|
||||||
|
if (rows == null || rows.isEmpty()) {
|
||||||
|
throw new ServiceException("导入数据为空。请检查Excel第13行是否为明细表列名(包含'商品名称'列),第14行开始是否为数据行");
|
||||||
|
}
|
||||||
|
log.info("成功读取 {} 行明细数据", rows.size());
|
||||||
|
|
||||||
|
// 3. 将表头信息应用到每一行(明细行值为空时使用表头值)
|
||||||
|
applyHeaderToRows(headerInfo, rows);
|
||||||
|
|
||||||
|
// 4. 过滤掉商品名称为空的空行
|
||||||
|
List<ReservationStockInOrderImportRowDTO> validRows = rows.stream()
|
||||||
|
.filter(r -> r != null && StringUtils.isNotBlank(r.getMaterialName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (validRows.isEmpty()) {
|
||||||
|
throw new ServiceException("导入数据为空(可能全为空行)。共读取 " + rows.size()
|
||||||
|
+ " 行数据,但商品名称都为空。请检查Excel第13行是否为明细表列名(包含'商品名称'列),第14行开始是否为数据行");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 仓库必填校验(与出库单导入保持一致,仓库由导入弹窗选择传入)
|
||||||
|
if (warehouseId == null) {
|
||||||
|
throw new ServiceException("导入失败:仓库ID不能为空");
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(warehouseCode)) {
|
||||||
|
throw new ServiceException("导入失败:仓库编码不能为空");
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(warehouseName)) {
|
||||||
|
throw new ServiceException("导入失败:仓库名称不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 以委托编号NO分组;若为空则归到同一组
|
||||||
|
Map<String, List<ReservationStockInOrderImportRowDTO>> groupMap = validRows.stream()
|
||||||
|
.collect(Collectors.groupingBy(r -> StringUtils.isBlank(r.getConsignmentNo()) ? "__EMPTY_CONSIGNMENT__" : r.getConsignmentNo()));
|
||||||
|
|
||||||
|
List<ReservationStockInOrderDO> orderList = new ArrayList<>();
|
||||||
|
for (Map.Entry<String, List<ReservationStockInOrderImportRowDTO>> entry : groupMap.entrySet()) {
|
||||||
|
ReservationStockInOrderDO order = buildStockInOrderFromImport(
|
||||||
|
entry.getValue(), headerInfo, loginUser, topOrganizationId,
|
||||||
|
warehouseId, warehouseCode, warehouseName);
|
||||||
|
if (order != null) {
|
||||||
|
orderList.add(order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果所有订单都被跳过(orderList为空),抛出异常提示用户
|
||||||
|
if (orderList.isEmpty()) {
|
||||||
|
throw new ServiceException("导入失败:所有入库单的明细行都因数据不完整(数量为空或物料基础信息缺失)被跳过,请检查Excel数据");
|
||||||
|
}
|
||||||
|
|
||||||
|
return stockInOrderDomainService.batchInsert(orderList);
|
||||||
|
} catch (ServiceException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("导入入库单失败", e);
|
||||||
|
throw new ServiceException("导入入库单失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单个委托编号分组的入库单组装(解析+校验,不落库)
|
||||||
|
* 明细行数量为空/容器类型行跳过;料号必填且必须在当前货主下存在,规格型号/SKU码与物料档案不一致直接报错
|
||||||
|
*
|
||||||
|
* @return 组装完成的入库单;该组明细全部被跳过时返回null
|
||||||
|
*/
|
||||||
|
private ReservationStockInOrderDO buildStockInOrderFromImport(List<ReservationStockInOrderImportRowDTO> groupRows,
|
||||||
|
ReservationStockInOrderImportRowDTO headerInfo,
|
||||||
|
LoginUser loginUser, Long topOrganizationId,
|
||||||
|
Long warehouseId, String warehouseCode, String warehouseName) {
|
||||||
|
ReservationStockInOrderImportRowDTO h = groupRows.get(0);
|
||||||
|
|
||||||
|
// 客户名称优先使用表头,表头为空时使用明细行
|
||||||
|
String customerName = StringUtils.isNotBlank(headerInfo.getShipperName()) ? headerInfo.getShipperName() : h.getShipperName();
|
||||||
|
if (StringUtils.isBlank(customerName)) {
|
||||||
|
throw new ServiceException("导入失败:客户名称不能为空(委托编号NO=" + h.getConsignmentNo()
|
||||||
|
+ ")。请确保Excel表头(第1-12行)中包含'客户名称'字段");
|
||||||
|
}
|
||||||
|
|
||||||
|
ReservationStockInOrderDO order = new ReservationStockInOrderDO();
|
||||||
|
// 组织/审计字段
|
||||||
|
if (loginUser.getUserPo() != null) {
|
||||||
|
order.setOrganizationId(loginUser.getUserPo().getOrganizationId());
|
||||||
|
order.setOrganizationName(loginUser.getUserPo().getOrganizationName());
|
||||||
|
order.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId());
|
||||||
|
}
|
||||||
|
order.setCreateBy(loginUser.getUserid());
|
||||||
|
order.setCreateByName(resolveUserRealName(loginUser));
|
||||||
|
order.setCreateTime(new Date());
|
||||||
|
|
||||||
|
// 仓库(导入弹窗选择传入)
|
||||||
|
order.setWarehouseId(warehouseId);
|
||||||
|
order.setWarehouseCode(warehouseCode);
|
||||||
|
order.setWarehouseName(warehouseName);
|
||||||
|
|
||||||
|
// 货主信息:名称/编号解析货主ID
|
||||||
|
String shipperCode = StringUtils.isNotBlank(headerInfo.getShipperCode()) ? headerInfo.getShipperCode() : h.getShipperCode();
|
||||||
|
Long shipperId = getShipperIdByNameOrCode(customerName, shipperCode);
|
||||||
|
order.setShipperId(shipperId);
|
||||||
|
if (StringUtils.isNotBlank(customerName)) {
|
||||||
|
order.setShipperName(customerName);
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(shipperCode)) {
|
||||||
|
order.setShipperCode(shipperCode);
|
||||||
|
}
|
||||||
|
order.setConsignmentNo(h.getConsignmentNo());
|
||||||
|
order.setRemark(h.getRemark());
|
||||||
|
// 导入入库单时,如果入库单类型为空,默认设置为普通入库
|
||||||
|
order.setOrderTypeCode("pu_tong_ru_ku");
|
||||||
|
order.setOrderTypeName("普通入库单");
|
||||||
|
// 订单来源:数据导入(与入库业务单多页签导入保持一致)
|
||||||
|
order.setReservationOrderSource("数据导入");
|
||||||
|
|
||||||
|
// 报关表头字段(优先使用表头字段,表头为空则使用明细行字段)
|
||||||
|
order.setTo(firstNotBlank(headerInfo.getTo(), h.getTo()));
|
||||||
|
order.setContactPerson(firstNotBlank(headerInfo.getContactPerson(), h.getContactPerson()));
|
||||||
|
order.setVehicleInfo(firstNotBlank(headerInfo.getVehicleInfo(), h.getVehicleInfo()));
|
||||||
|
order.setTel(firstNotBlank(headerInfo.getTel(), h.getTel()));
|
||||||
|
order.setDriverSignature(firstNotBlank(headerInfo.getDriverSignature(), h.getDriverSignature()));
|
||||||
|
order.setFax(firstNotBlank(headerInfo.getFax(), h.getFax()));
|
||||||
|
order.setDeclarationArea(firstNotBlank(headerInfo.getDeclarationArea(), h.getDeclarationArea()));
|
||||||
|
order.setManufacturer(firstNotBlank(headerInfo.getManufacturer(), h.getManufacturer()));
|
||||||
|
order.setCustomsBrokerInfo(firstNotBlank(headerInfo.getCustomsBrokerInfo(), h.getCustomsBrokerInfo()));
|
||||||
|
order.setDeliveryAddress(firstNotBlank(headerInfo.getDeliveryAddress(), h.getDeliveryAddress()));
|
||||||
|
order.setEntryExitCustoms(firstNotBlank(headerInfo.getEntryExitCustoms(), h.getEntryExitCustoms()));
|
||||||
|
order.setOriginDestinationCountry(firstNotBlank(headerInfo.getOriginDestinationCountry(), h.getOriginDestinationCountry()));
|
||||||
|
order.setCarrier(firstNotBlank(headerInfo.getCarrier(), h.getCarrier()));
|
||||||
|
order.setContainerNo(firstNotBlank(headerInfo.getContainerNo(), h.getContainerNo()));
|
||||||
|
order.setTransportMethod(firstNotBlank(headerInfo.getTransportMethod(), h.getTransportMethod()));
|
||||||
|
order.setSupervisionMethod(firstNotBlank(headerInfo.getSupervisionMethod(), h.getSupervisionMethod()));
|
||||||
|
|
||||||
|
// 报关字段默认值(优先使用表头字段)
|
||||||
|
Integer customsInspection = headerInfo.getCustomsInspection() != null ? headerInfo.getCustomsInspection() : h.getCustomsInspection();
|
||||||
|
order.setCustomsInspection(customsInspection == null ? 0 : customsInspection); // 默认:否
|
||||||
|
Integer needCustomsDeclaration = headerInfo.getNeedCustomsDeclaration() != null ? headerInfo.getNeedCustomsDeclaration() : h.getNeedCustomsDeclaration();
|
||||||
|
order.setNeedCustomsDeclaration(needCustomsDeclaration == null ? 1 : needCustomsDeclaration); // 默认:是
|
||||||
|
Integer needTransport = headerInfo.getNeedTransport() != null ? headerInfo.getNeedTransport() : h.getNeedTransport();
|
||||||
|
order.setNeedTransport(needTransport == null ? 0 : needTransport); // 默认:否
|
||||||
|
|
||||||
|
// 日期字段(优先使用表头字段)
|
||||||
|
String warehouseDateStr = firstNotBlank(headerInfo.getWarehouseDate(), h.getWarehouseDate());
|
||||||
|
if (StringUtils.isNotBlank(warehouseDateStr)) {
|
||||||
|
Date warehouseDate = parseDateString(warehouseDateStr);
|
||||||
|
if (warehouseDate != null) {
|
||||||
|
order.setWarehouseDate(warehouseDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String completionTimeStr = firstNotBlank(headerInfo.getCompletionTime(), h.getCompletionTime());
|
||||||
|
if (StringUtils.isNotBlank(completionTimeStr)) {
|
||||||
|
Date completionTime = parseDateString(completionTimeStr);
|
||||||
|
if (completionTime != null) {
|
||||||
|
order.setCompletionTime(completionTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String orderDateStr = firstNotBlank(headerInfo.getOrderDate(), h.getOrderDate());
|
||||||
|
if (StringUtils.isNotBlank(orderDateStr)) {
|
||||||
|
Date orderDate = parseDateString(orderDateStr);
|
||||||
|
if (orderDate != null) {
|
||||||
|
order.setOrderDate(orderDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 明细
|
||||||
|
List<ReservationInMaterialDetailDO> details = new ArrayList<>();
|
||||||
|
for (ReservationStockInOrderImportRowDTO r : groupRows) {
|
||||||
|
// 先检查数量(仓库数量优先,入库数量兜底),数量为空直接跳过该明细行
|
||||||
|
BigDecimal quantity = r.getWarehouseQuantity();
|
||||||
|
if (quantity == null) {
|
||||||
|
quantity = r.getInboundQuantity();
|
||||||
|
}
|
||||||
|
if (quantity == null) {
|
||||||
|
log.warn("跳过明细行:仓库数量或入库数量不能为空 - 委托编号NO: [{}], 商品名称: [{}], 料号: [{}]",
|
||||||
|
h.getConsignmentNo(), r.getMaterialName(), r.getMaterialCode());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否为容器类型等非物料信息,如果是则跳过
|
||||||
|
String materialName = r.getMaterialName();
|
||||||
|
if (StringUtils.isNotBlank(materialName)) {
|
||||||
|
String lowerName = materialName.trim();
|
||||||
|
if (lowerName.contains("尺柜") || lowerName.contains("尺集装箱")
|
||||||
|
|| lowerName.contains("Container") || lowerName.contains("container")
|
||||||
|
|| (lowerName.contains("柜") && (lowerName.contains("20") || lowerName.contains("40") || lowerName.contains("45")))) {
|
||||||
|
log.info("跳过明细行:识别为容器类型信息,非实际物料 - 委托编号NO: [{}], 商品名称: [{}]",
|
||||||
|
h.getConsignmentNo(), materialName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入文件中必须填写料号,根据料号获取商品信息,必须是货主下有的才可以匹配
|
||||||
|
if (StringUtils.isBlank(r.getMaterialCode())) {
|
||||||
|
throw new ServiceException("导入失败:请在导入文件中填写正确的料号");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据料号跨库查询物料基础信息(getByCode按组织过滤),查到后需在Java侧核对货主归属
|
||||||
|
MaterialBaseInfoPO materialBaseInfoPO = safeGetMaterialByCode(r.getMaterialCode(), loginUser);
|
||||||
|
if (materialBaseInfoPO != null && shipperId != null
|
||||||
|
&& (materialBaseInfoPO.getShipperId() == null || !materialBaseInfoPO.getShipperId().equals(shipperId))) {
|
||||||
|
// 查到但不属于该货主,等同于不存在,不允许导入
|
||||||
|
materialBaseInfoPO = null;
|
||||||
|
}
|
||||||
|
if (materialBaseInfoPO == null) {
|
||||||
|
// 如果提供了料号但查询不到(或不属于该货主),直接报错,不允许导入
|
||||||
|
throw new ServiceException(String.format("导入失败:该货主下不存在该料号(货主名称=%s,料号=%s)。请确保该料号已在物料管理中创建,且属于该货主",
|
||||||
|
order.getShipperName(), r.getMaterialCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验料号与规格型号/SKU码一致性(与WMS入库管理导入一致):任何一项不符直接报对应错误
|
||||||
|
String excelSpec = StringUtils.trimToEmpty(r.getSpecificationModel());
|
||||||
|
String dbSpec = StringUtils.trimToEmpty(materialBaseInfoPO.getSpecificationModel());
|
||||||
|
if (!excelSpec.equals(dbSpec)) {
|
||||||
|
throw new ServiceException(String.format("导入失败:料号[%s](商品名称:%s)规格型号与物料档案不一致(Excel:%s / 档案:%s)",
|
||||||
|
r.getMaterialCode(), r.getMaterialName(),
|
||||||
|
excelSpec.isEmpty() ? "空" : excelSpec, dbSpec.isEmpty() ? "空" : dbSpec));
|
||||||
|
}
|
||||||
|
String excelSku = StringUtils.trimToEmpty(r.getSkuCode());
|
||||||
|
String dbSku = StringUtils.trimToEmpty(materialBaseInfoPO.getBarCode());
|
||||||
|
if (!excelSku.equals(dbSku)) {
|
||||||
|
throw new ServiceException(String.format("导入失败:料号[%s](商品名称:%s)SKU码与物料档案不一致(Excel:%s / 档案:%s)",
|
||||||
|
r.getMaterialCode(), r.getMaterialName(),
|
||||||
|
excelSku.isEmpty() ? "空" : excelSku, dbSku.isEmpty() ? "空" : dbSku));
|
||||||
|
}
|
||||||
|
|
||||||
|
ReservationInMaterialDetailDO d = new ReservationInMaterialDetailDO();
|
||||||
|
d.setMaterialBaseInfoId(materialBaseInfoPO.getMaterialBaseInfoId());
|
||||||
|
// 从物料档案获取名称(与WMS入库导入一致)
|
||||||
|
d.setMaterialName(materialBaseInfoPO.getMaterialName());
|
||||||
|
d.setMaterialCode(r.getMaterialCode());
|
||||||
|
d.setQuantity(quantity);
|
||||||
|
d.setSpecificationModel(r.getSpecificationModel());
|
||||||
|
d.setSkuCode(r.getSkuCode());
|
||||||
|
d.setInvoiceNo(r.getInvoiceNo());
|
||||||
|
d.setDeclaredQuantity(r.getDeclaredQuantity());
|
||||||
|
d.setDeclarationUnit(r.getDeclarationUnit());
|
||||||
|
d.setLiter(r.getLiter());
|
||||||
|
d.setBoxCount(r.getBoxCount());
|
||||||
|
d.setPieceCount(r.getPieceCount());
|
||||||
|
d.setDimensions(r.getDimensions());
|
||||||
|
// 入库数量:优先使用入库数量,如果为空则使用仓库数量
|
||||||
|
BigDecimal inboundQuantity = r.getInboundQuantity();
|
||||||
|
if (inboundQuantity == null) {
|
||||||
|
inboundQuantity = r.getWarehouseQuantity();
|
||||||
|
}
|
||||||
|
d.setInboundQuantity(inboundQuantity);
|
||||||
|
// 单位:优先使用Excel中的"单位"字段,如果为空则使用"申报单位",如果还为空则从物料主数据获取
|
||||||
|
String unit = StringUtils.isNotBlank(r.getUnit()) ? r.getUnit() : null;
|
||||||
|
if (StringUtils.isBlank(unit) && StringUtils.isNotBlank(r.getDeclarationUnit())) {
|
||||||
|
unit = r.getDeclarationUnit();
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(unit) && StringUtils.isNotBlank(materialBaseInfoPO.getUnitName())) {
|
||||||
|
unit = materialBaseInfoPO.getUnitName();
|
||||||
|
}
|
||||||
|
d.setUnit(unit);
|
||||||
|
// 总净重/总毛重/总体积/总面积:优先使用主字段,如果为空则使用备用字段
|
||||||
|
d.setTotalNetWeight(r.getTotalNetWeight() != null ? r.getTotalNetWeight() : r.getTotalNetWeight2());
|
||||||
|
d.setTotalGrossWeight(r.getTotalGrossWeight() != null ? r.getTotalGrossWeight() : r.getTotalGrossWeight2());
|
||||||
|
d.setTotalVolume(r.getTotalVolume() != null ? r.getTotalVolume() : r.getTotalVolume2());
|
||||||
|
d.setTotalArea(r.getTotalArea() != null ? r.getTotalArea() : r.getTotalArea2());
|
||||||
|
d.setBatchRefNo(r.getBatchRefNo());
|
||||||
|
d.setSheetRefNo(r.getSheetRefNo());
|
||||||
|
// 原产国:优先使用Excel中的数据,如果为空则从物料主数据获取
|
||||||
|
String originCountry = StringUtils.isNotBlank(r.getOriginCountry()) ? r.getOriginCountry() : null;
|
||||||
|
if (StringUtils.isBlank(originCountry) && StringUtils.isNotBlank(materialBaseInfoPO.getOriginCountry())) {
|
||||||
|
originCountry = materialBaseInfoPO.getOriginCountry();
|
||||||
|
}
|
||||||
|
d.setOriginCountry(originCountry);
|
||||||
|
d.setBoxPalletNo(r.getBoxPalletNo());
|
||||||
|
d.setTotalPrice(r.getTotalPrice());
|
||||||
|
// 币制:优先使用Excel中的数据,如果为空则从物料主数据获取
|
||||||
|
String currency = StringUtils.isNotBlank(r.getCurrency()) ? r.getCurrency() : null;
|
||||||
|
if (StringUtils.isBlank(currency) && StringUtils.isNotBlank(materialBaseInfoPO.getCurrency())) {
|
||||||
|
currency = materialBaseInfoPO.getCurrency();
|
||||||
|
}
|
||||||
|
d.setCurrency(currency);
|
||||||
|
d.setRemark(r.getDetailRemark());
|
||||||
|
|
||||||
|
details.add(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果该订单的所有明细行都被跳过(details为空),跳过该订单
|
||||||
|
if (details.isEmpty()) {
|
||||||
|
log.warn("跳过入库单:委托编号NO [{}] 的所有明细行都因数据不完整被跳过,该入库单将不会被导入", h.getConsignmentNo());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
order.setMaterialDetailList(details);
|
||||||
|
|
||||||
|
// 导入入库单时,计划数量取所有明细行的入库数量之和
|
||||||
|
BigDecimal totalInboundQuantity = details.stream()
|
||||||
|
.map(detail -> detail.getInboundQuantity() != null ? detail.getInboundQuantity() : BigDecimal.ZERO)
|
||||||
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
order.setQuantity(totalInboundQuantity);
|
||||||
|
|
||||||
|
// 导入时补全货主/供应商/数据字典信息(与手动新增链路一致)
|
||||||
|
try {
|
||||||
|
shipperParam(order);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("导入入库单时设置货主信息失败,委托编号NO:{},货主名称:{},错误:{}",
|
||||||
|
h.getConsignmentNo(), order.getShipperName(), e.getMessage());
|
||||||
|
throw new ServiceException("导入失败:设置货主信息失败(委托编号NO=" + h.getConsignmentNo()
|
||||||
|
+ ",货主名称=" + order.getShipperName() + ")。" + e.getMessage());
|
||||||
|
}
|
||||||
|
supplierParam(order);
|
||||||
|
setDataDict(order);
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入用:按料号跨库查询物料基础信息(查询结果需在Java侧核对货主归属)
|
||||||
|
*/
|
||||||
|
private MaterialBaseInfoPO safeGetMaterialByCode(String materialCode, LoginUser loginUser) {
|
||||||
|
try {
|
||||||
|
Long orgId = loginUser.getUserPo() != null ? loginUser.getUserPo().getOrganizationId() : null;
|
||||||
|
return reservationStockInOrderMapper.getByCode(materialCode, orgId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("按料号查询物料基础信息失败,料号: [{}], 错误: {}", materialCode, e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将表头信息应用到每一行明细(明细行值为空时使用表头值;客户名称/货主编号表头有值时强制应用)
|
||||||
|
*/
|
||||||
|
private void applyHeaderToRows(ReservationStockInOrderImportRowDTO headerInfo, List<ReservationStockInOrderImportRowDTO> rows) {
|
||||||
|
for (ReservationStockInOrderImportRowDTO row : rows) {
|
||||||
|
if (row == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 应用表头中的委托编号NO
|
||||||
|
if (StringUtils.isBlank(row.getConsignmentNo()) && StringUtils.isNotBlank(headerInfo.getConsignmentNo())) {
|
||||||
|
row.setConsignmentNo(headerInfo.getConsignmentNo());
|
||||||
|
}
|
||||||
|
// 表头有客户名称/货主编号时,强制应用到所有明细行
|
||||||
|
if (StringUtils.isNotBlank(headerInfo.getShipperName())) {
|
||||||
|
row.setShipperName(headerInfo.getShipperName());
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(headerInfo.getShipperCode())) {
|
||||||
|
row.setShipperCode(headerInfo.getShipperCode());
|
||||||
|
}
|
||||||
|
// 应用其他表头字段(如果明细行为空,则使用表头值)
|
||||||
|
row.setTo(firstNotBlank(row.getTo(), headerInfo.getTo()));
|
||||||
|
row.setContactPerson(firstNotBlank(row.getContactPerson(), headerInfo.getContactPerson()));
|
||||||
|
row.setVehicleInfo(firstNotBlank(row.getVehicleInfo(), headerInfo.getVehicleInfo()));
|
||||||
|
row.setCarrier(firstNotBlank(row.getCarrier(), headerInfo.getCarrier()));
|
||||||
|
row.setTel(firstNotBlank(row.getTel(), headerInfo.getTel()));
|
||||||
|
row.setDriverSignature(firstNotBlank(row.getDriverSignature(), headerInfo.getDriverSignature()));
|
||||||
|
row.setFax(firstNotBlank(row.getFax(), headerInfo.getFax()));
|
||||||
|
row.setDeclarationArea(firstNotBlank(row.getDeclarationArea(), headerInfo.getDeclarationArea()));
|
||||||
|
row.setManufacturer(firstNotBlank(row.getManufacturer(), headerInfo.getManufacturer()));
|
||||||
|
row.setCustomsBrokerInfo(firstNotBlank(row.getCustomsBrokerInfo(), headerInfo.getCustomsBrokerInfo()));
|
||||||
|
row.setDeliveryAddress(firstNotBlank(row.getDeliveryAddress(), headerInfo.getDeliveryAddress()));
|
||||||
|
row.setEntryExitCustoms(firstNotBlank(row.getEntryExitCustoms(), headerInfo.getEntryExitCustoms()));
|
||||||
|
row.setOriginDestinationCountry(firstNotBlank(row.getOriginDestinationCountry(), headerInfo.getOriginDestinationCountry()));
|
||||||
|
row.setContainerNo(firstNotBlank(row.getContainerNo(), headerInfo.getContainerNo()));
|
||||||
|
row.setTransportMethod(firstNotBlank(row.getTransportMethod(), headerInfo.getTransportMethod()));
|
||||||
|
row.setSupervisionMethod(firstNotBlank(row.getSupervisionMethod(), headerInfo.getSupervisionMethod()));
|
||||||
|
if (row.getCustomsInspection() == null) {
|
||||||
|
row.setCustomsInspection(headerInfo.getCustomsInspection());
|
||||||
|
}
|
||||||
|
if (row.getNeedCustomsDeclaration() == null) {
|
||||||
|
row.setNeedCustomsDeclaration(headerInfo.getNeedCustomsDeclaration());
|
||||||
|
}
|
||||||
|
if (row.getNeedTransport() == null) {
|
||||||
|
row.setNeedTransport(headerInfo.getNeedTransport());
|
||||||
|
}
|
||||||
|
row.setWarehouseDate(firstNotBlank(row.getWarehouseDate(), headerInfo.getWarehouseDate()));
|
||||||
|
row.setCompletionTime(firstNotBlank(row.getCompletionTime(), headerInfo.getCompletionTime()));
|
||||||
|
row.setOrderDate(firstNotBlank(row.getOrderDate(), headerInfo.getOrderDate()));
|
||||||
|
row.setRemark(firstNotBlank(row.getRemark(), headerInfo.getRemark()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描表头区域(第1-12行)按标签提取字段值:
|
||||||
|
* 值优先取标签同行右侧最近的合法单元格,其次取下一行同列,最后取标签文本冒号后的内容;
|
||||||
|
* 看起来仍是标签文本(含冒号或命中标签关键词)的值会被跳过
|
||||||
|
*/
|
||||||
|
private void fillHeaderInfoByLabels(Sheet sheet, ReservationStockInOrderImportRowDTO headerInfo) {
|
||||||
|
for (int rowIdx = 0; rowIdx < 12; rowIdx++) {
|
||||||
|
Row row = sheet.getRow(rowIdx);
|
||||||
|
if (row == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int colIdx = 0; colIdx < row.getPhysicalNumberOfCells(); colIdx++) {
|
||||||
|
Cell labelCell = row.getCell(colIdx);
|
||||||
|
if (labelCell == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String labelValue = getCellValueAsString(labelCell);
|
||||||
|
if (StringUtils.isBlank(labelValue)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 标签文本冒号后的内容(兜底值)
|
||||||
|
String extractedFromSameCell = null;
|
||||||
|
if (labelValue.contains(":")) {
|
||||||
|
String[] parts = labelValue.split(":", 2);
|
||||||
|
if (parts.length > 1) {
|
||||||
|
extractedFromSameCell = parts[1].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String value = findValueRightwards(row, colIdx);
|
||||||
|
if (StringUtils.isBlank(value)) {
|
||||||
|
value = findValueBelow(sheet, rowIdx, colIdx);
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(value)) {
|
||||||
|
value = extractedFromSameCell;
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dispatchHeaderField(labelValue, value, headerInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在同行右侧(最多10列)查找第一个合法值
|
||||||
|
*/
|
||||||
|
private String findValueRightwards(Row row, int colIdx) {
|
||||||
|
for (int searchColIdx = colIdx + 1; searchColIdx < colIdx + 10 && searchColIdx < row.getPhysicalNumberOfCells(); searchColIdx++) {
|
||||||
|
Cell valueCell = row.getCell(searchColIdx);
|
||||||
|
if (valueCell == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String cellValue = getCellValueAsString(valueCell);
|
||||||
|
if (StringUtils.isNotBlank(cellValue)) {
|
||||||
|
return cellValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在下一行同列查找合法值(模板变体:值写在标签下一行)
|
||||||
|
*/
|
||||||
|
private String findValueBelow(Sheet sheet, int rowIdx, int colIdx) {
|
||||||
|
Row nextRow = sheet.getRow(rowIdx + 1);
|
||||||
|
if (nextRow == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Cell nextRowCell = nextRow.getCell(colIdx);
|
||||||
|
if (nextRowCell == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String nextRowValue = getCellValueAsString(nextRowCell);
|
||||||
|
return StringUtils.isNotBlank(nextRowValue) ? nextRowValue : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按标签关键词分发到表头字段
|
||||||
|
*/
|
||||||
|
private void dispatchHeaderField(String labelValue, String value, ReservationStockInOrderImportRowDTO headerInfo) {
|
||||||
|
// 备注:值内容任意,不做标签过滤
|
||||||
|
if (labelValue.contains("备注")) {
|
||||||
|
if (StringUtils.isBlank(headerInfo.getRemark())) {
|
||||||
|
headerInfo.setRemark(value);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 委托编号NO(支持"委托编号NO"、"委托编号"等格式,排除标签本身)
|
||||||
|
if (labelValue.contains("委托编号")) {
|
||||||
|
if (StringUtils.isBlank(headerInfo.getConsignmentNo())
|
||||||
|
&& !value.contains("委托编号") && !value.contains("NO")
|
||||||
|
&& !"委托编号NO".equals(value) && !"委托编号".equals(value)) {
|
||||||
|
headerInfo.setConsignmentNo(value);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 客户名称
|
||||||
|
if (labelValue.contains("客户名称")) {
|
||||||
|
if (StringUtils.isBlank(headerInfo.getShipperName())
|
||||||
|
&& !value.contains("客户") && !"客户名称".equals(value)) {
|
||||||
|
headerInfo.setShipperName(value);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// To(需要精确匹配,避免误匹配)
|
||||||
|
if (isToLabel(labelValue)) {
|
||||||
|
if (StringUtils.isBlank(headerInfo.getTo()) && !isToLabel(value) && !looksLikeLabel(value)) {
|
||||||
|
headerInfo.setTo(value);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 值是标签文本(含冒号或命中标签关键词)时跳过
|
||||||
|
if (value.contains(":") || value.contains(":") || looksLikeLabel(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (labelValue.contains("联系人") && StringUtils.isBlank(headerInfo.getContactPerson())) {
|
||||||
|
headerInfo.setContactPerson(value);
|
||||||
|
} else if ((labelValue.contains("车型") || labelValue.contains("车牌")) && StringUtils.isBlank(headerInfo.getVehicleInfo())) {
|
||||||
|
headerInfo.setVehicleInfo(value);
|
||||||
|
} else if ((labelValue.contains("承运方") || labelValue.contains("承运商")) && StringUtils.isBlank(headerInfo.getCarrier())) {
|
||||||
|
headerInfo.setCarrier(value);
|
||||||
|
} else if (labelValue.contains("Tel") && StringUtils.isBlank(headerInfo.getTel())) {
|
||||||
|
headerInfo.setTel(value);
|
||||||
|
} else if (labelValue.contains("司机签名") && StringUtils.isBlank(headerInfo.getDriverSignature())) {
|
||||||
|
headerInfo.setDriverSignature(value);
|
||||||
|
} else if (labelValue.contains("入仓日期") && StringUtils.isBlank(headerInfo.getWarehouseDate())) {
|
||||||
|
headerInfo.setWarehouseDate(value);
|
||||||
|
} else if (labelValue.contains("Fax") && StringUtils.isBlank(headerInfo.getFax())) {
|
||||||
|
headerInfo.setFax(value);
|
||||||
|
} else if (labelValue.contains("申报地关区") && StringUtils.isBlank(headerInfo.getDeclarationArea())) {
|
||||||
|
headerInfo.setDeclarationArea(value);
|
||||||
|
} else if (labelValue.contains("生产商") && StringUtils.isBlank(headerInfo.getManufacturer())) {
|
||||||
|
headerInfo.setManufacturer(value);
|
||||||
|
} else if (labelValue.contains("报关员") && StringUtils.isBlank(headerInfo.getCustomsBrokerInfo())) {
|
||||||
|
headerInfo.setCustomsBrokerInfo(value);
|
||||||
|
} else if (labelValue.contains("送货地址") && StringUtils.isBlank(headerInfo.getDeliveryAddress())) {
|
||||||
|
headerInfo.setDeliveryAddress(value);
|
||||||
|
} else if (labelValue.contains("进出境关别") && StringUtils.isBlank(headerInfo.getEntryExitCustoms())) {
|
||||||
|
headerInfo.setEntryExitCustoms(value);
|
||||||
|
} else if ((labelValue.contains("起运") || labelValue.contains("运抵国")) && StringUtils.isBlank(headerInfo.getOriginDestinationCountry())) {
|
||||||
|
headerInfo.setOriginDestinationCountry(value);
|
||||||
|
} else if (labelValue.contains("柜号") && StringUtils.isBlank(headerInfo.getContainerNo())) {
|
||||||
|
headerInfo.setContainerNo(value);
|
||||||
|
} else if (labelValue.contains("运输方式") && StringUtils.isBlank(headerInfo.getTransportMethod())) {
|
||||||
|
headerInfo.setTransportMethod(value);
|
||||||
|
} else if (labelValue.contains("监管方式") && StringUtils.isBlank(headerInfo.getSupervisionMethod())) {
|
||||||
|
headerInfo.setSupervisionMethod(value);
|
||||||
|
} else if (labelValue.contains("海关是否查验货物") && headerInfo.getCustomsInspection() == null) {
|
||||||
|
headerInfo.setCustomsInspection(parseYesNo(value));
|
||||||
|
} else if (labelValue.contains("是否需要报关") && headerInfo.getNeedCustomsDeclaration() == null) {
|
||||||
|
headerInfo.setNeedCustomsDeclaration(parseYesNo(value));
|
||||||
|
} else if (labelValue.contains("是否需要运输") && headerInfo.getNeedTransport() == null) {
|
||||||
|
headerInfo.setNeedTransport(parseYesNo(value));
|
||||||
|
} else if (labelValue.contains("完成时间") && StringUtils.isBlank(headerInfo.getCompletionTime())) {
|
||||||
|
headerInfo.setCompletionTime(value);
|
||||||
|
} else if (labelValue.contains("下单日期") && StringUtils.isBlank(headerInfo.getOrderDate())) {
|
||||||
|
headerInfo.setOrderDate(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为To标签(精确匹配,避免把Value/Total等英文词误认为To)
|
||||||
|
*/
|
||||||
|
private boolean isToLabel(String text) {
|
||||||
|
if (text == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String t = text.trim();
|
||||||
|
return "To".equals(t) || "To:".equals(t) || "To:".equals(t) || t.startsWith("To:") || t.startsWith("To:");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 值是否看起来仍是标签文本(命中常见标签关键词)
|
||||||
|
*/
|
||||||
|
private boolean looksLikeLabel(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String v = value.trim();
|
||||||
|
return v.contains("委托编号") || v.contains("司机签名") || v.contains("完成时间") || v.contains("下单日期")
|
||||||
|
|| v.contains("入仓日期") || v.contains("联系人") || v.contains("Tel") || v.contains("Fax")
|
||||||
|
|| v.contains("客户") || v.contains("地址") || v.contains("日期") || v.contains("方式")
|
||||||
|
|| v.contains("关区") || v.contains("关别") || v.contains("报关员") || v.contains("生产商")
|
||||||
|
|| v.contains("承运方") || v.contains("承运商") || v.contains("起运") || v.contains("运抵国")
|
||||||
|
|| v.contains("地区") || v.contains("车型") || v.contains("车牌") || v.contains("柜号")
|
||||||
|
|| v.contains("海关是否查验货物") || v.contains("是否需要报关") || v.contains("是否需要运输");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是/否(或1/0)转Integer;无法识别返回null
|
||||||
|
*/
|
||||||
|
private Integer parseYesNo(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String v = value.trim();
|
||||||
|
if (v.contains("是") || "1".equals(v)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (v.contains("否") || "0".equals(v)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取第一个非空字符串
|
||||||
|
*/
|
||||||
|
private String firstNotBlank(String a, String b) {
|
||||||
|
return StringUtils.isNotBlank(a) ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取登录人姓名(优先UserPo.userName,其次realname,最后username兜底)
|
||||||
|
*/
|
||||||
|
private String resolveUserRealName(LoginUser loginUser) {
|
||||||
|
if (loginUser.getUserPo() != null && StringUtils.isNotBlank(loginUser.getUserPo().getUserName())) {
|
||||||
|
return loginUser.getUserPo().getUserName();
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(loginUser.getRealname())) {
|
||||||
|
return loginUser.getRealname();
|
||||||
|
}
|
||||||
|
return loginUser.getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析日期字符串为Date对象
|
* 解析日期字符串为Date对象
|
||||||
* 支持多种日期格式:yyyy-MM-dd, yyyy/MM/dd, yyyy年MM月dd日, Date.toString()格式等
|
* 支持多种日期格式:yyyy-MM-dd, yyyy/MM/dd, yyyy年MM月dd日, Date.toString()格式等
|
||||||
|
|||||||
+176
-2
@@ -13,6 +13,9 @@ import com.mhd.common.core.web.domain.AjaxResult;
|
|||||||
import com.mhd.common.security.utils.SecurityUtils;
|
import com.mhd.common.security.utils.SecurityUtils;
|
||||||
import com.mhd.oms.domain.businessDocumentOrder.entity.BusinessDocumentOrder;
|
import com.mhd.oms.domain.businessDocumentOrder.entity.BusinessDocumentOrder;
|
||||||
import com.mhd.oms.domain.businessDocumentOrder.repository.mapper.BusinessDocumentOrderMapper;
|
import com.mhd.oms.domain.businessDocumentOrder.repository.mapper.BusinessDocumentOrderMapper;
|
||||||
|
import com.mhd.oms.domain.electronicSignature.entity.ElectronicSignature;
|
||||||
|
import com.mhd.oms.domain.electronicSignature.repository.facade.IElectronicSignatureMaterialDetailService;
|
||||||
|
import com.mhd.oms.domain.electronicSignature.repository.facade.IElectronicSignatureService;
|
||||||
import com.mhd.oms.domain.executionInMaterialDetail.entity.ExecutionInMaterialDetail;
|
import com.mhd.oms.domain.executionInMaterialDetail.entity.ExecutionInMaterialDetail;
|
||||||
import com.mhd.oms.domain.executionInMaterialDetail.repository.facade.IExecutionInMaterialDetailService;
|
import com.mhd.oms.domain.executionInMaterialDetail.repository.facade.IExecutionInMaterialDetailService;
|
||||||
import com.mhd.oms.domain.executionStockInOrder.entity.ExecutionStockInOrder;
|
import com.mhd.oms.domain.executionStockInOrder.entity.ExecutionStockInOrder;
|
||||||
@@ -91,6 +94,10 @@ public class ReservationStockInOrderDomainService {
|
|||||||
private ExecutionStockInOrderMapper executionStockInOrderMapper;
|
private ExecutionStockInOrderMapper executionStockInOrderMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ReserveStockInOrderMapper reserveStockInOrderMapper;
|
private ReserveStockInOrderMapper reserveStockInOrderMapper;
|
||||||
|
@Autowired
|
||||||
|
private IElectronicSignatureService electronicSignatureService;
|
||||||
|
@Autowired
|
||||||
|
private IElectronicSignatureMaterialDetailService electronicSignatureMaterialDetailService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询入库单列表
|
* 分页查询入库单列表
|
||||||
@@ -169,6 +176,10 @@ public class ReservationStockInOrderDomainService {
|
|||||||
// //统计总体积
|
// //统计总体积
|
||||||
// BigDecimal volumeLimit = stockInOrderDO.getMaterialDetailList().stream().map(ReservationInMaterialDetailDO::getVolumeLimit).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
|
// BigDecimal volumeLimit = stockInOrderDO.getMaterialDetailList().stream().map(ReservationInMaterialDetailDO::getVolumeLimit).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
// stockInOrderDO.setVolumeLimit(volumeLimit);
|
// stockInOrderDO.setVolumeLimit(volumeLimit);
|
||||||
|
// 订单来源为空时兜底为手动创建(导入/预约下发等路径已显式设置,不受影响),下发执行单时随业务单继承
|
||||||
|
if (stockInOrderDO.getReservationOrderSource() == null || stockInOrderDO.getReservationOrderSource().isEmpty()) {
|
||||||
|
stockInOrderDO.setReservationOrderSource("手动创建");
|
||||||
|
}
|
||||||
stockInOrderService.insert(stockInOrderDO);
|
stockInOrderService.insert(stockInOrderDO);
|
||||||
return materialDetailService.batchInsert(stockInOrderDO.getInOrderNumber(), stockInOrderDO.getMaterialDetailList());
|
return materialDetailService.batchInsert(stockInOrderDO.getInOrderNumber(), stockInOrderDO.getMaterialDetailList());
|
||||||
}
|
}
|
||||||
@@ -607,7 +618,6 @@ public class ReservationStockInOrderDomainService {
|
|||||||
reservationStockInOrder.setOrderTypeName("普通入库");
|
reservationStockInOrder.setOrderTypeName("普通入库");
|
||||||
reservationStockInOrder.setBusinessType("pu_tong_ru_ku");
|
reservationStockInOrder.setBusinessType("pu_tong_ru_ku");
|
||||||
}
|
}
|
||||||
reservationStockInOrder.setReservationOrderSource("客户预约");
|
|
||||||
reservationStockInOrder.setWarehouseId(warehouseId);
|
reservationStockInOrder.setWarehouseId(warehouseId);
|
||||||
if (warehouseId == null || warehouseId == 0L){
|
if (warehouseId == null || warehouseId == 0L){
|
||||||
reservationStockInOrder.setWarehouseId(byId.getWarehouseId());
|
reservationStockInOrder.setWarehouseId(byId.getWarehouseId());
|
||||||
@@ -767,6 +777,171 @@ public class ReservationStockInOrderDomainService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反审入库业务单(需求:已入库/已出库的单子客户通知需要回退的场景,单子回退到最原始状态):
|
||||||
|
* 页面状态列按 issueStatus(0-已创建 1-进行中 2-已审核 3-已驳回 4-已入库 5-部分完成)展示,
|
||||||
|
* 反审条件按 issueStatus 判断——进行中(1)/已审核(2)/已入库(4)/部分完成(5)可反审,
|
||||||
|
* 已创建(0)无需反审、已驳回(3)可直接编辑;已取消/已关闭(status 6/7)兜底拦截。
|
||||||
|
* 反审动作("审核不通过"同语义 + 全链路回退,补齐 auditState 审核不通过缺失的下游联动):
|
||||||
|
* <ul>
|
||||||
|
* <li>业务单状态改为"已驳回"(issueStatus=3),打已反审标记(REVERSE_AUDIT_STATUS=1)落入【已反审】tab,
|
||||||
|
* 之后可「编辑」→重新「审核」→重新「下发」生成全新执行单</li>
|
||||||
|
* <li>入库执行单+物料明细软删(del_flag=2)并打已反审标记——执行单【已反审】tab按
|
||||||
|
* "已软删+已反审"查询归档数据;软删后不出现在正常tab,避免归档单被误「审核/下发」</li>
|
||||||
|
* <li>清空WMS完成上架跨库回写的完成数据(业务单 IN_QUANTITY/STORAGE_TIME、明细 INBOUND_ITEMS/TOTAL_NET_WEIGHT)</li>
|
||||||
|
* <li>WMS侧逐执行单回退(omsReverseInOrder):W入库单有出库记录则拦截(按入库单号+动作"分配库存/出库"
|
||||||
|
* 查库存调整记录>0)→ 库存还原扣减 → W入库单/W收货单/W上架单整链软删并打已反审标记
|
||||||
|
* (数据划归W入库单/W收货单/W上架单【已反审】tab归档)→
|
||||||
|
* 写1条W入库异常【反审记录】用于记录跟踪</li>
|
||||||
|
* </ul>
|
||||||
|
* 已推送关务(pushStatus=3)直接拦截。WMS回退幂等,反审失败可安全重试。
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Boolean reverseAudit(ReservationStockInOrderDO stockInOrderDO) {
|
||||||
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
|
// 获取用户姓名(优先从UserPo获取,如果为空则使用realname,最后使用username作为兜底)
|
||||||
|
String userRealName = loginUser.getUserPo() != null && loginUser.getUserPo().getUserName() != null
|
||||||
|
? loginUser.getUserPo().getUserName()
|
||||||
|
: (loginUser.getRealname() != null ? loginUser.getRealname() : loginUser.getUsername());
|
||||||
|
|
||||||
|
List<Long> inOrderIds = stockInOrderDO.getInOrderIds();
|
||||||
|
if (inOrderIds == null || inOrderIds.isEmpty()) {
|
||||||
|
throw new ServiceException("未选择反审的入库业务单");
|
||||||
|
}
|
||||||
|
Date now = new Date();
|
||||||
|
for (Long inOrderId : inOrderIds) {
|
||||||
|
ReservationStockInOrder businessOrder = stockInOrderService.getById(inOrderId);
|
||||||
|
if (businessOrder == null
|
||||||
|
|| (businessOrder.getDelFlag() != null && businessOrder.getDelFlag() != 1)) {
|
||||||
|
throw new ServiceException("入库业务单不存在或已被删除,无法反审");
|
||||||
|
}
|
||||||
|
String businessNumber = businessOrder.getInOrderNumber();
|
||||||
|
Integer issueStatus = businessOrder.getIssueStatus();
|
||||||
|
if (issueStatus == null || issueStatus == 0) {
|
||||||
|
throw new ServiceException("入库业务单[" + businessNumber + "]为已创建状态,无需反审");
|
||||||
|
}
|
||||||
|
if (issueStatus == 3) {
|
||||||
|
throw new ServiceException("入库业务单[" + businessNumber + "]已驳回,无需反审,可直接编辑后重新提交审核");
|
||||||
|
}
|
||||||
|
if (issueStatus != 1 && issueStatus != 2 && issueStatus != 4 && issueStatus != 5) {
|
||||||
|
throw new ServiceException("入库业务单[" + businessNumber + "]仅进行中/已审核/已入库状态可反审,当前状态值:" + issueStatus);
|
||||||
|
}
|
||||||
|
if (businessOrder.getStatus() != null && (businessOrder.getStatus() == 6 || businessOrder.getStatus() == 7)) {
|
||||||
|
throw new ServiceException("入库业务单[" + businessNumber + "]已取消/已关闭,无法反审");
|
||||||
|
}
|
||||||
|
if (businessOrder.getPushStatus() != null && businessOrder.getPushStatus() == 3) {
|
||||||
|
throw new ServiceException("入库业务单[" + businessNumber + "]已推送关务/NC,无法反审,请先在关务侧处理");
|
||||||
|
}
|
||||||
|
|
||||||
|
//该业务单下全部有效执行单:逐张调WMS回退后软删并打已反审标记
|
||||||
|
List<ExecutionStockInOrder> executionOrders = executionStockInOrderService.list(
|
||||||
|
new LambdaQueryWrapper<ExecutionStockInOrder>()
|
||||||
|
.eq(ExecutionStockInOrder::getBusinessInOrderNumber, businessNumber)
|
||||||
|
.eq(ExecutionStockInOrder::getDelFlag, 1));
|
||||||
|
for (ExecutionStockInOrder executionOrder : executionOrders) {
|
||||||
|
String executionNumber = executionOrder.getInOrderNumber();
|
||||||
|
if (executionOrder.getPushStatus() != null && executionOrder.getPushStatus() == 3) {
|
||||||
|
throw new ServiceException("执行入库单[" + executionNumber + "]已推送关务/NC,无法反审,请先在关务侧处理");
|
||||||
|
}
|
||||||
|
//WMS侧回退(对称收货/上架完成链路,见 WMS StockInOrderDomainService.reverseExecutionOrder):
|
||||||
|
//有出库/库存已被分配时WMS报错,此处抛错整体回滚;WMS无数据时幂等成功(未下发执行单反审)
|
||||||
|
StockInOrder reverseParam = new StockInOrder();
|
||||||
|
reverseParam.setInOrderNumber(executionNumber);
|
||||||
|
reverseParam.setRemark("业务单号:" + businessNumber
|
||||||
|
+ (StringUtils.isNotEmpty(stockInOrderDO.getReverseAuditRemark())
|
||||||
|
? ",反审原因:" + stockInOrderDO.getReverseAuditRemark() : ""));
|
||||||
|
//组织三字段:WMS幂等跳过分支(未下发单反审)写反审记录时取不到W入库单,需经Feign透传
|
||||||
|
reverseParam.setOrganizationId(executionOrder.getOrganizationId());
|
||||||
|
reverseParam.setOrganizationName(executionOrder.getOrganizationName());
|
||||||
|
reverseParam.setTopOrganizationId(executionOrder.getTopOrganizationId());
|
||||||
|
reverseParam.setUpdateBy(loginUser.getUserid());
|
||||||
|
reverseParam.setUpdateByName(userRealName);
|
||||||
|
AjaxResult reverseResult = wmsServiceFeign.omsReverseInOrder(reverseParam);
|
||||||
|
if (reverseResult == null || !"200".equals(String.valueOf(reverseResult.get("code")))) {
|
||||||
|
throw new ServiceException("反审失败:执行入库单[" + executionNumber + "]WMS回退失败:"
|
||||||
|
+ (reverseResult == null ? "WMS服务不可用或已降级" : reverseResult.get("msg")));
|
||||||
|
}
|
||||||
|
//清理下发自动审核生成的待签名电子签名记录(signatureStatus=1,已签名记录保留作凭证)
|
||||||
|
cancelPendingSignature(executionNumber);
|
||||||
|
//执行单+物料明细软删(del_flag=2)并打已反审标记:
|
||||||
|
//执行单【已反审】tab按"已软删+已反审"(del_flag=2 and REVERSE_AUDIT_STATUS=1)查询归档数据;
|
||||||
|
//软删后不出现在正常tab,避免归档单被误「审核/下发」;业务单重新下发时生成全新执行单
|
||||||
|
executionInMaterialDetailService.update(new UpdateWrapper<ExecutionInMaterialDetail>().lambda()
|
||||||
|
.set(ExecutionInMaterialDetail::getDelFlag, 2)
|
||||||
|
.set(ExecutionInMaterialDetail::getUpdateBy, loginUser.getUserid())
|
||||||
|
.set(ExecutionInMaterialDetail::getUpdateByName, userRealName)
|
||||||
|
.set(ExecutionInMaterialDetail::getUpdateTime, now)
|
||||||
|
.eq(ExecutionInMaterialDetail::getInOrderNumber, executionNumber)
|
||||||
|
.eq(ExecutionInMaterialDetail::getDelFlag, 1));
|
||||||
|
executionStockInOrderService.update(new UpdateWrapper<ExecutionStockInOrder>().lambda()
|
||||||
|
.set(ExecutionStockInOrder::getDelFlag, 2)
|
||||||
|
.set(ExecutionStockInOrder::getReverseAuditStatus, 1)
|
||||||
|
.set(ExecutionStockInOrder::getReverseAuditRemark, stockInOrderDO.getReverseAuditRemark())
|
||||||
|
.set(ExecutionStockInOrder::getReverseAuditBy, loginUser.getUserid())
|
||||||
|
.set(ExecutionStockInOrder::getReverseAuditByName, userRealName)
|
||||||
|
.set(ExecutionStockInOrder::getReverseAuditTime, now)
|
||||||
|
.set(ExecutionStockInOrder::getUpdateBy, loginUser.getUserid())
|
||||||
|
.set(ExecutionStockInOrder::getUpdateByName, userRealName)
|
||||||
|
.set(ExecutionStockInOrder::getUpdateTime, now)
|
||||||
|
.eq(ExecutionStockInOrder::getInOrderId, executionOrder.getInOrderId())
|
||||||
|
.eq(ExecutionStockInOrder::getDelFlag, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
//清空WMS完成上架跨库回写的业务单明细入库数量(INBOUND_ITEMS/TOTAL_NET_WEIGHT 上架时按上架量覆写过)
|
||||||
|
materialDetailService.update(new UpdateWrapper<ReservationInMaterialDetail>().lambda()
|
||||||
|
.set(ReservationInMaterialDetail::getInboundQuantity, BigDecimal.ZERO)
|
||||||
|
.set(ReservationInMaterialDetail::getTotalNetWeight, BigDecimal.ZERO)
|
||||||
|
.eq(ReservationInMaterialDetail::getInOrderNumber, businessNumber)
|
||||||
|
.eq(ReservationInMaterialDetail::getDelFlag, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
//业务单状态改为"已驳回"(与审核不通过同语义)+ 已反审标记落入【已反审】tab + 清空完成回写数据
|
||||||
|
return stockInOrderService.update(null, new UpdateWrapper<ReservationStockInOrder>().lambda()
|
||||||
|
.set(ReservationStockInOrder::getIssueStatus, 3)
|
||||||
|
//清空下发/执行时间(前端"是否执行"列按执行时间判断,不清会仍显示已执行,与取消下发 cancelIssueOrder 同口径)
|
||||||
|
.set(ReservationStockInOrder::getIssueTime, null)
|
||||||
|
//入库进度状态(1-已创建 2-已审核 3-收货中 4-上架中 5-已入库):首轮"完成上架"会经WMS回传写成5,
|
||||||
|
//不重置的话重新下发时copy给执行单→W入库单→收货单,收货单带着"已完成"状态导致前端不给收货按钮;
|
||||||
|
//重置为1(已创建),与手工新单首次下发链路一致(W入库单/收货单起点=1-待收货)
|
||||||
|
.set(ReservationStockInOrder::getStatus, 1)
|
||||||
|
.set(ReservationStockInOrder::getInQuantity, BigDecimal.ZERO)
|
||||||
|
.set(ReservationStockInOrder::getStorageTime, null)
|
||||||
|
.set(ReservationStockInOrder::getExecutionInOrderNumber, null)
|
||||||
|
//反审标记与反审痕迹(【已反审】tab 按 reverseAuditStatus=1 过滤;业务单本身不软删,保持可编辑重新下发)
|
||||||
|
.set(ReservationStockInOrder::getReverseAuditStatus, 1)
|
||||||
|
.set(ReservationStockInOrder::getReverseAuditRemark, stockInOrderDO.getReverseAuditRemark())
|
||||||
|
.set(ReservationStockInOrder::getReverseAuditBy, loginUser.getUserid())
|
||||||
|
.set(ReservationStockInOrder::getReverseAuditByName, userRealName)
|
||||||
|
.set(ReservationStockInOrder::getReverseAuditTime, now)
|
||||||
|
.set(ReservationStockInOrder::getUpdateBy, loginUser.getUserid())
|
||||||
|
.set(ReservationStockInOrder::getUpdateByName, userRealName)
|
||||||
|
.set(ReservationStockInOrder::getUpdateTime, now)
|
||||||
|
.in(ReservationStockInOrder::getInOrderId, inOrderIds));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反审时清理下发自动审核生成的待签名电子签名记录(signatureStatus=1)及其明细,
|
||||||
|
* 已签名记录保留作凭证;清理失败仅记日志不阻断反审主流程(同取消下发)
|
||||||
|
*/
|
||||||
|
private void cancelPendingSignature(String inOrderNumber) {
|
||||||
|
try {
|
||||||
|
List<ElectronicSignature> signatures = electronicSignatureService.list(
|
||||||
|
new LambdaQueryWrapper<ElectronicSignature>()
|
||||||
|
.eq(ElectronicSignature::getOrderNumber, inOrderNumber)
|
||||||
|
.eq(ElectronicSignature::getOrderType, "IN")
|
||||||
|
.eq(ElectronicSignature::getSignatureStatus, 1)
|
||||||
|
.eq(ElectronicSignature::getDelFlag, 1));
|
||||||
|
for (ElectronicSignature signature : signatures) {
|
||||||
|
electronicSignatureMaterialDetailService.deleteBySignatureId(signature.getSignatureId());
|
||||||
|
electronicSignatureService.update(new UpdateWrapper<ElectronicSignature>().lambda()
|
||||||
|
.set(ElectronicSignature::getDelFlag, 2)
|
||||||
|
.eq(ElectronicSignature::getSignatureId, signature.getSignatureId()));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("反审清理电子签名记录失败,入库单号:{}", inOrderNumber, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Boolean executeAndIssue(ReservationStockInOrderDO stockInOrderDO) {
|
public Boolean executeAndIssue(ReservationStockInOrderDO stockInOrderDO) {
|
||||||
// 防重复执行校验(与 issueOrder 的防重逻辑对齐):issueTime 有值表示已下发/执行过。
|
// 防重复执行校验(与 issueOrder 的防重逻辑对齐):issueTime 有值表示已下发/执行过。
|
||||||
@@ -870,7 +1045,6 @@ public class ReservationStockInOrderDomainService {
|
|||||||
reservationStockInOrder.setOrderTypeName("普通入库");
|
reservationStockInOrder.setOrderTypeName("普通入库");
|
||||||
reservationStockInOrder.setBusinessType("pu_tong_ru_ku");
|
reservationStockInOrder.setBusinessType("pu_tong_ru_ku");
|
||||||
}
|
}
|
||||||
reservationStockInOrder.setReservationOrderSource("客户预约");
|
|
||||||
reservationStockInOrder.setWarehouseId(warehouseId);
|
reservationStockInOrder.setWarehouseId(warehouseId);
|
||||||
// reservationStockInOrder.setOrganizationId(orgId);
|
// reservationStockInOrder.setOrganizationId(orgId);
|
||||||
// reservationStockInOrder.setOrganizationName(orgName);
|
// reservationStockInOrder.setOrganizationName(orgName);
|
||||||
|
|||||||
+17
@@ -515,4 +515,21 @@ public class ReservationStockOutOrder extends BaseVOEntity {
|
|||||||
@ApiModelProperty("折扣")
|
@ApiModelProperty("折扣")
|
||||||
private BigDecimal discount;
|
private BigDecimal discount;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审状态: 0-无 1-已反审(反审后status回退为1-已创建,重新审核通过自动复位0)")
|
||||||
|
private Integer reverseStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人id")
|
||||||
|
private Long reverseBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人姓名")
|
||||||
|
private String reverseByName;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审时间")
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date reverseTime;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审原因")
|
||||||
|
private String reverseRemark;
|
||||||
|
|
||||||
}
|
}
|
||||||
+6
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
|||||||
import com.mhd.oms.domain.reservationOutMaterialDetail.repository.po.ReservationOutMaterialDetailPO;
|
import com.mhd.oms.domain.reservationOutMaterialDetail.repository.po.ReservationOutMaterialDetailPO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.entity.ReservationStockOutOrder;
|
import com.mhd.oms.domain.reservationStockOutOrder.entity.ReservationStockOutOrder;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderPO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderPO;
|
||||||
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderTotalPO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDO;
|
||||||
|
|
||||||
|
|
||||||
@@ -22,6 +23,11 @@ public interface IReservationStockOutOrderService extends IService<ReservationSt
|
|||||||
*/
|
*/
|
||||||
public List<ReservationStockOutOrderPO> queryList(ReservationStockOutOrderDO stockOutOrderDO);
|
public List<ReservationStockOutOrderPO> queryList(ReservationStockOutOrderDO stockOutOrderDO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表合计:按查询条件全量(不分页)汇总一级明细的计划数量与出库数量
|
||||||
|
*/
|
||||||
|
public ReservationStockOutOrderTotalPO queryListTotal(ReservationStockOutOrderDO stockOutOrderDO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增出库单
|
* 新增出库单
|
||||||
*/
|
*/
|
||||||
|
|||||||
+6
@@ -6,6 +6,7 @@ import com.mhd.common.core.domain.po.UserPo;
|
|||||||
import com.mhd.oms.domain.reservationOutMaterialDetail.repository.po.ReservationOutMaterialDetailPO;
|
import com.mhd.oms.domain.reservationOutMaterialDetail.repository.po.ReservationOutMaterialDetailPO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.entity.ReservationStockOutOrder;
|
import com.mhd.oms.domain.reservationStockOutOrder.entity.ReservationStockOutOrder;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderPO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderPO;
|
||||||
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderTotalPO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDO;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
//import com.mhd.wms.domain.stockOutOrder.entity.ReservationStockOutOrder;
|
//import com.mhd.wms.domain.stockOutOrder.entity.ReservationStockOutOrder;
|
||||||
@@ -27,6 +28,11 @@ public interface ReservationStockOutOrderMapper extends BaseMapper<ReservationSt
|
|||||||
*/
|
*/
|
||||||
public List<ReservationStockOutOrderPO> queryList(ReservationStockOutOrderDO stockOutOrderDO);
|
public List<ReservationStockOutOrderPO> queryList(ReservationStockOutOrderDO stockOutOrderDO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表合计:按查询条件全量(不分页)汇总一级明细的计划数量与出库数量
|
||||||
|
*/
|
||||||
|
public ReservationStockOutOrderTotalPO queryListTotal(ReservationStockOutOrderDO stockOutOrderDO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 出库单提货单打印-表头
|
* 出库单提货单打印-表头
|
||||||
*/
|
*/
|
||||||
|
|||||||
+10
@@ -11,6 +11,7 @@ import com.mhd.oms.domain.reservationStockOutOrder.entity.ReservationStockOutOrd
|
|||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.facade.IReservationStockOutOrderService;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.facade.IReservationStockOutOrderService;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.mapper.ReservationStockOutOrderMapper;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.mapper.ReservationStockOutOrderMapper;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderPO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderPO;
|
||||||
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderTotalPO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDO;
|
||||||
import com.mhd.system.api.model.LoginUser;
|
import com.mhd.system.api.model.LoginUser;
|
||||||
//import com.mhd.wms.domain.deliveTask.repository.facade.IDeliveTaskTypeService;
|
//import com.mhd.wms.domain.deliveTask.repository.facade.IDeliveTaskTypeService;
|
||||||
@@ -48,6 +49,15 @@ public class ReservationStockOutOrderImpl extends ServiceImpl<ReservationStockOu
|
|||||||
return reservationStockOutOrderMapper.queryList(stockOutOrderDO);
|
return reservationStockOutOrderMapper.queryList(stockOutOrderDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表合计:按查询条件全量(不分页)汇总一级明细的计划数量与出库数量
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ReservationStockOutOrderTotalPO queryListTotal(ReservationStockOutOrderDO stockOutOrderDO)
|
||||||
|
{
|
||||||
|
return reservationStockOutOrderMapper.queryListTotal(stockOutOrderDO);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增出库单
|
* 新增出库单
|
||||||
*/
|
*/
|
||||||
|
|||||||
+17
@@ -538,4 +538,21 @@ public class ReservationStockOutOrderPO extends BaseVOEntity {
|
|||||||
@ApiModelProperty("折扣")
|
@ApiModelProperty("折扣")
|
||||||
private BigDecimal discount;
|
private BigDecimal discount;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审状态: 0-无 1-已反审(反审后status回退为1-已创建,重新审核通过自动复位0)")
|
||||||
|
private Integer reverseStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人id")
|
||||||
|
private Long reverseBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人姓名")
|
||||||
|
private String reverseByName;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审时间")
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date reverseTime;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审原因")
|
||||||
|
private String reverseRemark;
|
||||||
|
|
||||||
}
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package com.mhd.oms.domain.reservationStockOutOrder.repository.po;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出库业务单列表合计PO
|
||||||
|
*
|
||||||
|
* <p>当前筛选条件下不分页的全量合计,随分页列表一起返回
|
||||||
|
* (totalPlanQuantity/totalOutboundQuantity),计划数量与出库数量分开计算</p>
|
||||||
|
*
|
||||||
|
* @author rcx
|
||||||
|
* @date 2026-09-14
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value = "出库业务单列表合计对象", description = "出库业务单列表合计响应对象")
|
||||||
|
public class ReservationStockOutOrderTotalPO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty("计划数量总数(一级明细计划数量之和)")
|
||||||
|
private BigDecimal totalPlanQuantity;
|
||||||
|
|
||||||
|
@ApiModelProperty("出库数量总数(一级明细出库数量之和)")
|
||||||
|
private BigDecimal totalOutboundQuantity;
|
||||||
|
}
|
||||||
+15
@@ -591,4 +591,19 @@ public class ReservationStockOutOrderDO extends BaseVOEntity {
|
|||||||
@ApiModelProperty("折扣")
|
@ApiModelProperty("折扣")
|
||||||
private BigDecimal discount;
|
private BigDecimal discount;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审状态: 0-无 1-已反审(反审后status回退为1-已创建,重新审核通过自动复位0)")
|
||||||
|
private Integer reverseStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人id")
|
||||||
|
private Long reverseBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人姓名")
|
||||||
|
private String reverseByName;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审时间")
|
||||||
|
private Date reverseTime;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审原因")
|
||||||
|
private String reverseRemark;
|
||||||
|
|
||||||
}
|
}
|
||||||
+17
@@ -580,4 +580,21 @@ public class ReservationStockOutOrderDTO extends ReservationStockOutOrderBaseDTO
|
|||||||
@ApiModelProperty("折扣")
|
@ApiModelProperty("折扣")
|
||||||
private BigDecimal discount;
|
private BigDecimal discount;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审状态: 0-无 1-已反审(反审后status回退为1-已创建,重新审核通过自动复位0)")
|
||||||
|
private Integer reverseStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人id")
|
||||||
|
private Long reverseBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审人姓名")
|
||||||
|
private String reverseByName;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审时间")
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date reverseTime;
|
||||||
|
|
||||||
|
@ApiModelProperty("反审原因")
|
||||||
|
private String reverseRemark;
|
||||||
|
|
||||||
}
|
}
|
||||||
+22
-5
@@ -45,6 +45,7 @@ import com.mhd.oms.domain.reservationStockOutOrder.entity.ReservationStockOutOrd
|
|||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.facade.IReservationStockOutOrderService;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.facade.IReservationStockOutOrderService;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.mapper.ReservationStockOutOrderMapper;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.mapper.ReservationStockOutOrderMapper;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderPO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderPO;
|
||||||
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderTotalPO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDTO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDTO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.vo.GwOutDetail;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.vo.GwOutDetail;
|
||||||
@@ -85,6 +86,8 @@ import java.util.stream.Collectors;
|
|||||||
public class ReservationStockOutOrderApplicationService {
|
public class ReservationStockOutOrderApplicationService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ReservationStockOutOrderDomainService stockOutOrderDomainService;
|
private ReservationStockOutOrderDomainService stockOutOrderDomainService;
|
||||||
|
@Autowired
|
||||||
|
private com.mhd.oms.application.service.WarehouseInfoApplicationService warehouseInfoApplicationService;
|
||||||
// @Autowired
|
// @Autowired
|
||||||
// private ReservationOutMaterialDetailDomainService outMaterialDetailDomainService;
|
// private ReservationOutMaterialDetailDomainService outMaterialDetailDomainService;
|
||||||
// @Autowired
|
// @Autowired
|
||||||
@@ -244,6 +247,15 @@ public class ReservationStockOutOrderApplicationService {
|
|||||||
return stockOutOrderPOS;
|
return stockOutOrderPOS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表合计:按查询条件全量(不分页)汇总一级明细的计划数量与出库数量。
|
||||||
|
* 须在 queryList/queryList1 之后用同一个DO调用:数据权限条件由列表查询原地写入DO,
|
||||||
|
* 合计查询才能继承生效后的筛选;且 PageHelper startPage 只拦截下一条SQL,本查询不会被分页。
|
||||||
|
*/
|
||||||
|
public ReservationStockOutOrderTotalPO queryListTotal(ReservationStockOutOrderDO stockOutOrderDO) {
|
||||||
|
return stockOutOrderDomainService.queryListTotal(stockOutOrderDO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增出库单
|
* 新增出库单
|
||||||
@@ -881,6 +893,14 @@ public class ReservationStockOutOrderApplicationService {
|
|||||||
return stockOutOrderDomainService.cancelIssueOrder(stockOutOrderDO);
|
return stockOutOrderDomainService.cancelIssueOrder(stockOutOrderDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 反审出库业务单(已出库/已复核单据回退到已创建, WMS库存还原并生成反审异常记录)
|
||||||
|
* @date 2026/9/8
|
||||||
|
*/
|
||||||
|
public Boolean reverseAudit(ReservationStockOutOrderDO stockOutOrderDO){
|
||||||
|
return stockOutOrderDomainService.reverseAudit(stockOutOrderDO);
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean executeAndIssue(ReservationStockOutOrderDO stockOutOrderDO){
|
public Boolean executeAndIssue(ReservationStockOutOrderDO stockOutOrderDO){
|
||||||
return stockOutOrderDomainService.executeAndIssue(stockOutOrderDO);
|
return stockOutOrderDomainService.executeAndIssue(stockOutOrderDO);
|
||||||
}
|
}
|
||||||
@@ -905,11 +925,8 @@ public class ReservationStockOutOrderApplicationService {
|
|||||||
if (stockOutOrderDO.getWarehouseId() == null) {
|
if (stockOutOrderDO.getWarehouseId() == null) {
|
||||||
throw new ServiceException("仓库ID不能为空");
|
throw new ServiceException("仓库ID不能为空");
|
||||||
}
|
}
|
||||||
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(stockOutOrderDO.getWarehouseId());
|
// Feign失败走本地跨库兜底, 避免新增/编辑保存被"获取仓库信息失败"阻断
|
||||||
if(!"200".equals(String.valueOf(ajaxResult.get("code")))){
|
WarehouseFeignPO warehouseFeignPO = warehouseInfoApplicationService.getWarehouseInfo(stockOutOrderDO.getWarehouseId());
|
||||||
throw new ServiceException("获取仓库信息失败");
|
|
||||||
}
|
|
||||||
WarehouseFeignPO warehouseFeignPO = JSON.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), WarehouseFeignPO.class);
|
|
||||||
stockOutOrderDO.setWarehouseCode(warehouseFeignPO.getWarehouseCode());
|
stockOutOrderDO.setWarehouseCode(warehouseFeignPO.getWarehouseCode());
|
||||||
stockOutOrderDO.setWarehouseName(warehouseFeignPO.getWarehouseName());
|
stockOutOrderDO.setWarehouseName(warehouseFeignPO.getWarehouseName());
|
||||||
}
|
}
|
||||||
|
|||||||
+510
-24
@@ -18,6 +18,10 @@ import com.mhd.oms.domain.businessDeliveryDetailsLink.repository.facade.IBusines
|
|||||||
import com.mhd.oms.domain.businessDeliveryDetailsLink.repository.mapper.BusinessDeliveryDetailsLinkMapper;
|
import com.mhd.oms.domain.businessDeliveryDetailsLink.repository.mapper.BusinessDeliveryDetailsLinkMapper;
|
||||||
import com.mhd.oms.domain.businessDocumentOrder.entity.BusinessDocumentOrder;
|
import com.mhd.oms.domain.businessDocumentOrder.entity.BusinessDocumentOrder;
|
||||||
import com.mhd.oms.domain.businessDocumentOrder.repository.mapper.BusinessDocumentOrderMapper;
|
import com.mhd.oms.domain.businessDocumentOrder.repository.mapper.BusinessDocumentOrderMapper;
|
||||||
|
import com.mhd.oms.domain.businessDocumentCargoMulti.entity.BusinessDocumentCargoMulti;
|
||||||
|
import com.mhd.oms.domain.businessDocumentCargoMulti.repository.mapper.BusinessDocumentCargoMultiMapper;
|
||||||
|
import com.mhd.oms.domain.businessDocumentAddressMulti.entity.BusinessDocumentAddressMulti;
|
||||||
|
import com.mhd.oms.domain.businessDocumentAddressMulti.repository.mapper.BusinessDocumentAddressMultiMapper;
|
||||||
import com.mhd.oms.domain.executeOrder.entity.ExecuteOrder;
|
import com.mhd.oms.domain.executeOrder.entity.ExecuteOrder;
|
||||||
import com.mhd.oms.domain.executeOrder.repository.mapper.ExecuteOrderMapper;
|
import com.mhd.oms.domain.executeOrder.repository.mapper.ExecuteOrderMapper;
|
||||||
import com.mhd.oms.domain.executionInMaterialDetail.entity.ExecutionInMaterialDetail;
|
import com.mhd.oms.domain.executionInMaterialDetail.entity.ExecutionInMaterialDetail;
|
||||||
@@ -48,13 +52,17 @@ import com.mhd.oms.domain.reservationStockInOrder.repository.mapper.ReservationS
|
|||||||
import com.mhd.oms.domain.reservationStockInOrder.repository.po.MaterialBaseInfoPO;
|
import com.mhd.oms.domain.reservationStockInOrder.repository.po.MaterialBaseInfoPO;
|
||||||
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDO;
|
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.entity.ReservationStockOutOrder;
|
import com.mhd.oms.domain.reservationStockOutOrder.entity.ReservationStockOutOrder;
|
||||||
|
import com.mhd.oms.domain.reservationInventoryAdjustmentRecord.entity.ReservationInventoryAdjustmentRecord;
|
||||||
|
import com.mhd.oms.domain.reservationInventoryAdjustmentRecord.repository.mapper.ReservationInventoryAdjustmentRecordMapper;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.facade.IReservationStockOutOrderService;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.facade.IReservationStockOutOrderService;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.mapper.ReservationStockOutOrderMapper;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.mapper.ReservationStockOutOrderMapper;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderPO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderPO;
|
||||||
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderTotalPO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDO;
|
||||||
import com.mhd.oms.domain.reserveStockOutOrder.repository.mapper.ReserveStockOutOrderMapper;
|
import com.mhd.oms.domain.reserveStockOutOrder.repository.mapper.ReserveStockOutOrderMapper;
|
||||||
import com.mhd.system.api.SystemServiceFeign;
|
import com.mhd.system.api.SystemServiceFeign;
|
||||||
import com.mhd.system.api.WmsServiceFeign;
|
import com.mhd.system.api.WmsServiceFeign;
|
||||||
|
import com.mhd.system.api.WlhyServiceFeign;
|
||||||
import com.mhd.system.api.domain.*;
|
import com.mhd.system.api.domain.*;
|
||||||
import com.mhd.system.api.model.LoginUser;
|
import com.mhd.system.api.model.LoginUser;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -118,6 +126,16 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
private ReserveStockOutOrderMapper reserveStockOutOrderMapper;
|
private ReserveStockOutOrderMapper reserveStockOutOrderMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private MaterialBaseInfoResolveService materialBaseInfoResolveService;
|
private MaterialBaseInfoResolveService materialBaseInfoResolveService;
|
||||||
|
@Resource
|
||||||
|
private ReservationInventoryAdjustmentRecordMapper inventoryAdjustmentRecordMapper;
|
||||||
|
@Resource
|
||||||
|
private WlhyServiceFeign wlhyServiceFeign;
|
||||||
|
@Autowired
|
||||||
|
private BusinessDocumentCargoMultiMapper businessDocumentCargoMultiMapper;
|
||||||
|
@Autowired
|
||||||
|
private BusinessDocumentAddressMultiMapper businessDocumentAddressMultiMapper;
|
||||||
|
@Autowired
|
||||||
|
private com.mhd.oms.application.service.WarehouseInfoApplicationService warehouseInfoApplicationService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询出库单列表
|
* 分页查询出库单列表
|
||||||
@@ -125,6 +143,13 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
public List<ReservationStockOutOrderPO> queryList(ReservationStockOutOrderDO stockOutOrderDO) {
|
public List<ReservationStockOutOrderPO> queryList(ReservationStockOutOrderDO stockOutOrderDO) {
|
||||||
return stockOutOrderService.queryList(stockOutOrderDO);
|
return stockOutOrderService.queryList(stockOutOrderDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表合计:按查询条件全量(不分页)汇总一级明细的计划数量与出库数量
|
||||||
|
*/
|
||||||
|
public ReservationStockOutOrderTotalPO queryListTotal(ReservationStockOutOrderDO stockOutOrderDO) {
|
||||||
|
return stockOutOrderService.queryListTotal(stockOutOrderDO);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 从单号列表中提取最大序号
|
* 从单号列表中提取最大序号
|
||||||
* 单号格式: prefix + 数字序号(如 RK260807001 中 prefix=RK260807, 序号=001)
|
* 单号格式: prefix + 数字序号(如 RK260807001 中 prefix=RK260807, 序号=001)
|
||||||
@@ -215,6 +240,14 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
if (stockOutOrderDO.getCheckTaskDistribution() == null) {
|
if (stockOutOrderDO.getCheckTaskDistribution() == null) {
|
||||||
stockOutOrderDO.setCheckTaskDistribution(2);
|
stockOutOrderDO.setCheckTaskDistribution(2);
|
||||||
}
|
}
|
||||||
|
// 订单来源为空时兜底为手动创建(导入/预约下发等路径已显式设置,不受影响),下发执行单时随业务单继承
|
||||||
|
if (stockOutOrderDO.getReservationOrderSource() == null || stockOutOrderDO.getReservationOrderSource().isEmpty()) {
|
||||||
|
stockOutOrderDO.setReservationOrderSource("手动创建");
|
||||||
|
}
|
||||||
|
// 下单冻结库存(防超卖):下单冻结模式仓库的明细先冻结WMS库存(可用-、冻结+,库存数量不变),
|
||||||
|
// 冻结失败阻断下单;本地落库失败补偿解冻(对齐执行并下发的补偿模式)
|
||||||
|
boolean frozen = tryFreezeOnCreate(stockOutOrderDO);
|
||||||
|
try {
|
||||||
stockOutOrderService.insert(stockOutOrderDO);
|
stockOutOrderService.insert(stockOutOrderDO);
|
||||||
List<ReservationDeliveryDetailsLink> reservationDeliveryDetailsLinkList = stockOutOrderDO.getReservationDeliveryDetailsLinkList();
|
List<ReservationDeliveryDetailsLink> reservationDeliveryDetailsLinkList = stockOutOrderDO.getReservationDeliveryDetailsLinkList();
|
||||||
if (reservationDeliveryDetailsLinkList != null && !reservationDeliveryDetailsLinkList.isEmpty()){
|
if (reservationDeliveryDetailsLinkList != null && !reservationDeliveryDetailsLinkList.isEmpty()){
|
||||||
@@ -231,6 +264,12 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
return outMaterialDetailService.batchInsert(stockOutOrderDO.getOutOrderNumber(), stockOutOrderDO.getMaterialDetailList());
|
return outMaterialDetailService.batchInsert(stockOutOrderDO.getOutOrderNumber(), stockOutOrderDO.getMaterialDetailList());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (frozen) {
|
||||||
|
compensateUnfreeze(stockOutOrderDO.getOutOrderNumber());
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -263,12 +302,186 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
if (stockOutOrderDO.getCheckTaskDistribution() == null) {
|
if (stockOutOrderDO.getCheckTaskDistribution() == null) {
|
||||||
stockOutOrderDO.setCheckTaskDistribution(2);
|
stockOutOrderDO.setCheckTaskDistribution(2);
|
||||||
}
|
}
|
||||||
|
// 下单冻结库存(防超卖):与 insert 同口径,冻结失败阻断、落库失败补偿解冻
|
||||||
|
boolean frozen = tryFreezeOnCreate(stockOutOrderDO);
|
||||||
|
try {
|
||||||
stockOutOrderService.insert(stockOutOrderDO);
|
stockOutOrderService.insert(stockOutOrderDO);
|
||||||
outMaterialDetailService.batchInsert(stockOutOrderDO.getOutOrderNumber(), stockOutOrderDO.getMaterialDetailList());
|
outMaterialDetailService.batchInsert(stockOutOrderDO.getOutOrderNumber(), stockOutOrderDO.getMaterialDetailList());
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (frozen) {
|
||||||
|
compensateUnfreeze(stockOutOrderDO.getOutOrderNumber());
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================================ 下单冻结库存(防超卖) ================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下单冻结模式仓库判断:stock_lock_mode=1-下单冻结(2-分配库存冻结为默认现有模式)。
|
||||||
|
* 复用仓库信息兜底组件(Feign优先+本地跨库兜底);兜底时 stockLockMode=null → 按模式2处理(保守降级不冻结)
|
||||||
|
*/
|
||||||
|
private boolean isOrderFreezeWarehouse(Long warehouseId) {
|
||||||
|
if (warehouseId == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
WarehouseFeignPO warehouseFeignPO = getWarehouseInfoWithFallback(warehouseId);
|
||||||
|
return warehouseFeignPO != null && warehouseFeignPO.getStockLockMode() != null && warehouseFeignPO.getStockLockMode() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出库单保存时下单冻结:筛选下单冻结模式仓库的明细,先冻结WMS库存再落库(失败抛异常阻断保存)。
|
||||||
|
* ★下单冻结模式仓库明细强制必选库存批次:未关联 materialInventoryId 无法确定冻结哪条库存行,直接阻断。
|
||||||
|
*
|
||||||
|
* @return true-已冻结(落库失败需补偿解冻);false-无冻结模式明细未冻结
|
||||||
|
*/
|
||||||
|
private boolean tryFreezeOnCreate(ReservationStockOutOrderDO stockOutOrderDO) {
|
||||||
|
List<ReservationOutMaterialDetailDO> details = stockOutOrderDO.getMaterialDetailList();
|
||||||
|
if (CollectionUtils.isEmpty(details)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 样品出库交接不扣减库存,同样不参与下单冻结
|
||||||
|
if ("yang_pin_chu_ku".equals(stockOutOrderDO.getBusinessType())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<OutMaterialDetail> freezeDetails = filterFreezeDetails(details, stockOutOrderDO.getWarehouseId(), stockOutOrderDO.getOutOrderNumber());
|
||||||
|
if (freezeDetails.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
OmsFreezeInventoryParam param = new OmsFreezeInventoryParam();
|
||||||
|
param.setOutOrderNumber(stockOutOrderDO.getOutOrderNumber());
|
||||||
|
param.setDetails(freezeDetails);
|
||||||
|
fillOperator(param);
|
||||||
|
AjaxResult result = wmsServiceFeign.omsFreezeInventory(param);
|
||||||
|
if (result == null || !"200".equals(String.valueOf(result.get("code")))) {
|
||||||
|
throw new ServiceException("下单冻结库存失败,无法保存出库单:"
|
||||||
|
+ (result == null ? "WMS服务不可用" : result.get("msg")));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出库单编辑-原子重锁:已下发的冻结单禁止编辑(判据=活动执行单存在,issueStatus 不可靠——下发不改该字段);
|
||||||
|
* 未下发则WMS同一事务内释放原冻结+按新明细重锁,成功后才改本地
|
||||||
|
*/
|
||||||
|
private void handleFreezeOnUpdate(ReservationStockOutOrderDO stockOutOrderDO, ReservationStockOutOrderPO existingPO) {
|
||||||
|
List<ReservationOutMaterialDetailDO> details = stockOutOrderDO.getMaterialDetailList();
|
||||||
|
if (CollectionUtils.isEmpty(details)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 样品出库交接不扣减库存,同样不参与下单冻结
|
||||||
|
if ("yang_pin_chu_ku".equals(stockOutOrderDO.getBusinessType())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<OutMaterialDetail> freezeDetails = filterFreezeDetails(details, stockOutOrderDO.getWarehouseId(), existingPO.getOutOrderNumber());
|
||||||
|
if (freezeDetails.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String orderNumber = existingPO.getOutOrderNumber();
|
||||||
|
// 已下发WMS(存在活动执行单)禁止编辑:执行单明细与冻结记录必须保持一致,须先取消下发
|
||||||
|
List<ExecutionStockOutOrder> activeExecutions = executionStockOutOrderService.list(new QueryWrapper<ExecutionStockOutOrder>().lambda()
|
||||||
|
.eq(ExecutionStockOutOrder::getBusinessOutOrderNumber, orderNumber)
|
||||||
|
.eq(ExecutionStockOutOrder::getDelFlag, 1));
|
||||||
|
if (!CollectionUtils.isEmpty(activeExecutions)) {
|
||||||
|
throw new ServiceException("出库单[" + orderNumber + "]已下发WMS,不可编辑,请先取消下发");
|
||||||
|
}
|
||||||
|
OmsFreezeInventoryParam param = new OmsFreezeInventoryParam();
|
||||||
|
param.setOutOrderNumber(orderNumber);
|
||||||
|
param.setDetails(freezeDetails);
|
||||||
|
fillOperator(param);
|
||||||
|
AjaxResult result = wmsServiceFeign.omsRelockInventory(param);
|
||||||
|
if (result == null || !"200".equals(String.valueOf(result.get("code")))) {
|
||||||
|
throw new ServiceException("编辑重锁库存失败,无法修改出库单:"
|
||||||
|
+ (result == null ? "WMS服务不可用" : result.get("msg")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除/关闭单据前释放冻结(WMS按业务单号幂等处理:无冻结直接成功;
|
||||||
|
* 冻结已被执行单承接时WMS拒绝并提示先取消下发;已交接时拒绝并提示走反审流程)
|
||||||
|
*/
|
||||||
|
private void unfreezeIfFrozen(ReservationStockOutOrderPO stockOutOrderPO) {
|
||||||
|
OmsFreezeInventoryParam param = new OmsFreezeInventoryParam();
|
||||||
|
param.setOutOrderNumber(stockOutOrderPO.getOutOrderNumber());
|
||||||
|
fillOperator(param);
|
||||||
|
AjaxResult result = wmsServiceFeign.omsUnfreezeInventory(param);
|
||||||
|
if (result == null || !"200".equals(String.valueOf(result.get("code")))) {
|
||||||
|
throw new ServiceException("释放冻结库存失败:"
|
||||||
|
+ (result == null ? "WMS服务不可用" : result.get("msg")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地落库失败的补偿解冻(对齐执行并下发的补偿模式):补偿失败仅告警,
|
||||||
|
* 凭业务单号在WMS冻结记录(out_order_freeze_record,relate_no 前缀=业务单号)中人工处理
|
||||||
|
*/
|
||||||
|
private void compensateUnfreeze(String outOrderNumber) {
|
||||||
|
try {
|
||||||
|
OmsFreezeInventoryParam param = new OmsFreezeInventoryParam();
|
||||||
|
param.setOutOrderNumber(outOrderNumber);
|
||||||
|
fillOperator(param);
|
||||||
|
AjaxResult result = wmsServiceFeign.omsUnfreezeInventory(param);
|
||||||
|
if (result == null || !"200".equals(String.valueOf(result.get("code")))) {
|
||||||
|
log.error("下单冻结补偿解冻失败,请人工核查WMS冻结记录:出库业务单[{}],返回:{}",
|
||||||
|
outOrderNumber, result == null ? "WMS服务不可用" : result.get("msg"));
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("下单冻结补偿解冻异常,请人工核查WMS冻结记录:出库业务单[{}]", outOrderNumber, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 筛选下单冻结模式仓库的明细并转换为冻结参数。
|
||||||
|
* ★下单冻结模式仓库明细强制必选库存批次(R2):materialInventoryId 为空直接阻断保存;
|
||||||
|
* 数量<=0的明细交由WMS侧兜底校验
|
||||||
|
*/
|
||||||
|
private List<OutMaterialDetail> filterFreezeDetails(List<ReservationOutMaterialDetailDO> details, Long defaultWarehouseId, String businessOrderNumber) {
|
||||||
|
List<OutMaterialDetail> freezeDetails = new ArrayList<>();
|
||||||
|
for (ReservationOutMaterialDetailDO detail : details) {
|
||||||
|
Long warehouseId = detail.getWarehouseId() != null ? detail.getWarehouseId() : defaultWarehouseId;
|
||||||
|
if (!isOrderFreezeWarehouse(warehouseId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 下单冻结必须知道冻结哪条库存行:模式1仓库明细未关联库存直接阻断
|
||||||
|
if (detail.getMaterialInventoryId() == null || detail.getMaterialInventoryId() <= 0) {
|
||||||
|
throw new ServiceException("出库单[" + (businessOrderNumber != null ? businessOrderNumber : "当前单据") + "]物料["
|
||||||
|
+ (detail.getMaterialCode() != null ? detail.getMaterialCode() : detail.getMaterialName())
|
||||||
|
+ "]未选择库存批次,下单冻结模式仓库必须先选择库存批次");
|
||||||
|
}
|
||||||
|
OutMaterialDetail out = new OutMaterialDetail();
|
||||||
|
out.setMaterialInventoryId(detail.getMaterialInventoryId());
|
||||||
|
out.setMaterialBaseInfoId(detail.getMaterialBaseInfoId());
|
||||||
|
out.setMaterialCode(detail.getMaterialCode());
|
||||||
|
out.setMaterialName(detail.getMaterialName());
|
||||||
|
out.setWarehouseId(warehouseId);
|
||||||
|
out.setQuantity(detail.getQuantity());
|
||||||
|
out.setBatchNumber(detail.getBatchNumber());
|
||||||
|
out.setBatchRefNo(detail.getBatchRefNo());
|
||||||
|
out.setSheetRefNo(detail.getSheetRefNo());
|
||||||
|
out.setBoxPalletNo(detail.getBoxPalletNo());
|
||||||
|
out.setUniqueId(detail.getUniqueId());
|
||||||
|
freezeDetails.add(out);
|
||||||
|
}
|
||||||
|
return freezeDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填充操作人(Feign调用WMS时兜底传递,WMS侧无登录上下文时使用)
|
||||||
|
*/
|
||||||
|
private void fillOperator(OmsFreezeInventoryParam param) {
|
||||||
|
try {
|
||||||
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
|
if (loginUser != null) {
|
||||||
|
param.setOperatorId(loginUser.getUserid());
|
||||||
|
param.setOperatorName(loginUser.getUsername());
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// 无登录上下文场景(Feign内部调用),操作人留空由WMS兜底
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改出库单
|
* 修改出库单
|
||||||
*/
|
*/
|
||||||
@@ -279,6 +492,9 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
}
|
}
|
||||||
//设置物料信息
|
//设置物料信息
|
||||||
setMaterialDetailInfo(stockOutOrderDO);
|
setMaterialDetailInfo(stockOutOrderDO);
|
||||||
|
// 下单冻结模式编辑重锁(原子):先WMS侧释放原冻结+按新明细重锁(同一事务),成功后才改本地;
|
||||||
|
// 已下发WMS的冻结单不可编辑,须先取消下发(判据=活动执行单,issueStatus 不可靠)
|
||||||
|
handleFreezeOnUpdate(stockOutOrderDO, stockInOrderPO);
|
||||||
//统计种类
|
//统计种类
|
||||||
stockOutOrderDO.setMaterialQuantity(stockOutOrderDO.getMaterialDetailList().size());
|
stockOutOrderDO.setMaterialQuantity(stockOutOrderDO.getMaterialDetailList().size());
|
||||||
//统计总数量
|
//统计总数量
|
||||||
@@ -358,6 +574,8 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
if (stockInOrderPO.getStatus() != 1){
|
if (stockInOrderPO.getStatus() != 1){
|
||||||
throw new ServiceException("只有已创建状态下能允许关闭出库单");
|
throw new ServiceException("只有已创建状态下能允许关闭出库单");
|
||||||
}
|
}
|
||||||
|
// 下单冻结模式:关闭单据不再出库,先释放冻结库存(幂等;冻结已被执行单承接时WMS会拒绝并提示先取消下发)
|
||||||
|
unfreezeIfFrozen(stockInOrderPO);
|
||||||
stockOutOrderDO.setStatus(11);
|
stockOutOrderDO.setStatus(11);
|
||||||
return stockOutOrderService.update(stockOutOrderDO);
|
return stockOutOrderService.update(stockOutOrderDO);
|
||||||
}
|
}
|
||||||
@@ -409,7 +627,8 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
String userRealName = loginUser.getUserPo() != null && loginUser.getUserPo().getUserName() != null
|
String userRealName = loginUser.getUserPo() != null && loginUser.getUserPo().getUserName() != null
|
||||||
? loginUser.getUserPo().getUserName()
|
? loginUser.getUserPo().getUserName()
|
||||||
: (loginUser.getRealname() != null ? loginUser.getRealname() : loginUser.getUsername());
|
: (loginUser.getRealname() != null ? loginUser.getRealname() : loginUser.getUsername());
|
||||||
boolean updateResult = stockOutOrderService.update(null, new UpdateWrapper<ReservationStockOutOrder>().lambda()
|
boolean updateResult;
|
||||||
|
LambdaUpdateWrapper<ReservationStockOutOrder> auditUpdateWrapper = new UpdateWrapper<ReservationStockOutOrder>().lambda()
|
||||||
.set(ReservationStockOutOrder::getReservationAuditId, loginUser.getUserid())
|
.set(ReservationStockOutOrder::getReservationAuditId, loginUser.getUserid())
|
||||||
.set(ReservationStockOutOrder::getReservationAuditName, userRealName)
|
.set(ReservationStockOutOrder::getReservationAuditName, userRealName)
|
||||||
.set(ReservationStockOutOrder::getReservationAuditTime, new Date())
|
.set(ReservationStockOutOrder::getReservationAuditTime, new Date())
|
||||||
@@ -417,7 +636,12 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
.set(ReservationStockOutOrder::getUpdateByName, userRealName)
|
.set(ReservationStockOutOrder::getUpdateByName, userRealName)
|
||||||
.set(ReservationStockOutOrder::getUpdateTime, new Date())
|
.set(ReservationStockOutOrder::getUpdateTime, new Date())
|
||||||
.set(ReservationStockOutOrder::getIssueStatus, issueStatus)
|
.set(ReservationStockOutOrder::getIssueStatus, issueStatus)
|
||||||
.in(ReservationStockOutOrder::getOutOrderId, stockOutOrderDO.getOutOrderIds()));
|
.in(ReservationStockOutOrder::getOutOrderId, stockOutOrderDO.getOutOrderIds());
|
||||||
|
// 重新审核通过时复位反审标记: 单据离开【已反审】页签回到正常流转
|
||||||
|
if (issueStatus == 2) {
|
||||||
|
auditUpdateWrapper.set(ReservationStockOutOrder::getReverseStatus, 0);
|
||||||
|
}
|
||||||
|
updateResult = stockOutOrderService.update(null, auditUpdateWrapper);
|
||||||
|
|
||||||
//审核通过时同步线路/车次到关联的运输业务单
|
//审核通过时同步线路/车次到关联的运输业务单
|
||||||
if (issueStatus == 2) {
|
if (issueStatus == 2) {
|
||||||
@@ -481,6 +705,125 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新下发前自愈清理: 历史版本反审/取消下发软删的执行明细(del_flag=2)仍占用unique_id
|
||||||
|
* (EXECUTION_OUT_MATERIAL_DETAIL 唯一约束不含del_flag), 重新下发从预留明细复制同一unique_id会撞键;
|
||||||
|
* 归档行的unique_id无功能用途(仅活动明细展示反查批号用到), 统一置空释放键位, 幂等可重复执行
|
||||||
|
*/
|
||||||
|
private void releaseArchivedDetailUniqueId(String businessOutOrderNumber) {
|
||||||
|
List<String> executionOrderNumbers = executionStockOutOrderService.list(new QueryWrapper<ExecutionStockOutOrder>().lambda()
|
||||||
|
.eq(ExecutionStockOutOrder::getBusinessOutOrderNumber, businessOutOrderNumber))
|
||||||
|
.stream()
|
||||||
|
.map(ExecutionStockOutOrder::getOutOrderNumber)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (executionOrderNumbers.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
executionOutMaterialDetailService.update(null, new UpdateWrapper<ExecutionOutMaterialDetail>().lambda()
|
||||||
|
.set(ExecutionOutMaterialDetail::getUniqueId, null)
|
||||||
|
.eq(ExecutionOutMaterialDetail::getDelFlag, 2)
|
||||||
|
.in(ExecutionOutMaterialDetail::getOutOrderNumber, executionOrderNumbers));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反审联动软删运输链路(编辑"是否运输"=是 且审核通过后生成的链), 真实层级:
|
||||||
|
* 运输业务单-主单(splicingOrderType=4, business_document_order)
|
||||||
|
* -> 审核运输业务单按配送明细生成子单(splicingOrderType=3, mainOrderId=主单id, 每明细一单)
|
||||||
|
* -> 下发每个子单各生成一个运输执行单(execute_order.businessDocumentId=【子单id】, goodsNumber=子单货源号, 主单下无直接执行单)
|
||||||
|
* -> 每个执行单各推一单TMS(tms_order.execute_order_number=执行单货源号) -> TMS运单/运输作业单(wlhy外部服务)。
|
||||||
|
* 定位运输业务单: ①出库单.businessNumber(创建运输业务单时回填的货源单号) ②inOrderNumber=出库单号兜底
|
||||||
|
* (businessNumber缺失/已清空场景); 均不滤del_flag——此前被旧版反审漏删或手工删过的主单,
|
||||||
|
* 其子单/执行单/TMS链可能仍存活, 同样要清理(幂等)。配送明细链的invoiceNumber存的是送货单号而非出库单号, 不能作为定位条件。
|
||||||
|
* 执行单逐个调 TMS deleteChainByExecuteOrderNumber 软删TMS侧整链(未下发过则TMS无数据, 幂等空操作), 失败抛异常整批回滚;
|
||||||
|
* 本地软删执行单/主单及子单及其货品/地址明细/配送明细链; 无关联运输单时直接跳过。
|
||||||
|
*/
|
||||||
|
private void reverseTransportChain(ReservationStockOutOrder byId) {
|
||||||
|
String outOrderNumber = byId.getOutOrderNumber();
|
||||||
|
Set<Long> transportOrderIds = new HashSet<>();
|
||||||
|
if (StringUtils.isNotBlank(byId.getBusinessNumber())) {
|
||||||
|
businessDocumentOrderMapper.selectList(new LambdaQueryWrapper<BusinessDocumentOrder>()
|
||||||
|
.eq(BusinessDocumentOrder::getGoodsNumber, byId.getBusinessNumber()))
|
||||||
|
.forEach(order -> transportOrderIds.add(order.getId()));
|
||||||
|
}
|
||||||
|
businessDocumentOrderMapper.selectList(new LambdaQueryWrapper<BusinessDocumentOrder>()
|
||||||
|
.eq(BusinessDocumentOrder::getInOrderNumber, outOrderNumber))
|
||||||
|
.forEach(order -> transportOrderIds.add(order.getId()));
|
||||||
|
if (transportOrderIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Long transportOrderId : transportOrderIds) {
|
||||||
|
BusinessDocumentOrder transportOrder = businessDocumentOrderMapper.selectById(transportOrderId);
|
||||||
|
if (transportOrder == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 0. 拆单主单自身无执行单: 收集主单+全部子单id, 执行单/货品/地址明细按全量id清理
|
||||||
|
List<BusinessDocumentOrder> subOrders = businessDocumentOrderMapper.selectList(new LambdaQueryWrapper<BusinessDocumentOrder>()
|
||||||
|
.eq(BusinessDocumentOrder::getMainOrderId, String.valueOf(transportOrderId))
|
||||||
|
.eq(BusinessDocumentOrder::getDelFlag, 1));
|
||||||
|
Set<Long> chainDocIds = new HashSet<>();
|
||||||
|
chainDocIds.add(transportOrderId);
|
||||||
|
subOrders.forEach(subOrder -> chainDocIds.add(subOrder.getId()));
|
||||||
|
// 1. 执行单: 不滤del_flag——历史周期已归档执行单的TMS链也幂等补清(防"执行单已删TMS残留"),
|
||||||
|
// 先逐个软删TMS侧整链(货源单->运单->作业单), 再软删本地执行单(已归档的跳过更新)
|
||||||
|
List<ExecuteOrder> executeOrders = executeOrderMapper.selectList(new LambdaQueryWrapper<ExecuteOrder>()
|
||||||
|
.in(ExecuteOrder::getBusinessDocumentId, chainDocIds));
|
||||||
|
for (ExecuteOrder executeOrder : executeOrders) {
|
||||||
|
if (StringUtils.isNotBlank(executeOrder.getGoodsNumber())) {
|
||||||
|
AjaxResult tmsResult = wlhyServiceFeign.deleteChainByExecuteOrderNumber(executeOrder.getGoodsNumber());
|
||||||
|
if (tmsResult == null || !"200".equals(String.valueOf(tmsResult.get("code")))) {
|
||||||
|
throw new ServiceException("出库单[" + outOrderNumber + "]反审失败: 运输执行单货源单号["
|
||||||
|
+ executeOrder.getGoodsNumber() + "]TMS链路软删失败: "
|
||||||
|
+ (tmsResult == null ? "TMS服务不可用" : tmsResult.get("msg")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Integer.valueOf(2).equals(executeOrder.getDelFlag())) {
|
||||||
|
executeOrder.setDelFlag(2);
|
||||||
|
executeOrderMapper.updateById(executeOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2. 软删运输业务单(主单+子单)及其货品/地址明细(主单已软删的只补清理子单/明细, 不重复更新主单)
|
||||||
|
if (!Integer.valueOf(2).equals(transportOrder.getDelFlag())) {
|
||||||
|
transportOrder.setDelFlag(2);
|
||||||
|
businessDocumentOrderMapper.updateById(transportOrder);
|
||||||
|
}
|
||||||
|
for (BusinessDocumentOrder subOrder : subOrders) {
|
||||||
|
subOrder.setDelFlag(2);
|
||||||
|
businessDocumentOrderMapper.updateById(subOrder);
|
||||||
|
}
|
||||||
|
List<String> chainDocIdStrings = chainDocIds.stream().map(String::valueOf).collect(Collectors.toList());
|
||||||
|
businessDocumentCargoMultiMapper.update(null, new LambdaUpdateWrapper<BusinessDocumentCargoMulti>()
|
||||||
|
.set(BusinessDocumentCargoMulti::getDelFlag, 2)
|
||||||
|
.in(BusinessDocumentCargoMulti::getOrderId, chainDocIdStrings)
|
||||||
|
.eq(BusinessDocumentCargoMulti::getDelFlag, 1));
|
||||||
|
businessDocumentAddressMultiMapper.update(null, new LambdaUpdateWrapper<BusinessDocumentAddressMulti>()
|
||||||
|
.set(BusinessDocumentAddressMulti::getDelFlag, 2)
|
||||||
|
.in(BusinessDocumentAddressMulti::getOrderId, chainDocIdStrings)
|
||||||
|
.eq(BusinessDocumentAddressMulti::getDelFlag, 1));
|
||||||
|
}
|
||||||
|
// 3. 配送明细链软删: 链的orderId固定指向运输业务单-主单, 按本次命中的主单id清理(含历史周期残留链)
|
||||||
|
businessDeliveryDetailsLinkService.update(null, new LambdaUpdateWrapper<BusinessDeliveryDetailsLink>()
|
||||||
|
.set(BusinessDeliveryDetailsLink::getDelFlag, 2)
|
||||||
|
.in(BusinessDeliveryDetailsLink::getOrderId, transportOrderIds)
|
||||||
|
.eq(BusinessDeliveryDetailsLink::getDelFlag, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取仓库信息(Feign优先, system服务失败/降级时本地跨库兜底), 统一委托共享组件
|
||||||
|
*/
|
||||||
|
private WarehouseFeignPO getWarehouseInfoWithFallback(Long warehouseId) {
|
||||||
|
return warehouseInfoApplicationService.getWarehouseInfo(warehouseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取仓库单号生成规则(下发生成执行单号用): 优先 Feign 调 system 服务;
|
||||||
|
* Feign 异常/降级/返回非200时回退本地跨库直查 NGWL_TEST_SYSTEM.WAREHOUSE(同库同表, 与 getWarehouseCode 等查询同数据源),
|
||||||
|
* 避免 system 服务抖动阻断下发; 两侧都取不到才报错并带出 Feign 真实返回原因。
|
||||||
|
*/
|
||||||
|
private WarehouseFeignPO getWarehouseOrderNoRule(Long warehouseId) {
|
||||||
|
return getWarehouseInfoWithFallback(warehouseId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 审核出库单
|
* @description 审核出库单
|
||||||
* @author ZhouGY
|
* @author ZhouGY
|
||||||
@@ -521,8 +864,14 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
if (activeExecutionCount > 0) {
|
if (activeExecutionCount > 0) {
|
||||||
throw new ServiceException("出库单[" + inOrderNumber + "]已下发,请勿重复下发;如需重新下发请先取消下发");
|
throw new ServiceException("出库单[" + inOrderNumber + "]已下发,请勿重复下发;如需重新下发请先取消下发");
|
||||||
}
|
}
|
||||||
|
// 自愈: 历史版本反审/取消下发的归档明细可能仍占用unique_id, 先置空释放键位再重新下发
|
||||||
|
releaseArchivedDetailUniqueId(inOrderNumber);
|
||||||
List<String> executionInOrderNumbers = new ArrayList<>();
|
List<String> executionInOrderNumbers = new ArrayList<>();
|
||||||
List<ReservationOutMaterialDetail> materialDetailList = outMaterialDetailService.list(new QueryWrapper<ReservationOutMaterialDetail>().lambda().eq(ReservationOutMaterialDetail::getOutOrderNumber, inOrderNumber));
|
// 下发明细必须过滤软删行(del_flag=1):编辑对账软删的旧行仍留表内,
|
||||||
|
// 不滤会参与按仓库分组/数量汇总,多生成执行单且数量叠加(入库侧同查询已过滤)
|
||||||
|
List<ReservationOutMaterialDetail> materialDetailList = outMaterialDetailService.list(new QueryWrapper<ReservationOutMaterialDetail>().lambda()
|
||||||
|
.eq(ReservationOutMaterialDetail::getOutOrderNumber, inOrderNumber)
|
||||||
|
.eq(ReservationOutMaterialDetail::getDelFlag, 1));
|
||||||
|
|
||||||
// 按照 warehouseId 分组
|
// 按照 warehouseId 分组
|
||||||
Map<Long, List<ReservationOutMaterialDetail>> materialDetailGroupByWarehouse = materialDetailList.stream()
|
Map<Long, List<ReservationOutMaterialDetail>> materialDetailGroupByWarehouse = materialDetailList.stream()
|
||||||
@@ -540,7 +889,6 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
reservationStockInOrder.setOrderTypeCode("pu_tong_chu_ku");
|
reservationStockInOrder.setOrderTypeCode("pu_tong_chu_ku");
|
||||||
reservationStockInOrder.setOrderTypeName("普通出库");
|
reservationStockInOrder.setOrderTypeName("普通出库");
|
||||||
reservationStockInOrder.setBusinessType("pu_tong_chu_ku");
|
reservationStockInOrder.setBusinessType("pu_tong_chu_ku");
|
||||||
reservationStockInOrder.setReservationOrderSource("客户预约");
|
|
||||||
reservationStockInOrder.setWarehouseId(warehouseId);
|
reservationStockInOrder.setWarehouseId(warehouseId);
|
||||||
reservationStockInOrder.setWarehouseCode(warehouseCode);
|
reservationStockInOrder.setWarehouseCode(warehouseCode);
|
||||||
reservationStockInOrder.setWarehouseName(warehouseName);
|
reservationStockInOrder.setWarehouseName(warehouseName);
|
||||||
@@ -556,14 +904,8 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
reservationStockInOrder.setBusinessOutOrderNumber(inOrderNumber);
|
reservationStockInOrder.setBusinessOutOrderNumber(inOrderNumber);
|
||||||
// reservationStockInOrder.setOutOrderNumber(generateExecutionOrderNumber(warehouseCode));
|
// reservationStockInOrder.setOutOrderNumber(generateExecutionOrderNumber(warehouseCode));
|
||||||
|
|
||||||
//设置单号
|
//设置单号(仓库单号规则: Feign优先, system服务失败/降级时本地跨库兜底)
|
||||||
Long warehouseId1 = reservationStockInOrder.getWarehouseId();
|
WarehouseFeignPO warehouseFeignPO = getWarehouseOrderNoRule(reservationStockInOrder.getWarehouseId());
|
||||||
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(warehouseId1);
|
|
||||||
if (!"200".equals(String.valueOf(ajaxResult.get("code")))) {
|
|
||||||
throw new ServiceException("获取仓库信息失败");
|
|
||||||
}
|
|
||||||
//仓库信息
|
|
||||||
WarehouseFeignPO warehouseFeignPO = com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson2.JSONObject.toJSONString(ajaxResult.get("data")), WarehouseFeignPO.class);
|
|
||||||
String orderNoGenerateRule = warehouseFeignPO.getOrderNoGenerateRule();
|
String orderNoGenerateRule = warehouseFeignPO.getOrderNoGenerateRule();
|
||||||
String prefix = warehouseFeignPO.getOutOrderNoPrefix();
|
String prefix = warehouseFeignPO.getOutOrderNoPrefix();
|
||||||
if ("yang_pin_chu_ku".equals(stockOutOrderDO.getBusinessType())) {
|
if ("yang_pin_chu_ku".equals(stockOutOrderDO.getBusinessType())) {
|
||||||
@@ -644,6 +986,18 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
List<ExecutionStockOutOrder> list = executionStockOutOrderService.list(new QueryWrapper<ExecutionStockOutOrder>().lambda()
|
List<ExecutionStockOutOrder> list = executionStockOutOrderService.list(new QueryWrapper<ExecutionStockOutOrder>().lambda()
|
||||||
.eq(ExecutionStockOutOrder::getBusinessOutOrderNumber, inOrderNumber)
|
.eq(ExecutionStockOutOrder::getBusinessOutOrderNumber, inOrderNumber)
|
||||||
.eq(ExecutionStockOutOrder::getDelFlag, 1));
|
.eq(ExecutionStockOutOrder::getDelFlag, 1));
|
||||||
|
// 取消下发必须同步WMS(修复存量不同步问题:此前只软删本地执行单,WMS残留单据与分配占用):
|
||||||
|
// WMS侧还原库存/承接冻结并软删单据;下单冻结模式下仅解除承接(冻结归还业务单),
|
||||||
|
// 分配库存冻结模式下还原分配占用;失败抛异常整体回滚本地软删(WMS清理幂等可重试)
|
||||||
|
for (ExecutionStockOutOrder executionStockOutOrder : list) {
|
||||||
|
StockOutOrder cancelParam = new StockOutOrder();
|
||||||
|
cancelParam.setOutOrderNumber(executionStockOutOrder.getOutOrderNumber());
|
||||||
|
AjaxResult cancelResult = wmsServiceFeign.omsCancelExecutionOrder(cancelParam);
|
||||||
|
if (cancelResult == null || !"200".equals(String.valueOf(cancelResult.get("code")))) {
|
||||||
|
throw new ServiceException("取消下发失败:WMS执行单[" + executionStockOutOrder.getOutOrderNumber()
|
||||||
|
+ "]清理失败:" + (cancelResult == null ? "WMS服务不可用" : cancelResult.get("msg")));
|
||||||
|
}
|
||||||
|
}
|
||||||
list.forEach(executionStockOutOrder -> {
|
list.forEach(executionStockOutOrder -> {
|
||||||
String outOrderNumber = executionStockOutOrder.getOutOrderNumber();
|
String outOrderNumber = executionStockOutOrder.getOutOrderNumber();
|
||||||
List<ExecutionOutMaterialDetail> materialDetails = executionOutMaterialDetailService.list(new QueryWrapper<ExecutionOutMaterialDetail>().lambda()
|
List<ExecutionOutMaterialDetail> materialDetails = executionOutMaterialDetailService.list(new QueryWrapper<ExecutionOutMaterialDetail>().lambda()
|
||||||
@@ -652,6 +1006,8 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
materialDetails.forEach(executionOutMaterialDetail -> {
|
materialDetails.forEach(executionOutMaterialDetail -> {
|
||||||
executionOutMaterialDetailService.update(null, new UpdateWrapper<ExecutionOutMaterialDetail>().lambda()
|
executionOutMaterialDetailService.update(null, new UpdateWrapper<ExecutionOutMaterialDetail>().lambda()
|
||||||
.set(ExecutionOutMaterialDetail::getDelFlag, 2)
|
.set(ExecutionOutMaterialDetail::getDelFlag, 2)
|
||||||
|
// 归档行置空uniqueId: 明细表唯一约束不含del_flag, 保留原值会与重新下发时从预留明细复制的同一uniqueId撞键
|
||||||
|
.set(ExecutionOutMaterialDetail::getUniqueId, null)
|
||||||
.eq(ExecutionOutMaterialDetail::getMaterialDetailId, executionOutMaterialDetail.getMaterialDetailId()));
|
.eq(ExecutionOutMaterialDetail::getMaterialDetailId, executionOutMaterialDetail.getMaterialDetailId()));
|
||||||
});
|
});
|
||||||
executionStockOutOrderService.update(null, new UpdateWrapper<ExecutionStockOutOrder>().lambda()
|
executionStockOutOrderService.update(null, new UpdateWrapper<ExecutionStockOutOrder>().lambda()
|
||||||
@@ -662,6 +1018,129 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
return update;
|
return update;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 反审出库业务单(按前端展示字段 issueStatus 判断可反审范围: 2-已审核(未出库) / 4-已出库 均可反审;
|
||||||
|
* 0-已创建无需反审、3-已驳回可直接编辑; status 字段在出库完成后不一定同步为9, 故不作为判断依据,
|
||||||
|
* 仅兜底排除 10-已取消/11-已关闭)。
|
||||||
|
* 反审将单据回退到已创建(status=1)且审核状态置为已驳回(issueStatus=3, 与入库侧反审同口径, 可编辑→重新审核→重新下发)
|
||||||
|
* 并打上反审标记:
|
||||||
|
* WMS 侧按出库单实际状态分支还原库存(已出库: 实物库存还原+推OMS; 未出库: 仅还原分配/冻结占用)、
|
||||||
|
* 清理执行单据(出库单/明细/拣货/复核/交接/配送, 软删+reverseFlag=1 划归【已反审】tab)、
|
||||||
|
* 每个执行出库单生成1条出库异常记录【反审记录】(out_order_abnormal, abnormalType=3);
|
||||||
|
* OMS 侧软删执行出库单及明细、软删预约库存调整记录。
|
||||||
|
* 反审后单据进入【已反审】页签(status=1 且 reverse_status=1), 可继续编辑/审核/撤销;
|
||||||
|
* 重新审核通过时 reverse_status 自动复位 0(见 auditState)。
|
||||||
|
* @date 2026/9/8
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Boolean reverseAudit(ReservationStockOutOrderDO stockOutOrderDO) {
|
||||||
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
|
// 获取用户姓名(优先从UserPo获取,如果为空则使用realname,最后使用username作为兜底)
|
||||||
|
String userRealName = loginUser.getUserPo() != null && loginUser.getUserPo().getUserName() != null
|
||||||
|
? loginUser.getUserPo().getUserName()
|
||||||
|
: (loginUser.getRealname() != null ? loginUser.getRealname() : loginUser.getUsername());
|
||||||
|
List<Long> outOrderIds = stockOutOrderDO.getOutOrderIds();
|
||||||
|
if (outOrderIds == null || outOrderIds.isEmpty()) {
|
||||||
|
throw new ServiceException("请选择需要反审的出库单");
|
||||||
|
}
|
||||||
|
String reverseRemark = stockOutOrderDO.getReverseRemark();
|
||||||
|
|
||||||
|
for (Long outOrderId : outOrderIds) {
|
||||||
|
ReservationStockOutOrder byId = stockOutOrderService.getById(outOrderId);
|
||||||
|
if (byId == null) {
|
||||||
|
throw new ServiceException("出库单不存在, outOrderId=" + outOrderId);
|
||||||
|
}
|
||||||
|
Integer status = byId.getStatus();
|
||||||
|
if (status != null && (status == 10 || status == 11)) {
|
||||||
|
throw new ServiceException("出库单[" + byId.getOutOrderNumber() + "]已取消/已关闭, 不能反审");
|
||||||
|
}
|
||||||
|
// 反审条件按前端展示字段 issueStatus 判断: 2-已审核(未出库) / 4-已出库 均可反审
|
||||||
|
Integer issueStatus = byId.getIssueStatus();
|
||||||
|
if (!Integer.valueOf(2).equals(issueStatus) && !Integer.valueOf(4).equals(issueStatus)) {
|
||||||
|
throw new ServiceException("出库单[" + byId.getOutOrderNumber() + "]当前状态(issueStatus=" + issueStatus
|
||||||
|
+ ")不支持反审, 仅已审核/已出库状态可反审");
|
||||||
|
}
|
||||||
|
// 逐个执行出库单调 WMS 反审: 还原实物库存+推送OMS库存、清理WMS单据、生成反审异常记录;
|
||||||
|
// 任一失败抛异常整体回滚, 避免单边反审
|
||||||
|
List<ExecutionStockOutOrder> executionOrders = executionStockOutOrderService.list(new QueryWrapper<ExecutionStockOutOrder>().lambda()
|
||||||
|
.eq(ExecutionStockOutOrder::getBusinessOutOrderNumber, byId.getOutOrderNumber())
|
||||||
|
.eq(ExecutionStockOutOrder::getDelFlag, 1));
|
||||||
|
for (ExecutionStockOutOrder executionStockOutOrder : executionOrders) {
|
||||||
|
String executionOutOrderNumber = executionStockOutOrder.getOutOrderNumber();
|
||||||
|
StockOutOrder wmsOutOrder = new StockOutOrder();
|
||||||
|
wmsOutOrder.setOutOrderNumber(executionOutOrderNumber);
|
||||||
|
// 反审原因经 remark 透传 WMS, 记入反审异常记录备注
|
||||||
|
wmsOutOrder.setRemark("业务单号:" + byId.getOutOrderNumber()
|
||||||
|
+ (reverseRemark != null && !reverseRemark.isEmpty() ? ",反审原因:" + reverseRemark : ""));
|
||||||
|
AjaxResult wmsResult = wmsServiceFeign.omsReverseExecutionOrder(wmsOutOrder);
|
||||||
|
if (wmsResult == null || !"200".equals(String.valueOf(wmsResult.get("code")))) {
|
||||||
|
throw new ServiceException("执行出库单[" + executionOutOrderNumber + "]WMS反审失败:"
|
||||||
|
+ (wmsResult == null ? "WMS服务不可用" : wmsResult.get("msg")));
|
||||||
|
}
|
||||||
|
// 软删执行出库单明细 + 执行出库单(与取消下发 cancelIssueOrder 一致)
|
||||||
|
List<ExecutionOutMaterialDetail> materialDetails = executionOutMaterialDetailService.list(new QueryWrapper<ExecutionOutMaterialDetail>().lambda()
|
||||||
|
.eq(ExecutionOutMaterialDetail::getOutOrderNumber, executionOutOrderNumber)
|
||||||
|
.eq(ExecutionOutMaterialDetail::getDelFlag, 1));
|
||||||
|
materialDetails.forEach(executionOutMaterialDetail -> {
|
||||||
|
executionOutMaterialDetailService.update(null, new UpdateWrapper<ExecutionOutMaterialDetail>().lambda()
|
||||||
|
.set(ExecutionOutMaterialDetail::getDelFlag, 2)
|
||||||
|
.set(ExecutionOutMaterialDetail::getReverseFlag, 1)
|
||||||
|
// 归档行置空uniqueId: 明细表唯一约束不含del_flag, 保留原值会与重新下发时从预留明细复制的同一uniqueId撞键
|
||||||
|
.set(ExecutionOutMaterialDetail::getUniqueId, null)
|
||||||
|
.eq(ExecutionOutMaterialDetail::getMaterialDetailId, executionOutMaterialDetail.getMaterialDetailId()));
|
||||||
|
});
|
||||||
|
executionStockOutOrderService.update(null, new UpdateWrapper<ExecutionStockOutOrder>().lambda()
|
||||||
|
.set(ExecutionStockOutOrder::getDelFlag, 2)
|
||||||
|
.set(ExecutionStockOutOrder::getReverseFlag, 1)
|
||||||
|
.eq(ExecutionStockOutOrder::getOutOrderNumber, executionOutOrderNumber));
|
||||||
|
// 软删预约库存调整记录(下发分配时生成): 仅软删留痕, 库存数量由 WMS omsEdit 推送还原
|
||||||
|
inventoryAdjustmentRecordMapper.update(null, new UpdateWrapper<ReservationInventoryAdjustmentRecord>().lambda()
|
||||||
|
.set(ReservationInventoryAdjustmentRecord::getDelFlag, 2)
|
||||||
|
.eq(ReservationInventoryAdjustmentRecord::getExecutionOrderNumber, executionOutOrderNumber)
|
||||||
|
.eq(ReservationInventoryAdjustmentRecord::getDelFlag, 1));
|
||||||
|
}
|
||||||
|
// 反审联动: 软删关联运输链路(运输业务单/运输执行单/TMS货源单-运单-作业单), 无关联运输单时跳过
|
||||||
|
reverseTransportChain(byId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主单回退到已创建并打反审标记; 更新条件带 issueStatus in (2,4) + 排除已取消/已关闭 兜底防并发重复反审
|
||||||
|
// issueStatus 回退到 3-已驳回(前端展示映射: 0-已创建 2-已审核 3-已驳回 4-已出库; 与入库侧反审同口径,
|
||||||
|
// 已驳回态可编辑→重新审核→重新下发, 重新审核通过时 reverse_status 自动复位 0 离开已反审tab);
|
||||||
|
// 清空执行出库单号——执行单已随反审软删, 前端"是否执行"列由该字段有无值推导, 不清空会仍显示已执行
|
||||||
|
return stockOutOrderService.update(null, new UpdateWrapper<ReservationStockOutOrder>().lambda()
|
||||||
|
.set(ReservationStockOutOrder::getStatus, 1)
|
||||||
|
.set(ReservationStockOutOrder::getIssueStatus, 3)
|
||||||
|
.set(ReservationStockOutOrder::getExecutionOutOrderNumber, null)
|
||||||
|
// 运输业务单已随反审软删, 货源单号一并清空, 重新审核后重建运输链路时自动回填
|
||||||
|
.set(ReservationStockOutOrder::getBusinessNumber, null)
|
||||||
|
.set(ReservationStockOutOrder::getIssueId, null)
|
||||||
|
.set(ReservationStockOutOrder::getIssueName, null)
|
||||||
|
.set(ReservationStockOutOrder::getIssueTime, null)
|
||||||
|
.set(ReservationStockOutOrder::getReservationAuditId, null)
|
||||||
|
.set(ReservationStockOutOrder::getReservationAuditName, null)
|
||||||
|
.set(ReservationStockOutOrder::getReservationAuditTime, null)
|
||||||
|
.set(ReservationStockOutOrder::getAllocationQuantity, BigDecimal.ZERO)
|
||||||
|
.set(ReservationStockOutOrder::getAlreadyAllocationQuantity, BigDecimal.ZERO)
|
||||||
|
.set(ReservationStockOutOrder::getPickingQuantity, BigDecimal.ZERO)
|
||||||
|
.set(ReservationStockOutOrder::getCheckQuantity, BigDecimal.ZERO)
|
||||||
|
.set(ReservationStockOutOrder::getCheckStatus, 1)
|
||||||
|
.set(ReservationStockOutOrder::getCheckTaskDistribution, 2)
|
||||||
|
.set(ReservationStockOutOrder::getCheckTaskDistributionTime, null)
|
||||||
|
.set(ReservationStockOutOrder::getCompleteTime, null)
|
||||||
|
.set(ReservationStockOutOrder::getOutboundTime, null)
|
||||||
|
.set(ReservationStockOutOrder::getReverseStatus, 1)
|
||||||
|
.set(ReservationStockOutOrder::getReverseBy, loginUser.getUserid())
|
||||||
|
.set(ReservationStockOutOrder::getReverseByName, userRealName)
|
||||||
|
.set(ReservationStockOutOrder::getReverseTime, new Date())
|
||||||
|
.set(ReservationStockOutOrder::getReverseRemark, reverseRemark)
|
||||||
|
.set(ReservationStockOutOrder::getUpdateBy, loginUser.getUserid())
|
||||||
|
.set(ReservationStockOutOrder::getUpdateByName, userRealName)
|
||||||
|
.set(ReservationStockOutOrder::getUpdateTime, new Date())
|
||||||
|
.in(ReservationStockOutOrder::getOutOrderId, outOrderIds)
|
||||||
|
.in(ReservationStockOutOrder::getIssueStatus, 2, 4)
|
||||||
|
.notIn(ReservationStockOutOrder::getStatus, 10, 11));
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Boolean executeAndIssue(ReservationStockOutOrderDO stockOutOrderDO) {
|
public Boolean executeAndIssue(ReservationStockOutOrderDO stockOutOrderDO) {
|
||||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
@@ -691,7 +1170,13 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
String warehouseCode1 = byId.getWarehouseCode();
|
String warehouseCode1 = byId.getWarehouseCode();
|
||||||
String warehouseName1 = byId.getWarehouseName();
|
String warehouseName1 = byId.getWarehouseName();
|
||||||
String inOrderNumber = byId.getOutOrderNumber();
|
String inOrderNumber = byId.getOutOrderNumber();
|
||||||
List<ReservationOutMaterialDetail> materialDetailList = outMaterialDetailService.list(new QueryWrapper<ReservationOutMaterialDetail>().lambda().eq(ReservationOutMaterialDetail::getOutOrderNumber, inOrderNumber));
|
// 自愈: 历史版本反审/取消下发的归档明细可能仍占用unique_id, 先置空释放键位再重新下发
|
||||||
|
releaseArchivedDetailUniqueId(inOrderNumber);
|
||||||
|
// 下发明细必须过滤软删行(del_flag=1):编辑对账软删的旧行仍留表内,
|
||||||
|
// 不滤会参与按仓库分组/数量汇总,多生成执行单且数量叠加(入库侧同查询已过滤)
|
||||||
|
List<ReservationOutMaterialDetail> materialDetailList = outMaterialDetailService.list(new QueryWrapper<ReservationOutMaterialDetail>().lambda()
|
||||||
|
.eq(ReservationOutMaterialDetail::getOutOrderNumber, inOrderNumber)
|
||||||
|
.eq(ReservationOutMaterialDetail::getDelFlag, 1));
|
||||||
|
|
||||||
// 按照 warehouseId 分组
|
// 按照 warehouseId 分组
|
||||||
Map<Long, List<ReservationOutMaterialDetail>> materialDetailGroupByWarehouse = materialDetailList.stream()
|
Map<Long, List<ReservationOutMaterialDetail>> materialDetailGroupByWarehouse = materialDetailList.stream()
|
||||||
@@ -716,7 +1201,8 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
if (!groupBaseValid) {
|
if (!groupBaseValid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//WMS库存校验(接口内部会聚合该仓全部明细的需求量后比对可用库存)
|
//WMS库存校验(接口内部会聚合该仓全部明细的需求量后比对可用库存;
|
||||||
|
// 传业务单号:下单冻结模式下行级可用量=库存可用+本单冻结记录量)
|
||||||
List<OutMaterialDetail> checkList = new ArrayList<>();
|
List<OutMaterialDetail> checkList = new ArrayList<>();
|
||||||
for (ReservationOutMaterialDetail detail : details) {
|
for (ReservationOutMaterialDetail detail : details) {
|
||||||
OutMaterialDetail checkDetail = new OutMaterialDetail();
|
OutMaterialDetail checkDetail = new OutMaterialDetail();
|
||||||
@@ -726,7 +1212,7 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
checkDetail.setMaterialName(detail.getMaterialName());
|
checkDetail.setMaterialName(detail.getMaterialName());
|
||||||
checkList.add(checkDetail);
|
checkList.add(checkDetail);
|
||||||
}
|
}
|
||||||
AjaxResult checkResult = wmsServiceFeign.checkOmsAssignInventory(checkList);
|
AjaxResult checkResult = wmsServiceFeign.checkOmsAssignInventory(checkList, inOrderNumber);
|
||||||
if (checkResult == null || !"200".equals(String.valueOf(checkResult.get("code")))) {
|
if (checkResult == null || !"200".equals(String.valueOf(checkResult.get("code")))) {
|
||||||
String groupWarehouseName = reservationStockInOrderMapper.getWarehouseName(warehouseId);
|
String groupWarehouseName = reservationStockInOrderMapper.getWarehouseName(warehouseId);
|
||||||
issueValidateErrors.add("仓库[" + (groupWarehouseName != null ? groupWarehouseName : warehouseId) + "]库存校验失败:"
|
issueValidateErrors.add("仓库[" + (groupWarehouseName != null ? groupWarehouseName : warehouseId) + "]库存校验失败:"
|
||||||
@@ -755,7 +1241,6 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
reservationStockInOrder.setOrderTypeCode("pu_tong_chu_ku");
|
reservationStockInOrder.setOrderTypeCode("pu_tong_chu_ku");
|
||||||
reservationStockInOrder.setOrderTypeName("普通出库");
|
reservationStockInOrder.setOrderTypeName("普通出库");
|
||||||
reservationStockInOrder.setBusinessType("pu_tong_chu_ku");
|
reservationStockInOrder.setBusinessType("pu_tong_chu_ku");
|
||||||
reservationStockInOrder.setReservationOrderSource("客户预约");
|
|
||||||
reservationStockInOrder.setWarehouseId(warehouseId);
|
reservationStockInOrder.setWarehouseId(warehouseId);
|
||||||
reservationStockInOrder.setOrganizationId(orgId);
|
reservationStockInOrder.setOrganizationId(orgId);
|
||||||
reservationStockInOrder.setOrganizationName(orgName);
|
reservationStockInOrder.setOrganizationName(orgName);
|
||||||
@@ -765,14 +1250,8 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
reservationStockInOrder.setBusinessOutOrderNumber(inOrderNumber);
|
reservationStockInOrder.setBusinessOutOrderNumber(inOrderNumber);
|
||||||
|
|
||||||
|
|
||||||
//设置单号
|
//设置单号(仓库单号规则: Feign优先, system服务失败/降级时本地跨库兜底)
|
||||||
// Long warehouseId1 = reservationStockInOrder.getWarehouseId();
|
WarehouseFeignPO warehouseFeignPO = getWarehouseOrderNoRule(warehouseId);
|
||||||
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(warehouseId);
|
|
||||||
if (!"200".equals(String.valueOf(ajaxResult.get("code")))) {
|
|
||||||
throw new ServiceException("获取仓库信息失败");
|
|
||||||
}
|
|
||||||
//仓库信息
|
|
||||||
WarehouseFeignPO warehouseFeignPO = com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson2.JSONObject.toJSONString(ajaxResult.get("data")), WarehouseFeignPO.class);
|
|
||||||
String orderNoGenerateRule = warehouseFeignPO.getOrderNoGenerateRule();
|
String orderNoGenerateRule = warehouseFeignPO.getOrderNoGenerateRule();
|
||||||
String prefix = warehouseFeignPO.getOutOrderNoPrefix();
|
String prefix = warehouseFeignPO.getOutOrderNoPrefix();
|
||||||
if ("yang_pin_chu_ku".equals(stockOutOrderDO.getBusinessType())) {
|
if ("yang_pin_chu_ku".equals(stockOutOrderDO.getBusinessType())) {
|
||||||
@@ -905,6 +1384,13 @@ public class ReservationStockOutOrderDomainService {
|
|||||||
* 批量删除出库单
|
* 批量删除出库单
|
||||||
*/
|
*/
|
||||||
public Boolean delete(Long[] outOrderIds) {
|
public Boolean delete(Long[] outOrderIds) {
|
||||||
|
// 下单冻结模式:删除单据前先释放冻结库存(幂等;冻结已被执行单承接时WMS会拒绝并提示先取消下发)
|
||||||
|
for (Long outOrderId : outOrderIds) {
|
||||||
|
ReservationStockOutOrderPO stockOutOrderPO = stockOutOrderService.getInfo(outOrderId);
|
||||||
|
if (stockOutOrderPO != null) {
|
||||||
|
unfreezeIfFrozen(stockOutOrderPO);
|
||||||
|
}
|
||||||
|
}
|
||||||
return stockOutOrderService.delete(outOrderIds);
|
return stockOutOrderService.delete(outOrderIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-5
@@ -68,6 +68,8 @@ import java.util.concurrent.ThreadLocalRandom;
|
|||||||
public class ReserveStockOutOrderApplicationService {
|
public class ReserveStockOutOrderApplicationService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ReserveStockOutOrderDomainService stockOutOrderDomainService;
|
private ReserveStockOutOrderDomainService stockOutOrderDomainService;
|
||||||
|
@Autowired
|
||||||
|
private com.mhd.oms.application.service.WarehouseInfoApplicationService warehouseInfoApplicationService;
|
||||||
// @Autowired
|
// @Autowired
|
||||||
// private ReserveOutMaterialDetailDomainService outMaterialDetailDomainService;
|
// private ReserveOutMaterialDetailDomainService outMaterialDetailDomainService;
|
||||||
// @Autowired
|
// @Autowired
|
||||||
@@ -895,11 +897,8 @@ public class ReserveStockOutOrderApplicationService {
|
|||||||
if (stockOutOrderDO.getWarehouseId() == null) {
|
if (stockOutOrderDO.getWarehouseId() == null) {
|
||||||
throw new ServiceException("仓库ID不能为空");
|
throw new ServiceException("仓库ID不能为空");
|
||||||
}
|
}
|
||||||
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(stockOutOrderDO.getWarehouseId());
|
// Feign失败走本地跨库兜底, 避免保存被"获取仓库信息失败"阻断
|
||||||
if(!"200".equals(String.valueOf(ajaxResult.get("code")))){
|
WarehouseFeignPO warehouseFeignPO = warehouseInfoApplicationService.getWarehouseInfo(stockOutOrderDO.getWarehouseId());
|
||||||
throw new ServiceException("获取仓库信息失败");
|
|
||||||
}
|
|
||||||
WarehouseFeignPO warehouseFeignPO = JSON.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), WarehouseFeignPO.class);
|
|
||||||
stockOutOrderDO.setWarehouseCode(warehouseFeignPO.getWarehouseCode());
|
stockOutOrderDO.setWarehouseCode(warehouseFeignPO.getWarehouseCode());
|
||||||
stockOutOrderDO.setWarehouseName(warehouseFeignPO.getWarehouseName());
|
stockOutOrderDO.setWarehouseName(warehouseFeignPO.getWarehouseName());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import java.util.Map;
|
|||||||
* 三方服务 Feign
|
* 三方服务 Feign
|
||||||
*/
|
*/
|
||||||
// 目标地址由 nacos 配置项 feign.url.mhd-third-party-service 指定,未配置时按服务名走服务发现
|
// 目标地址由 nacos 配置项 feign.url.mhd-third-party-service 指定,未配置时按服务名走服务发现
|
||||||
@FeignClient(contextId = "thirdPartyServiceFeign",url = "http://10.102.192.31:8012", value = ServiceNameConstants.THIRDPARTY_CENTER, configuration = FeignAutoConfiguration.class)
|
@FeignClient(contextId = "thirdPartyServiceFeign",url = "http://10.33.0.129:8012", value = ServiceNameConstants.THIRDPARTY_CENTER, configuration = FeignAutoConfiguration.class)
|
||||||
public interface ThirdPartyServiceFeign {
|
public interface ThirdPartyServiceFeign {
|
||||||
|
|
||||||
//查询当前组织所有三方接口信息
|
//查询当前组织所有三方接口信息
|
||||||
|
|||||||
+39
-6
@@ -1,15 +1,17 @@
|
|||||||
package com.mhd.oms.interfaces.facade.ReservationStockOutOrder;
|
package com.mhd.oms.interfaces.facade.ReservationStockOutOrder;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.github.pagehelper.PageInfo;
|
||||||
|
import com.mhd.common.core.constant.HttpStatus;
|
||||||
import com.mhd.common.core.web.controller.BaseController;
|
import com.mhd.common.core.web.controller.BaseController;
|
||||||
import com.mhd.common.core.web.domain.AjaxResult;
|
import com.mhd.common.core.web.domain.AjaxResult;
|
||||||
import com.mhd.common.core.web.page.TableDataInfo;
|
|
||||||
import com.mhd.oms.domain.reservationOutMaterialDetail.repository.po.ReservationOutMaterialDetailPO;
|
import com.mhd.oms.domain.reservationOutMaterialDetail.repository.po.ReservationOutMaterialDetailPO;
|
||||||
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDO;
|
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDO;
|
||||||
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDTO;
|
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDTO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.entity.ReservationStockOutOrder;
|
import com.mhd.oms.domain.reservationStockOutOrder.entity.ReservationStockOutOrder;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.facade.IReservationStockOutOrderService;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.facade.IReservationStockOutOrderService;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderPO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderPO;
|
||||||
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderTotalPO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDTO;
|
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDTO;
|
||||||
import com.mhd.oms.domain.reservationStockOutOrder.service.ReservationStockOutOrderApplicationService;
|
import com.mhd.oms.domain.reservationStockOutOrder.service.ReservationStockOutOrderApplicationService;
|
||||||
@@ -46,17 +48,19 @@ public class ReservationStockOutOrderApi extends BaseController {
|
|||||||
private IReservationStockOutOrderService stockOutOrderService;
|
private IReservationStockOutOrderService stockOutOrderService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询出库单列表
|
* 分页查询出库单列表(附:当前查询条件下全量不分页的计划数量总数/出库数量总数)
|
||||||
*/
|
*/
|
||||||
@ApiOperation("查询出库单列表")
|
@ApiOperation("查询出库单列表")
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
public TableDataInfo list(ReservationStockOutOrderDTO stockOutOrderDTO)
|
public HashMap<String, Object> list(ReservationStockOutOrderDTO stockOutOrderDTO)
|
||||||
{
|
{
|
||||||
//转换实体
|
//转换实体
|
||||||
ReservationStockOutOrderDO stockOutOrderDO = stockOutOrderAssembler.toDO(stockOutOrderDTO);
|
ReservationStockOutOrderDO stockOutOrderDO = stockOutOrderAssembler.toDO(stockOutOrderDTO);
|
||||||
startPage();
|
startPage();
|
||||||
List<ReservationStockOutOrderPO> list = stockOutOrderApplicationService.queryList(stockOutOrderDO);
|
List<ReservationStockOutOrderPO> list = stockOutOrderApplicationService.queryList(stockOutOrderDO);
|
||||||
return getDataTable(list);
|
// 合计须在列表查询后用同一个DO查询:数据权限条件由列表查询原地写入DO,且不再被分页拦截
|
||||||
|
ReservationStockOutOrderTotalPO total = stockOutOrderApplicationService.queryListTotal(stockOutOrderDO);
|
||||||
|
return getDataTableWithTotal(list, total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -159,6 +163,17 @@ public class ReservationStockOutOrderApi extends BaseController {
|
|||||||
return AjaxResult.success(stockOutOrderApplicationService.cancelIssueOrder(stockOutOrderDO));
|
return AjaxResult.success(stockOutOrderApplicationService.cancelIssueOrder(stockOutOrderDO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反审出库单: 已出库/已复核(issueStatus=2已审核)的单据回退到已创建并打反审标记,
|
||||||
|
* WMS还原实物库存并生成出库异常记录【反审记录】, 单据划归【已反审】页签
|
||||||
|
*/
|
||||||
|
@ApiOperation("反审出库单")
|
||||||
|
@PostMapping(value = "/reverseAudit")
|
||||||
|
public AjaxResult reverseAudit(@RequestBody ReservationStockOutOrderDTO stockOutOrderDTO) {
|
||||||
|
ReservationStockOutOrderDO stockOutOrderDO = stockOutOrderAssembler.toDO(stockOutOrderDTO);
|
||||||
|
return AjaxResult.success(stockOutOrderApplicationService.reverseAudit(stockOutOrderDO));
|
||||||
|
}
|
||||||
|
|
||||||
@ApiOperation("下发出库单")
|
@ApiOperation("下发出库单")
|
||||||
@PostMapping(value = "/executeAndIssue")
|
@PostMapping(value = "/executeAndIssue")
|
||||||
public AjaxResult executeAndIssue(@RequestBody ReservationStockOutOrderDTO stockOutOrderDTO) {
|
public AjaxResult executeAndIssue(@RequestBody ReservationStockOutOrderDTO stockOutOrderDTO) {
|
||||||
@@ -239,13 +254,31 @@ public class ReservationStockOutOrderApi extends BaseController {
|
|||||||
|
|
||||||
@ApiOperation("查询出库单列表")
|
@ApiOperation("查询出库单列表")
|
||||||
@GetMapping("/list1")
|
@GetMapping("/list1")
|
||||||
public TableDataInfo list1(ReservationStockOutOrderDTO stockOutOrderDTO)
|
public HashMap<String, Object> list1(ReservationStockOutOrderDTO stockOutOrderDTO)
|
||||||
{
|
{
|
||||||
//转换实体
|
//转换实体
|
||||||
ReservationStockOutOrderDO stockOutOrderDO = stockOutOrderAssembler.toDO(stockOutOrderDTO);
|
ReservationStockOutOrderDO stockOutOrderDO = stockOutOrderAssembler.toDO(stockOutOrderDTO);
|
||||||
startPage();
|
startPage();
|
||||||
List<ReservationStockOutOrderPO> list = stockOutOrderApplicationService.queryList1(stockOutOrderDO);
|
List<ReservationStockOutOrderPO> list = stockOutOrderApplicationService.queryList1(stockOutOrderDO);
|
||||||
return getDataTable(list);
|
// 合计须在列表查询后用同一个DO查询:数据权限条件由列表查询原地写入DO,且不再被分页拦截
|
||||||
|
ReservationStockOutOrderTotalPO total = stockOutOrderApplicationService.queryListTotal(stockOutOrderDO);
|
||||||
|
return getDataTableWithTotal(list, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页结果 + 合计:data/total/code/msg 之外追加计划数量总数/出库数量总数(key与TableDataInfo序列化结果一致,仅多两个字段)
|
||||||
|
*/
|
||||||
|
private HashMap<String, Object> getDataTableWithTotal(List<?> list, ReservationStockOutOrderTotalPO total) {
|
||||||
|
HashMap<String, Object> result = new HashMap<>();
|
||||||
|
result.put("data", list);
|
||||||
|
result.put("total", new PageInfo<>(list).getTotal());
|
||||||
|
result.put("code", HttpStatus.SUCCESS);
|
||||||
|
result.put("msg", "查询成功");
|
||||||
|
if (total != null) {
|
||||||
|
result.put("totalPlanQuantity", total.getTotalPlanQuantity());
|
||||||
|
result.put("totalOutboundQuantity", total.getTotalOutboundQuantity());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation("生成出库预约单号")
|
@ApiOperation("生成出库预约单号")
|
||||||
|
|||||||
+23
@@ -3,6 +3,7 @@ package com.mhd.oms.interfaces.facade.executionInMaterialDetail;
|
|||||||
import com.mhd.common.core.web.controller.BaseController;
|
import com.mhd.common.core.web.controller.BaseController;
|
||||||
import com.mhd.common.core.web.domain.AjaxResult;
|
import com.mhd.common.core.web.domain.AjaxResult;
|
||||||
import com.mhd.common.core.web.page.TableDataInfo;
|
import com.mhd.common.core.web.page.TableDataInfo;
|
||||||
|
import com.mhd.common.core.utils.poi.ExcelUtil;
|
||||||
import com.mhd.oms.domain.executionInMaterialDetail.repository.po.ExecutionInMaterialDetailPO;
|
import com.mhd.oms.domain.executionInMaterialDetail.repository.po.ExecutionInMaterialDetailPO;
|
||||||
import com.mhd.oms.domain.executionInMaterialDetail.repository.po.ExecutionInMaterialDetailWithOrderPO;
|
import com.mhd.oms.domain.executionInMaterialDetail.repository.po.ExecutionInMaterialDetailWithOrderPO;
|
||||||
import com.mhd.oms.domain.executionInMaterialDetail.repository.todo.ExecutionInMaterialDetailDO;
|
import com.mhd.oms.domain.executionInMaterialDetail.repository.todo.ExecutionInMaterialDetailDO;
|
||||||
@@ -16,6 +17,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.net.URLEncoder;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,4 +99,24 @@ public class ExecutionInMaterialDetailApi extends BaseController {
|
|||||||
return getDataTable(list);
|
return getDataTable(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出入库明细查询Excel(关联入库执行单,全量不分页;
|
||||||
|
* SQL固定只统计已入库(5)单据,可按单据类型(如退货入库tui_huo_ru_ku)与实际入仓日期区间过滤)
|
||||||
|
*/
|
||||||
|
@ApiOperation("导出入库明细查询Excel")
|
||||||
|
@GetMapping("/exportWithOrder")
|
||||||
|
public void exportWithOrder(HttpServletResponse response, ExecutionInMaterialDetailQueryDTO queryDTO) throws Exception
|
||||||
|
{
|
||||||
|
//转换实体(不分页,导出全量)
|
||||||
|
QueryOrderOverStockDO queryDO = inMaterialDetailAssembler.toQueryDO(queryDTO);
|
||||||
|
List<ExecutionInMaterialDetailWithOrderPO> list = inMaterialDetailApplicationService.exportInOrderDetailList(queryDO);
|
||||||
|
|
||||||
|
String fileName = URLEncoder.encode("入库明细查询", "UTF-8").replaceAll("\\+", "%20");
|
||||||
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||||
|
response.setCharacterEncoding("utf-8");
|
||||||
|
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
|
||||||
|
ExcelUtil<ExecutionInMaterialDetailWithOrderPO> util = new ExcelUtil<>(ExecutionInMaterialDetailWithOrderPO.class);
|
||||||
|
util.exportExcel(response, list, "入库明细查询");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+22
@@ -2,6 +2,7 @@ package com.mhd.oms.interfaces.facade.executionOutMaterialDetail;
|
|||||||
|
|
||||||
import com.mhd.common.core.web.controller.BaseController;
|
import com.mhd.common.core.web.controller.BaseController;
|
||||||
import com.mhd.common.core.web.page.TableDataInfo;
|
import com.mhd.common.core.web.page.TableDataInfo;
|
||||||
|
import com.mhd.common.core.utils.poi.ExcelUtil;
|
||||||
import com.mhd.oms.domain.executionOutMaterialDetail.repository.po.ExecutionOutMaterialDetailWithOrderPO;
|
import com.mhd.oms.domain.executionOutMaterialDetail.repository.po.ExecutionOutMaterialDetailWithOrderPO;
|
||||||
import com.mhd.oms.domain.executionOutMaterialDetail.repository.todo.ExecutionOutMaterialDetailQueryDTO;
|
import com.mhd.oms.domain.executionOutMaterialDetail.repository.todo.ExecutionOutMaterialDetailQueryDTO;
|
||||||
import com.mhd.oms.domain.executionOutMaterialDetail.service.ExecutionOutMaterialDetailApplicationService;
|
import com.mhd.oms.domain.executionOutMaterialDetail.service.ExecutionOutMaterialDetailApplicationService;
|
||||||
@@ -14,6 +15,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.net.URLEncoder;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,4 +44,23 @@ public class ExecutionOutMaterialDetailApi extends BaseController {
|
|||||||
List<ExecutionOutMaterialDetailWithOrderPO> list = outMaterialDetailApplicationService.queryOutOrderDetailList(queryDO);
|
List<ExecutionOutMaterialDetailWithOrderPO> list = outMaterialDetailApplicationService.queryOutOrderDetailList(queryDO);
|
||||||
return getDataTable(list);
|
return getDataTable(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出出库明细查询Excel(关联出库执行单,全量不分页;
|
||||||
|
* SQL固定只统计已出库(9/12/13)单据,可按单据类型与实际出仓日期区间过滤)
|
||||||
|
*/
|
||||||
|
@ApiOperation("导出出库明细查询Excel")
|
||||||
|
@GetMapping("/exportWithOrder")
|
||||||
|
public void exportWithOrder(HttpServletResponse response, ExecutionOutMaterialDetailQueryDTO queryDTO) throws Exception {
|
||||||
|
//转换实体(不分页,导出全量)
|
||||||
|
QueryOrderOverStockDO queryDO = outMaterialDetailAssembler.toQueryDO(queryDTO);
|
||||||
|
List<ExecutionOutMaterialDetailWithOrderPO> list = outMaterialDetailApplicationService.queryOutOrderDetailList(queryDO);
|
||||||
|
|
||||||
|
String fileName = URLEncoder.encode("出库明细查询", "UTF-8").replaceAll("\\+", "%20");
|
||||||
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||||
|
response.setCharacterEncoding("utf-8");
|
||||||
|
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
|
||||||
|
ExcelUtil<ExecutionOutMaterialDetailWithOrderPO> util = new ExcelUtil<>(ExecutionOutMaterialDetailWithOrderPO.class);
|
||||||
|
util.exportExcel(response, list, "出库明细查询");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
@@ -93,6 +93,16 @@ public class ExecutionStockInOrderApi extends BaseController {
|
|||||||
return AjaxResult.success(stockInOrderApplicationService.getInfo(inOrderId));
|
return AjaxResult.success(stockInOrderApplicationService.getInfo(inOrderId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询入库执行单【已反审】tab 归档详情(已软删+已反审标记的数据,含软删明细)
|
||||||
|
*/
|
||||||
|
@ApiOperation("查询入库执行单已反审归档详情")
|
||||||
|
@GetMapping(value = "/getReverseAuditInfo/{inOrderId}")
|
||||||
|
public AjaxResult getReverseAuditInfo(@PathVariable("inOrderId") Long inOrderId)
|
||||||
|
{
|
||||||
|
return AjaxResult.success(stockInOrderApplicationService.getReverseAuditInfo(inOrderId));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导入入库单
|
* 导入入库单
|
||||||
*/
|
*/
|
||||||
|
|||||||
+28
-6
@@ -1,13 +1,15 @@
|
|||||||
package com.mhd.oms.interfaces.facade.executionStockOutOrder;
|
package com.mhd.oms.interfaces.facade.executionStockOutOrder;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.github.pagehelper.PageInfo;
|
||||||
|
import com.mhd.common.core.constant.HttpStatus;
|
||||||
import com.mhd.common.core.web.controller.BaseController;
|
import com.mhd.common.core.web.controller.BaseController;
|
||||||
import com.mhd.common.core.web.domain.AjaxResult;
|
import com.mhd.common.core.web.domain.AjaxResult;
|
||||||
import com.mhd.common.core.web.page.TableDataInfo;
|
|
||||||
import com.mhd.oms.domain.executionOutMaterialDetail.repository.po.ExecutionOutMaterialDetailPO;
|
import com.mhd.oms.domain.executionOutMaterialDetail.repository.po.ExecutionOutMaterialDetailPO;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.entity.ExecutionStockOutOrder;
|
import com.mhd.oms.domain.executionStockOutOrder.entity.ExecutionStockOutOrder;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.facade.IExecutionStockOutOrderService;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.facade.IExecutionStockOutOrderService;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderPO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderPO;
|
||||||
|
import com.mhd.oms.domain.executionStockOutOrder.repository.po.ExecutionStockOutOrderTotalPO;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDO;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDTO;
|
import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockOutOrderDTO;
|
||||||
import com.mhd.oms.domain.executionStockOutOrder.service.ExecutionStockOutOrderApplicationService;
|
import com.mhd.oms.domain.executionStockOutOrder.service.ExecutionStockOutOrderApplicationService;
|
||||||
@@ -45,17 +47,19 @@ public class ExecutionStockOutOrderApi extends BaseController {
|
|||||||
private IExecutionStockOutOrderService stockOutOrderService;
|
private IExecutionStockOutOrderService stockOutOrderService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询出库单列表
|
* 分页查询出库单列表(附:当前查询条件下全量不分页的计划数量总数/出库数量总数)
|
||||||
*/
|
*/
|
||||||
@ApiOperation("查询出库单列表")
|
@ApiOperation("查询出库单列表")
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
public TableDataInfo list(ExecutionStockOutOrderDTO stockOutOrderDTO)
|
public HashMap<String, Object> list(ExecutionStockOutOrderDTO stockOutOrderDTO)
|
||||||
{
|
{
|
||||||
//转换实体
|
//转换实体
|
||||||
ExecutionStockOutOrderDO stockOutOrderDO = stockOutOrderAssembler.toDO(stockOutOrderDTO);
|
ExecutionStockOutOrderDO stockOutOrderDO = stockOutOrderAssembler.toDO(stockOutOrderDTO);
|
||||||
startPage();
|
startPage();
|
||||||
List<ExecutionStockOutOrderPO> list = stockOutOrderApplicationService.queryList(stockOutOrderDO);
|
List<ExecutionStockOutOrderPO> list = stockOutOrderApplicationService.queryList(stockOutOrderDO);
|
||||||
return getDataTable(list);
|
// 合计须在列表查询后用同一个DO查询:数据权限条件由列表查询原地写入DO,且不再被分页拦截
|
||||||
|
ExecutionStockOutOrderTotalPO total = stockOutOrderApplicationService.queryListTotal(stockOutOrderDO);
|
||||||
|
return getDataTableWithTotal(list, total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -234,13 +238,31 @@ public class ExecutionStockOutOrderApi extends BaseController {
|
|||||||
|
|
||||||
@ApiOperation("查询出库单列表")
|
@ApiOperation("查询出库单列表")
|
||||||
@GetMapping("/list1")
|
@GetMapping("/list1")
|
||||||
public TableDataInfo list1(ExecutionStockOutOrderDTO stockOutOrderDTO)
|
public HashMap<String, Object> list1(ExecutionStockOutOrderDTO stockOutOrderDTO)
|
||||||
{
|
{
|
||||||
//转换实体
|
//转换实体
|
||||||
ExecutionStockOutOrderDO stockOutOrderDO = stockOutOrderAssembler.toDO(stockOutOrderDTO);
|
ExecutionStockOutOrderDO stockOutOrderDO = stockOutOrderAssembler.toDO(stockOutOrderDTO);
|
||||||
startPage();
|
startPage();
|
||||||
List<ExecutionStockOutOrderPO> list = stockOutOrderApplicationService.queryList1(stockOutOrderDO);
|
List<ExecutionStockOutOrderPO> list = stockOutOrderApplicationService.queryList1(stockOutOrderDO);
|
||||||
return getDataTable(list);
|
// 合计须在列表查询后用同一个DO查询:数据权限条件由列表查询原地写入DO,且不再被分页拦截
|
||||||
|
ExecutionStockOutOrderTotalPO total = stockOutOrderApplicationService.queryListTotal(stockOutOrderDO);
|
||||||
|
return getDataTableWithTotal(list, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页结果 + 合计:data/total/code/msg 之外追加计划数量总数/出库数量总数(key与TableDataInfo序列化结果一致,仅多两个字段)
|
||||||
|
*/
|
||||||
|
private HashMap<String, Object> getDataTableWithTotal(List<?> list, ExecutionStockOutOrderTotalPO total) {
|
||||||
|
HashMap<String, Object> result = new HashMap<>();
|
||||||
|
result.put("data", list);
|
||||||
|
result.put("total", new PageInfo<>(list).getTotal());
|
||||||
|
result.put("code", HttpStatus.SUCCESS);
|
||||||
|
result.put("msg", "查询成功");
|
||||||
|
if (total != null) {
|
||||||
|
result.put("totalPlanQuantity", total.getTotalPlanQuantity());
|
||||||
|
result.put("totalOutboundQuantity", total.getTotalOutboundQuantity());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+38
@@ -12,8 +12,10 @@ import io.swagger.annotations.ApiOperation;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -80,6 +82,42 @@ public class OutEntrustOrderApi extends BaseController {
|
|||||||
return AjaxResult.success(outEntrustOrderApplicationService.getInfo(id));
|
return AjaxResult.success(outEntrustOrderApplicationService.getInfo(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传并解析出仓委托单Excel(模板与WMS出库管理导入一致:前11行表头+第12行起明细)
|
||||||
|
* 解析校验通过后落库为出仓委托单(生成CKWT委托单号,状态待审核/未下发)
|
||||||
|
*/
|
||||||
|
@ApiOperation("导入出仓委托单")
|
||||||
|
@PostMapping("/import")
|
||||||
|
public AjaxResult importOutEntrustOrder(
|
||||||
|
@RequestParam("file") MultipartFile file,
|
||||||
|
@RequestParam("warehouseId") Long warehouseId,
|
||||||
|
@RequestParam("warehouseCode") String warehouseCode,
|
||||||
|
@RequestParam("warehouseName") String warehouseName) {
|
||||||
|
|
||||||
|
// 验证文件
|
||||||
|
if (file.isEmpty()) {
|
||||||
|
return error("文件为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证文件类型
|
||||||
|
String fileName = file.getOriginalFilename();
|
||||||
|
if (fileName == null || !fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")) {
|
||||||
|
return error("文件类型错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 解析Excel并返回结果
|
||||||
|
StringBuilder result = outEntrustOrderApplicationService.importOutEntrustOrder(file, warehouseId, warehouseCode, warehouseName);
|
||||||
|
if (result.length() > 0) {
|
||||||
|
return AjaxResult.error(result.toString());
|
||||||
|
} else {
|
||||||
|
return AjaxResult.success(result);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
return error("解析失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量审核:待审核/已驳回 → 已审核(通过)或已驳回
|
* 批量审核:待审核/已驳回 → 已审核(通过)或已驳回
|
||||||
* 请求体:{"idList":[1,2],"orderStatus":3,"auditRemark":"备注"},orderStatus:2-审核通过 3-驳回(auditRemark选填)
|
* 请求体:{"idList":[1,2],"orderStatus":3,"auditRemark":"备注"},orderStatus:2-审核通过 3-驳回(auditRemark选填)
|
||||||
|
|||||||
+34
-11
@@ -99,18 +99,28 @@ public class ReservationStockInOrderApi extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导入入库单
|
* 导入入仓委托单(模板与WMS入库管理导入一致:第1-12行表头,第13行列名,第14行起明细)
|
||||||
|
* 按【委托编号NO】分组,同一委托编号的多条明细归为一张入库单
|
||||||
*/
|
*/
|
||||||
// @ApiOperation("导入入库单")
|
@ApiOperation("导入入仓委托单")
|
||||||
// @PostMapping(value = "/importData")
|
@PostMapping(value = "/importData")
|
||||||
// public AjaxResult importData(
|
public AjaxResult importData(
|
||||||
// @RequestParam("file") MultipartFile file,
|
@RequestParam("file") MultipartFile file,
|
||||||
// @RequestParam("warehouseId") Long warehouseId,
|
@RequestParam("warehouseId") Long warehouseId,
|
||||||
// @RequestParam("warehouseCode") String warehouseCode,
|
@RequestParam("warehouseCode") String warehouseCode,
|
||||||
// @RequestParam("warehouseName") String warehouseName)
|
@RequestParam("warehouseName") String warehouseName)
|
||||||
// {
|
{
|
||||||
// return AjaxResult.success(stockInOrderApplicationService.importData(file, warehouseId, warehouseCode, warehouseName));
|
// 验证文件
|
||||||
// }
|
if (file.isEmpty()) {
|
||||||
|
return error("文件为空");
|
||||||
|
}
|
||||||
|
// 验证文件类型
|
||||||
|
String fileName = file.getOriginalFilename();
|
||||||
|
if (fileName == null || !fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")) {
|
||||||
|
return error("文件类型错误");
|
||||||
|
}
|
||||||
|
return AjaxResult.success(stockInOrderApplicationService.importData(file, warehouseId, warehouseCode, warehouseName));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 审核入库单
|
* 审核入库单
|
||||||
@@ -122,6 +132,19 @@ public class ReservationStockInOrderApi extends BaseController {
|
|||||||
return AjaxResult.success(stockInOrderApplicationService.auditState(stockInOrderDO));
|
return AjaxResult.success(stockInOrderApplicationService.auditState(stockInOrderDO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反审入库业务单:已入库/已出库的单子客户通知需要回退的场景,单子回退到最原始状态——
|
||||||
|
* 业务单状态改为"已驳回"(issueStatus=3,与审核不通过同语义,可编辑→重新审核→重新下发)
|
||||||
|
* 并打已反审标记落入【已反审】tab;入库执行单+明细软删并打已反审标记;
|
||||||
|
* W入库单有出库记录时WMS侧拦截不允许反审;已推送关务(pushStatus=3)直接拦截
|
||||||
|
*/
|
||||||
|
@ApiOperation("反审入库业务单")
|
||||||
|
@PostMapping(value = "/reverseAudit")
|
||||||
|
public AjaxResult reverseAudit(@RequestBody ReservationStockInOrderDTO stockInOrderDTO) {
|
||||||
|
ReservationStockInOrderDO stockInOrderDO = stockInOrderAssembler.toDO(stockInOrderDTO);
|
||||||
|
return AjaxResult.success(stockInOrderApplicationService.reverseAudit(stockInOrderDO));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下发入库单
|
* 下发入库单
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -18,15 +18,15 @@ spring:
|
|||||||
discovery:
|
discovery:
|
||||||
# server-addr: 127.0.0.1:8848
|
# server-addr: 127.0.0.1:8848
|
||||||
# 线上测试环境配置 容器名+端口号
|
# 线上测试环境配置 容器名+端口号
|
||||||
# server-addr: 10.33.0.129:6010
|
server-addr: 10.33.0.129:6010
|
||||||
server-addr: 10.102.192.30:6848
|
# server-addr: 10.102.192.31:6848
|
||||||
username: nacos
|
username: nacos
|
||||||
password: manhuoda@2023
|
password: manhuoda@2023
|
||||||
config:
|
config:
|
||||||
# server-addr: 127.0.0.1:8848
|
# server-addr: 127.0.0.1:8848
|
||||||
# 线上测试环境配置 容器名+端口号
|
# 线上测试环境配置 容器名+端口号
|
||||||
# server-addr: 10.33.0.129:6010
|
server-addr: 10.33.0.129:6010
|
||||||
server-addr: 10.102.192.30:6848
|
# server-addr: 10.102.192.31:6848
|
||||||
username: nacos
|
username: nacos
|
||||||
password: manhuoda@2023
|
password: manhuoda@2023
|
||||||
#线上正式环境
|
#线上正式环境
|
||||||
|
|||||||
+2
-2
@@ -1278,8 +1278,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
select PUSH_VERIFY from NGWL_TEST_PRODUCT.SYS_ORGANIZATION where ORGANIZATION_ID = #{organizationId}
|
select PUSH_VERIFY from NGWL_TEST_PRODUCT.SYS_ORGANIZATION where ORGANIZATION_ID = #{organizationId}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="countByGoodsNumberPrefix" resultType="java.lang.Integer">
|
<select id="selectGoodsNumbersByPrefix" resultType="java.lang.String">
|
||||||
SELECT COUNT(1)
|
SELECT goods_number
|
||||||
FROM business_document_order
|
FROM business_document_order
|
||||||
WHERE goods_number LIKE concat(#{prefix}, '%')
|
WHERE goods_number LIKE concat(#{prefix}, '%')
|
||||||
AND LENGTH(goods_number) <= 12
|
AND LENGTH(goods_number) <= 12
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
|
|
||||||
<sql id="selectExecuteOrderPo1">
|
<sql id="selectExecuteOrderPo1">
|
||||||
<where>
|
<where>
|
||||||
|
<!-- 默认只查正常单(del_flag=1); 反审/删除的归档行需显式传delFlag=2(已反审tab) -->
|
||||||
|
<choose>
|
||||||
|
<when test="delFlag != null">and eo.DEL_FLAG = #{delFlag}</when>
|
||||||
|
<otherwise>and eo.DEL_FLAG = 1</otherwise>
|
||||||
|
</choose>
|
||||||
<if test="createStartTime != null">
|
<if test="createStartTime != null">
|
||||||
and eo.CREATE_TIME >= #{createStartTime}
|
and eo.CREATE_TIME >= #{createStartTime}
|
||||||
</if>
|
</if>
|
||||||
|
|||||||
+23
-3
@@ -365,6 +365,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<resultMap type="com.mhd.oms.domain.executionInMaterialDetail.repository.po.ExecutionInMaterialDetailWithOrderPO" id="InMaterialDetailWithOrderResult" extends="MaterialDetailResult">
|
<resultMap type="com.mhd.oms.domain.executionInMaterialDetail.repository.po.ExecutionInMaterialDetailWithOrderPO" id="InMaterialDetailWithOrderResult" extends="MaterialDetailResult">
|
||||||
<result property="inOrderStatus" column="in_order_status" />
|
<result property="inOrderStatus" column="in_order_status" />
|
||||||
<result property="inOrderAuditStatus" column="in_order_audit_status" />
|
<result property="inOrderAuditStatus" column="in_order_audit_status" />
|
||||||
|
<result property="issueStatus" column="issue_status" />
|
||||||
|
<result property="materialClassifyName" column="material_classify_name" />
|
||||||
|
<result property="orderTypeCode" column="order_type_code" />
|
||||||
|
<result property="orderTypeName" column="order_type_name" />
|
||||||
<result property="orderShipperId" column="order_shipper_id" />
|
<result property="orderShipperId" column="order_shipper_id" />
|
||||||
<result property="orderShipperCode" column="order_shipper_code" />
|
<result property="orderShipperCode" column="order_shipper_code" />
|
||||||
<result property="orderShipperName" column="order_shipper_name" />
|
<result property="orderShipperName" column="order_shipper_name" />
|
||||||
@@ -403,12 +407,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
a.single_volume, a.single_gross_weight, a.lot_no,
|
a.single_volume, a.single_gross_weight, a.lot_no,
|
||||||
a.drink_date, a.fire_insure_fee, a.unit_price,
|
a.drink_date, a.fire_insure_fee, a.unit_price,
|
||||||
a.line_num_gw, a.spec_gw, a.unit_gw, a.qty_gw, a.erp_curr_gw, a.erp_price_gw, a.amount_gw,
|
a.line_num_gw, a.spec_gw, a.unit_gw, a.qty_gw, a.erp_curr_gw, a.erp_price_gw, a.amount_gw,
|
||||||
c.insure_amount, c.goods_type,
|
c.insure_amount, c.goods_type, c.material_classify_name,
|
||||||
b.shipper_id as order_shipper_id,
|
b.shipper_id as order_shipper_id,
|
||||||
b.shipper_code as order_shipper_code,
|
b.shipper_code as order_shipper_code,
|
||||||
b.shipper_name as order_shipper_name,
|
b.shipper_name as order_shipper_name,
|
||||||
|
b.order_type_code,
|
||||||
|
b.order_type_name,
|
||||||
b.status as in_order_status,
|
b.status as in_order_status,
|
||||||
b.audit_status as in_order_audit_status,
|
b.audit_status as in_order_audit_status,
|
||||||
|
b.issue_status as issue_status,
|
||||||
b.warehouse_date
|
b.warehouse_date
|
||||||
from execution_in_material_detail a
|
from execution_in_material_detail a
|
||||||
left join execution_stock_in_order b on a.in_order_number = b.in_order_number
|
left join execution_stock_in_order b on a.in_order_number = b.in_order_number
|
||||||
@@ -450,15 +457,28 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<if test="inOrderTypeCode != null and inOrderTypeCode != ''">
|
<if test="inOrderTypeCode != null and inOrderTypeCode != ''">
|
||||||
and b.order_type_code = #{inOrderTypeCode}
|
and b.order_type_code = #{inOrderTypeCode}
|
||||||
</if>
|
</if>
|
||||||
<if test="inStatus != null ">
|
<if test="orderTypeCode != null and orderTypeCode != ''">
|
||||||
and b.status = #{inStatus}
|
and b.order_type_code = #{orderTypeCode}
|
||||||
</if>
|
</if>
|
||||||
|
<if test="orderTypeName != null and orderTypeName != ''">
|
||||||
|
and b.order_type_name like concat('%', #{orderTypeName}, '%')
|
||||||
|
</if>
|
||||||
|
<!-- 入库单展示状态issue_status: 0-已创建 2-已审核 3-已驳回 4-已入库(WMS回写status=5时同步为4);
|
||||||
|
"已入库"以issue_status=4为准(推送创建的单据直接置4,status不一定为5) -->
|
||||||
|
and b.issue_status = 4
|
||||||
<if test="inOrderExpectStartTime != null">
|
<if test="inOrderExpectStartTime != null">
|
||||||
and b.expect_time >= #{inOrderExpectStartTime}
|
and b.expect_time >= #{inOrderExpectStartTime}
|
||||||
</if>
|
</if>
|
||||||
<if test="inOrderExpectEndTime != null">
|
<if test="inOrderExpectEndTime != null">
|
||||||
and b.expect_time <= #{inOrderExpectEndTime}
|
and b.expect_time <= #{inOrderExpectEndTime}
|
||||||
</if>
|
</if>
|
||||||
|
<!-- 实际入仓日期区间(为空的历史单按建档时间兜底),用于统计当天入库 -->
|
||||||
|
<if test="warehouseDateStart != null and warehouseDateStart != ''">
|
||||||
|
and date_format(IFNULL(b.warehouse_date, b.create_time), '%Y-%m-%d') >= #{warehouseDateStart}
|
||||||
|
</if>
|
||||||
|
<if test="warehouseDateEnd != null and warehouseDateEnd != ''">
|
||||||
|
and date_format(IFNULL(b.warehouse_date, b.create_time), '%Y-%m-%d') <= #{warehouseDateEnd}
|
||||||
|
</if>
|
||||||
and a.del_flag = 1
|
and a.del_flag = 1
|
||||||
and b.del_flag = 1
|
and b.del_flag = 1
|
||||||
</where>
|
</where>
|
||||||
|
|||||||
+24
-4
@@ -454,6 +454,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
id="OutMaterialDetailWithOrderResult" extends="MaterialDetailResult">
|
id="OutMaterialDetailWithOrderResult" extends="MaterialDetailResult">
|
||||||
<result property="outOrderStatus" column="out_order_status" />
|
<result property="outOrderStatus" column="out_order_status" />
|
||||||
<result property="outOrderAuditStatus" column="out_order_audit_status" />
|
<result property="outOrderAuditStatus" column="out_order_audit_status" />
|
||||||
|
<result property="issueStatus" column="issue_status" />
|
||||||
|
<result property="materialClassifyName" column="material_classify_name" />
|
||||||
|
<result property="orderTypeCode" column="order_type_code" />
|
||||||
|
<result property="orderTypeName" column="order_type_name" />
|
||||||
<result property="orderShipperId" column="order_shipper_id" />
|
<result property="orderShipperId" column="order_shipper_id" />
|
||||||
<result property="orderShipperCode" column="order_shipper_code" />
|
<result property="orderShipperCode" column="order_shipper_code" />
|
||||||
<result property="orderShipperName" column="order_shipper_name" />
|
<result property="orderShipperName" column="order_shipper_name" />
|
||||||
@@ -495,14 +499,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
a.stock_unit, a.stock_quantity, a.inventory_quantity,
|
a.stock_unit, a.stock_quantity, a.inventory_quantity,
|
||||||
a.line_num_gw, a.spec_gw, a.unit_gw, a.qty_gw,
|
a.line_num_gw, a.spec_gw, a.unit_gw, a.qty_gw,
|
||||||
a.erp_curr_gw, a.erp_price_gw, a.amount_gw, a.in_order_number_gw, a.in_line_num_gw,
|
a.erp_curr_gw, a.erp_price_gw, a.amount_gw, a.in_order_number_gw, a.in_line_num_gw,
|
||||||
c.insure_amount, c.goods_type,
|
c.insure_amount, c.goods_type, c.material_classify_name,
|
||||||
b.shipper_id as order_shipper_id,
|
b.shipper_id as order_shipper_id,
|
||||||
b.shipper_code as order_shipper_code,
|
b.shipper_code as order_shipper_code,
|
||||||
b.shipper_name as order_shipper_name,
|
b.shipper_name as order_shipper_name,
|
||||||
|
b.order_type_code,
|
||||||
|
b.order_type_name,
|
||||||
b.OUTBOUND_DATE as order_outbound_date,
|
b.OUTBOUND_DATE as order_outbound_date,
|
||||||
b.OUTBOUND_TIME as order_outbound_time,
|
b.OUTBOUND_TIME as order_outbound_time,
|
||||||
b.status as out_order_status,
|
b.status as out_order_status,
|
||||||
b.audit_status as out_order_audit_status
|
b.audit_status as out_order_audit_status,
|
||||||
|
b.issue_status as issue_status
|
||||||
from execution_out_material_detail a
|
from execution_out_material_detail a
|
||||||
left join execution_stock_out_order b on a.out_order_number = b.out_order_number
|
left join execution_stock_out_order b on a.out_order_number = b.out_order_number
|
||||||
left join NGWL_TEST_WMS.material_base_info c on a.material_base_info_id = c.material_base_info_id
|
left join NGWL_TEST_WMS.material_base_info c on a.material_base_info_id = c.material_base_info_id
|
||||||
@@ -543,15 +550,28 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<if test="outOrderTypeCode != null and outOrderTypeCode != ''">
|
<if test="outOrderTypeCode != null and outOrderTypeCode != ''">
|
||||||
and b.order_type_code = #{outOrderTypeCode}
|
and b.order_type_code = #{outOrderTypeCode}
|
||||||
</if>
|
</if>
|
||||||
<if test="outStatus != null ">
|
<if test="orderTypeCode != null and orderTypeCode != ''">
|
||||||
and b.status = #{outStatus}
|
and b.order_type_code = #{orderTypeCode}
|
||||||
</if>
|
</if>
|
||||||
|
<if test="orderTypeName != null and orderTypeName != ''">
|
||||||
|
and b.order_type_name like concat('%', #{orderTypeName}, '%')
|
||||||
|
</if>
|
||||||
|
<!-- 出库单展示状态issue_status: 0-已创建 2-已审核 3-已驳回 4-已出库;
|
||||||
|
执行出库单status不随WMS出库完成流转(回写被注释),"已出库"以issue_status=4为准 -->
|
||||||
|
and b.issue_status = 4
|
||||||
<if test="outOrderExpectStartTime != null">
|
<if test="outOrderExpectStartTime != null">
|
||||||
and b.expect_time >= #{outOrderExpectStartTime}
|
and b.expect_time >= #{outOrderExpectStartTime}
|
||||||
</if>
|
</if>
|
||||||
<if test="outOrderExpectEndTime != null">
|
<if test="outOrderExpectEndTime != null">
|
||||||
and b.expect_time <= #{outOrderExpectEndTime}
|
and b.expect_time <= #{outOrderExpectEndTime}
|
||||||
</if>
|
</if>
|
||||||
|
<!-- 出库时间区间(统计当天出库):严格按出仓日期OUTBOUND_DATE过滤(页面所见即所筛) -->
|
||||||
|
<if test="outboundDateStart != null and outboundDateStart != ''">
|
||||||
|
and date_format(b.OUTBOUND_DATE, '%Y-%m-%d') >= #{outboundDateStart}
|
||||||
|
</if>
|
||||||
|
<if test="outboundDateEnd != null and outboundDateEnd != ''">
|
||||||
|
and date_format(b.OUTBOUND_DATE, '%Y-%m-%d') <= #{outboundDateEnd}
|
||||||
|
</if>
|
||||||
and a.del_flag = 1
|
and a.del_flag = 1
|
||||||
and b.del_flag = 1
|
and b.del_flag = 1
|
||||||
</where>
|
</where>
|
||||||
|
|||||||
+17
-2
@@ -108,6 +108,11 @@
|
|||||||
<result property="discount" column="DISCOUNT" />
|
<result property="discount" column="DISCOUNT" />
|
||||||
<result property="isFreeManpowerFee" column="IS_FREE_MANPOWER_FEE" />
|
<result property="isFreeManpowerFee" column="IS_FREE_MANPOWER_FEE" />
|
||||||
<result property="businessInOrderNumber" column="BUSINESS_IN_ORDER_NUMBER" />
|
<result property="businessInOrderNumber" column="BUSINESS_IN_ORDER_NUMBER" />
|
||||||
|
<result property="reverseAuditStatus" column="REVERSE_AUDIT_STATUS" />
|
||||||
|
<result property="reverseAuditRemark" column="REVERSE_AUDIT_REMARK" />
|
||||||
|
<result property="reverseAuditBy" column="REVERSE_AUDIT_BY" />
|
||||||
|
<result property="reverseAuditByName" column="REVERSE_AUDIT_BY_NAME" />
|
||||||
|
<result property="reverseAuditTime" column="REVERSE_AUDIT_TIME" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
|
|
||||||
@@ -145,7 +150,8 @@
|
|||||||
a.WAREHOUSE_DATE, a.COMPLETION_TIME, a.ORDER_DATE,a.reservation_audit_time,a.reservation_audit_name
|
a.WAREHOUSE_DATE, a.COMPLETION_TIME, a.ORDER_DATE,a.reservation_audit_time,a.reservation_audit_name
|
||||||
,a.reservation_audit_id,a.issue_name,a.issue_id,a.issue_time,a.issue_status,a.RESERVATION_ORDER_SOURCE,a.tms_order_number
|
,a.reservation_audit_id,a.issue_name,a.issue_id,a.issue_time,a.issue_status,a.RESERVATION_ORDER_SOURCE,a.tms_order_number
|
||||||
,a.DIRECTOR_NAME ,a.DIRECTOR_PHONE,a.DIRECTOR_ID,a.STORAGE_TIME,a.in_quantity,
|
,a.DIRECTOR_NAME ,a.DIRECTOR_PHONE,a.DIRECTOR_ID,a.STORAGE_TIME,a.in_quantity,
|
||||||
a.PUSH_TIME,a.PUSH_STATUS,a.PUSH_BY,a.PUSH_BY_NAME
|
a.PUSH_TIME,a.PUSH_STATUS,a.PUSH_BY,a.PUSH_BY_NAME,
|
||||||
|
a.REVERSE_AUDIT_STATUS,a.REVERSE_AUDIT_REMARK,a.REVERSE_AUDIT_BY,a.REVERSE_AUDIT_BY_NAME,a.REVERSE_AUDIT_TIME
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<sql id="joinStockInOrderPo">
|
<sql id="joinStockInOrderPo">
|
||||||
@@ -288,7 +294,7 @@
|
|||||||
and a.order_type_code = #{orderTypeCode}
|
and a.order_type_code = #{orderTypeCode}
|
||||||
</if>
|
</if>
|
||||||
<if test="orderTypeName != null and orderTypeName != ''">
|
<if test="orderTypeName != null and orderTypeName != ''">
|
||||||
and (a.BUSINESS_TYPE like concat('%', #{orderTypeName}, '%') or a.ORDER_TYPE_CODE like concat('%', #{orderTypeName}, '%'))
|
and (a.ORDER_TYPE_NAME like concat('%', #{orderTypeName}, '%') or a.BUSINESS_TYPE like concat('%', #{orderTypeName}, '%') or a.ORDER_TYPE_CODE like concat('%', #{orderTypeName}, '%'))
|
||||||
</if>
|
</if>
|
||||||
<if test="supplierId != null ">
|
<if test="supplierId != null ">
|
||||||
and a.supplier_id = #{supplierId}
|
and a.supplier_id = #{supplierId}
|
||||||
@@ -501,10 +507,19 @@
|
|||||||
</if>
|
</if>
|
||||||
)
|
)
|
||||||
</if>
|
</if>
|
||||||
|
<!-- 反审状态过滤:入库执行单【已反审】tab 传 reverseAuditStatus=1,查询已软删且打反审标记的归档数据 -->
|
||||||
|
<choose>
|
||||||
|
<when test="reverseAuditStatus != null">
|
||||||
|
and a.REVERSE_AUDIT_STATUS = #{reverseAuditStatus}
|
||||||
|
and a.del_flag = 2
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
<choose>
|
<choose>
|
||||||
<when test="delFlag != null"> and a.del_flag = #{delFlag} </when>
|
<when test="delFlag != null"> and a.del_flag = #{delFlag} </when>
|
||||||
<otherwise> and a.del_flag = 1 </otherwise>
|
<otherwise> and a.del_flag = 1 </otherwise>
|
||||||
</choose>
|
</choose>
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="queryList" parameterType="com.mhd.oms.domain.executionStockInOrder.repository.todo.ExecutionStockInOrderDO" resultMap="StockInOrderResult">
|
<select id="queryList" parameterType="com.mhd.oms.domain.executionStockInOrder.repository.todo.ExecutionStockInOrderDO" resultMap="StockInOrderResult">
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user