| | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; |
| | | import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; |
| | | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; |
| | | import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService; |
| | | import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.salesnotice.vo.MesWmSalesNoticePageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.salesnotice.vo.MesWmSalesNoticeSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO; |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.salesnotice.MesWmSalesNoticeMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.salesnotice.MesWmSalesNoticeLineMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmOutStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmSalesNoticeStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService; |
| | | import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi; |
| | |
| | | import cn.iocoder.yudao.module.erp.api.sale.enums.ErpSaleOrderOutStatusEnum; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.flowable.engine.repository.ProcessDefinition; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; |
| | |
| | | @Validated |
| | | @Slf4j |
| | | public class MesWmSalesNoticeServiceImpl implements MesWmSalesNoticeService { |
| | | |
| | | /** |
| | | * BPM 发货审批分类编码(审批人默认为物流) |
| | | */ |
| | | private static final String SALES_DELIVERY_APPROVE_CATEGORY_CODE = "sales_delivery_approve"; |
| | | |
| | | @Resource |
| | | private MesWmSalesNoticeMapper salesNoticeMapper; |
| | |
| | | @Resource |
| | | private MesWmSalesNoticeLineMapper salesNoticeLineMapper; |
| | | |
| | | @Resource |
| | | private BpmProcessInstanceApi bpmProcessInstanceApi; |
| | | @Resource |
| | | private BpmCategoryService bpmCategoryService; |
| | | @Resource |
| | | private BpmProcessDefinitionService bpmProcessDefinitionService; |
| | | |
| | | @Override |
| | | public Long createSalesNotice(MesWmSalesNoticeSaveReqVO createReqVO) { |
| | | // 校验数据 |
| | |
| | | // 插入 |
| | | MesWmSalesNoticeDO notice = BeanUtils.toBean(createReqVO, MesWmSalesNoticeDO.class); |
| | | notice.setStatus(PREPARE.getStatus()); // 草稿状态 |
| | | notice.setExpressFee(notice.getExpressFee() == null ? BigDecimal.ZERO : notice.getExpressFee()); |
| | | notice.setInvoiceStatus(notice.getInvoiceStatus() == null ? 0 : notice.getInvoiceStatus()); |
| | | salesNoticeMapper.insert(notice); |
| | | return notice.getId(); |
| | | } |
| | |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void submitSalesNotice(Long id) { |
| | | public void submitSalesNotice(Long id, String processDefinitionKey, Long userId) { |
| | | // 1.1 校验存在 + 草稿状态 |
| | | MesWmSalesNoticeDO notice = validateSalesNoticeExistsAndDraft(id); |
| | | // 1.2 检查是否有行项目 |
| | |
| | | throw exception(WM_SALES_NOTICE_LINE_EMPTY); |
| | | } |
| | | |
| | | // 2. 更新状态:草稿 → 待出库 |
| | | salesNoticeMapper.updateById(new MesWmSalesNoticeDO() |
| | | .setId(id).setStatus(APPROVED.getStatus())); |
| | | // 2. 全局免审批:直接进入待出库 |
| | | if (bpmProcessInstanceApi.isGlobalAutoApprove()) { |
| | | salesNoticeMapper.updateById(new MesWmSalesNoticeDO() |
| | | .setId(id).setProcessInstanceId("").setStatus(APPROVED.getStatus())); |
| | | reserveSaleOrderOutStatus(notice); |
| | | return; |
| | | } |
| | | |
| | | // 3. 回写 ERP 销售订单出库状态为"已预留" |
| | | // 3. 必须选择流程,且分类下存在激活的流程定义 |
| | | if (StrUtil.isBlank(processDefinitionKey)) { |
| | | throw exception(WM_SALES_NOTICE_SUBMIT_PROCESS_KEY_REQUIRED); |
| | | } |
| | | validateSalesNoticeApproveCategoryAndProcessDefinition(); |
| | | ProcessDefinition processDefinition = bpmProcessDefinitionService.getActiveProcessDefinition(processDefinitionKey); |
| | | if (processDefinition == null) { |
| | | throw exception(WM_SALES_NOTICE_BPM_PROCESS_DEFINITION_NOT_EXISTS); |
| | | } |
| | | |
| | | // 4. 创建审批流程实例,进入审批中 |
| | | String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO() |
| | | .setProcessDefinitionKey(processDefinitionKey).setBusinessKey(String.valueOf(id))); |
| | | salesNoticeMapper.updateById(new MesWmSalesNoticeDO() |
| | | .setId(id).setProcessInstanceId(processInstanceId).setStatus(APPROVING.getStatus())); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateSalesNoticeAuditStatus(Long id, Integer bpmResult) { |
| | | MesWmSalesNoticeDO notice = validateSalesNoticeExists(id); |
| | | if (ObjUtil.notEqual(APPROVING.getStatus(), notice.getStatus())) { |
| | | MesWmSalesNoticeStatusEnum current = MesWmSalesNoticeStatusEnum.getByStatus(notice.getStatus()); |
| | | log.error("[updateSalesNoticeAuditStatus][发货单({}) 不处于审批中,当前状态({}),无法更新审批结果({})]", |
| | | id, current != null ? current.getName() : notice.getStatus(), bpmResult); |
| | | throw exception(WM_SALES_NOTICE_UPDATE_AUDIT_STATUS_FAIL_NOT_APPROVING, notice.getCode(), |
| | | current != null ? current.getName() : String.valueOf(notice.getStatus())); |
| | | } |
| | | if (ObjUtil.equal(bpmResult, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { |
| | | // 审批通过:进入待出库,并回写销售订单出库状态 |
| | | salesNoticeMapper.updateById(new MesWmSalesNoticeDO().setId(id).setStatus(APPROVED.getStatus())); |
| | | reserveSaleOrderOutStatus(notice); |
| | | } else if (ObjUtil.equal(bpmResult, BpmProcessInstanceStatusEnum.REJECT.getStatus()) |
| | | || ObjUtil.equal(bpmResult, BpmProcessInstanceStatusEnum.CANCEL.getStatus())) { |
| | | // 驳回/取消:退回草稿,允许修改后重新提交 |
| | | salesNoticeMapper.updateById(new MesWmSalesNoticeDO().setId(id).setStatus(PREPARE.getStatus())); |
| | | } else { |
| | | log.warn("[updateSalesNoticeAuditStatus][发货单({}) 收到未知审批结果({}),忽略]", id, bpmResult); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public List<Map<String, Object>> getSalesNoticeApproveProcessDefinitionList() { |
| | | validateSalesNoticeApproveCategoryAndProcessDefinition(); |
| | | List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService |
| | | .getProcessDefinitionInfoListByCategory(SALES_DELIVERY_APPROVE_CATEGORY_CODE); |
| | | Set<String> processDefinitionIds = cn.iocoder.yudao.framework.common.util.collection.CollectionUtils |
| | | .convertSet(definitionInfoList, BpmProcessDefinitionInfoDO::getProcessDefinitionId); |
| | | List<ProcessDefinition> processDefinitions = bpmProcessDefinitionService.getProcessDefinitionList(processDefinitionIds); |
| | | Map<String, ProcessDefinition> latestVersionMap = new HashMap<>(); |
| | | for (ProcessDefinition pd : processDefinitions) { |
| | | if (pd.isSuspended()) { |
| | | continue; |
| | | } |
| | | ProcessDefinition existing = latestVersionMap.get(pd.getKey()); |
| | | if (existing == null || pd.getVersion() > existing.getVersion()) { |
| | | latestVersionMap.put(pd.getKey(), pd); |
| | | } |
| | | } |
| | | List<Map<String, Object>> result = new ArrayList<>(); |
| | | for (ProcessDefinition pd : latestVersionMap.values()) { |
| | | Map<String, Object> item = new HashMap<>(); |
| | | item.put("id", pd.getId()); |
| | | item.put("key", pd.getKey()); |
| | | item.put("name", pd.getName()); |
| | | item.put("version", pd.getVersion()); |
| | | result.add(item); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private void reserveSaleOrderOutStatus(MesWmSalesNoticeDO notice) { |
| | | if (notice.getSaleOrderId() != null) { |
| | | saleOrderApi.updateOrderOutStatusTo(notice.getSaleOrderId(), |
| | | ErpSaleOrderOutStatusEnum.RESERVED.getStatus()); |
| | | } |
| | | } |
| | | |
| | | private void validateSalesNoticeApproveCategoryAndProcessDefinition() { |
| | | List<String> codes = Collections.singletonList(SALES_DELIVERY_APPROVE_CATEGORY_CODE); |
| | | Map<String, ?> categoryMap = bpmCategoryService.getCategoryMap(codes); |
| | | if (!categoryMap.containsKey(SALES_DELIVERY_APPROVE_CATEGORY_CODE)) { |
| | | throw exception(WM_SALES_NOTICE_BPM_PROCESS_DEFINITION_NOT_EXISTS); |
| | | } |
| | | List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService |
| | | .getProcessDefinitionInfoListByCategory(SALES_DELIVERY_APPROVE_CATEGORY_CODE); |
| | | if (CollUtil.isEmpty(definitionInfoList)) { |
| | | throw exception(WM_SALES_NOTICE_BPM_PROCESS_DEFINITION_NOT_EXISTS); |
| | | } |
| | | Set<String> processDefinitionIds = cn.iocoder.yudao.framework.common.util.collection.CollectionUtils |
| | | .convertSet(definitionInfoList, BpmProcessDefinitionInfoDO::getProcessDefinitionId); |
| | | List<ProcessDefinition> processDefinitions = bpmProcessDefinitionService.getProcessDefinitionList(processDefinitionIds); |
| | | if (processDefinitions.stream().noneMatch(pd -> !pd.isSuspended())) { |
| | | throw exception(WM_SALES_NOTICE_BPM_PROCESS_DEFINITION_NOT_EXISTS); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public List<MesWmSalesNoticeDO> getSalesNoticeList(Collection<Long> ids) { |
| | | if (CollUtil.isEmpty(ids)) { |