# Sylhet Model Madrasah ERP — Project Progress

> সিলেট মডেল মাদরাসা — এক আলোকিত প্রজন্মের প্রত্যাশায়
> Last updated: 2026-10-27 (Phase 13 — Integration, QA & Deployment Readiness: verified)

## 1. Stack (installed and verified)

| Component | Version | Notes |
|---|---|---|
| PHP | 8.2.33 (winget) | php.ini: `extension=gd`, `extension=intl` enabled |
| Composer | 2.10.3 | installed next to php.exe (`composer` on PATH) |
| Laravel | 12.70.1 | Blade + Vite |
| Livewire | 4.4 | newer major than originally planned v3; current for Laravel 12 |
| Alpine.js | bundled with Livewire | no separate install |
| Tailwind CSS | v4 (Vite plugin) | brand tokens in `resources/css/app.css` `@theme` |
| MySQL | 8.0.46 Community (portable zip) | no admin rights needed on this machine |
| spatie/laravel-permission | 6.25 | 10 roles + granular permission registry seeded (Phase 02) |
| spatie/laravel-activitylog | 4.12 | `activity_log` table migrated |
| Pest | 3.8 | replaces default PHPUnit runner |
| Fonts | @fontsource Poppins + Hind Siliguri | bundled locally — no runtime internet needed |

## 2. Local environment — how to run

**MySQL (portable, at `C:\Users\user\mysql-8.0.46-winx64`)**

```
# start (background)
C:\Users\user\mysql-8.0.46-winx64\bin\mysqld --defaults-file="C:/Users/user/mysql-8.0.46-winx64/my.ini" --console

# stop
C:\Users\user\mysql-8.0.46-winx64\bin\mysqladmin.exe -h 127.0.0.1 -u root -p shutdown
```

- Root credentials স্থানীয় মেশিনে প্রজেক্টের বাইরে নিরাপদে সংরক্ষিত; কখনো প্রজেক্ট/ডকুমেন্ট/চ্যাটে রাখা হয় না।
- App DB user: `smm_app` (password in project `.env`) — grants limited to `sylhet_model_madrasah` only (least privilege).
- Server binds `127.0.0.1:3306`, charset `utf8mb4`, timezone `+06:00`.

**Application**

```
cd C:\Users\user\Documents\Qoder\2026-10-22\41837388
php artisan serve --host=127.0.0.1 --port=8080   # port 8000 is blocked on this machine
npm run dev    # or: npm run build (production build is already committed)
php artisan test
```

## 3. Architecture decisions

