Skip to content

PRD: Invoice Issuance Experience

ModuleTax & InvoicePRD IDPRD-ISS-001
StatusReady to devFEATISS · BANA-1422
EpicBANA-1318PlaneBANA-1564
Date2026-06-11Versionv1.0
Packages@nx/invoiceURDISS
SurfaceSale · POSClient · Owner/Mgr
OwnerViệt Võ · Phát Nguyễn

TL;DR

  • A cashier issues an e-invoice at the moment of payment at the POS - one action, or automatic per merchant configuration - without leaving the payment screen (🚧 not built; the issuance engine is ready behind the scenes, only the button/status on screen is missing).
  • On the Client, a new invoice management screen shows the owner every invoice with its issuance status and tax-authority code - distinct from the "Invoice" tab that already exists at the POS, which is really an order list sharing the same name (🚧 not built).
  • An owner adjusts an incorrectly issued invoice right from its detail screen (🔶 the processing engine is ready, the screen isn't); replacing an invoice isn't available at any layer yet, not even underneath the UI.
  • A company buyer's VAT invoice is issued by picking a business-customer profile that already has a tax code - the invoice then carries that exact tax code and company name, gated on the Business Customers (BIZ) feature shipping the same month (🔶 the system already accepts and stores an ad-hoc buyer tax code, there's just no saved profile to pick from yet).
  • A failed invoice shows up and can be retried from the management screen - no invoice silently vanishes because of a technical error (🚧 not built).
  • Printing/sharing an issued invoice, and verifying the provider (VNPAY) connection from a config screen, are two smaller pieces left for later - neither has any surface today (🚧).

1. Context & Problem

The invoice lifecycle (queue → issue → status), the VNPAY provider integration (original/adjustment/cancellation operations), and tax identity/groups all shipped in Phase 1. The issuance engine already listens for successful-payment events and can queue an invoice the moment a sale is paid. Merchants own a well-working issuance machine running behind the scenes - there is just almost no way to see or drive it from a screen.

What's missing is the front-facing experience: the POS payment-success screen has no invoice-issuance action at all today (only sale-receipt and kitchen-ticket printing); the Client has no screen that shows an invoice's issuance status or tax-authority code. Worth flagging: the "Invoice" tab that already exists at the POS is easy to mistake for this - it shows order status (draft/processing/complete/cancelled), not e-invoice issuance status; two different concepts are sharing one name.

Invoice adjustment already has a ready processing engine (checks the original is issued, locks against double-processing, calls the provider) but no screen calls it yet; invoice replacement doesn't exist at any layer - it's only a named concept in the data model. Company invoices already accept and store a buyer's tax code via information attached to the order, but there is no business-customer profile to pick one from until the Business Customers (BIZ) feature ships, in the same July window.

2. Goals / Non-Goals

Goals

  • A cashier issues an invoice at the POS at the moment of payment, in one action, or automatically per merchant configuration.
  • The Client has an invoice management screen: a list filterable by issuance status and period, and a detail view with the tax-authority code and the full processing history.
  • An owner issues an adjustment invoice for one that was issued incorrectly.
  • A company invoice carries the tax code and company name of the business customer selected.
  • A failed invoice shows up and can be retried from the management screen - no invoice silently disappears.
  • An issued invoice can be printed/shared with the customer.
  • The connection to the e-invoice provider can be verified from a config screen.

Non-Goals

  • Invoice replacement - a named concept in the data model, with no processing flow or screen at any layer; out of scope for this increment (FR-003, §11).
  • Tax declaration/filing (PRD-DCL-001).
  • Providers other than VNPAY.
  • Industry-specific issuance rules beyond what tax groups already define.
  • Building the business-customer profile itself - owned by the Business Customers feature (BIZ, PRD-BIZ-001); this PRD only consumes that profile when a company buyer is selected.
  • Rendering an invoice PDF - owned by the provider; the exact sharing mechanism is still an open question (§11).
  • The full provider configuration/onboarding screen (CFG) - this PRD only needs a connection-verification step, not the whole configuration flow rebuilt.

