@endora-commerce/mod-invoices 0.0.0-stage → 0.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +59 -2
- package/dist/admin/components/InvoiceSectionTabs.d.ts +22 -0
- package/dist/admin/components/InvoiceSectionTabs.d.ts.map +1 -0
- package/dist/admin/components/InvoiceSectionTabs.js +34 -0
- package/dist/admin/components/InvoiceSectionTabs.js.map +1 -0
- package/dist/admin/index.d.ts +34 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +69 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/InvoiceDetail.d.ts +10 -0
- package/dist/admin/pages/InvoiceDetail.d.ts.map +1 -0
- package/dist/admin/pages/InvoiceDetail.js +111 -0
- package/dist/admin/pages/InvoiceDetail.js.map +1 -0
- package/dist/admin/pages/InvoiceTemplateEditor.d.ts +11 -0
- package/dist/admin/pages/InvoiceTemplateEditor.d.ts.map +1 -0
- package/dist/admin/pages/InvoiceTemplateEditor.js +188 -0
- package/dist/admin/pages/InvoiceTemplateEditor.js.map +1 -0
- package/dist/admin/pages/InvoiceTemplatesPage.d.ts +10 -0
- package/dist/admin/pages/InvoiceTemplatesPage.d.ts.map +1 -0
- package/dist/admin/pages/InvoiceTemplatesPage.js +60 -0
- package/dist/admin/pages/InvoiceTemplatesPage.js.map +1 -0
- package/dist/admin/pages/InvoicesList.d.ts +9 -0
- package/dist/admin/pages/InvoicesList.d.ts.map +1 -0
- package/dist/admin/pages/InvoicesList.js +116 -0
- package/dist/admin/pages/InvoicesList.js.map +1 -0
- package/dist/admin/templates/described-blocks.d.ts +67 -0
- package/dist/admin/templates/described-blocks.d.ts.map +1 -0
- package/dist/admin/templates/described-blocks.js +83 -0
- package/dist/admin/templates/described-blocks.js.map +1 -0
- package/dist/admin/templates/invoice-builder-plugin.d.ts +4 -0
- package/dist/admin/templates/invoice-builder-plugin.d.ts.map +1 -0
- package/dist/admin/templates/invoice-builder-plugin.js +12 -0
- package/dist/admin/templates/invoice-builder-plugin.js.map +1 -0
- package/dist/admin/templates/invoice-puck-config.d.ts +37 -0
- package/dist/admin/templates/invoice-puck-config.d.ts.map +1 -0
- package/dist/admin/templates/invoice-puck-config.js +733 -0
- package/dist/admin/templates/invoice-puck-config.js.map +1 -0
- package/dist/backend/email-templates/invoice-issued.default.d.ts +10 -0
- package/dist/backend/email-templates/invoice-issued.default.d.ts.map +1 -0
- package/dist/backend/email-templates/invoice-issued.default.js +45 -0
- package/dist/backend/email-templates/invoice-issued.default.js.map +1 -0
- package/dist/backend/entities/invoice-external-attachment.entity.d.ts +20 -0
- package/dist/backend/entities/invoice-external-attachment.entity.d.ts.map +1 -0
- package/dist/backend/entities/invoice-external-attachment.entity.js +77 -0
- package/dist/backend/entities/invoice-external-attachment.entity.js.map +1 -0
- package/dist/backend/entities/invoice-line.entity.d.ts +20 -0
- package/dist/backend/entities/invoice-line.entity.d.ts.map +1 -0
- package/dist/backend/entities/invoice-line.entity.js +82 -0
- package/dist/backend/entities/invoice-line.entity.js.map +1 -0
- package/dist/backend/entities/invoice-number-counter.entity.d.ts +18 -0
- package/dist/backend/entities/invoice-number-counter.entity.d.ts.map +1 -0
- package/dist/backend/entities/invoice-number-counter.entity.js +63 -0
- package/dist/backend/entities/invoice-number-counter.entity.js.map +1 -0
- package/dist/backend/entities/invoice-template.entity.d.ts +22 -0
- package/dist/backend/entities/invoice-template.entity.d.ts.map +1 -0
- package/dist/backend/entities/invoice-template.entity.js +84 -0
- package/dist/backend/entities/invoice-template.entity.js.map +1 -0
- package/dist/backend/entities/invoice.entity.d.ts +64 -0
- package/dist/backend/entities/invoice.entity.d.ts.map +1 -0
- package/dist/backend/entities/invoice.entity.js +202 -0
- package/dist/backend/entities/invoice.entity.js.map +1 -0
- package/dist/backend/index.d.ts +111 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +291 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/pdf-components/descriptor.d.ts +29 -0
- package/dist/backend/pdf-components/descriptor.d.ts.map +1 -0
- package/dist/backend/pdf-components/descriptor.js +227 -0
- package/dist/backend/pdf-components/descriptor.js.map +1 -0
- package/dist/backend/pdf-components/embed-logo-images.d.ts +35 -0
- package/dist/backend/pdf-components/embed-logo-images.d.ts.map +1 -0
- package/dist/backend/pdf-components/embed-logo-images.js +101 -0
- package/dist/backend/pdf-components/embed-logo-images.js.map +1 -0
- package/dist/backend/pdf-components/sample.d.ts +4 -0
- package/dist/backend/pdf-components/sample.d.ts.map +1 -0
- package/dist/backend/pdf-components/sample.js +75 -0
- package/dist/backend/pdf-components/sample.js.map +1 -0
- package/dist/backend/pdf-components/sections.d.ts +34 -0
- package/dist/backend/pdf-components/sections.d.ts.map +1 -0
- package/dist/backend/pdf-components/sections.js +445 -0
- package/dist/backend/pdf-components/sections.js.map +1 -0
- package/dist/backend/pdf-components/tree-mapper.d.ts +37 -0
- package/dist/backend/pdf-components/tree-mapper.d.ts.map +1 -0
- package/dist/backend/pdf-components/tree-mapper.js +76 -0
- package/dist/backend/pdf-components/tree-mapper.js.map +1 -0
- package/dist/backend/plugin.d.ts +124 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +87 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +41 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +219 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.customer.d.ts +33 -0
- package/dist/backend/routes.customer.d.ts.map +1 -0
- package/dist/backend/routes.customer.js +124 -0
- package/dist/backend/routes.customer.js.map +1 -0
- package/dist/backend/seeds/generic-invoice-template.d.ts +18 -0
- package/dist/backend/seeds/generic-invoice-template.d.ts.map +1 -0
- package/dist/backend/seeds/generic-invoice-template.js +174 -0
- package/dist/backend/seeds/generic-invoice-template.js.map +1 -0
- package/dist/backend/services/amount-to-words.d.ts +20 -0
- package/dist/backend/services/amount-to-words.d.ts.map +1 -0
- package/dist/backend/services/amount-to-words.js +232 -0
- package/dist/backend/services/amount-to-words.js.map +1 -0
- package/dist/backend/services/auto-issue-reactor.d.ts +53 -0
- package/dist/backend/services/auto-issue-reactor.d.ts.map +1 -0
- package/dist/backend/services/auto-issue-reactor.js +122 -0
- package/dist/backend/services/auto-issue-reactor.js.map +1 -0
- package/dist/backend/services/corrective-invoice.d.ts +38 -0
- package/dist/backend/services/corrective-invoice.d.ts.map +1 -0
- package/dist/backend/services/corrective-invoice.js +257 -0
- package/dist/backend/services/corrective-invoice.js.map +1 -0
- package/dist/backend/services/cross-module-context.d.ts +82 -0
- package/dist/backend/services/cross-module-context.d.ts.map +1 -0
- package/dist/backend/services/cross-module-context.js +93 -0
- package/dist/backend/services/cross-module-context.js.map +1 -0
- package/dist/backend/services/duplicate-number-refusal.d.ts +33 -0
- package/dist/backend/services/duplicate-number-refusal.d.ts.map +1 -0
- package/dist/backend/services/duplicate-number-refusal.js +92 -0
- package/dist/backend/services/duplicate-number-refusal.js.map +1 -0
- package/dist/backend/services/erp-sale-document-write-port.d.ts +37 -0
- package/dist/backend/services/erp-sale-document-write-port.d.ts.map +1 -0
- package/dist/backend/services/erp-sale-document-write-port.js +192 -0
- package/dist/backend/services/erp-sale-document-write-port.js.map +1 -0
- package/dist/backend/services/invoice-attachment-fetch-registry.d.ts +75 -0
- package/dist/backend/services/invoice-attachment-fetch-registry.d.ts.map +1 -0
- package/dist/backend/services/invoice-attachment-fetch-registry.js +81 -0
- package/dist/backend/services/invoice-attachment-fetch-registry.js.map +1 -0
- package/dist/backend/services/invoice-copy-host.service.d.ts +9 -0
- package/dist/backend/services/invoice-copy-host.service.d.ts.map +1 -0
- package/dist/backend/services/invoice-copy-host.service.js +52 -0
- package/dist/backend/services/invoice-copy-host.service.js.map +1 -0
- package/dist/backend/services/invoice-email-dispatch.d.ts +89 -0
- package/dist/backend/services/invoice-email-dispatch.d.ts.map +1 -0
- package/dist/backend/services/invoice-email-dispatch.js +138 -0
- package/dist/backend/services/invoice-email-dispatch.js.map +1 -0
- package/dist/backend/services/invoice-line-builder.d.ts +29 -0
- package/dist/backend/services/invoice-line-builder.d.ts.map +1 -0
- package/dist/backend/services/invoice-line-builder.js +46 -0
- package/dist/backend/services/invoice-line-builder.js.map +1 -0
- package/dist/backend/services/invoice-number-collisions.d.ts +17 -0
- package/dist/backend/services/invoice-number-collisions.d.ts.map +1 -0
- package/dist/backend/services/invoice-number-collisions.js +154 -0
- package/dist/backend/services/invoice-number-collisions.js.map +1 -0
- package/dist/backend/services/invoice-number-generator.d.ts +96 -0
- package/dist/backend/services/invoice-number-generator.d.ts.map +1 -0
- package/dist/backend/services/invoice-number-generator.js +157 -0
- package/dist/backend/services/invoice-number-generator.js.map +1 -0
- package/dist/backend/services/invoice-pdf-block-registry.d.ts +65 -0
- package/dist/backend/services/invoice-pdf-block-registry.d.ts.map +1 -0
- package/dist/backend/services/invoice-pdf-block-registry.js +94 -0
- package/dist/backend/services/invoice-pdf-block-registry.js.map +1 -0
- package/dist/backend/services/invoice-pdf-renderer.d.ts +57 -0
- package/dist/backend/services/invoice-pdf-renderer.d.ts.map +1 -0
- package/dist/backend/services/invoice-pdf-renderer.js +158 -0
- package/dist/backend/services/invoice-pdf-renderer.js.map +1 -0
- package/dist/backend/services/invoice-pdf.d.ts +24 -0
- package/dist/backend/services/invoice-pdf.d.ts.map +1 -0
- package/dist/backend/services/invoice-pdf.js +52 -0
- package/dist/backend/services/invoice-pdf.js.map +1 -0
- package/dist/backend/services/invoice-placement-apply-port.d.ts +29 -0
- package/dist/backend/services/invoice-placement-apply-port.d.ts.map +1 -0
- package/dist/backend/services/invoice-placement-apply-port.js +40 -0
- package/dist/backend/services/invoice-placement-apply-port.js.map +1 -0
- package/dist/backend/services/invoice-read-port.d.ts +44 -0
- package/dist/backend/services/invoice-read-port.d.ts.map +1 -0
- package/dist/backend/services/invoice-read-port.js +95 -0
- package/dist/backend/services/invoice-read-port.js.map +1 -0
- package/dist/backend/services/invoice-service.d.ts +94 -0
- package/dist/backend/services/invoice-service.d.ts.map +1 -0
- package/dist/backend/services/invoice-service.js +374 -0
- package/dist/backend/services/invoice-service.js.map +1 -0
- package/dist/backend/services/invoice-template-service.d.ts +43 -0
- package/dist/backend/services/invoice-template-service.d.ts.map +1 -0
- package/dist/backend/services/invoice-template-service.js +147 -0
- package/dist/backend/services/invoice-template-service.js.map +1 -0
- package/dist/backend/services/numbering-configuration.d.ts +76 -0
- package/dist/backend/services/numbering-configuration.d.ts.map +1 -0
- package/dist/backend/services/numbering-configuration.js +176 -0
- package/dist/backend/services/numbering-configuration.js.map +1 -0
- package/dist/backend/services/numbering-write-validator.d.ts +22 -0
- package/dist/backend/services/numbering-write-validator.d.ts.map +1 -0
- package/dist/backend/services/numbering-write-validator.js +82 -0
- package/dist/backend/services/numbering-write-validator.js.map +1 -0
- package/dist/backend/services/seller-settings.d.ts +24 -0
- package/dist/backend/services/seller-settings.d.ts.map +1 -0
- package/dist/backend/services/seller-settings.js +38 -0
- package/dist/backend/services/seller-settings.js.map +1 -0
- package/dist/backend/services/vendor-number-hold.d.ts +10 -0
- package/dist/backend/services/vendor-number-hold.d.ts.map +1 -0
- package/dist/backend/services/vendor-number-hold.js +14 -0
- package/dist/backend/services/vendor-number-hold.js.map +1 -0
- package/dist/manifest.d.ts +203 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +390 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260629T125121_invoices_module.d.ts +17 -0
- package/dist/migrations/20260629T125121_invoices_module.d.ts.map +1 -0
- package/dist/migrations/20260629T125121_invoices_module.js +117 -0
- package/dist/migrations/20260629T125121_invoices_module.js.map +1 -0
- package/dist/migrations/20260801T111000_invoices_generic_template_reseed.d.ts +6 -0
- package/dist/migrations/20260801T111000_invoices_generic_template_reseed.d.ts.map +1 -0
- package/dist/migrations/20260801T111000_invoices_generic_template_reseed.js +25 -0
- package/dist/migrations/20260801T111000_invoices_generic_template_reseed.js.map +1 -0
- package/dist/migrations/20260817T201110_invoices_correction_idempotency_key.d.ts +20 -0
- package/dist/migrations/20260817T201110_invoices_correction_idempotency_key.d.ts.map +1 -0
- package/dist/migrations/20260817T201110_invoices_correction_idempotency_key.js +33 -0
- package/dist/migrations/20260817T201110_invoices_correction_idempotency_key.js.map +1 -0
- package/dist/migrations/20260903T101756_invoices_namespace_block_names.d.ts +6 -0
- package/dist/migrations/20260903T101756_invoices_namespace_block_names.d.ts.map +1 -0
- package/dist/migrations/20260903T101756_invoices_namespace_block_names.js +56 -0
- package/dist/migrations/20260903T101756_invoices_namespace_block_names.js.map +1 -0
- package/dist/migrations/20260914T133603_invoices_erp_imported_sale_documents.d.ts +12 -0
- package/dist/migrations/20260914T133603_invoices_erp_imported_sale_documents.d.ts.map +1 -0
- package/dist/migrations/20260914T133603_invoices_erp_imported_sale_documents.js +87 -0
- package/dist/migrations/20260914T133603_invoices_erp_imported_sale_documents.js.map +1 -0
- package/dist/migrations/20260926T230239_invoices_import_identity.d.ts +33 -0
- package/dist/migrations/20260926T230239_invoices_import_identity.d.ts.map +1 -0
- package/dist/migrations/20260926T230239_invoices_import_identity.js +83 -0
- package/dist/migrations/20260926T230239_invoices_import_identity.js.map +1 -0
- package/dist/migrations/index.d.ts +32 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +39 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/ports/index.d.ts +93 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +2 -0
- package/dist/ports/index.js.map +1 -0
- package/docs/invoices.md +113 -0
- package/i18n/en.json +45 -0
- package/i18n/pl.json +45 -0
- package/package.json +119 -3
- package/tailwind.css +14 -0
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createColorField, definePageBuilderComponent } from '@endora-commerce/page-builder-core';
|
|
3
|
+
import { createImageAssetField, createImageSourceField, createImageUrlField, } from '@endora-commerce/page-builder-admin';
|
|
4
|
+
import { fetchAssetDetail } from '@endora-commerce/admin-kit/components';
|
|
5
|
+
import { toAbsoluteAssetUrl } from '@endora-commerce/admin-kit/lib';
|
|
6
|
+
/**
|
|
7
|
+
* Dedicated Puck config for invoice PDF templates (feature 047, US6). Kept in
|
|
8
|
+
* the admin module (not the shared cms-components package) because invoice
|
|
9
|
+
* authoring is admin-only. The real PDF is produced server-side by pdfmake
|
|
10
|
+
* (`backend/.../pdf-components/sections.ts`); these on-canvas renderers mirror
|
|
11
|
+
* those section layouts against a representative sample invoice so the author
|
|
12
|
+
* sees the actual content of each dropped block instead of a placeholder.
|
|
13
|
+
* Component names + fields MUST match the backend tree mapper / descriptor.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Representative sample invoice — a frontend mirror of the backend
|
|
17
|
+
* `sampleInvoiceDetail()` used by the `/preview` endpoint, so the on-canvas
|
|
18
|
+
* content matches the rendered PDF.
|
|
19
|
+
*/
|
|
20
|
+
const sampleInvoice = {
|
|
21
|
+
kind: 'invoice',
|
|
22
|
+
number: 'INV-2026-0042',
|
|
23
|
+
currency: 'USD',
|
|
24
|
+
issuedAt: '2026-05-04',
|
|
25
|
+
saleDate: '2026-05-04',
|
|
26
|
+
paymentDueDate: '2026-05-18',
|
|
27
|
+
paymentMethod: 'Bank transfer',
|
|
28
|
+
netTotal: 2000,
|
|
29
|
+
taxTotal: 400,
|
|
30
|
+
grossTotal: 2400,
|
|
31
|
+
paidTotal: 0,
|
|
32
|
+
amountDue: 2400,
|
|
33
|
+
amountInWords: 'two thousand four hundred 00/100',
|
|
34
|
+
ksefReferenceNumber: null,
|
|
35
|
+
ksefProcessedAt: null,
|
|
36
|
+
lines: [
|
|
37
|
+
{
|
|
38
|
+
ordinal: 1,
|
|
39
|
+
name: 'Cloud hosting — annual plan',
|
|
40
|
+
unit: 'ea',
|
|
41
|
+
quantity: 1,
|
|
42
|
+
unitNetPrice: 500,
|
|
43
|
+
taxRate: 0.2,
|
|
44
|
+
netValue: 500,
|
|
45
|
+
grossValue: 600,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
ordinal: 2,
|
|
49
|
+
name: 'Professional services — onboarding',
|
|
50
|
+
unit: 'hr',
|
|
51
|
+
quantity: 10,
|
|
52
|
+
unitNetPrice: 150,
|
|
53
|
+
taxRate: 0.2,
|
|
54
|
+
netValue: 1500,
|
|
55
|
+
grossValue: 1800,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
vatSummary: [{ taxRate: 0.2, netTotal: 2000, vatAmount: 400, grossTotal: 2400 }],
|
|
59
|
+
seller: {
|
|
60
|
+
legalName: 'Acme Supplies Ltd.',
|
|
61
|
+
addressLine1: '100 Market Street, Suite 4',
|
|
62
|
+
addressLine2: '',
|
|
63
|
+
postalCode: '10001',
|
|
64
|
+
city: 'New York',
|
|
65
|
+
taxId: '12-3456789',
|
|
66
|
+
bankName: 'Example Bank',
|
|
67
|
+
bankAccount: 'US64 EXAMPLE 0000 0000 0000 00',
|
|
68
|
+
swift: 'EXAMPUS33',
|
|
69
|
+
},
|
|
70
|
+
buyer: {
|
|
71
|
+
name: 'Globex Trading Co.',
|
|
72
|
+
taxId: '98-7654321',
|
|
73
|
+
addressLine1: '250 Commerce Avenue',
|
|
74
|
+
postalCode: '60601',
|
|
75
|
+
city: 'Chicago',
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
const TITLES = {
|
|
79
|
+
invoice: 'Invoice',
|
|
80
|
+
};
|
|
81
|
+
function money(n) {
|
|
82
|
+
return n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
83
|
+
}
|
|
84
|
+
function pct(rate) {
|
|
85
|
+
return `${Math.round(rate * 100)}%`;
|
|
86
|
+
}
|
|
87
|
+
/** Interpolate `{{var invoice.x}}` / `{{var seller.x}}` tokens (mirrors backend). */
|
|
88
|
+
function interpolate(text) {
|
|
89
|
+
const inv = sampleInvoice;
|
|
90
|
+
const map = {
|
|
91
|
+
'invoice.number': inv.number,
|
|
92
|
+
'invoice.total': money(inv.grossTotal),
|
|
93
|
+
'invoice.currency': inv.currency,
|
|
94
|
+
'invoice.issuedAt': inv.issuedAt,
|
|
95
|
+
'invoice.saleDate': inv.saleDate,
|
|
96
|
+
'invoice.amountDue': money(inv.amountDue),
|
|
97
|
+
'buyer.name': inv.buyer.name,
|
|
98
|
+
'seller.legalName': inv.seller.legalName,
|
|
99
|
+
'seller.taxId': inv.seller.taxId,
|
|
100
|
+
};
|
|
101
|
+
return text.replace(/\{\{\s*var\s+([\w.]+)\s*\}\}/g, (_m, key) => map[key] ?? '');
|
|
102
|
+
}
|
|
103
|
+
const YES_NO = [
|
|
104
|
+
{ label: 'Yes', value: true },
|
|
105
|
+
{ label: 'No', value: false },
|
|
106
|
+
];
|
|
107
|
+
const ALIGN_OPTIONS = [
|
|
108
|
+
{ label: 'Left', value: 'left' },
|
|
109
|
+
{ label: 'Center', value: 'center' },
|
|
110
|
+
{ label: 'Right', value: 'right' },
|
|
111
|
+
];
|
|
112
|
+
function radio(label) {
|
|
113
|
+
return { type: 'radio', label, options: YES_NO };
|
|
114
|
+
}
|
|
115
|
+
function alignField(label = 'Align') {
|
|
116
|
+
return { type: 'select', label, options: ALIGN_OPTIONS };
|
|
117
|
+
}
|
|
118
|
+
function asBool(v, fallback) {
|
|
119
|
+
if (typeof v === 'boolean')
|
|
120
|
+
return v;
|
|
121
|
+
if (v === 'true')
|
|
122
|
+
return true;
|
|
123
|
+
if (v === 'false')
|
|
124
|
+
return false;
|
|
125
|
+
return fallback;
|
|
126
|
+
}
|
|
127
|
+
function asNum(v, fallback) {
|
|
128
|
+
return typeof v === 'number' && Number.isFinite(v) ? v : fallback;
|
|
129
|
+
}
|
|
130
|
+
function asAlign(v, fallback = 'left') {
|
|
131
|
+
return v === 'center' || v === 'right' || v === 'left' ? v : fallback;
|
|
132
|
+
}
|
|
133
|
+
function asColor(v, fallback) {
|
|
134
|
+
return typeof v === 'string' && v.trim() ? v : fallback;
|
|
135
|
+
}
|
|
136
|
+
const docStyle = {
|
|
137
|
+
fontFamily: 'system-ui, sans-serif',
|
|
138
|
+
fontSize: 12,
|
|
139
|
+
color: '#0f172a',
|
|
140
|
+
background: '#ffffff',
|
|
141
|
+
margin: '6px 0',
|
|
142
|
+
};
|
|
143
|
+
const thStyle = {
|
|
144
|
+
fontWeight: 600,
|
|
145
|
+
fontSize: 10,
|
|
146
|
+
textAlign: 'left',
|
|
147
|
+
borderBottom: '1px solid #cbd5e1',
|
|
148
|
+
padding: '3px 6px',
|
|
149
|
+
whiteSpace: 'nowrap',
|
|
150
|
+
};
|
|
151
|
+
const tdStyle = {
|
|
152
|
+
fontSize: 10,
|
|
153
|
+
borderBottom: '1px solid #e2e8f0',
|
|
154
|
+
padding: '3px 6px',
|
|
155
|
+
};
|
|
156
|
+
function Section({ children, marginTop, marginBottom, style, }) {
|
|
157
|
+
return (_jsx("div", { style: {
|
|
158
|
+
...docStyle,
|
|
159
|
+
marginTop: marginTop ?? 6,
|
|
160
|
+
marginBottom: marginBottom ?? 6,
|
|
161
|
+
...style,
|
|
162
|
+
}, children: children }));
|
|
163
|
+
}
|
|
164
|
+
function invoiceSection(config) {
|
|
165
|
+
return definePageBuilderComponent({ ...config, contexts: ['invoice'] });
|
|
166
|
+
}
|
|
167
|
+
const InvoiceHeader = invoiceSection({
|
|
168
|
+
label: 'Header (number + dates)',
|
|
169
|
+
fields: {
|
|
170
|
+
titleSize: { type: 'number', label: 'Title size' },
|
|
171
|
+
titleColor: createColorField({ label: 'Title color' }),
|
|
172
|
+
titleBold: radio('Title bold'),
|
|
173
|
+
align: alignField(),
|
|
174
|
+
showIssuedAt: radio('Show issue date'),
|
|
175
|
+
showSaleDate: radio('Show sale date'),
|
|
176
|
+
showPaymentDue: radio('Show payment due'),
|
|
177
|
+
showPaymentMethod: radio('Show payment method'),
|
|
178
|
+
labelIssuedAt: { type: 'text', label: 'Issue date label' },
|
|
179
|
+
labelSaleDate: { type: 'text', label: 'Sale date label' },
|
|
180
|
+
labelPaymentDue: { type: 'text', label: 'Payment due label' },
|
|
181
|
+
labelPaymentMethod: { type: 'text', label: 'Payment method label' },
|
|
182
|
+
labelKsefNumber: { type: 'text', label: 'KSeF number label' },
|
|
183
|
+
marginTop: { type: 'number', label: 'Margin top (px)' },
|
|
184
|
+
marginBottom: { type: 'number', label: 'Margin bottom (px)' },
|
|
185
|
+
},
|
|
186
|
+
defaultProps: {
|
|
187
|
+
titleSize: 14,
|
|
188
|
+
titleColor: '#0f172a',
|
|
189
|
+
titleBold: true,
|
|
190
|
+
align: 'left',
|
|
191
|
+
showIssuedAt: true,
|
|
192
|
+
showSaleDate: true,
|
|
193
|
+
showPaymentDue: true,
|
|
194
|
+
showPaymentMethod: true,
|
|
195
|
+
labelIssuedAt: 'Issue date',
|
|
196
|
+
labelSaleDate: 'Sale date',
|
|
197
|
+
labelPaymentDue: 'Payment due',
|
|
198
|
+
labelPaymentMethod: 'Payment method',
|
|
199
|
+
marginTop: 0,
|
|
200
|
+
marginBottom: 12,
|
|
201
|
+
},
|
|
202
|
+
render: (props) => {
|
|
203
|
+
const inv = sampleInvoice;
|
|
204
|
+
const align = asAlign(props.align, 'left');
|
|
205
|
+
const titleSize = asNum(props.titleSize, 14);
|
|
206
|
+
const titleBold = asBool(props.titleBold, true);
|
|
207
|
+
const titleColor = asColor(props.titleColor, '#0f172a');
|
|
208
|
+
return (_jsxs(Section, { marginTop: asNum(props.marginTop, 0), marginBottom: asNum(props.marginBottom, 12), children: [_jsxs("div", { style: {
|
|
209
|
+
fontSize: titleSize,
|
|
210
|
+
fontWeight: titleBold ? 700 : 400,
|
|
211
|
+
color: titleColor,
|
|
212
|
+
textAlign: align,
|
|
213
|
+
marginBottom: 6,
|
|
214
|
+
}, children: [TITLES[inv.kind], " No. ", inv.number] }), asBool(props.showIssuedAt, true) ? (_jsxs("div", { style: { textAlign: align }, children: [props.labelIssuedAt || 'Issue date', ": ", inv.issuedAt] })) : null, asBool(props.showSaleDate, true) ? (_jsxs("div", { style: { textAlign: align }, children: [props.labelSaleDate || 'Sale date', ": ", inv.saleDate] })) : null, asBool(props.showPaymentDue, true) ? (_jsxs("div", { style: { textAlign: align }, children: [props.labelPaymentDue || 'Payment due', ": ", inv.paymentDueDate] })) : null, asBool(props.showPaymentMethod, true) ? (_jsxs("div", { style: { textAlign: align }, children: [props.labelPaymentMethod || 'Payment method', ": ", inv.paymentMethod] })) : null] }));
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
const InvoiceParties = invoiceSection({
|
|
218
|
+
label: 'Seller & buyer',
|
|
219
|
+
fields: {
|
|
220
|
+
sellerLabel: { type: 'text', label: 'Seller label' },
|
|
221
|
+
buyerLabel: { type: 'text', label: 'Buyer label' },
|
|
222
|
+
showSellerBank: radio('Show seller bank'),
|
|
223
|
+
showSellerSwift: radio('Show seller SWIFT'),
|
|
224
|
+
showBuyerTaxId: radio('Show buyer tax ID'),
|
|
225
|
+
labelFontSize: { type: 'number', label: 'Label font size' },
|
|
226
|
+
bodyFontSize: { type: 'number', label: 'Body font size' },
|
|
227
|
+
columnGap: { type: 'number', label: 'Column gap (px)' },
|
|
228
|
+
sellerWidthPercent: { type: 'number', label: 'Seller width %' },
|
|
229
|
+
marginTop: { type: 'number', label: 'Margin top (px)' },
|
|
230
|
+
marginBottom: { type: 'number', label: 'Margin bottom (px)' },
|
|
231
|
+
},
|
|
232
|
+
defaultProps: {
|
|
233
|
+
sellerLabel: 'Seller',
|
|
234
|
+
buyerLabel: 'Buyer',
|
|
235
|
+
showSellerBank: true,
|
|
236
|
+
showSellerSwift: true,
|
|
237
|
+
showBuyerTaxId: true,
|
|
238
|
+
labelFontSize: 10,
|
|
239
|
+
bodyFontSize: 10,
|
|
240
|
+
columnGap: 16,
|
|
241
|
+
sellerWidthPercent: 50,
|
|
242
|
+
marginTop: 0,
|
|
243
|
+
marginBottom: 16,
|
|
244
|
+
},
|
|
245
|
+
render: (props) => {
|
|
246
|
+
const { seller, buyer } = sampleInvoice;
|
|
247
|
+
const labelFs = asNum(props.labelFontSize, 10);
|
|
248
|
+
const bodyFs = asNum(props.bodyFontSize, 10);
|
|
249
|
+
const sellerPct = Math.min(70, Math.max(30, asNum(props.sellerWidthPercent, 50)));
|
|
250
|
+
const gap = asNum(props.columnGap, 16);
|
|
251
|
+
const labelStyle = { fontWeight: 700, fontSize: labelFs, marginBottom: 2 };
|
|
252
|
+
const bodyStyle = { fontSize: bodyFs };
|
|
253
|
+
return (_jsx(Section, { marginTop: asNum(props.marginTop, 0), marginBottom: asNum(props.marginBottom, 16), children: _jsxs("div", { style: { display: 'flex', gap }, children: [_jsxs("div", { style: { flex: `0 0 ${sellerPct}%` }, children: [_jsx("div", { style: labelStyle, children: props.sellerLabel || 'Seller' }), _jsx("div", { style: bodyStyle, children: seller.legalName }), _jsx("div", { style: bodyStyle, children: seller.addressLine1 }), seller.addressLine2 ? _jsx("div", { style: bodyStyle, children: seller.addressLine2 }) : null, _jsxs("div", { style: bodyStyle, children: [seller.postalCode, " ", seller.city] }), _jsxs("div", { style: bodyStyle, children: ["Tax ID: ", seller.taxId] }), asBool(props.showSellerBank, true) && seller.bankAccount ? (_jsxs("div", { style: bodyStyle, children: [seller.bankName, " ", seller.bankAccount] })) : null, asBool(props.showSellerSwift, true) && seller.swift ? (_jsxs("div", { style: bodyStyle, children: ["SWIFT: ", seller.swift] })) : null] }), _jsxs("div", { style: { flex: `1 1 ${100 - sellerPct}%` }, children: [_jsx("div", { style: labelStyle, children: props.buyerLabel || 'Buyer' }), _jsx("div", { style: bodyStyle, children: buyer.name }), _jsx("div", { style: bodyStyle, children: buyer.addressLine1 }), _jsxs("div", { style: bodyStyle, children: [buyer.postalCode, " ", buyer.city] }), asBool(props.showBuyerTaxId, true) && buyer.taxId ? (_jsxs("div", { style: bodyStyle, children: ["Tax ID: ", buyer.taxId] })) : null] })] }) }));
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
const InvoiceLineItems = invoiceSection({
|
|
257
|
+
label: 'Line items table',
|
|
258
|
+
fields: {
|
|
259
|
+
fontSize: { type: 'number', label: 'Font size' },
|
|
260
|
+
headerBold: radio('Header bold'),
|
|
261
|
+
headerBackground: createColorField({ label: 'Header background' }),
|
|
262
|
+
headerColor: createColorField({ label: 'Header text color' }),
|
|
263
|
+
borderColor: createColorField({ label: 'Border color' }),
|
|
264
|
+
zebra: radio('Zebra rows'),
|
|
265
|
+
showUnit: radio('Show unit'),
|
|
266
|
+
showQty: radio('Show quantity'),
|
|
267
|
+
showUnitNet: radio('Show unit net price'),
|
|
268
|
+
showTaxRate: radio('Show tax rate'),
|
|
269
|
+
showNet: radio('Show net value'),
|
|
270
|
+
showGross: radio('Show gross value'),
|
|
271
|
+
marginTop: { type: 'number', label: 'Margin top (px)' },
|
|
272
|
+
marginBottom: { type: 'number', label: 'Margin bottom (px)' },
|
|
273
|
+
},
|
|
274
|
+
defaultProps: {
|
|
275
|
+
fontSize: 8,
|
|
276
|
+
headerBold: true,
|
|
277
|
+
headerBackground: '',
|
|
278
|
+
headerColor: '',
|
|
279
|
+
borderColor: '#cbd5e1',
|
|
280
|
+
zebra: false,
|
|
281
|
+
showUnit: true,
|
|
282
|
+
showQty: true,
|
|
283
|
+
showUnitNet: true,
|
|
284
|
+
showTaxRate: true,
|
|
285
|
+
showNet: true,
|
|
286
|
+
showGross: true,
|
|
287
|
+
marginTop: 0,
|
|
288
|
+
marginBottom: 12,
|
|
289
|
+
},
|
|
290
|
+
render: (props) => {
|
|
291
|
+
const fontSize = asNum(props.fontSize, 8);
|
|
292
|
+
const headerBold = asBool(props.headerBold, true);
|
|
293
|
+
const headerBg = asColor(props.headerBackground);
|
|
294
|
+
const headerColor = asColor(props.headerColor);
|
|
295
|
+
const borderColor = asColor(props.borderColor, '#cbd5e1') ?? '#cbd5e1';
|
|
296
|
+
const zebra = asBool(props.zebra, false);
|
|
297
|
+
const showUnit = asBool(props.showUnit, true);
|
|
298
|
+
const showQty = asBool(props.showQty, true);
|
|
299
|
+
const showUnitNet = asBool(props.showUnitNet, true);
|
|
300
|
+
const showTaxRate = asBool(props.showTaxRate, true);
|
|
301
|
+
const showNet = asBool(props.showNet, true);
|
|
302
|
+
const showGross = asBool(props.showGross, true);
|
|
303
|
+
const cols = [
|
|
304
|
+
{ key: 'lp', label: '#' },
|
|
305
|
+
{ key: 'name', label: 'Description' },
|
|
306
|
+
];
|
|
307
|
+
if (showUnit)
|
|
308
|
+
cols.push({ key: 'unit', label: 'Unit' });
|
|
309
|
+
if (showQty)
|
|
310
|
+
cols.push({ key: 'qty', label: 'Qty', right: true });
|
|
311
|
+
if (showUnitNet)
|
|
312
|
+
cols.push({ key: 'unitNet', label: 'Unit net', right: true });
|
|
313
|
+
if (showTaxRate)
|
|
314
|
+
cols.push({ key: 'tax', label: 'Tax', right: true });
|
|
315
|
+
if (showNet)
|
|
316
|
+
cols.push({ key: 'net', label: 'Net', right: true });
|
|
317
|
+
if (showGross)
|
|
318
|
+
cols.push({ key: 'gross', label: 'Gross', right: true });
|
|
319
|
+
const head = {
|
|
320
|
+
...thStyle,
|
|
321
|
+
fontSize,
|
|
322
|
+
fontWeight: headerBold ? 600 : 400,
|
|
323
|
+
color: headerColor,
|
|
324
|
+
background: headerBg,
|
|
325
|
+
borderBottom: `1px solid ${borderColor}`,
|
|
326
|
+
borderRight: `1px solid ${borderColor}`,
|
|
327
|
+
};
|
|
328
|
+
const cell = (right, fill) => ({
|
|
329
|
+
...tdStyle,
|
|
330
|
+
fontSize,
|
|
331
|
+
textAlign: right ? 'right' : 'left',
|
|
332
|
+
background: fill,
|
|
333
|
+
borderBottom: `1px solid ${borderColor}`,
|
|
334
|
+
borderRight: `1px solid ${borderColor}`,
|
|
335
|
+
});
|
|
336
|
+
return (_jsx(Section, { marginTop: asNum(props.marginTop, 0), marginBottom: asNum(props.marginBottom, 12), children: _jsxs("table", { style: { width: '100%', borderCollapse: 'collapse', border: `1px solid ${borderColor}` }, children: [_jsx("thead", { children: _jsx("tr", { children: cols.map((c) => (_jsx("th", { style: { ...head, textAlign: c.right ? 'right' : 'left' }, children: c.label }, c.key))) }) }), _jsx("tbody", { children: sampleInvoice.lines.map((l, i) => {
|
|
337
|
+
const fill = zebra && i % 2 === 1 ? '#f8fafc' : undefined;
|
|
338
|
+
const values = {
|
|
339
|
+
lp: String(l.ordinal),
|
|
340
|
+
name: l.name,
|
|
341
|
+
unit: l.unit,
|
|
342
|
+
qty: String(l.quantity),
|
|
343
|
+
unitNet: money(l.unitNetPrice),
|
|
344
|
+
tax: pct(l.taxRate),
|
|
345
|
+
net: money(l.netValue),
|
|
346
|
+
gross: money(l.grossValue),
|
|
347
|
+
};
|
|
348
|
+
return (_jsx("tr", { children: cols.map((c) => (_jsx("td", { style: cell(c.right, fill), children: values[c.key] }, c.key))) }, l.ordinal));
|
|
349
|
+
}) })] }) }));
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
const InvoiceVatSummary = invoiceSection({
|
|
353
|
+
label: 'VAT summary',
|
|
354
|
+
fields: {
|
|
355
|
+
fontSize: { type: 'number', label: 'Font size' },
|
|
356
|
+
headerBold: radio('Header bold'),
|
|
357
|
+
headerBackground: createColorField({ label: 'Header background' }),
|
|
358
|
+
headerColor: createColorField({ label: 'Header text color' }),
|
|
359
|
+
borderColor: createColorField({ label: 'Border color' }),
|
|
360
|
+
showTotalRow: radio('Show total row'),
|
|
361
|
+
totalLabel: { type: 'text', label: 'Total row label' },
|
|
362
|
+
marginTop: { type: 'number', label: 'Margin top (px)' },
|
|
363
|
+
marginBottom: { type: 'number', label: 'Margin bottom (px)' },
|
|
364
|
+
},
|
|
365
|
+
defaultProps: {
|
|
366
|
+
fontSize: 8,
|
|
367
|
+
headerBold: true,
|
|
368
|
+
headerBackground: '',
|
|
369
|
+
headerColor: '',
|
|
370
|
+
borderColor: '#cbd5e1',
|
|
371
|
+
showTotalRow: true,
|
|
372
|
+
totalLabel: 'Total',
|
|
373
|
+
marginTop: 0,
|
|
374
|
+
marginBottom: 12,
|
|
375
|
+
},
|
|
376
|
+
render: (props) => {
|
|
377
|
+
const inv = sampleInvoice;
|
|
378
|
+
const fontSize = asNum(props.fontSize, 8);
|
|
379
|
+
const headerBold = asBool(props.headerBold, true);
|
|
380
|
+
const headerBg = asColor(props.headerBackground);
|
|
381
|
+
const headerColor = asColor(props.headerColor);
|
|
382
|
+
const borderColor = asColor(props.borderColor, '#cbd5e1') ?? '#cbd5e1';
|
|
383
|
+
const headers = ['Tax rate', 'Net', 'VAT', 'Gross'];
|
|
384
|
+
const head = (i) => ({
|
|
385
|
+
...thStyle,
|
|
386
|
+
fontSize,
|
|
387
|
+
fontWeight: headerBold ? 600 : 400,
|
|
388
|
+
color: headerColor,
|
|
389
|
+
background: headerBg,
|
|
390
|
+
textAlign: i === 0 ? 'left' : 'right',
|
|
391
|
+
borderBottom: `1px solid ${borderColor}`,
|
|
392
|
+
borderRight: `1px solid ${borderColor}`,
|
|
393
|
+
});
|
|
394
|
+
const cell = (right, bold) => ({
|
|
395
|
+
...tdStyle,
|
|
396
|
+
fontSize,
|
|
397
|
+
fontWeight: bold ? 700 : 400,
|
|
398
|
+
textAlign: right ? 'right' : 'left',
|
|
399
|
+
borderBottom: `1px solid ${borderColor}`,
|
|
400
|
+
borderRight: `1px solid ${borderColor}`,
|
|
401
|
+
});
|
|
402
|
+
return (_jsx(Section, { marginTop: asNum(props.marginTop, 0), marginBottom: asNum(props.marginBottom, 12), children: _jsxs("table", { style: { width: '100%', borderCollapse: 'collapse', border: `1px solid ${borderColor}` }, children: [_jsx("thead", { children: _jsx("tr", { children: headers.map((h, i) => (_jsx("th", { style: head(i), children: h }, h))) }) }), _jsxs("tbody", { children: [inv.vatSummary.map((v) => (_jsxs("tr", { children: [_jsx("td", { style: cell(), children: pct(v.taxRate) }), _jsx("td", { style: cell(true), children: money(v.netTotal) }), _jsx("td", { style: cell(true), children: money(v.vatAmount) }), _jsx("td", { style: cell(true), children: money(v.grossTotal) })] }, v.taxRate))), asBool(props.showTotalRow, true) ? (_jsxs("tr", { children: [_jsx("td", { style: cell(false, true), children: props.totalLabel || 'Total' }), _jsx("td", { style: cell(true, true), children: money(inv.netTotal) }), _jsx("td", { style: cell(true, true), children: money(inv.taxTotal) }), _jsx("td", { style: cell(true, true), children: money(inv.grossTotal) })] })) : null] })] }) }));
|
|
403
|
+
},
|
|
404
|
+
});
|
|
405
|
+
const InvoiceTotals = invoiceSection({
|
|
406
|
+
label: 'Totals & amount in words',
|
|
407
|
+
fields: {
|
|
408
|
+
align: alignField(),
|
|
409
|
+
fontSize: { type: 'number', label: 'Font size' },
|
|
410
|
+
color: createColorField({ label: 'Text color' }),
|
|
411
|
+
amountDueColor: createColorField({ label: 'Amount due color' }),
|
|
412
|
+
amountDueBold: radio('Amount due bold'),
|
|
413
|
+
amountInWordsItalics: radio('Amount in words italics'),
|
|
414
|
+
showPaid: radio('Show paid'),
|
|
415
|
+
showAmountDue: radio('Show amount due'),
|
|
416
|
+
showGross: radio('Show gross'),
|
|
417
|
+
showAmountInWords: radio('Show amount in words'),
|
|
418
|
+
labelPaid: { type: 'text', label: 'Paid label' },
|
|
419
|
+
labelAmountDue: { type: 'text', label: 'Amount due label' },
|
|
420
|
+
labelGross: { type: 'text', label: 'Gross label' },
|
|
421
|
+
labelInWords: { type: 'text', label: 'In words label' },
|
|
422
|
+
marginTop: { type: 'number', label: 'Margin top (px)' },
|
|
423
|
+
marginBottom: { type: 'number', label: 'Margin bottom (px)' },
|
|
424
|
+
},
|
|
425
|
+
defaultProps: {
|
|
426
|
+
align: 'right',
|
|
427
|
+
fontSize: 10,
|
|
428
|
+
color: '#0f172a',
|
|
429
|
+
amountDueColor: '',
|
|
430
|
+
amountDueBold: true,
|
|
431
|
+
amountInWordsItalics: true,
|
|
432
|
+
showPaid: true,
|
|
433
|
+
showAmountDue: true,
|
|
434
|
+
showGross: true,
|
|
435
|
+
showAmountInWords: true,
|
|
436
|
+
labelPaid: 'Paid',
|
|
437
|
+
labelAmountDue: 'Amount due',
|
|
438
|
+
labelGross: 'Total',
|
|
439
|
+
labelInWords: 'In words',
|
|
440
|
+
marginTop: 0,
|
|
441
|
+
marginBottom: 16,
|
|
442
|
+
},
|
|
443
|
+
render: (props) => {
|
|
444
|
+
const inv = sampleInvoice;
|
|
445
|
+
const align = asAlign(props.align, 'right');
|
|
446
|
+
const fontSize = asNum(props.fontSize, 10);
|
|
447
|
+
const color = asColor(props.color, '#0f172a');
|
|
448
|
+
const amountDueColor = asColor(props.amountDueColor) ?? color;
|
|
449
|
+
return (_jsx(Section, { marginTop: asNum(props.marginTop, 0), marginBottom: asNum(props.marginBottom, 16), children: _jsxs("div", { style: { textAlign: align, fontSize, color }, children: [asBool(props.showPaid, true) ? (_jsxs("div", { children: [props.labelPaid || 'Paid', ": ", money(inv.paidTotal), " ", inv.currency] })) : null, asBool(props.showAmountDue, true) ? (_jsxs("div", { style: {
|
|
450
|
+
fontWeight: asBool(props.amountDueBold, true) ? 700 : 400,
|
|
451
|
+
color: amountDueColor,
|
|
452
|
+
}, children: [props.labelAmountDue || 'Amount due', ": ", money(inv.amountDue), " ", inv.currency] })) : null, asBool(props.showGross, true) ? (_jsxs("div", { children: [props.labelGross || 'Total', ": ", money(inv.grossTotal), " ", inv.currency] })) : null, asBool(props.showAmountInWords, true) ? (_jsxs("div", { style: {
|
|
453
|
+
fontStyle: asBool(props.amountInWordsItalics, true) ? 'italic' : 'normal',
|
|
454
|
+
marginTop: 6,
|
|
455
|
+
}, children: [props.labelInWords || 'In words', ": ", inv.amountInWords, " ", inv.currency] })) : null] }) }));
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
function renderTextBlock(props, defaults) {
|
|
459
|
+
const align = asAlign(props.align, 'left');
|
|
460
|
+
const fontSize = asNum(props.fontSize, defaults.fontSize);
|
|
461
|
+
const color = asColor(props.color, '#0f172a');
|
|
462
|
+
const raw = typeof props.text === 'string' ? props.text : '';
|
|
463
|
+
return (_jsxs(Section, { marginTop: asNum(props.marginTop, 0), marginBottom: asNum(props.marginBottom, defaults.marginBottom), children: [asBool(props.showTopDivider, defaults.showTopDivider) ? (_jsx("hr", { style: { border: 0, borderTop: '1px solid #e5e7eb', margin: '0 0 8px' } })) : null, raw ? (_jsx("div", { style: {
|
|
464
|
+
whiteSpace: 'pre-wrap',
|
|
465
|
+
textAlign: align,
|
|
466
|
+
fontSize,
|
|
467
|
+
color,
|
|
468
|
+
fontWeight: asBool(props.bold, false) ? 700 : 400,
|
|
469
|
+
fontStyle: asBool(props.italics, false) ? 'italic' : 'normal',
|
|
470
|
+
}, children: interpolate(raw) })) : (_jsx("div", { style: { fontSize: 11, color: '#94a3b8', textAlign: align }, children: defaults.emptyHint }))] }));
|
|
471
|
+
}
|
|
472
|
+
const InvoiceNotes = invoiceSection({
|
|
473
|
+
label: 'Notes (free text)',
|
|
474
|
+
fields: {
|
|
475
|
+
text: { type: 'textarea', label: 'Notes text' },
|
|
476
|
+
align: alignField(),
|
|
477
|
+
fontSize: { type: 'number', label: 'Font size' },
|
|
478
|
+
color: createColorField({ label: 'Text color' }),
|
|
479
|
+
bold: radio('Bold'),
|
|
480
|
+
italics: radio('Italics'),
|
|
481
|
+
showTopDivider: radio('Top divider'),
|
|
482
|
+
marginTop: { type: 'number', label: 'Margin top (px)' },
|
|
483
|
+
marginBottom: { type: 'number', label: 'Margin bottom (px)' },
|
|
484
|
+
},
|
|
485
|
+
defaultProps: {
|
|
486
|
+
text: '',
|
|
487
|
+
align: 'left',
|
|
488
|
+
fontSize: 11,
|
|
489
|
+
color: '#0f172a',
|
|
490
|
+
bold: false,
|
|
491
|
+
italics: false,
|
|
492
|
+
showTopDivider: false,
|
|
493
|
+
marginTop: 0,
|
|
494
|
+
marginBottom: 8,
|
|
495
|
+
},
|
|
496
|
+
render: (props) => renderTextBlock(props, {
|
|
497
|
+
fontSize: 11,
|
|
498
|
+
showTopDivider: false,
|
|
499
|
+
marginBottom: 8,
|
|
500
|
+
emptyHint: `Free text — supports {{var invoice.number}} tokens`,
|
|
501
|
+
}),
|
|
502
|
+
});
|
|
503
|
+
const InvoiceSpacer = invoiceSection({
|
|
504
|
+
label: 'Spacer',
|
|
505
|
+
fields: {
|
|
506
|
+
height: { type: 'number', label: 'Height (px)' },
|
|
507
|
+
backgroundColor: createColorField({ label: 'Background color' }),
|
|
508
|
+
},
|
|
509
|
+
defaultProps: {
|
|
510
|
+
height: 16,
|
|
511
|
+
backgroundColor: '',
|
|
512
|
+
},
|
|
513
|
+
render: (props) => {
|
|
514
|
+
const height = Math.min(120, Math.max(4, asNum(props.height, 16)));
|
|
515
|
+
const bg = asColor(props.backgroundColor);
|
|
516
|
+
return (_jsx("div", { style: {
|
|
517
|
+
height,
|
|
518
|
+
width: '100%',
|
|
519
|
+
background: bg ?? 'transparent',
|
|
520
|
+
margin: '0',
|
|
521
|
+
}, "aria-hidden": true }));
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
const InvoiceDivider = invoiceSection({
|
|
525
|
+
label: 'Divider',
|
|
526
|
+
fields: {
|
|
527
|
+
thickness: { type: 'number', label: 'Thickness (px)' },
|
|
528
|
+
color: createColorField({ label: 'Color' }),
|
|
529
|
+
style: {
|
|
530
|
+
type: 'select',
|
|
531
|
+
label: 'Style',
|
|
532
|
+
options: [
|
|
533
|
+
{ label: 'Solid', value: 'solid' },
|
|
534
|
+
{ label: 'Dashed', value: 'dashed' },
|
|
535
|
+
],
|
|
536
|
+
},
|
|
537
|
+
widthPercent: { type: 'number', label: 'Width %' },
|
|
538
|
+
align: alignField(),
|
|
539
|
+
marginY: { type: 'number', label: 'Vertical margin (px)' },
|
|
540
|
+
},
|
|
541
|
+
defaultProps: {
|
|
542
|
+
thickness: 1,
|
|
543
|
+
color: '#e5e7eb',
|
|
544
|
+
style: 'solid',
|
|
545
|
+
widthPercent: 100,
|
|
546
|
+
align: 'left',
|
|
547
|
+
marginY: 8,
|
|
548
|
+
},
|
|
549
|
+
render: (props) => {
|
|
550
|
+
const thickness = Math.min(8, Math.max(1, asNum(props.thickness, 1)));
|
|
551
|
+
const color = asColor(props.color, '#e5e7eb') ?? '#e5e7eb';
|
|
552
|
+
const widthPercent = Math.min(100, Math.max(10, asNum(props.widthPercent, 100)));
|
|
553
|
+
const align = asAlign(props.align, 'left');
|
|
554
|
+
const marginY = asNum(props.marginY, 8);
|
|
555
|
+
const dashed = props.style === 'dashed';
|
|
556
|
+
const justify = align === 'center' ? 'center' : align === 'right' ? 'flex-end' : 'flex-start';
|
|
557
|
+
return (_jsx("div", { style: { display: 'flex', justifyContent: justify, margin: `${marginY}px 0` }, children: _jsx("div", { style: {
|
|
558
|
+
width: `${widthPercent}%`,
|
|
559
|
+
borderTop: `${thickness}px ${dashed ? 'dashed' : 'solid'} ${color}`,
|
|
560
|
+
} }) }));
|
|
561
|
+
},
|
|
562
|
+
});
|
|
563
|
+
const InvoiceLogo = invoiceSection({
|
|
564
|
+
label: 'Logo',
|
|
565
|
+
fields: {
|
|
566
|
+
imageSource: createImageSourceField(),
|
|
567
|
+
src: createImageUrlField('Image URL'),
|
|
568
|
+
assetId: createImageAssetField('Image'),
|
|
569
|
+
width: { type: 'number', label: 'Width (px)' },
|
|
570
|
+
maxHeight: { type: 'number', label: 'Max height (px)' },
|
|
571
|
+
align: alignField(),
|
|
572
|
+
marginBottom: { type: 'number', label: 'Margin bottom (px)' },
|
|
573
|
+
},
|
|
574
|
+
defaultProps: {
|
|
575
|
+
imageSource: 'url',
|
|
576
|
+
src: '',
|
|
577
|
+
assetId: '',
|
|
578
|
+
width: 140,
|
|
579
|
+
maxHeight: 80,
|
|
580
|
+
align: 'center',
|
|
581
|
+
marginBottom: 12,
|
|
582
|
+
},
|
|
583
|
+
resolveFields: (data, { fields }) => {
|
|
584
|
+
const source = data.props['imageSource'] === 'library' ? 'library' : 'url';
|
|
585
|
+
const next = { ...fields };
|
|
586
|
+
if (source === 'library') {
|
|
587
|
+
delete next['src'];
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
delete next['assetId'];
|
|
591
|
+
}
|
|
592
|
+
return next;
|
|
593
|
+
},
|
|
594
|
+
resolveData: async ({ props }) => {
|
|
595
|
+
const source = props['imageSource'] === 'library' ? 'library' : 'url';
|
|
596
|
+
let src = typeof props['src'] === 'string' ? props['src'] : '';
|
|
597
|
+
const assetId = typeof props['assetId'] === 'string' ? props['assetId'] : '';
|
|
598
|
+
if (source === 'library' && assetId) {
|
|
599
|
+
try {
|
|
600
|
+
const detail = await fetchAssetDetail(assetId);
|
|
601
|
+
src = toAbsoluteAssetUrl(detail.url);
|
|
602
|
+
}
|
|
603
|
+
catch {
|
|
604
|
+
/* keep previous src */
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (source === 'url') {
|
|
608
|
+
return { props: { ...props, imageSource: 'url', assetId: '', src } };
|
|
609
|
+
}
|
|
610
|
+
return { props: { ...props, imageSource: 'library', assetId, src } };
|
|
611
|
+
},
|
|
612
|
+
render: (props) => {
|
|
613
|
+
const src = typeof props.src === 'string' ? props.src.trim() : '';
|
|
614
|
+
const width = asNum(props.width, 140);
|
|
615
|
+
const maxHeight = asNum(props.maxHeight, 80);
|
|
616
|
+
const align = asAlign(props.align, 'center');
|
|
617
|
+
const marginBottom = asNum(props.marginBottom, 12);
|
|
618
|
+
const imgMargin = align === 'center' ? '0 auto' : align === 'right' ? '0 0 0 auto' : '0';
|
|
619
|
+
return (_jsx("div", { style: { textAlign: align, marginBottom, width: '100%' }, children: src ? (_jsx("img", { src: src, alt: "Logo", style: {
|
|
620
|
+
display: 'block',
|
|
621
|
+
width,
|
|
622
|
+
maxWidth: '100%',
|
|
623
|
+
maxHeight,
|
|
624
|
+
height: 'auto',
|
|
625
|
+
objectFit: 'contain',
|
|
626
|
+
margin: imgMargin,
|
|
627
|
+
} })) : (_jsx("div", { style: {
|
|
628
|
+
display: 'inline-block',
|
|
629
|
+
width,
|
|
630
|
+
maxWidth: '100%',
|
|
631
|
+
minHeight: 40,
|
|
632
|
+
maxHeight,
|
|
633
|
+
background: '#e2e8f0',
|
|
634
|
+
color: '#64748b',
|
|
635
|
+
fontSize: 12,
|
|
636
|
+
lineHeight: '40px',
|
|
637
|
+
textAlign: 'center',
|
|
638
|
+
margin: imgMargin,
|
|
639
|
+
}, children: "[Logo]" })) }));
|
|
640
|
+
},
|
|
641
|
+
});
|
|
642
|
+
/* -------------------------------------------------------------------------- */
|
|
643
|
+
/* InvoiceFooter */
|
|
644
|
+
/* -------------------------------------------------------------------------- */
|
|
645
|
+
const DEFAULT_FOOTER_TEXT = 'Thank you for your business.\n{{var seller.legalName}} · Tax ID {{var seller.taxId}}';
|
|
646
|
+
const InvoiceFooter = invoiceSection({
|
|
647
|
+
label: 'Footer / legal',
|
|
648
|
+
fields: {
|
|
649
|
+
text: { type: 'textarea', label: 'Footer text' },
|
|
650
|
+
align: alignField(),
|
|
651
|
+
fontSize: { type: 'number', label: 'Font size' },
|
|
652
|
+
color: createColorField({ label: 'Text color' }),
|
|
653
|
+
bold: radio('Bold'),
|
|
654
|
+
italics: radio('Italics'),
|
|
655
|
+
showTopDivider: radio('Top divider'),
|
|
656
|
+
marginTop: { type: 'number', label: 'Margin top (px)' },
|
|
657
|
+
marginBottom: { type: 'number', label: 'Margin bottom (px)' },
|
|
658
|
+
},
|
|
659
|
+
defaultProps: {
|
|
660
|
+
text: DEFAULT_FOOTER_TEXT,
|
|
661
|
+
align: 'center',
|
|
662
|
+
fontSize: 9,
|
|
663
|
+
color: '#0f172a',
|
|
664
|
+
bold: false,
|
|
665
|
+
italics: false,
|
|
666
|
+
showTopDivider: true,
|
|
667
|
+
marginTop: 0,
|
|
668
|
+
marginBottom: 8,
|
|
669
|
+
},
|
|
670
|
+
render: (props) => renderTextBlock({ ...props, text: props.text || DEFAULT_FOOTER_TEXT }, {
|
|
671
|
+
fontSize: 9,
|
|
672
|
+
showTopDivider: true,
|
|
673
|
+
marginBottom: 8,
|
|
674
|
+
emptyHint: DEFAULT_FOOTER_TEXT,
|
|
675
|
+
}),
|
|
676
|
+
});
|
|
677
|
+
/* -------------------------------------------------------------------------- */
|
|
678
|
+
/* Config export */
|
|
679
|
+
/* -------------------------------------------------------------------------- */
|
|
680
|
+
/**
|
|
681
|
+
* The invoice renderer map — **the React half only**, keyed by namespaced block
|
|
682
|
+
* name (feature 096, T303; `contracts/block-definition.md` §4.3).
|
|
683
|
+
*
|
|
684
|
+
* **The `invoice` category is gone.** It is declared by `invoices` (with a
|
|
685
|
+
* `weight`, which is what makes `invoices` the namer) and jointly by `ksef`,
|
|
686
|
+
* and served merged by `GET /api/v1/admin/cms/page-builder/config`. Switching
|
|
687
|
+
* `invoices` off therefore leaves the section titled by `ksef`'s declaration
|
|
688
|
+
* with `ksef.InvoiceSection` still insertable — which a category list in this
|
|
689
|
+
* file could not express.
|
|
690
|
+
*
|
|
691
|
+
* **Every entry is this module's own.** A block another module declares is not
|
|
692
|
+
* bound here: its binding lives with its declarant, and no page-builder
|
|
693
|
+
* component contribution kind (α, F7's roadmap row) is built. The editor
|
|
694
|
+
* reaches such a block through the served descriptor instead —
|
|
695
|
+
* `withDescribedInvoiceBlocks` (`./described-blocks.tsx`,
|
|
696
|
+
* `specs/134-paid-module-extraction/` T138) makes a present contributor's block
|
|
697
|
+
* insertable and configurable with a neutral canvas stand-in, and keeps a stored
|
|
698
|
+
* block nothing covers as FR-019's placeholder. The PDF — the legal document —
|
|
699
|
+
* renders it through the contributor's registration on the backend.
|
|
700
|
+
*/
|
|
701
|
+
export const invoicePuckConfig = {
|
|
702
|
+
components: {
|
|
703
|
+
'invoices.InvoiceHeader': InvoiceHeader,
|
|
704
|
+
'invoices.InvoiceParties': InvoiceParties,
|
|
705
|
+
'invoices.InvoiceLineItems': InvoiceLineItems,
|
|
706
|
+
'invoices.InvoiceVatSummary': InvoiceVatSummary,
|
|
707
|
+
'invoices.InvoiceTotals': InvoiceTotals,
|
|
708
|
+
'invoices.InvoiceNotes': InvoiceNotes,
|
|
709
|
+
'invoices.InvoiceSpacer': InvoiceSpacer,
|
|
710
|
+
'invoices.InvoiceDivider': InvoiceDivider,
|
|
711
|
+
'invoices.InvoiceLogo': InvoiceLogo,
|
|
712
|
+
'invoices.InvoiceFooter': InvoiceFooter,
|
|
713
|
+
},
|
|
714
|
+
};
|
|
715
|
+
/**
|
|
716
|
+
* The invoice palette's one section.
|
|
717
|
+
*
|
|
718
|
+
* **Derived from the renderer map rather than written out** (FR-008, SC-005):
|
|
719
|
+
* this file no longer holds a mapping from a category to a list of block names,
|
|
720
|
+
* which is what let a section list five of another module's blocks. The
|
|
721
|
+
* *authoritative* declarations are `invoices`' and `ksef`'s `blockCategories`,
|
|
722
|
+
* merged and presence-filtered by
|
|
723
|
+
* `GET /api/v1/admin/cms/page-builder/config`; this local derivation is what
|
|
724
|
+
* the editor renders until the invoice builder reads that descriptor, and it
|
|
725
|
+
* carries no name of its own.
|
|
726
|
+
*/
|
|
727
|
+
export const invoicePuckPalette = {
|
|
728
|
+
invoice: {
|
|
729
|
+
title: 'Invoice sections',
|
|
730
|
+
components: Object.keys(invoicePuckConfig.components ?? {}),
|
|
731
|
+
},
|
|
732
|
+
};
|
|
733
|
+
//# sourceMappingURL=invoice-puck-config.js.map
|