| | |
| | | package cn.iocoder.yudao.module.mes.service.wm.productsales; |
| | | |
| | | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.hutool.core.util.ObjUtil; |
| | |
| | | @Resource |
| | | @Lazy |
| | | private MesQcOqcService oqcService; |
| | | @Resource |
| | | private ErpSaleOrderApi saleOrderApi; |
| | | |
| | | @Override |
| | | public Long createProductSalesLine(MesWmProductSalesLineSaveReqVO createReqVO) { |
| | |
| | | if (noticeLine != null) { |
| | | line.setSaleOrderItemId(noticeLine.getSaleOrderItemId()); |
| | | } |
| | | fillProductSalesLinePrice(line); |
| | | productSalesLineMapper.insert(line); |
| | | return line.getId(); |
| | | } |
| | | |
| | | /** |
| | | * 按销售订单行单价回填出库行金额,供开票/对账使用 |
| | | */ |
| | | private void fillProductSalesLinePrice(MesWmProductSalesLineDO line) { |
| | | if (line.getSaleOrderItemId() == null) { |
| | | return; |
| | | } |
| | | try { |
| | | var item = saleOrderApi.getSaleOrderItem(line.getSaleOrderItemId()).getData(); |
| | | if (item == null || item.getProductPrice() == null) { |
| | | return; |
| | | } |
| | | line.setPrice(item.getProductPrice()); |
| | | if (line.getQuantity() != null) { |
| | | line.setTotalPrice(item.getProductPrice().multiply(line.getQuantity())); |
| | | } |
| | | } catch (Exception ex) { |
| | | // 价格只是快照,订单行取不到时不阻断开单 |
| | | log.warn("[fillProductSalesLinePrice][销售订单行({}) 价格快照失败]", line.getSaleOrderItemId(), ex); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | if (noticeLine != null) { |
| | | updateObj.setSaleOrderItemId(noticeLine.getSaleOrderItemId()); |
| | | } |
| | | fillProductSalesLinePrice(updateObj); |
| | | productSalesLineMapper.updateById(updateObj); |
| | | |
| | | // 检查是否需要创建 OQC 检验单:从不需要检验变为需要检验 |
| | | if (Boolean.TRUE.equals(updateReqVO.getOqcCheckFlag()) |
| | | && !Boolean.TRUE.equals(oldLine.getOqcCheckFlag())) { |
| | | // 获取出库单信息 |
| | | MesWmProductSalesDO sales = productSalesService.getProductSales(updateReqVO.getSalesId()); |
| | | if (sales == null) { |
| | | log.warn("[updateProductSalesLine] 出库单不存在,salesId={}", updateReqVO.getSalesId()); |
| | | return; |
| | | } |
| | | |
| | | // 构建出库明细 DO |
| | | MesWmProductSalesLineDO line = new MesWmProductSalesLineDO(); |
| | | line.setId(updateReqVO.getId()); |
| | | line.setItemId(updateReqVO.getItemId()); |
| | | line.setSaleOrderItemId(updateObj.getSaleOrderItemId() != null |
| | | ? updateObj.getSaleOrderItemId() : oldLine.getSaleOrderItemId()); |
| | | line.setBatchCode(updateReqVO.getBatchCode()); |
| | | line.setQuantity(updateReqVO.getQuantity()); |
| | | |
| | | // 创建 OQC 检验单,让异常抛出 |
| | | Long oqcId = oqcService.createOqcFromProductSales( |
| | | sales.getId(), line, sales.getCode(), sales.getClientId()); |
| | | |
| | | // 更新出库明细的 oqcId 和质量状态 |
| | | productSalesLineMapper.updateById(new MesWmProductSalesLineDO() |
| | | .setId(updateReqVO.getId()) |
| | | .setOqcId(oqcId) |
| | | .setQualityStatus(MesWmQualityStatusEnum.PENDING.getStatus())); |
| | | } |
| | | // 提交出库单后由质检人员选择已完成的出厂检验单。 |
| | | } |
| | | |
| | | @Override |
| | |
| | | mdmItemService.validateItemExistsAndEnable(reqVO.getItemId()); |
| | | // 校验关联发货通知单行(存在性 + 归属 + 字段一致性) |
| | | MesWmSalesNoticeLineDO noticeLine = validateSalesNoticeLine(sales, reqVO); |
| | | if (noticeLine == null && sales.getSaleOrderId() != null) { |
| | | if (reqVO.getSaleOrderItemId() == null) { |
| | | throw exception(WM_SALES_NOTICE_SALE_ORDER_ITEM_EMPTY); |
| | | } |
| | | var item = saleOrderApi.getSaleOrderItem(reqVO.getSaleOrderItemId()).getCheckedData(); |
| | | if (item == null || !Objects.equals(item.getOrderId(), sales.getSaleOrderId()) |
| | | || !Objects.equals(item.getProductId(), reqVO.getItemId())) { |
| | | throw exception(WM_SALES_NOTICE_LINE_SALE_ITEM_MISMATCH); |
| | | } |
| | | } |
| | | // 根据 batchCode 解析 batchId |
| | | fillBatchId(reqVO); |
| | | return noticeLine; |
| | |
| | | Long salesId = currentLine.getSalesId(); |
| | | List<MesWmProductSalesLineDO> allLines = productSalesLineMapper.selectListBySalesId(salesId); |
| | | |
| | | // 2.1 检查是否有不合格行 → 取消出库单 |
| | | // 不合格行留待后续处置,不能因为检验结果自动取消整张出库单。 |
| | | boolean hasNg = CollectionUtils.anyMatch(allLines, |
| | | line -> Boolean.TRUE.equals(line.getOqcCheckFlag()) |
| | | && Objects.equals(line.getQualityStatus(), MesWmQualityStatusEnum.FAIL.getStatus())); |
| | | if (hasNg) { |
| | | log.info("[updateProductSalesLineWhenOqcFinish][销售出库单({}) 存在质检不合格行,取消出库单]", salesId); |
| | | productSalesService.cancelProductSales(salesId); |
| | | log.info("[updateProductSalesLineWhenOqcFinish][销售出库单({}) 存在质检不合格行,等待后续处置]", salesId); |
| | | return; |
| | | } |
| | | |
| | | // 2.2 检查是否所有 OQC 行都已完成,自动流转 CONFIRMED → APPROVING |
| | | boolean hasUnchecked = CollectionUtils.anyMatch(allLines, |
| | | line -> Boolean.TRUE.equals(line.getOqcCheckFlag()) && line.getOqcId() == null); |
| | | line -> Boolean.TRUE.equals(line.getOqcCheckFlag()) && !Objects.equals(line.getQualityStatus(), MesWmQualityStatusEnum.PASS.getStatus())); |
| | | if (!hasUnchecked) { |
| | | log.info("[updateProductSalesLineWhenOqcFinish][销售出库单({}) 所有 OQC 行检验完成,流转到待拣货]", salesId); |
| | | productSalesService.confirmProductSales(salesId); |