# AGENTS.md — Poultry Shop POS

Vanilla PHP/MySQL POS system. No framework, no Composer, no build tools, no test runner, no CI.

## Quick start

- **Serve:** XAMPP pointing to this dir. Import `database/schema.sql` (creates DB `poultry_shop` + 11 InnoDB tables + seed data).
- **BASE_URL:** Auto-detected in `includes/base_url.php` from `__DIR__` + `$_SERVER['DOCUMENT_ROOT']`. Works for any path — no config needed.
- **DB:** `includes/database.php` — `root` / no password, `poultry_shop`. PDO singleton via `getDB()`.
- **Logins (plaintext):** `admin` / `admin123`, `cashier` / `cashier123`.

## Entry points

- `index.php` — standalone (no standard includes). Requires `base_url.php`, starts own session, redirects to dashboard or login.
- `login.php` — no `auth_check.php` (unauthenticated). Plaintext comparison `$password === $user['password_hash']`. Sets `$_SESSION['user_id'|'user_name'|'user_role'|'last_activity']`.
- `logout.php` — destroys session.
- `pages/pos/pos_ajax.php` — JSON endpoint. Does own session start (no `auth_check.php`). Checks auth + CSRF only on `save_sale`.

## Protected page pattern

Every protected page includes in order:
1. `includes/auth_check.php` — starts session, checks login + 1h timeout (`time() - last_activity > 3600`), redirects to `login.php?expired=1`.
2. `includes/database.php` — `getDB()`.
3. `includes/functions.php` — 18 helpers.
   Then business logic (query, compute).
4. `includes/header.php` — `<head>`, topbar, flash messages via `setFlash()`/`flashMessage()`, sidebar via `includes/sidebar.php`. Uses `$page_title`.
5. `includes/footer.php` — `</body>`, loads DataTables, SweetAlert2, `sb-admin-custom.js`.

## Architecture

- **No routing.** Each page is a standalone `.php` file under `pages/module_name/`.
- **No server-side role gating.** `isAdmin()`/`isCashier()` only used in `header.php` for UI (badge color). Both roles can access all pages.
- **Module pattern:** `pages/module_name/index.php` = list/CRUD. Optional detail views (`view.php`, `invoice.php`, `ledger.php`, `create.php`, `manage.php`). Each module independent.

## POS flow (`pages/pos/`)

- `pages/pos/index.php` — frontend form with two-way calculator (weight ↔ amount) in `assets/js/pos.js`.
- `pages/pos/pos_ajax.php` — JSON endpoint. Actions: `get_rate` (rate + stock for chicken type), `today_rates` (all types), `search_customer`/`search_customers` (by name/phone), `save_sale` (inserts sale + stock_ledger + payment in transaction, requires CSRF).
- Stock checked before sale via `availableStock()`.
- Walk-in Customer (id=1 from seed) is default.

## Conventions

| Convention | Details |
|---|---|
| **CSRF** | `csrf_token()` / `verify_csrf()` — 64-char hex in `$_SESSION` |
| **Flash** | `setFlash()` / `flashMessage()` — auto-displayed in `header.php`, auto-dismiss after 4s |
| **Active nav** | `navActive()` (basename), `navActiveDir()` (URI segment), `isSectionActive()` (partial URI) |
| **XSS** | `sanitize()` = `htmlspecialchars(strip_tags(trim()))` on input; `htmlspecialchars()` on output |
| **Money** | `money($n)` → 2-decimal with thousands separator; `moneyRaw($n)` → no separator |
| **Invoice no.** | `generate_invoice_no()` → `INV-YYYYMMDD-NNNN` (auto-increment per day) |
| **Customer balance** | `getCustomerBalance(id)` = opening_balance + sales - payments |
| **Supplier balance** | `getSupplierBalance(id)` = opening_balance + purchases - payments |
| **Available stock** | `availableStock(type_id)` → `{birds, weight}` from stock_ledger (opening+purchase+adjustment - sale) |
| **Today's profit** | `todayProfit()` = revenue - avg purchase cost × weight sold - expenses |
| **DataTables** | Class `datatable` on `<table>`; init in `sb-admin-custom.js` with pageLength:25, stateSave:true |
| **Sidebar** | `#sidebarToggle`/`#sidebarToggleTop` toggles `.sidebar-toggled` on `<body>` — collapses to 80px icon-only |

## Modules

| Directory | Files | Purpose |
|---|---|---|
| `pages/dashboard/` | `index.php` | Landing page |
| `pages/pos/` | `index.php`, `pos_ajax.php` | POS calculator, save sale (AJAX) |
| `pages/customers/` | `index.php`, `create.php`, `ledger.php` | CRUD + balance |
| `pages/suppliers/` | `index.php`, `create.php`, `ledger.php` | CRUD + balance |
| `pages/sales/` | `index.php`, `view.php`, `invoice.php` | List + detail + printable invoice |
| `pages/purchases/` | `index.php`, `view.php` | List + detail |
| `pages/payments/` | `index.php` | Customer payments |
| `pages/supplier_payments/` | `index.php` | Supplier payments |
| `pages/expenses/` | `index.php` | Expense CRUD |
| `pages/stock/` | `index.php`, `summary.php`, `manage.php` | Ledger + summary + adjustments |
| `pages/chicken_types/` | `index.php` | Chicken type CRUD |
| `pages/chicken_rates/` | `index.php` | Daily rate CRUD per type |
| `pages/reports/` | 8 files | cash_book, bank_book, daily_sales, expense_report, stock_report, customer_ledger, supplier_ledger |

## Dependencies

All frontend via CDN (Bootstrap 5.3.3, jQuery 3.7.1, DataTables 1.13.7, SweetAlert2 11, Chart.js 4.4.1, Font Awesome 6.5.1). Custom CSS in `assets/css/sb-admin-custom.css` (`--primary: #059669`). Custom JS in `assets/js/pos.js`, `assets/js/sb-admin-custom.js`.

`package.json` has `bootstrap ^5.3.8` in `node_modules/` but no build step — not used at runtime.

## Notes

- Timezone: `Asia/Karachi` (set in `database.php` and `header.php`). Currency: PKR (Rs.).
- Passwords stored and compared as **plaintext** in `users.password_hash`.
- No `.gitignore`, no `.htaccess`, no URL rewriting, no `.env`.