3. Success Metrics

MetricTarget
POS issuanceA cashier issues an invoice at payment in one action, live merchant
No silent failuresEvery failed invoice shows up on the management screen and can be retried from it
Correct company tax codeA company invoice carries the selected tax code exactly, no manual fix-up after issuance

4. Personas & Use Cases

PersonaGoal in this feature
CashierIssue an invoice at payment without needing to know anything about tax
OwnerReview invoices, fix mistakes with an adjustment, serve company buyers
Company buyerReceive a valid VAT invoice carrying their company's exact tax code

Core scenario: a cashier finishes a payment at the POS, and the invoice is issued automatically or with one tap. If a buyer later reports a wrong amount, the owner reopens that invoice on the Client and issues an adjustment invoice right from the detail screen - with no need to understand the tax process behind it, and no need to contact support.

5. User Stories

  • As a cashier, I finish a payment and issue the invoice with one tap; the customer gets it immediately.
  • As an owner, a buyer reports a wrong amount - I issue an adjustment invoice from the detail screen.
  • As an owner, a company asks for a VAT invoice - I pick the business customer and the invoice carries their tax code.
  • As an owner, an invoice fails to issue - I see the reason right on the management screen and retry it without contacting support.
  • As an owner, a customer asks for their invoice again - I print or share the issued invoice from the detail screen.

6. Functional Requirements

#RequirementStatusURD ref
FR-001One-action issuance at the POS at payment, automatic or manual per merchant configuration🚧URD-ISS-001
FR-002Invoice management screen - list filterable by status/period + detail with tax-authority code and history🚧URD-ISS-002
FR-003Adjustment & replacement invoices for an issued invoice🔶 adjustment has an engine, replacement doesn'tURD-ISS-003
FR-004Company invoice carrying the tax code + name of the selected business customer🔶 gated on BIZURD-ISS-004
FR-005Failed issuance visible + retryable from the management screen🚧URD-ISS-005
FR-006Print/share an issued invoice with the customer🚧URD-ISS-006
FR-007Verify the provider connection (VNPAY) end-to-end from a config screen🚧URD-ISS-007

FR-001 - One-action issuance at the POS. Completing a payment at the POS triggers e-invoice issuance: per merchant configuration, this happens automatically (right when payment succeeds) or through a manual action by the cashier - an "Issue invoice" button next to the existing actions (print receipt, print kitchen ticket). The payment-success screen shows an issuance-status chip on the sale, with no need to leave the screen to know the outcome. The cashier needs no tax knowledge - just whether the invoice has been issued.

FR-002 - Invoice management screen. A new Client screen: an invoice list filterable by issuance status (Pending, Processing, Success, Failed, Cancelled) and by period; selecting a row opens a detail view showing the provider-assigned invoice number, the tax-authority code, and the full processing history (created, sent, signed, sent to the tax authority, accepted/rejected by the tax authority, adjusted, replaced, cancelled). This is a different screen from the "Invoice" tab that already exists at the POS - that tab actually shows order status (draft/processing/complete/cancelled), not e-invoice issuance status or the tax-authority code; the two concepts are easy to confuse from a quick look at the app (§11).

