feat(file-print): support ocean booking documents
This commit is contained in:
@@ -68,13 +68,7 @@ public class InternalPdfController {
|
||||
@Valid @RequestBody FreightA4PdfDtos.Request request,
|
||||
HttpServletRequest servletRequest) {
|
||||
tokenGuard.verify(internalToken);
|
||||
String requestId = CorrelationContext.requestId(servletRequest);
|
||||
FreightA4PdfDtos.Request effectiveRequest = request;
|
||||
if (request.downloadFileName() == null && requestId != null && !requestId.isBlank()) {
|
||||
effectiveRequest = new FreightA4PdfDtos.Request(request.templateCode(), request.pageSpec(),
|
||||
"freight-a4-documents-" + requestId + ".pdf", request.documents());
|
||||
}
|
||||
FreightA4PdfService.GeneratedPdf pdf = freightA4PdfService.generate(effectiveRequest);
|
||||
FreightA4PdfService.GeneratedPdf pdf = freightA4PdfService.generate(request);
|
||||
byte[] content = pdf.content();
|
||||
|
||||
ContentDisposition disposition = ContentDisposition.attachment()
|
||||
|
||||
@@ -27,7 +27,25 @@ public class FreightA4PdfService {
|
||||
private static final int MAX_FILE_NAME_CODE_POINTS = 120;
|
||||
private static final int MAX_FIELD_KEY_CODE_POINTS = 80;
|
||||
private static final int MAX_FIELD_VALUE_CODE_POINTS = 4000;
|
||||
private static final int MAX_CARGO_ROWS = 50;
|
||||
private static final Set<String> SHIPPING_FIELDS = Set.of(
|
||||
"toCompany", "issuerNameCn", "issuerNameEn", "issuerAddress", "issuerPostalCode",
|
||||
"issuerTel", "issuerFax", "shipper", "consignee", "notifyParty", "picName", "picTel",
|
||||
"picFax", "documentName", "documentTel", "documentFax", "jobNumber", "etdDate",
|
||||
"serviceTerm", "freightTerm", "apCode", "apName", "cargoQuantity", "transshipment",
|
||||
"partialShipment", "oceanVesselVoyage", "portOfLoading", "portOfDischarge",
|
||||
"placeOfDelivery", "totalPackagesInWords", "remark", "carrier", "bookingAgent", "printDate");
|
||||
private static final Set<String> SHIPPING_REQUIRED_FIELDS = Set.of(
|
||||
"toCompany", "shipper", "consignee", "notifyParty", "jobNumber", "cargoQuantity",
|
||||
"portOfLoading", "portOfDischarge", "placeOfDelivery", "totalPackagesInWords");
|
||||
private static final Set<String> BILL_FIELDS = Set.of(
|
||||
"shipper", "consignee", "notifyParty", "bookingNumber", "ensAmsCutoffDate",
|
||||
"customsCutoffDate", "expectedDepartureDate", "oceanVesselVoyage", "portOfLoading",
|
||||
"portOfDischarge", "placeOfDelivery", "finalDestination", "serviceTerm", "freightTerm",
|
||||
"shipperLoadCountSeal", "containerSpec", "shippedOnBoard", "draftText",
|
||||
"totalPackagesInWords", "containerSummary", "notes", "issuePlace", "issueDate");
|
||||
private static final Set<String> BILL_REQUIRED_FIELDS = Set.of(
|
||||
"shipper", "consignee", "notifyParty", "bookingNumber", "portOfLoading",
|
||||
"portOfDischarge", "placeOfDelivery", "serviceTerm", "freightTerm", "totalPackagesInWords");
|
||||
private static final BigDecimal A4_WIDTH_MM = new BigDecimal("210");
|
||||
private static final BigDecimal A4_HEIGHT_MM = new BigDecimal("297");
|
||||
private static final Set<String> SUPPORTED_TEMPLATES = Set.of(
|
||||
@@ -99,35 +117,45 @@ public class FreightA4PdfService {
|
||||
|
||||
List<FreightA4PdfDtos.Document> documents = new ArrayList<>(request.documents().size());
|
||||
for (int index = 0; index < request.documents().size(); index++) {
|
||||
documents.add(normalizeDocument(request.documents().get(index), index));
|
||||
documents.add(normalizeDocument(templateCode, request.documents().get(index), index));
|
||||
}
|
||||
return new NormalizedRequest(templateCode, normalizeFileName(request.downloadFileName()),
|
||||
return new NormalizedRequest(templateCode,
|
||||
normalizeFileName(request.downloadFileName(), templateCode, documents.get(0)),
|
||||
List.copyOf(documents));
|
||||
}
|
||||
|
||||
private FreightA4PdfDtos.Document normalizeDocument(FreightA4PdfDtos.Document document, int index) {
|
||||
private FreightA4PdfDtos.Document normalizeDocument(String templateCode,
|
||||
FreightA4PdfDtos.Document document, int index) {
|
||||
if (document == null) {
|
||||
invalid("documents[" + index + "] is required");
|
||||
}
|
||||
Map<String, String> fields = new LinkedHashMap<>();
|
||||
if (document.fields() != null) {
|
||||
Set<String> allowed = FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE.equals(templateCode)
|
||||
? SHIPPING_FIELDS : BILL_FIELDS;
|
||||
for (Map.Entry<String, String> entry : document.fields().entrySet()) {
|
||||
String key = normalizeRequiredLine(entry.getKey(), field(index, "fields key"));
|
||||
if (key.codePointCount(0, key.length()) > MAX_FIELD_KEY_CODE_POINTS) {
|
||||
invalid(field(index, "fields." + key) + " key is too long");
|
||||
}
|
||||
if (!allowed.contains(key)) {
|
||||
invalid(field(index, "fields." + key) + " is not supported by " + templateCode);
|
||||
}
|
||||
fields.put(key, normalizeOptionalBlock(entry.getValue(), field(index, "fields." + key)));
|
||||
}
|
||||
}
|
||||
List<FreightA4PdfDtos.CargoRow> rows = new ArrayList<>();
|
||||
if (document.cargoRows() != null) {
|
||||
if (document.cargoRows().size() > MAX_CARGO_ROWS) {
|
||||
invalid(field(index, "cargoRows") + " must not contain more than " + MAX_CARGO_ROWS + " items");
|
||||
}
|
||||
for (int rowIndex = 0; rowIndex < document.cargoRows().size(); rowIndex++) {
|
||||
rows.add(normalizeCargoRow(document.cargoRows().get(rowIndex), index, rowIndex));
|
||||
Set<String> required = FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE.equals(templateCode)
|
||||
? SHIPPING_REQUIRED_FIELDS : BILL_REQUIRED_FIELDS;
|
||||
for (String key : required) {
|
||||
if (!fields.containsKey(key) || fields.get(key).isBlank()) {
|
||||
invalid(field(index, "fields." + key) + " is required by " + templateCode);
|
||||
}
|
||||
}
|
||||
List<FreightA4PdfDtos.CargoRow> rows = new ArrayList<>();
|
||||
if (document.cargoRows() == null || document.cargoRows().size() != 1) {
|
||||
invalid(field(index, "cargoRows") + " must contain exactly one item");
|
||||
}
|
||||
rows.add(normalizeCargoRow(document.cargoRows().get(0), index, 0));
|
||||
return new FreightA4PdfDtos.Document(Map.copyOf(fields), List.copyOf(rows));
|
||||
}
|
||||
|
||||
@@ -138,19 +166,22 @@ public class FreightA4PdfService {
|
||||
}
|
||||
String prefix = "documents[" + documentIndex + "].cargoRows[" + rowIndex + "].";
|
||||
return new FreightA4PdfDtos.CargoRow(
|
||||
normalizeOptionalBlock(row.marksAndNos(), prefix + "marksAndNos"),
|
||||
normalizeOptionalBlock(row.quantity(), prefix + "quantity"),
|
||||
normalizeOptionalBlock(row.packaging(), prefix + "packaging"),
|
||||
normalizeOptionalBlock(row.descriptionOfGoods(), prefix + "descriptionOfGoods"),
|
||||
normalizeOptionalBlock(row.grossWeight(), prefix + "grossWeight"),
|
||||
normalizeOptionalBlock(row.measurement(), prefix + "measurement"));
|
||||
normalizeRequiredBlock(row.marksAndNos(), prefix + "marksAndNos"),
|
||||
normalizeRequiredBlock(row.quantity(), prefix + "quantity"),
|
||||
normalizeRequiredBlock(row.packaging(), prefix + "packaging"),
|
||||
normalizeRequiredBlock(row.descriptionOfGoods(), prefix + "descriptionOfGoods"),
|
||||
normalizeRequiredBlock(row.grossWeight(), prefix + "grossWeight"),
|
||||
normalizeRequiredBlock(row.measurement(), prefix + "measurement"));
|
||||
}
|
||||
|
||||
private String normalizeFileName(String value) {
|
||||
if (value == null) {
|
||||
return "freight-a4-documents.pdf";
|
||||
private String normalizeFileName(String value, String templateCode, FreightA4PdfDtos.Document firstDocument) {
|
||||
String candidate = value;
|
||||
if (candidate == null) {
|
||||
candidate = FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE.equals(templateCode)
|
||||
? firstDocument.field("jobNumber") + "-出口货物托运单.pdf"
|
||||
: firstDocument.field("bookingNumber") + "-提单确认件.pdf";
|
||||
}
|
||||
String normalized = normalizeRequiredLine(value, "downloadFileName");
|
||||
String normalized = normalizeRequiredLine(candidate, "downloadFileName");
|
||||
if (normalized.codePointCount(0, normalized.length()) > MAX_FILE_NAME_CODE_POINTS) {
|
||||
invalid("downloadFileName must not exceed 120 characters");
|
||||
}
|
||||
@@ -165,6 +196,12 @@ public class FreightA4PdfService {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private String normalizeRequiredBlock(String value, String field) {
|
||||
String normalized = normalizeOptionalBlock(value, field);
|
||||
if (normalized.isBlank()) invalid(field + " must not be blank");
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private String normalizeRequiredLine(String value, String field) {
|
||||
String normalized = normalizeOptionalBlock(value, field);
|
||||
if (normalized.isBlank()) {
|
||||
|
||||
@@ -173,6 +173,7 @@ components:
|
||||
type: string
|
||||
maxLength: 120
|
||||
example: BYSE2607044-托运书.pdf
|
||||
description: 未传时托运书为{jobNumber}-出口货物托运单.pdf,提单确认件为{bookingNumber}-提单确认件.pdf
|
||||
documents:
|
||||
type: array
|
||||
minItems: 1
|
||||
@@ -192,21 +193,77 @@ components:
|
||||
enum: [297]
|
||||
FreightA4Document:
|
||||
type: object
|
||||
required: [fields, cargoRows]
|
||||
x-template-contracts:
|
||||
SEA_FREIGHT_SHIPPING_INSTRUCTION_A4_V1:
|
||||
required: [toCompany, shipper, consignee, notifyParty, jobNumber, cargoQuantity, portOfLoading, portOfDischarge, placeOfDelivery, totalPackagesInWords]
|
||||
optional: [issuerNameCn, issuerNameEn, issuerAddress, issuerPostalCode, issuerTel, issuerFax, picName, picTel, picFax, documentName, documentTel, documentFax, etdDate, serviceTerm, freightTerm, apCode, apName, transshipment, partialShipment, oceanVesselVoyage, remark, carrier, bookingAgent, printDate]
|
||||
BILL_OF_LADING_CONFIRMATION_A4_V1:
|
||||
required: [shipper, consignee, notifyParty, bookingNumber, portOfLoading, portOfDischarge, placeOfDelivery, serviceTerm, freightTerm, totalPackagesInWords]
|
||||
optional: [ensAmsCutoffDate, customsCutoffDate, expectedDepartureDate, oceanVesselVoyage, finalDestination, shipperLoadCountSeal, containerSpec, shippedOnBoard, draftText, containerSummary, notes, issuePlace, issueDate]
|
||||
properties:
|
||||
fields:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
maxLength: 4000
|
||||
description: Template field map. Known keys are documented in the print module README.
|
||||
additionalProperties: false
|
||||
description: 只能提交所选模板登记的键;未知键和缺少必填键返回A0422。
|
||||
properties:
|
||||
toCompany: {$ref: '#/components/schemas/FreightField'}
|
||||
issuerNameCn: {$ref: '#/components/schemas/FreightField'}
|
||||
issuerNameEn: {$ref: '#/components/schemas/FreightField'}
|
||||
issuerAddress: {$ref: '#/components/schemas/FreightField'}
|
||||
issuerPostalCode: {$ref: '#/components/schemas/FreightField'}
|
||||
issuerTel: {$ref: '#/components/schemas/FreightField'}
|
||||
issuerFax: {$ref: '#/components/schemas/FreightField'}
|
||||
shipper: {$ref: '#/components/schemas/FreightField'}
|
||||
consignee: {$ref: '#/components/schemas/FreightField'}
|
||||
notifyParty: {$ref: '#/components/schemas/FreightField'}
|
||||
picName: {$ref: '#/components/schemas/FreightField'}
|
||||
picTel: {$ref: '#/components/schemas/FreightField'}
|
||||
picFax: {$ref: '#/components/schemas/FreightField'}
|
||||
documentName: {$ref: '#/components/schemas/FreightField'}
|
||||
documentTel: {$ref: '#/components/schemas/FreightField'}
|
||||
documentFax: {$ref: '#/components/schemas/FreightField'}
|
||||
jobNumber: {$ref: '#/components/schemas/FreightField'}
|
||||
etdDate: {$ref: '#/components/schemas/FreightField'}
|
||||
serviceTerm: {$ref: '#/components/schemas/FreightField'}
|
||||
freightTerm: {$ref: '#/components/schemas/FreightField'}
|
||||
apCode: {$ref: '#/components/schemas/FreightField'}
|
||||
apName: {$ref: '#/components/schemas/FreightField'}
|
||||
cargoQuantity: {$ref: '#/components/schemas/FreightField'}
|
||||
transshipment: {$ref: '#/components/schemas/FreightField'}
|
||||
partialShipment: {$ref: '#/components/schemas/FreightField'}
|
||||
oceanVesselVoyage: {$ref: '#/components/schemas/FreightField'}
|
||||
portOfLoading: {$ref: '#/components/schemas/FreightField'}
|
||||
portOfDischarge: {$ref: '#/components/schemas/FreightField'}
|
||||
placeOfDelivery: {$ref: '#/components/schemas/FreightField'}
|
||||
totalPackagesInWords: {$ref: '#/components/schemas/FreightField'}
|
||||
remark: {$ref: '#/components/schemas/FreightField'}
|
||||
carrier: {$ref: '#/components/schemas/FreightField'}
|
||||
bookingAgent: {$ref: '#/components/schemas/FreightField'}
|
||||
printDate: {$ref: '#/components/schemas/FreightField'}
|
||||
bookingNumber: {$ref: '#/components/schemas/FreightField'}
|
||||
ensAmsCutoffDate: {$ref: '#/components/schemas/FreightField'}
|
||||
customsCutoffDate: {$ref: '#/components/schemas/FreightField'}
|
||||
expectedDepartureDate: {$ref: '#/components/schemas/FreightField'}
|
||||
finalDestination: {$ref: '#/components/schemas/FreightField'}
|
||||
shipperLoadCountSeal: {$ref: '#/components/schemas/FreightField'}
|
||||
containerSpec: {$ref: '#/components/schemas/FreightField'}
|
||||
shippedOnBoard: {$ref: '#/components/schemas/FreightField'}
|
||||
draftText: {$ref: '#/components/schemas/FreightField'}
|
||||
containerSummary: {$ref: '#/components/schemas/FreightField'}
|
||||
notes: {$ref: '#/components/schemas/FreightField'}
|
||||
issuePlace: {$ref: '#/components/schemas/FreightField'}
|
||||
issueDate: {$ref: '#/components/schemas/FreightField'}
|
||||
cargoRows:
|
||||
type: array
|
||||
minItems: 1
|
||||
maxItems: 1
|
||||
description: V1 templates render at most one cargo row. Callers must not send additional rows.
|
||||
description: V1模板固定恰好一行货物,六个字段均必填且非空。
|
||||
items:
|
||||
$ref: '#/components/schemas/FreightA4CargoRow'
|
||||
FreightA4CargoRow:
|
||||
type: object
|
||||
required: [marksAndNos, quantity, packaging, descriptionOfGoods, grossWeight, measurement]
|
||||
properties:
|
||||
marksAndNos:
|
||||
type: string
|
||||
@@ -220,6 +277,9 @@ components:
|
||||
type: string
|
||||
measurement:
|
||||
type: string
|
||||
FreightField:
|
||||
type: string
|
||||
maxLength: 4000
|
||||
ApiError:
|
||||
type: object
|
||||
required: [code, message]
|
||||
|
||||
@@ -172,6 +172,11 @@ class InternalPdfControllerTest {
|
||||
.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("additionalProperties: false")
|
||||
.contains("minItems: 1")
|
||||
.contains("required: [marksAndNos, quantity, packaging, descriptionOfGoods, grossWeight, measurement]")
|
||||
.contains("maximum: 2000");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.baiyue.print.renderer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class FreightA4ChineseLayoutContractTest {
|
||||
@Test
|
||||
void freezesChineseTitlesAndBilingualLayoutLabels() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/com/baiyue/print/renderer/FreightA4Renderer.java"));
|
||||
|
||||
assertThat(source).contains(
|
||||
"出口货物托运单", "提单确认件",
|
||||
"Shipper(Full Name & Address)(托运人)", "Consignee(收货人)", "Notify Party(通知人)",
|
||||
"Ocean Vessel/Voy(船名航次)", "Port of Loading(起运港)",
|
||||
"Part of Discharge(卸货港)", "Place of Delivery(交货地)",
|
||||
"Marks & Nos\\n(标志和号码)", "Description of Goods(品名)",
|
||||
"Gross Weight(Kilos)\\n毛重(公斤)", "Measurement(CBM)\\n体积(立方米)",
|
||||
"截关日期", "预计开航日期", "SHIPPED ON BOARD");
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,8 @@ class FreightA4PdfServiceGenerationTest {
|
||||
assertA4(document);
|
||||
String text = new PDFTextStripper().getText(document);
|
||||
assertThat(text).contains("Sea Freight Shipping Instructions", "Job Nbr",
|
||||
"BYSE2607044", "CAR ACCESSORIES", "SAY SIX HUNDRED AND FIFTY NINE PACKAGES ONLY.");
|
||||
"BYSE2607044", "CAR ACCESSORIES",
|
||||
"SAY SIX HUNDRED AND FIFTY NINE PACKAGES ONLY.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+71
-6
@@ -25,7 +25,7 @@ class FreightA4PdfServiceValidationTest {
|
||||
@Test
|
||||
void rejectsUnsupportedTemplateCode() {
|
||||
assertValidationFailure(new FreightA4PdfDtos.Request("UNKNOWN",
|
||||
pageSpec(), "a4.pdf", List.of(document())), "templateCode");
|
||||
pageSpec(), "a4.pdf", List.of(shippingDocument())), "templateCode");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -33,18 +33,59 @@ class FreightA4PdfServiceValidationTest {
|
||||
assertValidationFailure(new FreightA4PdfDtos.Request(
|
||||
FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE,
|
||||
new FreightA4PdfDtos.PageSpec(new BigDecimal("100"), new BigDecimal("100")),
|
||||
"a4.pdf", List.of(document())), "pageSpec.widthMm");
|
||||
"a4.pdf", List.of(shippingDocument())), "pageSpec.widthMm");
|
||||
}
|
||||
|
||||
@Test
|
||||
void allowsMultilineDocumentFields() {
|
||||
FreightA4PdfDtos.Request request = new FreightA4PdfDtos.Request(
|
||||
FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE,
|
||||
pageSpec(), "a4.pdf", List.of(document()));
|
||||
pageSpec(), "a4.pdf", List.of(shippingDocument()));
|
||||
|
||||
assertThatCode(() -> service.generate(request)).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsMissingRequiredFieldForSelectedTemplate() {
|
||||
var fields = new java.util.HashMap<>(shippingDocument().fields());
|
||||
fields.remove("jobNumber");
|
||||
assertValidationFailure(request(FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE,
|
||||
new FreightA4PdfDtos.Document(fields, cargoRows())), "fields.jobNumber is required");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsUnknownFieldInsteadOfSilentlyIgnoringTypo() {
|
||||
var fields = new java.util.HashMap<>(shippingDocument().fields());
|
||||
fields.put("jobNubmer", "OB-260831-0001");
|
||||
assertValidationFailure(request(FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE,
|
||||
new FreightA4PdfDtos.Document(fields, cargoRows())), "fields.jobNubmer is not supported");
|
||||
}
|
||||
|
||||
@Test
|
||||
void requiresExactlyOneCompleteCargoRow() {
|
||||
assertValidationFailure(request(FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE,
|
||||
new FreightA4PdfDtos.Document(shippingDocument().fields(), List.of())),
|
||||
"cargoRows must contain exactly one item");
|
||||
assertValidationFailure(request(FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE,
|
||||
new FreightA4PdfDtos.Document(shippingDocument().fields(), List.of(cargo(), cargo()))),
|
||||
"cargoRows must contain exactly one item");
|
||||
assertValidationFailure(request(FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE,
|
||||
new FreightA4PdfDtos.Document(shippingDocument().fields(), List.of(
|
||||
new FreightA4PdfDtos.CargoRow("", "1", "PACKAGES", "GOODS", "1 KGS", "1 CBM")))),
|
||||
"marksAndNos must not be blank");
|
||||
}
|
||||
|
||||
@Test
|
||||
void derivesStableChineseFileNamesFromBusinessNumbers() {
|
||||
var shipping = service.generate(request(FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE,
|
||||
shippingDocument()));
|
||||
var bill = service.generate(request(FreightA4PdfDtos.BILL_CONFIRMATION_TEMPLATE,
|
||||
billDocument()));
|
||||
|
||||
assertThat(shipping.fileName()).isEqualTo("OB-260831-0001-出口货物托运单.pdf");
|
||||
assertThat(bill.fileName()).isEqualTo("BK-20260831-提单确认件.pdf");
|
||||
}
|
||||
|
||||
private void assertValidationFailure(FreightA4PdfDtos.Request request, String messagePart) {
|
||||
assertThatThrownBy(() -> service.generate(request))
|
||||
.isInstanceOfSatisfying(BizException.class, exception -> {
|
||||
@@ -53,12 +94,36 @@ class FreightA4PdfServiceValidationTest {
|
||||
});
|
||||
}
|
||||
|
||||
private FreightA4PdfDtos.Request request(String template, FreightA4PdfDtos.Document document) {
|
||||
return new FreightA4PdfDtos.Request(template, pageSpec(), null, List.of(document));
|
||||
}
|
||||
|
||||
private FreightA4PdfDtos.PageSpec pageSpec() {
|
||||
return new FreightA4PdfDtos.PageSpec(new BigDecimal("210"), new BigDecimal("297"));
|
||||
}
|
||||
|
||||
private FreightA4PdfDtos.Document document() {
|
||||
return new FreightA4PdfDtos.Document(Map.of("shipper", "A\nB"),
|
||||
List.of(new FreightA4PdfDtos.CargoRow("N/M", "1", "PACKAGES", "GOODS", "1 KGS", "1 CBM")));
|
||||
private FreightA4PdfDtos.Document shippingDocument() {
|
||||
return new FreightA4PdfDtos.Document(Map.of(
|
||||
"toCompany", "订舱代理", "shipper", "A\nB", "consignee", "C", "notifyParty", "N",
|
||||
"jobNumber", "OB-260831-0001", "cargoQuantity", "1*40HQ", "portOfLoading", "NINGBO",
|
||||
"portOfDischarge", "LOS ANGELES", "placeOfDelivery", "LOS ANGELES",
|
||||
"totalPackagesInWords", "SAY ONE PACKAGE ONLY."), cargoRows());
|
||||
}
|
||||
|
||||
private FreightA4PdfDtos.Document billDocument() {
|
||||
return new FreightA4PdfDtos.Document(Map.of(
|
||||
"shipper", "A", "consignee", "C", "notifyParty", "N", "bookingNumber", "BK-20260831",
|
||||
"portOfLoading", "NINGBO", "portOfDischarge", "LOS ANGELES",
|
||||
"placeOfDelivery", "LOS ANGELES", "serviceTerm", "CY-CY",
|
||||
"freightTerm", "FREIGHT PREPAID", "totalPackagesInWords", "SAY ONE PACKAGE ONLY."),
|
||||
cargoRows());
|
||||
}
|
||||
|
||||
private List<FreightA4PdfDtos.CargoRow> cargoRows() {
|
||||
return List.of(cargo());
|
||||
}
|
||||
|
||||
private FreightA4PdfDtos.CargoRow cargo() {
|
||||
return new FreightA4PdfDtos.CargoRow("N/M", "1", "PACKAGES", "GOODS", "1 KGS", "1 CBM");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user