@endora-commerce/mod-invoices 0.0.0-stage → 0.100.1

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,33 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ /**
3
+ * Turn a duplicated invoice number into the operator's account of it —
4
+ * feature 078, D-95.2.
5
+ *
6
+ * This is the layer that carries the **guarantee**. The channel-distinct
7
+ * default stops the ordinary path from arming a duplicate and the settings
8
+ * refusal stops a misconfiguration from being saved, but issuance is the one
9
+ * chokepoint every number passes through, so it is what makes platform-wide
10
+ * uniqueness hold. It refuses only an **actual** collision — a row the database
11
+ * has already rejected — never a possible one: a pre-flight "could this
12
+ * collide" at issuance would block sales that would have succeeded and would
13
+ * cost a settings read per channel on the hot path to do it.
14
+ *
15
+ * The transaction has already rolled back by the time this runs, so the counter
16
+ * draw is undone and a refused issuance leaves no gap in the sequence.
17
+ *
18
+ * Returns normally when `error` is a different failure; the caller re-throws it
19
+ * unchanged. `invoices_order_kind_uq` in particular is a different fact with a
20
+ * different answer and must keep surfacing as it does today.
21
+ */
22
+ export declare function refuseDuplicateInvoiceNumber(input: {
23
+ readonly error: unknown;
24
+ /**
25
+ * The number drawn inside the transaction. It has to be captured into the
26
+ * enclosing scope by the caller: the callback's result is lost when the
27
+ * transaction aborts, and this refusal exists to name the number.
28
+ */
29
+ readonly drawnNumber: string | null;
30
+ readonly salesChannelId: string | null;
31
+ readonly emFactory: () => EntityManager;
32
+ }): Promise<void>;
33
+ //# sourceMappingURL=duplicate-number-refusal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicate-number-refusal.d.ts","sourceRoot":"","sources":["../../../src/backend/services/duplicate-number-refusal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAc3D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,4BAA4B,CAAC,KAAK,EAAE;IACxD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,CAAC;CACzC,GAAG,OAAO,CAAC,IAAI,CAAC,CAmDhB"}
@@ -0,0 +1,92 @@
1
+ import { UniqueConstraintViolationException } from '@mikro-orm/core';
2
+ import { ERROR_CODES } from '@endora-commerce/contracts';
3
+ import { HttpError } from '@endora-commerce/platform/http';
4
+ import { SalesChannel } from '@endora-commerce/platform/kernel';
5
+ import { withSystemScope } from '@endora-commerce/platform/tenancy';
6
+ import { Invoice } from '../entities/invoice.entity.js';
7
+ import { channelName } from './numbering-configuration.js';
8
+ /**
9
+ * The unique index that makes an invoice number mean one document
10
+ * platform-wide (`20260425T050720_core_commerce_init.ts`).
11
+ */
12
+ const NUMBER_UNIQUE_CONSTRAINT = 'invoices_number_unique';
13
+ /**
14
+ * Turn a duplicated invoice number into the operator's account of it —
15
+ * feature 078, D-95.2.
16
+ *
17
+ * This is the layer that carries the **guarantee**. The channel-distinct
18
+ * default stops the ordinary path from arming a duplicate and the settings
19
+ * refusal stops a misconfiguration from being saved, but issuance is the one
20
+ * chokepoint every number passes through, so it is what makes platform-wide
21
+ * uniqueness hold. It refuses only an **actual** collision — a row the database
22
+ * has already rejected — never a possible one: a pre-flight "could this
23
+ * collide" at issuance would block sales that would have succeeded and would
24
+ * cost a settings read per channel on the hot path to do it.
25
+ *
26
+ * The transaction has already rolled back by the time this runs, so the counter
27
+ * draw is undone and a refused issuance leaves no gap in the sequence.
28
+ *
29
+ * Returns normally when `error` is a different failure; the caller re-throws it
30
+ * unchanged. `invoices_order_kind_uq` in particular is a different fact with a
31
+ * different answer and must keep surfacing as it does today.
32
+ */
33
+ export async function refuseDuplicateInvoiceNumber(input) {
34
+ if (!(input.error instanceof UniqueConstraintViolationException))
35
+ return;
36
+ if (violatedConstraint(input.error) !== NUMBER_UNIQUE_CONSTRAINT)
37
+ return;
38
+ const number = input.drawnNumber;
39
+ if (number === null)
40
+ return;
41
+ // The invoice already holding the number is very often another
42
+ // organization's, and `Invoice` is `@TransitivelyScoped(() => Order,
43
+ // 'orderId')`, so the read crosses organizations deliberately. It exposes
44
+ // **only** the number and the channel — never the buyer, the order or the
45
+ // amount.
46
+ const holder = await withSystemScope('invoices: name the channel already holding a duplicated invoice number', async () => {
47
+ const em = input.emFactory();
48
+ const existing = await em.findOne(Invoice, { number });
49
+ if (!existing)
50
+ return null;
51
+ const channel = existing.salesChannelId
52
+ ? await em.findOne(SalesChannel, { id: existing.salesChannelId })
53
+ : null;
54
+ return { salesChannelId: existing.salesChannelId ?? null, channel };
55
+ });
56
+ // The holder was deleted between the violation and this read. Fall back to
57
+ // the tokenless base sentence rather than fabricating a channel.
58
+ if (!holder || !holder.channel) {
59
+ throw new HttpError(409, ERROR_CODES.INVOICE_NUMBER_ALREADY_ISSUED, `Invoice number "${number}" is already in use. Nothing was issued.`, { number });
60
+ }
61
+ const sameChannel = holder.salesChannelId === input.salesChannelId;
62
+ throw new HttpError(409, ERROR_CODES.INVOICE_NUMBER_ALREADY_ISSUED, sameChannel
63
+ ? `Invoice number "${number}" has already been issued in this sales channel. ` +
64
+ `Nothing was issued.`
65
+ : `Invoice number "${number}" is already used by sales channel ` +
66
+ `"${channelName(holder.channel)}". Nothing was issued.`, {
67
+ code: sameChannel ? 'same_channel' : 'other_channel',
68
+ number,
69
+ channel: channelName(holder.channel),
70
+ channelCode: holder.channel.code,
71
+ });
72
+ }
73
+ /**
74
+ * The constraint the database named, dug out of the driver error.
75
+ *
76
+ * MikroORM wraps the `pg` error, and the constraint name is what tells
77
+ * `invoices_number_unique` apart from `invoices_order_kind_uq` — two different
78
+ * facts with two different answers, so guessing from the exception class alone
79
+ * would make the wrong one wear this refusal.
80
+ */
81
+ function violatedConstraint(error) {
82
+ let cursor = error;
83
+ for (let depth = 0; cursor !== null && cursor !== undefined && depth < 5; depth += 1) {
84
+ const named = cursor.constraint;
85
+ if (typeof named === 'string')
86
+ return named;
87
+ const wrapped = cursor;
88
+ cursor = wrapped.previous ?? wrapped.cause;
89
+ }
90
+ return null;
91
+ }
92
+ //# sourceMappingURL=duplicate-number-refusal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicate-number-refusal.js","sourceRoot":"","sources":["../../../src/backend/services/duplicate-number-refusal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import { type ErpSaleDocumentAttachmentContext, type ErpSaleDocumentUpsertInput, type ErpSaleDocumentUpsertResult, type ErpSaleDocumentWritePort } from '@endora-commerce/contracts';
3
+ export declare class ErpSaleDocumentWritePortService implements ErpSaleDocumentWritePort {
4
+ private readonly emFactory;
5
+ constructor(emFactory: () => EntityManager);
6
+ upsertImportedDocument(untrusted: ErpSaleDocumentUpsertInput): Promise<ErpSaleDocumentUpsertResult>;
7
+ resolveAttachmentContext(input: {
8
+ invoiceId: string;
9
+ attachmentId: string;
10
+ organizationId: string;
11
+ }): Promise<ErpSaleDocumentAttachmentContext | null>;
12
+ linkAttachmentAsset(input: {
13
+ invoiceId: string;
14
+ attachmentId: string;
15
+ organizationId: string;
16
+ assetId: string;
17
+ contentType?: string | null;
18
+ }): Promise<boolean>;
19
+ /**
20
+ * The imported document with this identity, in any organization.
21
+ *
22
+ * Raw SQL, deliberately outside the tenant global filter: identity belongs to
23
+ * the source document, not to an organization. Within one source system a
24
+ * document the source system reassigns to another contractor moves with it —
25
+ * that is the source system's decision, and `applyHeader` follows it. What the
26
+ * identity does guarantee is that a document from **another** system can never
27
+ * be reached (feature 134, T135; `research.md` D21 §3).
28
+ *
29
+ * The predicate states `invoices_external_document_uq`'s expression and
30
+ * partial-index condition verbatim, so the planner can answer it from that
31
+ * index.
32
+ */
33
+ private findImportedDocument;
34
+ private applyHeader;
35
+ private syncAttachments;
36
+ }
37
+ //# sourceMappingURL=erp-sale-document-write-port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erp-sale-document-write-port.d.ts","sourceRoot":"","sources":["../../../src/backend/services/erp-sale-document-write-port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAEL,KAAK,gCAAgC,EAErC,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAE9B,MAAM,4BAA4B,CAAC;AAMpC,qBAAa,+BAAgC,YAAW,wBAAwB;IAClE,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAErD,sBAAsB,CAC1B,SAAS,EAAE,0BAA0B,GACpC,OAAO,CAAC,2BAA2B,CAAC;IAoEjC,wBAAwB,CAAC,KAAK,EAAE;QACpC,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC,gCAAgC,GAAG,IAAI,CAAC;IA6B9C,mBAAmB,CAAC,KAAK,EAAE;QAC/B,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC7B,GAAG,OAAO,CAAC,OAAO,CAAC;IA4BpB;;;;;;;;;;;;;OAaG;YACW,oBAAoB;IAgBlC,OAAO,CAAC,WAAW;YAuBL,eAAe;CA4B9B"}
@@ -0,0 +1,192 @@
1
+ import { ERROR_CODES, erpSaleDocumentUpsertInputSchema, } from '@endora-commerce/contracts';
2
+ import { HttpError } from '@endora-commerce/platform/http';
3
+ import { isOrgInScope } from '@endora-commerce/platform/tenancy';
4
+ import { InvoiceExternalAttachment } from '../entities/invoice-external-attachment.entity.js';
5
+ import { Invoice } from '../entities/invoice.entity.js';
6
+ export class ErpSaleDocumentWritePortService {
7
+ emFactory;
8
+ constructor(emFactory) {
9
+ this.emFactory = emFactory;
10
+ }
11
+ async upsertImportedDocument(untrusted) {
12
+ // command-coverage-ignore: projection of a sale document the ERP owns.
13
+ // `origin: 'erp_import'` is the whole of this port's subject — the row
14
+ // mirrors a document the source system issued, and the decision to issue
15
+ // it was never taken in Endora, so there is no actor for a Command to name.
16
+ // The run that carried it is recorded by the calling connector in its own
17
+ // sync log. An Endora-issued invoice never reaches this method;
18
+ // `InvoiceService` owns that path and audits it.
19
+ //
20
+ // The input is parsed rather than trusted to its TypeScript type: a writer
21
+ // compiled against another version of the contract (feature 134, T135 —
22
+ // the pre-T135 key shape) is refused here, loudly and with
23
+ // nothing written, instead of by a database constraint.
24
+ const parsed = erpSaleDocumentUpsertInputSchema.safeParse(untrusted);
25
+ if (!parsed.success) {
26
+ throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'Imported sale document failed validation.', parsed.error.issues.map((issue) => ({
27
+ path: issue.path.map(String).join('.'),
28
+ issue: issue.message,
29
+ })));
30
+ }
31
+ const input = parsed.data;
32
+ if (!isOrgInScope(input.organizationId)) {
33
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Organization not found.');
34
+ }
35
+ const em = this.emFactory();
36
+ const existing = await this.findImportedDocument(em, {
37
+ system: input.externalDocumentRef.system,
38
+ externalId: input.externalDocumentRef.externalId,
39
+ });
40
+ if (existing) {
41
+ this.applyHeader(existing, input);
42
+ await this.syncAttachments(em, existing, input.attachments ?? []);
43
+ await em.flush();
44
+ return { invoiceId: existing.id, created: false };
45
+ }
46
+ const invoice = em.create(Invoice, {
47
+ origin: 'erp_import',
48
+ organizationId: input.organizationId,
49
+ orderId: input.orderId ?? null,
50
+ externalDocumentRef: input.externalDocumentRef,
51
+ kind: input.kind,
52
+ number: input.number,
53
+ currency: input.currency,
54
+ issuedAt: new Date(input.issuedAt),
55
+ saleDate: input.saleDate ?? null,
56
+ paymentDueDate: input.paymentDueDate ?? null,
57
+ paymentMethod: input.paymentMethod ?? null,
58
+ netTotal: input.netTotal ?? null,
59
+ taxTotal: input.taxTotal ?? null,
60
+ total: input.grossTotal,
61
+ paidTotal: input.paidTotal ?? '0',
62
+ buyerSnapshot: input.buyerSnapshot ?? null,
63
+ status: 'ready',
64
+ });
65
+ await em.persistAndFlush(invoice);
66
+ await this.syncAttachments(em, invoice, input.attachments ?? []);
67
+ await em.flush();
68
+ return { invoiceId: invoice.id, created: true };
69
+ }
70
+ async resolveAttachmentContext(input) {
71
+ const em = this.emFactory();
72
+ const invoice = await em.findOne(Invoice, {
73
+ id: input.invoiceId,
74
+ origin: 'erp_import',
75
+ organizationId: input.organizationId,
76
+ status: 'ready',
77
+ });
78
+ if (!invoice?.externalDocumentRef?.externalId) {
79
+ return null;
80
+ }
81
+ const attachment = await em.findOne(InvoiceExternalAttachment, {
82
+ id: input.attachmentId,
83
+ invoiceId: invoice.id,
84
+ });
85
+ if (!attachment) {
86
+ return null;
87
+ }
88
+ return {
89
+ system: invoice.externalDocumentRef.system,
90
+ externalId: invoice.externalDocumentRef.externalId,
91
+ externalAttachmentId: attachment.externalAttachmentId,
92
+ fileName: attachment.fileName,
93
+ contentType: attachment.contentType ?? null,
94
+ };
95
+ }
96
+ async linkAttachmentAsset(input) {
97
+ // command-coverage-ignore: idempotent stamp of the asset id for an
98
+ // attachment this port already imported. It records where the file was
99
+ // stored, not that it should exist — the import above decided that.
100
+ const em = this.emFactory();
101
+ const invoice = await em.findOne(Invoice, {
102
+ id: input.invoiceId,
103
+ origin: 'erp_import',
104
+ organizationId: input.organizationId,
105
+ status: 'ready',
106
+ });
107
+ if (!invoice)
108
+ return false;
109
+ const attachment = await em.findOne(InvoiceExternalAttachment, {
110
+ id: input.attachmentId,
111
+ invoiceId: invoice.id,
112
+ });
113
+ if (!attachment)
114
+ return false;
115
+ attachment.assetId = input.assetId;
116
+ if (input.contentType !== undefined) {
117
+ attachment.contentType = input.contentType;
118
+ }
119
+ attachment.downloadedAt = new Date();
120
+ await em.flush();
121
+ return true;
122
+ }
123
+ /**
124
+ * The imported document with this identity, in any organization.
125
+ *
126
+ * Raw SQL, deliberately outside the tenant global filter: identity belongs to
127
+ * the source document, not to an organization. Within one source system a
128
+ * document the source system reassigns to another contractor moves with it —
129
+ * that is the source system's decision, and `applyHeader` follows it. What the
130
+ * identity does guarantee is that a document from **another** system can never
131
+ * be reached (feature 134, T135; `research.md` D21 §3).
132
+ *
133
+ * The predicate states `invoices_external_document_uq`'s expression and
134
+ * partial-index condition verbatim, so the planner can answer it from that
135
+ * index.
136
+ */
137
+ async findImportedDocument(em, identity) {
138
+ const rows = await em.getConnection().execute(`select id from invoices
139
+ where origin = 'erp_import'
140
+ and (external_document_ref->>'system') = ?
141
+ and (external_document_ref->>'externalId') = ?
142
+ limit 1`, [identity.system, identity.externalId]);
143
+ const id = rows[0]?.id;
144
+ return id ? em.findOne(Invoice, { id }) : null;
145
+ }
146
+ applyHeader(invoice, input) {
147
+ invoice.organizationId = input.organizationId;
148
+ invoice.orderId = input.orderId ?? null;
149
+ invoice.externalDocumentRef = input.externalDocumentRef;
150
+ invoice.kind = input.kind;
151
+ invoice.number = input.number;
152
+ invoice.currency = input.currency;
153
+ invoice.issuedAt = new Date(input.issuedAt);
154
+ invoice.saleDate = input.saleDate ?? null;
155
+ invoice.paymentDueDate = input.paymentDueDate ?? null;
156
+ invoice.paymentMethod = input.paymentMethod ?? null;
157
+ invoice.netTotal = input.netTotal ?? null;
158
+ invoice.taxTotal = input.taxTotal ?? null;
159
+ invoice.total = input.grossTotal;
160
+ if (input.paidTotal !== undefined) {
161
+ invoice.paidTotal = input.paidTotal;
162
+ }
163
+ if (input.buyerSnapshot !== undefined) {
164
+ invoice.buyerSnapshot = input.buyerSnapshot;
165
+ }
166
+ invoice.status = 'ready';
167
+ }
168
+ async syncAttachments(em, invoice, attachments) {
169
+ // command-coverage-ignore: the attachment half of `upsertImportedDocument`,
170
+ // which carries the decision and the exemption's reason. Split out for
171
+ // readability only; it is never called from anywhere else.
172
+ if (attachments.length === 0)
173
+ return;
174
+ const existing = await em.find(InvoiceExternalAttachment, { invoiceId: invoice.id });
175
+ const byExternalId = new Map(existing.map((row) => [row.externalAttachmentId, row]));
176
+ for (const attachment of attachments) {
177
+ const row = byExternalId.get(attachment.externalAttachmentId);
178
+ if (row) {
179
+ row.fileName = attachment.fileName;
180
+ row.contentType = attachment.contentType ?? null;
181
+ continue;
182
+ }
183
+ em.create(InvoiceExternalAttachment, {
184
+ invoiceId: invoice.id,
185
+ externalAttachmentId: attachment.externalAttachmentId,
186
+ fileName: attachment.fileName,
187
+ contentType: attachment.contentType ?? null,
188
+ });
189
+ }
190
+ }
191
+ }
192
+ //# sourceMappingURL=erp-sale-document-write-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erp-sale-document-write-port.js","sourceRoot":"","sources":["../../../src/backend/services/erp-sale-document-write-port.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import type { InvoiceAttachmentFetchPort, InvoiceAttachmentFetchRegistration, InvoiceAttachmentFetchRegistryPort } from '@endora-commerce/contracts';
2
+ /**
3
+ * The invoice-owned check that the attachment belongs to the calling
4
+ * organization, answering the imported document's source system, or `null`.
5
+ * `ErpSaleDocumentWritePortService.resolveAttachmentContext` in production.
6
+ */
7
+ export type InvoiceAttachmentFetchContextResolver = (input: {
8
+ invoiceId: string;
9
+ attachmentId: string;
10
+ organizationId: string;
11
+ }) => Promise<{
12
+ system: string;
13
+ } | null>;
14
+ /** Two different providers for one source system. */
15
+ export declare class InvoiceAttachmentFetchConflictError extends Error {
16
+ constructor(system: string, existingModuleId: string, moduleId: string);
17
+ }
18
+ /**
19
+ * The first-download seam for ERP-imported attachments (feature 134, T061;
20
+ * `specs/134-paid-module-extraction/research.md` D12).
21
+ *
22
+ * `invoices` owns the customer route, the ownership check and what a first
23
+ * download does. What it does not own is the remote system the bytes live in,
24
+ * so a connector contributes a provider for the source system it imports from.
25
+ * Before this seam the route resolved one connector's port by name and this
26
+ * module's manifest carried an edge onto that connector.
27
+ *
28
+ * **A contribution registry in D-39's sense.** It is an ungated registration,
29
+ * because a connector pushes into it from a boot hook whatever this module's
30
+ * effective state is; the question of presence is answered here, on every
31
+ * download, keyed on the module recorded with each provider.
32
+ *
33
+ * **The policy it states: skip an absent contributor's provider.** A connector
34
+ * that is switched off must not reach its ERP with the operator's credentials,
35
+ * so its provider answers as if it were not registered: `null`, which the route
36
+ * turns into the 404 it already gave an attachment that could not be fetched.
37
+ * Nothing is lost with the skip — a file fetched earlier is stored in
38
+ * `assets_library` and the route serves it without asking any provider — and
39
+ * switching the connector back on takes effect on the next download with no
40
+ * restart, because nothing here is captured at composition.
41
+ *
42
+ * Three more rules, each stated because the alternative is plausible:
43
+ *
44
+ * 1. **Ownership is checked before any provider runs.** The context resolver
45
+ * is this module's own query, scoped to the calling organization, so a
46
+ * connector is never asked to fetch a foreign organization's file.
47
+ * 2. **There is no fallback.** A document is answered by the provider of its
48
+ * own source system or by nobody; another connector's provider is never
49
+ * tried, whatever is present.
50
+ * 3. **A second provider for one system is refused**, not ordered. Which
51
+ * provider serves a system must not depend on which module composed last.
52
+ * Registering the identical provider twice is accepted.
53
+ */
54
+ export declare class InvoiceAttachmentFetchRegistry implements InvoiceAttachmentFetchRegistryPort, InvoiceAttachmentFetchPort {
55
+ private readonly resolveContext;
56
+ private readonly isModulePresent;
57
+ private readonly registrations;
58
+ /**
59
+ * @param resolveContext the invoice-owned ownership check.
60
+ * @param isModulePresent the effective-state probe. Defaults to
61
+ * always-present, so a registry a unit test builds keeps answering about
62
+ * the providers that test registered; the module's composition wires it to
63
+ * the kernel's effective state.
64
+ */
65
+ constructor(resolveContext: InvoiceAttachmentFetchContextResolver, isModulePresent?: (moduleId: string) => boolean);
66
+ register(registration: InvoiceAttachmentFetchRegistration): void;
67
+ ensureAttachmentBytes(input: {
68
+ invoiceId: string;
69
+ attachmentId: string;
70
+ organizationId: string;
71
+ }): Promise<{
72
+ assetId: string;
73
+ } | null>;
74
+ }
75
+ //# sourceMappingURL=invoice-attachment-fetch-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoice-attachment-fetch-registry.d.ts","sourceRoot":"","sources":["../../../src/backend/services/invoice-attachment-fetch-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,kCAAkC,EAClC,kCAAkC,EACnC,MAAM,4BAA4B,CAAC;AAEpC;;;;GAIG;AACH,MAAM,MAAM,qCAAqC,GAAG,CAAC,KAAK,EAAE;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB,KAAK,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAAC;AAEzC,qDAAqD;AACrD,qBAAa,mCAAoC,SAAQ,KAAK;gBAChD,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAOvE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,qBAAa,8BACX,YAAW,kCAAkC,EAAE,0BAA0B;IAYvE,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAXlC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyD;IAEvF;;;;;;OAMG;gBAEgB,cAAc,EAAE,qCAAqC,EACrD,eAAe,GAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAoB;IAG9E,QAAQ,CAAC,YAAY,EAAE,kCAAkC,GAAG,IAAI;IAkB1D,qBAAqB,CAAC,KAAK,EAAE;QACjC,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CASxC"}
@@ -0,0 +1,81 @@
1
+ /** Two different providers for one source system. */
2
+ export class InvoiceAttachmentFetchConflictError extends Error {
3
+ constructor(system, existingModuleId, moduleId) {
4
+ super(`invoiceAttachmentFetchRegistry: source system "${system}" already has a fetch provider ` +
5
+ `from module "${existingModuleId}"; module "${moduleId}" cannot register a second one.`);
6
+ this.name = 'InvoiceAttachmentFetchConflictError';
7
+ }
8
+ }
9
+ /**
10
+ * The first-download seam for ERP-imported attachments (feature 134, T061;
11
+ * `specs/134-paid-module-extraction/research.md` D12).
12
+ *
13
+ * `invoices` owns the customer route, the ownership check and what a first
14
+ * download does. What it does not own is the remote system the bytes live in,
15
+ * so a connector contributes a provider for the source system it imports from.
16
+ * Before this seam the route resolved one connector's port by name and this
17
+ * module's manifest carried an edge onto that connector.
18
+ *
19
+ * **A contribution registry in D-39's sense.** It is an ungated registration,
20
+ * because a connector pushes into it from a boot hook whatever this module's
21
+ * effective state is; the question of presence is answered here, on every
22
+ * download, keyed on the module recorded with each provider.
23
+ *
24
+ * **The policy it states: skip an absent contributor's provider.** A connector
25
+ * that is switched off must not reach its ERP with the operator's credentials,
26
+ * so its provider answers as if it were not registered: `null`, which the route
27
+ * turns into the 404 it already gave an attachment that could not be fetched.
28
+ * Nothing is lost with the skip — a file fetched earlier is stored in
29
+ * `assets_library` and the route serves it without asking any provider — and
30
+ * switching the connector back on takes effect on the next download with no
31
+ * restart, because nothing here is captured at composition.
32
+ *
33
+ * Three more rules, each stated because the alternative is plausible:
34
+ *
35
+ * 1. **Ownership is checked before any provider runs.** The context resolver
36
+ * is this module's own query, scoped to the calling organization, so a
37
+ * connector is never asked to fetch a foreign organization's file.
38
+ * 2. **There is no fallback.** A document is answered by the provider of its
39
+ * own source system or by nobody; another connector's provider is never
40
+ * tried, whatever is present.
41
+ * 3. **A second provider for one system is refused**, not ordered. Which
42
+ * provider serves a system must not depend on which module composed last.
43
+ * Registering the identical provider twice is accepted.
44
+ */
45
+ export class InvoiceAttachmentFetchRegistry {
46
+ resolveContext;
47
+ isModulePresent;
48
+ registrations = new Map();
49
+ /**
50
+ * @param resolveContext the invoice-owned ownership check.
51
+ * @param isModulePresent the effective-state probe. Defaults to
52
+ * always-present, so a registry a unit test builds keeps answering about
53
+ * the providers that test registered; the module's composition wires it to
54
+ * the kernel's effective state.
55
+ */
56
+ constructor(resolveContext, isModulePresent = () => true) {
57
+ this.resolveContext = resolveContext;
58
+ this.isModulePresent = isModulePresent;
59
+ }
60
+ register(registration) {
61
+ const existing = this.registrations.get(registration.system);
62
+ if (existing) {
63
+ if (existing.moduleId === registration.moduleId &&
64
+ existing.provider === registration.provider) {
65
+ return;
66
+ }
67
+ throw new InvoiceAttachmentFetchConflictError(registration.system, existing.moduleId, registration.moduleId);
68
+ }
69
+ this.registrations.set(registration.system, registration);
70
+ }
71
+ async ensureAttachmentBytes(input) {
72
+ const context = await this.resolveContext(input);
73
+ if (!context)
74
+ return null;
75
+ const registration = this.registrations.get(context.system);
76
+ if (!registration || !this.isModulePresent(registration.moduleId))
77
+ return null;
78
+ return registration.provider.ensureAttachmentBytes(input);
79
+ }
80
+ }
81
+ //# sourceMappingURL=invoice-attachment-fetch-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoice-attachment-fetch-registry.js","sourceRoot":"","sources":["../../../src/backend/services/invoice-attachment-fetch-registry.ts"],"names":[],"mappings":"AAiBA,qDAAqD;AACrD,MAAM,OAAO,mCAAoC,SAAQ,KAAK;IAC5D,YAAY,MAAc,EAAE,gBAAwB,EAAE,QAAgB;QACpE,KAAK,CACH,kDAAkD,MAAM,iCAAiC;YACvF,gBAAgB,gBAAgB,cAAc,QAAQ,iCAAiC,CAC1F,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,qCAAqC,CAAC;IACpD,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,OAAO,8BAA8B;IAatB;IACA;IAXF,aAAa,GAAG,IAAI,GAAG,EAA8C,CAAC;IAEvF;;;;;;OAMG;IACH,YACmB,cAAqD,EACrD,kBAAiD,GAAG,EAAE,CAAC,IAAI;QAD3D,mBAAc,GAAd,cAAc,CAAuC;QACrD,oBAAe,GAAf,eAAe,CAA4C;IAC3E,CAAC;IAEJ,QAAQ,CAAC,YAAgD;QACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC7D,IAAI,QAAQ,EAAE,CAAC;YACb,IACE,QAAQ,CAAC,QAAQ,KAAK,YAAY,CAAC,QAAQ;gBAC3C,QAAQ,CAAC,QAAQ,KAAK,YAAY,CAAC,QAAQ,EAC3C,CAAC;gBACD,OAAO;YACT,CAAC;YACD,MAAM,IAAI,mCAAmC,CAC3C,YAAY,CAAC,MAAM,EACnB,QAAQ,CAAC,QAAQ,EACjB,YAAY,CAAC,QAAQ,CACtB,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,KAI3B;QACC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACjD,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAE1B,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC5D,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAE/E,OAAO,YAAY,CAAC,QAAQ,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC5D,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { InvoiceCopyHostPort, InvoiceCopyRecord, OrderReadPort } from '@endora-commerce/contracts';
3
+ export declare class InvoiceCopyHostService implements InvoiceCopyHostPort {
4
+ private readonly emFactory;
5
+ private readonly orders;
6
+ constructor(emFactory: () => EntityManager, orders: OrderReadPort);
7
+ getById(invoiceId: string): Promise<InvoiceCopyRecord | null>;
8
+ }
9
+ //# sourceMappingURL=invoice-copy-host.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoice-copy-host.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/invoice-copy-host.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAEV,mBAAmB,EACnB,iBAAiB,EAEjB,aAAa,EACd,MAAM,4BAA4B,CAAC;AAcpC,qBAAa,sBAAuB,YAAW,mBAAmB;IAE9D,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,SAAS,EAAE,MAAM,aAAa,EAC9B,MAAM,EAAE,aAAa;IAGlC,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;CAmCpE"}
@@ -0,0 +1,52 @@
1
+ import { Invoice } from '../entities/invoice.entity.js';
2
+ import { InvoiceLine as InvoiceLineEntity } from '../entities/invoice-line.entity.js';
3
+ const EMPTY_BUYER = {
4
+ name: '',
5
+ taxId: '',
6
+ addressLine1: '',
7
+ addressLine2: '',
8
+ postalCode: '',
9
+ city: '',
10
+ country: '',
11
+ };
12
+ export class InvoiceCopyHostService {
13
+ emFactory;
14
+ orders;
15
+ constructor(emFactory, orders) {
16
+ this.emFactory = emFactory;
17
+ this.orders = orders;
18
+ }
19
+ async getById(invoiceId) {
20
+ const em = this.emFactory();
21
+ const invoice = await em.findOne(Invoice, { id: invoiceId });
22
+ if (!invoice)
23
+ return null;
24
+ const order = invoice.orderId ? await this.orders.findById(invoice.orderId) : null;
25
+ const lineRows = await em.find(InvoiceLineEntity, { invoiceId }, { orderBy: { ordinal: 'asc' } });
26
+ const lines = lineRows.map((line) => ({
27
+ ordinal: line.ordinal,
28
+ name: line.name,
29
+ unit: line.unit,
30
+ quantity: Number(line.quantity),
31
+ unitNetPrice: Number(line.unitNetPrice),
32
+ taxRate: Number(line.taxRate),
33
+ netValue: Number(line.netValue),
34
+ grossValue: Number(line.grossValue),
35
+ }));
36
+ return {
37
+ invoiceId: invoice.id,
38
+ organizationId: order?.organizationId ?? invoice.organizationId ?? '',
39
+ number: invoice.number,
40
+ kind: invoice.kind,
41
+ salesChannelId: invoice.salesChannelId ?? null,
42
+ currency: invoice.currency,
43
+ saleDate: invoice.saleDate ?? null,
44
+ paymentDueDate: invoice.paymentDueDate ?? null,
45
+ paymentMethod: invoice.paymentMethod ?? null,
46
+ originalInvoiceId: invoice.originalInvoiceId ?? null,
47
+ buyer: invoice.buyerSnapshot ?? EMPTY_BUYER,
48
+ lines,
49
+ };
50
+ }
51
+ }
52
+ //# sourceMappingURL=invoice-copy-host.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoice-copy-host.service.js","sourceRoot":"","sources":["../../../src/backend/services/invoice-copy-host.service.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,WAAW,IAAI,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAEtF,MAAM,WAAW,GAAiB;IAChC,IAAI,EAAE,EAAE;IACR,KAAK,EAAE,EAAE;IACT,YAAY,EAAE,EAAE;IAChB,YAAY,EAAE,EAAE;IAChB,UAAU,EAAE,EAAE;IACd,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,EAAE;CACZ,CAAC;AAEF,MAAM,OAAO,sBAAsB;IAEd;IACA;IAFnB,YACmB,SAA8B,EAC9B,MAAqB;QADrB,cAAS,GAAT,SAAS,CAAqB;QAC9B,WAAM,GAAN,MAAM,CAAe;IACrC,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,SAAiB;QAC7B,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QAC7D,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACnF,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,IAAI,CAC5B,iBAAiB,EACjB,EAAE,SAAS,EAAE,EACb,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAChC,CAAC;QACF,MAAM,KAAK,GAAkB,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACnD,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC/B,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC;YACvC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;YAC7B,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC/B,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;SACpC,CAAC,CAAC,CAAC;QACJ,OAAO;YACL,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,cAAc,EAAE,KAAK,EAAE,cAAc,IAAI,OAAO,CAAC,cAAc,IAAI,EAAE;YACrE,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,IAAI;YAC9C,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAI;YAClC,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,IAAI;YAC9C,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,IAAI;YAC5C,iBAAiB,EAAE,OAAO,CAAC,iBAAiB,IAAI,IAAI;YACpD,KAAK,EAAE,OAAO,CAAC,aAAa,IAAI,WAAW;YAC3C,KAAK;SACN,CAAC;IACJ,CAAC;CACF"}