FR-003 - Adjustment & replacement invoices. From the detail screen of an invoice already issued successfully, an owner creates an adjustment invoice: entering a reason plus the list of corrected lines (ordinal, name, quantity, unit price, amount, tax); the new adjustment invoice references the original and goes through the same issuance lifecycle as any invoice. The adjustment processing engine (checking the original's status, locking against double-processing, calling the provider) is ready per PRD-INV-003 - only the screen is missing. That same backend increment also finished a cancellation flow with a mandatory reason, even though the ISS URD doesn't list cancellation as a requirement of its own (§11). Replacement - used when an invoice needs to be voided entirely and reissued rather than partially corrected - isn't available at any layer today: no processing flow, no API, no screen; it exists only as a named concept in the invoice data model.

FR-004 - Company invoice carrying the buyer's tax code. At checkout or at issuance, the owner/cashier picks a business customer that already has a profile (company name, tax code) - the issued invoice then carries that exact tax code and company name instead of a retail buyer's name. When no business customer is picked, the invoice uses the default name for a consumer not claiming a named invoice (per current regulation). The invoice system already accepts and stores a buyer's tax code via information attached to the order; what's missing is a business-customer profile to pick from - gated on the Business Customers (BIZ) feature, shipping the same July window.

FR-005 - Failed issuance visible + retryable. A failed invoice shows a Failed status with its reason on the invoice management screen, never disappearing from the owner's view. The system automatically retries on a schedule for failures identified as transient; the owner also needs to be able to retry from the detail screen (whether this is a separate manual action or just a display of the automatic retry in progress needs confirming, see §11). A retry - automatic or manual - never creates two invoices for the same sale.

FR-006 - Print/share an issued invoice. From the detail screen, the owner prints or shares a successfully issued invoice with the customer. This is distinct from the sale-receipt/kitchen-ticket printing already available at the POS. The exact sharing mechanism (a lookup link keyed on the tax-authority code, or something else) is still an open decision, since rendering the invoice PDF itself is out of scope for this PRD (owned by the provider).

FR-007 - Verify the provider connection. From a config screen, Operations/the merchant owner verifies that the connection to the e-invoice provider (VNPAY) is working - distinct from the existing VNPAY configuration for QR/Phone POS payments. This item depends on an invoice-provider config screen existing in the first place - no screen on the Client or Back Office reads or writes this configuration today, even though the backend (connection, retry policy) has shipped.

6.1 Acceptance criteria

  • Complete a payment at the POS → the invoice is queued for issuance (automatically or via one action) → the issuance status shows immediately on the payment-success screen (FR-001).
  • Open the invoice management screen on the Client, filter by Failed → see exactly the failed invoices with their reasons; open one → see its tax-authority code and full history (FR-002).
  • From a Success invoice, create an adjustment with a reason + corrected lines → a new adjustment invoice appears, referencing the correct original (FR-003 adjustment).
  • Try to create a replacement invoice → no action is available on the detail screen, matching current scope (FR-003 replacement, 🚧 out of scope).
  • Pick a business customer with a tax code at checkout → the issued invoice carries that exact tax code and company name, with no manual fix-up (FR-004).
  • Don't pick a business customer → the invoice uses the default retail-buyer name, with no tax code (FR-004).
  • Simulate a failed issuance → the invoice shows a Failed status with its reason on the management screen; retry → issuance succeeds, no duplicate invoice created (FR-005).
  • From a Success invoice, print it or get a share link → the customer receives a valid invoice (FR-006).
  • From the provider config screen, verify the connection → see the real success or failure result from the provider (FR-007).

7. Non-Functional Requirements

AreaRequirement
Doesn't block checkoutInvoice issuance runs in the background (async) - payment doesn't wait on the issuance result
IdempotentA retry (automatic or manual) never creates two invoices for the same sale
State integrityAn invoice being processed can't be cancelled or edited until the provider returns a result
Real-time displayInvoice status on the management screen updates without a page reload

8. UX & Flows

Issuing an invoice at the POS at payment:

Working an invoice from the management screen:

9. Data & Domain

EntityRole
Invoice issuanceA single-issuance record - status, tax-authority code, event history
Invoice requestBuyer information attached before issuance - name, tax code (if any), address
Business-customer profileOwned by the Business Customers (BIZ) feature - the source of a tax code/company name to pick
Provider configurationConnection + retry policy toward the e-invoice provider (VNPAY), per merchant

Conceptual only - the full schema lives in the developer domain model.

10. Dependencies & Assumptions

Depends on

  • Business Customers (BIZ, PRD-BIZ-001) - the tax code + company name profile to pick from when issuing a company invoice (FR-004).
  • Provider configuration per merchant (CFG, backend shipped) - the connection + retry policy for issuance (FR-001, FR-005, FR-007).
  • Retail correctness (PRD-RTL-001) - retail merchants issue with the right tax behaviour.

Assumptions

  • BIZ ships in the same July window as ISS; if it slips, FR-004 keeps the existing ad-hoc path (no profile to pick from, tax code entered manually if the buyer provides one) until BIZ is ready.
  • The automatic retry policy (max attempts, delay) is already configured per merchant; this PRD doesn't redefine that policy, only surfaces its outcome.

11. Risks & Open Questions

Risk / questionMitigation / status
The "Invoice" tab that already exists at the POS shares its name with the new e-invoice screen, which can make readers assume the feature already existsRename or replace the existing tab once the invoice management screen (FR-002) ships - decision pending
Invoice replacement (FR-003) doesn't exist at any layer - further behind than the "modeled but not issued" note in the previous version of this PRDAlready scoped out (§2); revisit only if there's a real ask to pull it into this increment
Retrying a failed issuance (FR-005) only has an automatic, scheduled mechanism today; unclear whether a separate manual-retry action is neededConfirm with the backend team before committing "retry from the screen" as a button, versus just showing the automatic retry in progress
The "share" mechanism for an invoice (FR-006) isn't decided at the data-contract level - it could be a lookup link keyed on the tax-authority code, or something elsePin down the design before writing detailed acceptance criteria for FR-006
Verifying the provider connection (FR-007) depends on a CFG config screen that doesn't exist on any surface todayConfirm whether FR-007 includes building a minimal config screen, or should be split off as a dependency on CFG shipping separately
Two old "Issue invoice" entry points in the Client are currently disabled (a quick-action and a Finance-module item)Decide whether to revive or delete them once FR-001/FR-002 ship, to avoid two conflicting entry points
Invoice cancellation already has a complete backend with a mandatory reason (PRD-INV-003), but URD-ISS-003 only names adjustment & replacement, never cancellation, as a requirement of the experienceConfirm whether cancellation belongs in the invoice management screen's scope (FR-002) for this increment, or is left to another PRD

12. Release Plan & Launch Criteria

AspectPlan
PhaseP2 - July (WK27-31), alongside BIZ
RolloutDirect launch to live merchants once criteria are met; the engine shipped in Phase 1, no separate feature flag needed for this increment
MigrationNone - this only builds a new operating surface on top of existing infrastructure
Launch criteriaAC-ISS-01 (URD) passes on a live merchant - one invoice issued at the POS, one company invoice with a tax code, one recovered failure; all of §6.1 passes except invoice replacement (FR-003, out of scope)
MonitoringSuccessful-issuance rate at payment; count of failed invoices still open on the management screen; average time from failure to a successful retry

13. FAQ

Is this rebuilding the invoice engine? No - the lifecycle, VNPAY integration, and tax identity all shipped in Phase 1; this PRD only builds the user-facing operating surface.

Is the "Invoice" tab that already exists at the POS the invoice management screen from this PRD? No - that tab shows order status, not issuance status or the tax-authority code; it will be replaced or renamed once FR-002 ships (§11).

Is invoice replacement part of this increment? No - it's out of scope because there's no engine for it at any layer, unlike adjustment which already has a processing engine (§2, §11).

What if Business Customers (BIZ) doesn't ship in time? A company invoice can still be issued through the existing ad-hoc path - no profile to pick from, tax code entered manually if the buyer provides one - until BIZ is ready.

What does "sharing an invoice" mean? Not decided yet - most likely a lookup link keyed on the tax-authority code from the provider, not a self-rendered PDF (§11).

References

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.