feat: support mixed-SKU dropship cartons
This commit is contained in:
@@ -4,9 +4,11 @@ import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.common.web.GlobalExceptionHandler;
|
||||
import com.baiyue.print.dto.FreightA4PdfDtos;
|
||||
import com.baiyue.print.dto.DropshipPalletA4PdfDtos;
|
||||
import com.baiyue.print.dto.InboundMarkPdfDtos;
|
||||
import com.baiyue.print.service.FreightA4PdfService;
|
||||
import com.baiyue.print.service.DispatchBolPdfService;
|
||||
import com.baiyue.print.service.DropshipPalletA4PdfService;
|
||||
import com.baiyue.print.service.InboundMarkPdfService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -45,6 +47,8 @@ class InternalPdfControllerTest {
|
||||
private FreightA4PdfService freightA4PdfService;
|
||||
@MockBean
|
||||
private DispatchBolPdfService dispatchBolPdfService;
|
||||
@MockBean
|
||||
private DropshipPalletA4PdfService dropshipPalletA4PdfService;
|
||||
@Resource
|
||||
private InternalPdfController controller;
|
||||
private MockMvc mockMvc;
|
||||
@@ -169,6 +173,27 @@ class InternalPdfControllerTest {
|
||||
verify(freightA4PdfService).generate(any(FreightA4PdfDtos.Request.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsDropshipPalletA4PdfWithDownloadHeaders() throws Exception {
|
||||
byte[] pdf = "%PDF-pallet-a4".getBytes(StandardCharsets.US_ASCII);
|
||||
when(dropshipPalletA4PdfService.generate(any())).thenReturn(
|
||||
new DropshipPalletA4PdfService.GeneratedPdf(pdf, "DP-260911-0034-A4托盘信息单.pdf", 1));
|
||||
|
||||
mockMvc.perform(post("/internal/print/v1/pdfs/dropship-pallet-a4-documents")
|
||||
.header(InternalPdfController.INTERNAL_TOKEN_HEADER, TOKEN)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_PDF, MediaType.APPLICATION_JSON)
|
||||
.content(validDropshipPalletA4RequestJson()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_PDF))
|
||||
.andExpect(content().bytes(pdf))
|
||||
.andExpect(header().string("X-Print-Page-Count", "1"))
|
||||
.andExpect(header().string("Content-Disposition",
|
||||
org.hamcrest.Matchers.containsString("filename*=")));
|
||||
|
||||
verify(dropshipPalletA4PdfService).generate(any(DropshipPalletA4PdfDtos.Request.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void endpointDoesNotPinProducedMediaTypeToPdf() throws Exception {
|
||||
PostMapping mapping = InternalPdfController.class
|
||||
@@ -185,11 +210,13 @@ class InternalPdfControllerTest {
|
||||
assertThat(contract)
|
||||
.contains("/internal/print/v1/pdfs/inbound-marks:")
|
||||
.contains("/internal/print/v1/pdfs/freight-a4-documents:")
|
||||
.contains("/internal/print/v1/pdfs/dropship-pallet-a4-documents:")
|
||||
.contains("application/pdf:")
|
||||
.contains("application/json:")
|
||||
.contains("name: X-Internal-Service-Token")
|
||||
.contains("SEA_FREIGHT_SHIPPING_INSTRUCTION_A4_V1:")
|
||||
.contains("BILL_OF_LADING_CONFIRMATION_A4_V1:")
|
||||
.contains("DROPSHIP_PALLET_A4_V1")
|
||||
.contains("additionalProperties: false")
|
||||
.contains("minItems: 1")
|
||||
.contains("required: [marksAndNos, quantity, packaging, descriptionOfGoods, grossWeight, measurement]")
|
||||
@@ -238,4 +265,30 @@ class InternalPdfControllerTest {
|
||||
""";
|
||||
}
|
||||
|
||||
private String validDropshipPalletA4RequestJson() {
|
||||
return """
|
||||
{
|
||||
"templateCode": "DROPSHIP_PALLET_A4_V1",
|
||||
"pageSpec": {"widthMm": 210, "heightMm": 297},
|
||||
"downloadFileName": "DP-260911-0034-A4托盘信息单.pdf",
|
||||
"documents": [{
|
||||
"palletNo": "DP-260911-0034",
|
||||
"inboundCodes": "NYEXX2609110249",
|
||||
"cartonCount": 3,
|
||||
"skuCount": 1,
|
||||
"weightKg": 90,
|
||||
"volumeCbm": 0.534,
|
||||
"barcodeContent": "DP-260911-0034",
|
||||
"skuRows": [{
|
||||
"inboundCode": "NYEXX2609110249",
|
||||
"skuCode": "SKU-001",
|
||||
"cargoName": "蓝牙音箱",
|
||||
"cartonCount": 3,
|
||||
"skuQuantity": 90
|
||||
}]
|
||||
}]
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.baiyue.print.renderer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class PdfBarcodeRendererTest {
|
||||
@Test
|
||||
void encodesCode128DeterministicallyWithQuietZone() {
|
||||
PdfBarcodeRenderer renderer = new PdfBarcodeRenderer();
|
||||
|
||||
BitMatrix first = renderer.encode("DP-260911-0034");
|
||||
BitMatrix second = renderer.encode("DP-260911-0034");
|
||||
|
||||
assertThat(first).isEqualTo(second);
|
||||
assertThat(first.getHeight()).isEqualTo(1);
|
||||
assertThat(first.getWidth()).isGreaterThan(100);
|
||||
assertThat(first.getEnclosingRectangle()).isNotNull();
|
||||
assertThat(first.getEnclosingRectangle()[0]).isGreaterThanOrEqualTo(5);
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package com.baiyue.print.service;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.print.config.PrintPdfProperties;
|
||||
import com.baiyue.print.dto.DropshipPalletA4PdfDtos;
|
||||
import com.baiyue.print.renderer.DropshipPalletA4Renderer;
|
||||
import com.baiyue.print.renderer.PdfBarcodeRenderer;
|
||||
import com.baiyue.print.renderer.PdfUnits;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.text.PDFTextStripper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
@SpringJUnitConfig(classes = {DropshipPalletA4PdfService.class, PrintPdfProperties.class,
|
||||
PdfGenerationLimiter.class, DropshipPalletA4Renderer.class, PdfBarcodeRenderer.class})
|
||||
class DropshipPalletA4PdfServiceGenerationTest {
|
||||
@Resource private DropshipPalletA4PdfService service;
|
||||
|
||||
@Test
|
||||
void generatesOneA4PageWithPalletBarcodeAndSkuDetails() throws Exception {
|
||||
DropshipPalletA4PdfDtos.Document document = new DropshipPalletA4PdfDtos.Document(
|
||||
"DP-260911-0034", "NYEXX2609110249", 3, 2,
|
||||
new BigDecimal("90"), new BigDecimal("0.534"), "DP-260911-0034",
|
||||
List.of(
|
||||
new DropshipPalletA4PdfDtos.SkuRow("NYEXX2609110249", "SKU-001", "蓝牙音箱", 2, 40),
|
||||
new DropshipPalletA4PdfDtos.SkuRow("NYEXX2609110249", "SKU-002", "充电线", 1, 50)));
|
||||
var generated = service.generate(new DropshipPalletA4PdfDtos.Request(
|
||||
DropshipPalletA4PdfDtos.TEMPLATE,
|
||||
new DropshipPalletA4PdfDtos.PageSpec(new BigDecimal("210"), new BigDecimal("297")),
|
||||
"DP-260911-0034-A4托盘信息单.pdf", List.of(document)));
|
||||
assertThat(generated.pageCount()).isEqualTo(1);
|
||||
assertThat(generated.content()).startsWith("%PDF-".getBytes(java.nio.charset.StandardCharsets.US_ASCII));
|
||||
try (PDDocument pdf = Loader.loadPDF(generated.content())) {
|
||||
assertThat(pdf.getNumberOfPages()).isEqualTo(1);
|
||||
assertThat(pdf.getPage(0).getMediaBox().getWidth()).isCloseTo(PdfUnits.mmToPt(210), org.assertj.core.data.Offset.offset(.002f));
|
||||
assertThat(pdf.getPage(0).getMediaBox().getHeight()).isCloseTo(PdfUnits.mmToPt(297), org.assertj.core.data.Offset.offset(.002f));
|
||||
assertThat(new PDFTextStripper().getText(pdf))
|
||||
.contains("DP-260911-0034", "NYEXX2609110249", "SKU 明细", "SKU-001", "SKU-002", "40", "50")
|
||||
.containsOnlyOnce("DP-260911-0034")
|
||||
.doesNotContain("状态", "待封托", "已封托");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsTenInboundCodesAndThirtyTwoSkuRowsOnOneA4Page() throws Exception {
|
||||
List<String> inboundCodes = new ArrayList<>();
|
||||
for (int index = 1; index <= 10; index++) inboundCodes.add("NYEXX260911" + String.format("%04d", 240 + index));
|
||||
List<DropshipPalletA4PdfDtos.SkuRow> rows = new ArrayList<>();
|
||||
for (int index = 1; index <= 32; index++) {
|
||||
rows.add(new DropshipPalletA4PdfDtos.SkuRow(inboundCodes.get((index - 1) % inboundCodes.size()),
|
||||
"SKU-" + String.format("%03d", index), "测试商品" + index, 2, index * 5));
|
||||
}
|
||||
DropshipPalletA4PdfDtos.Document document = new DropshipPalletA4PdfDtos.Document(
|
||||
"DP-260911-0034", String.join("/", inboundCodes), 64, 32,
|
||||
new BigDecimal("860.5"), new BigDecimal("6.728"), "DP-260911-0034", rows);
|
||||
var generated = service.generate(new DropshipPalletA4PdfDtos.Request(
|
||||
DropshipPalletA4PdfDtos.TEMPLATE,
|
||||
new DropshipPalletA4PdfDtos.PageSpec(new BigDecimal("210"), new BigDecimal("297")),
|
||||
"DP-260911-0034-高密度预览.pdf", List.of(document)));
|
||||
try (PDDocument pdf = Loader.loadPDF(generated.content())) {
|
||||
assertThat(pdf.getNumberOfPages()).isEqualTo(1);
|
||||
assertThat(new PDFTextStripper().getText(pdf))
|
||||
.contains("NYEXX2609110241", "NYEXX2609110250", "SKU-001", "SKU-032")
|
||||
.containsOnlyOnce("DP-260911-0034");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsSkuRowsThatCannotFitOnOneA4Page() {
|
||||
List<DropshipPalletA4PdfDtos.SkuRow> rows = new ArrayList<>();
|
||||
for (int index = 0; index < 35; index++) {
|
||||
rows.add(new DropshipPalletA4PdfDtos.SkuRow("INBOUND", "SKU-" + index, "商品", 1, 1));
|
||||
}
|
||||
DropshipPalletA4PdfDtos.Document document = new DropshipPalletA4PdfDtos.Document(
|
||||
"DP-260911-0034", "INBOUND", 35, 35,
|
||||
BigDecimal.ZERO, BigDecimal.ZERO, "DP-260911-0034", rows);
|
||||
|
||||
assertThatThrownBy(() -> service.generate(new DropshipPalletA4PdfDtos.Request(
|
||||
DropshipPalletA4PdfDtos.TEMPLATE,
|
||||
new DropshipPalletA4PdfDtos.PageSpec(new BigDecimal("210"), new BigDecimal("297")),
|
||||
"pallet.pdf", List.of(document))))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("one-page A4 capacity");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user