@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,124 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyRequest } from 'fastify';
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
3
|
+
import type { AssetReadPort, AssetsLibraryPort, InvoiceAttachmentFetchPort, OrderReadPort, OrderRecord, TransactionalEmailSender } from '@endora-commerce/contracts';
|
|
4
|
+
import { InvoiceService, type InvoiceAuditRecorder } from './services/invoice-service.js';
|
|
5
|
+
import type { LedgerNumberingLookup } from './services/vendor-number-hold.js';
|
|
6
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
7
|
+
import { InvoicePdfRenderer } from './services/invoice-pdf-renderer.js';
|
|
8
|
+
import type { InvoicePdfBlockRegistry } from './services/invoice-pdf-block-registry.js';
|
|
9
|
+
import type { LoadAssetImage } from './pdf-components/embed-logo-images.js';
|
|
10
|
+
import { InvoiceNumberGenerator } from './services/invoice-number-generator.js';
|
|
11
|
+
import { type SettingsReader } from './services/seller-settings.js';
|
|
12
|
+
import { InvoiceEmailDispatcher } from './services/invoice-email-dispatch.js';
|
|
13
|
+
import { InvoiceTemplateService } from './services/invoice-template-service.js';
|
|
14
|
+
import { createAutoIssueReactor } from './services/auto-issue-reactor.js';
|
|
15
|
+
import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
|
|
16
|
+
/**
|
|
17
|
+
* The attach function this module hands its composition root.
|
|
18
|
+
*
|
|
19
|
+
* Typed on `fastify`'s own `FastifyInstance` rather than on the platform's
|
|
20
|
+
* `ModulePlugin`, which `contracts/host-package.md` §1.4g classifies **A**: the
|
|
21
|
+
* host does not publish it, so a packaged module cannot name it. The
|
|
22
|
+
* already-packaged `quote_requests` types its attach function the same way.
|
|
23
|
+
*/
|
|
24
|
+
type ModuleAttach = (app: FastifyInstance) => Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Minimal event-bus surface this module needs. Emission only: the FR-002
|
|
27
|
+
* subscription moved to `backend.ts` and `ctx.subscribe` (issue #107), so this
|
|
28
|
+
* no longer carries `on`.
|
|
29
|
+
*/
|
|
30
|
+
export interface InvoicesEventBus {
|
|
31
|
+
/** Present on the real EventBus — used to emit invoice.issued/corrected.v1 (feature 059). */
|
|
32
|
+
emit?(eventName: string, payload: {
|
|
33
|
+
eventId: string;
|
|
34
|
+
occurredAt: string;
|
|
35
|
+
}): void;
|
|
36
|
+
}
|
|
37
|
+
export interface InvoicesModuleOptions {
|
|
38
|
+
emFactory: () => EntityManager;
|
|
39
|
+
/**
|
|
40
|
+
* `orders`' published read model (feature 075, Phase C). Not optional: an
|
|
41
|
+
* invoice is a document *about an order*, so a module that cannot read one
|
|
42
|
+
* has nothing to issue. Every read of the order rows used to be
|
|
43
|
+
* `em.findOne(Order, …)` from inside this module — a query no gate can see,
|
|
44
|
+
* so an invoice went on being issued against a module an operator had
|
|
45
|
+
* switched off.
|
|
46
|
+
*/
|
|
47
|
+
orderReadPort: OrderReadPort;
|
|
48
|
+
assetReadPort: AssetReadPort;
|
|
49
|
+
assetsLibrary: AssetsLibraryPort;
|
|
50
|
+
saleDocumentAttachments: InvoiceAttachmentFetchPort;
|
|
51
|
+
requireAdmin: RequireAdminFactory;
|
|
52
|
+
requireCustomer: (req: FastifyRequest, reply: unknown) => Promise<void>;
|
|
53
|
+
settingsService: SettingsReader;
|
|
54
|
+
/**
|
|
55
|
+
* The five that stopped being optional in
|
|
56
|
+
* `specs/110-instance-repository/` T118c.
|
|
57
|
+
*
|
|
58
|
+
* Each was a member of the contributed `invoicesBridge` and each was declared
|
|
59
|
+
* `?`, which was true of the *contribution* and never of the module: this
|
|
60
|
+
* module resolves all five itself now — the platform's two actor resolvers,
|
|
61
|
+
* `transactional_emails`' sender accessor, `customer_accounts`' record and a
|
|
62
|
+
* read of the channel — so no composition can decline to supply one. An
|
|
63
|
+
* option that is nonetheless typed as omittable is a branch no test can
|
|
64
|
+
* drive, which is what `loadAssetImage` was until D-223 and what the
|
|
65
|
+
* e-mail trio still is: `emailDispatcher` was built only when all three were
|
|
66
|
+
* present, so a composition that dropped one silently stopped sending
|
|
67
|
+
* invoices and answered `no_sender`.
|
|
68
|
+
*/
|
|
69
|
+
resolveAdminUserId: (req: FastifyRequest) => string | null;
|
|
70
|
+
resolveCustomerContext: (req: FastifyRequest) => {
|
|
71
|
+
customerAccountId: string;
|
|
72
|
+
organizationId: string;
|
|
73
|
+
};
|
|
74
|
+
/** US5 — transactional email sender (late-bound). */
|
|
75
|
+
getTransactionalEmailSender: () => TransactionalEmailSender | undefined;
|
|
76
|
+
/** US5 — recipient email for an order (customer account email). */
|
|
77
|
+
resolveRecipientEmail: (order: OrderRecord) => Promise<string | null>;
|
|
78
|
+
/** US5 — channel default language (BCP-47). */
|
|
79
|
+
resolveLanguage: (salesChannelId: string | null) => Promise<string>;
|
|
80
|
+
/** Feature 059 — emits `invoice.issued.v1` / `invoice.corrected.v1`. */
|
|
81
|
+
eventBus?: InvoicesEventBus;
|
|
82
|
+
/** FR-035 — audit-log recorder for issuance / correction. */
|
|
83
|
+
audit?: InvoiceAuditRecorder;
|
|
84
|
+
/** Feature 054 — full audit sink for invoice-template writes (co-transactional). */
|
|
85
|
+
auditLog?: AuditPort;
|
|
86
|
+
/**
|
|
87
|
+
* Load image bytes for InvoiceLogo from the Assets Library (avoids pdfmake
|
|
88
|
+
* self-fetching `/assets/file/:id` during Preview PDF).
|
|
89
|
+
*/
|
|
90
|
+
loadAssetImage: LoadAssetImage;
|
|
91
|
+
/** Mode B wait. Absent ledger degrades to today's ready path. */
|
|
92
|
+
ledgerRouting?: LedgerNumberingLookup;
|
|
93
|
+
/**
|
|
94
|
+
* The blocks other modules render onto the PDF (`specs/134-paid-module-extraction/`
|
|
95
|
+
* T063/T126) — this module's own `invoicePdfBlockRegistry`, which contributors
|
|
96
|
+
* push into from their composition roots.
|
|
97
|
+
*/
|
|
98
|
+
pdfBlocks: InvoicePdfBlockRegistry;
|
|
99
|
+
}
|
|
100
|
+
export interface InvoicesModuleHandle {
|
|
101
|
+
/** FR-002 reactor; `backend.ts` owns its subscription. */
|
|
102
|
+
autoIssueReactor: ReturnType<typeof createAutoIssueReactor>;
|
|
103
|
+
invoiceService: InvoiceService;
|
|
104
|
+
pdfRenderer: InvoicePdfRenderer;
|
|
105
|
+
numberGenerator: InvoiceNumberGenerator;
|
|
106
|
+
templateService: InvoiceTemplateService;
|
|
107
|
+
emailDispatcher: InvoiceEmailDispatcher;
|
|
108
|
+
/**
|
|
109
|
+
* The logo loader this module resolved for itself (T118c).
|
|
110
|
+
*
|
|
111
|
+
* On the handle because that is the only place the *composed* value is
|
|
112
|
+
* observable: the renderer holds it privately, and a test asserting it
|
|
113
|
+
* through a rendered PDF would be asserting pdfmake. D-223's survey found it
|
|
114
|
+
* supplied by one root and omitted by the other, so what this exposes is the
|
|
115
|
+
* exact thing that went unnoticed.
|
|
116
|
+
*/
|
|
117
|
+
loadAssetImage: LoadAssetImage;
|
|
118
|
+
}
|
|
119
|
+
export declare function invoicesModule(options: InvoicesModuleOptions): {
|
|
120
|
+
handle: InvoicesModuleHandle;
|
|
121
|
+
plugin: ModuleAttach;
|
|
122
|
+
};
|
|
123
|
+
export {};
|
|
124
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { InvoiceService } from './services/invoice-service.js';
|
|
2
|
+
import { InvoicePdfRenderer } from './services/invoice-pdf-renderer.js';
|
|
3
|
+
import { InvoiceNumberGenerator, createSettingsPatternResolver } from './services/invoice-number-generator.js';
|
|
4
|
+
import { SellerSettingsResolver } from './services/seller-settings.js';
|
|
5
|
+
import { InvoiceEmailDispatcher } from './services/invoice-email-dispatch.js';
|
|
6
|
+
import { InvoiceTemplateService } from './services/invoice-template-service.js';
|
|
7
|
+
import { createAutoIssueReactor } from './services/auto-issue-reactor.js';
|
|
8
|
+
import { registerInvoicesAdminRoutes } from './routes.admin.js';
|
|
9
|
+
import { registerInvoicesCustomerRoutes } from './routes.customer.js';
|
|
10
|
+
export function invoicesModule(options) {
|
|
11
|
+
const numberGenerator = new InvoiceNumberGenerator(createSettingsPatternResolver(options.settingsService));
|
|
12
|
+
const sellerSettings = new SellerSettingsResolver(options.settingsService);
|
|
13
|
+
const emailOnReadyHolder = {};
|
|
14
|
+
const invoiceService = new InvoiceService(options.emFactory, options.orderReadPort, numberGenerator, sellerSettings, options.audit, options.eventBus?.emit
|
|
15
|
+
? {
|
|
16
|
+
emit: (eventName, payload) => options.eventBus.emit(eventName, payload),
|
|
17
|
+
}
|
|
18
|
+
: undefined, async (invoiceId) => {
|
|
19
|
+
await emailOnReadyHolder.dispatch?.(invoiceId);
|
|
20
|
+
}, options.ledgerRouting);
|
|
21
|
+
const pdfRenderer = new InvoicePdfRenderer({
|
|
22
|
+
loadAssetImage: options.loadAssetImage,
|
|
23
|
+
blocks: options.pdfBlocks,
|
|
24
|
+
});
|
|
25
|
+
const templateService = new InvoiceTemplateService(options.emFactory, options.auditLog);
|
|
26
|
+
// T118c — built unconditionally. The three inputs used to be optional bridge
|
|
27
|
+
// members, so a composition that supplied two of the three got no dispatcher
|
|
28
|
+
// at all and every issued invoice answered `not_sent` / `no_sender` — a
|
|
29
|
+
// plausible-looking outcome rather than a visible failure. The module
|
|
30
|
+
// resolves all three itself now and there is nothing left to be absent.
|
|
31
|
+
const emailDispatcher = new InvoiceEmailDispatcher({
|
|
32
|
+
orderReadPort: options.orderReadPort,
|
|
33
|
+
invoiceService,
|
|
34
|
+
pdfRenderer,
|
|
35
|
+
settingsService: options.settingsService,
|
|
36
|
+
getSender: options.getTransactionalEmailSender,
|
|
37
|
+
resolveRecipientEmail: options.resolveRecipientEmail,
|
|
38
|
+
resolveLanguage: options.resolveLanguage,
|
|
39
|
+
});
|
|
40
|
+
emailOnReadyHolder.dispatch = async (invoiceId) => {
|
|
41
|
+
await emailDispatcher.dispatch(invoiceId);
|
|
42
|
+
};
|
|
43
|
+
// FR-002 — the auto-issue reactor. `backend.ts` registers it through
|
|
44
|
+
// `ctx.subscribe`, so a switched-off module issues nothing.
|
|
45
|
+
const autoIssueReactor = createAutoIssueReactor({
|
|
46
|
+
emFactory: options.emFactory,
|
|
47
|
+
invoiceService,
|
|
48
|
+
settingsService: options.settingsService,
|
|
49
|
+
emailDispatcher,
|
|
50
|
+
});
|
|
51
|
+
const handle = {
|
|
52
|
+
autoIssueReactor,
|
|
53
|
+
invoiceService,
|
|
54
|
+
pdfRenderer,
|
|
55
|
+
numberGenerator,
|
|
56
|
+
templateService,
|
|
57
|
+
emailDispatcher,
|
|
58
|
+
loadAssetImage: options.loadAssetImage,
|
|
59
|
+
};
|
|
60
|
+
const plugin = async (app) => {
|
|
61
|
+
// Seed the system generic template once (idempotent).
|
|
62
|
+
await templateService.ensureGenericSeed().catch(() => undefined);
|
|
63
|
+
await registerInvoicesAdminRoutes(app, {
|
|
64
|
+
emFactory: options.emFactory,
|
|
65
|
+
orderReadPort: options.orderReadPort,
|
|
66
|
+
requireAdmin: options.requireAdmin,
|
|
67
|
+
invoiceService,
|
|
68
|
+
pdfRenderer,
|
|
69
|
+
templateService,
|
|
70
|
+
emailDispatcher,
|
|
71
|
+
resolveAdminUserId: options.resolveAdminUserId,
|
|
72
|
+
});
|
|
73
|
+
await registerInvoicesCustomerRoutes(app, {
|
|
74
|
+
emFactory: options.emFactory,
|
|
75
|
+
orderReadPort: options.orderReadPort,
|
|
76
|
+
assetsLibrary: options.assetsLibrary,
|
|
77
|
+
saleDocumentAttachments: options.saleDocumentAttachments,
|
|
78
|
+
requireCustomer: options.requireCustomer,
|
|
79
|
+
resolveCustomerContext: options.resolveCustomerContext,
|
|
80
|
+
invoiceService,
|
|
81
|
+
pdfRenderer,
|
|
82
|
+
templateService,
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
return { handle, plugin };
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyRequest } from 'fastify';
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
3
|
+
import { type OrderReadPort } from '@endora-commerce/contracts';
|
|
4
|
+
import type { InvoiceService } from './services/invoice-service.js';
|
|
5
|
+
import type { InvoicePdfRenderer } from './services/invoice-pdf-renderer.js';
|
|
6
|
+
import type { InvoiceTemplateService } from './services/invoice-template-service.js';
|
|
7
|
+
import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
|
|
8
|
+
import type { InvoiceEmailDispatchResult } from './services/invoice-email-dispatch.js';
|
|
9
|
+
/** Minimal email-dispatch seam — implemented by the US5 dispatcher. */
|
|
10
|
+
export interface InvoiceEmailDispatcher {
|
|
11
|
+
dispatch(invoiceId: string, opts?: {
|
|
12
|
+
mode?: 'attachment' | 'link';
|
|
13
|
+
messageId?: string;
|
|
14
|
+
}): Promise<InvoiceEmailDispatchResult>;
|
|
15
|
+
sendOnIssueEnabled(salesChannelId: string | null): Promise<boolean>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Admin invoice routes (feature 047). Browse/list, view detail, download the
|
|
19
|
+
* PDF (rendered on demand), issue an invoice/proforma for an order, regenerate
|
|
20
|
+
* the PDF, and (re)send the invoice email.
|
|
21
|
+
*/
|
|
22
|
+
export interface InvoicesAdminDeps {
|
|
23
|
+
emFactory: () => EntityManager;
|
|
24
|
+
/** `orders`' published read model (feature 075, Phase C). */
|
|
25
|
+
orderReadPort: OrderReadPort;
|
|
26
|
+
requireAdmin: RequireAdminFactory;
|
|
27
|
+
invoiceService: InvoiceService;
|
|
28
|
+
pdfRenderer: InvoicePdfRenderer;
|
|
29
|
+
templateService: InvoiceTemplateService;
|
|
30
|
+
/**
|
|
31
|
+
* T118c — required, both of them. Each was optional because the *bridge*
|
|
32
|
+
* member behind it was, and a composition that omitted one got a route that
|
|
33
|
+
* silently did less: no dispatcher meant every issue answered
|
|
34
|
+
* `not_sent` / `no_sender`, and no resolver meant every invoice was recorded
|
|
35
|
+
* as issued by nobody. The module resolves both itself now.
|
|
36
|
+
*/
|
|
37
|
+
emailDispatcher: InvoiceEmailDispatcher;
|
|
38
|
+
resolveAdminUserId: (req: FastifyRequest) => string | null;
|
|
39
|
+
}
|
|
40
|
+
export declare function registerInvoicesAdminRoutes(app: FastifyInstance, deps: InvoicesAdminDeps): Promise<void>;
|
|
41
|
+
//# sourceMappingURL=routes.admin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.admin.d.ts","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,4BAA4B,CAAC;AAIpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAIrF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAEvF,uEAAuE;AACvE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CACN,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1D,OAAO,CAAC,0BAA0B,CAAC,CAAC;IACvC,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACrE;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,6DAA6D;IAC7D,aAAa,EAAE,aAAa,CAAC;IAC7B,YAAY,EAAE,mBAAmB,CAAC;IAClC,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,EAAE,kBAAkB,CAAC;IAChC,eAAe,EAAE,sBAAsB,CAAC;IACxC;;;;;;OAMG;IACH,eAAe,EAAE,sBAAsB,CAAC;IACxC,kBAAkB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,GAAG,IAAI,CAAC;CAC5D;AAID,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,IAAI,CAAC,CA4Of"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { issueInvoiceRequestSchema, sendInvoiceEmailRequestSchema, } from '@endora-commerce/contracts';
|
|
2
|
+
import { Invoice } from './entities/invoice.entity.js';
|
|
3
|
+
import { isOrgInScope } from '@endora-commerce/platform/tenancy';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { invoicePageBuilderDescriptor } from './pdf-components/descriptor.js';
|
|
6
|
+
import { sampleInvoiceDetail } from './pdf-components/sample.js';
|
|
7
|
+
import { pickLanguageTree } from './pdf-components/tree-mapper.js';
|
|
8
|
+
const RENDER_LANGUAGE = 'pl-PL';
|
|
9
|
+
export async function registerInvoicesAdminRoutes(app, deps) {
|
|
10
|
+
const { emFactory, orderReadPort, requireAdmin, invoiceService, pdfRenderer, templateService } = deps;
|
|
11
|
+
// List ------------------------------------------------------------------
|
|
12
|
+
app.get('/api/v1/admin/invoices', { preHandler: requireAdmin('invoices:read') }, async (request) => {
|
|
13
|
+
const q = (request.query ?? {});
|
|
14
|
+
const where = {};
|
|
15
|
+
if (q['filter[status]'])
|
|
16
|
+
where['status'] = q['filter[status]'];
|
|
17
|
+
if (q['filter[orderId]'])
|
|
18
|
+
where['orderId'] = q['filter[orderId]'];
|
|
19
|
+
if (q['filter[kind]'])
|
|
20
|
+
where['kind'] = q['filter[kind]'];
|
|
21
|
+
if (q['filter[salesChannelId]'])
|
|
22
|
+
where['salesChannelId'] = q['filter[salesChannelId]'];
|
|
23
|
+
const issuedAt = {};
|
|
24
|
+
if (q['filter[issuedFrom]'])
|
|
25
|
+
issuedAt['$gte'] = new Date(q['filter[issuedFrom]']);
|
|
26
|
+
if (q['filter[issuedTo]'])
|
|
27
|
+
issuedAt['$lte'] = new Date(q['filter[issuedTo]']);
|
|
28
|
+
if (Object.keys(issuedAt).length)
|
|
29
|
+
where['issuedAt'] = issuedAt;
|
|
30
|
+
const total = {};
|
|
31
|
+
if (q['filter[totalMin]'] && Number.isFinite(Number(q['filter[totalMin]'])))
|
|
32
|
+
total['$gte'] = Number(q['filter[totalMin]']);
|
|
33
|
+
if (q['filter[totalMax]'] && Number.isFinite(Number(q['filter[totalMax]'])))
|
|
34
|
+
total['$lte'] = Number(q['filter[totalMax]']);
|
|
35
|
+
if (Object.keys(total).length)
|
|
36
|
+
where['total'] = total;
|
|
37
|
+
const em = emFactory();
|
|
38
|
+
// Filter by order number (business id): resolve matching orders first,
|
|
39
|
+
// then constrain invoices to those order ids. A no-match short-circuits.
|
|
40
|
+
//
|
|
41
|
+
// Feature 080, T048 (D-169) — the lookup is `orders`' own search now. It
|
|
42
|
+
// was `em.find(Order, { businessId: { $ilike } }, { limit: 500 })` here,
|
|
43
|
+
// a plain read of that module's table, so it takes a read-port method and
|
|
44
|
+
// not an `EntityManager`-taking apply port. The short-circuit is what
|
|
45
|
+
// made it worth cutting: an absent `orders` used to answer "no invoice
|
|
46
|
+
// for that order number" out of a table nobody was serving.
|
|
47
|
+
const orderNumber = q['filter[orderNumber]']?.trim();
|
|
48
|
+
if (orderNumber) {
|
|
49
|
+
const matchingOrderIds = await orderReadPort.findIdsByBusinessIdLike(orderNumber, 500);
|
|
50
|
+
if (matchingOrderIds.length === 0) {
|
|
51
|
+
return { data: [], pagination: { cursor: null, hasMore: false, limit: 0 } };
|
|
52
|
+
}
|
|
53
|
+
where['orderId'] = { $in: matchingOrderIds };
|
|
54
|
+
}
|
|
55
|
+
const limit = Math.min(Math.max(Number.parseInt(q['limit'] ?? '50', 10), 1), 200);
|
|
56
|
+
const rows = await em.find(Invoice, where, { orderBy: { issuedAt: 'desc' }, limit });
|
|
57
|
+
const orderIds = [
|
|
58
|
+
...new Set(rows.map((r) => r.orderId).filter((id) => id != null)),
|
|
59
|
+
];
|
|
60
|
+
// Feature 075, Phase C — the order number and the owning organisation are
|
|
61
|
+
// `orders`' fields, read over its port rather than out of its table.
|
|
62
|
+
const orders = await orderReadPort.findByIds(orderIds);
|
|
63
|
+
const byOrder = new Map(orders.map((o) => [o.id, o.businessId]));
|
|
64
|
+
const orgByOrder = new Map(orders.map((o) => [o.id, o.organizationId]));
|
|
65
|
+
// Feature 050 — Invoice is transitively scoped via its Order's org; hide
|
|
66
|
+
// invoices whose order is out of the ambient tenant scope. ERP-imported
|
|
67
|
+
// rows without an order scope directly on `organizationId` (feature 119).
|
|
68
|
+
const scoped = rows.filter((i) => i.origin === 'erp_import'
|
|
69
|
+
? isOrgInScope(i.organizationId ?? '')
|
|
70
|
+
: isOrgInScope(orgByOrder.get(i.orderId ?? '') ?? ''));
|
|
71
|
+
return {
|
|
72
|
+
data: scoped.map((i) => serialize(i, i.orderId ? (byOrder.get(i.orderId) ?? null) : null)),
|
|
73
|
+
pagination: { cursor: null, hasMore: false, limit: scoped.length },
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
// Detail ----------------------------------------------------------------
|
|
77
|
+
app.get('/api/v1/admin/invoices/:id', { preHandler: requireAdmin('invoices:read') }, async (request) => {
|
|
78
|
+
return { data: await invoiceService.buildDetail(request.params.id) };
|
|
79
|
+
});
|
|
80
|
+
// PDF (rendered on demand) ---------------------------------------------
|
|
81
|
+
app.get('/api/v1/admin/invoices/:id/pdf', { preHandler: requireAdmin('invoices:read'), config: { streamingResponse: true } }, async (request, reply) => {
|
|
82
|
+
const detail = await invoiceService.buildDetail(request.params.id);
|
|
83
|
+
const tree = await templateService.resolveTree(detail.salesChannelId, RENDER_LANGUAGE);
|
|
84
|
+
const pdf = await pdfRenderer.render(detail, 'pl', tree);
|
|
85
|
+
reply
|
|
86
|
+
.header('content-type', 'application/pdf')
|
|
87
|
+
.header('content-disposition', `attachment; filename="invoice-${detail.number.replace(/\W+/g, '_')}.pdf"`);
|
|
88
|
+
return reply.send(pdf);
|
|
89
|
+
});
|
|
90
|
+
// Issue an invoice/proforma for an order -------------------------------
|
|
91
|
+
app.post('/api/v1/admin/orders/:orderId/invoices', { preHandler: requireAdmin('invoices:write'), schema: { body: issueInvoiceRequestSchema } }, async (request, reply) => {
|
|
92
|
+
const body = issueInvoiceRequestSchema.parse(request.body ?? {});
|
|
93
|
+
const issuedBy = deps.resolveAdminUserId(request) ?? undefined;
|
|
94
|
+
const detail = await invoiceService.issue(request.params.orderId, body.kind, {
|
|
95
|
+
...(body.saleDate ? { saleDate: body.saleDate } : {}),
|
|
96
|
+
...(body.paymentDueDate ? { paymentDueDate: body.paymentDueDate } : {}),
|
|
97
|
+
...(issuedBy ? { issuedBy } : {}),
|
|
98
|
+
});
|
|
99
|
+
const email = await sendOnIssue(deps, detail.id, detail.salesChannelId, detail.status);
|
|
100
|
+
// Still a 201 whatever `email` says: the notification is best-effort
|
|
101
|
+
// (FR-024) and a suppressed message must not undo an issued document.
|
|
102
|
+
reply.status(201);
|
|
103
|
+
return { data: detail, email };
|
|
104
|
+
});
|
|
105
|
+
// Regenerate PDF — the PDF is rendered on demand, so "regenerate" actually
|
|
106
|
+
// re-runs the full render pipeline (template tree + pdfmake) to validate that
|
|
107
|
+
// a fresh document can be produced and to surface any rendering error to the
|
|
108
|
+
// operator instead of silently doing nothing. Returns the refreshed detail.
|
|
109
|
+
app.post('/api/v1/admin/invoices/:id/regenerate-pdf', { preHandler: requireAdmin('invoices:write') }, async (request) => {
|
|
110
|
+
const detail = await invoiceService.buildDetail(request.params.id);
|
|
111
|
+
const tree = await templateService.resolveTree(detail.salesChannelId, RENDER_LANGUAGE);
|
|
112
|
+
await pdfRenderer.render(detail, 'pl', tree);
|
|
113
|
+
return { data: detail };
|
|
114
|
+
});
|
|
115
|
+
// (Re)send the invoice email -------------------------------------------
|
|
116
|
+
app.post('/api/v1/admin/invoices/:id/send-email', { preHandler: requireAdmin('invoices:write'), schema: { body: sendInvoiceEmailRequestSchema } }, async (request) => {
|
|
117
|
+
const body = sendInvoiceEmailRequestSchema.parse(request.body ?? {});
|
|
118
|
+
// The `if (!deps.emailDispatcher)` arm that stood here is gone with T118c
|
|
119
|
+
// and `no_sender` is not: a dispatcher always exists now, and it answers
|
|
120
|
+
// that name itself when `transactional_emails` has announced no sender.
|
|
121
|
+
// The reason word is unchanged — it was `email_not_configured` until
|
|
122
|
+
// issue #149, an eighth word for one of the seven reasons a caller can
|
|
123
|
+
// translate.
|
|
124
|
+
const messageId = `invoice_issued:${request.params.id}:resend:${Date.now()}`;
|
|
125
|
+
// Issue #103 — the operator asked for this send explicitly, so the reason
|
|
126
|
+
// it did not happen belongs in the answer rather than only in the log.
|
|
127
|
+
const result = await deps.emailDispatcher.dispatch(request.params.id, {
|
|
128
|
+
...(body.mode ? { mode: body.mode } : {}),
|
|
129
|
+
messageId,
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
data: result.sent ? { ok: true } : { ok: false, reason: result.reason },
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
// --- Invoice templates (US6) ------------------------------------------
|
|
136
|
+
app.get('/api/v1/admin/invoice-templates', { preHandler: requireAdmin('invoices:read') }, async () => ({ data: await templateService.list() }));
|
|
137
|
+
app.get('/api/v1/admin/invoice-templates/page-builder/config', { preHandler: requireAdmin('invoices:read') }, async () => ({ data: invoicePageBuilderDescriptor(pdfRenderer.blocks) }));
|
|
138
|
+
app.get('/api/v1/admin/invoice-templates/:id', { preHandler: requireAdmin('invoices:read') }, async (request) => ({ data: await templateService.get(request.params.id) }));
|
|
139
|
+
const createTemplateSchema = z.object({
|
|
140
|
+
code: z.string().min(1).max(180),
|
|
141
|
+
name: z.string().min(1).max(200),
|
|
142
|
+
salesChannelId: z.string().uuid().nullable().optional(),
|
|
143
|
+
});
|
|
144
|
+
app.post('/api/v1/admin/invoice-templates', { preHandler: requireAdmin('invoices:write'), schema: { body: createTemplateSchema } }, async (request, reply) => {
|
|
145
|
+
const body = createTemplateSchema.parse(request.body);
|
|
146
|
+
const created = await templateService.create({
|
|
147
|
+
code: body.code,
|
|
148
|
+
name: body.name,
|
|
149
|
+
salesChannelId: body.salesChannelId ?? null,
|
|
150
|
+
});
|
|
151
|
+
reply.status(201);
|
|
152
|
+
return { data: created };
|
|
153
|
+
});
|
|
154
|
+
const saveContentSchema = z.object({ data: z.unknown(), version: z.number().int() });
|
|
155
|
+
app.put('/api/v1/admin/invoice-templates/:id/content/:language', { preHandler: requireAdmin('invoices:write'), schema: { body: saveContentSchema } }, async (request) => {
|
|
156
|
+
const body = saveContentSchema.parse(request.body);
|
|
157
|
+
return {
|
|
158
|
+
data: await templateService.saveContent(request.params.id, request.params.language, body.data, body.version),
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
// Preview: sample invoice through this template's saved tree (GET) or a draft
|
|
162
|
+
// canvas tree (POST) without persisting.
|
|
163
|
+
app.get('/api/v1/admin/invoice-templates/:id/preview', { preHandler: requireAdmin('invoices:write'), config: { streamingResponse: true } }, async (request, reply) => {
|
|
164
|
+
const tpl = await templateService.get(request.params.id);
|
|
165
|
+
const tree = pickLanguageTree(tpl.content, RENDER_LANGUAGE);
|
|
166
|
+
const pdf = await pdfRenderer.render(sampleInvoiceDetail(), 'en', tree);
|
|
167
|
+
reply.header('content-type', 'application/pdf').header('content-disposition', 'inline; filename="preview.pdf"');
|
|
168
|
+
return reply.send(pdf);
|
|
169
|
+
});
|
|
170
|
+
app.post('/api/v1/admin/invoice-templates/:id/preview', { preHandler: requireAdmin('invoices:write') }, async (request, reply) => {
|
|
171
|
+
await templateService.get(request.params.id); // 404 if missing
|
|
172
|
+
const body = z.object({ data: z.unknown() }).parse(request.body);
|
|
173
|
+
const pdf = await pdfRenderer.render(sampleInvoiceDetail(), 'en', body.data);
|
|
174
|
+
reply.header('content-type', 'application/pdf').header('content-disposition', 'inline; filename="preview.pdf"');
|
|
175
|
+
return reply.send(pdf);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Send-on-issue, and what became of it (issue #149).
|
|
180
|
+
*
|
|
181
|
+
* The dispatch result used to be awaited and dropped, so an operator who
|
|
182
|
+
* clicked "issue and send" was told "issued" whether the e-mail went out or was
|
|
183
|
+
* suppressed for one of seven named reasons — the last site of the #67/#78/#115
|
|
184
|
+
* family. Nothing catches here: `dispatch` contains its own failures and names
|
|
185
|
+
* them in the result (FR-029), and the one thing it re-throws is a presence
|
|
186
|
+
* answer that must reach the caller as a 503.
|
|
187
|
+
*/
|
|
188
|
+
async function sendOnIssue(deps, invoiceId, salesChannelId, invoiceStatus) {
|
|
189
|
+
const dispatcher = deps.emailDispatcher;
|
|
190
|
+
// The `if (!dispatcher)` arm that stood here is gone with T118c: the
|
|
191
|
+
// dispatcher is built unconditionally, so "this composition has no e-mail
|
|
192
|
+
// surface" is no longer a state. `no_sender` is still reachable and is still
|
|
193
|
+
// the honest answer — it is the dispatcher's own, for a
|
|
194
|
+
// `transactional_emails` that has not announced its sender.
|
|
195
|
+
if (invoiceStatus === 'pending')
|
|
196
|
+
return { status: 'not_requested' };
|
|
197
|
+
if (!(await dispatcher.sendOnIssueEnabled(salesChannelId)))
|
|
198
|
+
return { status: 'not_requested' };
|
|
199
|
+
const result = await dispatcher.dispatch(invoiceId);
|
|
200
|
+
return result.sent ? { status: 'sent' } : { status: 'not_sent', reason: result.reason };
|
|
201
|
+
}
|
|
202
|
+
function serialize(i, orderBusinessId) {
|
|
203
|
+
return {
|
|
204
|
+
id: i.id,
|
|
205
|
+
orderId: i.orderId,
|
|
206
|
+
orderBusinessId,
|
|
207
|
+
salesChannelId: i.salesChannelId ?? null,
|
|
208
|
+
kind: i.kind,
|
|
209
|
+
number: i.number,
|
|
210
|
+
issuedAt: i.issuedAt.toISOString(),
|
|
211
|
+
currency: i.currency,
|
|
212
|
+
total: Number(i.total),
|
|
213
|
+
status: i.status,
|
|
214
|
+
pdfReady: i.status === 'ready',
|
|
215
|
+
createdAt: i.createdAt.toISOString(),
|
|
216
|
+
updatedAt: i.updatedAt.toISOString(),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=routes.admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.admin.js","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyRequest } from 'fastify';
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
3
|
+
import { type AssetsLibraryPort, type InvoiceAttachmentFetchPort, type OrderReadPort } from '@endora-commerce/contracts';
|
|
4
|
+
import type { InvoiceService } from './services/invoice-service.js';
|
|
5
|
+
import type { InvoicePdfRenderer } from './services/invoice-pdf-renderer.js';
|
|
6
|
+
import type { InvoiceTemplateService } from './services/invoice-template-service.js';
|
|
7
|
+
export interface InvoicesCustomerDeps {
|
|
8
|
+
emFactory: () => EntityManager;
|
|
9
|
+
/** `orders`' published read model — the ownership check (feature 075, Phase C). */
|
|
10
|
+
orderReadPort: OrderReadPort;
|
|
11
|
+
/** `assets_library` — resolves signed URLs for private attachment bytes. */
|
|
12
|
+
assetsLibrary: AssetsLibraryPort;
|
|
13
|
+
/**
|
|
14
|
+
* This module's own attachment fetch registry (feature 134, T061): the
|
|
15
|
+
* provider of the document's source system, or `null` when none is present.
|
|
16
|
+
*/
|
|
17
|
+
saleDocumentAttachments: InvoiceAttachmentFetchPort;
|
|
18
|
+
requireCustomer: (req: FastifyRequest, reply: unknown) => Promise<void>;
|
|
19
|
+
resolveCustomerContext: (req: FastifyRequest) => {
|
|
20
|
+
customerAccountId: string;
|
|
21
|
+
organizationId: string;
|
|
22
|
+
};
|
|
23
|
+
invoiceService: InvoiceService;
|
|
24
|
+
pdfRenderer: InvoicePdfRenderer;
|
|
25
|
+
templateService: InvoiceTemplateService;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Storefront customer invoice routes (feature 047, US4). A customer lists and
|
|
29
|
+
* downloads invoices for an order they (or their organization) own. Only
|
|
30
|
+
* `ready` invoices are exposed.
|
|
31
|
+
*/
|
|
32
|
+
export declare function registerInvoicesCustomerRoutes(app: FastifyInstance, deps: InvoicesCustomerDeps): Promise<void>;
|
|
33
|
+
//# sourceMappingURL=routes.customer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.customer.d.ts","sourceRoot":"","sources":["../../src/backend/routes.customer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,0BAA0B,EAC/B,KAAK,aAAa,EAEnB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAErF,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,mFAAmF;IACnF,aAAa,EAAE,aAAa,CAAC;IAC7B,4EAA4E;IAC5E,aAAa,EAAE,iBAAiB,CAAC;IACjC;;;OAGG;IACH,uBAAuB,EAAE,0BAA0B,CAAC;IACpD,eAAe,EAAE,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,sBAAsB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK;QAAE,iBAAiB,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IACvG,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,EAAE,kBAAkB,CAAC;IAChC,eAAe,EAAE,sBAAsB,CAAC;CACzC;AAED;;;;GAIG;AACH,wBAAsB,8BAA8B,CAClD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,IAAI,CAAC,CAyJf"}
|