skills/peterbamuhigire/skills-web-dev/saas-accounting-system

saas-accounting-system

SKILL.md

Required Plugins

Superpowers plugin: MUST be active for all work using this skill. Use throughout the entire build pipeline — design decisions, code generation, debugging, quality checks, and any task where it offers enhanced capabilities. If superpowers provides a better way to accomplish something, prefer it over the default approach.

SaaS Accounting System

Overview

This skill implements a hidden accounting engine inside business applications. End users never see debits, credits, or journal entries — they enter sales, record payments, move inventory. Under the hood, every transaction auto-posts balanced journal entries that accountants and auditors can query at any time.

Philosophy: Users think in business terms. The system thinks in double-entry.

Core Architecture

┌─────────────────────────────────────────────────┐
│  USER LAYER (Friendly)                          │
│  Sales, Purchases, Payments, Inventory, Expenses│
│  → User sees: "Sale #1042 to Customer X: $500"  │
└──────────────────────┬──────────────────────────┘
                       │ auto-posts
┌──────────────────────▼──────────────────────────┐
│  ACCOUNTING ENGINE (Hidden)                     │
│  Journal Entries, Ledger Postings, Trial Balance│
│  → Engine posts: DR Accounts Receivable $500    │
│                  CR Sales Revenue      $500     │
└──────────────────────┬──────────────────────────┘
                       │ aggregates
┌──────────────────────▼──────────────────────────┐
│  REPORTING LAYER (Dual)                         │
│  User Reports: Sales Summary, Aging, P&L Simple │
│  Accountant Reports: Trial Balance, BS, IS, CF  │
└─────────────────────────────────────────────────┘

The Golden Rule

Every transaction MUST produce balanced journal entries. No exceptions.

SUM(debits) = SUM(credits)  — ALWAYS

This is enforced at the database level via stored procedure or trigger. If entries don't balance, the entire transaction rolls back.

Chart of Accounts (COA)

The COA is the backbone. Every account has a type that determines its normal balance.

Type Code Range Normal Balance Examples
Asset 1000-1999 Debit Cash, Bank, AR, Inventory, Equipment
Liability 2000-2999 Credit AP, Loans, Tax Payable, Unearned Revenue
Equity 3000-3999 Credit Owner's Equity, Retained Earnings
Revenue 4000-4999 Credit Sales, Service Income, Interest Income
COGS 5000-5999 Debit Cost of Goods Sold, Direct Materials
Expense 6000-6999 Debit Rent, Salaries, Utilities, Marketing

Setup rules:

  • Tenant gets a default COA on creation (seeded from template)
  • Accounts are tenant-scoped (franchise_id)
  • Accounts cannot be deleted if they have posted entries
  • Sub-accounts supported (e.g., 1100 Cash → 1101 Petty Cash, 1102 Main Bank)

See: references/chart-of-accounts.md

Auto-Posting Rules

Users never create journal entries manually. Every business transaction has a posting rule.

Transaction → Journal Entry Map

User Action Debit Account Credit Account
Record Sale (Invoice) Accounts Receivable Sales Revenue
Record Sale + Tax AR + Tax Receivable Sales Revenue + Tax Payable
Receive Payment (Cash) Cash/Bank Accounts Receivable
Record Purchase Inventory/Expense Accounts Payable
Pay Supplier Accounts Payable Cash/Bank
Record Expense Expense Account Cash/Bank or AP
Inventory Sale (COGS) Cost of Goods Sold Inventory
Stock Adjustment (+) Inventory Inventory Adjustment (Income)
Stock Adjustment (-) Inventory Adjustment (Expense) Inventory
Salary Payment Salary Expense Cash/Bank
Loan Received Cash/Bank Loan Payable
Loan Repayment Loan Payable + Interest Exp Cash/Bank
Depreciation Depreciation Expense Accumulated Depreciation
Customer Refund Sales Returns Cash/Bank or AR

Multi-line entries: A single sale with tax and discount creates 3+ journal lines, all in one entry. The entry MUST balance.

See: references/journal-posting-rules.md

Void & Reversal Mechanics

Rule: Never delete a posted journal entry. Always create a reversing entry.

Void Process

