Commit 7a473282e56875927771e552fd18b615d4fcd2a1

Authored by 房远帅
1 parent 7e503a63

采购计划:正常变更审核通过后数据逻辑处理

... ... @@ -1936,6 +1936,7 @@ CREATE TABLE `procurement_plan`
1936 1936 `attachment_file_names` text DEFAULT NULL COMMENT '附件文件名称集合',
1937 1937 `change_status` varchar(50) DEFAULT NULL COMMENT '变更状态 CHANGING 变更中 CHANGED 变更完成',
1938 1938 `replenishment_status` varchar(50) DEFAULT NULL COMMENT '补单状态 REPLENISHING 补单中 REPLENISHED 补单完成',
  1939 + `deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除 0否 1是',
1939 1940 `create_by_id` varchar(32) DEFAULT NULL COMMENT '创建人ID',
1940 1941 `create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
1941 1942 `update_by_id` varchar(32) DEFAULT NULL COMMENT '更新人ID',
... ...
... ... @@ -394,6 +394,12 @@ public class GetProcurementPlanBo extends BaseBo<ProcurementPlan> {
394 394 private String attachmentFileNames;
395 395
396 396 /**
  397 + * 删除标记 true=已删除(变更归档原始数据)false=正常数据
  398 + */
  399 + @ApiModelProperty("删除标记")
  400 + private Boolean deleted;
  401 +
  402 + /**
397 403 * 创建人ID
398 404 */
399 405 @ApiModelProperty("创建人ID")
... ...
... ... @@ -325,6 +325,11 @@ public class ProcurementPlan extends BaseEntity implements BaseDto {
325 325 private String attachmentFileNames;
326 326
327 327 /**
  328 + * 删除标记 true=已删除(变更归档原始数据)false=正常数据
  329 + */
  330 + private Boolean deleted;
  331 +
  332 + /**
328 333 * 是否展示审核按钮(非持久化字段)
329 334 */
330 335 @TableField(exist = false)
... ...
... ... @@ -21,6 +21,8 @@ import com.lframework.starter.web.core.utils.*;
21 21 import com.lframework.starter.web.inner.components.oplog.OtherOpLogType;
22 22 import com.lframework.starter.web.inner.entity.SysRole;
23 23 import com.lframework.starter.web.inner.service.system.SysRoleService;
  24 +import com.lframework.xingyun.basedata.entity.Workshop;
  25 +import com.lframework.xingyun.basedata.service.workshop.WorkshopService;
24 26 import com.lframework.xingyun.sc.bo.procurement.GetProcurementPlanChangeBo;
25 27 import com.lframework.xingyun.sc.bo.procurement.GetProcurementPlanChangeLineBo;
26 28 import com.lframework.xingyun.sc.procurement.entity.ProcurementPlan;
... ... @@ -40,6 +42,9 @@ import com.lframework.xingyun.sc.procurement.vo.change.CreateProcurementPlanChan
40 42 import com.lframework.xingyun.sc.procurement.vo.change.QueryProcurementPlanChangeVo;
41 43 import com.lframework.xingyun.sc.procurement.vo.change.UpdateProcurementPlanChangeLineVo;
42 44 import com.lframework.xingyun.sc.procurement.vo.change.UpdateProcurementPlanChangeVo;
  45 +import com.lframework.xingyun.sc.procurement.vo.plan.CreateProcurementPlanVo;
  46 +import com.lframework.xingyun.sc.procurement.vo.plan.ProcurementPlanLineVo;
  47 +import java.math.BigDecimal;
43 48 import java.util.*;
44 49 import java.util.stream.Collectors;
45 50 import javax.annotation.Resource;
... ... @@ -67,6 +72,8 @@ public class ProcurementPlanChangeServiceImpl extends
67 72 private ProcurementPlanLineService procurementPlanLineService;
68 73 @Resource
69 74 private ProcurementBuyoutApplyService procurementBuyoutApplyService;
  75 + @Resource
  76 + private WorkshopService workshopService;
70 77
71 78
72 79 private static final List<String> PROCESS_STATUSES = Arrays.asList(
... ... @@ -404,11 +411,173 @@ public class ProcurementPlanChangeServiceImpl extends
404 411 .eq(ProcurementPlanChange::getId, id);
405 412 getBaseMapper().update(updateWrapper);
406 413
407   - Wrapper<ProcurementPlan> parentWrapper = Wrappers.lambdaUpdate(ProcurementPlan.class)
  414 + // 变更审核通过流程
  415 + String planId = entity.getProcurementPlanId();
  416 + // 原主计划(不带deleted筛选,因为归档/已有的都要读)
  417 + ProcurementPlan mainPlan = procurementPlanService.getById(planId);
  418 + if (mainPlan == null) {
  419 + throw new DefaultClientException("原采购计划不存在!");
  420 + }
  421 +
  422 + // 1) 第一次变更时:将"原主计划+原始子计划"复制一份到 deleted=true(只保留一次,多次变更仅第一次归档)
  423 + int existDeletedCount = procurementPlanService.count(Wrappers.lambdaQuery(ProcurementPlan.class)
  424 + .eq(ProcurementPlan::getCode, mainPlan.getCode() + "_HIST")
  425 + .eq(ProcurementPlan::getDeleted, Boolean.TRUE));
  426 + if (existDeletedCount == 0L) {
  427 + // 归档主计划(ID新生成,deleted=true,保持原code以区分,加后缀_HIST避免唯一索引冲突)
  428 + ProcurementPlan archivedMain = BeanUtil.copyProperties(mainPlan, ProcurementPlan.class);
  429 + archivedMain.setId(IdUtil.getId());
  430 + archivedMain.setCode(mainPlan.getCode() + "_HIST");
  431 + archivedMain.setDeleted(Boolean.TRUE);
  432 + procurementPlanService.save(archivedMain);
  433 + // 原主计划子表也复制一份
  434 + List<ProcurementPlanLine> mainLines = procurementPlanLineService.getByProcurementPlanId(planId);
  435 + if (CollectionUtil.isNotEmpty(mainLines)) {
  436 + List<ProcurementPlanLine> archivedMainLines = new ArrayList<>();
  437 + for (ProcurementPlanLine line : mainLines) {
  438 + ProcurementPlanLine nLine = BeanUtil.copyProperties(line, ProcurementPlanLine.class);
  439 + nLine.setId(IdUtil.getId());
  440 + nLine.setProcurementPlanId(archivedMain.getId());
  441 + archivedMainLines.add(nLine);
  442 + }
  443 + procurementPlanLineService.saveBatch(archivedMainLines);
  444 + }
  445 + }
  446 + // 2) 冻结现有子计划(_01 _02)
  447 + List<ProcurementPlan> existSubList = procurementPlanService.listSubPlan(planId);
  448 + if (CollectionUtil.isNotEmpty(existSubList)) {
  449 + // 子计划批量设 frozen=true
  450 + List<String> existSubIds = existSubList.stream().map(ProcurementPlan::getId).collect(Collectors.toList());
  451 + procurementPlanService.update(Wrappers.lambdaUpdate(ProcurementPlan.class)
  452 + .set(ProcurementPlan::getFrozen, Boolean.TRUE)
  453 + .in(ProcurementPlan::getId, existSubIds));
  454 + }
  455 +
  456 + // 3) 同步变更内容到主计划(主计划本身字段+明细行)
  457 + // 3.1 主计划主表字段:从变更单拷贝(除id/code/procurementPlanId外)
  458 + ProcurementPlan updatedMain = new ProcurementPlan();
  459 + BeanUtil.copyProperties(entity, updatedMain, "id", "code", "procurementPlanId", "processStatus", "processNode",
  460 + "approvalStatus", "changeReason", "changeStatus", "planType", "deleted", "frozen",
  461 + "createById", "createBy", "createTime", "updateById", "updateBy", "updateTime");
  462 + // 重新计算明细合计数量
  463 + List<ProcurementPlanChangeLine> changeLines = procurementPlanChangeLineService.getByChangeId(id);
  464 + BigDecimal totalQuantity = BigDecimal.ZERO;
  465 + if (CollectionUtil.isNotEmpty(changeLines)) {
  466 + for (ProcurementPlanChangeLine cl : changeLines) {
  467 + if (cl.getQuantity() != null) {
  468 + totalQuantity = totalQuantity.add(cl.getQuantity());
  469 + }
  470 + }
  471 + }
  472 + updatedMain.setTotalQuantity(totalQuantity);
  473 + // 变更完成状态
  474 + updatedMain.setChangeStatus("CHANGED");
  475 + updatedMain.setProcessNode(null);
  476 + procurementPlanService.update(Wrappers.lambdaUpdate(ProcurementPlan.class)
  477 + .setEntity(updatedMain)
  478 + .eq(ProcurementPlan::getId, planId));
  479 +
  480 + // 3.2 主计划明细:先删后插(保留历史deleted=true的不管)
  481 + procurementPlanLineService.remove(Wrappers.lambdaQuery(ProcurementPlanLine.class)
  482 + .eq(ProcurementPlanLine::getProcurementPlanId, planId));
  483 + if (CollectionUtil.isNotEmpty(changeLines)) {
  484 + List<ProcurementPlanLine> newLines = new ArrayList<>();
  485 + for (ProcurementPlanChangeLine cl : changeLines) {
  486 + ProcurementPlanLine nl = BeanUtil.copyProperties(cl, ProcurementPlanLine.class);
  487 + nl.setId(IdUtil.getId());
  488 + nl.setProcurementPlanId(planId);
  489 + newLines.add(nl);
  490 + }
  491 + procurementPlanLineService.saveBatch(newLines);
  492 + }
  493 +
  494 + // 4) 根据变更后明细的分厂分组,从_(N+1)开始生成新子计划
  495 + // 确定下一个子计划后缀:现有最大编号 +1
  496 + int nextSuffix = 1;
  497 + if (CollectionUtil.isNotEmpty(existSubList)) {
  498 + int maxSuf = 0;
  499 + for (ProcurementPlan s : existSubList) {
  500 + String c = s.getCode();
  501 + if (c != null && c.contains("_")) {
  502 + String suf = c.substring(c.lastIndexOf("_") + 1);
  503 + try {
  504 + maxSuf = Math.max(maxSuf, Integer.parseInt(suf));
  505 + } catch (Exception ignore) {
  506 + // 忽略非数字后缀
  507 + }
  508 + }
  509 + }
  510 + nextSuffix = maxSuf + 1;
  511 + }
  512 +
  513 + // 按分厂分组明细
  514 + if (CollectionUtil.isNotEmpty(changeLines)) {
  515 + // workshopId -> workshop code
  516 + List<String> allWorkshopIds = changeLines.stream()
  517 + .map(ProcurementPlanChangeLine::getWorkshopId)
  518 + .filter(StringUtil::isNotBlank)
  519 + .distinct()
  520 + .collect(Collectors.toList());
  521 + Map<String, String> workshop2Code = new HashMap<>();
  522 + if (CollectionUtil.isNotEmpty(allWorkshopIds)) {
  523 + List<Workshop> workshopList = workshopService.listByIds(allWorkshopIds);
  524 + if (CollectionUtil.isNotEmpty(workshopList)) {
  525 + workshop2Code = workshopList.stream()
  526 + .filter(w -> StringUtil.isNotBlank(w.getCode()))
  527 + .collect(Collectors.toMap(Workshop::getId, Workshop::getCode, (v1, v2) -> v1));
  528 + }
  529 + }
  530 + // 分厂顺序
  531 + Map<String, Integer> workOrderMap = new LinkedHashMap<>();
  532 + workOrderMap.put("yfc", 1);
  533 + workOrderMap.put("efc", 2);
  534 + workOrderMap.put("sfc", 3);
  535 + workOrderMap.put("ztfc", 4);
  536 + Map<String, List<ProcurementPlanChangeLine>> workshopLines = new LinkedHashMap<>();
  537 + for (ProcurementPlanChangeLine cl : changeLines) {
  538 + String code = workshop2Code.getOrDefault(cl.getWorkshopId(), "");
  539 + workshopLines.computeIfAbsent(code, k -> new ArrayList<>()).add(cl);
  540 + }
  541 + List<Map.Entry<String, List<ProcurementPlanChangeLine>>> sortedEntries = workshopLines.entrySet().stream()
  542 + .sorted(Comparator.comparingInt(e -> workOrderMap.getOrDefault(e.getKey(), Integer.MAX_VALUE)))
  543 + .collect(Collectors.toList());
  544 +
  545 + for (Map.Entry<String, List<ProcurementPlanChangeLine>> entry : sortedEntries) {
  546 + List<ProcurementPlanChangeLine> wLines = entry.getValue();
  547 + if (CollectionUtil.isEmpty(wLines)) {
  548 + continue;
  549 + }
  550 + String suffixStr = String.format("%02d", nextSuffix);
  551 + String subCode = mainPlan.getCode() + "_" + suffixStr;
  552 + nextSuffix++;
  553 +
  554 + CreateProcurementPlanVo subVo = BeanUtil.copyProperties(entity, CreateProcurementPlanVo.class);
  555 + subVo.setCode(subCode);
  556 + subVo.setParentId(planId);
  557 + subVo.setProcessStatus("COMPLETED");
  558 + subVo.setProcessNode(null);
  559 + subVo.setWorkshopId(CollectionUtil.isNotEmpty(wLines) ? wLines.get(0).getWorkshopId() : null);
  560 +
  561 + List<ProcurementPlanLineVo> subLineVos = new ArrayList<>();
  562 + for (ProcurementPlanChangeLine cl : wLines) {
  563 + ProcurementPlanLineVo lv = BeanUtil.copyProperties(cl, ProcurementPlanLineVo.class);
  564 + subLineVos.add(lv);
  565 + }
  566 + subVo.setProcurementPlanLineList(subLineVos);
  567 + // createSubPlan 内部会保存主表+明细+自动生成买断申请单
  568 + procurementPlanService.createSubPlan(subVo);
  569 + }
  570 + }
  571 +
  572 + // 5) 主计划标记冻结
  573 + procurementPlanService.update(Wrappers.lambdaUpdate(ProcurementPlan.class)
  574 + .set(ProcurementPlan::getFrozen, Boolean.TRUE)
408 575 .set(ProcurementPlan::getChangeStatus, "CHANGED")
409   - .eq(ProcurementPlan::getId, entity.getProcurementPlanId());
410   - procurementPlanService.update(parentWrapper);
411   - //todo 拆分分厂、生产买断操作申请单、生成合同,生成一份原始的计划数据(第一次变更)
  576 + .set(ProcurementPlan::getProcessStatus, "COMPLETED")
  577 + .set(ProcurementPlan::getProcessNode, null)
  578 + .eq(ProcurementPlan::getId, planId));
  579 +
  580 + //todo 废弃原合同;生成新合同
412 581 } else if ("REFUSE".equals(approvalStatus)) {
413 582 List<SysRole> roles = sysRoleService.getByUserId(entity.getCreateById());
414 583 List<String> roleCodes = CollectionUtil.isEmpty(roles) ? new java.util.ArrayList<>()
... ...
... ... @@ -223,7 +223,7 @@ public class ProcurementPlanServiceImpl extends BaseMpServiceImpl<ProcurementPla
223 223 * @param procurementPlanId 采购计划ID
224 224 * @param lineList 明细列表
225 225 */
226   - private void saveOrUpdateLines(String procurementPlanId, List<ProcurementPlanUpdateLineVo> lineList) {
  226 + public void saveOrUpdateLines(String procurementPlanId, List<ProcurementPlanUpdateLineVo> lineList) {
227 227 List<ProcurementPlanLine> oldLineList = procurementPlanLineService
228 228 .getByProcurementPlanId(procurementPlanId);
229 229 Map<String, ProcurementPlanLine> oldLineMap = CollectionUtil.isEmpty(oldLineList)
... ... @@ -648,6 +648,17 @@ public class ProcurementPlanServiceImpl extends BaseMpServiceImpl<ProcurementPla
648 648 procurementBuyoutApplyService.create(buyoutApplyVo);
649 649 }
650 650
  651 + @Override
  652 + public List<ProcurementPlan> listSubPlan(String parentId) {
  653 + if (StringUtil.isBlank(parentId)) {
  654 + return Collections.emptyList();
  655 + }
  656 + return this.list(Wrappers.lambdaQuery(ProcurementPlan.class)
  657 + .eq(ProcurementPlan::getParentId, parentId)
  658 + .eq(ProcurementPlan::getDeleted, false)
  659 + .orderByAsc(ProcurementPlan::getCode));
  660 + }
  661 +
651 662 /**
652 663 * 生成采购计划编号(业务要求按日期时间生成,保证唯一)
653 664 *
... ...
... ... @@ -5,6 +5,7 @@ import com.lframework.starter.web.core.service.BaseMpService;
5 5 import com.lframework.xingyun.sc.bo.procurement.GetProcurementPlanBo;
6 6 import com.lframework.xingyun.sc.procurement.entity.ProcurementPlan;
7 7 import com.lframework.xingyun.sc.procurement.vo.plan.CreateProcurementPlanVo;
  8 +import com.lframework.xingyun.sc.procurement.vo.plan.ProcurementPlanUpdateLineVo;
8 9 import com.lframework.xingyun.sc.procurement.vo.plan.QueryProcurementPlanVo;
9 10 import com.lframework.xingyun.sc.procurement.vo.plan.UpdateProcurementPlanVo;
10 11 import java.util.List;
... ... @@ -85,6 +86,14 @@ public interface ProcurementPlanService extends BaseMpService<ProcurementPlan> {
85 86 void update(UpdateProcurementPlanVo vo);
86 87
87 88 /**
  89 + * 保存或更新采购计划明细
  90 + *
  91 + * @param procurementPlanId 采购计划id
  92 + * @param lineList 明细
  93 + */
  94 + void saveOrUpdateLines(String procurementPlanId, List<ProcurementPlanUpdateLineVo> lineList);
  95 +
  96 + /**
88 97 * 根据ID删除
89 98 *
90 99 * @param id
... ... @@ -99,6 +108,21 @@ public interface ProcurementPlanService extends BaseMpService<ProcurementPlan> {
99 108 void generateContract(String id);
100 109
101 110 /**
  111 + * 子计划创建(不重新buildPlanCode,code=父code+_+后缀)
  112 + *
  113 + * @param vo
  114 + */
  115 + void createSubPlan(CreateProcurementPlanVo vo);
  116 +
  117 + /**
  118 + * 根据主计划ID查询所有子计划(按code升序)
  119 + *
  120 + * @param parentId 主计划ID
  121 + * @return
  122 + */
  123 + List<ProcurementPlan> listSubPlan(String parentId);
  124 +
  125 + /**
102 126 * 提交
103 127 *
104 128 * @param id
... ...
... ... @@ -63,6 +63,7 @@
63 63 <result column="salesman_quotation_id" property="salesmanQuotationId"/>
64 64 <result column="attachment_file_ids" property="attachmentFileIds"/>
65 65 <result column="attachment_file_names" property="attachmentFileNames"/>
  66 + <result column="deleted" property="deleted"/>
66 67 <result column="create_by_id" property="createById"/>
67 68 <result column="create_by" property="createBy"/>
68 69 <result column="update_by_id" property="updateById"/>
... ... @@ -133,6 +134,7 @@
133 134 pp.salesman_quotation_id,
134 135 pp.attachment_file_ids,
135 136 pp.attachment_file_names,
  137 + pp.deleted,
136 138 pp.create_by_id,
137 139 pp.create_by,
138 140 pp.update_by_id,
... ... @@ -151,6 +153,7 @@
151 153 <select id="query" resultMap="ProcurementPlan">
152 154 <include refid="ProcurementPlan_sql"/>
153 155 <where>
  156 + AND pp.deleted = 0
154 157 <if test="vo != null">
155 158 <if test="vo.code != null and vo.code != ''">
156 159 AND pp.code LIKE CONCAT('%', #{vo.code}, '%')
... ... @@ -210,6 +213,7 @@
210 213 <include refid="ProcurementPlan_sql"/>
211 214 <where>
212 215 pp.parent_id IS NULL
  216 + AND pp.deleted = 0
213 217 AND NOT EXISTS (
214 218 SELECT 1 FROM procurement_plan p
215 219 WHERE p.id = pp.parent_id
... ... @@ -277,6 +281,7 @@
277 281 AND (pp.change_status = '' OR pp.change_status IS NULL OR pp.change_status = 'CHANGED')
278 282 AND (pp.replenishment_status = '' OR pp.replenishment_status IS NULL OR pp.replenishment_status
279 283 = 'REPLENISHED')
  284 + AND pp.deleted = 0
280 285 AND NOT EXISTS (
281 286 SELECT 1 FROM procurement_plan p
282 287 WHERE p.id = pp.parent_id
... ...