# AlRajhi Financial Model

A self-contained, multi-tenant web application that reproduces an Excel
financial-forecasting model for a steel / building-materials trading
company. Sign up, configure your business units, enter assumptions on a
9-step wizard, and the app produces an Income Statement, Cash Flow
Statement, Balance Sheet, Summary, Dashboard, and Sensitivity Analysis —
converting 3 years of historical actuals into a 5-year forecast under a
chosen scenario (Best / Base / Worst) and business unit.

All calculations run **server-side in PHP** (`src/Engine.php`), are **fully
deterministic**, and require **no external APIs, AI, or network access at
runtime**. Charts are rendered with a small dependency-free SVG chart
renderer (`public/assets/js/charts.js`) — no CDN dependency — so the app
works fully offline.

> The project directory is still named `alrajhi-forecast/` for historical
> path reasons; the product itself is **AlRajhi Financial Model** everywhere
> it's user-visible (page titles, the topbar brand, the login screen).

## What's new since the first build

- **Accounts.** Full sign-up / sign-in, each account has its own saved
  project (password hashing via `password_hash()`/`password_verify()`,
  PHP native sessions). A demo account (`demo@alrajhi.local` /
  `Demo@12345`) is auto-created on first run.
- **Configurable business units.** Step 1 of the wizard now asks "how many
  business units?" (1–10) and lets you name each one. Every downstream
  screen — actuals, drivers, revenue mix, outputs, sensitivity — works off
  that list. The "Total" option is always the automatic N+1 aggregate the
  engine computes; you never enter it directly.
- **Calendar-year-correct actuals.** Historical actuals are stored against
  the real calendar year you enter them for, not a fixed "1st/2nd/3rd"
  slot. Change the Start Year and the 3 actual-year columns re-align to
  whichever years are now "the actuals" — any year with no saved data is
  flagged with a warning banner instead of silently reusing another year's
  numbers.
- **Reconstructed actual-year Cash Flow & Balance Sheet.** Both statements
  now show 3 actual + 5 forecast columns (previously forecast-only). The
  actual columns are derived from your actual P&L inputs and the one real
  balance-sheet snapshot you enter (opening cash/AR/Inventory/AP/PP&E/
  retained earnings as of the last actual year), rolled backward through
  the same roll-forward logic the forecast uses. This is a reconstruction
  for display continuity, not an audited historical balance sheet — the
  UI says so explicitly.
- **Sensitivity Analysis.** A new output screen: Growth-rate and COGS%
  two-way tables (±5/10/20% deltas vs. EBITDA / Net Income / Ending Cash
  in the final forecast year), plus a full Best/Base/Worst comparison
  across all 5 years.
- **More dashboard charts.** Revenue by year, EBITDA & Net Income trend,
  Ending Cash, Revenue by Business Unit, Working Capital trend, Margin
  trend, and Cash Flow components by year (grouped bar) — all
  dependency-free SVG, all computed server-side.
- **Per-field guidance.** Every input step and most field groups now carry
  a one-line description of what the input does and which output line(s)
  it feeds.
- **Verified engine generalization.** `Engine::calculate()` no longer
  hard-codes the 4 AlRajhi business units — it takes the project's
  business-unit list as a parameter. New tests confirm the "Total" sector
  aggregates correctly for an arbitrary subset/count of units, and that
  Best/Base/Worst are deterministic and correctly ordered.

## Tech stack

- **Backend / engine**: PHP 8.1+, plain PSR-4 autoloading, no framework.
  `src/Engine.php` is a pure, unit-testable calculation engine with no I/O.
- **Auth**: `src/Auth.php` — PHP native sessions, `password_hash()` /
  `password_verify()`. No third-party auth service.
- **Database**: SQLite via PDO (`data/app.sqlite`), zero-config.
- **Frontend**: server-rendered PHP views + a small hand-written CSS system
  (`public/assets/css/app.css`) + vanilla JS for the wizard's live totals /
  scenario tabs / business-unit-count toggle, and for chart rendering. No
  Bootstrap/Chart.js CDN — this keeps the hard "no internet access required
  at runtime" constraint true even in fully offline environments.
