zerp — System Overview
zerp (a.k.a. Zyncount) is a multi-tenant, multi-company ERP covering accounting, inventory, manufacturing, HR/payroll, projects, POS, CRM, and recruitment — delivered as a per-tenant NestJS GraphQL backend (
zerp-be) plus a per-tenant Next.js admin (zerp-admin). This document set (zerp) is a rebuild-grade requirements capture of that system, organized by domain and flow.
This is the entry point. For the doc index see README. For how the apps are built see architecture; for the tenant model see multi-tenancy; for the module correspondence see module-map.
1. What zerp is
A double-entry accounting core with a full ERP built around it. Scale: ~80 modules / ~277k LOC across the two apps (~37 backend modules, ~45 admin modules). It is sold/operated per tenant; each tenant is a fully isolated installation that can itself run multiple companies and branches.
The product appears tuned for gold/jewellery trading businesses (gold-purity item types, a dormant "Wingold" legacy-ERP export, weight-based POS), on top of a general-purpose ERP foundation.
2. The recurring architectural idea: ledgers, not stored balances
Two of the system's three heaviest engines share one design principle — state is derived by aggregating an append-only ledger, never stored as a mutable running total:
- Accounting — every financial event becomes balanced
AccountTransactionrows (debit/credit legs). An account's balance is alwaysΣ(debits) − Σ(credits)(by account type), never a stored field. Document headers (journal, contra, payment, invoice…) just group their legs. See finance/_overview. - Inventory — every inbound/outbound is one
Stockrow (IN/OUT). On-hand =Σ IN − Σ OUTper item per branch; there is no mutable "current quantity". See inventory/stock. - Manufacturing then sits on inventory (material consumption = Stock OUT, finished output = Stock IN) and posts WIP/cost to the GL. See manufacturing/_overview.
This makes movements auditable and reversible (delete a row → the derived total self-corrects).
3. The domains
| Domain | What it does | Doc |
|---|---|---|
| Finance | Double-entry GL: chart of accounts, journals, payments, contra, tax, notes, cashbook, trade AR/AP | finance |
| Inventory | Items/catalog, the stock ledger, purchases, sales, transfers, adjustments, costing (FIFO/AVCO) | inventory |
| Manufacturing | BOM, routing, work centers, production orders, MRP, quality, manufacturing accounting | manufacturing |
| HR | Employee master, attendance, timesheets, leave, payroll (Malaysian statutory), claims/loans/advances, ESS, training | hr |
| Projects | Projects, tasks, milestones, time entries, expenses, members, profitability | projects |
| Recruitment | Job postings, applicants, interviews, offers → convert to employee | recruitment |
| Sales / POS | Front-of-house terminal over the sales/order engine | sales-pos |
| CRM | Customers & suppliers (trading-partner master), KYC | crm |
| Master data | Reference data (UOM, currency, tax types, dimensions…), company/branch/store, fiscal periods, exchange rates, config | master-data |
| Assets | Fixed-asset register + depreciation | assets |
| Budget | Budgets per account/period | budget |
| Subscription/config | Plans, feature catalog, feature gating | subscription-config |
| User access | The unified users collection, admin users, profile, preferences |
user-access |
| Integrations | MCP server, Zoom, app version, Wingold | integrations |
Cross-cutting platform concerns (auth, RBAC, workflow/approvals, audit, notifications, files, reporting, multi-tenancy) live under platform/.
4. How a request flows
operator → zerp-admin (Next.js)
component → <Feature>Context → gql/query.ts → Apollo Client
│ GraphQL (bearer JWT)
▼
zerp-be (NestJS) Resolver → Service → Repository → Mongoose → MongoDB (this tenant's DB)
@ApGqlAuthorize (auth+RBAC) · @AuditMeta (audit) · TransactionManager (sessions)
A single business action commonly fans out into multiple ledger writes in one Mongo session — e.g. a sales invoice writes the order + items + Stock(OUT) + GL legs (AR, COGS, revenue, tax) + optional workflow submission + notifications + audit entry.
5. Deployment
Per-tenant PM2 process pair (zerp-<key>-be, zerp-<key>-admin), each with its own MongoDB database, ports, and TZ, started/stopped from the zerp-master control plane. A master DB holds the tenant registry, limits, and feature whitelist. See multi-tenancy.
6. Reading this doc set
Every module doc follows a fixed template (see TEMPLATE.md): purpose → data model → API surface → business rules/calculations → permissions → flows → admin UI → dependencies → gotchas. Docs are code-accurate and cite source files, and deliberately flag gaps, stubs, and bugs found in the code rather than presenting an idealized design. The methodology is in SPEC.md.