From b2b668a392679d106f395716d595326fdafc2ba6 Mon Sep 17 00:00:00 2001 From: hagiye Date: Sat, 27 Jun 2026 01:13:25 +0300 Subject: [PATCH] Invoice and clearance seal approval --- .../warehouses/warehouse-inventory.service.ts | 84 +++++++++------- .../warehouses/warehouse-invoice.service.ts | 95 +++++++++++++++++-- .../warehouse-release-document.service.ts | 71 ++++++++++++-- apps/edr-freight-web/backoffice/package.json | 1 + .../warehouses/InventoryDetailModal.tsx | 19 +++- .../src/components/warehouses/warehousePdf.ts | 46 +++++++-- .../backoffice/src/constants/apiConfig.ts | 4 - .../backoffice/src/types/warehouse.ts | 8 ++ 8 files changed, 265 insertions(+), 63 deletions(-) diff --git a/apps/edr-freight-api/src/modules/warehouses/warehouse-inventory.service.ts b/apps/edr-freight-api/src/modules/warehouses/warehouse-inventory.service.ts index c167e3077..dc3719207 100644 --- a/apps/edr-freight-api/src/modules/warehouses/warehouse-inventory.service.ts +++ b/apps/edr-freight-api/src/modules/warehouses/warehouse-inventory.service.ts @@ -1736,6 +1736,7 @@ export class WarehouseInventoryService { const [row] = await this.dataSource.query( `SELECT inv.id, inv.release_date AS "releaseDate", + inv.release_order_reference AS "releaseOrderReference", inv.quantity, inv.weight, inv.status, @@ -1777,8 +1778,10 @@ export class WarehouseInventoryService { throw new BadRequestException('A release order must be issued before downloading the exit paper'); } - const reference = `REL-${id.slice(0, 8).toUpperCase()}`; - const bookingReference = row?.bookingReference || row?.bookingId || 'N/A'; + const bookingReference = row?.bookingReference || 'N/A'; + const reference = + row?.releaseOrderReference || + (row?.bookingReference ? `REL-${String(row.bookingReference).replace(/^BK-?/i, '')}` : 'REL-N/A'); const issuedAt = new Date(row.releaseDate); const html = this.buildReleaseDocumentHtml({ reference, @@ -1796,6 +1799,7 @@ export class WarehouseInventoryService { yard: [row?.yardName, row?.yardCode].filter(Boolean).join(' / ') || null, zone: [row?.zoneName, row?.zoneCode].filter(Boolean).join(' / ') || null, inventoryStatus: row?.status ?? null, + clearanceStatus: 'CLEARED FOR WAREHOUSE EXIT', }); return { @@ -2300,6 +2304,7 @@ export class WarehouseInventoryService { yard: string | null; zone: string | null; inventoryStatus: string | null; + clearanceStatus: string; }): string { const esc = (value: unknown) => String(value ?? '-') @@ -2316,71 +2321,84 @@ export class WarehouseInventoryService { minute: '2-digit', }); const rows = [ - ['Booking reference', data.bookingReference], - ['Customer', data.customerName], - ['Booking status', data.bookingStatus], - ['Freight type', data.freightType], - ['Trade direction', data.tradeDirection], - ['Container number', data.containerNumber], - ['Cargo / goods', data.cargoDescription], + ['Booking Reference', data.bookingReference], + ['Customer / Consignee', data.customerName], + ['Booking Status', data.bookingStatus], + ['Freight Type', data.freightType], + ['Trade Direction', data.tradeDirection], + ['Container Number', data.containerNumber], + ['Cargo / Goods Description', data.cargoDescription], ['Quantity', data.quantity], - ['Weight', `${data.weight.toLocaleString()} kg`], + ['Declared Weight', `${data.weight.toLocaleString()} kg`], ['Warehouse', data.warehouse], ['Yard', data.yard], ['Zone', data.zone], - ['Inventory status', data.inventoryStatus], + ['Inventory Status', data.inventoryStatus], + ['Clearance Status', data.clearanceStatus], ]; return ` - Warehouse Release Exit Paper + Warehouse Gate Clearance / Release Order
-
EDR Warehouse Operations
-

Warehouse Release / Exit Paper

+
Ethio-Djibouti Railway S.C.
+

Warehouse Gate Clearance / Release Order

+
Official warehouse release and exit authorization
- Release reference + Document / Release No. ${esc(data.reference)} Issued: ${esc(issuedAt)}
+
EDR
Warehouse
Cleared
- This document authorizes the listed booking/goods to leave the warehouse after release checks. + This clearance document confirms that the listed booking/goods are authorized for warehouse exit, subject to gate identity verification and confirmation that no blocking warehouse fees remain unpaid.
+
Release Particulars
${rows.map(([label, value]) => ``).join('')}
${esc(label)}${esc(value)}
+
Authorization Clause
+
+ The warehouse officer and gate staff shall verify this document against the booking reference, customer or driver identity, + cargo details, clearance status, and payment records before permitting exit from the warehouse premises. +
-
Warehouse officer name / signature / date
+
Authorized warehouse officer name / signature / date
Customer or driver name / signature / date
diff --git a/apps/edr-freight-api/src/modules/warehouses/warehouse-invoice.service.ts b/apps/edr-freight-api/src/modules/warehouses/warehouse-invoice.service.ts index 7ec13602d..45c471db1 100644 --- a/apps/edr-freight-api/src/modules/warehouses/warehouse-invoice.service.ts +++ b/apps/edr-freight-api/src/modules/warehouses/warehouse-invoice.service.ts @@ -28,6 +28,22 @@ export interface PayInvoiceDto { const BLOCKING_STATUSES: WarehouseInvoiceStatus[] = ['ISSUED', 'PARTIALLY_PAID']; const ACTIVE_STATUSES: WarehouseInvoiceStatus[] = ['ISSUED', 'PARTIALLY_PAID', 'PAID']; +export interface InvoiceDocumentDetails { + bookingReference: string | null; + customerName: string | null; + inventoryReference: string | null; + inventoryInfo: string | null; + inventoryStatus: string | null; + containerNumber: string | null; + cargoDescription: string | null; + clearanceStatus: string; + warehouseName: string | null; + yardName: string | null; + zoneName: string | null; +} + +export type WarehouseFeeInvoiceWithDisplay = WarehouseFeeInvoice & Partial; + @Injectable() export class WarehouseInvoiceService { constructor( @@ -152,16 +168,18 @@ export class WarehouseInvoiceService { } // ── Reads ──────────────────────────────────────────────────────────────── - async findById(id: string): Promise { + async findById(id: string): Promise { const invoice = await this.invoiceRepository.findById(id); if (!invoice) throw new NotFoundException(`Invoice ${id} not found`); const items = await this.itemRepository.findAll({ where: { invoiceId: id } }); - return { ...invoice, items } as WarehouseFeeInvoice & { items: unknown[] }; + const details = await this.getInvoiceDocumentDetails(invoice); + return { ...invoice, ...details, items } as WarehouseFeeInvoiceWithDisplay & { items: unknown[] }; } async document(id: string): Promise<{ filename: string; buffer: Buffer }> { const invoice = await this.findById(id); - const html = this.buildInvoiceDocumentHtml(invoice, 'INVOICE'); + const details = await this.getInvoiceDocumentDetails(invoice); + const html = this.buildInvoiceDocumentHtml(invoice, 'INVOICE', details); return { filename: `warehouse-invoice-${this.safeFilename(invoice.invoiceNumber)}.pdf`, buffer: await this.documents.htmlToPdfBuffer(html), @@ -173,7 +191,8 @@ export class WarehouseInvoiceService { if (Number(invoice.paidAmount) <= 0) { throw new BadRequestException('A receipt is available only after payment is recorded.'); } - const html = this.buildInvoiceDocumentHtml(invoice, 'RECEIPT'); + const details = await this.getInvoiceDocumentDetails(invoice); + const html = this.buildInvoiceDocumentHtml(invoice, 'RECEIPT', details); return { filename: `warehouse-receipt-${this.safeFilename(invoice.invoiceNumber)}.pdf`, buffer: await this.documents.htmlToPdfBuffer(html), @@ -257,9 +276,66 @@ export class WarehouseInvoiceService { } } + private async getInvoiceDocumentDetails(invoice: WarehouseFeeInvoice): Promise { + const [row] = await this.dataSource.query( + `SELECT b.reference AS "bookingReference", + company.name AS "customerName", + COALESCE(inv.release_order_reference, b.reference) AS "inventoryReference", + inv.status AS "inventoryStatus", + COALESCE(container.container_number, booking_container.container_number) AS "containerNumber", + COALESCE(cargo_type.cargo_type_name, b.cargo_free_text, cargo.description) AS "cargoDescription", + CONCAT_WS( + ' / ', + NULLIF(inv.status, ''), + NULLIF(COALESCE(container.container_number, booking_container.container_number), ''), + NULLIF(COALESCE(cargo_type.cargo_type_name, b.cargo_free_text, cargo.description), '') + ) AS "inventoryInfo", + wh.name AS "warehouseName", + yard.name AS "yardName", + zone.name AS "zoneName", + CASE + WHEN inv.release_date IS NOT NULL THEN 'RELEASE ISSUED' + WHEN $2 = 'PAID' THEN 'FEE PAID - READY FOR RELEASE' + ELSE 'PENDING PAYMENT' + END AS "clearanceStatus" + FROM freight.warehouse_fee_invoices fee + LEFT JOIN freight.warehouse_inventory inv ON inv.id = fee.inventory_id AND inv.deleted_at IS NULL + LEFT JOIN freight.bookings b ON b.id = fee.booking_id AND b.deleted_at IS NULL + LEFT JOIN freight.companies company ON company.id = COALESCE(fee.customer_id, b.company_id) + LEFT JOIN freight.containers container ON container.id = inv.container_id AND container.deleted_at IS NULL + LEFT JOIN freight.booking_container booking_container ON ( + booking_container.booking_id = b.id + AND booking_container.deleted_at IS NULL + ) + LEFT JOIN freight.cargoes cargo ON cargo.id = inv.cargo_id AND cargo.deleted_at IS NULL + LEFT JOIN freight.cargo_types cargo_type ON cargo_type.id = COALESCE(cargo.cargo_type_id, b.cargo_type_id) + LEFT JOIN freight.warehouses wh ON wh.id = fee.warehouse_id + LEFT JOIN freight.warehouse_yards yard ON yard.id = fee.yard_id + LEFT JOIN freight.warehouse_zones zone ON zone.id = fee.zone_id + WHERE fee.id = $1 + LIMIT 1`, + [invoice.id, invoice.status], + ); + + return { + bookingReference: row?.bookingReference ?? null, + customerName: row?.customerName ?? null, + inventoryReference: row?.inventoryReference ?? null, + inventoryInfo: row?.inventoryInfo ?? null, + inventoryStatus: row?.inventoryStatus ?? null, + containerNumber: row?.containerNumber ?? null, + cargoDescription: row?.cargoDescription ?? null, + warehouseName: row?.warehouseName ?? null, + yardName: row?.yardName ?? null, + zoneName: row?.zoneName ?? null, + clearanceStatus: row?.clearanceStatus ?? (invoice.status === 'PAID' ? 'FEE PAID - READY FOR RELEASE' : 'PENDING PAYMENT'), + }; + } + private buildInvoiceDocumentHtml( - invoice: WarehouseFeeInvoice & { items: unknown[] }, + invoice: WarehouseFeeInvoiceWithDisplay & { items: unknown[] }, kind: 'INVOICE' | 'RECEIPT', + details: InvoiceDocumentDetails, ): string { const esc = (value: unknown) => String(value ?? '-') @@ -329,8 +405,13 @@ export class WarehouseInvoiceService {
Status${esc(invoice.status.replace(/_/g, ' '))}
Invoice type${esc(invoice.invoiceType.replace(/_/g, ' '))}
-
Booking ID${esc(invoice.bookingId)}
-
Inventory ID${esc(invoice.inventoryId)}
+
Booking reference${esc(details.bookingReference)}
+
Customer${esc(details.customerName)}
+
Inventory reference${esc(details.inventoryReference)}
+
Inventory info${esc(details.inventoryInfo)}
+
Clearance${esc(details.clearanceStatus)}
+
Warehouse${esc(details.warehouseName)}
+
Yard / Zone${esc([details.yardName, details.zoneName].filter(Boolean).join(' / ') || null)}
Period${esc(date(invoice.periodStart))} - ${esc(date(invoice.periodEnd))}
Payment${esc(lastPayment ? `${lastPayment.method ?? 'MANUAL'} / ${date(lastPayment.paidAt)}` : '-')}
diff --git a/apps/edr-freight-api/src/modules/warehouses/warehouse-release-document.service.ts b/apps/edr-freight-api/src/modules/warehouses/warehouse-release-document.service.ts index de9fac6e6..6b78d05bd 100644 --- a/apps/edr-freight-api/src/modules/warehouses/warehouse-release-document.service.ts +++ b/apps/edr-freight-api/src/modules/warehouses/warehouse-release-document.service.ts @@ -100,20 +100,33 @@ export class WarehouseReleaseDocumentService { private htmlToBasicPdfBuffer(html: string): Buffer { const text = this.htmlToPlainText(html); - const lines = this.wrapLines(text, 92).slice(0, 72); + const lines = this.wrapLines(text, 86).slice(0, 52); const body = lines .map((line, index) => { - const prefix = index === 0 ? '50 790 Td' : '0 -12 Td'; - return `${prefix} (${this.escapePdfText(line)}) Tj`; + const y = 770 - index * 12; + const isTitle = index < 2 || /clearance|release order/i.test(line); + const size = index === 0 ? 13 : isTitle ? 11 : 9.6; + const font = isTitle ? 'F2' : 'F1'; + return this.textOp(line, 48, y, size, font); }) .join('\n'); - const stream = `BT\n/F1 10 Tf\n12 TL\n${body}\nET`; + const stream = [ + this.lineOp(48, 752, 548, 752), + this.circularSealOps(470, 690), + body, + this.lineOp(48, 126, 238, 126, '0 0 0'), + this.textOp('Authorized warehouse officer name / signature / date', 48, 110, 9, 'F1'), + this.lineOp(312, 126, 548, 126, '0 0 0'), + this.textOp('Customer or driver name / signature / date', 312, 110, 9, 'F1'), + this.textOp('OFFICIAL WAREHOUSE GATE CLEARANCE DOCUMENT', 145, 52, 9, 'F2', '0.08 0.32 0.18'), + ].join('\n'); const objects = [ '<< /Type /Catalog /Pages 2 0 R >>', '<< /Type /Pages /Kids [3 0 R] /Count 1 >>', - '<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >>', - '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>', + '<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 4 0 R /F2 5 0 R >> >> /Contents 6 0 R >>', + '<< /Type /Font /Subtype /Type1 /BaseFont /Times-Roman >>', + '<< /Type /Font /Subtype /Type1 /BaseFont /Times-Bold >>', `<< /Length ${Buffer.byteLength(stream, 'latin1')} >>\nstream\n${stream}\nendstream`, ]; @@ -178,4 +191,50 @@ export class WarehouseReleaseDocumentService { private escapePdfText(value: string): string { return value.replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)'); } + + private textOp( + text: string, + x: number, + y: number, + size: number, + font: 'F1' | 'F2' = 'F1', + color = '0 0 0', + ): string { + return `BT\n${color} rg\n/${font} ${size} Tf\n${x} ${y} Td\n(${this.escapePdfText(text)}) Tj\nET`; + } + + private lineOp(x1: number, y1: number, x2: number, y2: number, color = '0.08 0.32 0.18'): string { + return `q\n${color} RG\n0.8 w\n${x1} ${y1} m\n${x2} ${y2} l\nS\nQ`; + } + + private circularSealOps(cx: number, cy: number): string { + return [ + 'q', + '0.08 0.32 0.18 RG', + '0.08 0.32 0.18 rg', + '2.2 w', + this.circlePath(cx, cy, 51), + 'S', + '0.8 w', + this.circlePath(cx, cy, 41), + 'S', + this.textOp('EDR', cx - 14, cy + 18, 14, 'F2', '0.08 0.32 0.18'), + this.textOp('WAREHOUSE', cx - 31, cy + 2, 9, 'F2', '0.08 0.32 0.18'), + this.textOp('CLEARED', cx - 27, cy - 16, 11, 'F2', '0.08 0.32 0.18'), + 'Q', + ].join('\n'); + } + + private circlePath(cx: number, cy: number, r: number): string { + const k = 0.5522847498; + const c = r * k; + return [ + `${cx + r} ${cy} m`, + `${cx + r} ${cy + c} ${cx + c} ${cy + r} ${cx} ${cy + r} c`, + `${cx - c} ${cy + r} ${cx - r} ${cy + c} ${cx - r} ${cy} c`, + `${cx - r} ${cy - c} ${cx - c} ${cy - r} ${cx} ${cy - r} c`, + `${cx + c} ${cy - r} ${cx + r} ${cy - c} ${cx + r} ${cy} c`, + 'h', + ].join('\n'); + } } diff --git a/apps/edr-freight-web/backoffice/package.json b/apps/edr-freight-web/backoffice/package.json index 8f0e233b4..67fe4e9cb 100644 --- a/apps/edr-freight-web/backoffice/package.json +++ b/apps/edr-freight-web/backoffice/package.json @@ -5,6 +5,7 @@ "type": "module", "scripts": { "dev": "vite --port 5183", + "prebuild": "node -e \"const fs=require('fs'); fs.rmSync('dist',{recursive:true,force:true});\"", "build": "vite build", "preview": "vite preview --port 5183", "lint": "eslint src", diff --git a/apps/edr-freight-web/backoffice/src/components/warehouses/InventoryDetailModal.tsx b/apps/edr-freight-web/backoffice/src/components/warehouses/InventoryDetailModal.tsx index ad079577b..b0b864e4d 100644 --- a/apps/edr-freight-web/backoffice/src/components/warehouses/InventoryDetailModal.tsx +++ b/apps/edr-freight-web/backoffice/src/components/warehouses/InventoryDetailModal.tsx @@ -24,6 +24,15 @@ function DetailRow({ label, value }: { label: string; value: React.ReactNode }) } export function InventoryDetailModal({ opened, onClose, item }: InventoryDetailModalProps) { + const bookingReference = item?.booking?.reference ?? '-'; + const inventorySummary = [ + item?.status?.replace(/_/g, ' '), + item?.releaseOrderReference ? `Release ${item.releaseOrderReference}` : null, + item?.warehouse ? `${item.warehouse.name} (${item.warehouse.code})` : null, + ] + .filter(Boolean) + .join(' / '); + return ( {!item ? ( @@ -33,10 +42,10 @@ export function InventoryDetailModal({ opened, onClose, item }: InventoryDetailM - {item.booking?.reference ?? item.bookingId ?? item.id} + {bookingReference} - Inventory ID: {item.id} + {inventorySummary || 'Inventory information'} @@ -51,12 +60,12 @@ export function InventoryDetailModal({ opened, onClose, item }: InventoryDetailM + - - - + + diff --git a/apps/edr-freight-web/backoffice/src/components/warehouses/warehousePdf.ts b/apps/edr-freight-web/backoffice/src/components/warehouses/warehousePdf.ts index b64ae9384..9f91456c8 100644 --- a/apps/edr-freight-web/backoffice/src/components/warehouses/warehousePdf.ts +++ b/apps/edr-freight-web/backoffice/src/components/warehouses/warehousePdf.ts @@ -130,12 +130,22 @@ function buildSimplePdf(lines: PdfLine[], rawOps: string[] = []): Blob { export function buildWarehouseInvoicePdf(invoice: WarehouseFeeInvoice, kind: 'INVOICE' | 'RECEIPT') { const paid = kind === 'RECEIPT' || invoice.status === 'PAID'; const title = `Warehouse Fee ${kind === 'RECEIPT' ? 'Receipt' : 'Invoice'}`; + const bookingReference = firstText(invoice.bookingReference); + const customerName = firstText(invoice.customerName); + const inventoryReference = firstText(invoice.inventoryReference); + const inventoryInfo = firstText(invoice.inventoryInfo, invoice.containerNumber, invoice.cargoDescription); + const clearanceStatus = firstText( + invoice.clearanceStatus, + paid ? 'FEE PAID - READY FOR RELEASE' : 'PENDING PAYMENT', + ); const lines: PdfLine[] = [ { text: 'Ethio-Djibouti Railway S.C.', size: 12, bold: true, yGap: 0, align: 'center' }, { text: title, size: 23, bold: true, yGap: 28, align: 'center' }, { text: `Document No: ${invoice.invoiceNumber}`, size: 12, bold: true, yGap: 32, align: 'center' }, { text: `Status: ${invoice.status.replace(/_/g, ' ')} Type: ${invoice.invoiceType.replace(/_/g, ' ')}`, align: 'center' }, - { text: `Booking: ${invoice.bookingId ?? '-'} Inventory: ${invoice.inventoryId}`, align: 'center' }, + { text: `Booking Reference: ${bookingReference} Customer: ${customerName}`, align: 'center' }, + { text: `Inventory Reference: ${inventoryReference} Inventory Info: ${inventoryInfo}`, align: 'center' }, + { text: `Clearance: ${clearanceStatus}`, align: 'center' }, { text: `Issued: ${fmtDate(invoice.issuedAt)} Paid At: ${fmtDate(invoice.paidAt)}`, align: 'center' }, { text: 'ITEMS', size: 13, bold: true, yGap: 30, align: 'center' }, ...(invoice.items ?? []).flatMap((item) => [ @@ -201,15 +211,33 @@ export function buildWarehouseExitPaperPdf(input: WarehouseFeeInvoice | Warehous const releasedItem = context.releasedItem; const inventory = context.inventory ?? releasedItem; const releasedAt = context.releasedAt ?? new Date(); - const releaseReference = `REL-${invoice.inventoryId.slice(0, 8).toUpperCase()}`; + const releaseReference = firstText( + inventory?.releaseOrderReference, + releasedItem?.releaseOrderReference, + invoice.inventoryReference, + booking?.reference ? `REL-${booking.reference.replace(/^BK-?/i, '')}` : null, + ); const customerName = firstText( booking?.company?.name, booking?.company?.companyName, booking?.company?.label, booking?.company?.contactPersonName, - invoice.customerId, + invoice.customerName, ); const weightTons = firstText(tons(booking?.cargoTotalWeightVgm), tons(inventory?.weight)); + const inventoryInfo = firstText( + invoice.inventoryInfo, + invoice.containerNumber, + invoice.cargoDescription, + inventory?.status, + releasedItem?.status, + ); + const bookingReference = firstText( + booking?.reference, + (inventory as unknown as { bookingReference?: string })?.bookingReference, + invoice.bookingReference, + releasedItem?.booking?.reference, + ); return buildSimplePdf([ { text: 'Ethio-Djibouti Railway S.C.', size: 12, bold: true, yGap: 0, align: 'center' }, @@ -217,16 +245,18 @@ export function buildWarehouseExitPaperPdf(input: WarehouseFeeInvoice | Warehous { text: '[ GATE CLEARANCE ]', size: 14, bold: true, color: 'green', yGap: 24, align: 'center' }, { text: `Release Reference: ${releaseReference}`, bold: true, yGap: 30, align: 'center' }, { text: `Invoice No: ${invoice.invoiceNumber}`, align: 'center' }, - { text: `Booking Reference: ${firstText(booking?.reference, (inventory as unknown as { bookingReference?: string })?.bookingReference, invoice.bookingId, releasedItem?.bookingId)}`, align: 'center' }, + { text: `Booking Reference: ${bookingReference}`, align: 'center' }, { text: `Customer: ${customerName}`, align: 'center' }, - { text: `Warehouse: ${firstText(inventory?.warehouse?.name, inventory?.warehouse?.code, invoice.warehouseId, releasedItem?.warehouseId)}`, align: 'center' }, - { text: `Yard: ${firstText(inventory?.yard?.name, inventory?.yard?.code, invoice.yardId, releasedItem?.yardId)}`, align: 'center' }, - { text: `Zone: ${firstText(inventory?.zone?.name, inventory?.zone?.code, invoice.zoneId, releasedItem?.zoneId)}`, align: 'center' }, + { text: `Inventory Info: ${inventoryInfo}`, align: 'center' }, + { text: `Warehouse: ${firstText(inventory?.warehouse?.name, inventory?.warehouse?.code)}`, align: 'center' }, + { text: `Yard: ${firstText(inventory?.yard?.name, inventory?.yard?.code)}`, align: 'center' }, + { text: `Zone: ${firstText(inventory?.zone?.name, inventory?.zone?.code)}`, align: 'center' }, { text: `Booking Container: ${containerSummary(booking, inventory)}`, align: 'center' }, { text: `Weight: ${weightTons}`, align: 'center' }, { text: `Inventory Status: ${releasedItem?.status ?? inventory?.status ?? 'RELEASED'}`, align: 'center' }, + { text: `Clearance: ${invoice.clearanceStatus ?? 'CLEARED FOR WAREHOUSE EXIT'}`, bold: true, color: 'green', align: 'center' }, { text: `Release Date & Time: ${fmtDate(releasedAt)}`, align: 'center' }, - { text: 'This document authorizes the listed booking/goods to leave the warehouse gate.', yGap: 30, align: 'center' }, + { text: 'This sealed document authorizes the listed booking/goods to leave the warehouse gate.', yGap: 30, align: 'center' }, ], [ ...buildAuthorizationBand('CLEARED'), textOp('Warehouse officer name / signature / date:', 72, 190, 10), diff --git a/apps/edr-freight-web/backoffice/src/constants/apiConfig.ts b/apps/edr-freight-web/backoffice/src/constants/apiConfig.ts index 3f552ab15..061305b11 100644 --- a/apps/edr-freight-web/backoffice/src/constants/apiConfig.ts +++ b/apps/edr-freight-web/backoffice/src/constants/apiConfig.ts @@ -1,10 +1,6 @@ export const API_BASE_URL = -<<<<<<< HEAD - import.meta.env.VITE_API_URL?.replace(/\/+$/, '') || 'https://edrfreightapi.triaplc.com'; -======= import.meta.env.VITE_BASE_API_URL || import.meta.env.VITE_API_URL || 'https://edrfreightapi.triaplc.com'; //export const API_BASE_URL = 'http://localhost:3001'; ->>>>>>> 2f06817e4811190cdc9ef8a2975aeca1e31c3484 diff --git a/apps/edr-freight-web/backoffice/src/types/warehouse.ts b/apps/edr-freight-web/backoffice/src/types/warehouse.ts index 74008d81b..b935ac2ec 100644 --- a/apps/edr-freight-web/backoffice/src/types/warehouse.ts +++ b/apps/edr-freight-web/backoffice/src/types/warehouse.ts @@ -737,8 +737,16 @@ export interface WarehouseFeeInvoice { id: string; invoiceNumber: string; bookingId?: string | null; + bookingReference?: string | null; customerId?: string | null; + customerName?: string | null; inventoryId: string; + inventoryReference?: string | null; + inventoryInfo?: string | null; + inventoryStatus?: string | null; + containerNumber?: string | null; + cargoDescription?: string | null; + clearanceStatus?: string | null; facilityId?: string | null; warehouseId?: string | null; yardId?: string | null;