| | |
| | | * 附件业务类型:MES 供应商退货单 |
| | | */ |
| | | public static final String RETURN_VENDOR_RECORD_TYPE = "mes_wm_return_vendor"; |
| | | private static final String RETURN_VENDOR_APPROVAL_KEY = "purchase_return_vendor_approve"; |
| | | |
| | | @Resource |
| | | private MesWmReturnVendorMapper returnVendorMapper; |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void submitReturnVendor(Long id) { |
| | | // 校验存在 + 草稿状态 |
| | | validateReturnVendorExistsAndPrepare(id); |
| | | // 校验至少有一条行 |
| | | List<MesWmReturnVendorLineDO> lines = returnVendorLineService.getReturnVendorLineListByReturnId(id); |
| | | if (CollUtil.isEmpty(lines)) { |
| | | throw exception(WM_RETURN_VENDOR_NO_LINE); |
| | | } |
| | | |
| | | // 提交(草稿 -> 待拣货) |
| | | returnVendorMapper.updateById(new MesWmReturnVendorDO() |
| | | .setId(id).setStatus(MesWmReturnVendorStatusEnum.APPROVING.getStatus())); |
| | | submitReturnVendorApproval(id, RETURN_VENDOR_APPROVAL_KEY); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void submitReturnVendorApproval(Long id, String processDefinitionKey) { |
| | | if (!RETURN_VENDOR_APPROVAL_KEY.equals(processDefinitionKey)) { |
| | | throw exception(WM_RETURN_VENDOR_APPROVAL_PROCESS_INVALID, RETURN_VENDOR_APPROVAL_KEY); |
| | | } |
| | | // 1. 校验存在 + 草稿状态 + 至少一条行 |
| | | MesWmReturnVendorDO returnVendor = validateReturnVendorExistsAndPrepare(id); |
| | | if (CollUtil.isEmpty(returnVendorLineService.getReturnVendorLineListByReturnId(id))) { |
| | | throw exception(WM_RETURN_VENDOR_NO_LINE); |
| | | } |
| | | validateReturnVendorOrderQuantity(returnVendor); |
| | | // 拣货可在审批通过后进行;如已提前录入明细,则提交前必须数量守恒。 |
| | | if (CollUtil.isNotEmpty(returnVendorDetailService.getReturnVendorDetailListByReturnId(id)) |
| | | && !checkReturnVendorQuantity(id)) { |
| | | throw exception(WM_RETURN_VENDOR_DETAIL_QUANTITY_MISMATCH); |
| | | } |
| | | // 1.1. 质量部发起退货必须上传附件(质检报告、不合格照片等),否则审批人无法判断退货依据 |
| | | if (CollUtil.isEmpty(storageAttachmentApi.listAttachments(RETURN_VENDOR_RECORD_TYPE, id))) { |
| | |
| | | if (ObjUtil.notEqual(MesWmReturnVendorStatusEnum.APPROVING.getStatus(), returnVendor.getStatus())) { |
| | | throw exception(WM_RETURN_VENDOR_STATUS_INVALID); |
| | | } |
| | | validateReturnVendorApproval(returnVendor); |
| | | if (!checkReturnVendorQuantity(id)) { |
| | | throw exception(WM_RETURN_VENDOR_DETAIL_QUANTITY_MISMATCH); |
| | | } |
| | | validateReturnVendorOrderQuantity(returnVendor); |
| | | // 执行拣货(待拣货 -> 待执行退货) |
| | | returnVendorMapper.updateById(new MesWmReturnVendorDO() |
| | | .setId(id).setStatus(MesWmReturnVendorStatusEnum.APPROVED.getStatus())); |
| | |
| | | if (ObjUtil.notEqual(MesWmReturnVendorStatusEnum.APPROVED.getStatus(), returnVendor.getStatus())) { |
| | | throw exception(WM_RETURN_VENDOR_STATUS_INVALID); |
| | | } |
| | | validateReturnVendorApproval(returnVendor); |
| | | // 1.2 校验明细不为空 |
| | | List<MesWmReturnVendorDetailDO> details = returnVendorDetailService.getReturnVendorDetailListByReturnId(id); |
| | | if (CollUtil.isEmpty(details)) { |
| | | throw exception(WM_RETURN_VENDOR_NO_DETAIL); |
| | | } |
| | | if (!checkReturnVendorQuantity(id)) { |
| | | throw exception(WM_RETURN_VENDOR_DETAIL_QUANTITY_MISMATCH); |
| | | } |
| | | validateReturnVendorOrderQuantity(returnVendor); |
| | | |
| | | // 1.3 校验处理方式(退款 / 换货) |
| | | if (!MesWmReturnVendorHandleTypeEnum.isValid(returnVendor.getHandleType())) { |
| | |
| | | } |
| | | } |
| | | |
| | | private void validateReturnVendorApproval(MesWmReturnVendorDO returnVendor) { |
| | | if (!RETURN_VENDOR_APPROVAL_KEY.equals(returnVendor.getProcessDefinitionKey()) |
| | | || returnVendor.getProcessInstanceId() == null) { |
| | | throw exception(WM_RETURN_VENDOR_APPROVAL_REQUIRED, returnVendor.getCode()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 把退货数量增量回写到关联采购订单的明细行,并同步订单级退货数量 |
| | | */ |
| | |
| | | return; |
| | | } |
| | | Map<Long, Long> purchaseOrderItemIdMap = new HashMap<>(); |
| | | Set<Long> ambiguousProductIds = new HashSet<>(); |
| | | orderItems.stream() |
| | | .filter(item -> item.getProductId() != null) |
| | | .forEach(item -> purchaseOrderItemIdMap.putIfAbsent(item.getProductId(), item.getId())); |
| | | .forEach(item -> { |
| | | if (purchaseOrderItemIdMap.putIfAbsent(item.getProductId(), item.getId()) != null) { |
| | | ambiguousProductIds.add(item.getProductId()); |
| | | } |
| | | }); |
| | | for (MesWmReturnVendorLineDO line : lines) { |
| | | if (ambiguousProductIds.contains(line.getItemId())) { |
| | | throw exception(WM_RETURN_VENDOR_PURCHASE_ITEM_AMBIGUOUS, line.getItemId()); |
| | | } |
| | | Long purchaseOrderItemId = purchaseOrderItemIdMap.get(line.getItemId()); |
| | | if (purchaseOrderItemId == null || line.getQuantity() == null) { |
| | | continue; |
| | | } |
| | | purchaseOrderApi.updateOrderItemReturnCount(purchaseOrderItemId, line.getQuantity()); |
| | | if (!Boolean.TRUE.equals(purchaseOrderApi.updateOrderItemReturnCount(purchaseOrderItemId, line.getQuantity()).getCheckedData())) { |
| | | throw exception(WM_RETURN_VENDOR_ITEM_NOT_IN_PURCHASE_ORDER); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void validateReturnVendorOrderQuantity(MesWmReturnVendorDO returnVendor) { |
| | | if (returnVendor.getPurchaseOrderId() == null) { |
| | | return; |
| | | } |
| | | List<ErpPurchaseOrderItemRespDTO> items = purchaseOrderApi |
| | | .getPurchaseOrderItemList(returnVendor.getPurchaseOrderId()).getCheckedData(); |
| | | Map<Long, ErpPurchaseOrderItemRespDTO> byProduct = new HashMap<>(); |
| | | Set<Long> ambiguousProductIds = new HashSet<>(); |
| | | for (ErpPurchaseOrderItemRespDTO item : items) { |
| | | if (item.getProductId() != null && byProduct.putIfAbsent(item.getProductId(), item) != null) { |
| | | ambiguousProductIds.add(item.getProductId()); |
| | | } |
| | | } |
| | | Map<Long, BigDecimal> requested = new HashMap<>(); |
| | | for (MesWmReturnVendorLineDO line : returnVendorLineService.getReturnVendorLineListByReturnId(returnVendor.getId())) { |
| | | requested.merge(line.getItemId(), line.getQuantity(), BigDecimal::add); |
| | | } |
| | | for (Map.Entry<Long, BigDecimal> entry : requested.entrySet()) { |
| | | if (ambiguousProductIds.contains(entry.getKey())) { |
| | | throw exception(WM_RETURN_VENDOR_PURCHASE_ITEM_AMBIGUOUS, entry.getKey()); |
| | | } |
| | | ErpPurchaseOrderItemRespDTO item = byProduct.get(entry.getKey()); |
| | | if (item == null) { |
| | | throw exception(WM_RETURN_VENDOR_ITEM_NOT_IN_PURCHASE_ORDER); |
| | | } |
| | | BigDecimal available = (item.getInCount() == null ? BigDecimal.ZERO : item.getInCount()) |
| | | .subtract(item.getReturnCount() == null ? BigDecimal.ZERO : item.getReturnCount()); |
| | | if (entry.getValue().compareTo(available) > 0) { |
| | | throw exception(WM_RETURN_VENDOR_QUANTITY_EXCEED, entry.getKey(), entry.getValue(), available); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | throw exception(WM_RETURN_VENDOR_CANCEL_NOT_ALLOWED); |
| | | } |
| | | |
| | | // 审批中先撤销 BPM 实例,撤销失败时保留单据原状态和待办。 |
| | | if (MesWmReturnVendorStatusEnum.BPM_APPROVING.getStatus().equals(returnVendor.getStatus())) { |
| | | if (StrUtil.isBlank(returnVendor.getProcessInstanceId())) { |
| | | throw exception(WM_RETURN_VENDOR_APPROVAL_REQUIRED, returnVendor.getCode()); |
| | | } |
| | | processInstanceApi.cancelProcessInstanceByStartUser(getCurrentUserId(), |
| | | returnVendor.getProcessInstanceId(), "供应商退货单取消"); |
| | | } |
| | | |
| | | // 取消 |
| | | returnVendorMapper.updateById(new MesWmReturnVendorDO() |
| | | .setId(id).setStatus(MesWmReturnVendorStatusEnum.CANCELED.getStatus())); |