| | |
| | | import cn.iocoder.yudao.module.aftersales.dal.redis.no.AfterSaleNoRedisDAO; |
| | | import cn.iocoder.yudao.module.aftersales.service.returnobj.AfterSaleReturnService; |
| | | import cn.iocoder.yudao.module.crm.api.contract.CrmContractApi; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.dao.DuplicateKeyException; |
| | |
| | | import java.util.Objects; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; |
| | | import static cn.iocoder.yudao.module.aftersales.enums.ErrorCodeConstants.*; |
| | | |
| | | /** |
| | |
| | | @Resource private AfterSaleNoRedisDAO noRedisDAO; |
| | | @Resource private AfterSaleReturnService returnService; |
| | | @Resource private CrmContractApi crmContractApi; |
| | | @Resource private ErpSaleOrderApi erpSaleOrderApi; |
| | | |
| | | public PageResult<AfterSaleCancellationDO> page(PageParam page, String code, String type, |
| | | Integer status, Long customerId) { |
| | |
| | | } |
| | | |
| | | /** |
| | | * 审批:通过后执行下游动作;驳回必须填意见。 |
| | | * 审批:驳回必须填意见;通过时先执行下游动作,全部成功才落审批结论。 |
| | | * |
| | | * <p>下游失败(如销售订单仍有关联生产计划)会整体回滚,受理单保持「待审批」, |
| | | * 由调用方看到具体原因,避免出现「审批通过了但业务没执行」的不一致状态。 |
| | | * |
| | | * @return 通过时生成的退货单编号(非退货类型为 null) |
| | | */ |
| | |
| | | AfterSaleCancellationDO task = get(req.getId()); |
| | | if (!Objects.equals(task.getStatus(), ST_PENDING)) throw exception(CANCELLATION_APPROVE_FAIL_STATUS); |
| | | |
| | | int target = Boolean.TRUE.equals(req.getApproved()) ? ST_APPROVED : ST_REJECTED; |
| | | // 驳回:只改状态,不执行下游 |
| | | if (!Boolean.TRUE.equals(req.getApproved())) { |
| | | if (mapper.update(null, new LambdaUpdateWrapper<AfterSaleCancellationDO>() |
| | | .eq(AfterSaleCancellationDO::getId, task.getId()) |
| | | .eq(AfterSaleCancellationDO::getStatus, ST_PENDING) |
| | | .set(AfterSaleCancellationDO::getStatus, ST_REJECTED) |
| | | .set(AfterSaleCancellationDO::getApproverId, userId) |
| | | .set(AfterSaleCancellationDO::getApprovalTime, LocalDateTime.now()) |
| | | .set(AfterSaleCancellationDO::getApprovalRemark, req.getRemark())) != 1) { |
| | | throw exception(CANCELLATION_APPROVE_FAIL_STATUS); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | // 通过:先执行下游动作(异常直接抛出,事务回滚,单据仍为待审批) |
| | | Long returnId = executeDownstream(task, userId); |
| | | |
| | | // 下游成功后再落审批结论 |
| | | if (mapper.update(null, new LambdaUpdateWrapper<AfterSaleCancellationDO>() |
| | | .eq(AfterSaleCancellationDO::getId, task.getId()) |
| | | .eq(AfterSaleCancellationDO::getStatus, ST_PENDING) |
| | | .set(AfterSaleCancellationDO::getStatus, target) |
| | | .set(AfterSaleCancellationDO::getStatus, ST_APPROVED) |
| | | .set(AfterSaleCancellationDO::getApproverId, userId) |
| | | .set(AfterSaleCancellationDO::getApprovalTime, LocalDateTime.now()) |
| | | .set(AfterSaleCancellationDO::getApprovalRemark, req.getRemark())) != 1) { |
| | | .set(AfterSaleCancellationDO::getApprovalRemark, req.getRemark()) |
| | | .set(AfterSaleCancellationDO::getExecuted, 1)) != 1) { |
| | | throw exception(CANCELLATION_APPROVE_FAIL_STATUS); |
| | | } |
| | | |
| | | if (!Boolean.TRUE.equals(req.getApproved())) return null; |
| | | return executeDownstream(task, userId); |
| | | return returnId; |
| | | } |
| | | |
| | | /** 审批通过后的下游动作;失败时记录原因但不回滚审批结论,便于人工补做。 */ |
| | | /** |
| | | * 审批通过后的下游动作。 |
| | | * |
| | | * <p>不吞异常:任一步失败直接抛出,由 {@link #approve} 的事务整体回滚, |
| | | * 保证「审批通过」与「下游已执行」永远一致。 |
| | | */ |
| | | private Long executeDownstream(AfterSaleCancellationDO task, Long userId) { |
| | | Long returnId = null; |
| | | String remark; |
| | | boolean ok = true; |
| | | try { |
| | | { |
| | | switch (task.getType()) { |
| | | case "RETURN" -> { |
| | | var vo = new AfterSaleReturnSaveReqVO(); |
| | |
| | | vo.setReturnType(task.getReturnType() == null ? 1 : task.getReturnType()); |
| | | vo.setReturnReason(task.getReason()); |
| | | returnId = returnService.createReturn(vo, userId); |
| | | remark = "已生成退货单 " + returnId; |
| | | if (task.getSaleOrderId() != null) { |
| | | erpSaleOrderApi.cancelSaleOrder(task.getSaleOrderId()).getCheckedData(); |
| | | remark = "已生成退货单 " + returnId |
| | | + ",并作废销售订单 " + task.getSaleOrderId(); |
| | | } else { |
| | | remark = "已生成退货单 " + returnId; |
| | | } |
| | | } |
| | | case "CONTRACT_TERMINATE" -> { |
| | | crmContractApi.updateContractStatus(task.getContractId(), CRM_CONTRACT_INVALID).getCheckedData(); |
| | | remark = "已作废 CRM 合同 " + task.getContractId(); |
| | | // 若关联 ERP 销售订单,一并作废(会被未取消的 MPS 计划拦截) |
| | | if (task.getSaleOrderId() != null) { |
| | | erpSaleOrderApi.cancelSaleOrder(task.getSaleOrderId()).getCheckedData(); |
| | | remark = "已作废 CRM 合同 " + task.getContractId() |
| | | + ",并作废销售订单 " + task.getSaleOrderId(); |
| | | } else { |
| | | remark = "已作废 CRM 合同 " + task.getContractId(); |
| | | } |
| | | } |
| | | case "CONTRACT_CHANGE" -> remark = "变更受理已通过;CRM 合同内容请按附件同步维护"; |
| | | default -> { ok = false; remark = "未知受理类型:" + task.getType(); } |
| | | default -> throw invalidParamException("未知受理类型:" + task.getType()); |
| | | } |
| | | } catch (Exception e) { |
| | | ok = false; |
| | | remark = "下游执行失败:" + e.getMessage(); |
| | | log.warn("退单受理[{}]下游动作失败:{}", task.getCode(), e.getMessage()); |
| | | } |
| | | var update = new AfterSaleCancellationDO(); |
| | | update.setId(task.getId()); |
| | | update.setExecuted(ok ? 1 : 0); |
| | | update.setExecuteRemark(remark); |
| | | if (returnId != null) update.setReturnId(returnId); |
| | | mapper.updateById(update); |