From 621128530a2de1543fc95549751f265c32e26797 Mon Sep 17 00:00:00 2001 From: caojia <1301869380@qq.com> Date: Thu, 12 Nov 2026 15:58:25 +0800 Subject: [PATCH 1/5] =?UTF-8?q?:zap:=20=E4=BC=98=E5=8C=96=E5=BA=94?= =?UTF-8?q?=E6=94=B6=E5=90=88=E8=AE=A1=E6=9F=A5=E8=AF=A2=E5=B9=B6=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E5=AF=BC=E5=87=BA=E6=B1=87=E6=80=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../receivable/ArBillController.java | 8 +- .../receivable/ArBillGeneratePreviewTest.java | 112 ++++++++++++++++++ .../ArBillGeneratePreviewExportRunner.java | 11 +- bms-front/src/api/ar/bill.js | 9 ++ .../ar/bill/components/GenerateBillDialog.vue | 42 +++++-- 5 files changed, 173 insertions(+), 9 deletions(-) create mode 100644 bms-back/bms-admin/src/test/java/com/bms/receivable/ArBillGeneratePreviewTest.java diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/receivable/ArBillController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/receivable/ArBillController.java index 8a5495c7..885c230b 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/receivable/ArBillController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/receivable/ArBillController.java @@ -173,10 +173,16 @@ public class ArBillController extends BaseController { AjaxResult result = success(); result.put("rows", pageData.getRows()); result.put("total", pageData.getTotal()); - result.put("totalAmount", billService.sumPreviewBill(query)); return result; } + /** 按需汇总生成预览范围内的应收金额。 */ + @PreAuthorize("@ss.hasPermi('ar:bill:generate')") + @PostMapping("/generate/preview/amount") + public AjaxResult previewAmount(@RequestBody ArBillGenerateQuery query) { + return success(billService.sumPreviewBill(query)); + } + /** 按生成预览使用的相同筛选条件提交异步导出任务。 */ @Log(title = "应收账单生成订单", businessType = BusinessType.EXPORT) @PreAuthorize("@ss.hasPermi('ar:bill:generate')") diff --git a/bms-back/bms-admin/src/test/java/com/bms/receivable/ArBillGeneratePreviewTest.java b/bms-back/bms-admin/src/test/java/com/bms/receivable/ArBillGeneratePreviewTest.java new file mode 100644 index 00000000..65820fc4 --- /dev/null +++ b/bms-back/bms-admin/src/test/java/com/bms/receivable/ArBillGeneratePreviewTest.java @@ -0,0 +1,112 @@ +package com.bms.receivable; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.InputStream; +import java.math.BigDecimal; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.test.util.ReflectionTestUtils; + +import com.github.pagehelper.PageHelper; +import com.bms.common.config.BmsConfig; +import com.bms.common.core.domain.AjaxResult; +import com.bms.common.core.redis.RedisCache; +import com.bms.price.domain.PrcOrderBill; +import com.bms.receivable.domain.vo.ArBillGenerateQuery; +import com.bms.receivable.security.ArBillScopeResolver; +import com.bms.receivable.service.IArBillService; +import com.bms.receivable.task.ArBillGeneratePreviewExportRunner; +import com.bms.web.controller.receivable.ArBillController; + +class ArBillGeneratePreviewTest { + @TempDir + Path tempDir; + + @Test + void previewDoesNotCalculateAmountUntilAmountEndpointIsCalled() { + IArBillService service = mock(IArBillService.class); + ArBillController controller = new ArBillController(); + ReflectionTestUtils.setField(controller, "billService", service); + ArBillGenerateQuery query = new ArBillGenerateQuery(); + when(service.previewBill(query)).thenReturn(List.of(new PrcOrderBill())); + when(service.sumPreviewBill(query)).thenReturn(new BigDecimal("12.34")); + + try { + AjaxResult preview = controller.preview(query); + assertFalse(preview.containsKey("totalAmount")); + verify(service, never()).sumPreviewBill(query); + } finally { + PageHelper.clearPage(); + } + + assertEquals(new BigDecimal("12.34"), controller.previewAmount(query).get("data")); + verify(service).sumPreviewBill(query); + } + + @Test + void exportEndsWithTheSumOfTheExportedAmounts() throws Exception { + String previousProfile = BmsConfig.getProfile(); + try { + new BmsConfig().setProfile(tempDir.toString()); + IArBillService service = mock(IArBillService.class); + ArBillGenerateQuery query = new ArBillGenerateQuery(); + when(service.previewBill(same(query), isNull())).thenReturn(List.of( + bill("A", "0.10"), bill("B", "0.20"), bill("C", "-0.05"), bill("D", null))); + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor() { + @Override + public void execute(Runnable task) { + task.run(); + } + }; + ArBillGeneratePreviewExportRunner runner = new ArBillGeneratePreviewExportRunner( + service, mock(RedisCache.class), mock(ArBillScopeResolver.class), executor); + for (String dateType : List.of("BIZ_DATE", "PICKUP_DATE")) { + query.setDateType(dateType); + String taskId = runner.submit(query, 1L); + Path file = tempDir.resolve("download/arBillGeneratePreviewExport/" + taskId + ".xlsx"); + + try (InputStream in = Files.newInputStream(file); Workbook workbook = WorkbookFactory.create(in)) { + Sheet sheet = workbook.getSheetAt(0); + Row header = sheet.getRow(0); + Row lastRow = sheet.getRow(sheet.getLastRowNum()); + assertEquals("合计", lastRow.getCell(column(header, "订单号")).getStringCellValue()); + assertEquals(0.25, lastRow.getCell(column(header, "应收金额")).getNumericCellValue(), 0.000001); + assertEquals(5, sheet.getLastRowNum()); + } + } + } finally { + new BmsConfig().setProfile(previousProfile); + } + } + + private static PrcOrderBill bill(String orderNo, String amount) { + PrcOrderBill bill = new PrcOrderBill(); + bill.setOrderNo(orderNo); + bill.setIncomeTotal(amount == null ? null : new BigDecimal(amount)); + return bill; + } + + private static int column(Row header, String name) { + for (int i = 0; i < header.getLastCellNum(); i++) { + if (name.equals(header.getCell(i).getStringCellValue())) return i; + } + throw new AssertionError("缺少导出列: " + name); + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/receivable/task/ArBillGeneratePreviewExportRunner.java b/bms-back/bms-system/src/main/java/com/bms/receivable/task/ArBillGeneratePreviewExportRunner.java index 39c8a12c..6b7ad2df 100644 --- a/bms-back/bms-system/src/main/java/com/bms/receivable/task/ArBillGeneratePreviewExportRunner.java +++ b/bms-back/bms-system/src/main/java/com/bms/receivable/task/ArBillGeneratePreviewExportRunner.java @@ -1,6 +1,7 @@ package com.bms.receivable.task; import java.io.OutputStream; +import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; @@ -75,10 +76,18 @@ public class ArBillGeneratePreviewExportRunner { put(taskId, userId, "RUNNING", 5, "正在查询订单", null, 0, fileName, null); List bills = billService.previewBill(query, scope); put(taskId, userId, "RUNNING", 40, "正在生成 Excel", null, bills.size(), fileName, null); - List rows = new ArrayList<>(bills.size()); + List rows = new ArrayList<>(bills.size() + 1); + BigDecimal totalAmount = BigDecimal.ZERO; for (PrcOrderBill bill : bills) { rows.add(ArBillGeneratePreviewExportRow.from(bill)); + if (bill.getIncomeTotal() != null) { + totalAmount = totalAmount.add(bill.getIncomeTotal()); + } } + ArBillGeneratePreviewExportRow totalRow = new ArBillGeneratePreviewExportRow(); + totalRow.setOrderNo("合计"); + totalRow.setIncomeTotal(totalAmount); + rows.add(totalRow); ExcelUtil util = new ExcelUtil<>(ArBillGeneratePreviewExportRow.class); if (ArBillDateType.PICKUP_DATE == ArBillDateType.of(query.getDateType())) { util.hideColumn("bizDate"); diff --git a/bms-front/src/api/ar/bill.js b/bms-front/src/api/ar/bill.js index e0ffc2f9..c0576f9a 100644 --- a/bms-front/src/api/ar/bill.js +++ b/bms-front/src/api/ar/bill.js @@ -58,6 +58,15 @@ export function previewArBill(query) { }) } +export function getArBillPreviewAmount(query) { + return request({ + url: '/ar/bill/generate/preview/amount', + method: 'post', + data: query, + timeout: 60000 + }) +} + export function previewExcludedArBill(query) { return request({ url: '/ar/bill/generate/exclusion-preview', diff --git a/bms-front/src/views/ar/bill/components/GenerateBillDialog.vue b/bms-front/src/views/ar/bill/components/GenerateBillDialog.vue index a0009cbd..3e143b91 100644 --- a/bms-front/src/views/ar/bill/components/GenerateBillDialog.vue +++ b/bms-front/src/views/ar/bill/components/GenerateBillDialog.vue @@ -41,7 +41,11 @@ 当前币种尚未配置公司默认收款账户
- 可选 {{ previewTotal }} 条订单,已选 {{ displaySelectedCount }} 条,应收合计 {{ money(displaySelectedAmount) }} + + 可选 {{ previewTotal }} 条订单,已选 {{ displaySelectedCount }} 条 + + 查询应收合计 + 请选择客户、币种和账单截止日后查询可生成的计费订单 导出查询结果 查询 @@ -104,7 +108,7 @@ @current-change="handlePreviewPageChange" /> @@ -163,7 +167,7 @@ -
已选择 {{ displaySelectedCount }} 条订单,应收合计 {{ money(displaySelectedAmount) }}
+
已选择 {{ displaySelectedCount }} 条订单