- **Tests**: PHPUnit, `tests/EngineTest.php` — 31 tests / 169 assertions,
  including the full Section 10 acceptance numbers from the source Excel
  model reproduced to within ±1 SAR, plus generalization/determinism tests.
- **Routing**: a tiny front controller (`public/index.php`) with query-string
  routing (`?page=...`), no router library.

## Run instructions

```bash
composer install                 # installs PHPUnit (dev-only dependency)
php db/seed.php                  # creates data/app.sqlite + demo account
php -S localhost:8000 -t public  # start the app
# open http://localhost:8000 and sign in with demo@alrajhi.local / Demo@12345
# (or register a new account — it starts with the same seeded AlRajhi model)
```

Run the test suite (definition of done):

```bash
./vendor/bin/phpunit
```

Expected: `OK (31 tests, 169 assertions)`.

The database also auto-seeds on first request if no users exist yet, so
`php db/seed.php` is a convenience step, not a hard requirement.

## File layout

```
public/index.php            Front controller / router (query-string based)
public/assets/css/app.css   Hand-written styling (no CDN)
public/assets/js/wizard.js  Scenario tabs, live revenue-mix totals, BU-count toggle
public/assets/js/charts.js  Dependency-free SVG bar/line/area/grouped-bar chart renderer
src/Engine.php              PURE calculation engine (Section 6 of the spec, generalized to N business units)
src/Auth.php                Session-based login/logout helper
src/SeedMapper.php          Maps db/seed_data.php's flat seed into Engine's input shapes
src/Repository.php          SQLite read/write via PDO (users, projects, business units, actuals-by-year, drivers, ...)
src/Controller.php          Request handling: auth, wizard steps, outputs, sensitivity, CSV export
src/Formatter.php           Money/percent formatting helpers
src/View.php                Minimal template renderer
views/                      auth_*.php, inputs_step1..9.php, outputs_*.php, layout.php, partials
db/schema.sql               Table definitions (users, projects, business_units, actuals, drivers, ...)
db/seed_data.php            Full-precision seed data (single source of truth)
db/seed.php                 Seeds data/app.sqlite with a demo account + the default AlRajhi project
tests/EngineTest.php        PHPUnit tests (Section 10 acceptance + generalization + determinism)
```

## How the model works

1. **Sign up / sign in.** Each account gets its own project, auto-seeded
   with the AlRajhi default data so it's usable immediately.
2. **Inputs wizard** (9 steps):
   1. **Business Units** — how many product lines (1–10) and their names.
   2. **Project & Timeline** — Start Year, Scenario, Business Unit,
      zakat/depreciation constants.
   3. **Historical Actuals** — 3 years, keyed by real calendar year.
   4. **Growth & Mix Drivers** — per-unit growth by scenario, revenue mix.
   5. **Cost Drivers** — COGS/S&D/G&A % per unit, scenario, year.
   6. **Strategic Initiatives** — new-business revenue pool + multipliers.
   7. **Capex & Depreciation** — per-year capex for the selected unit.
   8. **Working Capital & Inflation** — AR/Inventory/AP days.
   9. **Opening Balances & Constants** — the one real historical
      balance-sheet snapshot, plus balance-sheet constants.

   Every field carries a one-line hint explaining what it does and which
   output line it feeds. Pressing **Calculate** (end of step 9) runs
   `Engine::calculate()` and opens the Dashboard.
3. **Outputs**: Dashboard, Income Statement, Cash Flow, Balance Sheet,
   Summary, Sensitivity. A toolbar at the top of every output screen lets
   you change Scenario / Business Unit / Start Year — each change re-runs
   the engine server-side and re-renders. CSV export and browser Print/PDF
   are available on every statement.