1. User clicks "Void" on a transaction (e.g., Sale Invoice #1042)
2. System marks the original transaction as VOIDED (status change)
3. System auto-creates a REVERSING journal entry:
   - Same accounts, opposite directions
   - Reference: "REVERSAL of JE-{original_id}"
   - Same date OR current date (configurable)
4. Original entry + reversal entry net to ZERO
5. All sub-ledger balances update automatically

Reversal Entry Example

Original (Sale Invoice #1042):
  DR  Accounts Receivable    500.00
  CR  Sales Revenue           500.00

Reversal (Void of #1042):
  DR  Sales Revenue           500.00
  CR  Accounts Receivable    500.00

Net effect: ZERO

Partial Void Rules

  • Partially paid invoices: Void remaining balance only
  • Payment already received: Must void payment first, then invoice
  • Inventory already delivered: Must reverse stock movement first
  • Cascade protection: System warns if dependent transactions exist

See: references/void-reversal-patterns.md

Database Schema (Core Tables)

-- Chart of Accounts
accounts (id, franchise_id, code, name, type, parent_id,
          is_active, normal_balance, created_at)

-- Journal Entries (Header)
journal_entries (id, franchise_id, entry_date, reference_type,
                 reference_id, narration, is_reversal, reversed_entry_id,
                 posted_by, status, created_at)

-- Journal Entry Lines (Detail)
journal_entry_lines (id, journal_entry_id, account_id,
                     debit_amount, credit_amount, narration,
                     franchise_id, created_at)

-- Fiscal Periods
fiscal_periods (id, franchise_id, period_name, start_date,
                end_date, status, closed_by, closed_at)

-- Account Balances (Materialized for performance)
account_balances (id, franchise_id, account_id, period_id,
                  opening_balance, debit_total, credit_total,
                  closing_balance, updated_at)

Integrity constraints:

  • journal_entry_lines.debit_amount and credit_amount are DECIMAL(15,2)
  • CHECK constraint: Each line has debit OR credit, never both, never both zero
  • Stored procedure validates SUM(debit) = SUM(credit) per entry
  • franchise_id on every table (multi-tenant isolation)
  • status enum: DRAFT, POSTED, VOIDED

See: references/schema-design.md

Dual Reporting System

User-Friendly Reports (Non-Accountant)

Report What User Sees Data Source
Sales Summary Total sales by period, customer, product Sales transactions
Outstanding Invoices Who owes what, how old AR sub-ledger
Expense Report Spending by category Expense transactions
Profit & Loss (Simple) Revenue minus expenses Income/expense accounts
Cash Position Money in bank/cash Cash/bank accounts
Inventory Value Stock on hand with cost Inventory sub-ledger

Accountant/Auditor Reports (Technical)

Report What It Shows Source
Trial Balance All account balances (DR/CR columns) General Ledger
Balance Sheet Assets = Liabilities + Equity GL (type 1-3)
Income Statement Revenue - COGS - Expenses = Net Income GL (type 4-6)
Cash Flow Statement Operating + Investing + Financing Cash account entries
General Ledger Detail Every entry per account Journal entries
Journal Register All journal entries chronologically Journal entries
Aged Receivables AR aging (30/60/90/120 days) AR sub-ledger
Aged Payables AP aging AP sub-ledger
Audit Trail Who posted what, when Journal entries + audit log

See: references/financial-statements.md

Implementation Checklist

Phase 1: Foundation (Must Complete First)

  • Create accounts table with COA seed data
  • Create journal_entries and journal_entry_lines tables
  • Create fiscal_periods table
  • Build balance validation stored procedure
  • Build postJournalEntry() service function
  • Write tests: balanced entry passes, unbalanced rejects

Phase 2: Auto-Posting Integration

  • Wire sales invoice → auto-post AR/Revenue entry
  • Wire payment received → auto-post Cash/AR entry
  • Wire purchase → auto-post Inventory or Expense/AP entry
  • Wire supplier payment → auto-post AP/Cash entry
  • Wire inventory movement → auto-post COGS/Inventory entry
  • Write tests: each transaction type posts correct entries

Phase 3: Void & Reversal

  • Build void transaction service (creates reversing entry)
  • Handle partial void scenarios
  • Handle cascade dependencies (warn before void)
  • Write tests: void produces net-zero, balances correct

Phase 4: Reporting

  • Build Trial Balance report (all accounts, DR/CR totals)
  • Build Balance Sheet (Assets = Liabilities + Equity)
  • Build Income Statement (Revenue - Expenses)
  • Build user-friendly summary reports
  • Build audit trail report
  • Write tests: reports match expected values from test data

Phase 5: Period Management

  • Build period open/close functionality
  • Prevent posting to closed periods
  • Build year-end closing entry (Revenue/Expense → Retained Earnings)
  • Write tests: closed period rejects entries

Cross-Skill Integration

Area Skill How It Applies
Database schema mysql-best-practices DECIMAL(15,2), indexes, FK constraints
API endpoints api-error-handling Consistent error responses for failed postings
Multi-tenancy multi-tenant-saas-architecture franchise_id on all tables
UI reports webapp-gui-design DataTables for ledger, charts for P&L
Mobile reports jetpack-compose-ui Report screens with tables
PDF export android-pdf-export, report-print-pdf Financial statement PDFs
Security vibe-security-skill Protect financial data, audit trail
Auth dual-auth-rbac Permission: who can post/void/view reports
Inventory link inventory-management COGS posting on stock movements
Testing sdlc-testing Test every posting rule, every reversal
Implementation plan-implementation Execute accounting phases with TDD
Audit implementation-status-auditor Verify accounting system completeness

Anti-Patterns

Don't Do Instead
Let users enter journal entries directly Auto-post from business transactions
Use FLOAT for money Use DECIMAL(15,2) always
Delete journal entries Create reversing entries (void)
Skip balance validation Enforce DR=CR in stored procedure
Store calculated balances only Store individual entries, calculate on demand
Mix accounting with business logic Separate accounting engine as its own service layer
Hard-code account codes Use configurable COA with tenant-specific accounts
Skip audit trail Log every posting with user, timestamp, IP
Allow posting to closed periods Enforce period status check before posting
Show debits/credits to end users Show friendly labels (Income, Payment, etc.)

Accuracy Guarantee

Every implementation MUST pass these tests:

  1. Balance test: SELECT SUM(debit) - SUM(credit) FROM journal_entry_lines = 0.00
  2. Entry test: Every journal entry has SUM(debit) = SUM(credit)
  3. Trial Balance test: Total debits = Total credits
  4. Balance Sheet test: Assets = Liabilities + Equity
  5. Reversal test: Voided transaction + reversal nets to zero
  6. Period test: No entries in closed periods
  7. Tenant test: No cross-tenant data leakage in any query

See Also

  • references/chart-of-accounts.md — COA templates and setup
  • references/journal-posting-rules.md — Complete posting rules per transaction
  • references/void-reversal-patterns.md — Void mechanics and edge cases
  • references/financial-statements.md — Report SQL queries and formats
  • references/schema-design.md — Complete database schema with constraints
Weekly Installs
16
GitHub Stars
3
First Seen
Feb 28, 2026
Installed on
opencode16
gemini-cli16
github-copilot16
codex16
amp16
cline16