mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 13:40:57 +00:00
eims integration master test complete
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import { InvoiceDocumentModel, InvoiceDocumentService } from "./invoice-document.service";
|
||||
|
||||
const model = (over: Partial<InvoiceDocumentModel> = {}): InvoiceDocumentModel => ({
|
||||
kind: "INVOICE",
|
||||
title: "Freight",
|
||||
documentNumber: "INV-20260812-00001",
|
||||
issuedAt: new Date(2026, 7, 12),
|
||||
status: "PENDING",
|
||||
currency: "ETB",
|
||||
summary: [{ label: "Status", value: "PENDING" }],
|
||||
lines: [],
|
||||
totals: [{ label: "Total", amount: 100, grand: true }],
|
||||
...over,
|
||||
});
|
||||
|
||||
describe("InvoiceDocumentService.buildHtml — EIMS QR", () => {
|
||||
const service = new InvoiceDocumentService({} as never, {} as never);
|
||||
|
||||
it("renders no QR block when qrImageUrl is unset", () => {
|
||||
const html = service.buildHtml(model());
|
||||
expect(html).not.toContain('class="qr"');
|
||||
});
|
||||
|
||||
it("renders the QR image when qrImageUrl is set", () => {
|
||||
const html = service.buildHtml(model({ qrImageUrl: "data:image/png;base64,QR" }));
|
||||
expect(html).toContain('class="qr"');
|
||||
expect(html).toContain('src="data:image/png;base64,QR"');
|
||||
});
|
||||
|
||||
it("still shows the IRN text row via the ordinary summary grid", () => {
|
||||
const html = service.buildHtml(
|
||||
model({ summary: [{ label: "EIMS IRN", value: "IRN-123" }] }),
|
||||
);
|
||||
expect(html).toContain("EIMS IRN");
|
||||
expect(html).toContain("IRN-123");
|
||||
});
|
||||
|
||||
it("widens the summary's right margin only when a QR is present, to clear the QR block", () => {
|
||||
// "summary-with-qr" also appears in the always-present <style> rule, so the check has to be
|
||||
// the actual div's class attribute, not a bare substring match.
|
||||
expect(service.buildHtml(model())).not.toContain('class="summary summary-with-qr"');
|
||||
expect(service.buildHtml(model({ qrImageUrl: "data:image/png;base64,QR" }))).toContain(
|
||||
'class="summary summary-with-qr"',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -62,6 +62,12 @@ export interface InvoiceDocumentModel {
|
||||
* explicitly only to override that default for one document.
|
||||
*/
|
||||
stampImageUrl?: string | null;
|
||||
/**
|
||||
* MoR EIMS verification QR (data URL, pre-rendered by the caller from `Invoice.eimsSignedQr` —
|
||||
* see that column's comment). Set only once an invoice is actually registered; the IRN text
|
||||
* itself goes through the ordinary `summary` rows, not a dedicated field.
|
||||
*/
|
||||
qrImageUrl?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,9 +102,11 @@ export class InvoiceDocumentService {
|
||||
// summary grid, line-item table, totals) from the model — not a flat
|
||||
// plain-text dump — so it still reads as a proper invoice document.
|
||||
// ponytail: still draws the plain vector seal, not the uploaded stamp
|
||||
// image — embedding a raster image needs a new PDF XObject primitive
|
||||
// in styled-pdf.util.ts. Upgrade when the Chromium-less path needs to
|
||||
// carry the real stamp too; today it's a rare degraded fallback.
|
||||
// image, and omits the EIMS QR entirely — embedding a raster image
|
||||
// needs a new PDF XObject primitive in styled-pdf.util.ts. Upgrade
|
||||
// when the Chromium-less path needs to carry the real stamp/QR too;
|
||||
// today it's a rare degraded fallback. The IRN text itself still
|
||||
// comes through (buildFallbackPdf renders model.summary same as HTML).
|
||||
fallback: () => this.buildFallbackPdf(resolvedModel),
|
||||
}),
|
||||
};
|
||||
@@ -243,6 +251,10 @@ export class InvoiceDocumentService {
|
||||
const sealInner = sealMarkup(model.stampImageUrl, sealText);
|
||||
const sealCssClass = sealClass(model.stampImageUrl);
|
||||
|
||||
const qrMarkup = model.qrImageUrl
|
||||
? `<div class="qr"><img src="${esc(model.qrImageUrl)}" alt="EIMS verification QR" /><span>Scan to verify (MoR EIMS)</span></div>`
|
||||
: "";
|
||||
|
||||
const summaryRows = model.summary
|
||||
.map((row) => `<div><span>${esc(row.label)}</span>${esc(row.value)}</div>`)
|
||||
.join("");
|
||||
@@ -281,8 +293,19 @@ export class InvoiceDocumentService {
|
||||
.meta strong { display: block; color: #0f172a; font-size: 17px; margin-top: 5px; }
|
||||
.seal { position: absolute; right: 28px; top: 118px; width: 116px; height: 116px; border: 4px double #0f766e; border-radius: 999px; color: #0f766e; display: flex; align-items: center; justify-content: center; text-align: center; font-weight: 800; font-size: 18px; transform: rotate(-14deg); opacity: .82; }
|
||||
${sealImageCss()}
|
||||
.qr { position: absolute; right: 160px; top: 118px; width: 90px; text-align: center; }
|
||||
.qr img { width: 90px; height: 90px; }
|
||||
.qr span { display: block; font-size: 7px; color: #64748b; margin-top: 3px; }
|
||||
.summary { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 28px; margin: 24px 150px 16px 0; font-size: 13px; }
|
||||
.summary div { border-bottom: 1px solid #e2e8f0; padding: 7px 0; }
|
||||
/* The QR block (right:160px, width:90px) sits further inward than the seal alone did — the
|
||||
150px margin above only ever cleared the seal, so a QR-bearing invoice needs more room. */
|
||||
.summary.summary-with-qr { margin-right: 270px; }
|
||||
/* min-width: 0 overrides Grid's default min-width:auto on grid items — without it, a long
|
||||
unbroken value (a 20-digit VAT number) forces its column wider to fit un-wrapped rather than
|
||||
honouring overflow-wrap, which is what actually let text bleed into the seal/QR overlay
|
||||
(confirmed by isolating the two: margin-right alone already positioned the box correctly;
|
||||
the text itself was still escaping the box's own right edge until this was added). */
|
||||
.summary div { border-bottom: 1px solid #e2e8f0; padding: 7px 0; overflow-wrap: break-word; min-width: 0; }
|
||||
.summary span { color: #64748b; display: block; font-size: 11px; margin-bottom: 3px; }
|
||||
table { width: 100%; border-collapse: collapse; margin-top: 18px; }
|
||||
th { text-align: left; background: #f8fafc; color: #475569; }
|
||||
@@ -309,7 +332,8 @@ export class InvoiceDocumentService {
|
||||
</div>
|
||||
</div>
|
||||
<div class="${sealCssClass}">${sealInner}</div>
|
||||
<div class="summary">${summaryRows}</div>
|
||||
${qrMarkup}
|
||||
<div class="summary${model.qrImageUrl ? " summary-with-qr" : ""}">${summaryRows}</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
Reference in New Issue
Block a user