1. Single `users` table + profile tables later; roles via spatie (no multi-guard). 
2. Locale: session-driven (`SetLocale` web middleware), default `en`, supported `['en','bn']`, switcher via `GET /locale/{locale}` + Livewire `LanguageSwitcher`. All UI strings via `lang/en`, `lang/bn` (full validation/auth/pagination/passwords translations included).
3. Settings: key-value table + cached `App\Services\Settings` (rememberForever + invalidation). Institution identity seeds from `config/brand.php`; DB values override config.
4. Brand tokens (maroon #6B1236, gold #D4A017, gradients, page/ink/muted colors, Poppins/Hind Siliguri) defined once in Tailwind `@theme` + `config/brand.php`.
5. Alpine state for sidebar collapse (persisted in localStorage) + dark mode (class strategy, no-FOUC head script).
6. Navigation rendered from `config/brand.navigation` filtered by `Route::has()` — sidebar grows automatically as phases add routes (no fake links).
7. Tests run on sqlite `:memory:` (phpunit.xml) — the MySQL dev database is never touched by tests.
8. Timezone: `Asia/Dhaka` everywhere (app config + MySQL).

## 4. Phase 01 — Foundation (completed 2026-10-22)

**Migrations run against dev DB only (`sylhet_model_madrasah`), 16 tables:**
users, password_reset_tokens, sessions, cache, cache_locks, jobs, job_batches, failed_jobs,
settings, activity_log, permissions, roles, model_has_permissions, model_has_roles, role_has_permissions (+migrations bookkeeping).

**Seeders:** `SettingsSeeder` only — real institution identity (name/slogan EN+BN) from approved spec. **No fake users/students/transactions.**

**Key files created**
- `config/brand.php`, `app/Services/Settings.php`, `app/Models/Setting.php`
- `app/Http/Middleware/SetLocale.php` (registered in `bootstrap/app.php`)
- `app/Http/Controllers/{DashboardController,LocaleController}.php`, `app/Livewire/LanguageSwitcher.php`
- `database/migrations/2026_10_22_120000_create_settings_table.php`, `2026_10_22_120100_create_activity_log_table.php`, `…/create_permission_tables.php` (published)
- `resources/css/app.css` (brand @theme, dark variant, Bangla font switching)
- `resources/views/components/`: app-layout, sidebar, sidebar-link, topbar, brand, breadcrumb, card, alert, empty-state, loading, skeleton, button, label, input, badge, modal, icon
- `resources/views/{dashboard, errors/*}.blade.php`, `resources/views/livewire/language-switcher.blade.php`
- `lang/{en,bn}/app.php` + `lang/bn/{validation,auth,pagination,passwords}.php`
- `public/favicon.svg` (generic book mark — **not** the official logo; awaiting institution logo file)
- `tests/`: BrandConfigTest, SettingsTest, CoreLayoutTest, LocaleSwitchingTest, MigrationsTest, Pest.php

**Verification**
- `php artisan test` → **21 passed, 81 assertions** (config/brand, palette/fonts, migrations+constraints, settings service+cache, layout, empty-states, EN↔BN switching incl. Livewire component, branded 404).
- `vendor/bin/pint` → clean (auto-fixed 4 files).
- Browser-verified (dev server): dashboard EN + BN, session locale persistence, localized date via intl (`বৃহস্পতিবার, 22 অক্টোবর 2026`), branded error page, no JS console errors.

## 5. Phase 02 — Authentication & Authorization (completed 2026-10-22)

### 5.1 Secure initial admin provisioning (IMPORTANT — no default password exists)

The system ships with **zero user accounts and no seeded passwords**. The first Super Admin must be provisioned from the server CLI:

```bash
php artisan make:superadmin admin@example.com
# → prompts for display name and a password (hidden input, min 10 chars, confirmation)
```

- `--name="…"` skips the name prompt; `--generate` creates a 16-char random password printed **once**.
- The command refuses to run if the email already exists (accounts are never silently upgraded), syncs the permission registry, and writes an `activity_log` entry.
- On this dev machine the generated credentials are stored locally only, outside the project folder (never committed). Production procedure: run the command on the server, hand the password to the administrator through a secure channel, and force a change on first login (planned: P07 hardening).
- Further Super Admins: an existing Super Admin creates them through Users → New user with the Super Admin role (UI blocks non-Super-Admins from selecting that role).

### 5.2 What was built

**Authentication flows (Livewire, guest layout)**
- `Login` (`/login`): email+password+remember; server-side throttle 5 attempts / 60 s per `email|IP` (login lockout message with remaining seconds); blocks `is_active=false` accounts at `Auth::attempt`; session regeneration on login; `last_login_at`/`last_login_ip` recorded; success/failure/throttle events to `activity_log`.
- `LogoutController` (`POST /logout`, auth-only): activity log, session invalidate + token regenerate.
- `ForgotPassword` (`/forgot-password`) and `ResetPassword` (`/reset-password/{token}`): Laravel password broker, policy `min:8 + letters + numbers` (`Password::defaults`), translated mail (`App\Notifications\ResetPasswordNotification` — EN/BN), never reveals whether an email exists.
- `ManageProfile` (`/profile`, auth): update name/email (unique except self), change password (verifies `current_password`, revokes all other sessions), "log out other sessions" (database session driver).
- `EnsureUserIsActive` web middleware: any request by a deactivated account is logged out immediately.

**RBAC (10 roles, permission registry)**
- `config/permissions.php`: module→actions registry (dashboard, users, roles, settings, audit) and default role→permission map for all 10 roles (Super Admin, Admin, Accountant, Teacher/Class Teacher, Office Staff, Librarian, HR/Payroll Manager, Parent/Guardian, Student, Read-Only User).
- `App\Services\PermissionRegistry::sync()` — idempotent DB sync + spatie cache invalidation (run by seeder and `make:superadmin`); `PermissionSeeder` + `RoleSeeder`.
- Super Admin bypass via `Gate::before` **scoped to permission-string abilities only** (`module.action`); policy checks (self-protection, IDOR) still apply to Super Admins.
- `App\Policies\UserPolicy` + `App\Models\User::isManageableBy()`: Super Admin accounts are manageable only by Super Admins; nobody toggles/deletes themselves; `updateRoles` requires `roles.edit` and refuses granting `super-admin` to non-Super-Admins (escalation guard, enforced again in the form component).
- Route protection: `auth` + `permission:` middleware (spatie aliases in `bootstrap/app.php`); guests redirected to `/login`; authenticated users redirected away from guest pages; dashboard additionally requires `dashboard.view`.

**User management & role matrix**
- `UsersManager` (`/users`, `users.view`): search (name/email), pagination, role badges, active toggle (`toggleActive` policy), delete (self-delete + IDOR blocked; sessions of deleted user revoked; audit entry with target id).
- `UserForm` (`/users/create`, `users.create`; `/users/{user}/edit`, `users.edit`): role checkboxes gated by `roles.edit`, Super Admin checkbox hidden/locked for non-Super-Admins, self-edit keeps the Super Admin role, crafted Livewire calls for role escalation return 403.
- `RoleMatrix` (`/roles`, `roles.view`; save requires `roles.edit`): role × permission grid, Super Admin row locked, changes audited (`role.permissions_updated`), registry synced on load.
- Sidebar navigation items carry a `permission` key and only render when the user holds it; topbar now has a user menu (profile + logout).

**Session/security settings**: `SESSION_DRIVER=database`, `SESSION_ENCRYPT=true` (dev `.env`); CSRF on; login throttle; password policy centralized.

### 5.3 Phase 02 verification

- `php artisan test` → **66 passed, 246 assertions**:
  - Login: success, wrong password, unknown email, deactivated account, throttle after 5 failures, guest redirects (direct URL access), authenticated redirect away from `/login`, logout, deactivated session killed on next request.
  - Password reset: link sent (Notification fake), no user enumeration, invalid token rejected, password policy, confirmation match, full reset changes hash.
  - Profile: auth gate, update info, duplicate email rejected, own email allowed, password change with correct/wrong current password, other sessions revoked, logout-others keeps other users' sessions.
  - RBAC: teacher/read-only blocked from `/users`, admin blocked from `/roles`, super-admin everywhere; IDOR (admin→super-admin edit 403, toggle/delete via crafted calls 403); viewer role renders list but actions 403; roles preserved when saving without `roles.edit`; super-admin grant denied (create + edit crafted calls); self super-admin role retained; role matrix edit by unauthorized role 403, super-admin save persists + audit row; user create with role, policy/unique/password validation; self toggle/delete 403.
- `vendor/bin/pint` → clean (7 files auto-fixed).
- Browser-verified (dev server, EN + BN): login page, real login with provisioned Super Admin, dashboard with new nav, users table, role matrix (Super Admin locked), profile page in Bangla, logout → login page.
- `make:superadmin` exercised on dev DB (`admin@sylhetmodelmadrasah.local`, credentials in the local credentials file only).

### 5.4 Fixes made during Phase 02 (for the record)

1. `PermissionRegistry::sync()` used spatie's cache-based `findOrCreate` → duplicate-key failure on reseed; replaced with `firstOrCreate` + explicit `forgetCachedPermissions()`.
2. `activity()->performedBy()` does not exist in activitylog v4 → `causedBy()` (+ target ids in properties).
3. `Gate::before` originally bypassed *all* abilities for Super Admins, which also bypassed self-protection policies; scoped to permission-string abilities (`module.action`) only.
4. `UserFactory` lacked `is_active` → in-memory NULL made `EnsureUserIsActive` log out `actingAs` users in tests; added `is_active => true` to the factory.
5. `UserForm::mount(?User $user = null)` — the container injected an empty `User` when the route had no binding, flipping create mode into edit mode; create mode now checks `! $user->exists`.
6. `UpdateRoles` policy now takes the requested role list to reject `super-admin` grants by non-Super-Admins.

## 6. Phase 03 — Students & Admissions (completed 2026-10-25)

### 6.1 অ্যাকাডেমিক কাঠামো (অ্যাডমিন-নিয়ন্ত্রিত)

- নতুন ১২টি মাইগ্রেশন: `academic_years`, `divisions`, `departments`, `academic_classes`, `sections`, `admission_applications`, `admission_guardians`, `students`, `student_guardians`, `documents`, `student_enrollments`, `student_transfers`।
- `DivisionSeeder` কেবল অনুমোদিত ছয়টি ডিভিশন (নূরানী, নাযারা, হিফয, বালিকা হিফয, সাধারণ, সান্ধ্য কুরআন কোর্স) সিড করে; **কোনো শ্রেণি/সেকশন/বিভাগ সিড করা হয় না** — অ্যাডমিন UI থেকেই তৈরি, সম্পাদনা, আর্কাইভ ও পুনরুদ্ধার করেন।
- Academic years: একটির বেশি `is_current` নেওয়া যায় না; চলমান বর্ষ আর্কাইভ করা যায় না; `student_serial` ও `application_serial` লকসহ (race-safe) বৃদ্ধি পায়।
- Classes/sections/departments: ইউনিক কোড, ডিভিশনের অধীনে শ্রেণি, শ্রেণির অধীনে সেকশন; সক্রিয় শ্রেণি থাকা অবস্থায় ডিভিশন আর্কাইভ করা যায় না; আর্কাইভড কাঠামো read-only।
- ড্যাশবোর্ডে বর্তমান একাডেমিক বর্ষের নাম, সময়কাল ও "চলমান" ব্যাজ যুক্ত হয়েছে; অ্যাকাডেমিক ডেটা না থাকলে সেটআপের নির্দেশনা দেখায় (দুটি নতুন টেস্টসহ)।

### 6.2 ভর্তি (Admissions)

- `APP-{YEAR}-0001` ফরম্যাটে ইউনিক আবেদন নম্বর, লকসহ সিরিয়াল; ডুপ্লিকেট রোল, ভুল শ্রেণি-ডিভিশন-সেকশন সমন্বয় এবং আর্কাইভড প্লেসমেন্ট প্রত্যাখ্যান করা হয়।
- ফর্ম: ইংরেজি/বাংলা নাম, লিঙ্গ, জন্ম তারিখ, রক্তের গ্রুপ, ফোন/ইমেইল, ঠিকানা, পূর্ববর্তী প্রতিষ্ঠান/শ্রেণি, ভর্তির তারিখ, একাডেমিক বর্ষ, ডিভিশন, শ্রেণি, সেকশন, রোল, নোট, ছবি এবং একাধিক অভিভাবক (একজন প্রধান, জরুরি যোগাযোগ)।
- অনুমোদনে `SMM-{YEAR}-0001` ইউনিক শিক্ষার্থী আইডি তৈরি হয়; অভিভাবক, ডকুমেন্ট ও প্রাথমিক নথিভুক্তি (enrollment) কপি হয়; অডিট লগ লেখা হয়। অনুমোদনের পর আবেদন সম্পাদনা বন্ধ; প্রত্যাখ্যানে কারণ বাধ্যতামূলক।
- ছবি: JPG/PNG সর্বোচ্চ 2 MB; ডকুমেন্ট: PDF/JPG/PNG সর্বোচ্চ 5 MB; PHP বা অস্বীকৃত ফাইল প্রত্যাখ্যাত; সব ফাইল private `local` ডিস্কে থাকে এবং অনুমোদিত ব্যবহারকারীর জন্যই স্ট্রিমিং রুটে প্রকাশ পায়।
- আসল প্রমাণপত্র ছাড়া কিছু সিড করা হয়নি; QA যাচাইয়ের ডেমো আবেদন/শিক্ষার্থী/ফাইল শেষে সম্পূর্ণ মুছে ফেলা হয়েছে।

### 6.3 শিক্ষার্থী জীবনচক্র

- অনুসন্ধানযোগ্য, ফিল্টারযোগ্য ও পেজিনেটেড শিক্ষার্থী তালিকা (আইডি, নাম, ফোন; অবস্থা, বর্ষ, ডিভিশন; সক্রিয়/আর্কাইভড)।
- প্রোফাইল: ছবি, জনতথ্য, অভিভাবক, ডকুমেন্ট, বর্তমান placement, enrollment ও transfer ইতিহাস।
- সম্পাদনা: জনতথ্য/ঠিকানা/অভিভাবক/ছবি/ডকুমেন্ট; placement শুধু promotion/transfer দিয়ে বদলায় — ইতিহাস অটুট থাকে।
- Promotion: পুরোনো enrollment বন্ধ হয় (`promoted`), নতুন enrollment তৈরি হয়; একই placement, ডুপ্লিকেট রোল বা নিষ্ক্রিয় শ্রেণি প্রত্যাখ্যাত।
- Transfer: enrollment বন্ধ, শিক্ষার্থীর অবস্থা `Transferred`, পরবর্তী promotion বন্ধ; প্রতিষ্ঠান/ঠিকানা/কারণসহ ইতিহাস সংরক্ষিত।
- Archive/restore: রেকর্ড মুছে ফেলা হয় না (archive-not-delete); আর্কাইভড শিক্ষার্থীর ডকুমেন্ট/অ্যাকশন নিয়ন্ত্রিত।
- ডকুমেন্ট ডিলিটে shared-file রেফারেন্স যাচাই — অন্য রেকর্ড ব্যবহার করলে ফাইল থাকে; ফটো/ডকুমেন্ট ডাউনলোড শুধু অনুমোদিত ব্যবহারকারীর জন্য (IDOR টেস্টসহ)।

### 6.4 রিপোর্ট, এক্সপোর্ট ও প্রিন্ট

- ভর্তি রিপোর্ট: মোট/অপেক্ষমাণ/অনুমোদিত/প্রত্যাখ্যাত সারসংক্ষেপ, ডিভিশনভিত্তিক সারসংক্ষেপ, অনুসন্ধান ও বর্ষ/ডিভিশন/অবস্থা ফিল্টার।
- CSV এক্সপোর্ট: UTF-8 BOM (Excel-এ বাংলা ঠিক দেখায়), ফর্মুলা-ইনজেকশন সুরক্ষা, ফিল্টার অনুযায়ী সারি, অডিট এন্ট্রি।
- প্রিন্ট ভিউ: ব্র্যান্ডেড শিরোনাম, ফিল্টার সারসংক্ষেপ, তৈরি-তারিখ ও "কতজন তৈরি করেছেন" ফুটার; অডিট এন্ট্রি।
- রিপোর্ট/এক্সপোর্ট/প্রিন্ট আলাদা permission (`admissions.view`, `admissions.export`, `admissions.print`) দিয়ে সুরক্ষিত।

### 6.5 নিরাপত্তা ও অনুমতি

- `config/permissions.php`-এ `academics.*`, `admissions.*`, `students.*` permission যোগ; Admin, Office Staff, Accountant, Teacher, Read-Only, Super Admin ইত্যাদি রোলে যথাযথ ম্যাপিং।
- প্রতিটি Livewire action-এ policy/authorize; route middleware; crafted Livewire call (IDOR/অননুমোদিত placement/approval) 403 দেয়।
- `StudentPolicy`, `AdmissionApplicationPolicy`, `DocumentPolicy`; শিক্ষার্থী/আবেদনের ছবি ও ডকুমেন্ট রুটে ownership/permission যাচাই।
- ফাইল আপলোডে mime/extension/size যাচাই; private storage; `no secrets in logs`; বাস্তব ক্রেডেনশিয়াল কোথাও লেখা/প্রিন্ট করা হয়নি।

### 6.6 Phase 03 verification

- `./vendor/bin/pest` → **106 passed (611 assertions)** — Phase 01/02-এর সব টেস্ট অটুট; নতুন ৪০টি টেস্ট:
  - Academic structure (11): division seed/CRUD/archive, class+section ownership, department, single current year, permission boundaries, dashboard current year।
  - Admissions (12): route permission, validation, placement consistency, duplicate roll, sequential APP numbers, guardian primary, edit lock, approval→SMM ID + guardians/documents/enrollment copy + audit, reject flow, upload security, photo/document access, list filters/pagination।
  - Student lifecycle (11): permissions, search/filter/archive, pagination, demographics+guardians edit, promote, invalid promotion rejection, transfer, archive/restore, document upload/delete + IDOR, shared-file safety, photo/document access।
  - Admission report (4): summary/filters, export/print permissions, CSV BOM + formula-injection guard + audit, print view + audit।
  - Dashboard (2): current year display + empty-state instruction।
- `./vendor/bin/pint` → clean (117 files)।
- ডেভ MySQL-এ ১২টি মাইগ্রেশন প্রয়োগ; ছয়টি ডিভিশন সিড; **কোনো ভুয়া শিক্ষার্থী/লেনদেন সিড করা হয়নি**।
- ব্রাউজার যাচাই (EN + BN): অ্যাকাডেমিক বর্ষ তৈরি/বর্তমান নির্ধারণ, ছয় ডিভিশন, শ্রেণি+সেকশন, বিভাগ, ভর্তি আবেদন (ছবি+PDF আপলোড), অনুমোদন→SMM-2026-0001, প্রোফাইল/তালিকা/সম্পাদনা, promotion প্যানেল, রিপোর্ট সারসংক্ষেপ, CSV (BOM+বাংলা হেডার), প্রিন্ট ভিউ, ড্যাশবোর্ডে চলমান বর্ষ, বাংলা ভাষা সুইচ।
- যাচাই শেষে QA অ্যাকাউন্ট, আবেদন, শিক্ষার্থী, ডকুমেন্ট, ফাইল, শ্রেণি/সেকশন/বিভাগ, বর্ষ ও সংশ্লিষ্ট অডিট এন্ট্রি সম্পূর্ণ মুছে ফেলা হয়েছে; ডেভ DB-তে শুধু আসল Super Admin ও ছয় ডিভিশন আছে।

### 6.7 যাচাইকালে পাওয়া ও সংশোধিত ত্রুটি

1. `AcademicYearsManager::setCurrent/archive`— Livewire-এর পুরোনো মডেল ইনস্ট্যান্সে `update()` no-op হয়ে সব বছর `is_current=false` হতে পারত; `refresh()` যোগ করা হয়েছে।
2. বেস `Controller`-এ `AuthorizesRequests` trait ছিল না — ছবি/ডকুমেন্ট ডাউনলোড রুটে `authorize()` fatal error; trait যোগ করা হয়েছে।
3. `.php` ফাইল ছবি হিসেবে আপলোড করলে Blade `temporaryUrl()`-এ crash করত; `isPreviewable()` গার্ড যোগ করা হয়েছে।
4. রিপোর্ট সারাংশে division join-এর কারণে `name_en` অ্যাম্বিগুয়াস SQL; সব কলাম qualify করা হয়েছে।
5. ড্যাশবোর্ডে "একাডেমিক বর্ষ কনফিগার হয়নি" স্থায়ী placeholder থাকায় বর্তমান বর্ষ দেখানো হতো না; controller + view + lang + টেস্ট যোগ করা হয়েছে।

## 7. Phase 04 — Academic Management (completed 2026-10-25)

### 7.1 শিক্ষাবর্ষ, টার্ম, বিষয় ও শ্রেণি-কারিকুলাম

- নতুন ১১টি মাইগ্রেশন: `terms`, `subjects`, `subject_progress_fields`, `class_subjects`, `rooms`, `timetable_periods`, `class_teachers`, `subject_allocations`, `timetable_entries`, `academic_calendar_events`, `student_subject_progress`; সঙ্গে `academic_calendar_events.title_bn` nullable সংশোধনী।
- **কোনো নমুনা শ্রেণি, সেকশন, বিষয়, শিক্ষক, রুম, পিরিয়ড বা শিক্ষার্থী সিড করা হয়নি** — আগের ছয়টি অনুমোদিত division ছাড়া বাকি সব অ্যাডমিন UI থেকে তৈরি, সম্পাদনা, আর্কাইভ ও পুনরুদ্ধার হয়।
- টার্ম: নির্বাচিত বর্ষের ভেতরে ইংরেজি/বাংলা নাম, তারিখ ও ক্রম; একই বর্ষে ওভারল্যাপিং টার্ম প্রত্যাখ্যাত; বর্ষসীমার বাইরে টার্ম অসম্ভব; আর্কাইভ/restore।
- বিষয়: ডিভিশনভিত্তিক ইউনিক কোড ও নাম, category (general/quranic/language/activity), assessment mode (marks/progress); progress mode-এ কনফিগারযোগ্য প্রোগ্রেস ফিল্ড। ব্যবহৃত বিভাগ, মোড ও ফিল্ড লক হয়; ব্যবহৃত বিষয় আর্কাইভ করা যায় না।
- শ্রেণি-কারিকুলাম (`/academics/classes/{class}/configuration`): বর্ষভিত্তিক বিষয় + সাপ্তাহিক পিরিয়ড, ডুপ্লিকেট নিষিদ্ধ, প্রতি বিষয়ে পিরিয়ড ১–`max_weekly_periods` (ডিফল্ট ৪০)-এর মধ্যে; allocated বিষয় সরানো যায় না; ব্র্যান্ডেড প্রিন্ট ভিউ ও অডিট।
- শিক্ষক নিয়োগ: প্রতি শ্রেণি+সেকশনে একজন class teacher এবং বর্ষভিত্তিক subject allocation (শ্রেণি+সেকশন+বিষয়+শিক্ষক, ডুপ্লিকেট slot নয়); শুধু `teacher` রোল; টাইমটেবিলে ব্যবহৃত allocation লকড।
- রুম ও পিরিয়ড: রুমের ধরন/ধারণক্ষমতা/অবস্থান, পিরিয়ডের সময় (শেষ > শুরু), ক্রম ও break ফ্ল্যাগ; ইউনিক নাম ও ক্রম; ব্যবহৃত রুম/পিরিয়ড আর্কাইভ করা যায় না।

### 7.2 টাইমটেবিল, দ্বন্দ্ব ও সুরক্ষা

- সাপ্তাহিক গ্রিড: বর্ষ+শ্রেণি+সেকশন ফিল্টার; দিন, পিরিয়ড, বিষয়, শিক্ষক ও রুম নির্বাচন করে এন্ট্রি তৈরি/সম্পাদনা/মুছে ফেলা (অডিটসহ)।
- একই শ্রেণি-সেকশন slot, একই শিক্ষকের দিন+পিরিয়ড এবং একই রুমের দিন+পিরিয়ড — তিন স্তরে প্রতিরোধ: service validation + DB unique index (`timetable_section_slot_unique`, `timetable_teacher_slot_unique`, `timetable_room_slot_unique`)।
- বিষয় অবশ্যই সেই শ্রেণির কারিকুলামে কনফিগারড এবং শিক্ষকের allocation থাকতে হবে; নিষ্ক্রিয় দিন/পিরিয়ড/শ্রেণি, ভুল সেকশন ও আর্কাইভড বর্ষ প্রত্যাখ্যাত; আর্কাইভড বর্ষের এন্ট্রি সম্পাদনা/মুছে ফেলা বন্ধ।
- প্রিন্ট: বর্ষ+শ্রেণি+সেকশনভিত্তিক ব্র্যান্ডেড টাইমটেবিল, `timetables.view` + `timetables.print` এবং `timetable.printed` অডিট।

### 7.3 একাডেমিক ক্যালেন্ডার

- ইভেন্টের ধরন (holiday/exam/event/meeting/other), ইংরেজি শিরোনাম (আবশ্যক), বাংলা শিরোনাম (ঐচ্ছিক — না থাকলে ইংরেজিতে fallback), তারিখ-পরিসর, ঐচ্ছিক টার্ম, স্থান ও নোট।
- ইভেন্ট বর্ষসীমার ভেতরে; টার্ম দিলে সেটি একই বর্ষের হতে হবে; টাইপ/টার্ম ফিল্টার; শুধু অনুমোদিত ব্যবহারকারী `calendar.print` দিয়ে ব্র্যান্ডেড প্রিন্ট (অডিটসহ)।
- ক্যালেন্ডার ইভেন্ট hard-delete হয় এবং প্রতিটি পরিবর্তন `academic_event.*` অডিটে থাকে — কাঠামোগত রেকর্ডের মতো archive নয়।

### 7.4 প্রমোশন, ক্লাস-ট্রান্সফার ও প্রোগ্রেস

- Batch promotion: source বর্ষ/শ্রেণি/সেকশন → target বর্ষ/শ্রেণি/সেকশন; target বর্ষ অবশ্যই source-এর পরে শুরু হতে হবে, প্রমোশনের তারিখ target বর্ষের ভেতরে হতে হবে; preview-তে নির্বাচিত শিক্ষার্থী ও roll দেখা যায়।
- Roll: ক্রমিক (target-এর সর্বোচ্চ সংখ্যাসূচক roll + 1) অথবা সংরক্ষিত roll; সংরক্ষিত roll ডুপ্লিকেট হলে সম্পূর্ণ batch প্রত্যাখ্যাত; পুরোনো enrollment `promotion` exit দিয়ে বন্ধ হয়ে নতুন enrollment তৈরি হয়; প্রতি শিক্ষার্থীর `student.promoted` ও batch-এর `promotion.batch_completed` অডিট।
- ক্লাস-ট্রান্সফার: একই academic year-এ অন্য শ্রেণি/সেকশনে স্থানান্তর; পুরোনো enrollment `class_transfer` exit, নতুন enrollment, placement validation; একই placement ও ভুল/আর্কাইভড গন্তব্য প্রত্যাখ্যাত।
- প্রোগ্রেস: শুধু progress-mode বিষয়ে; assessed date বছরের ভেতরে; প্রতিটি ফিল্ডের `max_value` অনুযায়ী মান সীমিত (কনফিগারযোগ্য সর্বোচ্চ ১০০০); খালি মান ডিলিট করে; শিক্ষক শুধু allocated শ্রেণি/সেকশনের জন্য, `academics.edit` থাকলে সব; প্রতিটি সংরক্ষণ `student.progress_recorded` অডিট।

### 7.5 অনুমতি, নেভিগেশন ও অডিট

- `config/permissions.php`-এ নতুন মডিউল: `timetables.*`, `calendar.*`, `promotions.*`, `progress.*`; `academics` মডিউলে `print` যোগ।
- Admin সব Phase 04 permission পায়; Office Staff promotions (view/create/print) ও timetable/calendar view; Teacher progress view/edit এবং timetable/calendar/academics view (promotions নয়); Read-Only সব view; Parent/Student কোনো Phase 04 write নয়।
- প্রতিটি স্ক্রিনের mount ও প্রতিটি write action-এ `authorize()`; print route-এ view+print দুটোই লাগে; আর্কাইভড বর্ষ/শ্রেণি/বিষয় service স্তরেও প্রত্যাখ্যাত।
- নতুন করিকুলাম, টাইমটেবিল ও ক্যালেন্ডার প্রিন্ট ভিউ ব্র্যান্ডেড; সব UI স্ট্রিং EN/BN; সাইডবার permission ছাড়া লিংক দেখায় না।

### 7.6 Phase 04 verification

- `./vendor/bin/pest` → **150 passed (922 assertions)** — Phase 01/02/03-এর সব টেস্ট অটুট; Phase 04-এ ৪৪টি নতুন/সম্প্রসারিত টেস্ট: terms/years (4), subjects ও class curriculum (6), teacher assignments (5), timetable conflicts (9), academic calendar (5), promotion/transfer (8), progress (5) + migration ও regression coverage।
- `./vendor/bin/pint --test` → PASS (211 files)।
- ডেভ MySQL-এ ১১টি নতুন মাইগ্রেশন ও `title_bn` সংশোধনী প্রয়োগ; **কোনো নমুনা শিক্ষাবর্ষ/শ্রেণি/বিষয়/শিক্ষার্থী সিড করা হয়নি**।
- ব্রাউজার যাচাই (EN + BN): টার্ম তৈরি, বিষয় + progress fields, শ্রেণি কারিকুলাম (২ বিষয়/১০ পিরিয়ড) ও প্রিন্ট, class teacher/subject allocation, রুম ও পিরিয়ড, টাইমটেবিল conflict প্রত্যাখ্যান, বাংলা শিরোনাম ছাড়া ক্যালেন্ডার ইভেন্ট ও প্রিন্ট, promotion preview ও সফল batch promotion, ২টি ফিল্ডে প্রোগ্রেস সংরক্ষণ, টাইমটেবিল প্রিন্ট এবং ভাষা সুইচ।
- যাচাই শেষে QA অ্যাকাউন্ট, শিক্ষার্থী, enrollment, প্রোগ্রেস, টাইমটেবিল এন্ট্রি, allocation, class teacher, class subject, ক্যালেন্ডার ইভেন্ট, progress field, বিষয়, সেকশন, শ্রেণি, রুম, পিরিয়ড, টার্ম, বর্ষ ও সংশ্লিষ্ট অডিট এন্ট্রি সম্পূর্ণ মুছে ফেলা হয়েছে; ডেভ DB-তে শুধু আসল Super Admin ও ছয়টি division আছে।

### 7.7 যাচাইকালে পাওয়া ও সংশোধিত ত্রুটি

1. ক্যালেন্ডার ইভেন্টে বাংলা শিরোনাম ছাড়া সংরক্ষণ করলে MySQL `title_bn` NOT NULL-এ 500 হতো (validation nullable থাকলেও migration নয়) — nullable migration, `localizedTitle()` fallback এবং regression টেস্ট যোগ করা হয়েছে।
2. `ClassConfigurationPrintController` রুটের `{class}` প্যারামিটারকে input হিসেবে validate করায় অনুমোদিত প্রিন্টও 302 হতো — এখন শুধু `year` validate হয় এবং `$request->route('class')` থেকে মডেল নেওয়া হয়।
3. ফুল স্যুটে মাঝে মাঝে `academic_years.code` ডুপ্লিকেট (যেমন 2025) ব্যর্থতা আসত — factory-র 2000–2099 রেঞ্জ টেস্টের নির্দিষ্ট বছরগুলোর (2024–2028) সঙ্গে মিলে যেতে পারত; factory রেঞ্জ 2100–2999 করা হয়েছে এবং স্যুট স্থিতিশীল হয়েছে।

## 8. Phase 05 — Attendance, Examinations & Results (completed 2026-10-25)

**নতুন মাইগ্রেশন (১০টি টেবিল, dev MySQL-এ প্রয়োগ হয়েছে):** `student_attendances`, `staff_attendances`, `grading_scales`, `grade_bands`, `exams`, `exam_schedules`, `exam_marks`, `exam_results`, `hifz_lesson_types`, `hifz_progress_entries`।

**Services:** `AttendanceService`, `ExamService`, `GradingService`, `HifzService` — সব write `DB::transaction`-এর ভেতরে; year/class/section scope ও archived-guard service স্তরেই যাচাই হয়।

### 8.1 শিক্ষার্থী ও কর্মী উপস্থিতি

- শিক্ষার্থী রেজিস্টার: বর্ষ → শ্রেণি → সেকশন → তারিখ; Present/Absent/Late/Leave; একই শিক্ষার্থীর একই দিনে একটির বেশি রেকর্ড DB uniqueness-তে বাধা পায়।
- প্রথম সংরক্ষণের পর যেকোনো পরিবর্তনে করেকশন কারণ বাধ্যতামূলক; প্রথম রেকর্ড `attendance.student_recorded` এবং করেকশন `attendance.student_corrected` হিসেবে আলাদা অডিট হয় (আগের ও নতুন স্ট্যাটাসসহ)।
- মাসিক সারসংক্ষেপ: উপস্থিত দিন, অনুপস্থিত/বিলম্ব/ছুটি ও শতাংশ; `attendance.summary` স্ক্রিন, CSV export এবং ব্র্যান্ডেড A4 প্রিন্ট (দৈনিক শিট ও মাসিক সারসংক্ষেপ)। প্রিন্ট রুটে `attendance.view` + `attendance.print` দুটোই লাগে এবং প্রিন্ট নিজেও অডিট হয়।
- কর্মী উপস্থিতি: সব সক্রিয় employee অ্যাকাউন্টের দৈনিক রেজিস্টার, Mark-all, মাসিক সারসংক্ষেপ, export/print; `staff_attendance.*` আলাদা পারমিশন সেট। Admin ও HR Manager পূর্ণ নিয়ন্ত্রণ, Read-Only শুধু দেখতে পারে।

### 8.2 পরীক্ষা, নম্বর ও ফলাফল

- Exam: ধরন (class test/terminal/half yearly/annual/other), বর্ষ-সীমার ভেতরে তারিখ, grading scale (ঐচ্ছিক — না দিলে default), archive/restore; সব পরিবর্তন `exam.created`/`exam.updated`/`exam.archived`/`exam.restored` অডিটে থাকে।
- সূচি (schedule): প্রতি পরীক্ষা × শ্রেণি × সেকশন × বিষয়ের জন্য তারিখ, শুরু/শেষ সময়, কক্ষ, সর্বোচ্চ ও পাস নম্বর; একই scope-এ ডুপ্লিকেট সূচি প্রত্যাখ্যাত। নম্বর এন্ট্রি হয়ে গেলে সূচির শ্রেণি/সেকশন/বিষয় বদলানো বা সর্বোচ্চ নম্বর বর্তমান নম্বরের নিচে নামানো যায় না; নম্বর থাকলে সূচি ডিলিটও বন্ধ (`exam.schedule_deleted` কেবল নম্বরহীন সূচিতে)।
- নম্বর এন্ট্রি: subject maximum ও pass marks UI-তে দেখায়; 0–maximum-এর বাইরে মান প্রত্যাখ্যাত; absent চেকবক্সে নম্বর ছাড়া সংরক্ষণ হয়; খালি ঘর পুরোনো নম্বর মুছে দেয়; একই সূচিতে ডুপ্লিকেট শিক্ষার্থী প্রতিরোধ; সংরক্ষণ batch আকারে `exam.marks_saved` অডিটসহ।
- গ্রেডিং: কনফিগারেবল স্কেল (GPA/Percentage), ধারাবাহিক grade bands (0–100, ফাঁক/ওভারল্যাপ নিষিদ্ধ), grade letter/point/remark EN+BN; ডিফল্ট স্কেল নির্ধারণযোগ্য; archive/restore এবং `grading.scale_saved`/`archived`/`restored` অডিট।
- ফলাফল: প্রকাশের আগে সব সূচির প্রতিটি শিক্ষার্থীর নম্বর বা absent Entry থাকতে হয় (Mark completeness 6/6 ধরনের সূচক); প্রকাশে subject-wise pass/grade, মোট, শতাংশ, GPA (grade points-এর গড়), failed subjects, merit position (শুধু পাস শিক্ষার্থী) ও pass/fail গণনা করে `exam_results`-এ সংরক্ষণ করে এবং পরীক্ষা lock হয় (`exam.published`)।
- প্রকাশের পর Marks Entry স্ক্রিন read-only হয়ে যায় ("Marks are locked...") এবং service স্তরেও নম্বর পরিবর্তন প্রত্যাখ্যাত; সংশোধনে ফলাফল unpublish করতে হয় (`exam.unpublished`, গণনা করা ফলাফল মুছে যায়)।
- প্রিন্ট/এক্সপোর্ট: subject-wise marks sheet, merit list, শিক্ষার্থীভিত্তিক report card (বিষয়-গ্রেড, GPA, position, result) A4 ব্র্যান্ডেড প্রিন্ট এবং ফলাফলের CSV export (UTF-8, BOM-সহ); প্রতিটি প্রিন্ট/export অনুমোদিত পারমিশনসহ অডিট হয়।

### 8.3 হিফজ/নজরা অগ্রগতি

- কনফিগারেবল lesson type (Sabaq/Sabqi/Manzil ইত্যাদি) — শ্রেণি (memorization/revision/test/other), ক্রম, নাম EN+BN; archive/restore; নাম ডুপ্লিকেট নিষিদ্ধ।
- শিক্ষার্থীভিত্তিক dated এন্ট্রি: একাডেমিক বর্ষ, বিষয় (progress-mode), lesson type, তারিখ, memorization From/To + lines, revision From/To + lines, progress %, শিক্ষকের মন্তব্য; অন্তত একটি রেঞ্জ বা progress % বাধ্যতামূলক; From দিলে To বাধ্যতামূলক; তারিখ বর্ষ-সীমার ভেতরে ও ভবিষ্যতে নয়।
- শিক্ষক শুধু নিজের allocated শ্রেণি/সেকশন/বিষয়ের এন্ট্রি দেখতে ও করতে পারেন; Admin পূর্ণ অ্যাক্সেস; এন্ট্রি সংরক্ষণ/সম্পাদন/archive/restore `hifz.entry_created`/`updated`/`archived`/`restored` অডিটে থাকে।
- Paginated dated history + সারসংক্ষেপ (মোট এন্ট্রি, memorization/revision lines, সর্বশেষ progress, সর্বশেষ তারিখ); ব্র্যান্ডেড student progress print (শিক্ষক/প্রিন্ট অনুমতিসহ)।

### 8.4 অনুমতি, নেভিগেশন ও ভাষা

- `config/permissions.php`-এ নতুন মডিউল: `attendance.*`, `staff_attendance.*`, `exams.*`, `marks.*`, `hifz.*`; `permissions` মোট ৭০টি।
- Admin সব Phase 05 permission পায়; Teacher attendance ও marks/hifz (view/create/edit) এবং exams view; Office Staff attendance full + exams/marks/hifz view/export/print; HR Manager staff attendance full; Read-Only সব view; Accountant/Librarian/Parent/Student কোনো Phase 05 write নয়।
- প্রতিটি স্ক্রিনের mount ও প্রতিটি action-এ `authorize()`; marks/hifz-এ subject allocation অনুযায়ী scope; print route-এ view+print দুটোই লাগে।
- সব নতুন UI স্ট্রিং EN/BN উভয় ভাষায়; সাইডবারে নতুন লিংক (Attendance, Examinations, Hifz/Nazara, Staff attendance) permission অনুযায়ী দেখায়; exam/attendance/hifz সাব-নেভিগেশন যোগ হয়েছে।

### 8.5 Phase 05 verification

- `./vendor/bin/pest` → **181 passed (1232 assertions)** — Phase 01–04-এর ১৫০টি টেস্ট অটুট; Phase 05-এ ৩১টি নতুন টেস্ট: attendance uniqueness/correction/print permissions (9), exam service: grading calculations, marks validation, publication lock, merit rules (7), exam UI workflow ও permissions (8), Hifz history/teacher scope/audit (7)।
- `./vendor/bin/pint --test` → PASS (273 files)।
- ডেভ MySQL-এ ১০টি নতুন মাইগ্রেশন প্রয়োগ; **কোনো নমুনা পরীক্ষা/উপস্থিতি/হিফজ ডেটা সিড করা হয়নি**।
- ব্রাউজার যাচাই (EN + BN): শিক্ষার্থী উপস্থিতি সংরক্ষণ → করেকশন কারণ validation → সংশোধিত সংরক্ষণ → মাসিক সারসংক্ষেপ ও দৈনিক শিট প্রিন্ট; কর্মী উপস্থিতি রেজিস্টার ও মাসিক সারসংক্ষেপ; lesson type তৈরি/সম্পাদন/archive/restore, হিফজ এন্ট্রি তৈরি/সম্পাদন/archive/restore ও প্রিন্ট; পরীক্ষা তালিকা, grading scale সম্পাদনা ও band-coverage validation, marks entry maximum validation (105 প্রত্যাখ্যাত), absent এন্ট্রি, ফলাফল প্রকাশ (মেরিট/জিপিএ যাচাই), প্রকাশের পর নম্বর লক, marks sheet/merit list/report card প্রিন্ট এবং CSV export; ইংরেজি ও বাংলা উভয় ভাষায় UI যাচাই।
- যাচাই শেষে QA বর্ষ, শ্রেণি, সেকশন, বিষয়, শিক্ষার্থী, পরীক্ষা, সূচি, নম্বর, ফলাফল, grading scale, lesson type, হিফজ এন্ট্রি, উপস্থিতি, QA অ্যাকাউন্ট ও সংশ্লিষ্ট অডিট এন্ট্রি সম্পূর্ণ মুছে ফেলা হয়েছে; ডেভ DB-তে শুধু আসল Super Admin ও ছয়টি division আছে।

### 8.6 যাচাইকালে পাওয়া ও সংশোধিত ত্রুটি

1. Hifz lesson type-এর ক্যাটাগরি ড্রপডাউনে raw translation key (`app.enums.hifz_lesson_category.*`) দেখাচ্ছিল — `lang/{en,bn}/app.php`-এ কী-এর নাম `hifz_category` থেকে `hifz_lesson_category`-এ সংশোধন করা হয়েছে (`HifzLessonCategory::label()`-এর প্রত্যাশার সঙ্গে মিলিয়ে)।
2. Pest টেস্ট ফাইলের global-scope helper ফাংশন protected trait method (`makeUserWithRole()`) কল করতে পারছিল না — helper-গুলো `beforeEach` ক্লোজারে স্থানান্তর করে TestCase binding নিশ্চিত করা হয়েছে (পরীক্ষা زیرساخت, পণ্য নয়)।
3. শুধু `assertDontSee('Nazara')` দিয়ে subject-filter যাচাই ভুলভাবে fail করছিল, কারণ সাইডবারের aria-label-এ "Hifz / Nazara" সবসময় থাকে — assertion নির্দিষ্ট `<option>` টেক্সটে সীমিত করা হয়েছে।

## 9. Phase 06 — Fees & Student Collections (completed 2026-10-25)

**নতুন মাইগ্রেশন (১২টি টেবিল, dev MySQL-এ প্রয়োগকৃত):** `fee_heads`, `fee_structures`, `fee_structure_items`, `fee_waivers`, `fee_invoices`, `fee_invoice_items`, `fee_payments`, `fee_refunds`, `ledger_accounts`, `journal_entries`, `journal_lines`, `number_sequences`।

**Services:** `FeeStructureService`, `FeeInvoiceService`, `FeePaymentService`, `FeeRefundService`, `FeeWaiverService`, `FeeLedgerService`, `FeeReportService` — প্রতিটি আর্থিক write `DB::transaction`-এর ভেতরে; invoice/receipt/refund numbering locked sequence থেকে; journal entry ও line মডেল-স্তরে immutable।

### 9.1 ফি সেটআপ ও ইনভয়েস

- Fee head: ইউনিক কোড, EN/BN নাম, billing category (monthly/one-time/exam), ledger-mapped type (tuition/admission/examination/other), active/sort; active structure-এ ব্যবহৃত head archive করা যায় না, restore-এর সুযোগ আছে।
- Fee structure: একাডেমিক বর্ষভিত্তিক, division-wide অথবা class/section-specific; effective date অনুযায়ী প্রযোজ্য কাঠামো নির্বাচিত হয়; class-specific কাঠামো division-wide-কে override করে; প্রতিটি head-এর amount সহ structure items।
- মাসিক ইনভয়েস: বর্ষ → শ্রেণি → সেকশন → billing period (YYYY-MM); একবারের fee head বর্ষে একবার; invoice due day = মাসের ১০ তারিখ (`fees.due_day`)।
- পরীক্ষা ইনভয়েস: নির্দিষ্ট exam-এর schedule-ভুক্ত শ্রেণি/সেকশনের শিক্ষার্থীদের জন্য; fee বকেয়া হয় পরীক্ষার শুরুর তারিখে; schedule না থাকলে কোনো ইনভয়েস তৈরি হয় না।
- প্রতিটি invoice-এ subtotal, approved waiver discount, total, paid, refunded, balance ও status (issued/partially_paid/paid/cancelled); invoice prefix `INV`, receipt `RCPT`, refund `RFD`, journal `JRN` — মাসিক sequence।
- Duplicate প্রতিরোধ: monthly key `monthly:{student}:{period}`, exam key `exam:{exam}:{student}` এবং DB unique `generation_key`; preview-এ existing/ready গণনা দেখানো হয়, পুনরায় generate করলে already issued skip হয়।

### 9.2 কালেকশন, আংশিক পেমেন্ট ও রিফান্ড

- অনুমোদিত অফলাইন মাধ্যম: cash, bank, manual; staff-এর gateway payment এন্ট্রির সুযোগ নেই।
- Cash তাৎক্ষণিক verified; bank/manual pending থাকে যতক্ষণ `fees.verify` পারমিশনধারী যাচাই না করেন। Pending payment available balance reserve করে কিন্তু invoice balance কমায় না; verification-এ আবার balance যাচাই হয়।
- আংশিক পেমেন্ট, একাধিক পেমেন্ট, overpayment guard ও idempotency key — একই request দুইবার রেকর্ড হয় না; locked receipt sequence duplicate receipt number প্রতিরোধ করে।
- Refund শুধু verified payment-এর বিপরীতে, refundable amount-এর সীমার মধ্যে, বাধ্যতামূলক কারণে; refund হলে invoice-এর refunded বাড়ে এবং balance = total − paid + refunded। Refund নিজেও ledger reversal journal পোস্ট করে।
- কখনোই "সফল" পেমেন্ট ভুয়া রেকর্ড হয় না: গেটওয়ে callback ছাড়া অথবা signature/amount যাচাই ছাড়া online payment verified হয় না।

### 9.3 Waiver, লেজার ও অডিট

- Waiver request → approve/reject workflow; type percentage/fixed, fee head, বর্ষ, তারিখ-সীমা, কারণ; শুধু approved waiver billing/exam তারিখে প্রয়োগ হয় এবং discount হিসেবে 4090-এ পোস্ট হয়; approve/reject `fees.waiver_*` অডিটে থাকে।
- Double-entry ledger: 1010 Cash, 1020 Bank/Mobile, 1100 Receivables, 4010 Tuition, 4020 Admission, 4030 Examination, 4040 Other, 4090 Discounts; invoice issue, verified payment, refund ও waiver discount-এর journal সর্বদা balanced।
- JournalEntry/JournalLine মডেল immutable; প্রতিটি posting `fees.journal_posted` অডিটে থাকে; reconciliation স্ক্রিন invoice total, outstanding, collections, income ও discounts লেজারের সঙ্গে মিলিয়ে দেখায়।

### 9.4 রিপোর্ট, প্রিন্ট, এক্সপোর্ট ও গেটওয়ে

- Collections (তারিখ-রেঞ্জ, verified/pending/refund/net), Arrears (outstanding, overdue, not yet due, 1–30/31–60/60+ aging), Student statement (opening, charges, payments, refunds, closing; pending payments আলাদা) — সব ফিল্টারযোগ্য।
- A4 ব্র্যান্ডেড প্রিন্ট: invoice (paid/refunded/balance), receipt, arrears report, student statement; CSV export UTF-8 BOM সহ collections/arrears/statement; প্রতিটি print/export আলাদা permission ও audit event।
- Payment gateways পৃষ্ঠা bKash, Nagad, Rocket, Card, SSLCommerz-এর readiness দেখায় (default: Not ready), শুধু environment variable-এর নাম দেখায় — মান কখনো UI-তে দেখানো হয় না। Credentials, sandbox verification, signed callback ও reconciliation সব সম্পন্ন না হওয়া পর্যন্ত গেটওয়ে নিষ্ক্রিয়।

### 9.5 অনুমতি, নেভিগেশন ও ভাষা

- নতুন মডিউল: `fee_heads.*`, `fee_structures.*`, `fee_waivers.*`, `fees.*`, `fee_reports.*`, `fee_gateways.view` — মোট ২৩টি নতুন permission, permissions মোট ৯৩টি।
- Admin পূর্ণ ফি নিয়ন্ত্রণ; Accountant head/structure/waiver/invoice/payment/refund/report সবই পারে (invoice delete ছাড়া); Teacher সীমিত billing/collection; Read-Only শুধু view; Parent/Student-এর কোনো ফি write নেই; প্রতিটি mount/action-এ `authorize()` এবং print route-এ view+print দুটোই।
- সাইডবারে Fees সাব-নেভিগেশন (Invoices, Collections, Waivers, Fee heads, Structures, Arrears, Statement, Reconciliation, Gateways) permission অনুযায়ী; সব UI স্ট্রিং EN/BN, বাংলায় রিপোর্ট/প্রিন্ট লেবেল যাচাইকৃত।

### 9.6 Phase 06 verification

- `./vendor/bin/pest` → **232 passed (1669 assertions)** — Phase 01–05-এর ১৮১টি টেস্ট অটুট; ৫১টি নতুন টেস্ট: fee setup (7), invoice generation (8), payment workflow (8), refunds (7), ledger reconciliation (6), reports (5), UI workflow/permissions (8) এবং নতুন fee/ledger migration assertions (2)।
- `./vendor/bin/pint --test` → PASS (343 files; Phase 06-এর ৫টি ফাইলে style auto-fix)।
- ডেভ MySQL-এ ১২টি নতুন মাইগ্রেশন ও `LedgerAccountSeeder` প্রয়োগ; `PermissionSeeder`/`RoleSeeder` সিঙ্কে ২৩টি fee permission ও role mapping যুক্ত; **কোনো নমুনা শিক্ষার্থী, ইনভয়েস বা লেনদেন সিড করা হয়নি**।
- ব্রাউজার যাচাই (EN + BN): TUITION/ADMISSION/EXAM head ও ১৫০০/৫০০/৩০০ structure তৈরি; অক্টোবর মাসিক ইনভয়েস ২টি × ২০০০; cash ৮০০ verified (balance ১২০০), bank ৩০০ pending (`TRX-QA-1`, available ৯০০) → verify (balance ৯০০), refund ১০০ (balance ১০০০, refunded ১০০); ২৫% waiver approve করে নভেম্বরে ১৫০০ → ১১২৫; duplicate generation ০ created/২ skipped; collections ১১০০/০/১০০/১০০০; arrears ৫৬২৫ (overdue ৩০০০, not due ২৬২৫, ১–৩০ দিন); statement opening ০/charges ৩৬০০/payments ১১০০/closing ২৫০০; reconciliation matched; exam invoice ২ × ৩০০; invoice/receipt/arrears/statement প্রিন্ট; collections/arrears/statement CSV; gateway readiness; বাংলা locale-এ সব ফি স্ক্রিন; অডিট ট্রেইলে ৩৮টি QA event (fee/print/export/login)।
- QA শেষে অস্থায়ী QA ইউজার, বর্ষ, division, শ্রেণি, সেকশন, বিষয়, পরীক্ষা, শিক্ষার্থী, enrollment, fee master/data, journal, sequence ও সংশ্লিষ্ট অডিট এন্ট্রি সম্পূর্ণ মুছে ফেলা হয়েছে। ডেভ DB এখন সিডেড বেসলাইনে আছে: ১ জন আসল Super Admin, ৬টি division, ৮টি ledger account, ১০টি role, ৯৩টি permission, ৪টি setting; শিক্ষার্থী, ফি ও journal টেবিল শূন্য। QA অ্যাকাউন্ট মুছে যাওয়ায় বিদ্যমান ব্রাউজার সেশন লগইনে রিডাইরেক্ট হয়েছে।

### 9.7 যাচাইকালে পাওয়া ও সংশোধিত ত্রুটি

1. Pending bank payment invoice balance কমিয়ে দিচ্ছে ধরে নিয়ে UI টেস্ট fail করেছিল — প্রকৃত নিয়ম হলো pending available balance reserve করে, invoice balance কমায় না; verification-এর পরেই balance কমে। টেস্ট প্রত্যাশা সংশোধন করা হয়েছে।
2. Waiver ফর্মে বর্ষ পরিবর্তন করলে নির্ভরশীল student/head নির্বাচন রিসেট হয় (ইচ্ছাকৃত); UI টেস্টে field-ক্রম ভুল থাকায় fail করেছিল — টেস্ট ক্রম ঠিক করা হয়েছে।
3. Phase 06-এর ৫টি ফাইলে Pint style issue (unused import, operator spacing, import order) — `pint` দিয়ে auto-fix, পুনরায় PASS।
4. পরীক্ষার কোনো schedule না থাকলে exam invoice preview ০ শিক্ষার্থী দেখায় — এটি ইচ্ছাকৃত scope; QA-তে schedule তৈরি করে ২ শিক্ষার্থীর ৩০০ টাকা ইনভয়েস যাচাই করা হয়েছে।
5. Phase 06 মাইগ্রেশনের পর ডেভ DB-তে নতুন fee permission সিঙ্ক হয়নি — `PermissionSeeder` ও `RoleSeeder` চালিয়ে ২৩টি fee permission ও role mapping যোগ করা হয়েছে; ডিপ্লয়মেন্ট চেকলিস্টে migration-পরবর্তী seeder ধাপ নিশ্চিত করতে হবে।

## 10. Phase 07 — Accounts & Financial Ledger (completed 2026-10-25)

### 10.1 ডেটা মডেল ও ডাবল-এন্ট্রি ভিত্তি

- ১৪টি নতুন মাইগ্রেশন: `journal_entries`, `journal_lines`, `number_sequences`, `account_categories`, `financial_years`, `cash_accounts`, `bank_accounts`, `fundraising_campaigns`, `vouchers`, `voucher_lines`, `bank_reconciliations`, `bank_reconciliation_items`, `payroll_payments` এবং `ledger_accounts`-এ category যোগ। সব অর্থসংক্রান্ত কলাম `DECIMAL`, নম্বর-জেনারেশন ও পোস্টিং ডেটাবেস ট্রানজ্যাকশনের ভেতরে।
- `LedgerAccountSeeder` ১১টি system category ও ২১টি ledger account (1010 Cash … 5080 Other Expenses), ১টি cash account (CASH-MAIN) ও ১টি bank account (BANK-MAIN) সিড করে; system রেকর্ডের শুধু নাম সম্পাদনযোগ্য, code/type/active লক।
- পোস্টকৃত `journal_entries`/`journal_lines` immutable; ভাউচার শুধু reversal দিয়ে সংশোধনযোগ্য। কোনো posted আর্থিক রেকর্ড নীরবে মুছে ফেলা যায় না।

### 10.2 ভাউচার, প্রারম্ভিক জের, বেতন ও তহবিল

- পাঁচ ধরনের ভাউচার: Receipt, Payment, Contra, Transfer, Journal — ড্রাফট → পোস্ট → রিভার্সাল। মাসিক নম্বর `RV/PV/CV/TV/JV-YYYYMM-00001`; শুধু ড্রাফট মুছে ফেলা যায়, পোস্টের পর সম্পাদনা/ডিলিট নেই। রিভার্সাল একটি বিপরীত JV তৈরি করে `reversal_of_id`-এ লিংক করে।
- Opening balance শুধু প্রথম অর্থবছরে, অন্য কোনো জার্নালের আগে, একবারই পোস্ট করা যায়; পার্থক্য স্বয়ংক্রিয়ভাবে 3010 Opening Balance Equity-তে সমন্বিত হয়। ফি/বেতনের journal থাকলে আর opening balance গ্রহণ করা হয় না।
- Payroll payment প্রতি কর্মচারী+সময়কালে একবারই পোস্ট হয়; PAY/BR ভাউচার 5010 Salary & Allowances ডেবিট, নগদ/ব্যাংক ক্রেডিট। সংশোধন শুধু ভাউচার reversal দিয়ে।
- Fundraising campaign (active ও সময়সীমা) + donation রেকর্ড করলে সাথে সাথে posted Receipt/Fundraising (RV) তৈরি হয়, 4060 Fundraising Contributions ক্রেডিট হয়, campaign-এর সংগৃহীত/অগ্রগতি হালনাগাদ হয়।

### 10.3 ব্যাংক মিলকরণ

- ব্যাংক হিসাব + তারিখ অনুযায়ী worksheet; প্রস্তাবিত প্রারম্ভিক (খতিয়ান জের − পূর্বে ক্লিয়ারকৃত নিট) দেখায়, শুধু স্টেটমেন্টে থাকা পোস্টকৃত লাইন ক্লিয়ার করা যায়। মিলকরণ কেবল তখনই সম্পন্ন হয় যখন `statement opening + cleared debits − cleared credits = statement closing`; একই ব্যাংক+তারিখে একটির বেশি worksheet নয়। UI থেকে শুধু worksheet মুছে ফেলা যায়, খতিয়ান অপরিবর্তিত থাকে।

### 10.4 রিপোর্ট, প্রিন্ট, এক্সপোর্ট ও ড্যাশবোর্ড

- Trial balance (as-of), Ledger (opening/running/closing), Income & expenditure (grouped, net surplus/deficit), Accounts overview (cash/bank/receivables/payables/income/expenditure/net/fundraising; fee ও payroll reconciliation — পার্থক্য সর্বদা শূন্য হতে হবে)।
- ভাউচার, রেওয়ামিল, খতিয়ান ও আয়-ব্যয়ের A4 ব্র্যান্ডেড প্রিন্ট (সংস্থার নাম/স্লোগান settings থেকে, BN/EN); CSV এক্সপোর্ট UTF-8 BOM, localized header এবং spreadsheet formula-injection রক্ষা (`=`, `+`, `-`, `@` দিয়ে শুরু হওয়া টেক্সট escape)। প্রতিটি প্রিন্ট/এক্সপোর্টের আলাদা permission ও audit event।
- ড্যাশবোর্ডে অর্থসংক্রান্ত কার্ড এখন পোস্টকৃত journal থেকে বাস্তব সংখ্যা দেখায়; কোনো financial year বা entry না থাকলে সৎ empty-state দেখায়, কোনো নকল সংখ্যা নেই।

### 10.5 অনুমতি, নেভিগেশন ও ভাষা

- ৩১টি নতুন permission: `accounts.view`, `financial_years.*`, `chart_of_accounts.*`, `vouchers.*`, `opening_balances.*`, `payroll_payments.*`, `fundraising.*`, `bank_reconciliation.*`, `account_reports.*` — permissions মোট ১২৪টি।
- Admin ও Accountant পূর্ণ হিসাব নিয়ন্ত্রণ (year close, reversal, payroll, fundraising, reconciliation); Read-Only শুধু view; Parent/Student শুধু ড্যাশবোর্ডের অর্থসংক্রান্ত সারসংক্ষেপ। প্রিন্টে view+print এবং এক্সপোর্টে export permission আলাদাভাবে যাচাই হয়।
- Accounts সাব-নেভিগেশন permission-অনুযায়ী (Overview, Vouchers, Opening balances, Financial years, Chart, Payroll, Fundraising, Reconciliation, Ledger, Trial balance, Income & expenditure); সব UI স্ট্রিং EN/BN, বাংলায় টেবিল/প্রিন্ট/রিপোর্টসহ যাচাইকৃত।

### 10.6 Phase 07 verification

- `./vendor/bin/pest` → **276 passed (2133 assertions)** — Phase 01–06-এর ২৩২টি টেস্ট অটুট; ৪৪টি নতুন টেস্ট: voucher lifecycle (11), opening balance (7), bank reconciliation (8), payroll/fundraising (7), reports/global totals (4), accounts UI/permissions/print/export (6) এবং নতুন accounts migration assertion (1)।
- Accounting invariants পরীক্ষিত: প্রতিটি জার্নালের ডেবিট=ক্রেডিট, contra/transfer নিয়ম, reversal-এ জের শূন্য, cash balance, ব্যাংক মিলকরণের সম্পূর্ণতা-শর্ত, opening balance একবারই, payroll duplicate block, campaign তারিখ/সক্রিয়তা, fee ও payroll যেন খতিয়ানে দ্বিগুণ না হয়, global ledger totals, রিপোর্টের নির্ভুলতা।
- `./vendor/bin/pint --test` → PASS (405 files; AccountsOverview, OpeningBalanceService ও routes/web.php-এর style issue auto-fix)।
- ডেভ MySQL-এ ১৪টি accounts মাইগ্রেশন, ledger/category/cash/bank seeder ও ৩১টি নতুন permission সিঙ্ক; **কোনো নমুনা শিক্ষার্থী, ভাউচার বা লেনদেন সিড করা হয়নি**।
- ব্রাউজার যাচাই (EN + BN): FY-2026 (01 Jul 2026 — 30 Jun 2027, current) তৈরি; খালি overview-তে সব ০.০০ ও fee/payroll reconciliation মিলযুক্ত; chart-এ ১১ category/২১ হিসাব; Receipt `RV-202610-00001` (১০১০ Cash Dr ১,৫০০ / ৪০৫০ General Donations Cr ১,৫০০) ও Payment `PV-202610-00001` (৫০৮০ Other Expenses Dr ৪০০ / ১০১০ Cr ৪০০) পোস্ট; overview cash ১,১০০, income ১,৫০০, expenditure ৪০০, net ১,১০০; রেওয়ামিল ১,৫০০/১,৫০০ balanced; ১০১০ খতিয়ান opening ০, Dr ১,৫০০, Cr ৪০০, closing ১,১০০; আয়-ব্যয়ে আয় ১,৫০০, ব্যয় ৪০০, নিট ১,১০০; ভাউচার প্রিন্টে লাইন ও স্বাক্ষর-ব্লক; campaigning `QA-CAMP-1` (লক্ষ্য ৫,০০০) ও ২৫০ অনুদান → `RV-202610-00002`, সংগৃহীত ২৫০ (৫.০%); ব্যাংক মিলকরণ worksheet ও শর্তাবলি; বাংলায় সব স্ক্রিন ও প্রিন্টে হিসাবের নাম (হাতে নগদ, সাধারণ অনুদান); পোস্টের পর ড্যাশবোর্ডে cash ১,৩৫০, income ১,৭৫০, expenditure ৪০০, net ১,৩৫০ — সবই পোস্টকৃত journal থেকে।
- QA শেষে ৩টি ভাউচার, ৬টি journal line, ৩টি journal entry, ২টি sequence, ১টি campaign, ১টি financial year, ১২টি QA audit event ও অস্থায়ী QA ইউজার সম্পূর্ণ মুছে ফেলা হয়েছে। ডেভ DB বেসলাইন: ১ জন আসল Super Admin, ১০টি role, ১২৪টি permission, ১১টি category, ২১টি ledger account, ১টি cash ও ১টি bank account; students, fee invoices, payroll, reconciliation, journal, voucher ও sequence সব শূন্য।

### 10.7 যাচাইকালে পাওয়া ও সংশোধিত ত্রুটি

1. **Voucher create পৃষ্ঠা crash:** Laravel container `mount(?Voucher $voucher = null)`-এ খালি `Voucher` মডেল ইনজেক্ট করায় `/accounts/vouchers/create`-এ `$voucher->type->value` null হয়ে fatal error হতো। `$voucher === null || ! $voucher->exists` গার্ড যোগ করে ঠিক করা হয়েছে; ব্রাউজারে নতুন ভাউচার তৈরি/পোস্ট করে যাচাই।
2. Financial years টেবিলের হেডারে কাঁচা key `APP.ACCOUNTS.FIELDS.VOUCHERS_COUNT` দেখাচ্ছিল — EN/BN-এ `vouchers_count` অনুবাদ যোগ করা হয়েছে।
3. `AccountsReportTest`-এর global totals প্রত্যাশা ভুল ছিল (শুধু নির্দিষ্ট ভাউচারের বদলে সব journal ধরা উচিত) — ১,৪০০ এবং fee+payroll-সহ ১,৮০০-তে সংশোধন; অ্যাপের হিসাব সঠিক ছিল।
4. Empty-dashboard UI টেস্টে posted voucher থাকায় financial year delete-এ FK violation হচ্ছিল — টেস্টটি আলাদা করে ডেটা-শূন্য পরিস্থিতিতে চালানো হয়েছে।
5. QA-র শেষ Pint চেকে ৩টি ফাইলে style issue পাওয়া যায় — auto-fix করে পুনরায় ৪০৫ ফাইলে PASS।

## 11. Phase 08 — HR & Payroll (completed 2026-10-25)

### 11.1 ডেটা মডেল, কর্মী ও নিয়োগ-নথি

- ১২টি নতুন মাইগ্রেশন: `designations`, `staff_profiles`, `salary_components`, `salary_structures`, `salary_structure_items`, `staff_advances`, `payroll_runs`, `payslips`, `payslip_items`, `staff_advance_recoveries`, `leave_types`, `leave_applications`। সব বেতন/অগ্রিম/সংশোধনের অর্থসংক্রান্ত কলাম `DECIMAL(15,2)`; employee_no, designation code, component code ও active payroll period-এ uniqueness constrain।
- `staff_profiles` বিদ্যমান `users`-এর সঙ্গে ১:১; employee_no unique; department (Phase 03) ও designation; employment type permanent/contract/part-time/probation; status active/on_leave/suspended/resigned/terminated। Resigned/Terminated-এ `left_on` বাধ্যতামূলক এবং অন্য status-এ নিষিদ্ধ; employed গণ্য হয় শুধু active/on_leave।
- ব্যক্তিগত তথ্য (phone, national ID, জন্মতারিখ, gender, ঠিকানা, emergency contact, qualifications) ও নিয়োগ-নথি; নথি বিদ্যমান polymorphic `documents` টেবিলে private disk-এ (appointment letter, NID, academic certificate, experience letter, employment contract, other; PDF/JPG/PNG, সর্বোচ্চ ৫ MB), upload/delete শুধু অনুমোদিত ভূমিকার জন্য এবং প্রতিটি কার্যক্রম audit হয়।
- পদবি department-ভিত্তিক (ঐচ্ছিক), নাম EN/BN, unique code, archive/restore; employed কর্মী থাকা পদবি archive করা যায় না (ঐতিহাসিক রেকর্ড অক্ষত থাকে)। বেতন component-এ earning/deduction type ও unique code; ব্যবহৃত component archive করা যায় না এবং ব্যবহৃত হলে তার type লক থাকে।

### 11.2 বেতন কাঠামো ও payroll হিসাব

- প্রতি কর্মীর একটিই active salary structure: basic + earning/deduction components, effective_from ও notes। সংরক্ষণ transactional; পুনঃসংরক্ষণে পুরোনো items প্রতিস্থাপিত হয়, কিন্তু payslip-এ component-এর নাম/পরিমাণ snapshot থাকায় পরে কাঠামো বদলালেও পুরোনো payslip অপরিবর্তিত থাকে।
- Deduction gross-এর সমান বা বেশি হলে কাঠামো সংরক্ষণ বাতিল; payroll-এ gross = basic + earning, deductions = deduction components; প্রতিটি payslip-এ net > ০ বাধ্যতামূলক।
- অগ্রিম recovery: disbursed ও অপরিশোধিত অগ্রিমগুলো disbursement তারিখ অনুযায়ী; প্রতি অগ্রিমে min(monthly installment, outstanding, available) ধারাবাহিকভাবে কেটে নেওয়া হয়, ফলে একাধিক অগ্রিমও নিরাপদে সমন্বয় হয়; পূর্ণ পরিশোধে status `recovered`।
- যে কর্মীর active salary structure নেই, user নিষ্ক্রিয় বা মাসের শেষের পরে যোগ দিয়েছেন—তিনি payroll থেকে বাদ পড়েন; যোগ্য কেউ না থাকলে run তৈরি হয় না। এক payroll-এ একই কর্মীর জন্য একই period দুইবার পরিশোধ করা যায় না: DB unique (`period` + `active_flag`), active payslip check, row lock এবং Phase 07-এর direct payroll payment-এর সঙ্গে দ্বিমুখী conflict validation।

### 11.3 Payroll lifecycle, ভাউচার ও লেজার

- Lifecycle: draft → approved → paid → reversed। Draft মুছে ফেলা যায়; approved run কারণসহ reopen করে draft করা যায়; paid run সম্পাদনযোগ্য নয়—শুধু reversal দিয়ে সংশোধনযোগ্য।
- Payment: approved run থেকে একটিমাত্র cash বা bank account নির্বাচন করে posted Payment/Payroll voucher `PAY-YYYYMM-00001` তৈরি হয়—Dr 5010 Salary & Allowances (gross), Cr 1120 Staff Advances (recovery), Cr 2050 Payroll Deductions (deductions), Cr liquid account (net)। Database transaction-এর ভেতরে run/payslip/advance lock, ledger account সক্রিয়তা ও advance outstanding পুনঃযাচাই হয়; কোনো ধাপ ব্যর্থ হলে payslip বা advance আংশিক আপডেট হয় না।
- Voucher নিয়ম: সাধারণ Payment voucher-এ credit শুধু liquid account-এ হতে পারে; কিন্তু Payroll category-তে 1120/2050-এর মতো non-liquid credit অনুমোদিত, তবে অন্তত একটি liquid credit বাধ্যতামূলক।
- Reversal: posted Journal reversal তৈরি হয়; payslip reversed/inactive, advance-এর recovered_amount ফেরত যায়, run reversed/inactive এবং period পুনরায় ব্যবহারযোগ্য হয়। Payroll reversal-এর reversal করা যায় না—ভুল হলে নতুন সমন্বয় করতে হবে।
- **নতুন সুরক্ষা:** payroll ও staff-advance voucher Accounts-এর generic voucher স্ক্রিন/register থেকে সরাসরি reverse করা যায় না; কেবল নিজস্ব HR workflow (`PayrollService` বা `PayrollPaymentService`) থেকেই reverse হয়। নইলে লেজার reversed হয়ে গেলেও payroll run/advance ইতিমধ্যে paid/disbursed অবস্থায় থেকে যেত এবং ভবিষ্যৎ recovery ভুল হতো।

### 11.4 অগ্রিম ও ছুটি

- Advance lifecycle: pending → approved (নিজের অগ্রিম নিজে অনুমোদন করা নিষিদ্ধ) → disbursed। Disbursement posted Payment/StaffAdvance voucher `ADV-YYYYMM-00001` তৈরি করে (Dr 1120 / Cr cash-bank)। Pending/approved অবস্থায় কারণসহ cancel করা যায়; disbursed-এর পরে শুধু payroll recovery-ই সংশোধনের পথ। Outstanding = amount − recovered_amount।
- Leave type: unique code, নাম EN/BN, annual quota (nullable), paid/unpaid ও archive/restore; আর্কাইভকৃত type-এ নতুন আবেদন নিষিদ্ধ, তবে পুরোনো আবেদনের ইতিহাস অক্ষত থাকে।
- Leave application: employed কর্মী only; start অবশ্যই joining date-এর আগে নয়; দিন গণনা inclusive calendar days; pending+approved আবেদনের সঙ্গে ওভারল্যাপ নিষিদ্ধ; quota যাচাই হয় starts_on-এর বছরে pending+approved মোট দিনের ভিত্তিতে। Approve/reject (reject-এ কারণ বাধ্যতামূলক), pending/approved cancel (কারণসহ); নিজের ছুটি নিজে অনুমোদন করা নিষিদ্ধ। ছুটির payroll-এ কোনো প্রভাব নেই (Known limitations দেখুন)।

### 11.5 UI, প্রিন্ট, এক্সপোর্ট, অনুমতি ও ভাষা

- ১০টি Livewire স্ক্রিন: Staff list ও profile, Designations, Salary components, Salary structures, Payroll list ও run page, Staff advances, Leave, Leave types। HR সাব-নেভিগেশন প্রতিটি ট্যাবের নিজস্ব permission অনুযায়ী; staff profile-এ নথি, বেতন কাঠামো, অগ্রিম, ছুটি ও payslip ইতিহাস।
- Payroll run page থেকে generate/approve/reopen/delete draft/pay/reverse; payslip-level basic, gross, deductions, advance recovery, net; run-level totals ও voucher লিংক।
- A4 payroll sheet ও payslip প্রিন্ট—settings-ভিত্তিক সংস্থার নাম/স্লোগান, EN/BN, run/payslip status watermark, স্বাক্ষর ব্লক ও generated-at ফুটার; প্রতিটি প্রিন্ট আলাদা audit event লেখে। CSV-তে UTF-8 BOM, localized header, totals row এবং spreadsheet formula-injection রক্ষা (`=`, `+`, `-`, `@` ইত্যাদি দিয়ে শুরু হওয়া মান escape); export-এরও আলাদা audit event।
- ৪২টি নতুন permission: `staff.*` (৬), `designations.*` (৪), `salary_components.*` (৪), `salary_structures.*` (৪), `payroll.*` (৯), `staff_advances.*` (৬), `leave_types.*` (৪), `leave.*` (৫)—মোট ১৬৬টি। Admin-এর পূর্ণ নিয়ন্ত্রণ; HR Manager payroll approve করতে পারে কিন্তু pay/reverse নয়; Accountant payroll view/pay/reverse/print/export করতে পারে কিন্তু approve/create/edit/delete বা কর্মীর ব্যক্তিগত তথ্য নয়; Salary structure শুধু view; advance disburse করতে পারে কিন্তু create/approve নয়। Teacher, Parent, Student ও Read-Only—কোনো HR স্ক্রিনে প্রবেশ করতে পারে না (বেতন ও ব্যক্তিগত তথ্য সংরক্ষিত)।
- সব UI স্ট্রিং, payroll run, payslip, প্রিন্ট ও CSV header EN/BN-এ সম্পূর্ণ; বাংলায় হিসাব/পদবি/কর্মীর নামসহ স্ক্রিন ও প্রিন্ট যাচাই করা হয়েছে।

### 11.6 Phase 08 verification

- `./vendor/bin/pest` → **300 passed (2469 assertions)** — Phase 01–07-এর ২৭৬টি টেস্ট অটুট; ২৪টি নতুন টেস্ট: HR-focused ২৩টি (staff ৭, leave ৫, payroll ১১) ও নতুন migration assertion ১টি।
- পরীক্ষিত payroll invariants: basic + earning − deduction − advance recovery = net; deduction gross ছাড়িয়ে যেতে পারে না; net শূন্য/ঋণাত্মক হলে run বাতিল; একই period-এ duplicate run এবং direct payroll payment—দুই দিক থেকেই বন্ধ; payment-এ Dr = Cr ও নির্দিষ্ট ৫০১০/১১২০/২০৫০/নগদ-ব্যাংক লাইন; reversal-এ advance পুনরুদ্ধার, period মুক্ত ও জের শূন্য; immutability; leave approval/quota/overlap/self-approval; staff document policy; HR voucher reversal guard; ভূমিকা-ভিত্তিক access; print/CSV/audit।
- `./vendor/bin/pint --test` → PASS (462 files)।
- ডেভ MySQL-এ ১২টি HR মাইগ্রেশন, লেজারে নতুন 1120 Staff Advances ও 2050 Payroll Deductions account, ৪২টি নতুন permission ও ১০টি role সিঙ্ক; **কোনো নমুনা কর্মী, বেতন বা লেনদেন সিড করা হয়নি**।
- ব্রাউজার যাচাই (EN + BN): department `QA-DEPT`, designation `QA-TEACHER`, কর্মী `EMP-8001`; components HOUSE-RENT (earning ৫,০০০) ও PF-EMPLOYEE (deduction ১,০০০); basic ২০,০০০ → gross ২৫,০০০। FY-2026-এর অধীনে অগ্রিম ৬,০০০ (installment ২,০০০) → approve → CASH-MAIN থেকে ০৫ Oct 2026 disbursement `ADV-202610-00001`; ANNUAL leave type (১৫ দিন) ও ২ দিনের অনুমোদিত ছুটি। Payroll 2026-10: ১ কর্মী, gross ২৫,০০০, deductions ১,০০০, recovery ২,০০০, net ২২,০০০ → approve → ৩১ Oct 2026 payment `PAY-202610-00001`; journal-এ 5010 Dr ২৫,০০০ / 1120 Cr ২,০০০ / 2050 Cr ১,০০০ / 1010 Cr ২২,০০০; অগ্রিম outstanding ৪,০০০; payslip net ২২,০০০। payroll sheet ও payslip প্রিন্ট EN/BN যাচাই; CSV HTTP 200 (`text/csv`) ও localized header; একই period-এ দ্বিতীয় run প্রত্যাখ্যাত।
- QA শেষে কর্মী, department, designation, components, structure, advance, leave, payroll run, payslip, voucher, journal, sequence, financial year, QA ইউজার ও QA audit event সম্পূর্ণ মুছে ফেলা হয়েছে। ডেভ DB বেসলাইন: ১ জন আসল Super Admin, ১০টি role, ১৬৬টি permission, ২৩টি ledger account, ৩টি baseline activity; staff/HR attendance/staff advances/leave/payroll/voucher/journal/sequence/financial year সব শূন্য।

### 11.7 যাচাইকালে পাওয়া ও সংশোধিত ত্রুটি

1. **Staff create 500:** Livewire-এর খালি optional date property (`date_of_birth=''`, `left_on=''`) MySQL `date` কলামে যাওয়ায় `SQLSTATE 22007` হতো (middleware hydrated state-এ চলে না)। `StaffService`-এ `filled()` দিয়ে null-normalization যোগ করা হয়েছে; regression টেস্ট যুক্ত।
2. **Salary components পৃষ্ঠা 500:** Blade-এর `@forelse ($components as $component)` loop variable `<x-badge>`-এর অভ্যন্তরীণ `$component`-কে shadow করায় `Undefined property: AnonymousComponent::$type` হতো। loop variable `$salaryComponent`-এ rename করা হয়েছে; Livewire render regression টেস্ট যুক্ত।
3. **HR voucher reversal loophole:** Accounts-এর generic voucher reversal payroll/advance voucher-ও reverse করতে পারত, ফলে লেজার reversed হলেও payroll run paid ও advance disbursed থেকে যেত এবং পরবর্তী recovery/period পুনঃব্যবহারে অসঙ্গতি আসত। `VoucherService::reverse`-এ HR category guard যোগ করা হয়েছে; শুধু `PayrollService` ও `PayrollPaymentService` অনুমোদিত bypass পায়; নতুন regression টেস্টে সরাসরি reversal blocked এবং HR-path reversal সফল—দুইটিই যাচাই করা হয়েছে।
4. দুটি টেস্ট প্রত্যাশা ভুল ছিল (অ্যাপের ত্রুটি নয়): already-approved অগ্রিম দিয়ে disbursement error প্রত্যাশা—আলাদা pending অগ্রিম ব্যবহার করে সংশোধিত; draft payroll voucher-এ `voucher_no` আশা—post করার পর `PAY-202610-00001` যাচাই করে সংশোধিত।

## 12. Phase 09 — Library, Inventory & Operations (completed 2026-10-25)

### 12.1 লাইব্রেরি ক্যাটালগ, কপি ও সদস্য-নীতি

- ৬টি নতুন মাইগ্রেশন: `book_categories`, `books`, `book_copies`, `library_policies`, `book_loans`, `book_reservations`।
- বই: ইউনিক কোড/ISBN (থাকলে), শিরোনাম, লেখক, প্রকাশক, সংস্করণ, ভাষা, প্রকাশনা সাল, শ্রেণি (ঐচ্ছিক), তাক/শেলফিং তথ্য ও নোট; আর্কাইভ/restore; আর্কাইভড বই বা কপি নতুন ইস্যুতে ব্যবহার করা যায় না।
- কপি: প্রতি বইয়ের অধীনে ইউনিক accession no, কপি-স্ট্যাটাস (available/issued/reserved/lost/retired) ও অবস্থা; একই বইয়ের ভিন্ন ভিন্ন কপি আলাদাভাবে ইস্যু/ফেরত হয়।
- সদস্য-নীতি (`library_policies`) member type অনুযায়ী: loan days (ডিফল্ট student ১৪/staff ২১), সর্বোচ্চ renewal ও renewal days, দৈনিক জরিমানা, grace days, সর্বোচ্চ জরিমানা, সর্বোচ্চ সক্রিয় লোন ও reservation সক্ষমতা; ডিফল্ট `LibraryPolicySeeder` থেকে, সম্পাদনা `library_policies.edit`-এ সীমিত এবং প্রতিটি পরিবর্তন অডিট হয়।

### 12.2 ইস্যু, ফেরত, রিনিউ, জরিমানা ও রিজার্ভেশন

- ইস্যু: শিক্ষার্থী অথবা কর্মী সদস্য, বই ও নির্দিষ্ট কপি; due date নীতি থেকে স্বয়ংক্রিয় (ছুটির দিন আলাদা করা হয় না); সক্রিয় লোনের সর্বোচ্চ সীমা, কপি ইতিমধ্যে issued/reserved/retired হলে প্রত্যাখ্যান, বই-কপি সমন্বয় যাচাই ও আর্কাইভড-বই guard — সব service স্তরে লকসহ।
- ফেরত: ফেরতের তারিখ ও overdue দিন গণনা; জরিমানা = overdue দিন × দৈনিক হার, grace day বাদে, সর্বোচ্চ সীমায় capped; জরিমানা শুধু `library_circulation.fines` পারমিশনধারী settle করতে পারেন এবং একবারই; বকেয়া জরিমানা থাকলে নতুন ইস্যু ব্লক হয়।
- রিনিউ: সর্বোচ্চ renewal সংখ্যা পর্যন্ত, renewal days অনুযায়ী due date বাড়ে; overdue, reserved বা renewal-শেষ লোন প্রত্যাখ্যাত। প্রতিটি ইস্যু/ফেরত/রিনিউ/জরিমানা `library.loan_issued`/`loan_returned`/`loan_renewed`/`fine_settled` অডিটে থাকে।
- রিজার্ভেশন: সদস্য-নীতিতে সক্ষম থাকলে ভবিষ্যৎ ইস্যুর জন্য সংরক্ষণ (`expires_on` ডিফল্ট ৭ দিন); duplicate সক্রিয় রিজার্ভেশন ব্লক; expires_on পার হলে পরবর্তী ইস্যু/রেন্ডারের সময় বাতিল হয়; ইস্যু হলে সংশ্লিষ্ট রিজার্ভেশন fulfilled হয়; pending রিজার্ভেশন শুধু কারণসহ cancel করা যায়।
- প্রিন্ট/এক্সপোর্ট: A4 loan slip (ব্র্যান্ডেড, due date/জরিমানা), overdue তালিকা প্রিন্ট এবং লোন CSV export (UTF-8 BOM, localized header, formula-injection guard); `library_circulation.view`+`print` / `export` আলাদা যাচাই এবং প্রতিটি প্রিন্ট/export অডিট।

### 12.3 ইনভেন্টরি ক্যাটালগ, সাপ্লায়ার ও ক্রয়-আবেদন

- ১৫টি নতুন মাইগ্রেশন: `inventory_categories`, `suppliers`, `inventory_items`, `purchase_requests`, `purchase_request_items`, `stock_receipts`, `stock_receipt_items`, `stock_issues`, `stock_issue_items`, `stock_adjustments`, `stock_adjustment_items`, `inventory_stock_movements`, `inventory_assets`, `asset_maintenance_records`, `inventory_settings`।
- ক্যাটাগরি ও সাপ্লায়ার: ইউনিক কোড, EN/BN নাম, description; ব্যবহৃত ক্যাটাগরি/সাপ্লায়ার archive করা যায় না (restore আছে); সাপ্লায়ার যোগাযোগ/ঠিকানা/ট্যাক্স আইডি।
- সামগ্রী: SKU ইউনিক, ধরন consumable/fixed asset, ইউনিট, minimum stock, বর্তমান মজুদ, চলতি গড় মূল্য, ডিফল্ট ব্যয় খাত (লেজার ৫০৮০ বা অন্য), নোট; আর্কাইভড সামগ্রী কোনো stock movement-এ ব্যবহার করা যায় না। মজুদ মূল্যায়ন weighted-average; প্রতিটি গ্রহণ/সমন্বয় বৃদ্ধিতে গড় মূল্য পুনঃগণনা হয়।
- ক্রয় আবেদন: মাসিক সিকোয়েন্স `PR-YYYYMM-00001`; একাধিক লাইন (সামগ্রী, পরিমাণ, আনুমানিক একক মূল্য), সরবরাহকারী, প্রয়োজনীয় তারিখ, কারণ/নোট; draft → submitted → approved/rejected/cancelled → received lifecycle। নিজের তৈরি আবেদন নিজে approve করা যায় না (separation of duties); approve/reject/cancel-এ যথাযথ permission ও কারণ/অডিট; শুধু draft সম্পাদনা/মুছে ফেলা যায়।

### 12.4 স্টক গ্রহণ, বিতরণ, সমন্বয় ও ডাবল-এন্ট্রি হিসাব

- স্টক গ্রহণ (GRN): অনুমোদিত ক্রয় আবেদনের বিপরীতে আংশিক বা পূর্ণ গ্রহণ; বাকি পরিমাণের বেশি গ্রহণ প্রত্যাখ্যাত; প্রতিটি লাইনে প্রকৃত একক মূল্য (আনুমানিক মূল্যের সঙ্গে পার্থক্য অনুমোদিত) ও weighted average পুনঃগণনা; সিকোয়েন্স `GRN-YYYYMM-00001`।
- পেমেন্ট পদ্ধতি: supplier credit (Accounts Payable 2010), cash (1010) বা bank/mobile (1020); প্রতিটি গ্রহণ একটি balanced voucher তৈরি করে — credit-এ 1130 Inventory Dr / 2010 Cr, cash/bank-এ 1130 Dr / 1010 বা 1020 Cr; ভাউচার নম্বর GRN-এ সংরক্ষিত ও প্রিন্টে দৃশ্যমান; একই GRN-এর জন্য একাধিক voucher তৈরি হয় না।
- স্টক বিতরণ (ISS): সামগ্রী, পরিমাণ, গ্রহীতা ও কারণ; সামগ্রীর ডিফল্ট ব্যয় খাত (না থাকলে 5080 Other Expenses) Dr এবং 1130 Inventory Cr — balanced voucher; সিকোয়েন্স `ISS-YYYYMM-00001`।
- **নেগেটিভ স্টক:** ডিফল্টভাবে নিষিদ্ধ (service lock ও stock movement একই transaction-এ); শুধুমাত্র `inventory_settings`-এ স্পষ্ট নীতি চালু করলে ঋণাত্মক মজুদ অনুমোদিত হয়, বন্ধ করলে আবার ব্লক। প্রতিটি নীতি পরিবর্তন `inventory.settings_updated` অডিটে থাকে।
- স্টক সমন্বয় (ADJ): increase/decrease, কারণ বাধ্যতামূলক, একাধিক লাইন; increase-এ ডিফল্ট ব্যয় খাত Cr / 1130 Dr, decrease-এ 1130 Cr / ব্যয় খাত Dr; সিকোয়েন্স `ADJ-YYYYMM-00001`; সমন্বয়ের পরেও current stock ও weighted average সামঞ্জস্যপূর্ণ।
- প্রতিটি গ্রহণ/বিতরণ/সমন্বয় একটি `inventory_stock_movements` সারি লেখে (movement type, quantity in/out, balance after, unit cost, total value, reference, actor); একই ডকুমেন্টের voucher এক-একটি (one-to-one) এবং সব ভাউচার debit = credit।
- লেজারে নতুন **1130 Inventory** account `LedgerAccountSeeder`-এ idempotentভাবে যোগ করা হয়েছে (একইসঙ্গে 2010/5080 আগের ফেজ থেকেই ছিল); migration-পরবর্তী ডিপ্লয়মেন্টে এই seeder চালানো আবশ্যক, নইলে GRN/ISS/ADJ পোস্টিং ব্যর্থ হবে।

### 12.5 সম্পদ রেজিস্টার, রক্ষণাবেক্ষণ, রিপোর্ট ও সেটিংস

- সম্পদ রেজিস্টার: asset tag ইউনিক, EN/BN নাম, ক্যাটাগরি/সামগ্রী/সাপ্লায়ার (ঐচ্ছিক), ক্রয় তারিখ/মূল্য, acquisition reference, অবস্থান, custodian, status (in use/in store/under maintenance/retired/disposed), warranty expiry, নোট; সম্পদ তৈরি/সম্পাদনা/অবস্থা পরিবর্তন ও প্রতিটি কার্যক্রম অডিট।
- রক্ষণাবেক্ষণ: preventive/corrective/inspection ধরন, তারিখ, বিবরণ, খরচ, সরবরাহকারী, performed by, next due, নোট; একাধিক রেকর্ড, মোট রক্ষণাবেক্ষণ খরচ সম্পদে roll-up; রক্ষণাবেক্ষণ খরচ শুধু রেকর্ড হয় (লেজার পোস্টিং নয় — Known limitations)।
- স্টক ইতিহাস: সামগ্রী/আন্দোলনের ধরন/তারিখ-রেঞ্জ ফিল্টার, বর্তমান মজুদ মূল্য, কম-মজুদ সারাংশ, paginated movements; A4 stock report প্রিন্ট ও CSV export (ফিল্টার অনুযায়ী, BOM ও formula-injection guard); asset register প্রিন্ট (মোট মূল্য ও রক্ষণাবেক্ষণ খরচ, স্বাক্ষর ব্লক)।
- সেটিংস পৃষ্ঠা: নেগেটিভ স্টক নীতি on/off; শুধু `inventory_settings.edit`; বাংলা/ইংরেজি ব্যাখ্যা ও সতর্কবার্তা।
- ডকুমেন্ট প্রিন্ট: purchase request, goods received note (voucher no সহ), stock issue note (expense head সহ), asset register ও stock report — ব্র্যান্ডেড A4, EN/BN, generating timestamp; প্রতিটি প্রিন্ট/export আলাদা audit event।

### 12.6 UI, অনুমতি, নেভিগেশন ও ভাষা

- ১০টি ইনভেন্টরি Livewire স্ক্রিন (items, categories, suppliers, purchase requests, receipts, issues, adjustments, assets, stock history, settings) এবং ৫টি লাইব্রেরি স্ক্রিন (books, categories, circulation, reservations, policies); সব form validation, error message, empty-state ও pagination EN/BN।
- ৫৮টি নতুন permission (library ২১ + inventory ৩৭) — মোট ২২৪; Admin পূর্ণ নিয়ন্ত্রণ, Librarian সংখ্যা-সীমিত লাইব্রেরি কার্যক্রম, Office Staff ইনভেন্টরি কার্যক্রম, Accountant ও Read-Only প্রয়োজন অনুযায়ী view/print/export, Teacher/Parent/Student-এর লাইব্রেরি/ইনভেন্টরি write নেই।
- সাইডবারে Library ও Inventory সাব-নেভিগেশন permission অনুযায়ী; print route-এ view+print এবং export route-এ export permission আলাদা; প্রতিটি Livewire mount/action-এ `authorize()` এবং service-স্তরে IDOR/archived/policy guard।
- সব নতুন UI স্ট্রিং, টেবিল হেডার, ভ্যালিডেশন, প্রিন্ট ও CSV EN/BN-এ সম্পূর্ণ; বাংলা locale-এ English-only লেবেল (যেমন ব্যয় খাত ও সদস্যের নাম) পাওয়া গিয়ে `localizedName()` ব্যবহার করে সংশোধন করা হয়েছে।

### 12.7 Phase 09 verification

- `./vendor/bin/pest` (সম্পূর্ণ স্যুট) → **315 passed (2700 assertions)**, 948.14s; কোনো failure নেই। Phase 01–08-এর সব টেস্ট অটুট।
- `./vendor/bin/pest tests/Feature/Library tests/Feature/Inventory` → **13 passed (203 assertions)** (Library ৭টি, Inventory ৬টি); weighted average, partial receipt, over-receipt, negative-stock policy, purchase approval separation, adjustment accounting, one-to-one voucher, overdue fine, renewal limit, reservation expiry, print/export permissions ও UI workflow পরীক্ষিত।
- `./vendor/bin/pint --test` → PASS (554 files; Phase 09-এর LibraryService, PurchaseRequestService ও MigrationsTest-এ style auto-fix)।
- ডেভ MySQL-এ ২১টি নতুন মাইগ্রেশন ও `LedgerAccountSeeder` (1130 Inventory সহ) প্রয়োগ; `PermissionSeeder`/`RoleSeeder` সিঙ্কে ৫৮টি permission যোগ; **কোনো নমুনা বই, সামগ্রী, শিক্ষার্থী বা লেনদেন সিড করা হয়নি** (শুধু লাইব্রেরি নীতির ডিফল্ট ২টি সারি)।
- ব্রাউজার যাচাই (EN + BN): বই `QA কুরআন তাফসীর` + কপি `QA-ACC-0001` তৈরি; কর্মী সদস্য `QA-EMP-9001`-কে ইস্যু, ২ বার রিনিউ, overdue ফেরত (জরিমানা ৬৫.০০) ও জরিমানা settle; রিজার্ভেশন তৈরি → ইস্যুতে fulfilled; লোন স্লিপ/overdue তালিকা প্রিন্ট ও লোন CSV export।
- ইনভেন্টরি: ক্যাটাগরি `QA-CAT-01`, সাপ্লায়ার `QA-SUP-01`, সামগ্রী `QA-ITEM-01` (consumable, pcs, min ৫, ব্যয় খাত ৫০৮০); ক্রয় আবেদন ১০ × ১০০ = ১,০০০ draft→submit; নিজের আবেদন approve ব্লকড, দ্বিতীয় অনুমোদনকারীর approve; GRN ৬ × ১০০ = ৬০০ supplier credit (`JV-202610-00001`: 1130 Dr ৬০০ / 2010 Cr ৬০০) ও GRN ৪ × ১৫০ = ৬০০ cash (`PV-202610-00001`: 1130 Dr ৬০০ / 1010 Cr ৬০০); weighted average ১২০, মজুদ ১০। বিতরণ ৩ × ১২০ = ৩৬০ (`JV-202610-00002`: 5080 Dr ৩৬০ / 1130 Cr ৩৬০, মজুদ ৭); নেগেটিভ স্টকে ৮ বিতরণ ব্লকড → নীতি চালু করে ৮ × ১২০ = ৯৬০ (`JV-202610-00003`, মজুদ −১) → নীতি আবার বন্ধ; সমন্বয় +৩ × ১২০ = ৩৬০ (`JV-202610-00004`: 1130 Dr ৩৬০ / 5080 Cr ৩৬০, মজুদ ২, মূল্য ২৪০)। প্রতিটি ডকুমেন্টের ঠিক একটি voucher, সব voucher balanced, trial balance পার্থক্য ০।
- সম্পদ `QA-AST-0001` (ক্রয়মূল্য ২৫,০০০, warranty ২০২৭) + preventive maintenance ১,৫০০ (next due ২০২৭) → মোট রক্ষণাবেক্ষণ খরচ ১,৫০০; asset register/stock report/PR/GRN/issue প্রিন্ট ও stock history CSV (BOM, localized header, ফিল্টারকৃত মোট ১,৩২০) যাচাই; ফিল্টার (সামগ্রী/ধরন/তারিখ), low-stock সারাংশ ও সেটিংস পৃষ্ঠা EN/BN-এ যাচাই।
- QA শেষে ২ জন QA ইউজার, কর্মী প্রোফাইল/department/designation, বই/কপি/লোন/রিজার্ভেশন, ক্যাটাগরি/সাপ্লায়ার/সামগ্রী/PR/GRN/ISS/ADJ/movements, সম্পদ/রক্ষণাবেক্ষণ, ৫টি voucher + journal, FY-2026, sequences, inventory settings ও ৪৫টি QA audit event সম্পূর্ণ মুছে ফেলা হয়েছে। ডেভ DB বেসলাইন: ১ জন আসল Super Admin, ১০টি role, ২২৪টি permission, ২৪টি ledger account, ১টি cash ও ১টি bank account, ২টি library policy; লাইব্রেরি/ইনভেন্টরি/হিসাবের সব লেনদেন ডেটা শূন্য।

### 12.8 যাচাইকালে পাওয়া ও সংশোধিত ত্রুটি

1. **MySQL 1059 identifier too long:** Phase 09-এর কিছু composite index-এর auto-generated নাম MySQL-এর ৬৪-অক্ষরের সীমা ছাড়িয়ে migration ব্যর্থ করছিল — স্পষ্ট, সংক্ষিপ্ত index নাম দিয়ে migration সংশোধন করা হয়েছে।
2. **লাইব্রেরি CSV-তে ডুপ্লিকেট বাংলা হেডার:** due date কলামে ভুল key ব্যবহারের কারণে বাংলায় দুই কলামের শিরোনাম একই হচ্ছিল — `app.library.fields.due_on` ব্যবহার করে সংশোধন এবং regression টেস্ট যোগ করা হয়েছে।
3. **ডেভ DB-তে 1130 Inventory account অনুপস্থিত:** আগের ফেজে সিড করা চার্টে 1130 না থাকায় stock receipt পোস্টিং ব্যর্থ হতো (`inventory ledger account is missing or inactive`); `LedgerAccountSeeder` পুনরায় চালিয়ে account যোগ করা হয়েছে — migration-পরবর্তী ডিপ্লয়মেন্ট ধাপে এটি বাধ্যতামূলক।
4. **পুরোনো success notice ভ্যালিডেশন error-এর সঙ্গে দেখানো:** ১৪টি Inventory/Library manager-এ form খোলা বা save শুরু করার সময় আগের success message পরিষ্কার হতো না; `create`/`save`/workflow action-এর শুরুতে `$this->notice = null` যোগ করা হয়েছে এবং StockIssuesManager-এর জন্য regression টেস্ট (notice reset + validation error) যোগ করা হয়েছে।
5. **বাংলা UI-তে ইংরেজি লেবেল:** item list-এ ব্যয় খাতের নাম hardcoded `name_en` এবং লাইব্রেরি ইস্যু/রিজার্ভেশন সদস্য-নির্বাচনে শিক্ষার্থীর নাম ইংরেজিতে দেখাচ্ছিল — `localizedName()` ব্যবহার করে সংশোধন ও বাংলায় যাচাই করা হয়েছে।
6. ফুল Pint চেকে Phase 09-এর ৩টি ফাইলে (LibraryService, PurchaseRequestService, MigrationsTest) unused import/operator spacing/import order issue পাওয়া যায় — auto-fix করে ৫৫৪ ফাইলে PASS।

## 13. Phase 10 — Portals & Communication (completed 2026-10-26)

### 13.1 স্কিমা ও ডেটা মডেল

- ৭টি নতুন টেবিল: `student_user` ও `guardian_user` (unique user–student/guardian pair, cascade delete; guardian পিভট `student_guardians`-কে রেফারেন্স করে), `notices`, `homework_assignments`, `learning_materials`, `notification_preferences` (per-user unique, email/sms Boolean ও topics JSON), `notification_deliveries` (channel, topic, recipient, subject/body, status, provider, morph reference, error, attempted_at/sent_at)।
- `notices`: type (notice/announcement), দ্বিভাষিক title/body, audience (all/students/parents/teachers/class/section), optional class/section scope, publish ও expiry timestamp, notified_at, attachment metadata, created_by/updated_by; published/scope/expiry index।
- `homework_assignments`: year/class/section (section null = class-wide)/subject/teacher, type (homework/assignment), assigned_on/due_on, publish ও notified timestamp, attachment metadata, scope/teacher/due index। `learning_materials`: বাধ্যতামূলক private file (disk/path/name/mime/size), optional subject/section, publish ও notified timestamp, scope/teacher index।
- ৬টি enum: `NoticeAudience`, `NoticeType`, `HomeworkType`, `NotificationChannel`, `NotificationStatus`, `NotificationTopic`; সব মডেলে factory ও relationship, portal pivot (`portalStudents`, `portalGuardians`) এবং notification preference/delivery relation যোগ করা হয়েছে।

### 13.2 পোর্টাল অ্যাক্সেস, ড্যাশবোর্ড ও record-level authorization

- `PortalAccessService` precedence: direct student link → guardian link → teacher role → student role → parent role; `authorizeStudent`, `children`, `linkedStudents`, `ownStudents`, `isPortalStudent`, `isPortalTeacher`, `teacherCanAccessStudent`, `teacherCanManageScope`, `teacherOptions`, `audienceUsers`।
- শিক্ষার্থী শুধু নিজের রেকর্ড; অভিভাবক শুধু লিংকড সন্তান (multi-child dashboard সহ); শিক্ষক শুধু class-teacher (whole class বা নির্দিষ্ট section) অথবা subject-allocation scope; admin পোর্টাল ড্যাশবোর্ডে 403; guest login-এ redirect। পোর্টাল route-গুলো auth-protected কিন্তু permission middleware ছাড়া — প্রতিটি পৃষ্ঠা/ডাউনলোডে service-স্তরের record-level authorization।
- আলাদা student/parent/teacher dashboard; shared portal layout ও sidebar, ভাষা switcher, Staff dashboard ফেরার লিংক। শিক্ষার্থী: attendance summary, published results, fees statement/invoice/receipt print, documents, notices, homework, materials, preferences। অভিভাবক: child summary সহ একই পৃষ্ঠাগুলো। শিক্ষক: assigned classes/students/today attendance, published-vs-draft counts, সাম্প্রতিক notices/content এবং homework/material manager, attendance summary ও published results।
- Published-only নীতি: notices (published, unexpired, audience ও class/section scope), homework/material (published ও scope), results (শুধু published exam); teacher manager নিজের draft দেখতে পারে, অন্য শিক্ষক/শিক্ষার্থী পারে না। শিক্ষক পোর্টালের লিংক legacy staff sidebar-এ শুধু teacher role-এর জন্য দেখানো হয় (admin/অন্য role-এ hidden)।
- Admin portal accounts পৃষ্ঠা থেকে বিদ্যমান active user-কে student/guardian-এর সঙ্গে link/unlink করা যায়; duplicate link প্রতিরোধ ও unlink audit।

### 13.3 Communication, notification pipeline ও delivery log

- admin communication পৃষ্ঠা: notices manager (create/edit/delete, publish/unpublish, audience/class/section, expiry, attachment, search), delivery log (channel/status/topic filter, search, sent/failed/skipped summary, failed retry) এবং portal accounts (search, link/unlink)।
- ২১টি নতুন permission — `notices.*` ৫, `homework.*` ৫, `materials.*` ৫, `communication.view/send` ২, `portal_accounts.view/create/delete` ৩, `portals.view` ১; মোট ২৪৫। Admin পূর্ণ নিয়ন্ত্রণ, Teacher শুধু homework/material manage (নিজের scope), Student/Parent শুধু portal view; communication route তিনটি `communication.view`/`portal_accounts.view`-এ সুরক্ষিত।
- Notification pipeline synchronous: publish করলে `notified_at` দিয়ে একবারই পাঠানো হয়; audience class/section ও role অনুযায়ী নির্ধারিত; প্রতি recipient-এ preference ও system channel config যাচাই; skip precedence `channel_disabled → preference_disabled → topic_disabled → missing_recipient`; প্রতি attempt `notification_deliveries`-এ Sent/Failed/Skipped হিসেবে সংরক্ষিত; gateway exception ধরা হয় যাতে publication ব্যর্থ না হয়; failed delivery শুধু retry করা যায়, disabled channel retry করলে Skipped-এ রূপান্তরিত হয়। SMS body ৩০০ অক্ষরে truncated।
- `EmailGateway`/`SmsGateway` abstraction; email app mailer ব্যবহার করে, SMS integration-ready কিন্তু ডিফল্টে disabled (`COMMUNICATION_EMAIL_ENABLED`, `COMMUNICATION_SMS_*` env; কোনো credential commit করা হয়নি)। টেস্টে `MAIL_MAILER=array`; delivery provider ও error সংরক্ষিত।

### 13.4 UI, files, ভাষা ও ডিজাইন

- ১৮টি পোর্টাল Livewire component ও ৩টি communication component; portal layout/sidebar, communication nav, child summary component; বিদ্যমান maroon/gold design system, Poppins/Hind Siliguri, dark mode ও EN/BN switcher অপরিবর্তিত।
- `PortalFileService` extension allowlist (pdf/jpg/jpeg/png/doc/docx/xls/xlsx/ppt/pptx/txt) ও 10 MB সীমা যাচাই করে; সব portal ফাইল private `local` disk-এ থাকে এবং `Storage::download`-এ `Cache-Control: private, no-store` + `X-Content-Type-Options: nosniff` সহ stream হয়। Fee invoice/receipt বিদ্যমান A4 print view পুনর্ব্যবহার করে।
- ২৪টি `/portal/*` route (auth) ও ৩টি `/communication/*` route (permission-gated); CSRF, throttle, audit log ও বিদ্যমান permission registry অটুট। Phase 10-এর ২২টি ভিউতে ব্যবহৃত ৪৩৫টি translation key EN ও BN দুই locale-এ সম্পূর্ণ (0 missing, 0 empty); মোট key parity ৩,৫৩৬।

### 13.5 Phase 10 verification

- `php artisan test tests/Feature/Portal` → **22 passed (339 assertions)**, 40.07s: PortalAccessTest ৯, PortalCommunicationTest ৯, PortalContentTest ৪। guardian-child boundary, direct student, multi-child, role dashboards, published vs draft results, teacher class/subject scope, fee/receipt/document authorization, notice audience/expiry/scope, homework/material file protection, notification skip/failure/retry/audience de-dup, preferences validation, account link/unlink, communication permission gate ও public privacy পরীক্ষিত।
- সম্পূর্ণ স্যুট → **337 passed (3042 assertions)**, 813.92s; কোনো failure নেই। Phase 01–09-এর সব টেস্ট অটুট।
- `./vendor/bin/pint --test` → **PASS (618 files)**; কোনো style violation নেই।
- ডেভ MySQL-এ ৭টি নতুন মাইগ্রেশন প্রয়োগ; `PermissionSeeder`/`RoleSeeder` সিঙ্কে ২১টি permission যোগ (মোট ২৪৫, role ১০); **কোনো নমুনা শিক্ষার্থী, নোটিশ, হোমওয়ার্ক, ফি বা লেনদেন সিড করা হয়নি**।
- ব্রাউজার যাচাই (EN + BN, dev server 8080): QA ডেটাসেটে একজন শিক্ষার্থী (QA-SMM-0001), একজন অভিভাবক, একজন শিক্ষক ও একজন communication admin; শিক্ষার্থী dashboard-এ 75.00% attendance, ৳500 outstanding, GPA 5.00 A+, published result; draft notice hidden; homework attachment, material, 4-day attendance, results, fee statement (৳1,200/৳700/৳500) + A4 invoice/receipt print, documents ও preferences যাচাই। অভিভাবক dashboard-এ linked child summary; parent-এর teacher/student dashboard 403।
- শিক্ষক পোর্টাল: dashboard counts, notices, homework manager (UI থেকে draft তৈরি → publish → notification), materials, attendance, results; EN/BN। Admin communication: draft notice publish → delivery log 5 Sent/5 Skipped (SMS disabled; email Sent), portal accounts link/unlink; EN/BN। Legacy sidebar-এ শুধু শিক্ষকের জন্য “Teacher portal” লিংক যাচাই।
- QA শেষে ৪টি QA user, year/class/section/student/enrollment/guardian, teacher allocation, ৩টি notice, ২টি homework, material, invoice/payment, attendance, exam/result, document, ১০টি delivery, sessions ও Phase 10 activity log সম্পূর্ণ মুছে ফেলা হয়েছে; ভুলবশত তৈরি দুটি factory student ও একটি invoice-ও অপসারণ করা হয়েছে। ডেভ DB বেসলাইন: ১ জন আসল Super Admin, ১০টি role, ২৪৫টি permission, ৬টি পূর্ব-existing division, ২৪টি ledger account, ১টি cash ও ১টি bank account, ২টি library policy; শিক্ষার্থী/পোর্টাল/কমিউনিকেশন ডেটা শূন্য।

### 13.6 যাচাইকালে পাওয়া ও সংশোধিত ত্রুটি

1. **Notification summary-তে skipped count ভুল:** চারটি skip branch-এ delivery log লেখা হলেও `skipped` counter বাড়ছিল না; প্রতিটি `logSkipped()`-এর পরে increment যোগ করা হয়েছে এবং notification count টেস্টে যাচাই করা হয়েছে।
2. **Livewire property ও view data collision:** `NotificationPreferencesPage`-এর public `array $topics` render-এর `topics` (enum cases) কে shadow করায় “Attempt to read property value on string” TypeError হচ্ছিল; view variable-এর নাম `topicOptions` করা হয়েছে।
3. **অনুপস্থিত translation key:** পাঁচটি নতুন manager/log টেবিলে `app.actions.actions` ব্যবহার হয়েছিল, যা কোথাও সংজ্ঞায়িত নয় — UI-তে `APP.ACTIONS.ACTIONS` দেখাচ্ছিল; সঠিক `app.portal.common.actions` ব্যবহার করে সংশোধন এবং ২২টি Phase 10 ভিউয়ের ৪৩৫টি key EN/BN-এ scan করে 0 missing নিশ্চিত করা হয়েছে।
4. **শিক্ষক পোর্টাল discoverability:** legacy staff dashboard/sidebar-এ পোর্টালের কোনো লিংক ছিল না; শুধু teacher role-এর জন্য conditional “Teacher portal” sidebar link যোগ করা হয়েছে এবং admin-এর কাছে দেখা যায় না — regression assertion যোগ করা হয়েছে।
5. **ছোট ফাইলের size label:** ১ KB-এর কম ফাইলে “0 KB” দেখাচ্ছিল; এখন ১০২৪ বাইটের নিচে byte মান (যেমন `25 B`) দেখায় এবং materials পৃষ্ঠায় regression assertion যোগ করা হয়েছে।
6. **টেস্ট helper-এ roll collision:** `makeEnrolledStudent()` সব শিক্ষার্থীকে roll `3` দিত, ফলে একই placement-এ দুজন তৈরি হলে unique constraint ব্যর্থ হতো; year/class/section ভিত্তিক পরবর্তী roll হিসাব করা হয়েছে (explicit override অটুট)।
7. **PortalContentTest-এ attachment column mismatch:** `storedPortalFile()`-এর generic key সরাসরি Notice/Homework/Material-এ spread করায় `disk`/`path` column না পাওয়া QueryException হচ্ছিল; `attachmentColumns()` ও `materialColumns()` mapping helper দিয়ে সংশোধন করা হয়েছে।
8. **টেস্ট destructuring ও Pint:** `makePortalStudent()`-এর `[User, Student]` return ভুলভাবে destructure করায় `actingAs()`-এ Student যাচ্ছিল — সংশোধিত; `routes/web.php`-এ Pint-এর `no_multiple_statements_per_line` ও একটি অপ্রয়োজনীয় `use RuntimeException` warning সরানো হয়েছে (Pint 618 files PASS)।

## 14. Phase 11 — Reports & Document Center (completed 2026-10-26)

### 14.1 স্কিমা, মডেল ও enum

- ২টি নতুন টেবিল: `certificate_templates` (বাধ্যতামূলক দ্বিভাষিক নাম, type, দ্বিভাষিক title/body, signatory name/title, is_active, created_by/updated_by) ও `issued_certificates` (unique `certificate_no`, template/student/academic year, issued_on/issued_by, notes, private file disk/path/name/size, immutable JSON snapshot)।
- `CertificateType` enum — testimonial, character, transfer, completion, custom; `CertificateTemplate`-এ localized title/body/signatory helper ও placeholder তালিকা; `IssuedCertificate`-এ template/student/year/issuer relationship, snapshot cast ও human-readable file size।
- `config/reports.php` — per-page 25, print/PDF row limit 2,000, CSV/XLSX format, private certificate disk (ডিফল্ট `local`) ও `certificates/YYYY/MM` folder, ১০টি placeholder।
- ডেভ MySQL-এ ২টি মাইগ্রেশন প্রয়োগ; **কোনো নমুনা শিক্ষার্থী, সার্টিফিকেট বা রিপোর্ট ডেটা সিড করা হয়নি**।

### 14.2 রিপোর্ট ইঞ্জিন, কুয়েরি ও ফিল্টার

- `ReportDefinition`/`ReportColumn`/`ReportFilter`/`ReportSource` ভিত্তিক পুনর্ব্যবহারযোগ্য ইঞ্জিন; `QueryReportSource` (paged rows, count, totals, summary) ও `ArrayReportSource`; `ReportRunner` ফিল্টার resolve/validate, date-range guard, page clamp (1..last) ও per-page 10–200 clamp করে; `ReportResult`, `ReportFormatter`, `ReportOptions`, `ReportAccess`, `ReportRegistry`।
- ৪ গ্রুপে ১৭টি রিপোর্ট: Academic — admissions, enrollment, attendance, exams, results; HR — staff attendance, payroll, leave; Finance — fee collections, arrears, income, expenses, donations, cash & bank, ledger; Operations — inventory, library circulation। প্রতিটি রিপোর্টে `reports.view` + সংশ্লিষ্ট domain permission।
- Academic year, financial year, division/class/section, date/month range, status, exam, payment method, department/designation, leave type, account, campaign, item/category, loan status প্রভৃতি প্রাসঙ্গিক ফিল্টার; dependent select (division→class→section, year→exam, category→item) server-side option list থেকে যাচাই হয় — client-এর পাঠানো id কখনো authorization হিসেবে গৃহীত হয় না।
- Role mapping: admin পূর্ণ; accountant/office-staff/librarian/hr-manager reports view/export/print; teacher view/print (export নেই); read-only শুধু view; parent/student-এর কোনো রিপোর্ট access নেই।

### 14.3 Print, PDF ও export

- Report Center-এ server-side ফিল্টার, default মান, pagination, totals ও summary row; print/PDF-এ প্রতিষ্ঠানের নাম, স্লোগান, ফিল্টার সারাংশ, প্রস্তুতির সময় ও পৃষ্ঠা নম্বর থাকে।
- PDF: mPDF + Hind Siliguri font embedding; রিপোর্টভেদে A4 portrait/landscape; ২,০০০-এর বেশি সারিতে `ValidationException` (মেমোরি সুরক্ষা)। Print একই A4 HTML ভিউ ব্যবহার করে।
- CSV: UTF-8 BOM + formula-injection guard (`=`, `+`, `-`, `@`, tab, CR দিয়ে শুরু হলে `'` prefix), header ও totals row। XLSX: OpenSpout streaming writer, bold header/total style, `storage/app/exports`-এ সাময়িক ফাইল তৈরি করে send-এর পরে মুছে যায়।
- Audit: `report.printed`, `report.pdf_generated`, `report.exported` (report key, filters, format, row count)। PDF inline, CSV/XLSX attachment; filename report key + timestamp।

### 14.4 Document Center, টেমপ্লেট ও সার্টিফিকেট

- Document Center: template/active template/issued/এই মাসে issued পরিসংখ্যান, সাম্প্রতিক ৮টি সার্টিফিকেট এবং permission-aware action।
- Certificate template manager: create/edit, activate/deactivate, delete (শুধু কোনো issued certificate না থাকলে), দ্বিভাষিক title/body ও signatory, ১০টি placeholder-এর validation।
- Issuance: active template + student + academic year + issue date + notes; `NumberSequenceService` দিয়ে `CERT-YYYYMM-00001`; localized template/student/institution snapshot সংরক্ষণ; PDF private `local` disk-এ; DB transaction ব্যর্থ হলে ফাইল cleanup; audit `certificates.issued`।
- Issued certificate manager: search, template/year/date filter, pagination, download ও delete (ফাইল+রেকর্ড, আগে audit `certificates.deleted`)। Issued PDF immutable — পরে student/template সম্পাদনা করলে পুরোনো সার্টিফিকেট বদলায় না।
- Secure download: `certificates.view` gate, private disk stream, `Content-Type: application/pdf`, `Cache-Control: private, no-store`, `X-Content-Type-Options: nosniff`; ফাইল অনুপস্থিত হলে 404; Windows path separator normalize করে শুধু basename filename পাঠানো হয়; audit `certificates.downloaded`।

### 14.5 UI, permissions, routes ও ভাষা

- Report Center (৪ গ্রুপে permission-filtered report card), Report Viewer (Livewire) ও Document Center; বিদ্যমান maroon/gold design system, Poppins/Hind Siliguri, dark mode ও EN/BN switcher অপরিবর্তিত; sidebar-এ Reports ও Documents যোগ করা হয়েছে।
- ৮টি নতুন permission — `reports.view/export/print`, `certificates.view/create/edit/delete/issue`; মোট ২৫৩, role ১০। Admin পূর্ণ; accountant/office-staff/librarian/hr-manager reports view/export/print; teacher view/print; read-only শুধু view; office-staff certificates.view/issue; read-only certificates.view; parent/student-এর কোনো access নেই।
- ৯টি নতুন route: `/reports/center` (index, show, print, pdf, export/{csv|xlsx}) ও `/documents` (index, templates, certificates, download); সব auth + permission middleware-এ সুরক্ষিত, export ও print আলাদা permission; CSRF, throttle ও audit অটুট।
- ৯টি Phase 11 ভিউয়ের ১০২টি translation key EN ও BN দুই locale-এ সম্পূর্ণ (0 missing, 0 empty); মোট key parity ৩,৮৯৩। নতুন Tailwind class-এর জন্য `npm run build` চালানো হয়েছে।

### 14.6 Phase 11 verification

- `php artisan test tests/Feature/Reports tests/Feature/Documents` → **31 passed (292 assertions)**, 92.67s: ReportEngineTest ৭, ReportCenterTest ১১, ReportExportTest ৫, CertificateServiceTest ২, DocumentCenterTest ৬। attendance/fee/arrears totals source rows-এর সঙ্গে মিলিয়ে যাচাই; dependent filter/reset/validation, permission-before-filter, export/print denial, CSV BOM/formula escaping, XLSX workbook, PDF row limit, certificate snapshot/PDF/numbering, inactive template rejection, secure download headers, missing-file 404, template deletion guard ও audit পরীক্ষিত।
- সম্পূর্ণ স্যুট → **368 passed (3,337 assertions)**; Phase 01–10-এর সব টেস্ট অটুট।
- `./vendor/bin/pint --test` → **PASS (655 files)**।
- ব্রাউজার যাচাই (EN + BN, dev server 8080): QA year/class/section/student (QA-P11-0001), একটি template, একটি issued certificate (CERT-202610-00001) ও তিন QA user (admin, read-only, office-staff)। Admin: Report Center-এ ১৭টি রিপোর্ট, viewer-এ ফিল্টার/totals/pagination, print, PDF ও CSV/XLSX download; Document Center-এ template তৈরি/সম্পাদনা এবং সার্টিফিকেট issue/download/delete। Read-only: CSV/XLSX/print/PDF → 403, template পৃষ্ঠা 200, certificate download 200, issue/delete button অনুপস্থিত। Office-staff: CSV 156 B, XLSX 4,613 B, PDF 30,086 B, print 7,319 B — সব 200; issue visible, template delete hidden। Certificate download response: 41,456 B PDF, attachment filename `CERT-202610-00001.pdf`, `Cache-Control: no-store, private`, `nosniff`; বাংলা certificate ও enrollment report PDF-এ Hind Siliguri সঠিকভাবে রেন্ডার হয়েছে।
- QA শেষে ৩টি QA user, year/class/section/student/enrollment, template, certificate, number sequence, sessions ও Phase 11 activity log সম্পূর্ণ মুছে ফেলা হয়েছে। ডেভ DB বেসলাইন: ১ জন আসল Super Admin, ১০টি role, ২৫৩টি permission, ৬টি division, ২৪টি ledger account, ১টি cash ও ১টি bank account, ২টি library policy; শিক্ষার্থী/সার্টিফিকেট/রিপোর্ট ডেটা ও certificate ফাইল শূন্য।

### 14.7 যাচাইকালে পাওয়া ও সংশোধিত বিষয়

1. **Windows path separator:** stored certificate file name/path-এ backslash থাকলে Content-Disposition-এ পূর্ণ path যেতে পারত; `str_replace('\\', '/')` + `basename()` দিয়ে শুধু filename পাঠানো হয় এবং ব্রাউজারে যাচাই করা হয়েছে।
2. **CSV formula injection:** `=`, `+`, `-`, `@`, tab ও CR দিয়ে শুরু হওয়া টেক্সট `'` prefix পায়; regression test-এ যাচাই করা হয়েছে।
3. **PDF/print row limit:** সীমার বেশি সারিতে PDF জেনারেশন মেমোরি নষ্ট না করে স্পষ্ট validation বার্তা দেয়; CSV/XLSX সব মিলে যাওয়া সারি stream করে।
4. **Client filter hardening:** array input বাতিল, select মান server-side option list-এ সীমাবদ্ধ এবং `from > to` প্রত্যাখ্যাত — authorization শুধু permission থেকে আসে, ফিল্টার থেকে নয়।
5. **Pint drift:** ৪টি Phase 11 ফাইলে style violation পাওয়া যায়; Pint চালিয়ে 655 files PASS নিশ্চিত করা হয়েছে।
6. **Translation scan false positive:** প্রথম স্ক্যানে key-এ `app.` দুইবার যোগ হচ্ছিল; স্ক্রিপ্ট সংশোধন করে ৯টি ভিউয়ের ১০২টি key-এ 0 missing/empty যাচাই করা হয়েছে।

## 15. Phase 12 — Settings, Security & Backup (completed 2026-10-27)

### 15.1 সেটিংস রেজিস্ট্রি, স্কিমা ও config

- `settings` টেবিল (unique `key`, indexed `group`, `value`, `type`) এবং `App\Services\SystemSettings` — typed `DEFINITIONS` রেজিস্ট্রি, built-in defaults, `get/put/putMany/forget`। `App\Services\Settings` প্রতি key আলাদা `Cache::rememberForever('settings:{key}')` ক্যাশ করে এবং লেখার সঙ্গে সঙ্গে invalidate করে; DB-তে না থাকা key ডিফল্টে ফিরে যায়।
- নিয়ন্ত্রিত (clamped) accessor: password ন্যূনতম দৈর্ঘ্য ৮–৬৪, login প্রচেষ্টা ৩–২০, lockout ৩০–৯০০ সেকেন্ড, idle ০ (=বন্ধ) অথবা ৫–৪৮০ মিনিট, fee due day ১–২৮, sequence width ৪–৮, retention ১–৩৬৫০ দিন / ১–১০০ কপি। প্রিফিক্স uppercase + নির্ধারিত অক্ষর-সেটে সীমিত; সীমার বাইরের মান `ValidationException`।
- ১২টি নতুন permission (মোট ২৬১): `settings.view/edit`, `audit.view/export`, `backup.view/create/verify/download/delete`, `data_transfer.view/export/import`। `admin` ও `super-admin` পূর্ণ; read-only ও teacher-এর এই মডিউলে কোনো access নেই।
- `config/backup.php` — private `local` ডিস্ক, `backups` path, retention ৩০ দিন/২০ কপি, ৬০০s timeout, routines/events ডিফল্টে বন্ধ, mysqldump/mysql বাইনারি, dedicated restore connection ও disposable-DB prefix; `backup.connection` hook টেস্টে MySQL-আকৃতির connection ব্যবহার করতে দেয়।
- নতুন `database_backups` টেবিল (unique filename, disk/path, size, SHA-256, status, manifest JSON, failure_reason, created_by/verified_by/verified_at) — dev MySQL-এ migration batch 15 হিসেবে প্রয়োগ হয়েছে। **কোনো নমুনা শিক্ষার্থী বা লেনদেন সিড করা হয়নি।**

### 15.2 সেটিংস UI: প্রতিষ্ঠান, সিকোয়েন্স, ভাষা ও নোটিফিকেশন

- Settings → General পৃষ্ঠায় প্রতিষ্ঠান পরিচয় (EN/BN নাম ও স্লোগান), লোগো (PNG/JPEG/WebP, সর্বোচ্চ ২ MB, public branding ফোল্ডার, পুরোনো ফাইল প্রতিস্থাপনে মুছে যায়), যোগাযোগ তথ্য (ঠিকানা EN/BN, ফোন, ইমেইল, ওয়েবসাইট, EIIN), বর্তমান একাডেমিক বছর, মাসিক ফি due day, ১২টি ডকুমেন্ট প্রিফিক্স ও sequence width, ডিফল্ট ভাষা + ভাষা-সুইচার, ইমেইল/SMS master switch।
- একাডেমিক বছর বদলালে আগের `is_current` flag পড়ে যায়; প্রিফিক্স ও width নতুন ডকুমেন্টে কার্যকর হয়, পুরোনো নম্বর অপরিবর্তিত থাকে। সব সেভ `settings.updated`, সিকিউরিটি নীতি `settings.security_updated`, retention `settings.backup_retention_updated`, লোগো `settings.logo_removed` audit করে।
- Values server-side validate হয় (নাম/স্লোগান, ওয়েবসাইট/ইমেইল URL, prefix format, সীমা); permission ছাড়া পৃষ্ঠা view-only, save call করলে Livewire `authorize()`-এ 403।

### 15.3 সিকিউরিটি পলিসি, হেডার, সেশন ও `security:check`

- `SecuritySettingsPage` (Settings → Security): password ন্যূনতম দৈর্ঘ্য, সংখ্যা/চিহ্ন বাধ্যতামূলক, login max attempts, lockout window, idle session timeout। মানগুলো login throttling, password rule ও `EnforceSessionIdle` middleware-এ সরাসরি প্রযোজ্য।
- `SecurityHeaders` middleware: `X-Content-Type-Options: nosniff`, `X-Frame-Options: SAMEORIGIN`, strict-origin referrer, permissions policy; HSTS শুধু HTTPS request-এ।
- `SecurityAuditService::checks()` (পঠনযোগ্য, কোনো গোপন মান প্রকাশ করে না): app key, debug, environment, session driver/encryption/cookie flags, CSRF, rate limit, idle timeout, upload allowlist, private backup storage, public storage link, active super admin; UI-তে `pass/warn/fail` ব্যাজ। `php artisan security:check` একই চেক CLI টেবিলে দেখায় এবং `--without-dependencies` ছাড়া `composer audit` চালায়।
- ডেভ রানে `security:check` → **০টি blocking failure**, ৪টি প্রত্যাশিত local warning (debug, local env, `SESSION_SECURE_COOKIE`, idle timeout বন্ধ); `dependency_audit` PASS (“Composer reports no known vulnerabilities”)।

### 15.4 অডিট লগ: অনুসন্ধান, লালন (redaction) ও CSV এক্সপোর্ট

- `AuditLogService` — text (description/properties), log name, event, causer, তারিখ-সীমা ফিল্টার; causer email/নাম সার্চ; distinct log/event/causer option; pagination; `filtersFromRequest` validation।
- Redaction recursive: password/token/secret/authorization/card/pin/otp-জাতীয় property, এমনকি nested array-ও `[redacted]`; `pin`/`otp` শুধু exact বা `_pin`/`_otp` suffix মিললে (যাতে `shipping_address` অক্ষত থাকে); দীর্ঘ text ২,০০০ অক্ষরে কাটা হয়।
- CSV export: UTF-8 BOM, নিরাপদ কলাম, একই লালন, spreadsheet formula guard (`=`, `+`, `-`, `@`, tab, CR → `'` prefix), `audit.exported` audit, `audit.view` + `audit.export` দুটোই আবশ্যক।
- Audit পৃষ্ঠায় ফিল্টার + View detail (redacted); `audit.view` থাকলেও export permission না থাকলে CSV 403।

### 15.5 ডেটা ট্রান্সফার: student CSV export, template, preview ও atomic import

- `StudentDataTransferService` — canonical ২১-কলাম HEADERS, template download (BOM+headers), active student + primary guardian export (formula-safe), এবং upload→validate→preview→import flow।
- Preview কখনো লেখে না; প্রতি সারিতে required value, option code/name resolve (academic year, division, class, section), duplicate UID, duplicate নাম+জন্মতারিখ, duplicate roll (DB ও ফাইলের ভেতরে) চিহ্নিত করে; header missing/unknown/duplicate, empty file, extra column ও ১০০০-সারির বেশি হলে স্পষ্ট validation error।
- Import সব-or-কিছু (DB transaction): auto UID `SMM-{YEAR}-{0001}`, guardian relation, enrollment, serial; ব্যর্থ হলে কোনো শিক্ষার্থী/audit লেখা হয় না। সফল import/export `students.imported` / `students.exported` audit করে; `data_transfer.export/import` আলাদা permission (view-only শুধু preview পৃষ্ঠা দেখতে পারে, import/export 403)।

### 15.6 ব্যাকআপ, রিটেনশন ও restore drill

- `BackupService`: `mysqldump` (Symfony Process, array arguments, shell নেই) → gzip → private disk, SHA-256, manifest (table list, core counts, server version), `DatabaseBackup` row। পাসওয়ার্ড কখনো কমান্ড-লাইনে যায় না — ০৬০০ অনুমতির অস্থায়ী `[client]` ফাইল `--defaults-extra-file` দিয়ে, `finally`-তে মুছে যায়।
- Verify: checksum + gzip header পুনঃগণনা; mismatch → `failed` + কারণ (silent repair নেই)। Delete: ফাইল+রেকর্ড, audit। Retention: newest N কপি সবসময় থাকে, তার বাইরে age-এর বেশি হলে মুছে যায়; শেষ কপি কখনো মুছে যায় না।
- Download route (permission + usable status + path guard) private disk stream; `absolutePath` traversal/absolute path/drive-letter/অন্য ডিস্ক ও public-root ডিস্ক প্রত্যাখ্যান করে; restore target হিসেবে live/system/malformed ডাটাবেস নাম প্রত্যাখ্যাত।
- Browser-এ restore বা arbitrary command নেই; `backup:create`, `backup:restore {filename} --database=…`, `backup:test-restore` কেবল CLI। `testRestore` disposable database তৈরি → restore → manifest table ও core row counts মিলিয়ে দেখা → DROP।
- **প্রকৃত restore drill (2026-10-27):** `sylhet_model_madrasah_2026_10_27_114406.sql.gz`, SHA-256 `9d9c73a8…a7bc18e`, ১১৪ টেবিল, `users=1`, `students=0`, `migrations=107`, `academic_years=0`; disposable DB drop নিশ্চিত; documentation `docs/BACKUP_RESTORE.md`-এ (retention, scheduling, dedicated restore account, disaster-recovery ধাপ, troubleshooting)।
- Backup পৃষ্ঠায় stored count, private storage used, latest/latest-verified, retention ফর্ম ও প্রতি-সারির Verify/Download/Delete; `backup.*` permission অনুযায়ী action দেখা যায়।

### 15.7 UI, routes ও ভাষা

- ৫টি নতুন পৃষ্ঠা: `/settings`, `/settings/security`, `/settings/backups`, `/data-transfer`, `/audit` (+ `/settings/backups/{backup}/download`, `/data-transfer/export`, `/data-transfer/template`, `/audit/export`); সব `auth` + `permission:*` middleware-এ সুরক্ষিত, বিদ্যমান maroon/gold ডিজাইন, sidebar ও Administration ট্যাব অপরিবর্তিত ধাঁচে যুক্ত।
- Phase 12-এর সব UI string EN ও BN দুই locale-এ; `settings.*` ২৫৩, `audit.*` ২৯ key — মোট parity ৪,১৮১ key, ০ missing/০ empty; বাংলা পৃষ্ঠাগুলো ব্রাউজারে যাচাই করা হয়েছে। ডিফল্ট ভাষা নির্বাচনে raw key-এর বদলে `English`/`বাংলা` দেখানো হয়।

### 15.8 Phase 12 verification

- `php artisan test tests/Feature/Settings tests/Feature/Security tests/Feature/Audit` → **58 passed (336 assertions)**, 129.56s (SettingsPages ৯, SecurityControls ১১, AuditLog ৭, DataTransfer ১২, Backup ১৯)।
- সম্পূর্ণ স্যুট → **426 passed (3,676 assertions)**, 646.63s (`php artisan test --parallel --processes=4`, exit 0; log `storage/logs/full-suite-parallel.log`)। একক-প্রসেস রান mPDF OOM (১২৮ MB) সংশোধনের আগে ও পরের দুটি host reboot-এ বিচ্ছিন্ন হয়েছিল, তাই চূড়ান্ত সবুজ রানটি parallel-এ নেওয়া হয়।
- `./vendor/bin/pint --test` → **PASS (684 files)**।
- `php artisan security:check` → ১৪টি চেক, ০ blocking failure, ৪ warn (local) এবং dependency audit PASS; `composer audit` আলাদাভাবে “No security vulnerability advisories found”।
- ব্রাউজার যাচাই (EN + BN, dev server 8080): super-admin দিয়ে General/Security/Backups/Data transfer/Audit পৃষ্ঠা, ট্যাব, ফর্ম মান, retention ফর্ম, backup সারি (Verified, ২৮.৯ KB, ১১৪ tables), audit ফিল্টার ও সারি, বাংলা অনুবাদ ও ভাষা সুইচার দেখা হয়েছে। কোনো নমুনা শিক্ষার্থী import করা হয়নি; destructive save করা হয়নি।
- QA শেষে QA super-admin, তার session ও audit সারি, অস্থায়ী পাসওয়ার্ড ফাইল এবং টেস্ট-সৃষ্ট temp ফাইল মুছে ফেলা হয়েছে; ডেভ DB-তে শুধু আসল Super Admin (১ user), ১০ role, ২৬১ permission, ৪টি institution setting ও ১টি verified ব্যাকআপ আছে — শিক্ষার্থী/লেনদেন শূন্য।

### 15.9 যাচাইকালে পাওয়া ও সংশোধিত বিষয়

1. **ডিফল্ট ভাষা dropdown-এ raw translation key:** `__('app.language.'.$locale)` key না থাকায় `app.language.en`/`app.language.bn` দেখাচ্ছিল; `config('brand.locale_names.'.$locale)`-এ বদলে `English`/`বাংলা` দেখানো হয় এবং টেস্টে রক্ষা করা হয়েছে।
2. **সেটিংস পৃষ্ঠায় language key অনুপস্থিত:** ব্যবহৃত key-এর অস্তিত্ব যাচাই করে দেখা যায় সেটিংস পেজ ভিন্ন key ব্যবহার করছিল; উপরের config-ভিত্তিক সমাধানেই দুই ভাষার নাম সঠিক।
3. **CSV test helper temp-file leak:** `tempnam()` ফেরত দেওয়া `.tmp` ফাইল `rename` না করে `.csv` suffix যোগ করায় প্রতিটি কল একটি শূন্য-বাইট temp ফাইল রেখে যেত (২৬টি জমেছিল); `rename` করে ঠিক করা হয়েছে এবং জমা ফাইল মুছে ফেলা হয়েছে।
4. **সম্পূর্ণ স্যুটে mPDF memory exhaustion:** ১২৮ MB `memory_limit`-এ দীর্ঘ single-process রানে Portal টেস্টের সময় mPDF ফন্ট লেখার সময় fatal error; টেস্ট-only `phpunit.xml`-এ `memory_limit=512M` যোগ করা হয়েছে (production config অপরিবর্তিত)।
5. **Fatal dump failure leak:** অনুপস্থিত mysqldump বাইনারিতে ব্যাকআপ ব্যর্থ হলে ফাঁকা dump ফাইল থাকত না — `finally`-তে cleanup; `safeMessage()` credential pattern ও path লুকিয়ে ছোট কারণ সংরক্ষণ করে।
6. **Restore access model:** অ্যাপ অ্যাকাউন্ট least-privilege হওয়ায় `CREATE DATABASE` নেই; dedicated `smm_restore` account ও wildcard grant শুধু `smm_restore_test_%`-এ সীমিত রাখার নিয়ম ডকুমেন্ট করা হয়েছে (আগের ড্রিলে `Access denied` এটাই কারণ ছিল)।
7. **Duplicate roll detection:** শুধু DB নয়, একই CSV-এর ভেতরের duplicate roll-ও ধরা পড়ে; roll duplicate পেলে name+DOB চেক short-circuit হয় — টেস্ট দুটি আলাদা case-এ ভাগ করে যাচাই করা হয়েছে।
8. **Windows temp-file naming:** `tempnam()` prefix ৩ অক্ষরে কাটে; restore/dump temp ফাইল rename করে suffix দেওয়া হয়, তাই নাম-সংঘর্ষ বা ভুল cleanup হয় না।
9. **Host reboot during verification:** ২০২৬-১০-২৭ তারিখে ১২:৪৬ ও ১৩:২৩-এ দুবার অপ্রত্যাশিত Windows reboot হয়; প্রথম reboot-এ ৩২১টি টেস্ট (০ failure) পার হওয়া single-process রান বিচ্ছিন্ন হয়, দ্বিতীয়টিতে তৃতীয় রানের ~১৩ টেস্টের পর। প্রতিবার MySQL ও dev-server বন্ধ হয়ে যায় এবং `Start-Process … mysqld.exe --defaults-file=…` দিয়ে MySQL ও `php artisan serve --port=8080` দিয়ে dev server ম্যানুয়ালি পুনরায় চালু করা হয়। reboot-এর পর dev DB অক্ষত যাচাই: ১ user, ১০ role, ২৬১ permission, ১ verified backup, ০ student। চূড়ান্ত সবুজ রানটি (`--parallel --processes=4`, 426 tests) পরবর্তী reboot-এর আগেই সম্পন্ন হয়।

## 16. Phase 13 — Integration, QA & Deployment Readiness (completed 2026-10-27)

### 16.1 অডিটের পরিধি

- সম্পূর্ণ অ্যাপ্লিকেশনের end-to-end অডিট: ১৮২টি রুট, ২৬১টি পারমিশন, ১০টি রোল, ১০৭টি মাইগ্রেশন/১১৪টি টেবিল, কনফিগ, লগিং, পেজিনেশন, ইনডেক্স, সিকিউরিটি হেডার, ব্যাকআপ/রিস্টোর এবং সব মডিউলের ক্রিটিক্যাল ফ্লো।
- নতুন কোয়ালিটি টেস্ট স্যুট (৪ ফাইল, ১৭ টেস্ট): রুট/পারমিশন, ভাষা, অ্যাকাউন্টিং রিকনসিলিয়েশন ও রিগ্রেশন।
- ব্রাউজার-ভিত্তিক রেসপন্সিভ QA (headless Chrome CDP): অ্যাডমিনের ৮টি পৃষ্ঠা × ৩টি ব্রেকপয়েন্ট (ডেস্কটপ/ট্যাবলেট/মোবাইল) ইংরেজিতে এবং ৪টি পৃষ্ঠা × ৩টি ব্রেকপয়েন্ট বাংলায়; ডার্ক ও লাইট — উভয় মোডে।
- উইন্ডোজ/XAMPP, cPanel ও VPS-এর জন্য ধাপে-ধাপে ডিপ্লয়মেন্ট গাইড, ফিচার চেকলিস্ট ও go-live চেকলিস্ট তৈরি — কোথাও বাস্তব credential নেই।
- **কোনো উৎপাদন সার্ভারে ডিপ্লয় করা হয়নি এবং উৎপাদন ডেটা পরিবর্তন করা হয়নি** (অনুমোদন সাপেক্ষে)।

### 16.2 নতুন কোয়ালিটি টেস্ট

- `RouteAuditTest` (৭): প্রতিটি অ্যাডমিন রুটে `auth` + স্পষ্ট permission; পোর্টাল রুট authenticated কিন্তু ইচ্ছাকৃতভাবে permission-মুক্ত; শুধু রেজিস্ট্রিভুক্ত permission ব্যবহার; প্রতিটি permission/role ঠিক একবার; ডুপ্লিকেট/অনুপস্থিত রুট নাম নেই; super-admin-এ সব প্যারামিটারবিহীন অ্যাডমিন স্ক্রিন ২০০; permissionহীন সাইন-ইন ইউজার ড্যাশবোর্ডে 403।
- `LocalizationAuditTest` (৪): EN/BN key parity, খালি মান নেই, ডিফল্ট ইংরেজি ও শুধু en/bn, বাংলা পৃষ্ঠায় raw `app.*` key leak নেই।
- `AccountingIntegrityTest` (৩): fee invoice + cash collection-এর পরে লেজার balanced; প্রতিটি voucher type balanced ও reversible; trial balance ও ledger মোট journal line-এর সঙ্গে মেলে।
- `RegressionAuditTest` (৩): party filter-সহ voucher report; gateway readiness environment নয়, config থেকে; fundraising campaign title দুই ভাষায়।

### 16.3 যাচাইকালে পাওয়া ও সংশোধিত ত্রুটি

1. **সাইডবারে ইংরেজি স্লোগান overflow:** ২৫৬px সাইডবারে brand block ২৬৬px হয়ে ডান দিকে ৩৩৪px পর্যন্ত বেরিয়ে যাচ্ছিল (মোবাইল/ট্যাবলেটে লেআউট ভাঙার ঝুঁকি)। `resources/views/components/brand.blade.php`-এ root-এ `min-w-0` যোগ করে ঠিক করা হয়েছে; পুনঃমাপে ১৭২px (ডান প্রান্ত ২৪০px) এবং স্ক্রিনশটে নিশ্চিত।
2. **Pint drift:** নতুন দুটি কোয়ালিটি টেস্ট ফাইলে style violation; ফাইল দুইটিতে Pint চালিয়ে সংশোধিত।
3. **XLSX export-এ ফাইল-নাম সংঘর্ষ (parallel স্যুটে ধরা পড়ে):** একই মিনিটে একই রিপোর্টের দুটি export একই `storage/app/exports/{report}-report-{timestamp}.xlsx` ফাইল ব্যবহার করত; একটি response ফাইলটি মুছে দিলে অন্যটি পড়তে ব্যর্থ হত (`Could not open … for reading`). `ReportExporter::xlsx()`-এ প্রতিটি রিকোয়েস্টের জন্য random suffix-সহ অনন্য টেম্প-ফাইল তৈরি করা হয়েছে (ডাউনলোডের নাম অপরিবর্তিত), এবং একই টেস্ট-ক্লকে পরপর দুটি export ভিন্ন path পায়—এমন regression টেস্ট যোগ করা হয়েছে। এটি শুধু টেস্ট-ফ্লেক নয়, একসঙ্গে দুজন ব্যবহারকারীর export-এও ঘটত।
4. **ডকুমেন্টেশনে ভুল অক্ষর:** `PROGRESS.md`-এ অনুপ্রবেশ করা অ-বাংলা অক্ষর (`발견`, `প্রয়োগ됨`) সংশোধন।
5. **Restore drill ব্লক — ত্রুটি নয়, নকশা:** least-privilege অ্যাপ অ্যাকাউন্টে `CREATE DATABASE` নেই, তাই নতুন ডাম্পে disposable restore চালানো যায়নি; dedicated `BACKUP_RESTORE_*` অ্যাকাউন্টের প্রয়োজন স্পষ্টভাবে ডকুমেন্ট করা হয়েছে এবং Phase 12-এর সফল ড্রিল প্রমাণ হিসেবে রাখা হয়েছে।

### 16.4 যাচাইয়ের প্রমাণ

- `php artisan test tests/Feature/Quality` → **১৭ passed (৪১৭ assertions)**।
- সম্পূর্ণ স্যুট (`php artisan test --parallel --processes=4`) → **৪৪৪ passed (৪,০৯৬ assertions)**, ৮৬৪.৫৫s, exit 0 (`storage/logs/full-suite-phase13-rerun.log`)।
- `vendor/bin/phpstan analyse --memory-limit=1G` (level 1) → **No errors**; ডিফল্ট ১২৮ MB limit-এ parallel worker OOM হয়, তাই গাইডে ১G সুপারিশ; level 5-এ ৬১৪টি type finding (runtime ত্রুটি নয়) — সীমাবদ্ধতা হিসেবে নথিভুক্ত।
- `vendor/bin/pint --test` → **PASS (688 files)**।
- `php artisan security:check` → ১৪টি চেক, **০ blocking failure**, ৪টি প্রত্যাশিত local warning; `composer audit` → কোনো known vulnerability নেই।
- মাইগ্রেশন: ডিসপোজেবল ডাটাবেসে fresh → rollback → migrate সফল; ১০৭ মাইগ্রেশন, ১১৪ টেবিল; dev DB-তে pending নেই।
- পারমিশন: ২৬১ permission / ১০ role; config↔DB parity ও route usage টেস্টে যাচাই।
- ভাষা: EN/BN key parity, খালি মান নেই, raw key leak নেই; ডিফল্ট ইংরেজি।
- Responsive QA: ৮ পৃষ্ঠা × ৩ width (EN) + ৪ পৃষ্ঠা × ৩ width (BN), horizontal overflow শূন্য; ১টি প্রকৃত overflow বাগ ধরা পড়ে ও ঠিক হয়েছে।
- পারফরম্যান্স: নমুনা পৃষ্ঠায় ১৯–৩১টি query, N+1 চোখে পড়েনি; তালিকাগুলো paginated।
- ব্যাকআপ: SHA-256 `41a1ca6b…fdf64e2`, gzip integrity OK, ১১৪ টেবিল ও manifest মিলেছে; রিস্টোর অ্যাকাউন্ট-সীমাবদ্ধতা ডকুমেন্টেড।
- ব্রাউজার QA শেষে অস্থায়ী QA super-admin, তার session/audit সারি ও সব scratch artifact মুছে ফেলা হয়েছে; dev DB-তে শিক্ষার্থী/লেনদেন শূন্য।

### 16.5 ডেলিভারেবল

- `docs/DEPLOYMENT_WINDOWS_XAMPP.md` — Windows 11/XAMPP: PHP এক্সটেনশন, php.ini, DB grant, migrate/seed, `make:superadmin`, Apache vhost, backup Task Scheduler, upgrade/rollback, troubleshooting।
- `docs/DEPLOYMENT_CPANEL.md` — docroot `public`, PHP/extensions, upload/vendor, DB import, AutoSSL, cron, ModSecurity, rollback।
- `docs/DEPLOYMENT_VPS.md` — Ubuntu/Nginx/PHP-FPM 8.3/MySQL, releases + symlink, `.env`, systemd/cron, certbot, hardening, deploy/rollback স্ক্রিপ্ট।
- `docs/FEATURE_CHECKLIST.md` — ১২ মডিউল, ১৭ রিপোর্ট, ১০ রোল, cross-cutting সক্ষমতা ও ইচ্ছাকৃত বাদ।
- `docs/GO_LIVE_CHECKLIST.md` — blocking আইটেম, সাক্ষর ছক, known limitations ও উৎপাদন-পূর্ব শর্ত।
- সব গাইডে: APP_KEY একবারই তৈরি, `storage:link` ইচ্ছাকৃতভাবে নিষিদ্ধ, queue/scheduler বর্তমানে ব্যবহৃত নয়, HTTPS ও private backup ডিস্ক বাধ্যতামূলক, কোনো বাস্তব credential নেই।

## 17. Known limitations / notes

1. Dark-mode/light-mode ও mobile sidebar headless Chrome-এ যাচাই হয়েছে (Phase 13); sidebar collapse-এর pointer-level interaction এখনো একবার ম্যানুয়ালি দেখে নেওয়া উচিত।
2. Port 8000 blocked by Windows on this machine — dev server uses 8080.
3. spatie/activitylog v4 does not publish its migrations via `vendor:publish` (package-tools behavior); a project-owned migration with the final schema (incl. `event`, `batch_uuid`) was created instead.
4. Livewire resolved to v4.4 (not v3 as originally planned) — current stable for Laravel 12; component conventions used are v3/v4-compatible.
5. Official logo not yet provided — generic favicon/icon placeholder in use. Never invented the official logo.
6. উইন্ডোজ/XAMPP, cPanel ও VPS ডিপ্লয়মেন্ট গাইড এবং go-live চেকলিস্ট Phase 13-এ তৈরি হয়েছে (`docs/DEPLOYMENT_WINDOWS_XAMPP.md`, `DEPLOYMENT_CPANEL.md`, `DEPLOYMENT_VPS.md`, `GO_LIVE_CHECKLIST.md`); বাস্তব উৎপাদন ডিপ্লয় এখনো অনুমোদন সাপেক্ষে বাকি।
7. `make:superadmin --generate` prints the password to the console once (by design); non-interactive/piped hidden input is not supported by laravel/prompts on this machine.
8. Password reset e-mails use the `log` mailer in dev (messages land in `storage/logs/laravel.log`); SMTP configuration is a deployment-phase task.
9. অডিট লগ UI (search/detail/CSV) Phase 12-এ সম্পন্ন; `activity_log`-এ Phase 02–12-এর admissions, students, academics, attendance, exams, grading, hifz, fees, ledger, payroll, HR, library, inventory, portals, reports, certificates, settings, backup ও data-transfer ইভেন্ট লেখা হয়।
10. ড্যাশবোর্ডে একাডেমিক বর্ষ ও (কনফিগার থাকলে) বাস্তব আর্থিক সারসংক্ষেপ দেখায়; কোনো বর্ষ/অর্থবছর কনফিগার না থাকলে setup prompt দেখায়। আরও চার্ট/ট্রেন্ড ভবিষ্যতে যোগ করা যাবে।
11. Promotion/transfer প্যানেলের preview ও চূড়ান্ত batch submit ব্রাউজারে সম্পন্ন করা হয়েছে (নতুন enrollment, exit type, roll ও অডিট যাচাইসহ); class-transfer-এর UI submit স্বয়ংক্রিয় টেস্টে সম্পূর্ণ কভার করা হয়েছে।
12. মডিউলভিত্তিক পুরোনো প্রিন্ট ভিউগুলো (attendance, exams, payslip, voucher ইত্যাদি) এখনো A4 HTML ও ব্রাউজার প্রিন্ট; Phase 11-এ Report Center ও সার্টিফিকেটের জন্য সার্ভার-সাইড PDF যোগ হয়েছে, কিন্তু বাকি প্রিন্ট ভিউগুলোর PDF রূপান্তর আলাদা কাজ।
13. টাইমটেবিলে পিরিয়ডের সময়-ওভারল্যাপ (যেমন 09:00–09:45 ও 09:30–10:15) আলাদাভাবে প্রতিরোধ করা হয় না — শুধু শেষ সময় শুরুর পরে হতে হয়; পিরিয়ড কনফিগারেশন অ্যাডমিন-নিয়ন্ত্রিত।
14. পরীক্ষা প্রকাশের পর নম্বর লক থাকে; সংশোধনের প্রয়োজন হলে আগে ফলাফল unpublish করতে হয় (গণনা করা ফলাফল মুছে যায়)। প্রকাশিত ফলাফলের অনুমোদিত re-evaluation/সংশোধনী ওয়ার্কফ্লো ভবিষ্যতের ফেজের কাজ।
15. Promotion-এ target class/section ম্যানুয়ালি নির্বাচন করতে হয় (স্বয়ংক্রিয় next-class ম্যাপিং নেই) — প্রতিষ্ঠানভেদে কাঠামো ভিন্ন হওয়ায় ইচ্ছাকৃত।
16. নম্বর এন্ট্রি গ্রিডে ম্যানুয়াল; নম্বরের বাল্ক CSV import নেই (ফলাফল export আছে)। বড় পরীক্ষায় দ্রুত এন্ট্রির প্রয়োজন হলে ভবিষ্যতে যোগ করা যাবে।
17. অনলাইন ফি গেটওয়ে (bKash/Nagad/Rocket/Card/SSLCommerz) এখনো সংযুক্ত নয় — শুধু readiness/status পৃষ্ঠা আছে। Credentials, sandbox verification, signed callback endpoint, reconciliation ও স্পষ্ট enablement ছাড়া কোনো লাইভ পেমেন্ট গ্রহণ করা যাবে না; staff শুধু cash/bank/manual entry করতে পারে।
18. ডিসকাউন্ট শুধু approved waiver (percentage/fixed)-এর মাধ্যমে; স্বতন্ত্র discount scheme/slab বা বর্ষব্যাপী concession engine ভবিষ্যতের কাজ।
19. Exam fee invoice শুধু exam schedule-ভুক্ত শ্রেণি/সেকশনের শিক্ষার্থীদের জন্য তৈরি হয়; schedule না থাকলে ইনভয়েস হবে না (ইচ্ছাকৃত scope)।
20. Invoice/receipt/refund/journal number মাসিক `number_sequences` থেকে স্বয়ংক্রিয়ভাবে তৈরি হয়; manual override বা UI থেকে sequence reset করার সুযোগ নেই।
21. Refund শুধু verified payment-এর বিপরীতে এবং refundable সীমার মধ্যে করা যায়; ইতিমধ্যে approve করা refund বাতিল/reverse করার UI workflow এখনো নেই — ভবিষ্যতে accounting adjustment হিসেবে যোগ করতে হবে।
22. নতুন permission যোগ হলে migration-এর পরে `PermissionSeeder` ও `RoleSeeder` চালানো আবশ্যক (dev-এ Phase 06-এর সময় এই ধাপটি ধরতে সাহায্য করেছে); গাইডগুলোর ডিপ্লয়মেন্ট ধাপে ও go-live চেকলিস্টে এটি অন্তর্ভুক্ত।
23. Opening balance শুধু প্রথম অর্থবছরে এবং অন্য journal-এর আগে পোস্ট করা যায় — মাঝপথে ERP চালু করা প্রতিষ্ঠানকে প্রারম্ভিক অবস্থা journal voucher দিয়ে আনতে হবে (ইচ্ছাকৃত সুরক্ষা)।
24. Posted voucher সম্পাদনা/unpost করা যায় না; সংশোধন শুধু reversal দিয়ে, এবং reversal-এর আবার reversal করা যায় না। ভুল reversal হলে নতুন সমন্বয় journal voucher দিতে হবে।
25. Bank reconciliation-এ ব্যাংক স্টেটমেন্টের CSV/OFX import বা অটো-ম্যাচিং নেই; manual selection ও statement opening/closing এন্ট্রি করতে হয়।
26. Phase 07-এর direct payroll payment (একক কর্মীর এককালীন বেতন) এবং Phase 08-এর payroll run (মাসিক batch) দুটোই আছে; একই কর্মী ও period-এ দুই পথে পরিশোধ validation দিয়ে নিষিদ্ধ। Direct payment payslip তৈরি করে না—payroll run-ই payslip, advance recovery ও পূর্ণ হিসাব তৈরি করে।
27. Fundraising-এ campaign ও received donation আছে, কিন্তু pledge/commitment বা দাতা-ভিত্তিক দীর্ঘমেয়াদি tracking নেই।
28. Financial year close শুধু draft-মুক্ত ও balanced যাচাই করে বছর লক করে; স্বয়ংক্রিয় year-end closing journal (surplus transfer to equity) তৈরি হয় না — প্রয়োজনে পরবর্তী ফেজে যোগ করতে হবে। Phase 11-এর Report Center-এ income/expenses/donations/cash-bank/ledger রিপোর্টের PDF আছে, তবে মডিউল-ভিত্তিক রিপোর্ট পৃষ্ঠাগুলো এখনো A4 HTML প্রিন্ট ও CSV।
29. Payroll হিসাব শুধু বেতন কাঠামো-ভিত্তিক; উপস্থিতি/LOP, overtime, বোনাস, আয়কর, provident fund, gratuity বা অন্য statutory contribution গণনা এখনো নেই—deduction components ম্যানুয়ালি কনফিগার করতে হয়।
30. প্রতি কর্মীর একটিই বর্তমান salary structure; সম্পাদনা পুরোনো কাঠামো overwrite করে (payslip snapshot থাকায় পুরোনো payslip অক্ষত)। Effective-dated বেতন বৃদ্ধি/রিভিশন ইতিহাস আলাদাভাবে সংরক্ষিত হয় না।
31. Payslip বিতরণের জন্য PDF, ইমেইল/পোর্টাল delivery বা ব্যাংক-ট্রান্সফার advice নেই—A4 প্রিন্ট ও CSV-ই বর্তমান আউটপুট।
32. এক payroll run-এ একটিমাত্র cash বা bank account দিয়ে সম্পূর্ণ net পরিশোধ হয়; একাধিক অ্যাকাউন্টে split payment বা কর্মীভিত্তিক payment method নেই।
33. ছুটির দিন inclusive calendar days হিসেবে গণনা হয়—সাপ্তাহিক ছুটি/সরকারি ছুটি বাদ যায় না; annual quota নির্দিষ্ট হয় starts_on-এর বছরে এবং carry-forward/accrual নেই। ছুটি payroll বা বেতন কর্তনে কোনো প্রভাব ফেলে না।
34. Staff advance শুধু payroll recovery-এর মাধ্যমে ফেরত আসে; সরাসরি নগদ repayment, write-off বা disbursed advance বাতিলের UI নেই। HR-নিয়ন্ত্রিত integrity রক্ষায় payroll/advance voucher Accounts থেকে সরাসরি reverse করা যায় না।
35. Paid payroll সম্পাদনা করা যায় না; সংশোধনের জন্য reversal করতে হয় (এতে advance পুনরুদ্ধার ও period মুক্ত হয়)। Payment-এর আগে approved run reopen করা যায়, পরে নয়।
36. কর্মীর নথির retention/expiry সতর্কতা বা version history নেই—upload/download/delete ও audit আছে, কিন্তু policy-driven archival নেই।
37. লাইব্রেরি রিজার্ভেশন শুধু তখনই করা যায় যখন বইয়ের অন্তত একটি কপি available; ইস্যুতে প্রথম pending রিজার্ভেশন fulfilled হয়। Waiting queue/priority বা ভবিষ্যৎ কপির জন্য অপেক্ষমাণ তালিকা নেই, এবং রিজার্ভেশন মেয়াদোত্তীর্ণ হয় শুধু পরবর্তী ইস্যু/রেন্ডারের মতো lazy যাচাইয়ে — scheduled expiry job নেই।
38. লাইব্রেরির জরিমানা ও নতুন বই অধিগ্রহণ/ক্রয় এই মডিউল থেকে লেজারে পোস্ট হয় না; জরিমানা settle শুধু লোন রেকর্ড ও অডিটে থাকে। প্রাপ্ত জরিমানা বা বই ক্রয়ের হিসাব Accounts মডিউলে আলাদা voucher দিয়ে আনতে হবে।
39. সম্পদের রক্ষণাবেক্ষণ খরচ শুধু সম্পদ-রেকর্ডে roll-up হয়, লেজারে ব্যয় হিসেবে পোস্ট হয় না; সম্পদের অবচয় (depreciation), disposal gain/loss বা পুনর্মূল্যায়ন posting নেই।
40. সাপ্লায়ার-রিটার্ন/debit note, stock write-off বা purchase invoice 3-way matching নেই; ভুল stock সমন্বয় (adjustment) দিয়ে সংশোধন করতে হয়। GRN-এ supplier invoice no রাখা হয়, কিন্তু invoice total/price স্বয়ংক্রিয়ভাবে মিলিয়ে দেখা হয় না।
41. Transport ও hostel মডিউল এই ফেজে বাদ দেওয়া হয়েছে — scope অনুযায়ী অনুমোদিত/কনফিগারড না হলে যোগ করা যাবে না; ভবিষ্যতে আলাদা অনুমোদন সাপেক্ষে যোগ করতে হবে।
42. বই/সামগ্রীর barcode/QR স্ক্যানিং, কপি-লেবেল প্রিন্ট বা দ্রুত inventory scanning নেই; accession no, SKU ও tag ম্যানুয়ালি এন্ট্রি করতে হয়।
43. নেগেটিভ স্টক নীতি প্রতিষ্ঠানব্যাপী একক Boolean setting; আলাদা role/warehouse/item-ভিত্তিক override বা সীমা নেই।
44. মজুদ মূল্যায়ন শুধু weighted-average পদ্ধতিতে; FIFO/LIFO বা specific-identification costing নেই, এবং valuation report-এ হিসাব-বছরের closing snapshot সংরক্ষণ হয় না।
45. লাইব্রেরি জরিমানা/লোন-নীতি member type (student/staff) ভিত্তিক; নির্দিষ্ট সদস্যের জন্য ব্যতিক্রম/waiver বা জরিমানা মওকুফের অনুমোদন workflow নেই।
46. নোটিফিকেশন সম্পূর্ণ synchronous — publish request-এর মধ্যেই প্রতিটি recipient ও channel-এ পাঠানো হয়; কোনো queue/worker, background job বা automatic backoff নেই। বড় audience-এ publication ধীর হতে পারে এবং gateway timeout (SMS ডিফল্ট ১০s) পর্যন্ত অপেক্ষা করতে পারে; failed delivery শুধু manual retry করা যায়।
47. SMS integration-ready কিন্তু ডিফল্টে disabled; প্রকৃত SMS provider/sandbox যাচাই করা হয়নি। Email app mailer-এ যায় (dev-এ log mailer)। Provider-side delivery receipt/bounce/webhook বা message ID সংরক্ষণ হয় না।
48. পোর্টাল অ্যাকাউন্ট শুধু admin-link করা বিদ্যমান active user-এর সঙ্গে যুক্ত করা যায়; self-registration, invite email/SMS, OTP verification বা first-login password change নেই। ভর্তি সম্পন্ন হলেই স্বয়ংক্রিয় portal account তৈরি হয় না।
49. পোর্টাল ফাইল private streamed download; extension allowlist ও 10 MB সীমা আছে, কিন্তু antivirus/CDR scan, inline preview, thumbnail বা signed temporary URL নেই। Notice/Homework-এ ফাইল থাকলে শুধু নাম দেখায়, size দেখায় না।
50. Student/Parent portal সম্পূর্ণ read-only — online fee payment নেই (Phase 06-এর gateway readiness-ই আছে), অভিভাবক–শিক্ষক messaging নেই, শিক্ষার্থী portal থেকে homework submit করতে পারে না।
51. Teacher portal-এ শুধু homework/material create-edit-publish ও attendance/results read-only; attendance marking, marks entry ও notice authoring admin মডিউলেই থাকে। শিক্ষক শুধু class-teacher/allocation scope দেখেন।
52. Delivery log-এ filter, summary ও failed retry আছে, কিন্তু retention/archival policy, CSV export, bulk resend, scheduled retry বা per-provider bounce handling নেই। Skipped delivery retry করা যায় না (শুধু failed)।
53. Notification topic ছয়টি fixed (notices, homework, materials, results, fees, attendance); topic subscription all-or-nothing, digest frequency/quiet hours বা সন্তান-ভিত্তিক আলাদা preference নেই। Publication-এর আগে করা topic পরিবর্তন পরবর্তী publication-এ কার্যকর হয়।
54. Report Center-এ ১৭টি নির্দিষ্ট রিপোর্ট আছে; custom query/report builder, saved filter preset, scheduled report বা ইমেইল/portal-এ report delivery নেই। ফিল্টার session-এর মধ্যে সংরক্ষিত হয় না, প্রতিটি পৃষ্ঠায় পুনরায় নির্বাচন করতে হয়।
55. CSV/XLSX সব মিলে যাওয়া সারি stream করে, কিন্তু print/PDF সর্বোচ্চ ২,০০০ সারি (config); এর বেশি হলে validation error। বড় export কোনো queue/background job-এ চলে না এবং XLSX ফাইল `storage/app/exports`-এ সাময়িক তৈরি হয়।
56. Report access শুধু permission-ভিত্তিক (`reports.*` + domain permission); teacher/allocation-ভিত্তিক row-level scope রিপোর্টে প্রয়োগ করা হয় না — `reports.view` + `attendance.view` থাকা শিক্ষক যেকোনো শ্রেণির attendance রিপোর্ট দেখতে পারেন। Row-level report scope ভবিষ্যতের কাজ।
57. সার্টিফিকেট PDF-এ ডিজিটাল স্বাক্ষর, QR/verification code, verify endpoint, watermark বা প্রতিষ্ঠানের লোগো নেই; শুধু প্রতিষ্ঠানের নাম/স্লোগান ও certificate number ব্যবহৃত হয়।
58. Certificate template-এ fixed ১০টি placeholder ও সাধারণ body; rich-text/WYSIWYG, conditional block, টেবিল, ছবি/লোগো আপলোড বা তৃতীয় ভাষা নেই। Template সম্পাদনা পুরোনো template overwrite করে (version history নেই), তবে issued certificate-এর snapshot অপরিবর্তিত থাকে।
59. Issued certificate delete করলে রেকর্ড ও PDF ফাইল দুই-ই মুছে যায়; revoke/void status, reissue chain বা "duplicate copy" আলাদা workflow নেই। Template delete শুধু issued certificate না থাকলে হয়; retire করতে deactivate করতে হয়।
60. Document Center-এ bulk issue, batch PDF/ZIP, CSV export বা portal-এ published certificate access নেই; সার্টিফিকেট শুধু staff ডাউনলোড করতে পারেন।
61. PDF-এ Hind Siliguri font bundle করা, তবে mPDF output tagged/accessible PDF বা PDF/A archival format নয় এবং report PDF-এ প্রতিষ্ঠানের লোগো/custom letterhead ব্যবহার হয় না।
62. সেটিংস ক্যাশ per-key `rememberForever`; একাধিক অ্যাপ সার্ভারে shared cache (database/redis) ব্যবহার না করলে সার্ভারগুলো ভিন্ন মান দেখতে পারে — ডিপ্লয়মেন্টে shared cache আবশ্যক।
63. `activity_log`-এ retention/archival policy, purge command বা legal hold নেই; অডিট লগ ক্রমাগত বাড়তে থাকে। Log viewer read-only, কোনো entry delete/edit করার UI ইচ্ছাকৃতভাবে নেই।
64. ডেটা ট্রান্সফার শুধু শিক্ষার্থী CSV (২১ কলাম); ফি, হিসাব, লাইব্রেরি বা অন্যান্য মডিউলের import/export নেই, XLSX import নেই, এবং preview ফাইল সার্ভারে সংরক্ষিত হয় না (আবার আপলোড করতে হয়)।
65. ব্যাকআপ এনক্রিপ্টেড নয় এবং off-site/secondary copy বা incremental/point-in-time backup নেই; encrypted disk ও ACL-ই সুরক্ষা। বড় ডাটাবেসে browser-এর Create backup request ধরে রাখে — cron/CLI ব্যবহার করতে হবে।
66. `backup:test-restore`-এর জন্য CREATE/DROP privilege-যুক্ত আলাদা MySQL account দরকার; ডিফল্ট least-privilege অ্যাপ account দিয়ে restore drill চলবে না। Restore verification scheduled job নয় — চালাতে হয় ম্যানুয়ালি/cron-এ।
67. সিকিউরিটিতে 2FA, password history/reuse prevention, বাধ্যতামূলক password rotation, per-user lockout, IP allowlist বা SSO নেই; idle timeout ডিফল্টে বন্ধ। `security:check`-এর dependency audit ইন্টারনেট/Composer Packagist access-এর উপর নির্ভরশীল।
68. স্ট্যাটিক অ্যানালাইসিসের গেট **PHPStan level 1**; এই মেশিনে ডিফল্ট ১২৮ MB `memory_limit`-এ parallel worker OOM হয়, তাই `--memory-limit=1G` দিয়ে চালাতে হয়। level 5-এ ৬১৪টি finding (মূলত Livewire/Blade-এর dynamic property ও array shape) — এগুলো runtime ত্রুটি নয়, তবে level বাড়াতে typed DTO/array-shape annotation-এর আলাদা কাজ দরকার।
69. অডিট লগের টেক্সট সার্চ `LIKE '%term%'`-ভিত্তিক (description/properties/causer join); full-text index নেই, তাই `activity_log` অনেক বড় হলে সার্চ ধীর হবে — retention/purge-ও নেই (দেখুন ৬৩)।
70. **CSP header এখনো নেই**; `X-Content-Type-Options`, `X-Frame-Options`, referrer ও permissions policy আছে। CDN/inline-script নীতি নির্ধারণ করে আলাদা ফেজে CSP যোগ করতে হবে।
71. একক-প্রসেসে সম্পূর্ণ টেস্ট স্যুট চালালে mPDF ফন্ট লেখার সময় ১২৮ MB `memory_limit`-এ OOM হতে পারে; `phpunit.xml`-এ টেস্ট-only ৫১২ MB এবং চূড়ান্ত রান `--parallel --processes=4`-এ নেওয়া হয় (উৎপাদন কনফিগ অপরিবর্তিত)।
72. ডেভ হোস্টে মাঝে মাঝে অপ্রত্যাশিত Windows reboot হয়েছে (Phase 12/13-এ কয়েকবার); প্রতিবার MySQL ও dev server ম্যানুয়ালি পুনরায় চালু করতে হয়েছে। এটি অ্যাপের limitation নয়, তবে দীর্ঘ টেস্ট রানের জন্য স্থিতিশীল হোস্ট বা CI runner সুপারিশ করা হয়।
73. নতুন ব্যাকআপের disposable restore drill এই মুহূর্তে চালানো যায়নি (dedicated `BACKUP_RESTORE_*` অ্যাকাউন্ট নেই); Phase 12-এর সফল ড্রিল (১১৪ টেবিল) একমাত্র restore-verified প্রমাণ। উৎপাদনে যাওয়ার আগে অবশ্যই নতুন ডাম্পে ড্রিল চালিয়ে `backup:test-restore` সবুজ করতে হবে।

## 18. Roadmap status

| Phase | Scope | Status |
|---|---|---|
| P01 | Foundation: env, scaffold, brand, layout, locale, settings | ✅ done, verified |
| P02 | Auth + RBAC + user management + role matrix + audit wiring | ✅ done, verified |
| P03 | Academic structure, admissions, student lifecycle, admission reports | ✅ done, verified |
| P04 | Academic management: terms, subjects, curriculum, assignments, timetable, calendar, promotions, progress | ✅ done, verified |
| P05 | Attendance (student + staff), examinations, grading, marks, results/publication, Hifz/Nazara progress | ✅ done, verified |
| P06 | Fees & student collections: fee heads/structures, invoices, partial payments, refunds, waivers, arrears, statements, double-entry ledger reconciliation, gateway readiness | ✅ done, verified |
| P07 | Accounts & financial ledger: financial years, chart of accounts, vouchers (receipt/payment/contra/transfer/journal), opening balances, double-entry ledger, payroll payments, fundraising, bank reconciliation, trial balance, ledger, income & expenditure, prints/exports, real dashboard figures | ✅ done, verified |
| P08 | HR & payroll: staff records, designations, employment documents, salary components/structures, payroll runs, leave types/applications, staff advances, approval workflows, payslips, payroll summaries, salary payment vouchers, prints/exports, ledger integration | ✅ done, verified |
| P09 | Library, inventory & operations: cataloguing, copies, issue/return/renewals/reservations/fines, suppliers, purchase requests and approvals, stock receipts/issues/adjustments, asset register and maintenance, stock history, prints/exports, ledger integration | ✅ done, verified |
| P10 | Portals & communication: student/parent/teacher portals, record-level authorization, notices/homework/learning materials, published results/fees/documents, notification preferences, email/SMS delivery log, admin communication screens | ✅ done, verified |
| P11 | Reports & Document Center: 17 permission-controlled reports, relevant filters, totals, pagination, print, PDF, CSV/XLSX exports, bilingual certificate templates, issuance, secure downloads, audit | ✅ done, verified |
| P12 | Settings, security & backup: institution/branding/contact/academic-year/fee/sequences/locale/notification settings, configurable security policies, audit-log search & redacted CSV export, validated atomic student CSV import/export, private checksummed database backups with retention and CLI-only restore plus disposable-database restore drill, security headers/audit/`security:check` | ✅ done, verified |
| P13 | Integration, QA & deployment readiness: full route/permission/migration/config audit, 17 quality tests, accounting reconciliation, localization parity, responsive browser QA, static analysis, security & backup verification, Windows/cPanel/VPS deployment guides, feature checklist, go-live checklist | ✅ done, verified — production deployment pending approval |
| P14 | Actual production deployment & UAT (Windows/XAMPP or cPanel/VPS), SMTP/SMS credentials, restore drill with dedicated account, sign-off | pending approval |
