From 87733eb3623593da247c051593d64b8ff9e1d3a4 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 06 十一月 2026 19:00:39 +0800
Subject: [PATCH] feat(crm,mes): 扩展客户字段与重构批次号生成逻辑
---
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java | 167 ++++++++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 140 insertions(+), 27 deletions(-)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
index 1b67a0c..1b8abc1 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
@@ -71,6 +71,7 @@
@Validated
@Slf4j
public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
+ @Resource private cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesShipmentMapper shipmentMapper;
@Resource
private MesWmProductSalesMapper productSalesMapper;
@@ -181,20 +182,26 @@
@Override
public boolean checkProductSalesQuantity(Long id) {
- // 鏍¢獙姣忚鏄庣粏鏁伴噺涔嬪拰鏄惁绛変簬琛屽嚭搴撴暟閲�
+ // 鏈鑷冲皯鎷d竴鏉★紝绱鎷h揣涓嶈秴杩囪鍒掞紱鍏佽璺ㄨ銆佸垎娆″彂璐с��
List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(id);
+ boolean hasPending = false;
for (MesWmProductSalesLineDO line : lines) {
List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListByLineId(line.getId());
- if (CollUtil.isEmpty(details)) {
- return false;
- }
+ if (details.stream().anyMatch(d -> d.getShipmentId() == null)) hasPending = true;
+ if (details.stream().anyMatch(d -> d.getQuantity() == null || d.getQuantity().signum() <= 0)) return false;
BigDecimal totalDetailQty = CollectionUtils.getSumValue(details,
MesWmProductSalesDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO);
- if (line.getQuantity() != null && totalDetailQty.compareTo(line.getQuantity()) != 0) {
+ if (line.getQuantity() == null || totalDetailQty.compareTo(line.getQuantity()) > 0) {
return false;
}
}
- return true;
+ return hasPending;
+ }
+
+ @Override
+ public List<cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO> getShipments(Long id) {
+ validateProductSalesExists(id);
+ return shipmentMapper.selectBySalesId(id);
}
private record PickingScope(String type, Long id, MesProBatchDO batch, List<MesProBagCodeDO> bags) { }
@@ -272,7 +279,8 @@
List<MesWmProductSalesScanRespVO.BagOption> options = scope.bags().stream().map(b -> {
String reason = unavailable(b, stockBags.get(b.getId()), stocks, occupied, scope.batch());
return new MesWmProductSalesScanRespVO.BagOption().setId(b.getId()).setCode(b.getCode()).setBusinessCode(b.getBusinessCode())
- .setPalletId(b.getPalletId()).setSelectable(reason == null).setUnavailableReason(reason);
+ .setPalletId(b.getPalletId()).setMaterialStockId(stockBags.get(b.getId()) == null ? null : stockBags.get(b.getId()).getMaterialStockId())
+ .setSelectable(reason == null).setUnavailableReason(reason);
}).toList();
return new MesWmProductSalesScanRespVO().setScanType(scope.type()).setCodeId(scope.id()).setScanContent(code)
.setBatchCode(scope.batch().getCode()).setItemId(scope.batch().getItemId()).setBagCount(options.size()).setBags(options)
@@ -282,6 +290,8 @@
@Override
@Transactional(rollbackFor = Exception.class)
public MesWmProductSalesScanRespVO scanProductSales(MesWmProductSalesScanReqVO reqVO) {
+ if (Boolean.TRUE.equals(reqVO.getManual()) && (reqVO.getLineId() == null || reqVO.getMaterialStockId() == null))
+ throw new IllegalArgumentException("鎵嬪伐閫夋嫨琚嬬爜蹇呴』鎸囧畾鍑哄簱鍗曡鍜屽簱瀛樿褰�");
MesWmProductSalesDO sales = productSalesMapper.selectByIdForUpdate(reqVO.getSalesId());
if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS);
if (!Integer.valueOf(2).equals(sales.getStatus())) throw exception(WM_PRODUCT_SALES_CANNOT_PICK);
@@ -302,6 +312,10 @@
List<MesWmProductSalesDetailDO> occupied = productSalesDetailMapper.selectPickingByBatchCodeForUpdate(scope.batch().getCode());
List<MesWmProductSalesDetailDO> own = occupied.stream().filter(d -> Objects.equals(d.getSalesId(), sales.getId())).toList();
long already = selected.stream().filter(b -> own.stream().anyMatch(d -> Objects.equals(d.getBagCodeId(), b.getId()))).count();
+ if (already > 0 && own.stream().anyMatch(d -> selectedIds.contains(d.getBagCodeId())
+ && (reqVO.getLineId() != null && !Objects.equals(reqVO.getLineId(), d.getLineId())
+ || reqVO.getMaterialStockId() != null && !Objects.equals(reqVO.getMaterialStockId(), d.getMaterialStockId()))))
+ throw new IllegalArgumentException("鎵�閫夎鐮佸凡鍔犲叆鏈崟鍏朵粬琛屾垨搴撳瓨璁板綍");
if (already == selected.size()) return new MesWmProductSalesScanRespVO().setScanType(scope.type()).setCodeId(scope.id())
.setScanContent(reqVO.getScanContent()).setSelectedBagIds(ids).setAlreadyScanned(true).setOutboundQuantity(quantity)
.setDetailId(own.stream().filter(d -> selectedIds.contains(d.getBagCodeId())).findFirst().orElseThrow().getId()).setMessage("鎵�閫夎鐮佸凡鍦ㄦ湰鍗曟嫞璐ф槑缁嗕腑");
@@ -313,9 +327,17 @@
for (MesProBagCodeDO bag : selected) {
String reason = unavailable(bag, stockBags.get(bag.getId()), stocks, occupied, scope.batch());
if (reason != null) throw new IllegalArgumentException("琚嬬爜 " + bag.getCode() + "锛�" + reason);
+ if (reqVO.getMaterialStockId() != null && !Objects.equals(reqVO.getMaterialStockId(), stockBags.get(bag.getId()).getMaterialStockId()))
+ throw new IllegalArgumentException("鎵�閫夎鐮佷笉灞炰簬鎸囧畾搴撳瓨璁板綍");
}
MesWmProductSalesLineDO line = productSalesLineService.getProductSalesLineListBySalesId(sales.getId()).stream()
- .filter(l -> Objects.equals(l.getItemId(), scope.batch().getItemId())).findFirst().orElse(null);
+ .filter(l -> Objects.equals(l.getItemId(), scope.batch().getItemId())
+ && (reqVO.getLineId() == null || Objects.equals(l.getId(), reqVO.getLineId()))
+ && (l.getBatchCode() == null || l.getBatchCode().isBlank() || Objects.equals(l.getBatchCode(), wmBatch.getCode())))
+ .filter(l -> l.getQuantity() != null && productSalesDetailService.getProductSalesDetailListByLineId(l.getId()).stream()
+ .map(MesWmProductSalesDetailDO::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add)
+ .add(quantity).compareTo(l.getQuantity()) <= 0)
+ .findFirst().orElse(null);
if (line == null) throw exception(WM_PRODUCT_SALES_DETAIL_ITEM_MISMATCH);
BigDecimal scanned = productSalesDetailService.getProductSalesDetailListByLineId(line.getId()).stream()
.map(MesWmProductSalesDetailDO::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
@@ -334,7 +356,11 @@
.setItemId(scope.batch().getItemId()).setQuantity(BigDecimal.ONE).setBagCodeId(bag.getId())
.setPalletCodeId(bag.getPalletId()).setBatchId(wmBatch.getId()).setBatchCode(wmBatch.getCode()).setMaterialStockId(stock.getId())
.setWarehouseId(stock.getWarehouseId()).setLocationId(stock.getLocationId()).setAreaId(stock.getAreaId())
- .setRemark("鎵爜绫诲瀷 " + scope.type() + "锛涙潵婧愮爜 " + reqVO.getScanContent() + "锛涜鐮� " + bag.getCode());
+ .setRemark((Boolean.TRUE.equals(reqVO.getManual()) ? "鎵嬪伐閫夋嫨锛�" : "") + "鎵爜绫诲瀷 " + scope.type() + "锛�" + switch (scope.type()) {
+ case "BATCH" -> "鏉ユ簮鎵规鐮� ";
+ case "PALLET" -> "鏉ユ簮鎵樼洏鐮� ";
+ default -> "鏉ユ簮琚嬬爜 ";
+ } + reqVO.getScanContent() + "锛涜鐮� " + bag.getCode());
productSalesDetailMapper.insert(detail);
if (firstDetail == null) firstDetail = detail.getId();
}
@@ -347,19 +373,21 @@
@Transactional(rollbackFor = Exception.class)
public void stockProductSales(Long id) {
// 鏍¢獙瀛樺湪
- MesWmProductSalesDO sales = validateProductSalesExists(id);
+ MesWmProductSalesDO sales = productSalesMapper.selectByIdForUpdate(id);
+ if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS);
if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.APPROVING.getStatus(), sales.getStatus())) {
throw exception(WM_PRODUCT_SALES_CANNOT_PICK);
}
+ if (!checkProductSalesQuantity(id)) throw new IllegalArgumentException("璇锋嫞閫夋湰娆″彂璐ф槑缁嗭紝涓旂疮璁℃暟閲忎笉鑳借秴杩囪鍒掓暟閲�");
+ var shipment = new cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO();
+ shipment.setSalesId(id); shipment.setStatus(10); shipmentMapper.insert(shipment);
// 鏍¢獙姣忚鑷冲皯鏈夋嫞璐ф槑缁嗭紝骞舵牎楠屽簱瀛樺厖瓒�
List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(id);
for (MesWmProductSalesLineDO line : lines) {
List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListByLineId(line.getId());
- if (CollUtil.isEmpty(details)) {
- throw exception(WM_PRODUCT_SALES_DETAILS_EMPTY);
- }
// 鏍¢獙搴撳瓨鍏呰冻骞跺垱寤哄崰鐢�
for (MesWmProductSalesDetailDO detail : details) {
+ if (detail.getShipmentId() != null) continue;
materialStockService.validateSelectedStock(
detail.getMaterialStockId(),
detail.getItemId(),
@@ -380,6 +408,7 @@
detail.getItemId(),
detail.getQuantity()
);
+ productSalesDetailMapper.updateById(new MesWmProductSalesDetailDO().setId(detail.getId()).setShipmentId(shipment.getId()));
}
}
@@ -392,10 +421,15 @@
@Transactional(rollbackFor = Exception.class)
public void shippingProductSales(MesWmProductSalesShippingReqVO reqVO) {
// 鏍¢獙瀛樺湪
- MesWmProductSalesDO sales = validateProductSalesExists(reqVO.getId());
+ MesWmProductSalesDO sales = productSalesMapper.selectByIdForUpdate(reqVO.getId());
+ if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS);
if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.SHIPPING.getStatus(), sales.getStatus())) {
throw exception(WM_PRODUCT_SALES_CANNOT_SHIPPING);
}
+
+ var shipment = ensureActiveShipment(sales, 10);
+ shipmentMapper.updateById(new cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO()
+ .setId(shipment.getId()).setStatus(20).setCarrier(reqVO.getCarrier()).setShippingNumber(reqVO.getShippingNumber()));
// 濉啓杩愬崟锛堝緟濉啓杩愬崟 鈫� 寰呮墽琛屽嚭搴擄級
productSalesMapper.updateById(new MesWmProductSalesDO()
@@ -409,16 +443,31 @@
@Transactional(rollbackFor = Exception.class)
public void finishProductSales(Long id) {
// 1. 鏍¢獙瀛樺湪
- MesWmProductSalesDO sales = validateProductSalesExists(id);
+ MesWmProductSalesDO sales = productSalesMapper.selectByIdForUpdate(id);
+ if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS);
if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.APPROVED.getStatus(), sales.getStatus())) {
throw exception(WM_PRODUCT_SALES_CANNOT_FINISH);
}
+ var shipment = ensureActiveShipment(sales, 20);
+ List<MesWmProductSalesDetailDO> outDetails = productSalesDetailService.getProductSalesDetailListBySalesId(id).stream()
+ .filter(d -> Objects.equals(d.getShipmentId(), shipment.getId())).toList();
+ if (outDetails.isEmpty()) throw new IllegalArgumentException("鏈鍙戣揣娌℃湁鍑哄簱鏄庣粏");
// 2. 纭鍗犵敤杞嚭搴擄紙閲婃斁鍗犵敤閲忥級
stockReserveService.confirmReserveToOutbound(MesWmStockReserveBizTypeEnum.PRODUCT_SALES.getType(), id);
// 3. 閬嶅巻鎵�鏈夋槑缁嗭紝鍒涘缓搴撳瓨浜嬪姟锛堟墸鍑忓簱瀛� + 璁板綍娴佹按锛�
- createTransactionList(sales);
+ createTransactionList(sales, outDetails);
+
+ shipmentMapper.updateById(new cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO()
+ .setId(shipment.getId()).setStatus(30).setOutboundTime(java.time.LocalDateTime.now())
+ .setOutboundUserId(SecurityFrameworkUtils.getLoginUserId()));
+ boolean complete = productSalesLineService.getProductSalesLineListBySalesId(id).stream().allMatch(line -> {
+ BigDecimal total = productSalesDetailService.getProductSalesDetailListByLineId(line.getId()).stream()
+ .filter(d -> d.getShipmentId() != null).map(MesWmProductSalesDetailDO::getQuantity)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+ return line.getQuantity() != null && total.compareTo(line.getQuantity()) == 0;
+ });
// 4. 鍥哄寲缁忛攢鍟嗕笌瀹為檯鍑哄簱鎿嶄綔淇℃伅
CrmCustomerRespDTO customer = customerApi.getCustomer(sales.getClientId());
@@ -427,15 +476,14 @@
.setClientName(customer != null ? customer.getName() : null)
.setOutboundTime(java.time.LocalDateTime.now())
.setOutboundUserId(SecurityFrameworkUtils.getLoginUserId())
- .setStatus(MesWmProductSalesStatusEnum.FINISHED.getStatus()));
+ .setStatus(complete ? MesWmProductSalesStatusEnum.FINISHED.getStatus() : MesWmProductSalesStatusEnum.APPROVING.getStatus()));
// 5. 鏇存柊鍏宠仈鐨勫彂璐ч�氱煡鍗曞嚭搴撴暟閲忓拰鐘舵��
if (sales.getNoticeId() != null) {
- updateSalesNoticeOutCount(sales);
+ updateSalesNoticeOutCount(sales, outDetails);
}
// 6. 鍥炲啓琚嬬爜/鎵樼洏鍑哄簱鍘诲悜锛堜緵绐滆揣绋芥煡瀹氫綅缁忛攢鍟嗗湴鍖猴級锛屽苟鐢熸垚鍑哄簱瀹屾暣鎬ф憳瑕�
- List<MesWmProductSalesDetailDO> outDetails = productSalesDetailService.getProductSalesDetailListBySalesId(id);
for (MesWmProductSalesDetailDO detail : outDetails) {
if (detail.getBagCodeId() != null) {
bagCodeService.markOutbound(detail.getBagCodeId(), id);
@@ -445,13 +493,13 @@
palletService.markOutbound(detail.getPalletCodeId(), id);
}
}
- integrityService.recordSalesIntegrity(id);
+ if (complete) integrityService.recordSalesIntegrity(id);
}
/**
* 鏇存柊鍙戣揣閫氱煡鍗曠殑鍑哄簱鏁伴噺鍜岀姸鎬�
*/
- private void updateSalesNoticeOutCount(MesWmProductSalesDO sales) {
+ private void updateSalesNoticeOutCount(MesWmProductSalesDO sales, List<MesWmProductSalesDetailDO> outDetails) {
// 鑾峰彇鍑哄簱鍗曡
List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(sales.getId());
if (CollUtil.isEmpty(lines)) {
@@ -459,44 +507,109 @@
}
// 閬嶅巻琛岋紝鎸� noticeLineId 鏇存柊鍑哄簱鏁伴噺
for (MesWmProductSalesLineDO line : lines) {
- if (line.getNoticeLineId() != null && line.getQuantity() != null) {
- salesNoticeService.updateSalesNoticeLineOutCount(line.getNoticeLineId(), line.getQuantity());
+ BigDecimal quantity = outDetails.stream().filter(d -> Objects.equals(d.getLineId(), line.getId()))
+ .map(MesWmProductSalesDetailDO::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
+ if (line.getNoticeLineId() != null && quantity.signum() > 0) {
+ salesNoticeService.updateSalesNoticeLineOutCount(line.getNoticeLineId(), quantity);
}
}
}
- private void createTransactionList(MesWmProductSalesDO sales) {
- List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListBySalesId(sales.getId());
+ private void createTransactionList(MesWmProductSalesDO sales, List<MesWmProductSalesDetailDO> details) {
+ Set<Long> bagIds = details.stream().map(MesWmProductSalesDetailDO::getBagCodeId).filter(Objects::nonNull).collect(Collectors.toSet());
+ Map<Long, MesProBagCodeDO> bags = bagIds.isEmpty() ? Map.of() : pickingBagMapper.selectBatchIds(bagIds).stream()
+ .collect(Collectors.toMap(MesProBagCodeDO::getId, b -> b));
wmTransactionService.createTransactionList(convertList(details, detail -> new MesWmTransactionSaveReqDTO()
.setType(MesWmTransactionTypeEnum.OUT.getType()).setItemId(detail.getItemId())
.setQuantity(detail.getQuantity().negate()) // 鍑哄簱鏁伴噺涓鸿礋鏁�
.setBatchId(detail.getBatchId()).setBatchCode(detail.getBatchCode())
.setBagCodeId(detail.getBagCodeId()) // 鏂版壂鐮佹槑缁嗛�愯鍏宠仈锛屾墽琛屽嚭搴撻�愯鏇存柊搴撳瓨鐘舵��
+ .setShipmentId(detail.getShipmentId())
+ .setBagCode(detail.getBagCodeId() == null || !bags.containsKey(detail.getBagCodeId()) ? null : bags.get(detail.getBagCodeId()).getCode())
.setWarehouseId(detail.getWarehouseId()).setLocationId(detail.getLocationId()).setAreaId(detail.getAreaId())
.setBizType(MesBizTypeConstants.WM_PRODUCT_SALES).setBizId(sales.getId())
.setBizCode(sales.getCode()).setBizLineId(detail.getLineId())
.setMaterialStockId(detail.getMaterialStockId()))); // 鎸囧畾搴撳瓨璁板綍ID锛岄伩鍏嶉噸鏂板尮閰�
}
+ /** 鍏煎鍗囩骇鍓嶅凡杩涘叆寰呰繍鍗曟垨寰呭嚭搴撶姸鎬佺殑鏃у崟鎹�傝皟鐢ㄦ柟椤绘寔鏈夊嚭搴撳崟琛岄攣銆� */
+ private cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO ensureActiveShipment(
+ MesWmProductSalesDO sales, int expectedStatus) {
+ var shipment = shipmentMapper.selectActive(sales.getId());
+ if (shipment != null) {
+ if (!Integer.valueOf(expectedStatus).equals(shipment.getStatus())) throw new IllegalArgumentException("鍙戣揣璁板綍鐘舵�佸紓甯革紝璇峰埛鏂板悗閲嶈瘯");
+ return shipment;
+ }
+ shipment = new cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO();
+ shipment.setSalesId(sales.getId()); shipment.setStatus(expectedStatus);
+ shipment.setCarrier(sales.getCarrier()); shipment.setShippingNumber(sales.getShippingNumber());
+ shipmentMapper.insert(shipment);
+ for (MesWmProductSalesDetailDO detail : productSalesDetailService.getProductSalesDetailListBySalesId(sales.getId())) {
+ if (detail.getShipmentId() == null)
+ productSalesDetailMapper.updateById(new MesWmProductSalesDetailDO().setId(detail.getId()).setShipmentId(shipment.getId()));
+ }
+ return shipment;
+ }
+
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelProductSales(Long id) {
// 鏍¢獙瀛樺湪
- MesWmProductSalesDO sales = validateProductSalesExists(id);
+ MesWmProductSalesDO sales = productSalesMapper.selectByIdForUpdate(id);
+ if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS);
// 宸插畬鎴愬拰宸插彇娑堜笉鍏佽鍙栨秷
if (ObjectUtils.equalsAny(sales.getStatus(),
MesWmProductSalesStatusEnum.FINISHED.getStatus(),
- MesWmProductSalesStatusEnum.CANCELED.getStatus())) {
+ MesWmProductSalesStatusEnum.CANCELED.getStatus(), MesWmProductSalesStatusEnum.CLOSED.getStatus())) {
throw exception(WM_PRODUCT_SALES_CANNOT_CANCEL);
}
+ if (shipmentMapper.selectBySalesId(id).stream().anyMatch(s -> Integer.valueOf(30).equals(s.getStatus())))
+ throw new IllegalArgumentException("璇ュ崟宸叉湁鍑哄簱璁板綍锛岃鍏抽棴鍓╀綑鏁伴噺锛屼笉鑳藉彇娑堝凡鍙戣揣鍗曟嵁");
// 閲婃斁搴撳瓨鍗犵敤
stockReserveService.releaseReserve(MesWmStockReserveBizTypeEnum.PRODUCT_SALES.getType(), id);
+ var shipment = shipmentMapper.selectActive(id);
+ if (shipment != null) shipmentMapper.updateById(new cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO()
+ .setId(shipment.getId()).setStatus(40));
// 鍙栨秷
productSalesMapper.updateById(new MesWmProductSalesDO()
.setId(id).setStatus(MesWmProductSalesStatusEnum.CANCELED.getStatus()));
}
@Override
+ @Transactional(rollbackFor = Exception.class)
+ public void closeRemaining(Long id, String reason) {
+ var sales = productSalesMapper.selectByIdForUpdate(id);
+ if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS);
+ if (!MesWmProductSalesStatusEnum.APPROVING.getStatus().equals(sales.getStatus()))
+ throw new IllegalArgumentException("浠呭緟鎷h揣鐘舵�佸彲鍏抽棴鍓╀綑鏁伴噺");
+ if (reason == null || reason.isBlank() || reason.trim().length() > 500)
+ throw new IllegalArgumentException("璇峰~鍐欎笉瓒呰繃500瀛楃殑鍏抽棴鍘熷洜");
+ if (shipmentMapper.selectBySalesId(id).stream().noneMatch(s -> Integer.valueOf(30).equals(s.getStatus())))
+ throw new IllegalArgumentException("灏氭湭鍙戠敓鍑哄簱锛岃浣跨敤鍙栨秷鎿嶄綔");
+ if (productSalesDetailService.getProductSalesDetailListBySalesId(id).stream().anyMatch(d -> d.getShipmentId() == null))
+ throw new IllegalArgumentException("璇峰厛鍒犻櫎鏈鏈彂璐х殑鎷h揣鏄庣粏");
+ productSalesMapper.updateById(new MesWmProductSalesDO().setId(id)
+ .setStatus(MesWmProductSalesStatusEnum.CLOSED.getStatus()).setCloseReason(reason.trim()));
+ integrityService.recordSalesIntegrity(id);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void cancelShipment(Long id) {
+ var sales = productSalesMapper.selectByIdForUpdate(id);
+ if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS);
+ if (!ObjectUtils.equalsAny(sales.getStatus(), MesWmProductSalesStatusEnum.SHIPPING.getStatus(), MesWmProductSalesStatusEnum.APPROVED.getStatus()))
+ throw new IllegalArgumentException("浠呭緟杩愬崟鎴栧緟鍑哄簱鐨勫彂璐ц褰曞彲鍙栨秷");
+ int expected = MesWmProductSalesStatusEnum.SHIPPING.getStatus().equals(sales.getStatus()) ? 10 : 20;
+ var shipment = ensureActiveShipment(sales, expected);
+ stockReserveService.releaseReserve(MesWmStockReserveBizTypeEnum.PRODUCT_SALES.getType(), id);
+ productSalesDetailMapper.unassignShipment(shipment.getId());
+ shipmentMapper.updateById(new cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO()
+ .setId(shipment.getId()).setStatus(40));
+ productSalesMapper.updateById(new MesWmProductSalesDO().setId(id).setStatus(MesWmProductSalesStatusEnum.APPROVING.getStatus()));
+ }
+
+ @Override
public void confirmProductSales(Long id) {
// 鏍¢獙瀛樺湪 + 寰呮娴嬬姸鎬�
MesWmProductSalesDO sales = validateProductSalesExists(id);
--
Gitblit v1.9.3