Skip to content

PRD: Finance categories

ModuleFinance (CORE-12)PRD IDPRD-CAT-001
StatusShippedOwnerFinance squad
Date2026-05-25Versionv1.0
Packages@nx/finance · apps/clientURDCAT

TL;DR

Lets a merchant read its books by income vs expense and by reason (Sale, Purchase, Rent, Salary…). A fixed catalog of platform-owned system categories is applied automatically to every auto-generated voucher, while merchants can add their own categories nested under a parent through a management screen. The result: every money movement carries a consistent, reportable classification - no unlabelled vouchers, no ad-hoc tagging.

1. Context & Problem

Every money movement in Finance needs a classification so owners can read their books by income vs expense and by reason (Sale, Purchase, Rent, Salary…). Without a category catalog, auto-generated vouchers - receipts on paid sales, payments on purchase-order receipt - have no consistent label, and merchants cannot group or report their movements. This blocks the basic bookkeeping read that HKD/SME owners expect.

This increment delivers the category axis of the double-entry voucher model: a fixed set of categories the platform owns and applies automatically, plus the ability for a merchant to add its own categories nested under a parent, surfaced through a management screen in the client.

2. Goals & Non-Goals

Goals

  • Provide a fixed catalog of system categories spanning income and expense, owned by the platform (not merchant-scoped).
  • Type each category as income or expense.
  • Apply a system category automatically to auto-generated vouchers (Sale, Purchase, etc.) via stable fixed identifiers.
  • Let merchants add custom categories nested under a parent, managed via a client screen with live preview, parent picker, and icon/badge display.

Non-Goals

  • Budget tracking and variance (URD Non-Goal).
  • Recurring / scheduled expense automation (URD Non-Goal).
  • Profit & loss and cash-flow forecasting (URD Non-Goal).
  • The voucher and ledger-line posting mechanics themselves - owned by areas VCH and TXN.

3. Success Metrics

MetricTarget / signal
Classification coverage100% of auto-generated vouchers carry a system category
Catalog stabilityRe-running the seeder never duplicates a system category (idempotent by identifier)
Custom adoptionMerchants who add at least one custom category trends up
Reporting readinessMovements group cleanly by income/expense and by category in reports

4. Personas & Use Cases

PersonaGoal in this feature
OwnerRead the books by income vs expense and by reason; tailor categories to the business
Accountant / ManagerClassify movements consistently for reporting
PlatformApply a stable, known category to every automatic posting

Core scenarios: the platform seeds a fixed catalog at startup → auto-generated vouchers resolve a stable system category by fixed identifier → an owner adds custom categories nested under a parent through the management screen → all movements report by income/expense and by category.

5. User Stories

  • As an owner, I want money movements classified as income or expense, so that I can read my books at a glance.
  • As an owner, I want auto-generated vouchers to carry a consistent system category, so that sales and purchases group correctly without manual tagging.
  • As an owner, I want to add my own categories nested under a parent, so that classification fits how my business thinks about money.
  • As an owner, I want a management screen with a live preview and icon picker, so that creating and editing categories is clear.
  • As the platform, I want a fixed set of system categories the seeder can re-run safely, so that automatic postings always resolve a stable category.

6. Functional Requirements

#RequirementURD ref
FR-1Provide a fixed catalog of seeded system categories spanning income and expense, platform-owned (merchantId: null); the seeder is idempotent (create-or-update by identifier/type)URD-CAT-001
FR-2Each category is typed INCOME or EXPENSEURD-CAT-002
FR-3System categories are keyed by stable fixed identifiers (SALE, PURCHASE, REFUND_*, …) so auto-generated vouchers resolve a stable system category automaticallyURD-CAT-003
FR-4Merchants may add custom categories nested under a parent, with icon metadataURD-CAT-004
FR-5Merchant-scoped CRUD over categories (x-merchant-id), authorized via finance category permissionsURD-CAT-004
FR-6A client management screen for create / edit / list, with name input, parent picker, live preview, badge + icon set, and bilingual labelsURD-CAT-004

Full requirement text and acceptance criteria live in the Finance URD. This PRD references them rather than restating them.

7. Non-Functional Requirements

AreaRequirement
Data integritySystem categories carry stable fixed identifiers; auto-posting resolves by identifier, never by name
IdempotencyThe seeder is create-or-update by identifier/type - re-running never duplicates a system category
Tenancy & authzSystem categories are platform-owned (merchantId: null); custom categories are merchant-scoped (x-merchant-id) and permission-gated
Performance / scaleCategory catalog is small and cacheable; resolution by fixed identifier is a constant-time lookup
i18nCategory names and screen labels are bilingual ({ en, vi })

8. UX & Flows

Key screens (in apps/client): category list, create, and edit - with name input, parent picker, live preview, and a category badge + icon set.

9. Data & Domain

EntityRole
FinanceCategoryThe category record - type (income/expense), parentId (nesting), icon metadata, merchantId (null = platform-owned)
FixedFinanceCategoriesStable fixed identifiers (SALE, PURCHASE, REFUND_*, …) used by auto-posting to resolve a system category
FinanceCategoryTypesIncome / expense type enum
Category seederIdempotent startup process that seeds the fixed system catalog (finance-0001-seed-categories.ts)

Conceptual only - full schema and invariants in the finance domain model.

10. Dependencies & Assumptions

Depends on

  • Vouchers & Posting (URD-VCH) - auto-generated vouchers consume categories.
  • Ledger Lines (URD-TXN) - a ledger line carries the optional category.
  • @nx/finance - the seeder, schema, and merchant-scoped controller live here.

Assumptions

  • The fixed system categories cover the income/expense reasons automatic postings emit (Sale, Purchase, refunds, etc.).
  • A merchant exists before creating custom merchant-scoped categories.

11. Risks & Open Questions

Risk / questionMitigation / status
Auto-posting could fail to resolve a categoryResolution is by stable fixed identifier, seeded idempotently at startup
Re-running the seeder could duplicate categoriesSeeder is create-or-update by identifier/type - never duplicates
Deep custom-category nesting could complicate reportsSingle parent level is the documented model; revisit if multi-level demand appears
Renaming a system category by handSystem categories are platform-owned, not merchant-editable

12. Release Plan & Launch Criteria

AspectPlan
PhaseP1 (foundation) - see URD feature catalog
RolloutAll merchants; no feature flag
MigrationNone for merchants; the fixed system catalog is seeded at startup (idempotent)
Launch criteria14 system categories seeded with correct income/expense typing; auto-generated vouchers resolve a system category by fixed identifier; custom category create/edit/list works merchant-scoped
MonitoringAuto-posting category-resolution failures; custom-category create rate; seeder idempotency on restart

13. FAQ

Are system categories editable by a merchant? No - the fixed system categories are platform-owned (merchantId: null) and applied automatically; merchants add their own custom categories instead.

How does an auto-generated voucher pick a category? By a stable fixed identifier (SALE, PURCHASE, REFUND_*, …), so the same event always resolves the same system category.

Can a custom category be nested? Yes - a custom category nests under a parent, with icon metadata for display.

Does running the seeder twice create duplicates? No - the seeder is idempotent (create-or-update by identifier/type).

What classifies as income vs expense? Each category is typed income or expense; the seeded catalog covers both (e.g. Sales Revenue, Refund Received as income; Purchase, Rent & Utilities, Salaries as expense).

References

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