4. **Dynamic forecast year.** Changing Start Year `Y` re-derives which 3
   calendar years are "the actuals" (`Y-3, Y-2, Y-1`) and which 5 years are
   the forecast (`Y..Y+4`). Actual data is looked up by the real calendar
   year it was entered for — a year with no data yet is flagged, never
   silently mislabeled as a different year's numbers.
5. **Business units.** The model tracks however many units you configure
   (N, 1–10). Every BU-scoped input (actuals, drivers, mix) is collected
   per unit. Outputs offer an (N+1)th "Total" option that the engine
   computes automatically by aggregating across every configured unit —
   you never enter it directly.

## Acceptance tests

`tests/EngineTest.php` builds the engine from the seed data
(`db/seed_data.php`) with scenario = `best`, sector = `Trading Steel`,
start year = 2026, and asserts every Income Statement / Cash Flow /
Balance Sheet line from Section 10 of the original spec to within ±1 SAR,
plus cross-checks (`Total Revenue = Revenue + Initiatives`,
`Gross Profit = Total Revenue + Total COGS`, cash roll-forward from
opening balance) and the newer generalization/reconstruction tests:

- The "Total" sector correctly aggregates an arbitrary business-unit list
  (not just the hard-coded 4).
- The engine runs correctly with an arbitrary subset/count of business
  units.
- Best/Base/Worst are deterministic (same inputs ⇒ same output) and
  correctly ordered (Best ≥ Base ≥ Worst on total revenue).
- The reconstructed actual-year Cash Flow/Balance Sheet exactly reproduces
  the one real snapshot you enter (opening cash/PP&E/retained
  earnings/AR/Inventory/AP) and rolls backward consistently.

All 31 tests pass (169 assertions).

## Known model quirks (intentionally reproduced — see spec §13)

The following are faithful reproductions of the source Excel model's
behaviour, not bugs in this build. They are surfaced in the UI:

1. **Blended vs single-BU aggregation.** For a single business unit,
   growth/cost ratios are used directly. For "Total" (all configured units
   combined), growth and COGS% use a mix-weighted average (SUMPRODUCT)
   while S&D% and G&A% use a plain, unweighted average across units.
2. **The forecast Balance Sheet does not balance.** The Balance Check row
   (`Total Liabilities & Equity − Total Assets`) is large and non-zero in
   every forecast year — shown with a red "Forecast not balanced" badge.
   This reproduces the source model exactly; reconciling it is a future
   correction, not part of this build. (The reconstructed *actual*-year
   Balance Sheet is a separate, explicitly-labeled approximation and is
   also not expected to balance to the cent — see its footnote.)
3. **Zakat placement.** The Income Statement's Net Income equals EBT (its
   zakat line is blank); the Cash Flow Statement separately deducts a
   "Paid Zakat" line. Both behaviours are kept, including in the
   reconstructed actual-year Cash Flow.
4. **Financing double-counts Net Income** as "change in common equity" in
   the Cash Flow Statement; dividends are 0. Kept as-is.
5. **Forecast Total Liabilities excludes non-current liabilities** — the
   model drops that line after the first actual year. The
   reconstructed *actual*-year Balance Sheet does include non-current
   liabilities, since that exclusion is specific to the forecast quirk,
   not to historical presentation — this difference is called out on the
   Balance Sheet screen.

## Hard constraints honored

- No external APIs, AI, or third-party calculation services — every number
  comes from `src/Engine.php`.
- All calculations run server-side in PHP; the frontend only collects
  inputs and renders backend-computed results (including chart data, which
  is computed server-side and merely drawn client-side as SVG).
- Deterministic: identical inputs always yield identical outputs (enforced
  by the acceptance + determinism tests).
- No internet access required at runtime — CSS and JS are hand-written and
  bundled locally; there is no CDN dependency anywhere in the app.
- Money is SAR; percentages are stored and entered as fractions
  (0.08 = 8%), never mixed with whole-number percents.
