@endora-commerce/mod-invoices 0.0.0-stage → 0.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +59 -2
- package/dist/admin/components/InvoiceSectionTabs.d.ts +22 -0
- package/dist/admin/components/InvoiceSectionTabs.d.ts.map +1 -0
- package/dist/admin/components/InvoiceSectionTabs.js +34 -0
- package/dist/admin/components/InvoiceSectionTabs.js.map +1 -0
- package/dist/admin/index.d.ts +34 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +69 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/InvoiceDetail.d.ts +10 -0
- package/dist/admin/pages/InvoiceDetail.d.ts.map +1 -0
- package/dist/admin/pages/InvoiceDetail.js +111 -0
- package/dist/admin/pages/InvoiceDetail.js.map +1 -0
- package/dist/admin/pages/InvoiceTemplateEditor.d.ts +11 -0
- package/dist/admin/pages/InvoiceTemplateEditor.d.ts.map +1 -0
- package/dist/admin/pages/InvoiceTemplateEditor.js +188 -0
- package/dist/admin/pages/InvoiceTemplateEditor.js.map +1 -0
- package/dist/admin/pages/InvoiceTemplatesPage.d.ts +10 -0
- package/dist/admin/pages/InvoiceTemplatesPage.d.ts.map +1 -0
- package/dist/admin/pages/InvoiceTemplatesPage.js +60 -0
- package/dist/admin/pages/InvoiceTemplatesPage.js.map +1 -0
- package/dist/admin/pages/InvoicesList.d.ts +9 -0
- package/dist/admin/pages/InvoicesList.d.ts.map +1 -0
- package/dist/admin/pages/InvoicesList.js +116 -0
- package/dist/admin/pages/InvoicesList.js.map +1 -0
- package/dist/admin/templates/described-blocks.d.ts +67 -0
- package/dist/admin/templates/described-blocks.d.ts.map +1 -0
- package/dist/admin/templates/described-blocks.js +83 -0
- package/dist/admin/templates/described-blocks.js.map +1 -0
- package/dist/admin/templates/invoice-builder-plugin.d.ts +4 -0
- package/dist/admin/templates/invoice-builder-plugin.d.ts.map +1 -0
- package/dist/admin/templates/invoice-builder-plugin.js +12 -0
- package/dist/admin/templates/invoice-builder-plugin.js.map +1 -0
- package/dist/admin/templates/invoice-puck-config.d.ts +37 -0
- package/dist/admin/templates/invoice-puck-config.d.ts.map +1 -0
- package/dist/admin/templates/invoice-puck-config.js +733 -0
- package/dist/admin/templates/invoice-puck-config.js.map +1 -0
- package/dist/backend/email-templates/invoice-issued.default.d.ts +10 -0
- package/dist/backend/email-templates/invoice-issued.default.d.ts.map +1 -0
- package/dist/backend/email-templates/invoice-issued.default.js +45 -0
- package/dist/backend/email-templates/invoice-issued.default.js.map +1 -0
- package/dist/backend/entities/invoice-external-attachment.entity.d.ts +20 -0
- package/dist/backend/entities/invoice-external-attachment.entity.d.ts.map +1 -0
- package/dist/backend/entities/invoice-external-attachment.entity.js +77 -0
- package/dist/backend/entities/invoice-external-attachment.entity.js.map +1 -0
- package/dist/backend/entities/invoice-line.entity.d.ts +20 -0
- package/dist/backend/entities/invoice-line.entity.d.ts.map +1 -0
- package/dist/backend/entities/invoice-line.entity.js +82 -0
- package/dist/backend/entities/invoice-line.entity.js.map +1 -0
- package/dist/backend/entities/invoice-number-counter.entity.d.ts +18 -0
- package/dist/backend/entities/invoice-number-counter.entity.d.ts.map +1 -0
- package/dist/backend/entities/invoice-number-counter.entity.js +63 -0
- package/dist/backend/entities/invoice-number-counter.entity.js.map +1 -0
- package/dist/backend/entities/invoice-template.entity.d.ts +22 -0
- package/dist/backend/entities/invoice-template.entity.d.ts.map +1 -0
- package/dist/backend/entities/invoice-template.entity.js +84 -0
- package/dist/backend/entities/invoice-template.entity.js.map +1 -0
- package/dist/backend/entities/invoice.entity.d.ts +64 -0
- package/dist/backend/entities/invoice.entity.d.ts.map +1 -0
- package/dist/backend/entities/invoice.entity.js +202 -0
- package/dist/backend/entities/invoice.entity.js.map +1 -0
- package/dist/backend/index.d.ts +111 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +291 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/pdf-components/descriptor.d.ts +29 -0
- package/dist/backend/pdf-components/descriptor.d.ts.map +1 -0
- package/dist/backend/pdf-components/descriptor.js +227 -0
- package/dist/backend/pdf-components/descriptor.js.map +1 -0
- package/dist/backend/pdf-components/embed-logo-images.d.ts +35 -0
- package/dist/backend/pdf-components/embed-logo-images.d.ts.map +1 -0
- package/dist/backend/pdf-components/embed-logo-images.js +101 -0
- package/dist/backend/pdf-components/embed-logo-images.js.map +1 -0
- package/dist/backend/pdf-components/sample.d.ts +4 -0
- package/dist/backend/pdf-components/sample.d.ts.map +1 -0
- package/dist/backend/pdf-components/sample.js +75 -0
- package/dist/backend/pdf-components/sample.js.map +1 -0
- package/dist/backend/pdf-components/sections.d.ts +34 -0
- package/dist/backend/pdf-components/sections.d.ts.map +1 -0
- package/dist/backend/pdf-components/sections.js +445 -0
- package/dist/backend/pdf-components/sections.js.map +1 -0
- package/dist/backend/pdf-components/tree-mapper.d.ts +37 -0
- package/dist/backend/pdf-components/tree-mapper.d.ts.map +1 -0
- package/dist/backend/pdf-components/tree-mapper.js +76 -0
- package/dist/backend/pdf-components/tree-mapper.js.map +1 -0
- package/dist/backend/plugin.d.ts +124 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +87 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +41 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +219 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.customer.d.ts +33 -0
- package/dist/backend/routes.customer.d.ts.map +1 -0
- package/dist/backend/routes.customer.js +124 -0
- package/dist/backend/routes.customer.js.map +1 -0
- package/dist/backend/seeds/generic-invoice-template.d.ts +18 -0
- package/dist/backend/seeds/generic-invoice-template.d.ts.map +1 -0
- package/dist/backend/seeds/generic-invoice-template.js +174 -0
- package/dist/backend/seeds/generic-invoice-template.js.map +1 -0
- package/dist/backend/services/amount-to-words.d.ts +20 -0
- package/dist/backend/services/amount-to-words.d.ts.map +1 -0
- package/dist/backend/services/amount-to-words.js +232 -0
- package/dist/backend/services/amount-to-words.js.map +1 -0
- package/dist/backend/services/auto-issue-reactor.d.ts +53 -0
- package/dist/backend/services/auto-issue-reactor.d.ts.map +1 -0
- package/dist/backend/services/auto-issue-reactor.js +122 -0
- package/dist/backend/services/auto-issue-reactor.js.map +1 -0
- package/dist/backend/services/corrective-invoice.d.ts +38 -0
- package/dist/backend/services/corrective-invoice.d.ts.map +1 -0
- package/dist/backend/services/corrective-invoice.js +257 -0
- package/dist/backend/services/corrective-invoice.js.map +1 -0
- package/dist/backend/services/cross-module-context.d.ts +82 -0
- package/dist/backend/services/cross-module-context.d.ts.map +1 -0
- package/dist/backend/services/cross-module-context.js +93 -0
- package/dist/backend/services/cross-module-context.js.map +1 -0
- package/dist/backend/services/duplicate-number-refusal.d.ts +33 -0
- package/dist/backend/services/duplicate-number-refusal.d.ts.map +1 -0
- package/dist/backend/services/duplicate-number-refusal.js +92 -0
- package/dist/backend/services/duplicate-number-refusal.js.map +1 -0
- package/dist/backend/services/erp-sale-document-write-port.d.ts +37 -0
- package/dist/backend/services/erp-sale-document-write-port.d.ts.map +1 -0
- package/dist/backend/services/erp-sale-document-write-port.js +192 -0
- package/dist/backend/services/erp-sale-document-write-port.js.map +1 -0
- package/dist/backend/services/invoice-attachment-fetch-registry.d.ts +75 -0
- package/dist/backend/services/invoice-attachment-fetch-registry.d.ts.map +1 -0
- package/dist/backend/services/invoice-attachment-fetch-registry.js +81 -0
- package/dist/backend/services/invoice-attachment-fetch-registry.js.map +1 -0
- package/dist/backend/services/invoice-copy-host.service.d.ts +9 -0
- package/dist/backend/services/invoice-copy-host.service.d.ts.map +1 -0
- package/dist/backend/services/invoice-copy-host.service.js +52 -0
- package/dist/backend/services/invoice-copy-host.service.js.map +1 -0
- package/dist/backend/services/invoice-email-dispatch.d.ts +89 -0
- package/dist/backend/services/invoice-email-dispatch.d.ts.map +1 -0
- package/dist/backend/services/invoice-email-dispatch.js +138 -0
- package/dist/backend/services/invoice-email-dispatch.js.map +1 -0
- package/dist/backend/services/invoice-line-builder.d.ts +29 -0
- package/dist/backend/services/invoice-line-builder.d.ts.map +1 -0
- package/dist/backend/services/invoice-line-builder.js +46 -0
- package/dist/backend/services/invoice-line-builder.js.map +1 -0
- package/dist/backend/services/invoice-number-collisions.d.ts +17 -0
- package/dist/backend/services/invoice-number-collisions.d.ts.map +1 -0
- package/dist/backend/services/invoice-number-collisions.js +154 -0
- package/dist/backend/services/invoice-number-collisions.js.map +1 -0
- package/dist/backend/services/invoice-number-generator.d.ts +96 -0
- package/dist/backend/services/invoice-number-generator.d.ts.map +1 -0
- package/dist/backend/services/invoice-number-generator.js +157 -0
- package/dist/backend/services/invoice-number-generator.js.map +1 -0
- package/dist/backend/services/invoice-pdf-block-registry.d.ts +65 -0
- package/dist/backend/services/invoice-pdf-block-registry.d.ts.map +1 -0
- package/dist/backend/services/invoice-pdf-block-registry.js +94 -0
- package/dist/backend/services/invoice-pdf-block-registry.js.map +1 -0
- package/dist/backend/services/invoice-pdf-renderer.d.ts +57 -0
- package/dist/backend/services/invoice-pdf-renderer.d.ts.map +1 -0
- package/dist/backend/services/invoice-pdf-renderer.js +158 -0
- package/dist/backend/services/invoice-pdf-renderer.js.map +1 -0
- package/dist/backend/services/invoice-pdf.d.ts +24 -0
- package/dist/backend/services/invoice-pdf.d.ts.map +1 -0
- package/dist/backend/services/invoice-pdf.js +52 -0
- package/dist/backend/services/invoice-pdf.js.map +1 -0
- package/dist/backend/services/invoice-placement-apply-port.d.ts +29 -0
- package/dist/backend/services/invoice-placement-apply-port.d.ts.map +1 -0
- package/dist/backend/services/invoice-placement-apply-port.js +40 -0
- package/dist/backend/services/invoice-placement-apply-port.js.map +1 -0
- package/dist/backend/services/invoice-read-port.d.ts +44 -0
- package/dist/backend/services/invoice-read-port.d.ts.map +1 -0
- package/dist/backend/services/invoice-read-port.js +95 -0
- package/dist/backend/services/invoice-read-port.js.map +1 -0
- package/dist/backend/services/invoice-service.d.ts +94 -0
- package/dist/backend/services/invoice-service.d.ts.map +1 -0
- package/dist/backend/services/invoice-service.js +374 -0
- package/dist/backend/services/invoice-service.js.map +1 -0
- package/dist/backend/services/invoice-template-service.d.ts +43 -0
- package/dist/backend/services/invoice-template-service.d.ts.map +1 -0
- package/dist/backend/services/invoice-template-service.js +147 -0
- package/dist/backend/services/invoice-template-service.js.map +1 -0
- package/dist/backend/services/numbering-configuration.d.ts +76 -0
- package/dist/backend/services/numbering-configuration.d.ts.map +1 -0
- package/dist/backend/services/numbering-configuration.js +176 -0
- package/dist/backend/services/numbering-configuration.js.map +1 -0
- package/dist/backend/services/numbering-write-validator.d.ts +22 -0
- package/dist/backend/services/numbering-write-validator.d.ts.map +1 -0
- package/dist/backend/services/numbering-write-validator.js +82 -0
- package/dist/backend/services/numbering-write-validator.js.map +1 -0
- package/dist/backend/services/seller-settings.d.ts +24 -0
- package/dist/backend/services/seller-settings.d.ts.map +1 -0
- package/dist/backend/services/seller-settings.js +38 -0
- package/dist/backend/services/seller-settings.js.map +1 -0
- package/dist/backend/services/vendor-number-hold.d.ts +10 -0
- package/dist/backend/services/vendor-number-hold.d.ts.map +1 -0
- package/dist/backend/services/vendor-number-hold.js +14 -0
- package/dist/backend/services/vendor-number-hold.js.map +1 -0
- package/dist/manifest.d.ts +203 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +390 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260629T125121_invoices_module.d.ts +17 -0
- package/dist/migrations/20260629T125121_invoices_module.d.ts.map +1 -0
- package/dist/migrations/20260629T125121_invoices_module.js +117 -0
- package/dist/migrations/20260629T125121_invoices_module.js.map +1 -0
- package/dist/migrations/20260801T111000_invoices_generic_template_reseed.d.ts +6 -0
- package/dist/migrations/20260801T111000_invoices_generic_template_reseed.d.ts.map +1 -0
- package/dist/migrations/20260801T111000_invoices_generic_template_reseed.js +25 -0
- package/dist/migrations/20260801T111000_invoices_generic_template_reseed.js.map +1 -0
- package/dist/migrations/20260817T201110_invoices_correction_idempotency_key.d.ts +20 -0
- package/dist/migrations/20260817T201110_invoices_correction_idempotency_key.d.ts.map +1 -0
- package/dist/migrations/20260817T201110_invoices_correction_idempotency_key.js +33 -0
- package/dist/migrations/20260817T201110_invoices_correction_idempotency_key.js.map +1 -0
- package/dist/migrations/20260903T101756_invoices_namespace_block_names.d.ts +6 -0
- package/dist/migrations/20260903T101756_invoices_namespace_block_names.d.ts.map +1 -0
- package/dist/migrations/20260903T101756_invoices_namespace_block_names.js +56 -0
- package/dist/migrations/20260903T101756_invoices_namespace_block_names.js.map +1 -0
- package/dist/migrations/20260914T133603_invoices_erp_imported_sale_documents.d.ts +12 -0
- package/dist/migrations/20260914T133603_invoices_erp_imported_sale_documents.d.ts.map +1 -0
- package/dist/migrations/20260914T133603_invoices_erp_imported_sale_documents.js +87 -0
- package/dist/migrations/20260914T133603_invoices_erp_imported_sale_documents.js.map +1 -0
- package/dist/migrations/20260926T230239_invoices_import_identity.d.ts +33 -0
- package/dist/migrations/20260926T230239_invoices_import_identity.d.ts.map +1 -0
- package/dist/migrations/20260926T230239_invoices_import_identity.js +83 -0
- package/dist/migrations/20260926T230239_invoices_import_identity.js.map +1 -0
- package/dist/migrations/index.d.ts +32 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +39 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/ports/index.d.ts +93 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +2 -0
- package/dist/ports/index.js.map +1 -0
- package/docs/invoices.md +113 -0
- package/i18n/en.json +45 -0
- package/i18n/pl.json +45 -0
- package/package.json +119 -3
- package/tailwind.css +14 -0
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
|
-
#
|
|
1
|
+
# Invoices module (feature 047)
|
|
2
2
|
|
|
3
|
-
|
|
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
|