@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Endora sp. z o.o. and the Endora Commerce contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,60 @@
1
- # Temporary Holding Version
1
+ # Invoices module (feature 047)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Generates, numbers, renders, corrects, and emails invoices for orders.
4
+
5
+ ## What works today
6
+
7
+ - **Issuance** — manual (`POST /api/v1/admin/orders/:orderId/invoices`) and
8
+ automatic when an order reaches the configured trigger status
9
+ (`invoices.auto_issue.trigger_status`, per channel). Idempotent per
10
+ `(order, kind)`; blocked with 422 when seller settings are missing.
11
+ - **Numbering** — gap-free, row-locked counter per `(sales_channel, kind, year)`
12
+ with a configurable pattern (`invoices.numbering.<kind>.pattern`) supporting
13
+ `{seq}`, `{seq:N}`, `{YYYY}`, `{YY}`, `{MM}`. Resets yearly.
14
+ - **PDF** — pdfmake renderer with a built-in generic layout modelled on the
15
+ reference invoice (seller w/ NIP + bank, buyer, line table, per-rate VAT
16
+ summary, totals, amount-in-words, KSeF area). Rendered on demand. KSeF is
17
+ **layout-ready only** — no live integration.
18
+ - **Corrections** — the returns `CorrectiveInvoicePort` draws correction-sequence
19
+ numbers (when an original invoice exists), references the original, snapshots
20
+ corrected lines, and caps the credited total at the original gross.
21
+ - **Email** — `invoice_issued` transactional-email definition (admin-editable via
22
+ the `transactional_emails` module); sent on issue (per channel) or on demand,
23
+ with the PDF attached (`attachment` mode) or a storefront download link
24
+ (`link` mode). Idempotent, best-effort.
25
+ - **Customer access** — `GET /api/v1/orders/:id/invoices` and `.../:invoiceId/pdf`
26
+ (ownership-guarded); surfaced on the storefront order page.
27
+ - **WYSIWYG templates (US6)** — a seeded global generic template + optional
28
+ per-channel templates, authored in a dedicated Puck editor in the admin
29
+ (`/invoices/templates`). The PDF renderer maps the template's Puck tree to
30
+ pdfmake sections (`InvoiceHeader`, `InvoiceParties`, `InvoiceLineItems`,
31
+ `InvoiceVatSummary`, `InvoiceTotals`, `InvoiceNotes`, `InvoiceKsef`), falling
32
+ back to the built-in layout when no valid template applies (FR-016). Endpoints
33
+ under `/api/v1/admin/invoice-templates` (list/get/create, `PUT content/:lang`,
34
+ `GET .../preview`, page-builder config).
35
+ - **Settings** — seller VAT/NIP + company data (JSON), numbering patterns,
36
+ auto-issue trigger, email toggle + delivery mode, storefront base URL — all
37
+ global + per-channel.
38
+
39
+ ## Settings
40
+
41
+ `invoices.seller.tax_id`, `invoices.seller.company_data`,
42
+ `invoices.auto_issue.trigger_status`, `invoices.email.send_on_issue`,
43
+ `invoices.email.delivery_mode`, `invoices.numbering.{invoice,proforma,correction}.pattern`,
44
+ `invoices.storefront_base_url`.
45
+
46
+ ## Permissions
47
+
48
+ `invoices:read`, `invoices:write`.
49
+
50
+ ## Deferred / follow-ups
51
+
52
+ - The invoice Puck config lives in the **admin module** (not
53
+ `packages/cms-components`) — invoice authoring is admin-only and the real
54
+ layout is server-side pdfmake; this avoids coupling invoice components into the
55
+ shared CMS/email component package.
56
+ - Audit-log entries are written on issue (`invoice.issued`) and correction
57
+ (`invoice.corrected`).
58
+ - PDF is rendered on demand; caching into `assets_library` (`pdf_asset_id`) is a
59
+ future optimization.
60
+ - A dedicated admin invoice detail page (management actions live on the list).
@@ -0,0 +1,22 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * The switch between invoices and the templates they are rendered with.
4
+ *
5
+ * A template is not a destination an operator sets out for; it is the thing
6
+ * they go and adjust when an invoice came out wrong. Giving it a sidebar row of
7
+ * its own put a rarely-used setting at the same level as the documents
8
+ * themselves, and pushed everything below it one line further down.
9
+ *
10
+ * Both tabs belong to `invoices`, so the whole strip is the module's
11
+ * contribution and goes with it (feature 073 / FR-031).
12
+ *
13
+ * **It lives here since feature 091's P4c**, and until then it did not. The
14
+ * `admin-surface` ledger recorded it as a host component that *"renders on two
15
+ * modules' pages and belongs to neither"*, retiring with P4 as a two-contributor
16
+ * zone — the sentence `OrderEntryTabs` earns and this one never did. Measured:
17
+ * both tabs are `/invoices*`, the presence gate asks about `invoices`, and both
18
+ * consumers are `invoices`' own screens. There was nothing here for a mechanism
19
+ * to do; it was a file in the wrong directory.
20
+ */
21
+ export declare function InvoiceSectionTabs(): ReactNode;
22
+ //# sourceMappingURL=InvoiceSectionTabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InvoiceSectionTabs.d.ts","sourceRoot":"","sources":["../../../src/admin/components/InvoiceSectionTabs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,IAAI,SAAS,CAa9C"}
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { RouteTabs } from '@endora-commerce/admin-kit/ui';
3
+ import { useTranslation } from '@endora-commerce/admin-kit/i18n';
4
+ import { useModulePresence } from '@endora-commerce/admin-kit/lib';
5
+ /**
6
+ * The switch between invoices and the templates they are rendered with.
7
+ *
8
+ * A template is not a destination an operator sets out for; it is the thing
9
+ * they go and adjust when an invoice came out wrong. Giving it a sidebar row of
10
+ * its own put a rarely-used setting at the same level as the documents
11
+ * themselves, and pushed everything below it one line further down.
12
+ *
13
+ * Both tabs belong to `invoices`, so the whole strip is the module's
14
+ * contribution and goes with it (feature 073 / FR-031).
15
+ *
16
+ * **It lives here since feature 091's P4c**, and until then it did not. The
17
+ * `admin-surface` ledger recorded it as a host component that *"renders on two
18
+ * modules' pages and belongs to neither"*, retiring with P4 as a two-contributor
19
+ * zone — the sentence `OrderEntryTabs` earns and this one never did. Measured:
20
+ * both tabs are `/invoices*`, the presence gate asks about `invoices`, and both
21
+ * consumers are `invoices`' own screens. There was nothing here for a mechanism
22
+ * to do; it was a file in the wrong directory.
23
+ */
24
+ export function InvoiceSectionTabs() {
25
+ const t = useTranslation('core');
26
+ const { isPresent } = useModulePresence();
27
+ if (!isPresent('invoices'))
28
+ return null;
29
+ return (_jsx(RouteTabs, { className: "mb-4", tabs: [
30
+ { to: '/invoices', label: t('invoiceTabs.invoices') },
31
+ { to: '/invoices/templates', label: t('invoiceTabs.templates') },
32
+ ] }));
33
+ }
34
+ //# sourceMappingURL=InvoiceSectionTabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InvoiceSectionTabs.js","sourceRoot":"","sources":["../../../src/admin/components/InvoiceSectionTabs.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,EAAE,SAAS,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAC1C,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,CACL,KAAC,SAAS,IACR,SAAS,EAAC,MAAM,EAChB,IAAI,EAAE;YACJ,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC,sBAAsB,CAAC,EAAE;YACrD,EAAE,EAAE,EAAE,qBAAqB,EAAE,KAAK,EAAE,CAAC,CAAC,uBAAuB,CAAC,EAAE;SACjE,GACD,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * `invoices`' admin surface — four routes and one sidebar entry, declared by
3
+ * the module that owns them (feature 091, Phase 4, batch 12;
4
+ * `specs/091-module-owned-admin-surfaces/contracts/admin-contribution.md`).
5
+ *
6
+ * **This module is a zone *host* as well as a contributor of screens**, which
7
+ * makes it the batch's real content. `InvoiceDetail` used to import
8
+ * `InvoiceKsefPanel` from `@/modules/ksef/components/` — the single key in
9
+ * `backend/scripts/ledgers/cross-module-imports/invoices.ts` — and that entry
10
+ * says in its own words that neither moving the file nor rewriting the
11
+ * specifier as `ksef`'s package subpath retires it, because both leave the same
12
+ * coupling under a supported name. What retires it is this module publishing a
13
+ * **place**: `<AdminZone name="invoice.detail.after" …>` in
14
+ * `pages/InvoiceDetail.tsx`, with `ksef` declaring the contribution. The member,
15
+ * the host render and the contribution land in this one merge request, because
16
+ * `check:admin-zones` refuses a member no host renders and there is no
17
+ * intermediate state that is green.
18
+ *
19
+ * **The templates screens keep no sidebar row**, which is not an omission: the
20
+ * host table's own comment records that a template is reached through
21
+ * `InvoiceSectionTabs` — the strip this module already owns since P4c — rather
22
+ * than from the sidebar. Both template routes are declared here all the same,
23
+ * because a route is what `App.tsx` used to hold and a tab is not a route.
24
+ *
25
+ * **Both palette actions were already declared** (`open-invoices`,
26
+ * `invoice-templates`) and are untouched; this module had no hand-written
27
+ * `PALETTE_ITEMS` row for the batch to delete.
28
+ *
29
+ * **This entry exports data and nothing else** (R2); every component is a
30
+ * dynamic-import factory (R6).
31
+ */
32
+ import type { AdminContributions } from '@endora-commerce/admin-kit/contributions';
33
+ export declare const contributions: AdminContributions;
34
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAqBnF,eAAO,MAAM,aAAa,EAAE,kBAmD3B,CAAC"}
@@ -0,0 +1,69 @@
1
+ /** The module's landing route: the issued-invoice list. */
2
+ const ROUTE_PATH = '/invoices';
3
+ const TEMPLATES_ROUTE = `${ROUTE_PATH}/templates`;
4
+ /**
5
+ * The one code every screen below needs to open.
6
+ *
7
+ * Read from the routes the four screens call on mount — the invoice list, one
8
+ * invoice, the template list and one template are all
9
+ * `requireAdmin('invoices:read')` in
10
+ * `packages/modules/invoices/src/backend/routes.admin.ts`. Issuing, correcting,
11
+ * sending and saving a template enforce `invoices:write` on the API, and each
12
+ * screen gates its own controls on that code through
13
+ * `useAuth().hasPermission`; the read code is what opens the screen (issue
14
+ * #232's rule, and the code the hand-written sidebar row carried).
15
+ */
16
+ const READ_PERMISSION = 'invoices:read';
17
+ export const contributions = {
18
+ routes: [
19
+ {
20
+ path: ROUTE_PATH,
21
+ component: () => import('./pages/InvoicesList.js'),
22
+ requiredPermission: READ_PERMISSION,
23
+ index: true,
24
+ },
25
+ {
26
+ path: TEMPLATES_ROUTE,
27
+ component: () => import('./pages/InvoiceTemplatesPage.js'),
28
+ requiredPermission: READ_PERMISSION,
29
+ },
30
+ {
31
+ path: `${TEMPLATES_ROUTE}/:id`,
32
+ component: () => import('./pages/InvoiceTemplateEditor.js'),
33
+ requiredPermission: READ_PERMISSION,
34
+ },
35
+ {
36
+ // Declared last, and `App.tsx` declared it last too: `/invoices/:id`
37
+ // matches `/invoices/templates` as well. `<Routes>` ranks by specificity
38
+ // rather than by declaration order, so the static segment wins whatever
39
+ // the order — the ordering is kept anyway, so a reader does not have to
40
+ // know that to see why the detail route is safe here.
41
+ path: `${ROUTE_PATH}/:id`,
42
+ component: () => import('./pages/InvoiceDetail.js'),
43
+ requiredPermission: READ_PERMISSION,
44
+ },
45
+ ],
46
+ nav: [
47
+ {
48
+ to: ROUTE_PATH,
49
+ // Module-relative (R8), resolved in this module's own namespace out of
50
+ // `packages/modules/invoices/i18n/`. It was `appShell.nav.invoices` in
51
+ // the shared `_i18n` bundle, one of the four shared files a module author
52
+ // had to edit.
53
+ labelKey: 'nav.invoices.label',
54
+ // The glyph `AppShell.tsx` rendered by hand (`Receipt`), and the one
55
+ // this module's `open-invoices` action already names is `FileText`. The
56
+ // sidebar's is kept: this row is the sidebar's row.
57
+ icon: 'Receipt',
58
+ section: 'sales',
59
+ // The hand-written position times a hundred (batch four's convention).
60
+ // *Sales* keeps its three `orders` rows, so a registry entry appends
61
+ // after them and these weights order the three module rows this batch
62
+ // contributes among themselves: `quote_requests` 400, this one 500,
63
+ // `ksef` 600 — the order the hand-written table had.
64
+ weight: 500,
65
+ requiredPermission: READ_PERMISSION,
66
+ },
67
+ ],
68
+ };
69
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAiCA,2DAA2D;AAC3D,MAAM,UAAU,GAAG,WAAW,CAAC;AAE/B,MAAM,eAAe,GAAG,GAAG,UAAU,YAAY,CAAC;AAElD;;;;;;;;;;;GAWG;AACH,MAAM,eAAe,GAAG,eAAe,CAAC;AAExC,MAAM,CAAC,MAAM,aAAa,GAAuB;IAC/C,MAAM,EAAE;QACN;YACE,IAAI,EAAE,UAAU;YAChB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,yBAAyB,CAAC;YAClD,kBAAkB,EAAE,eAAe;YACnC,KAAK,EAAE,IAAI;SACZ;QACD;YACE,IAAI,EAAE,eAAe;YACrB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,iCAAiC,CAAC;YAC1D,kBAAkB,EAAE,eAAe;SACpC;QACD;YACE,IAAI,EAAE,GAAG,eAAe,MAAM;YAC9B,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,kCAAkC,CAAC;YAC3D,kBAAkB,EAAE,eAAe;SACpC;QACD;YACE,qEAAqE;YACrE,yEAAyE;YACzE,wEAAwE;YACxE,wEAAwE;YACxE,sDAAsD;YACtD,IAAI,EAAE,GAAG,UAAU,MAAM;YACzB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,0BAA0B,CAAC;YACnD,kBAAkB,EAAE,eAAe;SACpC;KACF;IACD,GAAG,EAAE;QACH;YACE,EAAE,EAAE,UAAU;YACd,uEAAuE;YACvE,uEAAuE;YACvE,0EAA0E;YAC1E,eAAe;YACf,QAAQ,EAAE,oBAAoB;YAC9B,qEAAqE;YACrE,wEAAwE;YACxE,oDAAoD;YACpD,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,OAAO;YAChB,uEAAuE;YACvE,qEAAqE;YACrE,sEAAsE;YACtE,oEAAoE;YACpE,qDAAqD;YACrD,MAAM,EAAE,GAAG;YACX,kBAAkB,EAAE,eAAe;SACpC;KACF;CACF,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { type ReactNode } from 'react';
2
+ /** Single-invoice detail view: snapshot data, line items, VAT summary, PDF. */
3
+ export declare function InvoiceDetail(): ReactNode;
4
+ /**
5
+ * The registry loads a route component through a dynamic-import factory and
6
+ * reads its default export (feature 091, FR-013). The named export stays: it is
7
+ * the spelling this module's own code and its tests use.
8
+ */
9
+ export default InvoiceDetail;
10
+ //# sourceMappingURL=InvoiceDetail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InvoiceDetail.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/InvoiceDetail.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAkBzE,+EAA+E;AAC/E,wBAAgB,aAAa,IAAI,SAAS,CAiWzC;AAWD;;;;GAIG;AACH,eAAe,aAAa,CAAC"}
@@ -0,0 +1,111 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { Link, useParams } from 'react-router-dom';
4
+ import { FileDown, Mail, RefreshCw } from 'lucide-react';
5
+ import { apiBaseUrl, apiClient, ApiError, formatDateTime, formatMoney, sendInvoiceEmailMessage, useAuth } from '@endora-commerce/admin-kit/lib';
6
+ import { Alert, AlertDescription, Badge, Button, Card, CardContent, CardHeader, CardTitle, PageHeader, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@endora-commerce/admin-kit/ui';
7
+ import { useTranslation } from '@endora-commerce/admin-kit/i18n';
8
+ import { AdminZone } from '@endora-commerce/admin-kit/zones';
9
+ const STATUS_VARIANT = {
10
+ pending: 'warning',
11
+ ready: 'success',
12
+ cancelled: 'destructive',
13
+ };
14
+ /** Single-invoice detail view: snapshot data, line items, VAT summary, PDF. */
15
+ export function InvoiceDetail() {
16
+ const t = useTranslation('core');
17
+ const { id = '' } = useParams();
18
+ const { hasPermission } = useAuth();
19
+ const canWrite = hasPermission('invoices:write');
20
+ const [invoice, setInvoice] = useState(null);
21
+ const [loading, setLoading] = useState(true);
22
+ const [error, setError] = useState(null);
23
+ const [notice, setNotice] = useState(null);
24
+ const [busy, setBusy] = useState(false);
25
+ /**
26
+ * `apiBaseUrl` and not `import.meta.env`: this file compiles under `tsc`
27
+ * inside its own package, where Vite's client types are not in scope, and the
28
+ * kit publishes the resolved value for exactly this (feature 091). The
29
+ * fallback moves with it — this read defaulted to `''` while the `apiClient`
30
+ * beside it has always defaulted to `http://localhost:3001`, so the download
31
+ * href now points where the fetch that lists it already went.
32
+ */
33
+ const baseUrl = apiBaseUrl;
34
+ const refresh = useCallback(async () => {
35
+ setLoading(true);
36
+ setError(null);
37
+ try {
38
+ const res = await apiClient.get(`/api/v1/admin/invoices/${id}`);
39
+ setInvoice(res.data);
40
+ }
41
+ catch (err) {
42
+ if (err instanceof ApiError && err.status === 404) {
43
+ setInvoice(null);
44
+ }
45
+ else {
46
+ setError(err instanceof ApiError ? err.envelope.error.message : t('invoiceDetail.error.load'));
47
+ }
48
+ }
49
+ finally {
50
+ setLoading(false);
51
+ }
52
+ }, [id, t]);
53
+ useEffect(() => {
54
+ void refresh();
55
+ }, [refresh]);
56
+ const regenerate = useCallback(async () => {
57
+ setBusy(true);
58
+ setError(null);
59
+ try {
60
+ await apiClient.post(`/api/v1/admin/invoices/${id}/regenerate-pdf`, {});
61
+ setNotice(t('invoiceDetail.regenerated'));
62
+ await refresh();
63
+ }
64
+ catch (err) {
65
+ setError(err instanceof ApiError ? err.envelope.error.message : t('invoiceDetail.regenerateError'));
66
+ }
67
+ finally {
68
+ setBusy(false);
69
+ }
70
+ }, [id, refresh, t]);
71
+ const resendEmail = useCallback(async () => {
72
+ setBusy(true);
73
+ setError(null);
74
+ try {
75
+ const res = await apiClient.post(`/api/v1/admin/invoices/${id}/send-email`, {});
76
+ // Issue #149 — the same answer the list surface now reads: a send that
77
+ // was suppressed says so instead of being confirmed.
78
+ const outcome = sendInvoiceEmailMessage(res.data, t);
79
+ if (outcome.ok)
80
+ setNotice(outcome.message);
81
+ else
82
+ setError(outcome.message);
83
+ }
84
+ catch (err) {
85
+ setError(err instanceof ApiError ? err.envelope.error.message : t('invoiceDetail.error.load'));
86
+ }
87
+ finally {
88
+ setBusy(false);
89
+ }
90
+ }, [id, t]);
91
+ if (loading)
92
+ return _jsx("p", { className: "text-sm text-muted-foreground", children: t('common.state.loading') });
93
+ if (!invoice)
94
+ return (_jsx(Alert, { variant: "warning", children: _jsx(AlertDescription, { children: t('invoiceDetail.notFound') }) }));
95
+ const money = (n) => formatMoney(n, invoice.currency);
96
+ return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { back: { label: t('invoiceDetail.back'), to: '/invoices' }, title: _jsxs(_Fragment, { children: [t('invoiceDetail.title', { number: invoice.number }), _jsx(Badge, { className: "text-xs font-medium", variant: STATUS_VARIANT[invoice.status] ?? 'secondary', children: t(`invoices.status.${invoice.status}`) })] }), actions: _jsxs(_Fragment, { children: [invoice.status === 'ready' ? (_jsx(Button, { asChild: true, variant: "outline", size: "sm", className: "bg-card", children: _jsxs("a", { href: `${baseUrl}/api/v1/admin/invoices/${invoice.id}/pdf`, target: "_blank", rel: "noreferrer", children: [_jsx(FileDown, {}), t('invoiceDetail.action.downloadPdf')] }) })) : null, canWrite ? (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "outline", size: "sm", className: "bg-card", disabled: busy, onClick: () => void regenerate(), children: [_jsx(RefreshCw, {}), t('invoiceDetail.action.regeneratePdf')] }), _jsxs(Button, { variant: "outline", size: "sm", className: "bg-card", disabled: busy, onClick: () => void resendEmail(), children: [_jsx(Mail, {}), t('invoiceDetail.action.resendEmail')] })] })) : null] }) }), error ? (_jsx(Alert, { variant: "destructive", className: "mb-4", children: _jsx(AlertDescription, { children: error }) })) : null, notice ? (_jsx(Alert, { variant: "success", className: "mb-4", children: _jsx(AlertDescription, { children: notice }) })) : null, invoice.status !== 'ready' ? (_jsx(Alert, { variant: "warning", className: "mb-4", children: _jsx(AlertDescription, { children: t('invoiceDetail.pdfNotReady') }) })) : null, _jsxs("div", { className: "grid gap-4 md:grid-cols-3", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: t('invoiceDetail.section.details') }) }), _jsxs(CardContent, { className: "space-y-1 text-sm", children: [_jsx(Row, { label: t('invoiceDetail.field.kind'), value: t(`invoices.kind.${invoice.kind}`) }), _jsx(Row, { label: t('invoiceDetail.field.issuedAt'), value: formatDateTime(invoice.issuedAt) }), _jsx(Row, { label: t('invoiceDetail.field.saleDate'), value: invoice.saleDate ?? '—' }), _jsx(Row, { label: t('invoiceDetail.field.paymentDueDate'), value: invoice.paymentDueDate ?? '—' }), _jsx(Row, { label: t('invoiceDetail.field.paymentMethod'), value: invoice.paymentMethod ?? '—' }), invoice.ksefReferenceNumber ? (_jsx(Row, { label: t('invoiceDetail.field.ksefNumber'), value: _jsx("span", { className: "font-mono text-xs", children: invoice.ksefReferenceNumber }) })) : null, _jsx(Row, { label: t('invoiceDetail.field.order'), value: invoice.orderId ? (_jsx(Link, { to: `/orders/${invoice.orderId}`, className: "font-mono text-xs underline underline-offset-2", children: invoice.orderBusinessId ?? invoice.orderId.slice(0, 8) })) : ('—') })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: t('invoiceDetail.section.seller') }) }), _jsxs(CardContent, { className: "space-y-0.5 text-sm", children: [_jsx("p", { className: "font-medium", children: invoice.seller.legalName }), _jsx("p", { children: invoice.seller.addressLine1 }), invoice.seller.addressLine2 ? _jsx("p", { children: invoice.seller.addressLine2 }) : null, _jsxs("p", { children: [invoice.seller.postalCode, " ", invoice.seller.city] }), invoice.seller.taxId ? (_jsxs("p", { className: "text-muted-foreground", children: [t('invoiceDetail.field.taxId'), ": ", invoice.seller.taxId] })) : null] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: t('invoiceDetail.section.buyer') }) }), _jsxs(CardContent, { className: "space-y-0.5 text-sm", children: [_jsx("p", { className: "font-medium", children: invoice.buyer.name }), _jsx("p", { children: invoice.buyer.addressLine1 }), invoice.buyer.addressLine2 ? _jsx("p", { children: invoice.buyer.addressLine2 }) : null, _jsxs("p", { children: [invoice.buyer.postalCode, " ", invoice.buyer.city] }), invoice.buyer.taxId ? (_jsxs("p", { className: "text-muted-foreground", children: [t('invoiceDetail.field.taxId'), ": ", invoice.buyer.taxId] })) : null] })] })] }), _jsxs(Card, { className: "mt-4", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: t('invoiceDetail.section.lines') }) }), _jsx(CardContent, { children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: t('invoiceDetail.col.no') }), _jsx(TableHead, { children: t('invoiceDetail.col.name') }), _jsx(TableHead, { children: t('invoiceDetail.col.unit') }), _jsx(TableHead, { className: "text-right", children: t('invoiceDetail.col.qty') }), _jsx(TableHead, { className: "text-right", children: t('invoiceDetail.col.unitNet') }), _jsx(TableHead, { className: "text-right", children: t('invoiceDetail.col.taxRate') }), _jsx(TableHead, { className: "text-right", children: t('invoiceDetail.col.net') }), _jsx(TableHead, { className: "text-right", children: t('invoiceDetail.col.gross') })] }) }), _jsx(TableBody, { children: invoice.lines.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 8, className: "text-center text-muted-foreground", children: t('common.state.empty') }) })) : (invoice.lines.map((l) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: l.ordinal }), _jsx(TableCell, { children: l.name }), _jsx(TableCell, { children: l.unit }), _jsx(TableCell, { className: "text-right tabular-nums", children: l.quantity }), _jsx(TableCell, { className: "text-right tabular-nums", children: money(l.unitNetPrice) }), _jsxs(TableCell, { className: "text-right tabular-nums", children: [Math.round(l.taxRate * 100), "%"] }), _jsx(TableCell, { className: "text-right tabular-nums", children: money(l.netValue) }), _jsx(TableCell, { className: "text-right tabular-nums", children: money(l.grossValue) })] }, l.ordinal)))) })] }) })] }), _jsxs("div", { className: "mt-4 grid gap-4 md:grid-cols-2", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: t('invoiceDetail.section.vatSummary') }) }), _jsx(CardContent, { children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "text-right", children: t('invoiceDetail.col.taxRate') }), _jsx(TableHead, { className: "text-right", children: t('invoiceDetail.col.net') }), _jsx(TableHead, { className: "text-right", children: t('invoiceDetail.totals.tax') }), _jsx(TableHead, { className: "text-right", children: t('invoiceDetail.col.gross') })] }) }), _jsx(TableBody, { children: invoice.vatSummary.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 4, className: "text-center text-muted-foreground", children: t('common.state.empty') }) })) : (invoice.vatSummary.map((v) => (_jsxs(TableRow, { children: [_jsxs(TableCell, { className: "text-right tabular-nums", children: [Math.round(v.taxRate * 100), "%"] }), _jsx(TableCell, { className: "text-right tabular-nums", children: money(v.netTotal) }), _jsx(TableCell, { className: "text-right tabular-nums", children: money(v.vatAmount) }), _jsx(TableCell, { className: "text-right tabular-nums", children: money(v.grossTotal) })] }, v.taxRate)))) })] }) })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: t('invoiceDetail.section.totals') }) }), _jsxs(CardContent, { className: "space-y-1 text-sm", children: [_jsx(Row, { label: t('invoiceDetail.totals.net'), value: money(invoice.netTotal) }), _jsx(Row, { label: t('invoiceDetail.totals.tax'), value: money(invoice.taxTotal) }), _jsx(Row, { label: t('invoiceDetail.totals.gross'), value: _jsx("strong", { children: money(invoice.grossTotal) }) }), _jsx(Row, { label: t('invoiceDetail.totals.paid'), value: money(invoice.paidTotal) }), _jsx(Row, { label: t('invoiceDetail.totals.due'), value: money(invoice.amountDue) })] })] }), _jsx(AdminZone, { name: "invoice.detail.after", props: {
97
+ invoiceId: invoice.id,
98
+ kind: invoice.kind,
99
+ ksefReferenceNumber: invoice.ksefReferenceNumber,
100
+ } })] })] })] }));
101
+ }
102
+ function Row({ label, value }) {
103
+ return (_jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsx("span", { className: "text-muted-foreground", children: label }), _jsx("span", { className: "text-right", children: value })] }));
104
+ }
105
+ /**
106
+ * The registry loads a route component through a dynamic-import factory and
107
+ * reads its default export (feature 091, FR-013). The named export stays: it is
108
+ * the spelling this module's own code and its tests use.
109
+ */
110
+ export default InvoiceDetail;
111
+ //# sourceMappingURL=InvoiceDetail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InvoiceDetail.js","sourceRoot":"","sources":["../../../src/admin/pages/InvoiceDetail.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { type ReactNode } from 'react';
2
+ import '@measured/puck/puck.css';
3
+ /** Admin editor for an invoice PDF template (feature 047, US6). */
4
+ export declare function InvoiceTemplateEditor(): ReactNode;
5
+ /**
6
+ * The registry loads a route component through a dynamic-import factory and
7
+ * reads its default export (feature 091, FR-013). The named export stays: it is
8
+ * the spelling this module's own code and its tests use.
9
+ */
10
+ export default InvoiceTemplateEditor;
11
+ //# sourceMappingURL=InvoiceTemplateEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InvoiceTemplateEditor.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/InvoiceTemplateEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGlF,OAAO,yBAAyB,CAAC;AA8DjC,mEAAmE;AACnE,wBAAgB,qBAAqB,IAAI,SAAS,CA4OjD;AAED;;;;GAIG;AACH,eAAe,qBAAqB,CAAC"}
@@ -0,0 +1,188 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useState } from 'react';
3
+ import { useParams, useNavigate, Link } from 'react-router-dom';
4
+ import { Puck } from '@measured/puck';
5
+ import '@measured/puck/puck.css';
6
+ import { apiBaseUrl, apiClient, ApiError, cn } from '@endora-commerce/admin-kit/lib';
7
+ import { Alert, AlertDescription, Button, Card, CardContent, PageHeader, SaveButtonGroup } from '@endora-commerce/admin-kit/ui';
8
+ import { useTranslation } from '@endora-commerce/admin-kit/i18n';
9
+ import { PageBuilderHeaderActions } from '@endora-commerce/page-builder-admin';
10
+ import { PageBuilderOverlayBridge } from '@endora-commerce/page-builder-admin';
11
+ import { countBlockNames } from '@endora-commerce/page-builder-core';
12
+ import { invoicePuckConfig, invoicePuckPalette } from '../templates/invoice-puck-config.js';
13
+ import { withDescribedInvoiceBlocks, } from '../templates/described-blocks.js';
14
+ import { createInvoiceBuilderEditorPlugin } from '../templates/invoice-builder-plugin.js';
15
+ const emptyData = { root: { props: {} }, content: [] };
16
+ const LANGUAGE = 'pl-PL';
17
+ /**
18
+ * `apiBaseUrl` and not `import.meta.env`: this file compiles under `tsc` inside
19
+ * its own package, where Vite's client types are not in scope, and the kit
20
+ * publishes the resolved value — with this exact fallback — for exactly this
21
+ * (feature 091).
22
+ */
23
+ const API_BASE = apiBaseUrl;
24
+ const invoiceBuilderPlugin = createInvoiceBuilderEditorPlugin();
25
+ /**
26
+ * This module's own config: the namespaced renderer map plus its one derived
27
+ * section (feature 096, T303). The config Puck renders is this merged with the
28
+ * served descriptor and the stored document's names
29
+ * (`withDescribedInvoiceBlocks`, `specs/134-paid-module-extraction/` T138).
30
+ */
31
+ const localConfig = { ...invoicePuckConfig, categories: invoicePuckPalette };
32
+ /**
33
+ * Fill missing props from each component's `defaultProps`. Persisted trees
34
+ * (esp. older seeds) often only store `{ id }`, which leaves radio/select
35
+ * fields unselected in the Puck sidebar even though canvas fallbacks work.
36
+ */
37
+ function hydrateInvoiceDefaults(data) {
38
+ const components = invoicePuckConfig.components ?? {};
39
+ const content = (data.content ?? []).map((node) => {
40
+ const defaults = components[node.type]?.defaultProps;
41
+ if (!defaults)
42
+ return node;
43
+ const props = node.props;
44
+ const merged = { ...defaults };
45
+ for (const [key, value] of Object.entries(props)) {
46
+ if (value !== undefined)
47
+ merged[key] = value;
48
+ }
49
+ return { ...node, props: merged };
50
+ });
51
+ return { ...data, content };
52
+ }
53
+ /** Admin editor for an invoice PDF template (feature 047, US6). */
54
+ export function InvoiceTemplateEditor() {
55
+ const t = useTranslation('core');
56
+ const { id = '' } = useParams();
57
+ const navigate = useNavigate();
58
+ const [tpl, setTpl] = useState(null);
59
+ const [draft, setDraft] = useState(emptyData);
60
+ const [loaded, setLoaded] = useState(false);
61
+ const [error, setError] = useState(null);
62
+ const [notice, setNotice] = useState(null);
63
+ const [fullscreen, setFullscreen] = useState(false);
64
+ const [canvasEpoch, setCanvasEpoch] = useState(0);
65
+ const [previewBusy, setPreviewBusy] = useState(false);
66
+ /**
67
+ * The served descriptor — `null` until it arrives or when it cannot be
68
+ * fetched, in which case the editor keeps its own blocks and gives every
69
+ * stored block it cannot render a placeholder; it never drops a node (T138).
70
+ */
71
+ const [descriptor, setDescriptor] = useState(null);
72
+ useEffect(() => {
73
+ let cancelled = false;
74
+ apiClient
75
+ .get('/api/v1/admin/invoice-templates/page-builder/config')
76
+ .then((res) => {
77
+ if (!cancelled)
78
+ setDescriptor(res.data);
79
+ })
80
+ .catch(() => {
81
+ if (!cancelled)
82
+ setDescriptor(null);
83
+ });
84
+ return () => {
85
+ cancelled = true;
86
+ };
87
+ }, []);
88
+ // Keyed on the sorted set of stored names, as the CMS editor's merge is: a
89
+ // keystroke moves the document, not the names, so typing does not rebuild
90
+ // the Puck config.
91
+ const storedNamesKey = [...countBlockNames(draft).keys()].sort().join('\n');
92
+ const puckConfig = useMemo(() => {
93
+ const text = {
94
+ standIn: ({ label, owner }) => t('invoiceTemplates.describedBlock.standIn', { label, owner }),
95
+ placeholder: ({ owner, name }) => owner === null
96
+ ? t('invoiceTemplates.describedBlock.placeholderNoOwner', { name })
97
+ : t('invoiceTemplates.describedBlock.placeholder', { owner }),
98
+ };
99
+ return withDescribedInvoiceBlocks(localConfig, descriptor, storedNamesKey === '' ? [] : storedNamesKey.split('\n'), text);
100
+ }, [descriptor, storedNamesKey, t]);
101
+ useEffect(() => {
102
+ if (!fullscreen)
103
+ return undefined;
104
+ const onKey = (e) => {
105
+ if (e.key === 'Escape')
106
+ setFullscreen(false);
107
+ };
108
+ window.addEventListener('keydown', onKey);
109
+ return () => window.removeEventListener('keydown', onKey);
110
+ }, [fullscreen]);
111
+ const load = useCallback(async () => {
112
+ try {
113
+ const res = await apiClient.get(`/api/v1/admin/invoice-templates/${id}`);
114
+ setTpl(res.data);
115
+ const tree = res.data.content.languages?.[LANGUAGE] ?? emptyData;
116
+ setDraft(hydrateInvoiceDefaults(tree));
117
+ setLoaded(true);
118
+ }
119
+ catch (err) {
120
+ setError(err instanceof ApiError ? err.envelope.error.message : 'Failed to load template.');
121
+ }
122
+ }, [id]);
123
+ useEffect(() => {
124
+ void load();
125
+ }, [load]);
126
+ const applyCanvasData = useCallback((next) => {
127
+ setDraft(next);
128
+ setCanvasEpoch((n) => n + 1);
129
+ }, []);
130
+ const save = useCallback(async () => {
131
+ if (!tpl)
132
+ return false;
133
+ setError(null);
134
+ setNotice(null);
135
+ try {
136
+ const res = await apiClient.put(`/api/v1/admin/invoice-templates/${tpl.id}/content/${LANGUAGE}`, { version: tpl.version, data: draft });
137
+ setTpl((prev) => (prev ? { ...prev, version: res.data.version } : prev));
138
+ setNotice(t('invoiceTemplates.saved'));
139
+ return true;
140
+ }
141
+ catch (err) {
142
+ setError(err instanceof ApiError ? err.envelope.error.message : 'Failed to save.');
143
+ return false;
144
+ }
145
+ }, [tpl, draft, t]);
146
+ const saveAndExit = useCallback(async () => {
147
+ if (await save())
148
+ navigate('/invoices/templates');
149
+ }, [save, navigate]);
150
+ const previewPdf = useCallback(async () => {
151
+ if (!tpl)
152
+ return;
153
+ setPreviewBusy(true);
154
+ setError(null);
155
+ try {
156
+ const res = await fetch(`${API_BASE.replace(/\/+$/, '')}/api/v1/admin/invoice-templates/${tpl.id}/preview`, {
157
+ method: 'POST',
158
+ credentials: 'include',
159
+ headers: { 'Content-Type': 'application/json', Accept: 'application/pdf' },
160
+ body: JSON.stringify({ data: draft }),
161
+ });
162
+ if (!res.ok) {
163
+ throw new Error(t('invoiceTemplates.previewError'));
164
+ }
165
+ const blob = await res.blob();
166
+ const url = URL.createObjectURL(blob);
167
+ window.open(url, '_blank', 'noopener,noreferrer');
168
+ window.setTimeout(() => URL.revokeObjectURL(url), 60_000);
169
+ }
170
+ catch (err) {
171
+ setError(err instanceof Error ? err.message : t('invoiceTemplates.previewError'));
172
+ }
173
+ finally {
174
+ setPreviewBusy(false);
175
+ }
176
+ }, [tpl, draft, t]);
177
+ return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { title: tpl?.name ?? t('invoiceTemplates.editTitle'), description: t('invoiceTemplates.editDescription'), actions: _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", asChild: true, children: _jsx(Link, { to: "/invoices/templates", children: t('common.action.back') }) }), tpl ? (_jsx(Button, { type: "button", variant: "outline", size: "sm", disabled: previewBusy, onClick: () => void previewPdf(), children: previewBusy ? t('invoiceTemplates.previewLoading') : t('invoiceTemplates.preview') })) : null, _jsx(SaveButtonGroup, { size: "sm", onSave: () => void save(), onSaveAndExit: () => void saveAndExit(), saveLabel: t('common.action.save'), savingLabel: t('common.action.save'), saveAndExitLabel: t('common.action.saveAndExit') })] }) }), error ? (_jsx(Alert, { variant: "destructive", className: "mb-4", children: _jsx(AlertDescription, { children: error }) })) : null, notice ? (_jsx(Alert, { className: "mb-4", children: _jsx(AlertDescription, { children: notice }) })) : null, _jsx(Card, { children: _jsx(CardContent, { className: "pt-6", children: _jsx("div", { className: cn('cms-page-builder space-y-3', fullscreen && 'fixed inset-0 z-50 flex flex-col overflow-auto bg-background p-4'), children: _jsx("div", { className: cn('overflow-hidden rounded-md border', fullscreen ? 'min-h-0 flex-1' : 'min-h-[560px]'), children: loaded ? (_jsx(Puck, { config: puckConfig, data: draft, onChange: setDraft, plugins: [invoiceBuilderPlugin], overrides: {
178
+ headerActions: () => (_jsx(PageBuilderHeaderActions, { fullscreen: fullscreen, onToggleFullscreen: () => setFullscreen((f) => !f), currentData: draft, onClearCanvas: () => applyCanvasData(emptyData), t: t })),
179
+ componentOverlay: ({ children, hover, isSelected, componentId, componentType, }) => (_jsx(PageBuilderOverlayBridge, { hover: hover, isSelected: isSelected, componentId: componentId, componentType: componentType, children: children })),
180
+ } }, `${id}:${LANGUAGE}:${canvasEpoch}`)) : (_jsx("div", { className: "flex min-h-[560px] items-center justify-center text-sm text-muted-foreground", children: t('invoiceTemplates.loading') })) }) }) }) })] }));
181
+ }
182
+ /**
183
+ * The registry loads a route component through a dynamic-import factory and
184
+ * reads its default export (feature 091, FR-013). The named export stays: it is
185
+ * the spelling this module's own code and its tests use.
186
+ */
187
+ export default InvoiceTemplateEditor;
188
+ //# sourceMappingURL=InvoiceTemplateEditor.js.map