云
6 天以前 1d9cfc91e7255b9e139b20ff230cb19246f19193
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java
@@ -28,6 +28,7 @@
import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderMapper;
import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;
import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
import cn.iocoder.yudao.module.erp.enums.ErpSaleTypeEnum;
import cn.iocoder.yudao.module.erp.api.sale.enums.ErpSaleOrderOutStatusEnum;
import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
@@ -127,6 +128,8 @@
        List<ErpSaleOrderItemDO> saleOrderItems = validateSaleOrderItems(createReqVO.getItems());
        // 1.2 校验客户(使用 CRM 客户 API)
        customerApi.validateCustomer(createReqVO.getCustomerId());
        // 1.2.1 校验销售类型
        validateSaleType(createReqVO.getSaleType());
        // 1.3 校验结算账户
        if (createReqVO.getAccountId() != null) {
            accountService.validateAccount(createReqVO.getAccountId());
@@ -136,10 +139,8 @@
            adminUserApi.validateUser(createReqVO.getSaleUserId());
        }
        // 1.5 生成订单号,并校验唯一性
        String no = noRedisDAO.generate(ErpNoRedisDAO.SALE_ORDER_NO_PREFIX);
        if (saleOrderMapper.selectByNo(no) != null) {
            throw exception(SALE_ORDER_NO_EXISTS);
        }
        String no = noRedisDAO.generate(ErpNoRedisDAO.SALE_ORDER_NO_PREFIX,
                candidate -> saleOrderMapper.selectByNo(candidate) != null);
        // 2.1 插入订单(创建后为未提交状态,需手动提交审批到协同办公)
        ErpSaleOrderDO saleOrder = BeanUtils.toBean(createReqVO, ErpSaleOrderDO.class, in -> in
@@ -169,6 +170,8 @@
        }
        // 1.3 校验客户(使用 CRM 客户 API)
        customerApi.validateCustomer(updateReqVO.getCustomerId());
        // 1.3.1 校验销售类型
        validateSaleType(updateReqVO.getSaleType());
        // 1.3 校验结算账户
        if (updateReqVO.getAccountId() != null) {
            accountService.validateAccount(updateReqVO.getAccountId());
@@ -285,6 +288,10 @@
            }
            ErpSaleOrderSaveReqVO.Item itemVO = new ErpSaleOrderSaveReqVO.Item();
            itemVO.setProductId(item.getId());
            if (row.getReelNo() != null && row.getReelNo().length() > 128) {
                throw new IllegalArgumentException("产品[" + row.getProductCode() + "]的盘号不能超过128个字符");
            }
            itemVO.setReelNo(row.getReelNo());
            itemVO.setProductUnitId(item.getUnitMeasureId());
            // 单价:未填写时取物料的销售价(与页面选产品后的默认值一致)
            itemVO.setProductPrice(row.getProductPrice() != null ? row.getProductPrice()
@@ -297,10 +304,15 @@
        }
        // 3. 单号:填了就用(校验唯一),没填自动生成
        String no = StrUtil.isNotBlank(head.getNo()) ? head.getNo().trim()
                : noRedisDAO.generate(ErpNoRedisDAO.SALE_ORDER_NO_PREFIX);
        if (saleOrderMapper.selectByNo(no) != null) {
            throw new IllegalArgumentException("销售单编号已存在:" + no);
        String no;
        if (StrUtil.isNotBlank(head.getNo())) {
            no = head.getNo().trim();
            if (saleOrderMapper.selectByNo(no) != null) {
                throw new IllegalArgumentException("销售单编号已存在:" + no);
            }
        } else {
            no = noRedisDAO.generate(ErpNoRedisDAO.SALE_ORDER_NO_PREFIX,
                    candidate -> saleOrderMapper.selectByNo(candidate) != null);
        }
        // 4. 组装主表:导入后固定为“未提交(0)”状态
@@ -321,6 +333,12 @@
        itemDOs.forEach(item -> item.setOrderId(saleOrder.getId()));
        saleOrderItemMapper.insertBatch(itemDOs);
        return saleOrder;
    }
    private void validateSaleType(Integer saleType) {
        if (!ErpSaleTypeEnum.isValid(saleType)) {
            throw exception(SALE_ORDER_SALE_TYPE_INVALID, saleType);
        }
    }
    /**
@@ -400,7 +418,24 @@
            throw exception(SALE_ORDER_SUBMIT_FAIL_ITEM_EMPTY, saleOrder.getNo());
        }
        // 4. 创建 BPM 流程实例(审批在协同办公完成)
        // 4. 合同转来的销售订单(contractId 非空)无需再走审批:
        //    合同在 CRM 已审批通过,订单内容由合同带入,提交即生效。
        //    生效后:需要生产的明细自动创建 MPS 流转生产模块;无需生产的明细可直接在仓储生成发货通知单。
        //    历史合同生成的订单明细可能未维护“是否需要生产”,按系统默认(需要生产)补齐后再生效。
        if (saleOrder.getContractId() != null) {
            items.stream()
                    .filter(item -> item.getNeedProduction() == null)
                    .forEach(item -> saleOrderItemMapper.updateById(
                            new ErpSaleOrderItemDO().setId(item.getId()).setNeedProduction(1)));
            saleOrderMapper.updateById(new ErpSaleOrderDO()
                    .setId(id)
                    .setStatus(ErpAuditStatus.APPROVING.getStatus())
                    .setProcessInstanceId(""));
            updateSaleOrderAuditStatus(id, cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum.APPROVE.getStatus());
            return;
        }
        // 5. 创建 BPM 流程实例(审批在协同办公完成)
        // 全局免审批:业务校验通过后直接执行审批通过逻辑,无需流程模型。
        if (processInstanceApi.isGlobalAutoApprove()) {
            saleOrderMapper.updateById(new ErpSaleOrderDO()
@@ -416,7 +451,7 @@
                        .setProcessDefinitionKey(processDefinitionKey)
                        .setBusinessKey(String.valueOf(id)));
        // 5. 更新销售订单状态为审批中,记录流程实例编号
        // 6. 更新销售订单状态为审批中,记录流程实例编号
        saleOrderMapper.updateById(new ErpSaleOrderDO()
                .setId(id)
                .setStatus(ErpAuditStatus.APPROVING.getStatus())
@@ -568,6 +603,7 @@
        createReqDTO.setSaleOrderId(saleOrder.getId());
        createReqDTO.setSaleOrderNo(saleOrder.getNo());
        createReqDTO.setSaleOrderItemId(item.getId());
        createReqDTO.setReelNo(item.getReelNo());
        createReqDTO.setMdmItemId(item.getProductId());
        createReqDTO.setProductName(productName);
        createReqDTO.setQuantity(item.getCount());
@@ -743,8 +779,7 @@
    @Override
    public PageResult<ErpSaleOrderDO> getSaleOrderPage(ErpSaleOrderPageReqVO pageReqVO) {
        List<Long> permittedCustomerIds = customerApi.getPermittedCustomerIds();
        return saleOrderMapper.selectPage(pageReqVO, permittedCustomerIds);
        return saleOrderMapper.selectPage(pageReqVO);
    }
    // ==================== 订单项 ====================