# E-Invoice pages: split into 3 menu pages (mockup) Date: 19 Oct 2026. Status: mockup for review. Not built. ## Scope Change 1 of the e-invoice adjustments. One BO page with 3 tabs becomes 3 menu pages. | Before | After | |---|---| | Finance > E-Invoice, one page `/finance/einvoice` | Finance > E-Invoice > Configuration `/finance/einvoice/config` | | Tabs: Configuration / Consolidation / Submission | Finance > E-Invoice > Submission `/finance/einvoice/submission` | | Consolidation and Submission tabs locked until setup is done | Finance > E-Invoice > Consolidation `/finance/einvoice/consolidation` | Nothing else changes: same columns, same buttons, same detail pages. ## Files - `mockup/index.html`: start here - `mockup/einvoice-as-is.html`: the current design (before) - `mockup/einvoice-separate-pages.html`: the new design (after) ## Decisions in the "after" design 1. The sidebar opens a third menu level under Finance > E-Invoice. The sidebar already supports nested items (`NavChild.children` in `frontend/src/lib/navigation.ts`). 2. Each menu item has its own access right: `finance.einvoice.config.view`, `.consolidation.view`, `.submission.view`. Today the menu item needs `config.view`, so staff who only look at submissions cannot open the page. This change fixes that. 3. Configuration opens an overview first: a ready / not-ready banner and the Company, Stores and Items cards. "Continue configuration" / "Edit Configuration" opens the 4-step wizard. Back and Finish return to the overview. 4. When setup is not finished, Consolidation and Submission show "Finish the company and store setup in Configuration" and a "Go to Configuration" button. This replaces the locked tabs. 5. On a detail page, Back returns to its list page, not to a tab. 6. The list pages use the BO list standard: one header card (title, store picker, search, date range, settings, export) and filters in the column headers. There is no top "Filter" button, because PR 11283 removed those on 19 Oct 2026. 7. "Response" is now "Status". Consolidation had 2 columns (Status and Response), and now has one Status column: Pending, Submitted, Valid, Invalid, Cancelled, Error. **This needs the backend to send one combined value.** 8. A new "Total" column (Subtotal + Tax) comes after Tax. The tables already store it as `total_payable`. ## Real code affected (when built) - `frontend/src/lib/navigation.ts`: E-Invoice item gets `children` - `frontend/src/lib/routes.ts`, `frontend/src/App.tsx`: 2 new list routes - `frontend/src/pages/einvoice/EInvoicePage.tsx`: removed or reduced to a redirect to Configuration - `frontend/src/pages/einvoice/tabs/*.tsx`: become pages - `ConsolidationDetailPage.tsx`, `SubmissionDetailPage.tsx`: Back link - `frontend/src/lib/breadcrumb-overrides.ts`: new crumbs - The "is setup ready" check must not need `config.view`. Today it comes from GET `/api/einvoice/config/company`, which needs `config.view`. The list pages need a way to read it without that right. - Old link `/finance/einvoice` should redirect, so bookmarks keep working. ## How this works elsewhere The outside research step was skipped on purpose. This change only moves menu items. Outside products do not change any decision here. 9. Configuration is a settings page, not a one-time setup. The page uses numbered cards: 1 Sending method, 2 Company, 3 Stores, 4 Items. When nothing is saved yet, the page shows only card 1 with two choice tiles (each with "You need"). Cards 2-4 are not shown, because locked cards confused users. After saving, card 1 shows the choice with a "Change" button. The words "setup" and "Finish setup" are gone: buttons say "Save & continue" and "Save configuration". 10. The sending method names are "Sellable" and "External system". "Our system" was removed because it is unclear who "our" is. 11. Changing the sending method shows a "Before you switch" card: old → new method, what changes for that direction, the waiting e-invoices with a link to Submission, and the tick box "I understand. Switch to ...". The button names the result: "Switch to External system" or "Switch to Sellable". 12. Consolidation and Submission tables copy the real BO DataTable: sort arrows on each header, a filter row under the header with "All ▾" filters (date filter shows the picked range), row click opens the detail, and a "Totals:" row at the bottom for Subtotal, Tax and Total. In the real build the totals must come from the server over all filtered rows, not only the page shown (same as Sales > Sales Order, `SalesOrderPage.tsx` "Footer totals come from the SERVER"). 13. Icons are the real BO icons (lucide-react, the same names the BO imports), not emoji. The breadcrumb arrow is `ChevronRight`, the same as `components/ui/breadcrumb.tsx`. 14. Menu order is Configuration, Submission, Consolidation. Submission comes first because staff open it most. 15. Page headers show the title and one line of help only. No company name chip: the real BO list pages (for example `SalesOrderPage.tsx`) do not show one. 16. Types use only what Sellable makes (`v2-sellable-api/src/modules/einvoice/einvoice_constants.py:50`): Invoice (customer asked), Consolidated (monthly), Credit Note (cancel or return). "Debit Note" and "Refund Note" are removed: no code makes them. The real BO type lists (`SubmissionTab.tsx:14`, `ConsolidationTab.tsx:16`) must change the same way and add "Consolidated". The monthly job saves the consolidation row with no type (`einvoice_query.py:2091`), so the build must save it or the Consolidation Type column is empty. 17. The page name stays "Submission" (decided 21 Oct 2026). It is the word LHDN MyInvois uses. "E-Invoice Request" was rejected: the list also holds monthly combined invoices and credit notes, and "request" already means the customer form (`ft_einvoice_request`). 18. User review (21 Oct 2026). "Send again" is the one retry button. It shows only on Invalid and Error rows, never on Pending (that could send twice). Resend, Regenerate and Re-run are gone. Invalid rows show "Why LHDN did not accept it" and "What to do" in plain words, with the LHDN code small, for support. 19. Every detail page shows one line that explains its status. A cancelled row shows its cancel reason. The cancel form warns that LHDN allows a cancel only within 72 hours and that a cancel cannot be undone. 20. Switching from External back to Sellable makes Configuration show "E-Invoice is not ready" until the Company step is saved (plan D5). The Company step keeps Save & Next enabled. Pressing it before "Validate" shows a red box and a message. The words Validate / Validated / Not Validated stay as in the real page (`EInvoiceConfigPage.tsx:239`). 21. List totals leave out Invalid and Cancelled rows and take off credit notes. Credit notes show as minus amounts. A note under each table says so. The real server totals must use the same rule. 22. The monthly invoice is sent on a set day ("Monthly invoice send day", 5 in the mockup), not the 25th. Store pages and detail pages use the store that was opened, and the store's own TIN. 23. Row "⋯" menu on Submission and Consolidation (today the real menu has only View, `SubmissionTab.tsx:109`). It shows only what fits the row: View always; Print when Valid; Open QR link when Valid or Cancelled (Submission); Send again when Invalid or Error; Cancel when Valid, red, and greyed out with a reason after 72 hours. External mode shows only View, Print and Open QR link. Cancel opens the detail page with the cancel form, so there is still no pop-up. 24. Monthly combined invoices are shown only on Consolidation, not on Submission (decided 21 Oct 2026). Submission lists customer e-invoices and credit notes only. Build: the submission list query (`einvoice_repository.py:121`) adds `AND eh.einv_type <> 'CONSOLIDATION'`. 25. The Consolidation detail page now shows the LHDN proof (Submission ID, UUID, Long ID, QR link) and has Print and QR Link buttons. Build: read them from the matching `ft_einv_header` row (`einv_consolidation_id`), because `ft_einv_consolidation_header` does not hold them. 26. Submission drops "Requested by customer" and "Period" (always "Yes" and empty once monthly rows are gone). A new "Credit note for" column shows the original e-invoice of a credit note: its reference and "Customer e-invoice" or "Monthly invoice · ". Build: join on `parent_einv_header_id`. 27. Doc references use the real BO formats. Customer e-invoice = the sale number `SI-{store_id}/{register}/{yyMMddHHmmssSSS}` (`sales_commit_repository.py:184`), e.g. `SI-12/2/261019102108233`. Credit note = the return sale number `CI-{store_id}/{register}/{yyMMddHHmmssSSS}` (prefix `CREDIT_INVOICE_DOC_PREFIX`, default "CI", `company_settings.py:144`), e.g. `CI-12/2/261018181204552`. Note: the e-invoice cancel credit note in `einvoice_service.py:1778` still builds `CN-` + the original reference; the build must use the CI number instead. Monthly invoice = `CONS-{company code}-{store code}-{YYYYMM}-001` (`einvoice_scheduler.py:589,773`), e.g. `CONS-ACME-KLCC01-202609-001`. The register number (2, 3) in the mockup is sample data. ## Known limitations Review: 1 critic pass and 1 re-check. The critic found 1 CRITICAL and 2 HIGH problems, and all of them are fixed. The CRITICAL one was missing Re-run, Resend and Regenerate buttons in the "after" file. These limits stay: - In the real app, Test LHDN Connection opens as a pop-up. Both mockups show it inline as a still picture with a note. - Table column filters, sorting and the column chooser are not drawn. - Permission variants (buttons hidden without the access right) and loading states are not shown. - No mock row has the Error response. The Re-run rule covers Error, Invalid and no answer yet. - On the detail pages of the "after" file, "E-Invoice > Submission" is one breadcrumb item, not two links. - Neither file was clicked through in a real browser. Only the scripts were checked.