@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.
Files changed (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -2
  3. package/dist/admin/components/InvoiceSectionTabs.d.ts +22 -0
  4. package/dist/admin/components/InvoiceSectionTabs.d.ts.map +1 -0
  5. package/dist/admin/components/InvoiceSectionTabs.js +34 -0
  6. package/dist/admin/components/InvoiceSectionTabs.js.map +1 -0
  7. package/dist/admin/index.d.ts +34 -0
  8. package/dist/admin/index.d.ts.map +1 -0
  9. package/dist/admin/index.js +69 -0
  10. package/dist/admin/index.js.map +1 -0
  11. package/dist/admin/pages/InvoiceDetail.d.ts +10 -0
  12. package/dist/admin/pages/InvoiceDetail.d.ts.map +1 -0
  13. package/dist/admin/pages/InvoiceDetail.js +111 -0
  14. package/dist/admin/pages/InvoiceDetail.js.map +1 -0
  15. package/dist/admin/pages/InvoiceTemplateEditor.d.ts +11 -0
  16. package/dist/admin/pages/InvoiceTemplateEditor.d.ts.map +1 -0
  17. package/dist/admin/pages/InvoiceTemplateEditor.js +188 -0
  18. package/dist/admin/pages/InvoiceTemplateEditor.js.map +1 -0
  19. package/dist/admin/pages/InvoiceTemplatesPage.d.ts +10 -0
  20. package/dist/admin/pages/InvoiceTemplatesPage.d.ts.map +1 -0
  21. package/dist/admin/pages/InvoiceTemplatesPage.js +60 -0
  22. package/dist/admin/pages/InvoiceTemplatesPage.js.map +1 -0
  23. package/dist/admin/pages/InvoicesList.d.ts +9 -0
  24. package/dist/admin/pages/InvoicesList.d.ts.map +1 -0
  25. package/dist/admin/pages/InvoicesList.js +116 -0
  26. package/dist/admin/pages/InvoicesList.js.map +1 -0
  27. package/dist/admin/templates/described-blocks.d.ts +67 -0
  28. package/dist/admin/templates/described-blocks.d.ts.map +1 -0
  29. package/dist/admin/templates/described-blocks.js +83 -0
  30. package/dist/admin/templates/described-blocks.js.map +1 -0
  31. package/dist/admin/templates/invoice-builder-plugin.d.ts +4 -0
  32. package/dist/admin/templates/invoice-builder-plugin.d.ts.map +1 -0
  33. package/dist/admin/templates/invoice-builder-plugin.js +12 -0
  34. package/dist/admin/templates/invoice-builder-plugin.js.map +1 -0
  35. package/dist/admin/templates/invoice-puck-config.d.ts +37 -0
  36. package/dist/admin/templates/invoice-puck-config.d.ts.map +1 -0
  37. package/dist/admin/templates/invoice-puck-config.js +733 -0
  38. package/dist/admin/templates/invoice-puck-config.js.map +1 -0
  39. package/dist/backend/email-templates/invoice-issued.default.d.ts +10 -0
  40. package/dist/backend/email-templates/invoice-issued.default.d.ts.map +1 -0
  41. package/dist/backend/email-templates/invoice-issued.default.js +45 -0
  42. package/dist/backend/email-templates/invoice-issued.default.js.map +1 -0
  43. package/dist/backend/entities/invoice-external-attachment.entity.d.ts +20 -0
  44. package/dist/backend/entities/invoice-external-attachment.entity.d.ts.map +1 -0
  45. package/dist/backend/entities/invoice-external-attachment.entity.js +77 -0
  46. package/dist/backend/entities/invoice-external-attachment.entity.js.map +1 -0
  47. package/dist/backend/entities/invoice-line.entity.d.ts +20 -0
  48. package/dist/backend/entities/invoice-line.entity.d.ts.map +1 -0
  49. package/dist/backend/entities/invoice-line.entity.js +82 -0
  50. package/dist/backend/entities/invoice-line.entity.js.map +1 -0
  51. package/dist/backend/entities/invoice-number-counter.entity.d.ts +18 -0
  52. package/dist/backend/entities/invoice-number-counter.entity.d.ts.map +1 -0
  53. package/dist/backend/entities/invoice-number-counter.entity.js +63 -0
  54. package/dist/backend/entities/invoice-number-counter.entity.js.map +1 -0
  55. package/dist/backend/entities/invoice-template.entity.d.ts +22 -0
  56. package/dist/backend/entities/invoice-template.entity.d.ts.map +1 -0
  57. package/dist/backend/entities/invoice-template.entity.js +84 -0
  58. package/dist/backend/entities/invoice-template.entity.js.map +1 -0
  59. package/dist/backend/entities/invoice.entity.d.ts +64 -0
  60. package/dist/backend/entities/invoice.entity.d.ts.map +1 -0
  61. package/dist/backend/entities/invoice.entity.js +202 -0
  62. package/dist/backend/entities/invoice.entity.js.map +1 -0
  63. package/dist/backend/index.d.ts +111 -0
  64. package/dist/backend/index.d.ts.map +1 -0
  65. package/dist/backend/index.js +291 -0
  66. package/dist/backend/index.js.map +1 -0
  67. package/dist/backend/pdf-components/descriptor.d.ts +29 -0
  68. package/dist/backend/pdf-components/descriptor.d.ts.map +1 -0
  69. package/dist/backend/pdf-components/descriptor.js +227 -0
  70. package/dist/backend/pdf-components/descriptor.js.map +1 -0
  71. package/dist/backend/pdf-components/embed-logo-images.d.ts +35 -0
  72. package/dist/backend/pdf-components/embed-logo-images.d.ts.map +1 -0
  73. package/dist/backend/pdf-components/embed-logo-images.js +101 -0
  74. package/dist/backend/pdf-components/embed-logo-images.js.map +1 -0
  75. package/dist/backend/pdf-components/sample.d.ts +4 -0
  76. package/dist/backend/pdf-components/sample.d.ts.map +1 -0
  77. package/dist/backend/pdf-components/sample.js +75 -0
  78. package/dist/backend/pdf-components/sample.js.map +1 -0
  79. package/dist/backend/pdf-components/sections.d.ts +34 -0
  80. package/dist/backend/pdf-components/sections.d.ts.map +1 -0
  81. package/dist/backend/pdf-components/sections.js +445 -0
  82. package/dist/backend/pdf-components/sections.js.map +1 -0
  83. package/dist/backend/pdf-components/tree-mapper.d.ts +37 -0
  84. package/dist/backend/pdf-components/tree-mapper.d.ts.map +1 -0
  85. package/dist/backend/pdf-components/tree-mapper.js +76 -0
  86. package/dist/backend/pdf-components/tree-mapper.js.map +1 -0
  87. package/dist/backend/plugin.d.ts +124 -0
  88. package/dist/backend/plugin.d.ts.map +1 -0
  89. package/dist/backend/plugin.js +87 -0
  90. package/dist/backend/plugin.js.map +1 -0
  91. package/dist/backend/routes.admin.d.ts +41 -0
  92. package/dist/backend/routes.admin.d.ts.map +1 -0
  93. package/dist/backend/routes.admin.js +219 -0
  94. package/dist/backend/routes.admin.js.map +1 -0
  95. package/dist/backend/routes.customer.d.ts +33 -0
  96. package/dist/backend/routes.customer.d.ts.map +1 -0
  97. package/dist/backend/routes.customer.js +124 -0
  98. package/dist/backend/routes.customer.js.map +1 -0
  99. package/dist/backend/seeds/generic-invoice-template.d.ts +18 -0
  100. package/dist/backend/seeds/generic-invoice-template.d.ts.map +1 -0
  101. package/dist/backend/seeds/generic-invoice-template.js +174 -0
  102. package/dist/backend/seeds/generic-invoice-template.js.map +1 -0
  103. package/dist/backend/services/amount-to-words.d.ts +20 -0
  104. package/dist/backend/services/amount-to-words.d.ts.map +1 -0
  105. package/dist/backend/services/amount-to-words.js +232 -0
  106. package/dist/backend/services/amount-to-words.js.map +1 -0
  107. package/dist/backend/services/auto-issue-reactor.d.ts +53 -0
  108. package/dist/backend/services/auto-issue-reactor.d.ts.map +1 -0
  109. package/dist/backend/services/auto-issue-reactor.js +122 -0
  110. package/dist/backend/services/auto-issue-reactor.js.map +1 -0
  111. package/dist/backend/services/corrective-invoice.d.ts +38 -0
  112. package/dist/backend/services/corrective-invoice.d.ts.map +1 -0
  113. package/dist/backend/services/corrective-invoice.js +257 -0
  114. package/dist/backend/services/corrective-invoice.js.map +1 -0
  115. package/dist/backend/services/cross-module-context.d.ts +82 -0
  116. package/dist/backend/services/cross-module-context.d.ts.map +1 -0
  117. package/dist/backend/services/cross-module-context.js +93 -0
  118. package/dist/backend/services/cross-module-context.js.map +1 -0
  119. package/dist/backend/services/duplicate-number-refusal.d.ts +33 -0
  120. package/dist/backend/services/duplicate-number-refusal.d.ts.map +1 -0
  121. package/dist/backend/services/duplicate-number-refusal.js +92 -0
  122. package/dist/backend/services/duplicate-number-refusal.js.map +1 -0
  123. package/dist/backend/services/erp-sale-document-write-port.d.ts +37 -0
  124. package/dist/backend/services/erp-sale-document-write-port.d.ts.map +1 -0
  125. package/dist/backend/services/erp-sale-document-write-port.js +192 -0
  126. package/dist/backend/services/erp-sale-document-write-port.js.map +1 -0
  127. package/dist/backend/services/invoice-attachment-fetch-registry.d.ts +75 -0
  128. package/dist/backend/services/invoice-attachment-fetch-registry.d.ts.map +1 -0
  129. package/dist/backend/services/invoice-attachment-fetch-registry.js +81 -0
  130. package/dist/backend/services/invoice-attachment-fetch-registry.js.map +1 -0
  131. package/dist/backend/services/invoice-copy-host.service.d.ts +9 -0
  132. package/dist/backend/services/invoice-copy-host.service.d.ts.map +1 -0
  133. package/dist/backend/services/invoice-copy-host.service.js +52 -0
  134. package/dist/backend/services/invoice-copy-host.service.js.map +1 -0
  135. package/dist/backend/services/invoice-email-dispatch.d.ts +89 -0
  136. package/dist/backend/services/invoice-email-dispatch.d.ts.map +1 -0
  137. package/dist/backend/services/invoice-email-dispatch.js +138 -0
  138. package/dist/backend/services/invoice-email-dispatch.js.map +1 -0
  139. package/dist/backend/services/invoice-line-builder.d.ts +29 -0
  140. package/dist/backend/services/invoice-line-builder.d.ts.map +1 -0
  141. package/dist/backend/services/invoice-line-builder.js +46 -0
  142. package/dist/backend/services/invoice-line-builder.js.map +1 -0
  143. package/dist/backend/services/invoice-number-collisions.d.ts +17 -0
  144. package/dist/backend/services/invoice-number-collisions.d.ts.map +1 -0
  145. package/dist/backend/services/invoice-number-collisions.js +154 -0
  146. package/dist/backend/services/invoice-number-collisions.js.map +1 -0
  147. package/dist/backend/services/invoice-number-generator.d.ts +96 -0
  148. package/dist/backend/services/invoice-number-generator.d.ts.map +1 -0
  149. package/dist/backend/services/invoice-number-generator.js +157 -0
  150. package/dist/backend/services/invoice-number-generator.js.map +1 -0
  151. package/dist/backend/services/invoice-pdf-block-registry.d.ts +65 -0
  152. package/dist/backend/services/invoice-pdf-block-registry.d.ts.map +1 -0
  153. package/dist/backend/services/invoice-pdf-block-registry.js +94 -0
  154. package/dist/backend/services/invoice-pdf-block-registry.js.map +1 -0
  155. package/dist/backend/services/invoice-pdf-renderer.d.ts +57 -0
  156. package/dist/backend/services/invoice-pdf-renderer.d.ts.map +1 -0
  157. package/dist/backend/services/invoice-pdf-renderer.js +158 -0
  158. package/dist/backend/services/invoice-pdf-renderer.js.map +1 -0
  159. package/dist/backend/services/invoice-pdf.d.ts +24 -0
  160. package/dist/backend/services/invoice-pdf.d.ts.map +1 -0
  161. package/dist/backend/services/invoice-pdf.js +52 -0
  162. package/dist/backend/services/invoice-pdf.js.map +1 -0
  163. package/dist/backend/services/invoice-placement-apply-port.d.ts +29 -0
  164. package/dist/backend/services/invoice-placement-apply-port.d.ts.map +1 -0
  165. package/dist/backend/services/invoice-placement-apply-port.js +40 -0
  166. package/dist/backend/services/invoice-placement-apply-port.js.map +1 -0
  167. package/dist/backend/services/invoice-read-port.d.ts +44 -0
  168. package/dist/backend/services/invoice-read-port.d.ts.map +1 -0
  169. package/dist/backend/services/invoice-read-port.js +95 -0
  170. package/dist/backend/services/invoice-read-port.js.map +1 -0
  171. package/dist/backend/services/invoice-service.d.ts +94 -0
  172. package/dist/backend/services/invoice-service.d.ts.map +1 -0
  173. package/dist/backend/services/invoice-service.js +374 -0
  174. package/dist/backend/services/invoice-service.js.map +1 -0
  175. package/dist/backend/services/invoice-template-service.d.ts +43 -0
  176. package/dist/backend/services/invoice-template-service.d.ts.map +1 -0
  177. package/dist/backend/services/invoice-template-service.js +147 -0
  178. package/dist/backend/services/invoice-template-service.js.map +1 -0
  179. package/dist/backend/services/numbering-configuration.d.ts +76 -0
  180. package/dist/backend/services/numbering-configuration.d.ts.map +1 -0
  181. package/dist/backend/services/numbering-configuration.js +176 -0
  182. package/dist/backend/services/numbering-configuration.js.map +1 -0
  183. package/dist/backend/services/numbering-write-validator.d.ts +22 -0
  184. package/dist/backend/services/numbering-write-validator.d.ts.map +1 -0
  185. package/dist/backend/services/numbering-write-validator.js +82 -0
  186. package/dist/backend/services/numbering-write-validator.js.map +1 -0
  187. package/dist/backend/services/seller-settings.d.ts +24 -0
  188. package/dist/backend/services/seller-settings.d.ts.map +1 -0
  189. package/dist/backend/services/seller-settings.js +38 -0
  190. package/dist/backend/services/seller-settings.js.map +1 -0
  191. package/dist/backend/services/vendor-number-hold.d.ts +10 -0
  192. package/dist/backend/services/vendor-number-hold.d.ts.map +1 -0
  193. package/dist/backend/services/vendor-number-hold.js +14 -0
  194. package/dist/backend/services/vendor-number-hold.js.map +1 -0
  195. package/dist/manifest.d.ts +203 -0
  196. package/dist/manifest.d.ts.map +1 -0
  197. package/dist/manifest.js +390 -0
  198. package/dist/manifest.js.map +1 -0
  199. package/dist/migrations/20260629T125121_invoices_module.d.ts +17 -0
  200. package/dist/migrations/20260629T125121_invoices_module.d.ts.map +1 -0
  201. package/dist/migrations/20260629T125121_invoices_module.js +117 -0
  202. package/dist/migrations/20260629T125121_invoices_module.js.map +1 -0
  203. package/dist/migrations/20260801T111000_invoices_generic_template_reseed.d.ts +6 -0
  204. package/dist/migrations/20260801T111000_invoices_generic_template_reseed.d.ts.map +1 -0
  205. package/dist/migrations/20260801T111000_invoices_generic_template_reseed.js +25 -0
  206. package/dist/migrations/20260801T111000_invoices_generic_template_reseed.js.map +1 -0
  207. package/dist/migrations/20260817T201110_invoices_correction_idempotency_key.d.ts +20 -0
  208. package/dist/migrations/20260817T201110_invoices_correction_idempotency_key.d.ts.map +1 -0
  209. package/dist/migrations/20260817T201110_invoices_correction_idempotency_key.js +33 -0
  210. package/dist/migrations/20260817T201110_invoices_correction_idempotency_key.js.map +1 -0
  211. package/dist/migrations/20260903T101756_invoices_namespace_block_names.d.ts +6 -0
  212. package/dist/migrations/20260903T101756_invoices_namespace_block_names.d.ts.map +1 -0
  213. package/dist/migrations/20260903T101756_invoices_namespace_block_names.js +56 -0
  214. package/dist/migrations/20260903T101756_invoices_namespace_block_names.js.map +1 -0
  215. package/dist/migrations/20260914T133603_invoices_erp_imported_sale_documents.d.ts +12 -0
  216. package/dist/migrations/20260914T133603_invoices_erp_imported_sale_documents.d.ts.map +1 -0
  217. package/dist/migrations/20260914T133603_invoices_erp_imported_sale_documents.js +87 -0
  218. package/dist/migrations/20260914T133603_invoices_erp_imported_sale_documents.js.map +1 -0
  219. package/dist/migrations/20260926T230239_invoices_import_identity.d.ts +33 -0
  220. package/dist/migrations/20260926T230239_invoices_import_identity.d.ts.map +1 -0
  221. package/dist/migrations/20260926T230239_invoices_import_identity.js +83 -0
  222. package/dist/migrations/20260926T230239_invoices_import_identity.js.map +1 -0
  223. package/dist/migrations/index.d.ts +32 -0
  224. package/dist/migrations/index.d.ts.map +1 -0
  225. package/dist/migrations/index.js +39 -0
  226. package/dist/migrations/index.js.map +1 -0
  227. package/dist/ports/index.d.ts +93 -0
  228. package/dist/ports/index.d.ts.map +1 -0
  229. package/dist/ports/index.js +2 -0
  230. package/dist/ports/index.js.map +1 -0
  231. package/docs/invoices.md +113 -0
  232. package/i18n/en.json +45 -0
  233. package/i18n/pl.json +45 -0
  234. package/package.json +119 -3
  235. 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"}