| doc/20260522_首页财务接口升级前端变更文档.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/device/service/impl/DeviceLedgerServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/resources/mapper/account/purchase/AccountPaymentApplicationMapper.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/resources/mapper/account/sales/AccountInvoiceApplicationMapper.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/resources/mapper/stock/StockInventoryMapper.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
doc/20260522_Ê×Ò³²ÆÎñ½Ó¿ÚÉý¼¶Ç°¶Ë±ä¸üÎĵµ.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,120 @@ # é¦é¡µè´¢å¡æ¥å£å级åç«¯åæ´ææ¡£ æ´æ°æ¶é´ï¼2026-05-22 éç¨æ¨¡åï¼é¦é¡µï¼`/home`ï¼ ## 1. åæ´æ¦è§ æ¬æ¬¡ä¸º **å ¼å®¹å¼å级**ï¼æ¥å£ URLã请æ±åæ°ãè¿ååæ®µä¿æä¸åã 主è¦åæ´æ¯é¦é¡µè´¢å¡æ°æ®ä»å ä½/åæåé»è¾ï¼åæ¢ä¸ºæè´¢å¡ç宿°æ®å£å¾è®¡ç®ã æ¶åæ¥å£ï¼ 1. `GET /home/statisticsReceivablePayable` 2. `GET /home/monthlyIncome` 3. `GET /home/monthlyExpenditure` ## 2. åæ°è¯´æï¼æ æ°å¢ï¼ ### 2.1 `GET /home/statisticsReceivablePayable` - `type`ï¼`1` æ¬å¨ï¼`2` æ¬æï¼`3` æ¬å£åº¦ï¼é»è®¤ `1`ï¼ ### 2.2 `GET /home/monthlyIncome` - æ åæ° ### 2.3 `GET /home/monthlyExpenditure` - æ åæ° ## 3. è¿ååæ®µå£å¾åæ´ ### 3.1 åºæ¶åºä»ç»è®¡ `statisticsReceivablePayable` è¿ååæ®µä¸åï¼ - `receivableMoney` - `payableMoney` - `advanceMoney` - `prepayMoney` æ°å£å¾ï¼ - `receivableMoney = max(éå®ååéé¢å计 - æ¶æ¬¾éé¢å计, 0)` - `payableMoney = max(éè´ååéé¢å计 - 仿¬¾éé¢å计, 0)` - `advanceMoney = æ¶æ¬¾éé¢å计` - `prepayMoney = 仿¬¾éé¢å计` 以ä¸éé¢åæ `type` å¯¹åºæ¶é´èå´ç»è®¡ï¼ä¿ç两ä½å°æ°ã è¿å示ä¾ï¼ ```json { "receivableMoney": 128000.00, "payableMoney": 76000.00, "advanceMoney": 42000.00, "prepayMoney": 31000.00 } ``` ### 3.2 æåº¦æ¶å ¥ `monthlyIncome` è¿ååæ®µä¸åï¼ - `monthlyIncome` - `collectionRate` - `overdueNum` - `overdueRate` æ°å£å¾ï¼ - `monthlyIncome`ï¼å½ææ¶æ¬¾å计 - `collectionRate`ï¼`å½ææ¶æ¬¾å计 / 彿éå®ååéé¢å计 * 100` - `overdueNum`ï¼åå²åºæ¶å¯¹è´¦åï¼`account_statement.account_type=1`ï¼ä¸ï¼æ©äºå½æä¸ `closing_balance > 0` çæ°é - `overdueRate`ï¼`overdueNum / åå²åºæ¶å¯¹è´¦åæ»æ° * 100` è¿å示ä¾ï¼ ```json { "monthlyIncome": 89500.00, "collectionRate": "62.80", "overdueNum": 4, "overdueRate": "18.18" } ``` ### 3.3 æåº¦æ¯åº `monthlyExpenditure` è¿ååæ®µä¸åï¼ - `monthlyExpenditure` - `paymentRate` - `grossProfit` - `profitMarginRate` æ°å£å¾ï¼ - `monthlyExpenditure`ï¼å½æä»æ¬¾å计 - `paymentRate`ï¼`彿仿¬¾å计 / 彿éè´ååéé¢å计 * 100` - `grossProfit`ï¼`å½ææ¶æ¬¾å计 - 彿仿¬¾å计` - `profitMarginRate`ï¼`grossProfit / å½ææ¶æ¬¾å计 * 100` è¿å示ä¾ï¼ ```json { "monthlyExpenditure": 73400.00, "paymentRate": "57.34", "grossProfit": 16100.00, "profitMarginRate": "17.99" } ``` ## 4. å端èè°è¯´æ 1. åç«¯åæ®µæ å°æ éè°æ´ï¼å¯ç´æ¥æ²¿ç¨ç°æè§£æé»è¾ã 2. ç¾åæ¯å段ä»ä¸ºä¸å¸¦ `%` çå符串ï¼å端å¦éå±ç¤º `%` 请继ç»åç«¯æ¼æ¥ã 3. æ¬æ¬¡å端è¿åå¼ç±çå®è´¢å¡æ°æ®é©±å¨ï¼å»ºè®®éç¹åå½å¡çæ±æ»ä¸è¶å¿å¾çæ°å¼èå¨ã src/main/java/com/ruoyi/device/service/impl/DeviceLedgerServiceImpl.java
@@ -116,7 +116,10 @@ deviceLedger.setTaxIncludingPriceTotal(c.getTaxIncludingPriceUnit()); deviceLedger.setNumber(BigDecimal.ONE); deviceLedger.setPlanRuntimeTime(DateUtils.toLocalDate(c.getPlanRuntimeTime())); deviceLedger.setUnTaxIncludingPriceTotal(deviceLedger.getTaxIncludingPriceTotal().divide(BigDecimal.ONE.add(c.getTaxRate()),2, RoundingMode.HALF_UP)); // 计ç®ä¸å«ç¨æ»ä»·ï¼å¤çç©ºå¼æ åµ if (deviceLedger.getTaxIncludingPriceTotal() != null && c.getTaxRate() != null) { deviceLedger.setUnTaxIncludingPriceTotal(deviceLedger.getTaxIncludingPriceTotal().divide(BigDecimal.ONE.add(c.getTaxRate()), 2, RoundingMode.HALF_UP)); } deviceLedgerMapper.insert(deviceLedger); }); src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -3,8 +3,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import com.ruoyi.account.mapper.AccountStatementMapper; import com.ruoyi.account.mapper.purchase.AccountPurchasePaymentMapper; import com.ruoyi.account.mapper.sales.AccountSalesCollectionMapper; import com.ruoyi.account.pojo.AccountStatement; import com.ruoyi.account.pojo.purchase.AccountPurchasePayment; import com.ruoyi.account.pojo.sales.AccountSalesCollection; import com.ruoyi.approve.mapper.ApproveProcessMapper; import com.ruoyi.approve.pojo.ApproveProcess; import com.ruoyi.basic.mapper.CustomerMapper; @@ -104,6 +108,7 @@ private final AccountPurchasePaymentMapper accountPurchasePaymentMapper; private final AccountSalesCollectionMapper accountSalesCollectionMapper; private final AccountStatementMapper accountStatementMapper; private final ProductionAccountMapper productionAccountMapper; @@ -394,43 +399,21 @@ */ @Override public StatisticsReceivablePayableDto statisticsReceivablePayable(Integer type) { StatisticsReceivablePayableDto statisticsReceivablePayableDto = new StatisticsReceivablePayableDto(); LocalDate today = LocalDate.now(); LocalDate startDate = null; LocalDate endDate = null; switch (type) { case 1: // è·åæ¬å¨å¨ä¸ startDate = today.with(DayOfWeek.MONDAY); // è·åæ¬å¨å¨æ¥ endDate = today.with(DayOfWeek.SUNDAY); break; case 2: startDate = today.with(TemporalAdjusters.firstDayOfMonth()); endDate = today.with(TemporalAdjusters.lastDayOfMonth()); break; case 3: Month currentMonth = today.getMonth(); Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter(); Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2); StatisticsReceivablePayableDto dto = new StatisticsReceivablePayableDto(); LocalDate[] range = resolveFinanceRange(type); LocalDate startDate = range[0]; LocalDate endDate = range[1]; startDate = today.withMonth(firstMonthOfQuarter.getValue()) .with(TemporalAdjusters.firstDayOfMonth()); endDate = today.withMonth(lastMonthOfQuarter.getValue()) .with(TemporalAdjusters.lastDayOfMonth()); break; } // åºæ¶ BigDecimal receivableBase = sumSalesContractAmount(startDate, endDate); BigDecimal payableBase = sumPurchaseContractAmount(startDate, endDate); BigDecimal advanceMoney = sumCollectionAmount(startDate, endDate); BigDecimal prepayMoney = sumPaymentAmount(startDate, endDate); // åºä» // 颿¶ // é¢ä» return statisticsReceivablePayableDto; dto.setReceivableMoney(scaleMoney(maxZero(receivableBase.subtract(advanceMoney)))); dto.setPayableMoney(scaleMoney(maxZero(payableBase.subtract(prepayMoney)))); dto.setAdvanceMoney(scaleMoney(advanceMoney)); dto.setPrepayMoney(scaleMoney(prepayMoney)); return dto; } public static <T> BigDecimal sumAmount(List<T> list, java.util.function.Function<T, BigDecimal> amountExtractor) { @@ -1239,102 +1222,58 @@ @Override public MonthlyIncomeDto monthlyIncome() { MonthlyIncomeDto dto = new MonthlyIncomeDto(); LocalDate now = LocalDate.now(); YearMonth currentMonth = YearMonth.from(now); LocalDateTime startOfMonth = currentMonth.atDay(1).atStartOfDay(); LocalDateTime endOfMonth = currentMonth.atEndOfMonth().atTime(23, 59, 59); LocalDate today = LocalDate.now(); YearMonth currentMonth = YearMonth.from(today); LocalDate startDate = currentMonth.atDay(1); LocalDate endDate = currentMonth.atEndOfMonth(); LambdaQueryWrapper<SalesLedgerProduct> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(SalesLedgerProduct::getType, 1); wrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth); wrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth); BigDecimal monthlyIncome = sumCollectionAmount(startDate, endDate); BigDecimal receivableBase = sumSalesContractAmount(startDate, endDate); String collectionRate = toRateString(monthlyIncome, receivableBase); List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(wrapper); String currentMonthText = currentMonth.toString(); List<AccountStatement> receivableStatements = defaultList(accountStatementMapper.selectList( new LambdaQueryWrapper<AccountStatement>() .eq(AccountStatement::getAccountType, 1) .le(AccountStatement::getStatementMonth, currentMonthText))); if (CollectionUtils.isEmpty(products)) { return dto; } long overdueNum = receivableStatements.stream() .filter(item -> item.getStatementMonth() != null && item.getStatementMonth().compareTo(currentMonthText) < 0) .filter(item -> defaultDecimal(item.getClosingBalance()).compareTo(BigDecimal.ZERO) > 0) .count(); long totalReceivableCount = receivableStatements.size(); String overdueRate = totalReceivableCount == 0 ? "0.00" : BigDecimal.valueOf(overdueNum) .multiply(BigDecimal.valueOf(100)) .divide(BigDecimal.valueOf(totalReceivableCount), 2, RoundingMode.HALF_UP) .toString(); dto.setMonthlyIncome(scaleMoney(monthlyIncome)); dto.setCollectionRate(collectionRate); dto.setOverdueNum(BigDecimal.valueOf(overdueNum)); dto.setOverdueRate(overdueRate); return dto; } @Override public MonthlyExpenditureDto monthlyExpenditure() { MonthlyExpenditureDto dto = new MonthlyExpenditureDto(); LocalDate today = LocalDate.now(); YearMonth currentMonth = YearMonth.from(today); LocalDate startDate = currentMonth.atDay(1); LocalDate endDate = currentMonth.atEndOfMonth(); // 彿æ¶é´èå´ LocalDate now = LocalDate.now(); YearMonth currentMonth = YearMonth.from(now); LocalDateTime startOfMonth = currentMonth.atDay(1).atStartOfDay(); LocalDateTime endOfMonth = currentMonth.atEndOfMonth().atTime(23, 59, 59); BigDecimal monthlyExpenditure = sumPaymentAmount(startDate, endDate); BigDecimal payableBase = sumPurchaseContractAmount(startDate, endDate); BigDecimal monthlyIncome = sumCollectionAmount(startDate, endDate); BigDecimal grossProfit = monthlyIncome.subtract(monthlyExpenditure); // éè´å°è´¦ï¼type = 2ï¼ LambdaQueryWrapper<SalesLedgerProduct> purchaseWrapper = new LambdaQueryWrapper<>(); purchaseWrapper.eq(SalesLedgerProduct::getType, 2); purchaseWrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth); purchaseWrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth); List<SalesLedgerProduct> purchaseProducts = salesLedgerProductMapper.selectList(purchaseWrapper); BigDecimal rawMaterialCost = BigDecimal.ZERO; // åææææ¬ BigDecimal paidAmount = BigDecimal.ZERO; // 已仿¬¾éé¢ BigDecimal pendingAmount = BigDecimal.ZERO; // å¾ ä»æ¬¾éé¢ if (!CollectionUtils.isEmpty(purchaseProducts)) { for (SalesLedgerProduct p : purchaseProducts) { if (p.getTaxInclusiveTotalPrice() != null) { rawMaterialCost = rawMaterialCost.add(p.getTaxInclusiveTotalPrice()); } } } // å ¶ä»è´¹ç¨ // æåº¦æ»æ¯åº BigDecimal monthlyExpenditure = BigDecimal.ZERO; dto.setMonthlyExpenditure(monthlyExpenditure); // 已仿¬¾ ÷ï¼å·²ä»æ¬¾ + å¾ ä»æ¬¾ï¼ BigDecimal totalPayable = paidAmount.add(pendingAmount); if (totalPayable.compareTo(BigDecimal.ZERO) > 0) { String paymentRate = paidAmount .divide(totalPayable, 4, RoundingMode.HALF_UP) .multiply(BigDecimal.valueOf(100)) .setScale(2, RoundingMode.HALF_UP) .toString(); dto.setPaymentRate(paymentRate); } // å®å°è´¦ï¼type = 1ï¼ LambdaQueryWrapper<SalesLedgerProduct> salesWrapper = new LambdaQueryWrapper<>(); salesWrapper.eq(SalesLedgerProduct::getType, 1); salesWrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth); salesWrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth); List<SalesLedgerProduct> salesProducts = salesLedgerProductMapper.selectList(salesWrapper); BigDecimal revenue = BigDecimal.ZERO; // æ¯å©æ¶¦ & 婿¶¦ç if (revenue.compareTo(BigDecimal.ZERO) > 0) { // æ¯å©æ¶¦ = é宿¶å ¥ - åææææ¬ BigDecimal grossProfit = revenue.subtract(rawMaterialCost); dto.setGrossProfit(grossProfit); // 婿¶¦ç = (é宿¶å ¥ - æåº¦æ»æ¯åº) / é宿¶å ¥ BigDecimal profit = revenue.subtract(monthlyExpenditure); String profitMarginRate = profit .divide(revenue, 4, RoundingMode.HALF_UP) .multiply(BigDecimal.valueOf(100)) .setScale(2, RoundingMode.HALF_UP) .toString(); dto.setProfitMarginRate(profitMarginRate); } dto.setMonthlyExpenditure(scaleMoney(monthlyExpenditure)); dto.setPaymentRate(toRateString(monthlyExpenditure, payableBase)); dto.setGrossProfit(scaleMoney(grossProfit)); dto.setProfitMarginRate(toRateString(grossProfit, monthlyIncome)); return dto; } @@ -2331,6 +2270,97 @@ return productionOperationTaskMapper.calculateProductionStatistics(startDateTime, endDateTime, userId, processIds); } private LocalDate[] resolveFinanceRange(Integer type) { LocalDate today = LocalDate.now(); int safeType = type == null ? 1 : type; LocalDate startDate; LocalDate endDate; switch (safeType) { case 1: startDate = today.with(DayOfWeek.MONDAY); endDate = today.with(DayOfWeek.SUNDAY); break; case 2: startDate = today.with(TemporalAdjusters.firstDayOfMonth()); endDate = today.with(TemporalAdjusters.lastDayOfMonth()); break; case 3: Month firstMonthOfQuarter = today.getMonth().firstMonthOfQuarter(); startDate = LocalDate.of(today.getYear(), firstMonthOfQuarter, 1); endDate = startDate.plusMonths(2).with(TemporalAdjusters.lastDayOfMonth()); break; default: startDate = today.with(DayOfWeek.MONDAY); endDate = today.with(DayOfWeek.SUNDAY); break; } return new LocalDate[]{startDate, endDate}; } private BigDecimal sumSalesContractAmount(LocalDate startDate, LocalDate endDate) { List<SalesLedger> salesLedgers = defaultList(salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>() .ge(SalesLedger::getEntryDate, toDate(startDate)) .lt(SalesLedger::getEntryDate, toExclusiveEndDate(endDate)))); return sumAmount(salesLedgers, SalesLedger::getContractAmount); } private BigDecimal sumPurchaseContractAmount(LocalDate startDate, LocalDate endDate) { List<PurchaseLedger> purchaseLedgers = defaultList(purchaseLedgerMapper.selectList(new LambdaQueryWrapper<PurchaseLedger>() .ge(PurchaseLedger::getEntryDate, toDate(startDate)) .lt(PurchaseLedger::getEntryDate, toExclusiveEndDate(endDate)))); return sumAmount(purchaseLedgers, PurchaseLedger::getContractAmount); } private BigDecimal sumCollectionAmount(LocalDate startDate, LocalDate endDate) { List<AccountSalesCollection> collections = defaultList(accountSalesCollectionMapper.selectList( new LambdaQueryWrapper<AccountSalesCollection>() .ge(AccountSalesCollection::getCollectionDate, startDate) .le(AccountSalesCollection::getCollectionDate, endDate))); return sumAmount(collections, AccountSalesCollection::getCollectionAmount); } private BigDecimal sumPaymentAmount(LocalDate startDate, LocalDate endDate) { List<AccountPurchasePayment> payments = defaultList(accountPurchasePaymentMapper.selectList( new LambdaQueryWrapper<AccountPurchasePayment>() .ge(AccountPurchasePayment::getPaymentDate, startDate) .le(AccountPurchasePayment::getPaymentDate, endDate))); return sumAmount(payments, AccountPurchasePayment::getPaymentAmount); } private Date toDate(LocalDate localDate) { return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); } private Date toExclusiveEndDate(LocalDate localDate) { return Date.from(localDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant()); } private String toRateString(BigDecimal numerator, BigDecimal denominator) { if (denominator == null || denominator.compareTo(BigDecimal.ZERO) <= 0) { return "0.00"; } return defaultDecimal(numerator) .divide(denominator, 4, RoundingMode.HALF_UP) .multiply(BigDecimal.valueOf(100)) .setScale(2, RoundingMode.HALF_UP) .toString(); } private BigDecimal maxZero(BigDecimal value) { if (value == null) { return BigDecimal.ZERO; } return value.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : value; } private BigDecimal scaleMoney(BigDecimal value) { return defaultDecimal(value).setScale(2, RoundingMode.HALF_UP); } private <T> List<T> defaultList(List<T> list) { return list == null ? List.of() : list; } private BigDecimal defaultDecimal(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
@@ -311,7 +311,7 @@ if (matchedOperation == null) { matchedOperation = insertRoutingOperationSnapshot(orderRouting.getId(), productionOrderId, desiredOperation); } else { updateRoutingOperationSnapshotIfNecessary(desiredOperation, orderRouting.getId(), productionOrderId, matchedOperation); updateRoutingOperationSnapshotIfNecessary(matchedOperation, orderRouting.getId(), productionOrderId, desiredOperation); } finalOperationList.add(matchedOperation); } @@ -382,17 +382,32 @@ Map<Long, ProductionBomStructure> structureById = structureList.stream() .filter(item -> item != null && item.getId() != null) .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left)); Map<String, ProductionBomStructure> uniqueOperationMap = new LinkedHashMap<>(); for (ProductionBomStructure bomStructure : structureList) { if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) { continue; // æå»ºç¶-åæ å°å ³ç³» Map<Long, List<ProductionBomStructure>> treeMap = buildParentChildMap(structureList); // 使ç¨ååºéåæå»ºæä½å表ï¼å ååç¶ï¼ç¡®ä¿å·¥èºè·¯çº¿é¡ºåºæ£ç¡®ï¼ // ä½¿ç¨æ·±åº¦ä½ä¸ºæåºä¾æ®çè¾ å©ç»æ Map<String, ProductionBomStructure> operationMap = new LinkedHashMap<>(); Map<String, Integer> depthMap = new HashMap<>(); buildOperationListPostOrderWithDepth(null, treeMap, operationMap, depthMap, structureById, rootProductModelId, 1); // ææ·±åº¦æåºï¼æ·±åº¦å¤§çæåé¢ List<Map.Entry<String, ProductionBomStructure>> sortedEntries = new ArrayList<>(operationMap.entrySet()); sortedEntries.sort((a, b) -> { int depthCompare = Integer.compare( depthMap.getOrDefault(b.getKey(), 0), depthMap.getOrDefault(a.getKey(), 0)); if (depthCompare != 0) { return depthCompare; } Long outputProductModelId = resolveOutputProductModelId(resolveOperationOutputNode(bomStructure, structureById), rootProductModelId); uniqueOperationMap.putIfAbsent(buildBomOperationDedupKey(bomStructure, outputProductModelId), bomStructure); } return 0; }); List<ProductionOrderRoutingOperation> desiredOperationList = new ArrayList<>(); int dragSort = 1; for (ProductionBomStructure bomStructure : uniqueOperationMap.values()) { for (Map.Entry<String, ProductionBomStructure> entry : sortedEntries) { ProductionBomStructure bomStructure = entry.getValue(); Long outputProductModelId = resolveOutputProductModelId(resolveOperationOutputNode(bomStructure, structureById), rootProductModelId); TechnologyOperation technologyOperation = getTechnologyOperation(bomStructure.getTechnologyOperationId()); ProductionOrderRoutingOperation routingOperation = new ProductionOrderRoutingOperation(); @@ -406,6 +421,127 @@ desiredOperationList.add(routingOperation); } return desiredOperationList; } private void buildOperationListPostOrderWithDepth(Long parentId, Map<Long, List<ProductionBomStructure>> treeMap, Map<String, ProductionBomStructure> operationMap, Map<String, Integer> depthMap, Map<Long, ProductionBomStructure> structureById, Long rootProductModelId, int currentDepth) { List<ProductionBomStructure> children = treeMap.get(parentId); if (children == null || children.isEmpty()) { return; } for (ProductionBomStructure child : children) { // å éå½å¤çåèç¹ buildOperationListPostOrderWithDepth(child.getId(), treeMap, operationMap, depthMap, structureById, rootProductModelId, currentDepth + 1); // åå¤çå½åèç¹ if (child.getTechnologyOperationId() != null) { Long outputProductModelId = resolveOutputProductModelId(resolveOperationOutputNode(child, structureById), rootProductModelId); String key = buildBomOperationDedupKey(child, outputProductModelId); // ä¿ç深度æå¤§çæä½ Integer existingDepth = depthMap.get(key); if (existingDepth == null || currentDepth > existingDepth) { operationMap.put(key, child); depthMap.put(key, currentDepth); } } } } private Map<Long, List<ProductionBomStructure>> buildParentChildMap(List<ProductionBomStructure> structureList) { Map<Long, List<ProductionBomStructure>> treeMap = new LinkedHashMap<>(); Map<Long, ProductionBomStructure> structureById = new HashMap<>(); // æå»ºç¶-åæ å°åIDæ å° for (ProductionBomStructure structure : structureList) { if (structure == null) continue; Long parentId = structure.getParentId(); treeMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(structure); if (structure.getId() != null) { structureById.put(structure.getId(), structure); } } // è®¡ç®æ¯ä¸ªèç¹ç深度ï¼ä»æ ¹èç¹å°å½åèç¹çè·ç¦»ï¼æ ¹èç¹æ·±åº¦ä¸º1ï¼ Map<Long, Integer> depthMap = new HashMap<>(); for (ProductionBomStructure structure : structureList) { if (structure == null || structure.getId() == null) continue; computeDepthFromRoot(structure.getId(), structureById, depthMap); } // 对æ¯ä¸ªç¶èç¹ä¸çåèç¹ææ·±åº¦ååºæåºï¼ææ·±å±çä¼å ï¼ for (Map.Entry<Long, List<ProductionBomStructure>> entry : treeMap.entrySet()) { List<ProductionBomStructure> children = entry.getValue(); children.sort((a, b) -> { // ä¼å ææ·±åº¦æåºï¼æ·±åº¦å¤§çæåé¢ï¼ææ·±å±ä¼å ï¼ int depthCompare = Integer.compare( depthMap.getOrDefault(b.getId(), 0), depthMap.getOrDefault(a.getId(), 0)); if (depthCompare != 0) { return depthCompare; } // 深度ç¸åæ¶æIDæåºä¿è¯ç¨³å®æ§ return Long.compare(a.getId(), b.getId()); }); } return treeMap; } /** * 计ç®èç¹æ·±åº¦ï¼ä»æ ¹èç¹å°å½åèç¹çè·ç¦»ï¼ * æ ¹èç¹æ·±åº¦ä¸º1ï¼æ¯åä¸ä¸å±æ·±åº¦å 1 */ private int computeDepthFromRoot(Long nodeId, Map<Long, ProductionBomStructure> structureById, Map<Long, Integer> depthMap) { if (depthMap.containsKey(nodeId)) { return depthMap.get(nodeId); } ProductionBomStructure structure = structureById.get(nodeId); if (structure == null) { depthMap.put(nodeId, 1); return 1; } Long parentId = structure.getParentId(); if (parentId == null || parentId == 0L) { // æ ¹èç¹æ·±åº¦ä¸º1 depthMap.put(nodeId, 1); return 1; } // åèç¹æ·±åº¦ = ç¶èç¹æ·±åº¦ + 1 int parentDepth = computeDepthFromRoot(parentId, structureById, depthMap); int depth = parentDepth + 1; depthMap.put(nodeId, depth); return depth; } private void buildOperationListPostOrder(Long parentId, Map<Long, List<ProductionBomStructure>> treeMap, Map<String, ProductionBomStructure> uniqueOperationMap, Map<Long, ProductionBomStructure> structureById, Long rootProductModelId) { List<ProductionBomStructure> children = treeMap.get(parentId); if (children == null || children.isEmpty()) { return; } for (ProductionBomStructure child : children) { // å éå½å¤çåèç¹ buildOperationListPostOrder(child.getId(), treeMap, uniqueOperationMap, structureById, rootProductModelId); // åå¤çå½åèç¹ if (child.getTechnologyOperationId() != null) { Long outputProductModelId = resolveOutputProductModelId(resolveOperationOutputNode(child, structureById), rootProductModelId); String key = buildBomOperationDedupKey(child, outputProductModelId); // å»éæ¶ä¿ç深度æå¤§çæä½ï¼ååºéåå éå°æ·±å±èç¹ï¼æä»¥ç´æ¥è¦çå³å¯ï¼ uniqueOperationMap.put(key, child); } } } private Map<String, Deque<ProductionOrderRoutingOperation>> buildExistingRoutingOperationBucketMap(List<ProductionOrderRoutingOperation> existingOperationList) { @@ -572,7 +708,7 @@ return; } if (defaultDecimal(task.getCompleteQuantity()).compareTo(BigDecimal.ZERO) > 0) { throw new ServiceException("å·¥åºå·²äº§çæ¥å·¥è®°å½ï¼æ æ³æ ¹æ® BOM åæ´å é¤å¯¹åºå·¥åºå¿«ç §"); throw new ServiceException("å·¥åºå·²äº§çæ¥å·¥è®°å½ï¼æ æ³æ ¹æ® BOM åæ´å é¤å¯¹åºå·¥åºå¿«ç §" + task.getWorkOrderNo()); } long reportCount = productionProductMainMapper.selectCount( Wrappers.<ProductionProductMain>lambdaQuery() src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -401,7 +401,7 @@ productionAccount.setSchedulingUserId(user == null ? null : user.getUserId()); productionAccount.setSchedulingUserName(user == null ? dto.getUserName() : user.getNickName()); productionAccount.setFinishedNum(productQty); productionAccount.setWorkHours(workHours); productionAccount.setWorkHours(technologyOperation.getSalaryQuota()); productionAccount.setTechnologyOperationName(technologyOperation == null ? null : technologyOperation.getName()); productionAccount.setSchedulingDate(LocalDateTime.now()); productionAccountMapper.insert(productionAccount); src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java
@@ -80,21 +80,7 @@ stockInRecordDto.setProductModelId(stockUninventoryDto.getProductModelId()); stockInRecordDto.setType("1"); stockInRecordService.add(stockInRecordDto); //åè¿è¡æ°å¢åºåæ°éåºå //å æ¥è¯¢åºå表ä¸çäº§åæ¯å¦åå¨ï¼ä¸å卿°å¢ï¼å卿´æ° StockUninventory oldStockUnInventory = stockUninventoryMapper.selectOne(wrapper); if (ObjectUtils.isEmpty(oldStockUnInventory)) { StockUninventory newStockUnInventory = new StockUninventory(); newStockUnInventory.setProductModelId(stockUninventoryDto.getProductModelId()); newStockUnInventory.setQualitity(stockUninventoryDto.getQualitity()); newStockUnInventory.setLockedQuantity(stockUninventoryDto.getLockedQuantity()); newStockUnInventory.setBatchNo(stockUninventoryDto.getBatchNo()); newStockUnInventory.setVersion(1); newStockUnInventory.setRemark(stockUninventoryDto.getRemark()); stockUninventoryMapper.insert(newStockUnInventory); }else { stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto); } //审æ¹åæ·»å return 1; } src/main/resources/mapper/account/purchase/AccountPaymentApplicationMapper.xml
@@ -89,7 +89,7 @@ <select id="existsByStockInRecordId" resultType="java.lang.Boolean"> SELECT COUNT(*) > 0 FROM account_payment_application WHERE WHERE status != 2 <foreach collection="stockInRecordIds" item="id" open="(" separator=" OR " close=")"> FIND_IN_SET(#{id}, stock_in_record_ids) </foreach> src/main/resources/mapper/account/sales/AccountInvoiceApplicationMapper.xml
@@ -64,7 +64,7 @@ <select id="existsByStockOutRecordId" resultType="java.lang.Boolean"> SELECT COUNT(*) > 0 FROM account_invoice_application WHERE WHERE status!=2 <foreach collection="stockOutRecordIds" item="id" open="(" separator=" OR " close=")"> FIND_IN_SET(#{id}, stock_out_record_ids) </foreach> src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -512,8 +512,9 @@ and (si.qualitity - ifnull(si.locked_quantity, 0)) > 0 order by si.product_model_id, si.batch_no </select> <select id="getByModelId" resultType="com.ruoyi.stock.pojo.StockInventory"> select si.id, si.batch_no, si.locked_quantity, (si.qualitity - IFNULL(sd.qualitity, 0)) as qualitity <select id="getByModelId" resultType="com.ruoyi.stock.dto.StockInventoryDto"> select si.id, si.batch_no, si.locked_quantity, (si.qualitity - IFNULL(sd.qualitity, 0)) as qualitity, p.product_name, pm.model, pm.unit from stock_inventory si left join ( select spd.stock_inventory_id, sum(spd.quantity) as qualitity @@ -530,6 +531,8 @@ ) group by spd.stock_inventory_id ) as sd on sd.stock_inventory_id = si.id left join product_model pm on si.product_model_id = pm.id left join product p on pm.product_id = p.id where si.product_model_id = #{productModelId} and si.qualitity > IFNULL(sd.qualitity, 0) </select>