diff --git a/firstleg/src/main/java/com/baiyue/firstleg/client/PrintPdfClient.java b/firstleg/src/main/java/com/baiyue/firstleg/client/PrintPdfClient.java index 8eaf88622..4fbe1ea09 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/client/PrintPdfClient.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/client/PrintPdfClient.java @@ -55,7 +55,6 @@ public interface PrintPdfClient { String downloadFileName, List documents) {} record DropshipPalletDocument(String palletNo, String destinationWarehouse, - int palletSequence, int totalPallets, String barcodeContent, List inboundRows, List skuRows) {} diff --git a/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformDropshipPalletizationController.java b/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformDropshipPalletizationController.java index a9569169a..52b93c7b7 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformDropshipPalletizationController.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformDropshipPalletizationController.java @@ -150,6 +150,17 @@ public class PlatformDropshipPalletizationController { return ApiResponse.success(service.inboundPallets(accountId, inboundId)); } + @GetMapping("/inbound-orders/{inboundId}/pallets/print") + @PreAuthorize("hasAuthority('platform:firstleg:inbound:list')") + public ResponseEntity printInboundPallets(@AuthenticationPrincipal AuthenticatedUser user, + @PathVariable @Positive long inboundId, + @RequestParam @Positive long accountId, + @RequestHeader(value = "X-Request-Id", required = false) String requestId) { + FirstlegScopeGuard.platform(user, accountId); + return printService.generateDropshipPallets( + service.inboundPallets(accountId, inboundId), service.inboundCode(accountId, inboundId), requestId); + } + @GetMapping("/inbound-orders/{inboundId}/pallets/{palletId}/print") @PreAuthorize("hasAuthority('platform:firstleg:inbound:list')") public ResponseEntity printPallet(@AuthenticationPrincipal AuthenticatedUser user, diff --git a/firstleg/src/main/java/com/baiyue/firstleg/mapper/DropshipPalletizationMapper.java b/firstleg/src/main/java/com/baiyue/firstleg/mapper/DropshipPalletizationMapper.java index 6bcaff395..3376c9a59 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/mapper/DropshipPalletizationMapper.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/mapper/DropshipPalletizationMapper.java @@ -254,15 +254,6 @@ public interface DropshipPalletizationMapper { """) List printSkuRows(@Param("palletId") long palletId); - @Select(""" - SELECT (SELECT COUNT(*) FROM fl_dropship_pallet listed - WHERE listed.task_id=target.task_id AND listed.id<=target.id) sequence_no, - (SELECT COUNT(*) FROM fl_dropship_pallet listed - WHERE listed.task_id=target.task_id) total - FROM fl_dropship_pallet target - WHERE target.id=#{palletId} - """) - PalletPosition palletPosition(@Param("palletId") long palletId); @Select(""" SELECT p.id pallet_id,p.pallet_no,'SEALED_PALLET' unit_type,TRUE indivisible,COUNT(DISTINCT c.id) carton_count, (SELECT COUNT(DISTINCT sku.sku_code) FROM fl_dropship_pallet_carton pc @@ -306,6 +297,5 @@ public interface DropshipPalletizationMapper { record PalletMetrics(BigDecimal weightKg,BigDecimal volumeCbm) {} @Getter @Setter class PrintInboundRow { private Long tenantId,inboundId; private String inboundCode,destinationWarehouse; private Integer cartonCount,itemQuantity; } @Getter @Setter class PrintSkuRow { private String skuCode; private Integer cartonCount,itemQuantity,minUnitsPerCarton,maxUnitsPerCarton; } - record PalletPosition(int sequenceNo,int total) {} @Getter @Setter class PalletLine { private Long palletId,tenantId,accountId,inboundId,orderId,warehouseId,destinationWarehouseId,inboundDetailId; private String palletNo,palletStatus,orderNo,inboundCode,channelCode,channelName,destinationWarehouseCode,destinationWarehouseName,customerName,skuCode,cargoName; private Integer cartonCount,actualSkuQuantity; private BigDecimal unitGrossWeightKg,unitNetWeightKg,unitVolumeCbm; } } diff --git a/firstleg/src/main/java/com/baiyue/firstleg/service/InboundCartonLabelPrintService.java b/firstleg/src/main/java/com/baiyue/firstleg/service/InboundCartonLabelPrintService.java index b959c9aac..3c20b1fd9 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/service/InboundCartonLabelPrintService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/InboundCartonLabelPrintService.java @@ -172,12 +172,8 @@ public class InboundCartonLabelPrintService { .stream().map(row -> new PrintPdfClient.DropshipPalletSkuRow( row.getSkuCode(), number(row.getCartonCount()), number(row.getItemQuantity()), unitsPerCarton(row))).toList(); - DropshipPalletizationMapper.PalletPosition position = - palletizationMapper.palletPosition(pallet.palletId()); return new PrintPdfClient.DropshipPalletDocument( - pallet.palletNo(), destinationWarehouse, - position == null ? 1 : position.sequenceNo(), position == null ? 1 : position.total(), - pallet.palletNo(), inboundRows, skuRows); + pallet.palletNo(), destinationWarehouse, pallet.palletNo(), inboundRows, skuRows); } private String unitsPerCarton(DropshipPalletizationMapper.PrintSkuRow row) { diff --git a/firstleg/src/main/resources/openapi/dropship-palletization-v1.yaml b/firstleg/src/main/resources/openapi/dropship-palletization-v1.yaml index d2011e866..4d9956aa4 100644 --- a/firstleg/src/main/resources/openapi/dropship-palletization-v1.yaml +++ b/firstleg/src/main/resources/openapi/dropship-palletization-v1.yaml @@ -68,7 +68,7 @@ paths: /api/platform/v1/first-leg/pda-dropship/tasks/{taskId}/pallets/{palletId}/print: get: summary: PDA 获取预生成托盘码的 A4 托盘信息单 - description: 测量审核通过并生成托盘码后即可获取;每个托盘固定一页 A4,包含托盘号及其 Code 128 一维码、目的仓、托盘序号、入仓码/客户昵称/箱数/件数汇总以及 SKU/箱数/件数/每箱件数汇总,不展示作业状态、重量或体积。空托显示暂无已归托 SKU。仅当前仓库、当前操作员可访问。 + description: 测量审核通过并生成托盘码后即可获取;每个托盘固定一页 A4,包含托盘号及其 Code 128 一维码、目的仓、入仓码/客户昵称/箱数/件数汇总以及 SKU/箱数/件数/每箱件数汇总,不展示托盘序号、作业状态、重量或体积。空托显示暂无已归托 SKU。仅当前仓库、当前操作员可访问。 x-required-authority: platform:firstleg:pda:view parameters: - {$ref: '#/components/parameters/TaskId'} @@ -89,6 +89,13 @@ paths: summary: Admin 查询入仓单权威托盘结果 x-required-authority: platform:firstleg:inbound:list responses: {'200': {description: OK}} + /api/platform/v1/first-leg/pda-dropship/inbound-orders/{inboundId}/pallets/print: + get: + summary: Admin 合并打印入仓单关联的全部一件代发托盘 + description: 测量审核预生成托盘码后即可打印;每托一页,空托也保留托盘码和当前入仓单信息,供电脑连接打印机后在 PDA 作业前贴码。 + x-required-authority: platform:firstleg:inbound:list + responses: + '200': {description: 多页 A4 托盘信息单 PDF, content: {application/pdf: {schema: {type: string, format: binary}}}} /api/platform/v1/first-leg/pda-dropship/inbound-orders/{inboundId}/pallets/{palletId}/print: get: summary: 获取包含托盘码、目的仓、入仓码和 SKU 汇总的 A4 托盘信息单 diff --git a/firstleg/src/test/java/com/baiyue/firstleg/repository/DropshipPalletizationSqlContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/repository/DropshipPalletizationSqlContractTest.java index fe1bf0f99..8c4365b28 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/repository/DropshipPalletizationSqlContractTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/repository/DropshipPalletizationSqlContractTest.java @@ -16,7 +16,8 @@ class DropshipPalletizationSqlContractTest { void inboundPalletReadAndPrintUseTheRegisteredPlatformInboundPermission() throws Exception { var controller = com.baiyue.firstleg.controller.PlatformDropshipPalletizationController.class; for (var method : controller.getDeclaredMethods()) { - if (method.getName().equals("inboundPallets") || method.getName().equals("printPallet")) { + if (method.getName().equals("inboundPallets") || method.getName().equals("printPallet") + || method.getName().equals("printInboundPallets")) { assertThat(method.getAnnotation(org.springframework.security.access.prepost.PreAuthorize.class).value()) .isEqualTo("hasAuthority('platform:firstleg:inbound:list')"); } @@ -24,6 +25,7 @@ class DropshipPalletizationSqlContractTest { String api = new ClassPathResource("openapi/dropship-palletization-v1.yaml") .getContentAsString(StandardCharsets.UTF_8); assertThat(api).contains("x-required-authority: platform:firstleg:inbound:list") + .contains("/api/platform/v1/first-leg/pda-dropship/inbound-orders/{inboundId}/pallets/print:") .doesNotContain("x-required-authority: firstleg:inbound:list"); } diff --git a/firstleg/src/test/java/com/baiyue/firstleg/service/InboundCartonLabelPrintServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/service/InboundCartonLabelPrintServiceTest.java index 2d6c609de..43dfe4d4b 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/InboundCartonLabelPrintServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/InboundCartonLabelPrintServiceTest.java @@ -45,7 +45,7 @@ class InboundCartonLabelPrintServiceTest { @Test void dropshipPalletLabelIsAvailableBeforeSeal() { Pallet open = dropshipPallet("OPEN"); - stubPalletFacts(90001L, 1, 2); + stubPalletFacts(90001L); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_PDF); when(print.generateDropshipPalletA4Documents(org.mockito.ArgumentMatchers.anyString(), @@ -66,8 +66,6 @@ class InboundCartonLabelPrintServiceTest { assertThat(request.getValue().documents()).singleElement().satisfies(document -> { assertThat(document.palletNo()).isEqualTo("DP-260911-0001"); assertThat(document.destinationWarehouse()).isEqualTo("NJDF"); - assertThat(document.palletSequence()).isEqualTo(1); - assertThat(document.totalPallets()).isEqualTo(2); assertThat(document.inboundRows()).singleElement().satisfies(row -> { assertThat(row.inboundCode()).isEqualTo("PCHK2608040174"); assertThat(row.customerNickname()).isEqualTo("小虫科技"); @@ -88,8 +86,8 @@ class InboundCartonLabelPrintServiceTest { Pallet first = dropshipPallet("OPEN"); Pallet second = new Pallet(90002L, "DP-260911-0002", "OPEN", 0, 0, 0, BigDecimal.ZERO, BigDecimal.ZERO, 1, List.of(), List.of(), null); - stubPalletFacts(90001L, 1, 2); - stubPalletFacts(90002L, 2, 2); + stubPalletFacts(90001L); + stubPalletFacts(90002L); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_PDF); when(print.generateDropshipPalletA4Documents(org.mockito.ArgumentMatchers.anyString(), @@ -242,7 +240,7 @@ class InboundCartonLabelPrintServiceTest { BigDecimal.ONE, BigDecimal.ONE, 1, List.of(item), List.of(), null); } - private void stubPalletFacts(long palletId, int sequence, int total) { + private void stubPalletFacts(long palletId) { DropshipPalletizationMapper.PrintInboundRow inbound = new DropshipPalletizationMapper.PrintInboundRow(); inbound.setTenantId(10001L); @@ -260,8 +258,6 @@ class InboundCartonLabelPrintServiceTest { sku.setMaxUnitsPerCarton(19); when(palletization.printSkuRows(palletId)) .thenReturn(palletId == 90001L ? List.of(sku) : List.of()); - when(palletization.palletPosition(palletId)) - .thenReturn(new DropshipPalletizationMapper.PalletPosition(sequence, total)); when(customerSnapshots.customerNickname(10001L)).thenReturn("小虫科技"); } } diff --git a/print/README.md b/print/README.md index bc2e22e8e..031bb4aaf 100644 --- a/print/README.md +++ b/print/README.md @@ -8,7 +8,7 @@ - 服务发现名:`print-service`。 - 内部地址:`POST /internal/print/v1/pdfs/inbound-marks`。 -- 一件代发 A4 托盘信息单:`POST /internal/print/v1/pdfs/dropship-pallet-a4-documents`;模板固定为 `DROPSHIP_PALLET_A4_V2`,采用黑白打印版,每托一页 A4,包含托盘 Code 128 一维码、目的仓、托盘序号、最多 10 行入仓码/客户昵称汇总和最多 34 行 SKU 汇总,不展示作业状态、重量或体积。 +- 一件代发 A4 托盘信息单:`POST /internal/print/v1/pdfs/dropship-pallet-a4-documents`;模板固定为 `DROPSHIP_PALLET_A4_V2`,采用黑白打印版,每托一页 A4,包含托盘 Code 128 一维码、目的仓、最多 10 行入仓码/客户昵称汇总和最多 34 行 SKU 汇总,不展示托盘序号、作业状态、重量或体积。 - A4 海运单据内部地址:`POST /internal/print/v1/pdfs/freight-a4-documents`。 - 海外仓排车 BOL 内部地址:`POST /internal/print/v1/pdfs/dispatch-bol-documents`;模板固定为 `DISPATCH_BILL_OF_LADING_A4_V2`,每个 document 对应一张 A4、包含 1-26 行明细。调用方首张按 18 行、续页按 26 行稳定分页;同页连续相同柜号和托盘号由 Print 纵向合并显示。 - 本地直连:`http://127.0.0.1:9207`。 diff --git a/print/output/pdf/dropship-pallet-a4-v2-preview.pdf b/print/output/pdf/dropship-pallet-a4-v2-preview.pdf index 6cc08e92c..aa2a399dc 100644 Binary files a/print/output/pdf/dropship-pallet-a4-v2-preview.pdf and b/print/output/pdf/dropship-pallet-a4-v2-preview.pdf differ diff --git a/print/src/main/java/com/baiyue/print/dto/DropshipPalletA4PdfDtos.java b/print/src/main/java/com/baiyue/print/dto/DropshipPalletA4PdfDtos.java index fefa1eb33..bc5b9eb9d 100644 --- a/print/src/main/java/com/baiyue/print/dto/DropshipPalletA4PdfDtos.java +++ b/print/src/main/java/com/baiyue/print/dto/DropshipPalletA4PdfDtos.java @@ -15,7 +15,6 @@ public final class DropshipPalletA4PdfDtos { public record PageSpec(BigDecimal widthMm, BigDecimal heightMm) {} public record Document(String palletNo, String destinationWarehouse, - int palletSequence, int totalPallets, String barcodeContent, List inboundRows, List skuRows) {} diff --git a/print/src/main/java/com/baiyue/print/renderer/DropshipPalletA4Renderer.java b/print/src/main/java/com/baiyue/print/renderer/DropshipPalletA4Renderer.java index 1ff6f3f64..c12e0037e 100644 --- a/print/src/main/java/com/baiyue/print/renderer/DropshipPalletA4Renderer.java +++ b/print/src/main/java/com/baiyue/print/renderer/DropshipPalletA4Renderer.java @@ -35,9 +35,6 @@ public class DropshipPalletA4Renderer { stream.setLineWidth(PdfUnits.mmToPt(.25)); border(stream, units); colorText(stream, units, font, "一件代发备货托", 14, 18, 22, INK); - rightText(stream, units, font, - "第 " + data.palletSequence() + " 托 / 共 " + data.totalPallets() + " 托", - 10, 192, 22, new Color(75, 75, 75)); barcodeRenderer.draw(stream, barcodeMatrix, PdfUnits.mmToPt(18), units.topDownMmToPdfY(51), PdfUnits.mmToPt(80), PdfUnits.mmToPt(22)); @@ -157,12 +154,6 @@ public class DropshipPalletA4Renderer { colorText(s, u, f, fit(f, value, size, PdfUnits.mmToPt(widthMm)), size, x, y, color); } - private void rightText(PDPageContentStream s, PdfUnits u, PDFont f, String value, float size, - double right, double y, Color color) throws IOException { - double widthMm = f.getStringWidth(value) / 1000 * size / PdfUnits.mmToPt(1); - colorText(s, u, f, value, size, right - widthMm, y, color); - } - private void centeredCell(PDPageContentStream s, PdfUnits u, PDFont f, String value, float size, double x1, double x2, double y) throws IOException { String fitted = fit(f, value, size, PdfUnits.mmToPt(x2 - x1)); diff --git a/print/src/main/java/com/baiyue/print/service/DropshipPalletA4PdfService.java b/print/src/main/java/com/baiyue/print/service/DropshipPalletA4PdfService.java index 139baa94b..ca04d1565 100644 --- a/print/src/main/java/com/baiyue/print/service/DropshipPalletA4PdfService.java +++ b/print/src/main/java/com/baiyue/print/service/DropshipPalletA4PdfService.java @@ -68,8 +68,6 @@ public class DropshipPalletA4PdfService { private DropshipPalletA4PdfDtos.Document normalizeDocument(DropshipPalletA4PdfDtos.Document value, int index) { if (value == null) invalid("documents[" + index + "] is required"); - if (value.palletSequence() <= 0 || value.totalPallets() <= 0 - || value.palletSequence() > value.totalPallets()) invalid("documents[" + index + "] pallet position is invalid"); if (value.inboundRows() == null || value.inboundRows().isEmpty() || value.inboundRows().size() > MAX_INBOUND_ROWS) invalid("documents[" + index + "].inboundRows exceeds the one-page A4 capacity"); if (value.skuRows() == null || value.skuRows().size() > MAX_SKU_ROWS) invalid("documents[" + index + "].skuRows exceeds the one-page A4 capacity"); @@ -86,8 +84,8 @@ public class DropshipPalletA4PdfService { row.itemQuantity(), text(row.unitsPerCarton(), 30))); } return new DropshipPalletA4PdfDtos.Document(text(value.palletNo(), 80), - text(value.destinationWarehouse(), 200), value.palletSequence(), value.totalPallets(), - barcode(value.barcodeContent()), List.copyOf(inboundRows), List.copyOf(rows)); + text(value.destinationWarehouse(), 200), barcode(value.barcodeContent()), + List.copyOf(inboundRows), List.copyOf(rows)); } private String barcode(String value) { diff --git a/print/src/main/resources/openapi/print-internal-v1.yaml b/print/src/main/resources/openapi/print-internal-v1.yaml index c9c2adcb6..a0c53fbbc 100644 --- a/print/src/main/resources/openapi/print-internal-v1.yaml +++ b/print/src/main/resources/openapi/print-internal-v1.yaml @@ -290,12 +290,10 @@ components: items: { $ref: '#/components/schemas/DropshipPalletA4Document' } DropshipPalletA4Document: type: object - required: [palletNo, destinationWarehouse, palletSequence, totalPallets, barcodeContent, inboundRows, skuRows] + required: [palletNo, destinationWarehouse, barcodeContent, inboundRows, skuRows] properties: palletNo: { type: string, maxLength: 80 } destinationWarehouse: { type: string, maxLength: 200 } - palletSequence: { type: integer, minimum: 1 } - totalPallets: { type: integer, minimum: 1 } barcodeContent: type: string maxLength: 80 diff --git a/print/src/test/java/com/baiyue/print/controller/InternalPdfControllerTest.java b/print/src/test/java/com/baiyue/print/controller/InternalPdfControllerTest.java index a0e7e4e40..ec08c6df6 100644 --- a/print/src/test/java/com/baiyue/print/controller/InternalPdfControllerTest.java +++ b/print/src/test/java/com/baiyue/print/controller/InternalPdfControllerTest.java @@ -274,8 +274,6 @@ class InternalPdfControllerTest { "documents": [{ "palletNo": "DP-260911-0034", "destinationWarehouse": "NJDF", - "palletSequence": 1, - "totalPallets": 3, "barcodeContent": "DP-260911-0034", "inboundRows": [{ "inboundCode": "NYEXX2609110249", diff --git a/print/src/test/java/com/baiyue/print/service/DropshipPalletA4PdfServiceGenerationTest.java b/print/src/test/java/com/baiyue/print/service/DropshipPalletA4PdfServiceGenerationTest.java index cf2a311f8..9388c1bb6 100644 --- a/print/src/test/java/com/baiyue/print/service/DropshipPalletA4PdfServiceGenerationTest.java +++ b/print/src/test/java/com/baiyue/print/service/DropshipPalletA4PdfServiceGenerationTest.java @@ -27,7 +27,7 @@ class DropshipPalletA4PdfServiceGenerationTest { @Test void generatesOneA4PageWithPalletBarcodeAndSkuDetails() throws Exception { DropshipPalletA4PdfDtos.Document document = new DropshipPalletA4PdfDtos.Document( - "DP-260911-0034", "NJDF", 1, 3, "DP-260911-0034", + "DP-260911-0034", "NJDF", "DP-260911-0034", List.of(new DropshipPalletA4PdfDtos.InboundRow( "NYEXX2609110249", "小虫科技", 3, 90)), List.of( @@ -64,8 +64,7 @@ class DropshipPalletA4PdfServiceGenerationTest { "SKU-" + String.format("%03d", index), 2, index * 5, "5-10")); } DropshipPalletA4PdfDtos.Document document = new DropshipPalletA4PdfDtos.Document( - "DP-260911-0034", "NJDF", 1, 1, - "DP-260911-0034", inboundRows, rows); + "DP-260911-0034", "NJDF", "DP-260911-0034", inboundRows, rows); var generated = service.generate(new DropshipPalletA4PdfDtos.Request( DropshipPalletA4PdfDtos.TEMPLATE, new DropshipPalletA4PdfDtos.PageSpec(new BigDecimal("210"), new BigDecimal("297")), @@ -85,7 +84,7 @@ class DropshipPalletA4PdfServiceGenerationTest { rows.add(new DropshipPalletA4PdfDtos.SkuRow("SKU-" + index, 1, 1, "1")); } DropshipPalletA4PdfDtos.Document document = new DropshipPalletA4PdfDtos.Document( - "DP-260911-0034", "NJDF", 1, 1, "DP-260911-0034", + "DP-260911-0034", "NJDF", "DP-260911-0034", List.of(new DropshipPalletA4PdfDtos.InboundRow("INBOUND", "客户", 35, 35)), rows); assertThatThrownBy(() -> service.generate(new DropshipPalletA4PdfDtos.Request(