# বেতন ক্যালকুলেটর — জাতীয় বেতনস্কেল ২০২৬ (Bangladesh)

A PHP 8.4 / MySQL-MariaDB salary calculator, ported **1:1** from the source
spreadsheet's formulas (sheet "১. ক্যালকুলেটর", rates from sheet
"২. গ্রেড, স্কেল ও ভাতা তথ্য"), plus one addition: an editable
**"রেভিনিউ স্ট্যাম্প চার্জ" (revenue stamp charge)** deduction, defaulting
to ৳10, included in the final deduction/net-salary summary.

No framework — a small hand-rolled MVC skeleton, raw PDO (wired up, not yet
required — see below), server-rendered pages that progressively enhance
into an AJAX calculator.

## Requirements
- PHP **8.2+** (target/tested: 8.3/8.4 — no version-specific syntax used)
- Apache with `mod_rewrite` (an `.htaccess` is included) — or Nginx/PHP-FPM,
  see below
- MySQL/MariaDB — **optional**, only needed once you build the next feature
  (see "Database" below); the calculator itself needs no database

## Quick start (local)
```bash
cd salary-calculator
cp .env.example .env
# generate a real secret:
php -r "echo bin2hex(random_bytes(32)), PHP_EOL;"
# paste it into .env as APP_KEY=...

php -S 127.0.0.1:8000 -t public
# open http://127.0.0.1:8000
```

## Deploying
Point your web server's **document root at `/public`** — every other folder
(`app`, `config`, `storage`, `.env`, `database`) must stay **outside** the
web root or be denied by your server config. `public/.htaccess` handles this
on Apache automatically; for Nginx use something like:

```nginx
root /path/to/salary-calculator/public;
index index.php;
location / {
    try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.4-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\. { deny all; }
```

`storage/` (logs + PHP session files) must be writable by the web server
user: `chmod -R 775 storage && chown -R www-data:www-data storage`.

## Project layout
```
public/               ← web root (ONLY this is exposed)
  index.php           ← front controller
  assets/css, assets/js
  .htaccess
app/
  Core/               ← App kernel, Router, Request/Response, Session,
                         Csrf, Honeypot, RateLimiter, Database (PDO), Logger, View
  Middleware/          SecurityHeaders, VerifyCsrf
  Controllers/          SalaryController
  Services/             SalaryCalculator (the formulas), SalaryInput (DTO), ValidationException
  Views/                layouts/, salary/, errors/
config/                app.php, security.php, database.php, salary_data.php (rates table)
database/migrations/   optional schema for a future admin/history feature
storage/               logs/, sessions/ (never web-accessible)
.env.example
```

## How the calculation maps to the spreadsheet
`app/Services/SalaryCalculator.php` re-implements every formula from
`১. ক্যালকুলেটর` cell-by-cell (each block has the source cell reference in
a comment, e.g. `// B33`), reading its lookup tables from
`config/salary_data.php`, which is a straight, generated dump of sheet
`২. গ্রেড, স্কেল ও ভাতা তথ্য` (grade scale, HRA %, medical/tiffin/mobile/
education/dholai rates, hill/haor/char allowance, workplace list, GPF/BF
rate lists). Verified against the workbook's own worked example
(Grade 10 / step 4 / ৳37,100 / other-area / bank): gross ৳53,235, net
(before the new stamp charge) ৳29,290 → ৳38,090 with lunch — the app
reproduces both, then subtracts the new ৳10 stamp charge to ৳29,280 /
৳38,080.

**Editing rates**: when the government revises grades/allowances, edit
`config/salary_data.php` directly (or regenerate it from an updated
spreadsheet — no code changes needed elsewhere).

## Security
- **XSS** — every dynamic value is escaped through the `e()` helper
  (`htmlspecialchars`, ENT_QUOTES); a strict `Content-Security-Policy`
  header (`script-src 'self'`, no inline `<script>`) is sent on every response.
- **CSRF** — a per-session token (`Csrf::token()`), verified with
  `hash_equals()` on every POST (`app/Middleware/VerifyCsrf.php`), for both
  the classic form post and the JSON endpoint (header `X-CSRF-Token`).
- **Session hijacking** — `app/Core/Session.php`: `HttpOnly` +
  `SameSite=Strict` + `Secure` (auto-detected HTTPS) cookies, a custom
  cookie name, `session.use_strict_mode`, a fingerprint (hashed
  UA + IP /24 + app key) checked every request — a mismatch destroys and
  restarts the session — periodic `session_regenerate_id()`, and
  server-enforced idle/absolute expiry (not just cookie lifetime).
- **Honeypot** — a CSS-hidden decoy field plus a minimum-elapsed-time check
  (`app/Core/Honeypot.php`); a bot submission is dropped silently.
- **Rate limiting** — a simple session-scoped sliding window
  (`app/Core/RateLimiter.php`) on the calculate endpoints.
- **SQL injection** — `App\Core\Database` only ever uses real prepared
  statements (`PDO::ATTR_EMULATE_PREPARES => false`); nothing in this app
  builds SQL by concatenation.
- **Server-side re-validation** — every input (grade, basic-pay step,
  workplace, rates, loan figures, etc.) is re-validated in
  `SalaryCalculator::validate()`; client-side dropdown/JS values are never
  trusted as-is.
- Errors are logged to `storage/logs/` (never shown to visitors); security
  headers (`X-Frame-Options: DENY`, `X-Content-Type-Options: nosniff`,
  `Referrer-Policy`, `Permissions-Policy`, HSTS over HTTPS) are sent on
  every response.

## Database (for the next feature)
`app/Core/Database.php` is a lazy PDO singleton, already reading
`config/database.php` / `.env` — nothing in the current calculator uses it.
`database/migrations/001_create_settings_and_calculations.sql` sketches two
tables (`settings` — e.g. an admin-editable revenue-stamp default;
`calculations` — an anonymous history log) for whenever you're ready to add
an admin panel, saved calculations, sharing links, etc. Run it with:
```bash
mysql -u root -p salary_calculator < database/migrations/001_create_settings_and_calculations.sql
```

## Extending
This is intentionally the "one feature" version you asked for. Natural next
additions, given the structure already in place:
- An admin screen backed by the `settings` table (grade scale, allowance
  rates, revenue stamp charge, etc. editable without a deploy)
- Income tax estimation (explicitly out of scope in the source sheet too)
- Save/share a calculation (`calculations` table + a signed short link)
- PDF/print export of the result card
- A REST-only mode (the JSON endpoint `/api/calculate` already exists)

## Note on the source spreadsheet
The uploaded workbook's own notes credit it to an individual author and
say commercial use is discouraged without permission. This build keeps
100% of the **calculation logic, rates and layout structure** (that's the
part you asked to replicate), but I left the personal author signature
block out of the generated site rather than copy someone else's name/
phone/email into a new codebase without their consent — swap in your own
branding in `app/Views/salary/index.php` and the footer.
