All work
Case study Live

Promatrix ERP

A multi-tenant ERP for Saudi businesses, built solo. 18 modules — from the general ledger to payroll and point of sale — post to one ledger. VAT, ZATCA e-invoicing, GOSI and WPS live inside the system, in Arabic, English and French.

Illustration: 18 module tiles post into one balanced ledger, which produces a tax invoice with a QR code.

The problem

Most small and mid-sized Saudi businesses do not lack a system. They have four: an accounting package, a stock program, a payroll spreadsheet and a purchasing notebook. Each is right on its own, and no two of them agree. At month end someone reconciles them by hand.

Compliance makes it worse. The VAT return, ZATCA e-invoicing, GOSI and WPS are each handled somewhere else. And Arabic is usually a rushed translation with printing that breaks direction.

The goal: one system where every module posts to the same ledger, Saudi rules are part of the data model, and one codebase serves very different industries without forks.

What I did

  • Designed a schema-per-tenant model on PostgreSQL: each company is isolated, and all run from one codebase.
  • Built the finance core: double-entry ledger, cost centres, fiscal periods, and a trial balance that checks itself. Every document posts to the ledger automatically.
  • Built the tax engine: ZATCA tax codes with exemption reasons, VAT computed from ledger movement, tax invoices with QR, and Phase 2 clearance and reporting through a shared broker.
  • Designed the category engine (below): one setting decides tracking, costing and the ten accounts each item posts to.
  • Built Saudi payroll: salary components as formulas, GOSI for employee and employer, end-of-service provisions, WPS bank files, and posting to the ledger.
  • Built a report registry: each module declares its reports as data, and one shared reports screen reads them — 155 reports today, each with its own permission.
  • Built module control: switch modules on or off, with dependency checks. At sign-up, the company picks its activity and the system sets itself up.
  • Built three full interfaces — Arabic, English and French — with right-to-left support, one shared print renderer, A4 and thermal receipts, and approval workflows.

Architecture

  1. Clients
    • Web app · Vue 3 + Inertia
    • POS terminals
    • Print: A4 & thermal
  2. Core
    • Laravel API
    • Tenant resolver
    • Category engine (key component)
    • Accounting ledger
    • Report registry
  3. Data & integrations
    • PostgreSQL · schema per tenant
    • Queue workers
    • ZATCA broker
    • WPS bank files

The hard part: One category setting decides tracking, costing and accounts.

A pharmacy tracks batches and expiry dates. An equipment dealer tracks serial numbers. A wholesaler only needs quantity and value. Each one issues stock differently and posts to different accounts.

The easy path is a separate code branch per sector — and a separate product to maintain. Instead, every item belongs to a category. The category holds its behaviour (tracking type, issue policy, costing method, default tax code) and its ten ledger accounts. Receipt, issue, transfer and stock-count screens read the category at runtime and adapt their fields, checks and journal entries. A new sector is new settings, not new code.

Category Tracking What the screens do
Medicines Batch + expiry Ask for batch and expiry; issue first-in, first-out
Serialised equipment Serial number Track every unit by its serial, from receipt to sale
Goods, raw materials Standard Quantity and value only

Results

  • 18 modules on one ledger
  • 155 ready reports, plus 59 inquiry screens
  • 3 full interfaces: Arabic, English, French
  • 0 sector-specific forks
  • P2 ZATCA Phase 2 built in
  • 1 engineer, end to end

What I’d do differently

Build the category engine’s test matrix on day one. Every new category setting multiplies the cases you must check.

ERP dashboard in Arabic: live indicators for payables, overdue invoices, stock alerts and pending approvals.

Dashboard — live payables, overdue invoices, stock alerts and approvals waiting. Sample data. · 1 / 8