Commit 61aab9a83543fa4efcb1dc69e004554a3a72033f

Authored by 房远帅
1 parent 3432964f

采购计划:选择分厂

@@ -94,8 +94,8 @@ public class ProcurementPlanController extends DefaultBaseController { @@ -94,8 +94,8 @@ public class ProcurementPlanController extends DefaultBaseController {
94 /** 94 /**
95 * 修改 95 * 修改
96 */ 96 */
97 - @ApiOperation("修改")  
98 - @HasPermission({"plan-manage:domestic-plan:supple"}) 97 + @ApiOperation("修改/选择分厂")
  98 + @HasPermission({"plan-manage:domestic-plan:supple","plan-manage:domestic-plan:split"})
99 @PutMapping 99 @PutMapping
100 public InvokeResult<Void> update(@RequestBody @Valid UpdateProcurementPlanVo vo) { 100 public InvokeResult<Void> update(@RequestBody @Valid UpdateProcurementPlanVo vo) {
101 101
@@ -120,6 +120,19 @@ public class ProcurementPlanController extends DefaultBaseController { @@ -120,6 +120,19 @@ public class ProcurementPlanController extends DefaultBaseController {
120 } 120 }
121 121
122 /** 122 /**
  123 + * 提交
  124 + */
  125 + @ApiOperation("提交")
  126 + @HasPermission({"plan-manage:domestic-plan:submit"})
  127 + @PostMapping("/submit")
  128 + public InvokeResult<Void> submit(@Valid String id) {
  129 +
  130 + procurementPlanService.submit(id);
  131 +
  132 + return InvokeResultBuilder.success();
  133 + }
  134 +
  135 + /**
123 * 删除 136 * 删除
124 */ 137 */
125 @ApiOperation("删除") 138 @ApiOperation("删除")
@@ -14,6 +14,8 @@ import com.lframework.starter.common.utils.StringUtil; @@ -14,6 +14,8 @@ import com.lframework.starter.common.utils.StringUtil;
14 import com.lframework.starter.web.core.impl.BaseMpServiceImpl; 14 import com.lframework.starter.web.core.impl.BaseMpServiceImpl;
15 import com.lframework.starter.web.core.utils.IdUtil; 15 import com.lframework.starter.web.core.utils.IdUtil;
16 import com.lframework.starter.web.core.utils.PageHelperUtil; 16 import com.lframework.starter.web.core.utils.PageHelperUtil;
  17 +import com.lframework.starter.web.inner.entity.SysDept;
  18 +import com.lframework.starter.web.inner.service.system.SysDeptService;
17 import com.lframework.starter.web.core.components.resp.PageResult; 19 import com.lframework.starter.web.core.components.resp.PageResult;
18 import com.lframework.starter.web.core.utils.PageResultUtil; 20 import com.lframework.starter.web.core.utils.PageResultUtil;
19 import com.lframework.xingyun.sc.bo.procurement.GetProcurementPlanBo; 21 import com.lframework.xingyun.sc.bo.procurement.GetProcurementPlanBo;
@@ -32,6 +34,9 @@ import java.time.LocalDate; @@ -32,6 +34,9 @@ import java.time.LocalDate;
32 import java.time.format.DateTimeFormatter; 34 import java.time.format.DateTimeFormatter;
33 import java.util.ArrayList; 35 import java.util.ArrayList;
34 import java.util.Collections; 36 import java.util.Collections;
  37 +import java.util.Comparator;
  38 +import java.util.HashMap;
  39 +import java.util.LinkedHashMap;
35 import java.util.List; 40 import java.util.List;
36 import java.util.Map; 41 import java.util.Map;
37 import java.util.stream.Collectors; 42 import java.util.stream.Collectors;
@@ -46,6 +51,8 @@ public class ProcurementPlanServiceImpl extends BaseMpServiceImpl<ProcurementPla @@ -46,6 +51,8 @@ public class ProcurementPlanServiceImpl extends BaseMpServiceImpl<ProcurementPla
46 51
47 @Autowired 52 @Autowired
48 private ProcurementPlanLineService procurementPlanLineService; 53 private ProcurementPlanLineService procurementPlanLineService;
  54 + @Autowired
  55 + private SysDeptService sysDeptService;
49 56
50 @Override 57 @Override
51 public PageResult<ProcurementPlan> query(Integer pageIndex, Integer pageSize, 58 public PageResult<ProcurementPlan> query(Integer pageIndex, Integer pageSize,
@@ -218,6 +225,10 @@ public class ProcurementPlanServiceImpl extends BaseMpServiceImpl<ProcurementPla @@ -218,6 +225,10 @@ public class ProcurementPlanServiceImpl extends BaseMpServiceImpl<ProcurementPla
218 if (StringUtil.isBlank(id)) { 225 if (StringUtil.isBlank(id)) {
219 throw new InputErrorException("ID不能为空!"); 226 throw new InputErrorException("ID不能为空!");
220 } 227 }
  228 + ProcurementPlan procurementPlan = getBaseMapper().selectById(id);
  229 + if (procurementPlan == null || procurementPlan.getCompanyShortId() == null) {
  230 + throw new InputErrorException("请先补充公司名称!");
  231 + }
221 //todo 后面合同模块需要补充生成合同的逻辑 232 //todo 后面合同模块需要补充生成合同的逻辑
222 LambdaUpdateWrapper<ProcurementPlan> updateWrapper = Wrappers.lambdaUpdate(ProcurementPlan.class) 233 LambdaUpdateWrapper<ProcurementPlan> updateWrapper = Wrappers.lambdaUpdate(ProcurementPlan.class)
223 .set(ProcurementPlan::getProcessNode, "GYGLKZLGLG") 234 .set(ProcurementPlan::getProcessNode, "GYGLKZLGLG")
@@ -227,6 +238,146 @@ public class ProcurementPlanServiceImpl extends BaseMpServiceImpl<ProcurementPla @@ -227,6 +238,146 @@ public class ProcurementPlanServiceImpl extends BaseMpServiceImpl<ProcurementPla
227 getBaseMapper().update(updateWrapper); 238 getBaseMapper().update(updateWrapper);
228 } 239 }
229 240
  241 + @Transactional(rollbackFor = Exception.class)
  242 + @Override
  243 + public void submit(String id) {
  244 + if (StringUtil.isBlank(id)) {
  245 + throw new InputErrorException("ID不能为空!");
  246 + }
  247 + ProcurementPlan mainPlan = getBaseMapper().findById(id);
  248 + if (mainPlan == null) {
  249 + throw new DefaultClientException("采购计划不存在!");
  250 + }
  251 + List<ProcurementPlanLine> allLines = procurementPlanLineService.getByProcurementPlanId(id);
  252 +
  253 + if (CollectionUtil.isEmpty(allLines)) {
  254 + throw new DefaultClientException("采购计划明细不能为空,无法提交!");
  255 + }
  256 +
  257 + // 1. 校验明细:只要有任意一条明细未配置分厂ID,就不允许拆分
  258 + boolean hasEmptyWorkshopId = allLines.stream()
  259 + .anyMatch(line -> line.getWorkshopId() == null); // 检查是否存在空的分厂ID
  260 +
  261 + if (hasEmptyWorkshopId) {
  262 + throw new DefaultClientException("部分采购计划明细未选择分厂,请先选择分厂!");
  263 + }
  264 +
  265 + // 2. 校验通过后,再提取所有的 workshopId(此时可保证全部非空)
  266 + List<String> workshopIds = allLines.stream()
  267 + .map(ProcurementPlanLine::getWorkshopId)
  268 + .distinct()
  269 + .collect(Collectors.toList());
  270 + Map<String, String> deptId2CodeMap = new HashMap<>();
  271 + List<SysDept> deptList = sysDeptService.listByIds(workshopIds);
  272 + if (CollectionUtil.isNotEmpty(deptList)) {
  273 + deptId2CodeMap = deptList.stream()
  274 + .filter(d -> StringUtil.isNotBlank(d.getCode()))
  275 + .collect(Collectors.toMap(SysDept::getId, SysDept::getCode, (v1, v2) -> v1));
  276 + }
  277 +
  278 + // 分厂顺序:yfc(01) / efc(02) / sfc(03) / ztfc(04)
  279 + Map<String, Integer> workOrderMap = new LinkedHashMap<>();
  280 + workOrderMap.put("yfc", 1);
  281 + workOrderMap.put("efc", 2);
  282 + workOrderMap.put("sfc", 3);
  283 + workOrderMap.put("ztfc", 4);
  284 +
  285 + // 按分厂code分组明细(非法/缺失code的明细都提示)
  286 + Map<String, List<ProcurementPlanLine>> code2LinesMap = new LinkedHashMap<>();
  287 + for (ProcurementPlanLine line : allLines) {
  288 + String code = deptId2CodeMap.get(line.getWorkshopId());
  289 + code2LinesMap.computeIfAbsent(code, k -> new ArrayList<>()).add(line);
  290 + }
  291 +
  292 + long distinctFactoryCount = code2LinesMap.keySet().stream().distinct().count();
  293 + if (distinctFactoryCount <= 1) {
  294 + // 所有明细都同一分厂,不需要拆分,直接把主计划推到下一节点
  295 + LambdaUpdateWrapper<ProcurementPlan> updateWrapper = Wrappers.lambdaUpdate(ProcurementPlan.class)
  296 + .set(ProcurementPlan::getProcessNode, "GYGLKZG")
  297 + .set(ProcurementPlan::getProcessStatus, "TO_SUBMIT_REVIEW")
  298 + .eq(ProcurementPlan::getId, id);
  299 + getBaseMapper().update(updateWrapper);
  300 + //todo 生成买断操作申请单
  301 + return;
  302 + }
  303 +
  304 + // 2. 多分厂:按 yfc/efc/sfc/ztfc 的顺序生成子计划
  305 + List<Map.Entry<String, List<ProcurementPlanLine>>> sortedEntries = code2LinesMap.entrySet().stream()
  306 + .sorted(Comparator.comparingInt(e -> workOrderMap.getOrDefault(e.getKey(), Integer.MAX_VALUE)))
  307 + .collect(Collectors.toList());
  308 +
  309 + int subSeq = 0;
  310 + for (Map.Entry<String, List<ProcurementPlanLine>> entry : sortedEntries) {
  311 + List<ProcurementPlanLine> factoryLines = entry.getValue();
  312 + if (CollectionUtil.isEmpty(factoryLines)) {
  313 + continue;
  314 + }
  315 + subSeq++;
  316 + String suffix = String.format("%02d", subSeq);
  317 + String subCode = mainPlan.getCode() + "_" + suffix;
  318 +
  319 + // 3. 子计划主表:除明细/编号/parentId/流程状态节点外,其余同主计划
  320 + CreateProcurementPlanVo subVo = BeanUtil.copyProperties(mainPlan, CreateProcurementPlanVo.class);
  321 + subVo.setCode(subCode);
  322 + subVo.setParentId(mainPlan.getId());
  323 + subVo.setProcessStatus("TO_SUBMIT_REVIEW");
  324 + subVo.setProcessNode("GYGLKZG");
  325 +
  326 + // 子明细:只保留当前分厂,workshopId 保持原明细的,其它字段按原明细 copy
  327 + List<ProcurementPlanLineVo> subLineVoList = new ArrayList<>();
  328 + for (ProcurementPlanLine line : factoryLines) {
  329 + ProcurementPlanLineVo lineVo = BeanUtil.copyProperties(line, ProcurementPlanLineVo.class);
  330 + lineVo.setWorkshopId(line.getWorkshopId());
  331 + subLineVoList.add(lineVo);
  332 + }
  333 + subVo.setProcurementPlanLineList(subLineVoList);
  334 +
  335 + // 子计划创建(不重新生成编号,因为使用父code+后缀)
  336 + createSubPlan(subVo);
  337 + //todo 生成买断操作申请单
  338 + }
  339 +
  340 + // 4. 冻结主计划(不再变更主计划)
  341 + LambdaUpdateWrapper<ProcurementPlan> mainUpdateWrapper = Wrappers.lambdaUpdate(ProcurementPlan.class)
  342 + .set(ProcurementPlan::getFrozen, Boolean.TRUE)
  343 + .set(ProcurementPlan::getProcessNode, null)
  344 + .set(ProcurementPlan::getProcessStatus, null)
  345 + .eq(ProcurementPlan::getId, id);
  346 + getBaseMapper().update(mainUpdateWrapper);
  347 + }
  348 +
  349 + /**
  350 + * 子计划创建:与 create() 逻辑一致,但不重新 buildPlanCode(因为子计划编号已按父code+后缀生成)
  351 + */
  352 + @Transactional(rollbackFor = Exception.class)
  353 + public void createSubPlan(CreateProcurementPlanVo vo) {
  354 + vo.validate();
  355 +
  356 + ProcurementPlan plan = BeanUtil.copyProperties(vo, ProcurementPlan.class);
  357 + plan.setId(IdUtil.getId());
  358 +
  359 + BigDecimal totalQuantity = BigDecimal.ZERO;
  360 + if (!CollectionUtil.isEmpty(vo.getProcurementPlanLineList())) {
  361 + for (ProcurementPlanLineVo lineVo : vo.getProcurementPlanLineList()) {
  362 + if (lineVo.getQuantity() != null) {
  363 + totalQuantity = NumberUtil.add(totalQuantity, lineVo.getQuantity());
  364 + }
  365 + }
  366 + }
  367 + plan.setTotalQuantity(totalQuantity);
  368 +
  369 + getBaseMapper().insert(plan);
  370 +
  371 + if (!CollectionUtil.isEmpty(vo.getProcurementPlanLineList())) {
  372 + for (ProcurementPlanLineVo lineVo : vo.getProcurementPlanLineList()) {
  373 + ProcurementPlanLine line = BeanUtil.copyProperties(lineVo, ProcurementPlanLine.class);
  374 + line.setId(IdUtil.getId());
  375 + line.setProcurementPlanId(plan.getId());
  376 + procurementPlanLineService.save(line);
  377 + }
  378 + }
  379 + }
  380 +
230 /** 381 /**
231 * 生成采购计划编号(业务要求按日期时间生成,保证唯一) 382 * 生成采购计划编号(业务要求按日期时间生成,保证唯一)
232 * 383 *
@@ -781,6 +781,8 @@ public class SalesmanQuotationServiceImpl @@ -781,6 +781,8 @@ public class SalesmanQuotationServiceImpl
781 lineVo.setQuantity(line.getQuantity()); 781 lineVo.setQuantity(line.getQuantity());
782 // 实际采购价 = 报价明细采购价 782 // 实际采购价 = 报价明细采购价
783 lineVo.setActualPurchasePrice(line.getPurchasePrice()); 783 lineVo.setActualPurchasePrice(line.getPurchasePrice());
  784 + //含运到厂价(实际采购价+另附运费)
  785 + lineVo.setIncludingShippingPrice(line.getPurchasePrice());
784 // 价格表价格 786 // 价格表价格
785 if (latest != null && CollectionUtil.isNotEmpty(latest.getPriceSubTableList())) { 787 if (latest != null && CollectionUtil.isNotEmpty(latest.getPriceSubTableList())) {
786 List<PriceSubTable> priceSubTableList = latest.getPriceSubTableList(); 788 List<PriceSubTable> priceSubTableList = latest.getPriceSubTableList();
@@ -791,7 +793,20 @@ public class SalesmanQuotationServiceImpl @@ -791,7 +793,20 @@ public class SalesmanQuotationServiceImpl
791 .orElse(null); // 如果没有匹配到,返回 null 793 .orElse(null); // 如果没有匹配到,返回 null
792 lineVo.setPriceListPrices(targetPrice); 794 lineVo.setPriceListPrices(targetPrice);
793 } 795 }
  796 + //买断预计损耗
794 lineVo.setEstimatedLoss(line.getEstimatedLoss()); 797 lineVo.setEstimatedLoss(line.getEstimatedLoss());
  798 + //协调幅度=含运到厂价+买断料预计损耗-价格表价格
  799 + BigDecimal estimatedLoss = line.getEstimatedLoss();
  800 + BigDecimal priceListPrices = lineVo.getPriceListPrices();
  801 + BigDecimal coordinationRange = lineVo.getIncludingShippingPrice(); // 初始值为含运到厂价
  802 +
  803 + if (estimatedLoss != null) {
  804 + coordinationRange = coordinationRange.add(estimatedLoss);
  805 + }
  806 + if (priceListPrices != null) {
  807 + coordinationRange = coordinationRange.subtract(priceListPrices);
  808 + }
  809 + lineVo.setCoordinationRange(coordinationRange);
795 lineVo.setApplicationAttrition(line.getApplicationAttrition()); 810 lineVo.setApplicationAttrition(line.getApplicationAttrition());
796 lineVo.setItemId(line.getItemId()); 811 lineVo.setItemId(line.getItemId());
797 lineVoList.add(lineVo); 812 lineVoList.add(lineVo);
@@ -65,4 +65,11 @@ public interface ProcurementPlanService extends BaseMpService<ProcurementPlan> { @@ -65,4 +65,11 @@ public interface ProcurementPlanService extends BaseMpService<ProcurementPlan> {
65 * @param id 65 * @param id
66 */ 66 */
67 void generateContract(String id); 67 void generateContract(String id);
  68 +
  69 + /**
  70 + * 提交
  71 + *
  72 + * @param id
  73 + */
  74 + void submit(String id);
68 } 75 }