Claude/alshuyukh accounting saas uf09e2 - #26269
Closed
nwfynnaa99-cpu wants to merge 10 commits into
Closed
nwfynnaa99-cpu wants to merge 10 commits into
nwfynnaa99-cpu wants to merge 10 commits into
Conversation
Foundation for a Saudi multi-tenant accounting SaaS, built in a separate alshuyukh-accounting/ folder so the existing docs site is untouched. - PostgreSQL schema with migrations: tenants, settings, users, memberships, sessions, roles, permissions, companies, branches, warehouses, append-only audit log - Tenant isolation in three layers: app-level tenant filters, Row-Level Security under a non-privileged DB role, and composite (id, tenant_id) foreign keys - Auth: argon2id, short-lived JWT, rotating refresh token in an HttpOnly cookie with reuse detection, account lockout, CSRF header - RBAC catalog with 45 permissions and 9 system roles, custom roles, and privilege-escalation checks - Arabic RTL React frontend for the Phase 1 screens - 54 integration tests against a real database Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiHbDZ5HpzN6NRCMJmydEq
- Schema: fiscal years and periods (no overlaps), account groups, hierarchical chart of accounts, cost centers, journal entries and lines, gap-free numbering per fiscal year - Ledger rules enforced in the database as well as the API: balanced posting, open-period posting, postable active same-company accounts, posted entries immutable except for being marked reversed, no deletes - Engine: draft, post, reverse with linked correcting entry, year-end closing to retained earnings; works inside the caller's transaction so later documents (invoices, payments) post atomically - Default Saudi chart and current fiscal year created for every company; the engine finds accounts by system key, not code - Trial balance with opening, period, and closing columns - Arabic RTL screens: journal list, entry form with live balance, entry detail with post and reverse, chart, fiscal years, trial balance - Fixes found while testing: premature red borders on required inputs, error box crash on structured error details, save-and-post sending a draft - 40 new tests (94 total) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiHbDZ5HpzN6NRCMJmydEq
- Customers and suppliers share one implementation with separate tables, permissions, numbering, and control accounts - Saudi national address fields (ZATCA format) enforced in the database - Party balances come only from journal lines tagged with customer_id or supplier_id; the journal form offers a party picker on receivable and payable lines, which also covers opening balances - Products and services with units (UN/ECE codes), nested categories, 4-decimal unit prices, ZATCA VAT category, and optional account overrides checked by type; services can never track stock - Gap-free per-company document numbering, reused by invoices later - Accountant role can now manage customers and suppliers - Arabic screens for customers, suppliers, products, units, categories - 29 new tests (123 total) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiHbDZ5HpzN6NRCMJmydEq
- Six commercial documents share one implementation: sales quotes, invoices, returns (credit notes); purchase orders, invoices, returns (debit notes). Legal documents are numbered gap-free at issue - Each invoice and return posts its journal entry in the same transaction (AR/AP tagged with the party, revenue or inventory and expense per line, VAT output or input) - Server-side calculation module: line rounding, document VAT rounded once per category (ZATCA-consistent), VAT-inclusive prices, line discounts; the form previews totals through a calculate endpoint - Tax rates are data with effective dates; the current standard rate is seeded and can be changed without code - Returns are prorated from the original lines; the last return takes exactly what is left, so a full return nets the invoice to zero - Payments with configurable methods, allocation across invoices, advances, refunds, and voiding; settlement drives invoice status - Credit limit check, party snapshot, invoice kind (standard/simplified), cancellation by reversal, quote and PO conversion - Issued documents, their lines, and payments are protected by triggers - Fixes found while testing: decimal.js isPositive() is true for zero, pg DATE values shifted by the server time zone, section tabs resolving against the full URL, product form picking the wrong default unit - 46 new tests (169 total) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiHbDZ5HpzN6NRCMJmydEq
- Stock changes only through append-only stock movements; balances per product and warehouse are updated under a row lock in the same transaction, and can never go negative or hold value without quantity - Weighted-average cost per warehouse behind a costing interface (FIFO can be added as another implementation); the last unit out takes the exact remaining value, so values stay to the halala - Invoices, returns and purchases move stock as they are issued: sales add Dr COGS / Cr Inventory to the invoice entry, sales returns come back at the cost they left at, purchase returns leave at average cost with the price difference to COGS, stocked purchases always post to Inventory - Cancelling a document reverses its movements at the same costs; value left in an emptied warehouse is expensed with its own entry - Transfers between warehouses (no entry), adjustments and stock counts against an offset account (capital for opening stock), stock card, balances, and a valuation report reconciled with the Inventory account - Products show on-hand quantity; type, tracking and deletion lock once a product has stock history - Fixes found while testing: forms rendering before their option lists loaded picked wrong defaults (transfer destination); warehouse lists now put the main warehouse first - 19 new tests (188 total) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiHbDZ5HpzN6NRCMJmydEq
- Expense vouchers (EXP-) with categories linked to expense accounts and a default VAT treatment; cash/bank expenses post Dr expense + Dr VAT input / Cr payment method, credit expenses post to the supplier's payable and are settled by supplier payments allocated to the expense. - Drafts are editable; posted expenses are immutable and cancelled through a reversing entry. Cancellation is refused while payments are allocated. - Append-only tax_transactions ledger written by invoices, returns, purchase invoices and expenses, with negated rows on cancellation. - VAT return report (ZATCA boxes 1-16) reconciled against the VAT output and input accounts; unsupported boxes are reported as zero and listed. - Tax rate management in settings, expense pages, VAT return page, and expense allocation in supplier payments. - 15 new integration tests (203 total). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiHbDZ5HpzN6NRCMJmydEq
- Financial statements from posted journal lines: income statement, balance sheet with unclosed earnings, direct-method cash flow that reconciles opening + net = closing, general ledger with running balance, and journal report. Year-closing entries are excluded from income figures; reversals inherit their original's document type. - Receivables/payables aging reconciled per party to the ledger, with unapplied credits shown separately; customer and supplier statements. - Sales and purchase reports by party, product, month or document (net of returns, cost and gross profit from stock movements); expense report from expense accounts. - Dashboard KPIs and a 12-month series from the ledger, with SVG charts, hover tooltips and a table view. - New financial_report.view permission separates financial statements from operational reports held by sales, purchase and warehouse managers. - Reports UI with date presets, print styles and CSV export. - 17 new integration tests (220 total). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiHbDZ5HpzN6NRCMJmydEq
- EGS units per company or branch: secp256k1 key pair encrypted at rest (AES-256-GCM), PKCS#10 CSR with ZATCA's certificate template and SAN fields, compliance CSID via OTP, compliance checks for every document type, production CSID. - UBL 2.1 invoices in the KSA profile (standard/simplified invoices and credit notes; debit-note XML for compliance checks), invoice hash over C14N without signature and QR, ECDSA signature, XAdES block, phase-2 QR (tags 1-9). - Generation inside the issuing transaction: the unit row is locked, the document gets the next ICV and the previous hash; missing seller/buyer data or exemption codes reject the issue before anything is posted. The hash chain cannot fork (unique previous hash per unit). - Reporting and clearance with recorded submissions and validation messages, exponential backoff on transport failures, cleared XML kept, background submitter. E-invoiced documents can only be corrected with a credit note unless ZATCA rejected them. - Company national address, product exemption codes, e-invoicing page, e-invoice status on invoices, printable tax invoice with QR, phase-1 QR when no unit is active. - Tests use a stand-in gateway that issues real certificates via openssl and verifies every hash and signature. Not yet run against ZATCA's developer portal, simulation or production gateways. - 22 new tests (242 total). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiHbDZ5HpzN6NRCMJmydEq
- Plans with prices, trial and grace days and limits stored as data (edited by the platform administrator); a zero-price trial plan is the default for sign-ups. Subscriptions keep a price snapshot in items, and every change is an append-only billing event. - Subscription state derived from dates: TRIALING/ACTIVE, GRACE (full access), EXPIRED (read-only: writes refused with 402, sign-in and the subscription page stay available). - Limits enforced at creation of users, companies, branches, warehouses, products and at invoice issue, under a lock on the subscription row so concurrent requests cannot overshoot; monthly API calls metered in memory and flushed to usage_records. - /admin for platform administrators: overview and system health, organizations (create with temporary password, suspend/activate, change plan, record payment, extend, cancel, limit and feature overrides), users, subscriptions, plans, revenue and MRR, usage, platform and tenant audit logs, server errors, feature flags. - Cross-tenant reads through a read-only RLS policy active only when the admin guard sets app.platform_admin after re-checking the user flag; admin writes run in the target tenant's context. Admin actions are logged; 500 errors are recorded in system_errors. - Tenant subscription page and expiry/trial banners; CLI to grant platform access. - 16 new tests (258 total). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiHbDZ5HpzN6NRCMJmydEq
Security review fixes: - Trust X-Forwarded-For only from configured proxies (TRUST_PROXY) - Existing users join an organization only after accepting an invitation; the inviter cannot see their details before that - Sign-in lockout per user and IP, with the same 401 as a wrong password - Spreadsheet formula injection guard in CSV exports - Plan user limit enforced on reactivation and invitation acceptance - Managers cannot disable or edit members with more access than they hold - Separate RLS read/write policies for subscription and billing tables, triggers guarding tenant status and platform admin access - Statement timeout, 5-year report range limit, numeric overflow -> 400 - Cross-tab refresh coordination with the Web Locks API - Expense categories cannot point at cash, bank or control accounts Performance: general ledger and statements join document numbers once instead of a per-row subquery (2.3 s -> 53 ms on 20k ledger lines); lazy-loaded sections cut the initial bundle from 529 KB to 394 KB; self-hosted Arabic font. Load-test script in apps/api/scripts/perf.ts. Production: multi-stage non-root Docker images, docker-compose.prod.yml with a migration job and nginx (strict CSP, security headers), /api/ready readiness probe, backup and restore scripts, CI workflow, browser smoke test (npm run e2e) and an Arabic deployment guide. Tests: 272 API tests (91% statements), web unit tests, e2e smoke. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiHbDZ5HpzN6NRCMJmydEq
✅ Deploy Preview for docsdocker ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Related issues or tickets
Reviews