| | |
| | | @Validated |
| | | @Slf4j |
| | | public class MesWmProductSalesServiceImpl implements MesWmProductSalesService { |
| | | @Resource private cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesShipmentMapper shipmentMapper; |
| | | |
| | | @Resource |
| | | private MesWmProductSalesMapper productSalesMapper; |
| | |
| | | |
| | | @Override |
| | | public boolean checkProductSalesQuantity(Long id) { |
| | | // 校验每行明细数量之和是否等于行出库数量 |
| | | // 本次至少拣一条,累计拣货不超过计划;允许跨行、分次发货。 |
| | | 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) { } |
| | |
| | | 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) |
| | |
| | | @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); |
| | |
| | | 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("所选袋码已在本单拣货明细中"); |
| | |
| | | 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); |
| | |
| | | .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(); |
| | | } |
| | |
| | | @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(), |
| | |
| | | detail.getItemId(), |
| | | detail.getQuantity() |
| | | ); |
| | | productSalesDetailMapper.updateById(new MesWmProductSalesDetailDO().setId(detail.getId()).setShipmentId(shipment.getId())); |
| | | } |
| | | } |
| | | |
| | |
| | | @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() |
| | |
| | | @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()); |
| | |
| | | .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); |
| | |
| | | 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)) { |
| | |
| | | } |
| | | // 遍历行,按 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("仅待拣货状态可关闭剩余数量"); |
| | | 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("请先删除本次未发货的拣货明细"); |
| | | 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); |