From e115e52676f72ba7ae44c40dc0fc16e18e5e6496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E9=B9=8F=E9=A3=9E?= Date: Mon, 21 Apr 2025 15:43:13 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../technology/pojo/dto/EBomCostCacheDTO.java | 6 + .../service/BomCostCalculateService.java | 121 +++++++++++++++++- .../service/MaterialMainAttrService.java | 10 ++ 3 files changed, 136 insertions(+), 1 deletion(-) diff --git a/nflg_project_dev/nflg-technology/src/main/java/com/nflg/product/technology/pojo/dto/EBomCostCacheDTO.java b/nflg_project_dev/nflg-technology/src/main/java/com/nflg/product/technology/pojo/dto/EBomCostCacheDTO.java index 010356ac..0e0ecdc4 100644 --- a/nflg_project_dev/nflg-technology/src/main/java/com/nflg/product/technology/pojo/dto/EBomCostCacheDTO.java +++ b/nflg_project_dev/nflg-technology/src/main/java/com/nflg/product/technology/pojo/dto/EBomCostCacheDTO.java @@ -147,8 +147,14 @@ public class EBomCostCacheDTO implements Serializable { */ public BigDecimal materialPrice; + /** + * 采购类型是否是E50 + */ private boolean purchaseTypeIsE50; + /** + * 采购类型是否是F + */ private boolean purchaseTypeIsF; @Override diff --git a/nflg_project_dev/nflg-technology/src/main/java/com/nflg/product/technology/service/BomCostCalculateService.java b/nflg_project_dev/nflg-technology/src/main/java/com/nflg/product/technology/service/BomCostCalculateService.java index bb459687..8c7d6f58 100644 --- a/nflg_project_dev/nflg-technology/src/main/java/com/nflg/product/technology/service/BomCostCalculateService.java +++ b/nflg_project_dev/nflg-technology/src/main/java/com/nflg/product/technology/service/BomCostCalculateService.java @@ -76,6 +76,12 @@ public class BomCostCalculateService { */ private static final Duration CACHE_DURATION = Duration.ofHours(1); + /** + * 计算指定物料在指定月份的成本缓存数据。 + * + * @param materialNo 物料编号,用于标识需要计算成本的物料。 + * @param month 月份,格式为字符串,用于指定计算成本的月份,如果不为空,则从历史表中获取配置信息。 + */ public List calculate(String materialNo, String month) { VUtils.isTure(StrUtil.isBlank(materialNo)).throwMessage("物料编号不能为空"); List datas = new ArrayList<>(); @@ -98,11 +104,25 @@ public class BomCostCalculateService { return result; } + /** + * 计算BOM(Bill of Materials)成本,并将结果缓存到指定的结果列表中。 + * + * @param rootMaterialNo 根物料的编号,用于标识当前计算的BOM的根节点,主要用于日志记录。 + * @param month 计算的月份,用于确定成本计算的时间范围。 + * @param isRoot 标识当前计算的物料是否为根节点,与E50逻辑相关。 + * @param dto 当前物料的BOM数据传输对象,包含物料的基本信息和成本数据。 + * @param datas 所有BOM的单层数据。 + * @param config BOM成本计算的配置对象,包含计算过程中需要的配置参数。 + * @param result 计算结果缓存列表,用于存储每个物料的成本计算结果。 + * @return 返回当前物料的BOM成本计算结果,类型为EBomCostCacheDTO。 + */ private EBomCostCacheDTO calculateBom(String rootMaterialNo, String month, boolean isRoot, EBomDTO dto, List datas, BomCostCalculateConfig config, List result) { String cdata = redisTemplate.opsForValue().get(buildKey(dto, month)); boolean purchaseTypeIsF = materialMainAttrService.purchaseTypeIsF(dto.getMaterialNo()); boolean purchaseTypeIsE50 = materialMainAttrService.purchaseTypeIsE50(dto.getMaterialNo()); + //如果是根节点,且采购类型为E50,或者缓存中没有数据,则进行计算。 if ((isRoot && purchaseTypeIsE50) || StrUtil.isBlank(cdata)) { + //如果是根节点,或者不是E50,则查询工时。 List workingHours = isRoot || !purchaseTypeIsE50 ? processRouteTaskProcessesService.getWorkingHour(dto.getMaterialNo()) : new ArrayList<>(); log.info(StrUtil.format("{},BOM成本计算 {} 物料工时: {}", rootMaterialNo, dto.getMaterialNo(), JsonUtil.toJson(workingHours))); EBomCostCacheDTO cdto = new EBomCostCacheDTO(); @@ -113,26 +133,39 @@ public class BomCostCalculateService { cdto.setCategoryCode(dto.getMaterialCategoryCode()); if (purchaseTypeIsF) { cdto.setHasChildren(false); + // 采购类型为F,则视为外购件 cdto.setPurchasedParts(true); + // 计算采购成本 cdto.setPurchasedPartsCost(calculatePurchasedPartsCost(dto)); // 计算损耗成本 cdto.setPurchasedPartsWasteCost(calculateWasteCost(dto, month)); } else { + //标准计件工资 cdto.setPieceRateSalary(calculatePieceRateSalary(dto, config, workingHours)); + //福利费用 cdto.setWelfareExpenses(calculateWelfareExpenses(dto, config, workingHours)); + //水电生产耗材成本 cdto.setHydropowerCost(calculateHydropowerCost(dto, config, workingHours)); + //折旧成本 cdto.setDepreciationCost(calculateDepreciationCost(dto, config, workingHours)); + //车间管理人工费 cdto.setWorkshopManagementLaborCost(calculateWorkshopManagementLaborCost(dto, config, workingHours)); + //车间办公室费用 cdto.setWorkshopOfficeExpenses(calculateWorkshopOfficeExpenses(dto, config, workingHours)); + //辅助部门人工费 cdto.setAuxiliaryDepartmentLaborCost(calculateAuxiliaryDepartmentLaborCost(dto, config, workingHours)); + //辅助部门费用 cdto.setAuxiliaryDepartmentExpenses(calculateAuxiliaryDepartmentExpenses(dto, config, workingHours)); + //制作成本 cdto.setProductionCosts(calculateProductionCosts(dto, config, workingHours)); + //获取该物料的子节点 Set children = datas.stream() .filter(d -> StrUtil.equals(d.getParentMaterialNo(), dto.getMaterialNo())) .collect(Collectors.toSet()); if (CollectionUtil.isEmpty(children)) { cdto.setHasChildren(false); // 计算自身 + //不在钢材类型列表中,就是外购件 cdto.setPurchasedParts(!BomConstant.STEELS_CATEGORY_CODE.contains(dto.getMaterialCategoryCode())); if (cdto.isPurchasedParts()) { // 外购件只计算采购成本 @@ -155,28 +188,46 @@ public class BomCostCalculateService { List childrenCost = children.parallelStream() .map(c -> calculateBom(rootMaterialNo, month, false, c, datas, config, result)) .collect(Collectors.toList()); + //遍历子级成本,将数据汇总到自身 for (EBomCostCacheDTO c : childrenCost) { EBomDTO dto1 = children.stream().filter(cc -> StrUtil.equals(cc.getMaterialNo(), c.getMaterialNo())).findFirst().orElse(null); + //钢材成本 cdto.setSteelsCost(cdto.getSteelsCost().add(c.getSteelsCost().multiply(dto1.getNum()))); + //钢材损耗 cdto.setSteelsWasteCost(cdto.getSteelsWasteCost().add(c.getSteelsWasteCost().multiply(dto1.getNum()))); + //外购件成本 cdto.setPurchasedPartsCost(cdto.getPurchasedPartsCost().add(c.getPurchasedPartsCost().multiply(dto1.getNum()))); + //外购件损耗 cdto.setPurchasedPartsWasteCost(cdto.getPurchasedPartsWasteCost().add(c.getPurchasedPartsWasteCost().multiply(dto1.getNum()))); + //标准计件工资 cdto.setPieceRateSalary(cdto.getPieceRateSalary().add(c.getPieceRateSalary().multiply(dto1.getNum()))); + //福利费用 cdto.setWelfareExpenses(cdto.getWelfareExpenses().add(c.getWelfareExpenses().multiply(dto1.getNum()))); + //水电生产耗材成本 cdto.setHydropowerCost(cdto.getHydropowerCost().add(c.getHydropowerCost().multiply(dto1.getNum()))); + //折旧成本 cdto.setDepreciationCost(cdto.getDepreciationCost().add(c.getDepreciationCost().multiply(dto1.getNum()))); + //车间管理人工费 cdto.setWorkshopManagementLaborCost(cdto.getWorkshopManagementLaborCost().add(c.getWorkshopManagementLaborCost().multiply(dto1.getNum()))); + //车间办公室费用 cdto.setWorkshopOfficeExpenses(cdto.getWorkshopOfficeExpenses().add(c.getWorkshopOfficeExpenses().multiply(dto1.getNum()))); + //辅助部门人工费 cdto.setAuxiliaryDepartmentLaborCost(cdto.getAuxiliaryDepartmentLaborCost().add(c.getAuxiliaryDepartmentLaborCost().multiply(dto1.getNum()))); + //辅助部门费用 cdto.setAuxiliaryDepartmentExpenses(cdto.getAuxiliaryDepartmentExpenses().add(c.getAuxiliaryDepartmentExpenses().multiply(dto1.getNum()))); if (StrUtil.equalsIgnoreCase(dto1.getMaterialUnit(), "kg")) { + //设置油漆重量,数量作为重量 cdto.setPaintWeight(cdto.getPaintWeight().add(dto1.getNum())); + //油漆成本 cdto.setPaintCost(cdto.getPaintCost().add(calculatePaintCost(rootMaterialNo, dto1, month))); } else { + //设置油漆重量 cdto.setPaintWeight(cdto.getPaintWeight().add(c.getPaintWeight().multiply(dto1.getNum()))); + //油漆成本 cdto.setPaintCost(cdto.getPaintCost().add(c.getPaintCost().multiply(dto1.getNum()))); } if (CollectionUtil.isNotEmpty(c.getProductionCosts())) { + //将制作成本根据工艺工序进行分组累加 c.getProductionCosts().forEach(pc -> { ProductionCostDTO pdto = cdto.getProductionCosts().parallelStream() .filter(p -> StrUtil.equals(p.getName(), pc.getName())) @@ -196,6 +247,7 @@ public class BomCostCalculateService { } log.debug(StrUtil.format("BOM成本计算 {} 实时计算", dto.getMaterialNo())); result.add(cdto); + //如果不是E50且不是根节点,则缓存数据 if (!(isRoot && purchaseTypeIsE50)) { cdata = JsonUtil.toJson(cdto); //log.debug(cdata); @@ -207,6 +259,7 @@ public class BomCostCalculateService { //log.debug(cdata); EBomCostCacheDTO cdto = JsonUtil.fromJson(cdata, EBomCostCacheDTO.class); result.add(cdto); + //如果不是F,则继续计算子节点 if (!purchaseTypeIsF) { List children = datas.parallelStream() .filter(d -> StrUtil.equals(d.getParentMaterialNo(), dto.getMaterialNo())) @@ -217,6 +270,9 @@ public class BomCostCalculateService { } } + /** + * 计算制作成本 + */ private List calculateProductionCosts(EBomDTO d, BomCostCalculateConfig config, List workHours) { List productionCosts = new ArrayList<>(); for (VirtualWorkingItemVO vw : config.getVirtualWorkings()) { @@ -246,6 +302,14 @@ public class BomCostCalculateService { return productionCosts; } + /** + * 计算油漆成本 + * + * @param rootMaterialNo 根物料编号 + * @param dto 物料信息 + * @param month 月份 + * @return 成本 + */ private BigDecimal calculatePaintCost(String rootMaterialNo, EBomDTO dto, String month) { if (!StrUtil.equalsIgnoreCase(StrUtil.trim(dto.getMaterialUnit()), "kg")) { log.info("{},{},油漆计算:单位不符合要求({})", rootMaterialNo, dto.getMaterialNo(), dto.getMaterialUnit()); @@ -253,6 +317,7 @@ public class BomCostCalculateService { } BigDecimal price = null; if (StrUtil.isNotBlank(month)) { + //从历史表获取配置 PaintCostConfigHistoryEntity history = paintCostConfigHistoryService.getCost(dto.getRawMaterialGroup(), month); if (Objects.nonNull(history)) { price = history.getCost(); @@ -260,6 +325,7 @@ public class BomCostCalculateService { } } if (Objects.isNull(price)) { + //从最新表获取配置 PaintCostConfigEntity entity = paintCostConfigService.getCost(dto.getRawMaterialGroup()); if (Objects.nonNull(entity)) { price = entity.getCost(); @@ -280,6 +346,9 @@ public class BomCostCalculateService { } } + /** + * 车间办公室费用 + */ private BigDecimal calculateWorkshopOfficeExpenses(EBomDTO dto, BomCostCalculateConfig config, List workHours) { BigDecimal cost = BigDecimal.ZERO; for (VirtualWorkingItemVO vw : config.getVirtualWorkings()) { @@ -288,6 +357,9 @@ public class BomCostCalculateService { return cost; } + /** + * 计算辅助部门费用 + */ private BigDecimal calculateAuxiliaryDepartmentExpenses(EBomDTO dto, BomCostCalculateConfig config, List workHours) { BigDecimal cost = BigDecimal.ZERO; for (VirtualWorkingItemVO vw : config.getVirtualWorkings()) { @@ -296,6 +368,9 @@ public class BomCostCalculateService { return cost; } + /** + * 计算辅助部门人工成本 + */ private BigDecimal calculateAuxiliaryDepartmentLaborCost(EBomDTO dto, BomCostCalculateConfig config, List workHours) { BigDecimal cost = BigDecimal.ZERO; for (VirtualWorkingItemVO vw : config.getVirtualWorkings()) { @@ -304,6 +379,9 @@ public class BomCostCalculateService { return cost; } + /** + * 计算车间管理人工成本 + */ private BigDecimal calculateWorkshopManagementLaborCost(EBomDTO dto, BomCostCalculateConfig config, List workHours) { BigDecimal cost = BigDecimal.ZERO; for (VirtualWorkingItemVO vw : config.getVirtualWorkings()) { @@ -312,6 +390,9 @@ public class BomCostCalculateService { return cost; } + /** + * 计算折旧成本 + */ private BigDecimal calculateDepreciationCost(EBomDTO dto, BomCostCalculateConfig config, List workHours) { BigDecimal cost = BigDecimal.ZERO; for (VirtualWorkingItemVO vw : config.getVirtualWorkings()) { @@ -320,6 +401,9 @@ public class BomCostCalculateService { return cost; } + /** + * 计算水电生产耗材成本 + */ private BigDecimal calculateHydropowerCost(EBomDTO dto, BomCostCalculateConfig config, List workHours) { BigDecimal cost = BigDecimal.ZERO; for (VirtualWorkingItemVO vw : config.getVirtualWorkings()) { @@ -328,6 +412,9 @@ public class BomCostCalculateService { return cost; } + /** + * 根据工序名称获取工时 + */ private BigDecimal getGsForWorkingType(EBomDTO dto, List workHours, VirtualWorkingItemVO vw) { String key = dto.getMaterialNo() + '-' + StrUtil.trim(vw.getWorkingTypeName()); return workHours.stream() @@ -337,14 +424,26 @@ public class BomCostCalculateService { .getWorkHours(); } + /** + * 计算福利费用 + */ private BigDecimal calculateWelfareExpenses(EBomDTO dto, BomCostCalculateConfig config, List workHours) { return getTGS(dto, config, workHours).multiply(Optional.ofNullable(config.getManday().getBenefit()).orElse(BigDecimal.ZERO)); } + /** + * 计算标准计件工资 + * @param dto 物料信息 + * @param config 计算参数 + * @param workHours 工时信息 + */ private BigDecimal calculatePieceRateSalary(EBomDTO dto, BomCostCalculateConfig config, List workHours) { return getTGS(dto, config, workHours).multiply(Optional.ofNullable(config.getManday().getHourlyWages()).orElse(BigDecimal.ZERO)); } + /** + * 计算总工时 + */ private BigDecimal getTGS(EBomDTO dto, BomCostCalculateConfig config, List workHours) { BigDecimal gs = BigDecimal.ZERO; for (VirtualWorkingItemVO vw : config.getVirtualWorkings()) { @@ -353,10 +452,16 @@ public class BomCostCalculateService { return gs; } + /** + * 计算外购件费用 + */ private BigDecimal calculatePurchasedPartsCost(EBomDTO dto) { return Optional.ofNullable(dto.getMaterialPrice()).orElse(BigDecimal.ZERO); } + /** + * 计算钢材价格 + */ private BigDecimal calculateSteelsCost(EBomDTO dto) { //是钢材 BigDecimal price = Optional.ofNullable(dto.getMaterialPrice()).orElse(BigDecimal.ZERO); @@ -369,6 +474,9 @@ public class BomCostCalculateService { return price; } + /** + * 获取损耗 + */ private BigDecimal getWastage(EBomDTO dto, String month) { if (StrUtil.isNotBlank(month)) { SteelsCostConfigHistoryEntity history = steelsCostConfigHistoryService.getCost(dto.getRawMaterialGroup(), month); @@ -387,8 +495,10 @@ public class BomCostCalculateService { } } + /** + * 计算损耗 + */ private BigDecimal calculateWasteCost(EBomDTO dto, String month) { - //是钢材 BigDecimal price = Optional.ofNullable(dto.getMaterialPrice()).orElse(BigDecimal.ZERO); log.info("{},计算损耗:价格{}", dto.getMaterialNo(), price.toPlainString()); BigDecimal wastage = BigDecimal.ZERO; @@ -405,12 +515,18 @@ public class BomCostCalculateService { return price; } + /** + * 生成缓存key + */ private String buildKey(EBomDTO dto, String month) { return StrUtil.format("technology:cost:{}:{}:{}", dto.getMaterialNo() , StrUtil.isBlank(dto.getCurrentVersion()) ? "A00" : dto.getCurrentVersion() , StrUtil.isBlank(month) ? DateUtil.format(LocalDateTime.now(), "yyyyMM") : month); } + /** + * 获取子物料 + */ private void getChildren(String materialNo, List datas, String month) { EBomParentEntity parent = ebomService.getParent(materialNo); if (Objects.nonNull(parent)) { @@ -434,6 +550,9 @@ public class BomCostCalculateService { return dto; } + /** + * 设置物料价格 + */ private void setMaterialPrice(EBomDTO dto, String month) { if (StrUtil.isNotBlank(month)) { BigDecimal price = materialMainPriceService.getPrice(dto.getMaterialNo(), month); diff --git a/nflg_project_dev/nflg-technology/src/main/java/com/nflg/product/technology/service/MaterialMainAttrService.java b/nflg_project_dev/nflg-technology/src/main/java/com/nflg/product/technology/service/MaterialMainAttrService.java index f21f5437..f0b0b215 100644 --- a/nflg_project_dev/nflg-technology/src/main/java/com/nflg/product/technology/service/MaterialMainAttrService.java +++ b/nflg_project_dev/nflg-technology/src/main/java/com/nflg/product/technology/service/MaterialMainAttrService.java @@ -17,6 +17,11 @@ public class MaterialMainAttrService extends ServiceImpl redisTemplate; + /** + * 判断物料采购类型是否为F类 + * + * @param materialNo 物料编码 + */ public boolean purchaseTypeIsF(String materialNo) { String key = "technology:materialNo:purchaseTypeIsF"; HashOperations hashOps = redisTemplate.opsForHash(); @@ -37,6 +42,11 @@ public class MaterialMainAttrService extends ServiceImpl hashOps = redisTemplate.opsForHash();