# Requirements Traceability

Requirement-to-implementation comparison against the 55 sections of the *Master Development Prompt*
(see §55 of the prompt). Each requirement is classified as:

| Mark | Meaning |
|---|---|
| ✅ | **Implemented** (and covered by automated tests where testable) |
| ➖ | **Not applicable** (with the reason) |
| ⏳ | **Deferred / future scope** (with the reason) |
| ❌ | **Missing** — *none* |

**Summary:** every MVP requirement is implemented. There are two documented deviations: Laravel 12 instead of 11
(security) and MariaDB 10.4 as the local database (production target MySQL 8). Deferred items are either
future scope by the prompt's own definition (§45) or **real-browser QA**, which could not run in the build
environment and is handed over as a checklist (TESTING.md §4). There is no unexplained missing item.

---

## §1 Project purpose · §44 Definition of MVP · §53 Principles

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Vendor → Main Reseller → Sub-Reseller → Seller → Customer → Package → Work Order → Deadline → Status | ✅ | Data model in [DATABASE.md](DATABASE.md); flow in [WORK_ORDER_FLOW.md](WORK_ORDER_FLOW.md) |
| Customers, service addresses, vendors, sellers, services, packages, prices, Single/Double/Triple Play, work orders, dates, deadlines, status, reports | ✅ | Modules listed in [README.md](README.md) |
| MVP list (auth, RBAC, isolation, dashboard … tests) | ✅ | All present; see the sections below |
| NO payment gateway, wallet, bill collection, RADIUS, OLT, MikroTik, usage accounting, suspension, reseller financial accounting | ✅ | None exist. Prices are informational snapshots only. |
| Operationally simple, not an ISP billing platform | ✅ | No billing tables or flows |

## §2 Technology stack

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| PHP 8.2 | ✅ | `composer.json` `php: ^8.2`; runs on XAMPP PHP 8.2 |
| **Laravel 11 preferred** (10 only if required) | ✅ deviation | **Laravel 12.69.3**. Every Laravel 11 release is blocked by Composer because of unpatched advisories (incl. high-severity CVE-2026-48019). The 12.x structure is the same. Rationale in [CHANGELOG.md](CHANGELOG.md). |
| MySQL 8.x | ✅ | Schema targets MySQL 8.0.16+. Developed and tested locally on **MariaDB 10.4** (XAMPP) with the same `mysql` driver. Only features shared by both are used (CHECK, triggers, JSON). Not yet run on a MySQL 8 server here; run the suite there once before go-live. |
| Blade, Livewire 3, Alpine, Tailwind, Vite | ✅ | Livewire 3.8.10, Tailwind 4.3, Vite 7.3 |
| Chart.js only where valuable | ✅ | 4 dashboard charts, imported lazily (`resources/js/charts.js`) |
| SPA-like navigation, no full refresh, `wire:navigate`, Livewire forms/filters/pagination, modals, loading indicators, skeletons, toasts | ✅ | See §27 |
| No React/Vue/Angular/Next, Redis, Docker, K8s, microservices, payment, RADIUS, MikroTik, OLT, usage accounting, suspension, wallet | ✅ | `package.json` and `composer.json` contain none of them |
| Runs locally on Windows without Docker | ✅ | [INSTALLATION.md](INSTALLATION.md); developed on Windows 11 + XAMPP |

## §3 Architectural principle

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Modular monolith: Controller/Livewire → validation → service → query layer → Eloquent → MySQL | ✅ | [ARCHITECTURE.md](ARCHITECTURE.md); `app/Services`, `app/Queries`, `app/Reports` |
| No complex business logic in controllers or components | ✅ | Components validate input and delegate to services (e.g. `WorkOrderService`, `WorkOrderStatusService`) |
| Policies, Gates, middleware, permission checks | ✅ | 11 policies, 42 permission gates, `can:` on every route, custom middleware |
| Form Request validation | ✅ | `LoginRequest` for authentication; Livewire validation (permitted by §24) for component forms; `$request->validate()` in small auth controllers |
| DTO/value objects where beneficial | ✅ | `CustomerFormData` (Livewire Form), `ReportFilters`, enums as value objects |
| Transactions for multi-step processes | ✅ | Create/update/transition/attachment flows use `DB::transaction` with row locks |
| Events/listeners for side effects | ➖ | Audit and notification side effects are called explicitly inside the service transaction, so they are atomic, visible and testable. No event bus was needed. |
| Queues only when necessary; database queue, sessions and cache | ✅ | `QUEUE_CONNECTION=database`, `SESSION_DRIVER=database`, `CACHE_STORE=database`. Notifications run synchronously, so no worker is required. |
| No hidden Redis dependency | ✅ | No Redis config is used anywhere |

## §4 Tenancy / data isolation · §30 rule 15

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Main Reseller → Sub-Resellers → own Sellers, Customers, Work Orders | ✅ | `organizations.parent_id`; `organization_id` on every tenant-owned record |
| A sub-reseller never accesses another's customers, sellers, work orders, reports, attachments, notes or internal info | ✅ | Global `OrganizationScope` + `visibleTo()` + policies. Tests: `WorkOrderAccessTest`, `CustomerManagementTest`, `SellerManagementTest`, `ReportsTest`, `DashboardTest`, `AttackSurfaceTest::audit_log_viewer_is_scoped_to_the_tenant` |
| Never trust IDs from the browser; tenant context from the authenticated user | ✅ | Organization derived from customer or user; IDs are ULID lookups inside the user's scope. Test: `client_supplied_prices_services_and_organization_are_ignored` |
| Prevent IDOR/BOLA; backend authorization even when buttons are hidden | ✅ | Route binding through the scope (404) + policies (403). Tests: `guessing_numeric_ids_or_business_numbers_never_resolves`, `unauthorized_livewire_status_request_is_rejected` |

## §5 Main modules / navigation

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Sidebar: Dashboard; Customers (All, Add); Vendors (All, Add); Services & Packages (Service Master, Packages, Add Package); Sellers (All, Add); Work Orders (All, Add, Upcoming Deadlines, Overdue, Completed); Reports; Administration (Resellers/Sub-Resellers, Users, Roles & Permissions, Audit Logs, Settings) | ✅ | `resources/views/components/layouts/partials/nav-items.blade.php`. Exactly these entries, each shown only with the matching permission. |

## §6 Dashboard

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Cards: customers, vendors, sellers, packages, active, pending, scheduled, completed, due soon, overdue work orders | ✅ | 10 KPI cards (`livewire/dashboard.blade.php`) |
| Charts: by status, monthly, customers by vendor, customers by service type | ✅ | `DashboardCharts` (lazy) |
| Recent work orders, upcoming deadlines, recently added customers, work orders requiring attention | ✅ | Dashboard sections |
| Figures respect permissions and tenant boundary | ✅ | `DashboardService` uses `WorkOrderQuery`/`CustomerQuery`. Tests: `counts_are_tenant_scoped_and_consistent`, `seller_dashboard_only_counts_own_records` |
| Cards clickable to the filtered list | ✅ | Test: `card_totals_match_the_lists_they_link_to` |

## §7 Customer management

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| List: ID, name, company, phone, e-mail, service address, city, state, ZIP, seller, vendor, package, service type, current order status, created, status, actions | ✅ | `customer-index` columns: Customer (number, name, company), Contact, Service address, Seller, Latest service (vendor, package, play type), Order status, Created, Status, Actions |
| Search, filter, sort, pagination, view, edit, activate/deactivate, create work order, export where authorized | ✅ | `CustomerIndex` + `CustomerExportController` (`customer.export`) |
| Search by ID, name, phone, e-mail, address, ZIP, seller, vendor | ✅ | `CustomerQuery` (phone digits normalized, ZIP prefix, LIKE escaped). Test: `search_finds_customers_by_phone_digits_and_zip` |
| Capture type, first/middle/last name, company (business), phone, alternate phone, e-mail, service address (lines 1–2, unit, city, state, ZIP, county), optional mailing address, seller, preferred contact, notes, status | ✅ | `CustomerFormData`. Status: new customers start Active and are switched with Activate/Deactivate (audited). |
| Realistic US ZIP and contact validation | ✅ | ZIP / ZIP+4, USPS states, 10-digit NANP phones, e-mail required if preferred. Tests: `validation_rules_for_us_contact_and_address_fields`, `email_is_required_when_preferred_contact_is_email` |
| Unique customer number `CUS-000001`, not the auto-increment ID | ✅ | `NumberGenerator::customerNumber()` (atomic sequence). Test: `customer_numbers_are_sequential_and_unique` |

## §8 Vendor management

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Fields: code, name, legal name, contact person, phone, e-mail, website, address 1/2, city, state, ZIP, account/reference number, notes, active/inactive | ✅ | `VendorForm`; account reference encrypted |
| Profile: information, packages, supported services, customer count, work orders, status | ✅ | `vendor-show` |
| Confidential data hidden from unauthorized users | ✅ | `VendorPolicy::viewConfidential`. Test: `confidential_vendor_data_is_hidden_from_sub_resellers` |

## §9 Service Master · §10 Play / bundle logic

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Internet, TV / Streaming, Home Phone / Voice, Mobile; fields code, name, description, icon, status, display order | ✅ | `ServiceSeeder`, `ServiceIndex` (modal form) |
| Mobile configurable for the future | ✅ | Seeded **inactive**; enabling it makes Quad Play available |
| No bundle types hard-coded in package logic | ✅ | Play type derived from the service count (`PlayType::fromServiceCount`) |
| 1/2/3/4 services → Single/Double/Triple/Quad, calculated by the system | ✅ | `PackageService`, `WorkOrderService`. Tests: `three_services_are_automatically_triple_play`, `single_and_double_play_are_calculated` |
| Prominent Single/Double/Triple; Quad supported but not prominent | ✅ | `PlayType::filterOptions()`: list, report and package filters offer Single/Double/Triple; Quad appears once Quad packages or orders exist, and the dashboard mix works the same way. Test: `filters_offer_single_double_triple_and_only_other_play_types_in_use` |
| "DOUBLE PLAY", never "Secondary Play" | ✅ | Labels in `PlayType` |
| Backend guarantees consistency (no 3 services = Single) | ✅ | Client value ignored + DB CHECK. Tests: `client_supplied_play_type_is_ignored`, `database_rejects_inconsistent_play_type`, `triple_play_never_matches_a_single_service` |

## §11 Package management · §12 Service-specific information

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Vendor has many packages; package has 1..n services | ✅ | `packages.vendor_id`, `package_services` |
| Fields: code, name, vendor, description, price, frequency (monthly / one-time / promotional), standard monthly price, promotional price + start/end, installation, activation, services, auto play type, effective from/to, status | ✅ | `PackageForm`; CHECK constraints on prices and dates |
| Pricing history | ✅ | `package_price_histories` with reason and author. Test: `price_change_creates_history_and_is_audited` |
| Work orders store a price snapshot; later changes never alter them | ✅ | Test: `package_price_change_never_alters_existing_work_orders` |
| Internet: technology, down/up speed, unit, data allowance, unlimited, equipment, router, notes | ✅ | `ServiceDetailSchema` (internet) |
| TV: tier, delivery type, channels, premium, set-top box, DVR, notes | ✅ | `ServiceDetailSchema` (tv) |
| Voice: plan, lines, local, long distance, international, porting, notes | ✅ | `ServiceDetailSchema` (voice) |
| Mobile: plan, lines, data, unlimited talk/text, 5G, notes | ✅ | `ServiceDetailSchema` (mobile) |
| Conditional UI, no irrelevant fields | ✅ | Package form shows a details panel only for each selected service. Unknown keys are dropped. Test: `service_details_are_validated_and_sanitized` |

## §13 Seller management

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Fields: code, first/last name, company, e-mail, phone, alternate phone, address, city, state, ZIP, organization, start date, notes, status | ✅ | `SellerForm`; generated `SLR-000001` |
| Profile: information, customers, work orders, vendor distribution, package distribution | ✅ | `seller-show` |
| No commissions | ✅ | Not implemented, by scope |
| Seller login optional; seller record ≠ user login | ✅ | Separate `sellers` and `users`; optional link (`sellers.user_id`) through a row-restricted role |

## §14 Work orders · §15 Number · §23 Snapshots

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Links organization → seller → customer → service address → vendor → package → services → price snapshot → dates → status | ✅ | `work_orders` + `work_order_services` |
| Number, external vendor order number | ✅ | `work_order_number`, `external_order_number` |
| Snapshots: service address, vendor name, package name/description, services, play type, monthly/promotional/installation/activation prices; seller reference | ✅ | `WorkOrderService::packageSnapshot/addressSnapshot/snapshotServices`; `seller_id` uses RESTRICT, so it is never lost. Tests: `work_order_captures_services_play_type_price_and_address_snapshot`, `customer_address_change_does_not_rewrite_history` |
| No payment collection | ✅ | — |
| Dates: created, work order, requested installation, scheduled installation, deadline, completion, cancellation | ✅ | `created_at`, `work_order_date`, `requested_installation_date`, `scheduled_installation_date` (+ window), `deadline_date`, `completion_date`, `cancelled_at` |
| Statuses Draft … Cancelled + optional Failed | ✅ | `WorkOrderStatus` (8 statuses) |
| Priority, assigned operations user, vendor reference, customer notes, internal notes, cancellation reason, completion notes, attachments | ✅ | All present |
| `WO-2026-000001` business number; internal IDs not exposed; ULIDs | ✅ | `NumberGenerator::workOrderNumber()`; `HasPublicUlid`. Test: `numbers_are_unique_and_sequential` |

## §16 Status workflow · §30 rules 9, 10, 14

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Logical transitions (Draft → Submitted → In Progress → Scheduled → Completed; Submitted → On Hold → In Progress; cancellation from Draft/Submitted/In Progress/Scheduled) | ✅ | `WorkOrderStatus::transitions()`. Test: `documented_happy_path_is_allowed` |
| Invalid transitions rejected (Completed → Draft) | ✅ | Tests: `completed_work_orders_are_final`, `completed_work_order_cannot_return_to_draft_or_any_status` |
| Cancelled → Completed only through an authorized recovery workflow | ✅ | Reopen to Submitted with `work-order.reopen` and a reason. Test: `cancelled_cannot_complete_and_reopen_needs_recovery_permission_and_reason` |
| History: previous/new status, by, at, remarks, IP/user agent | ✅ | `work_order_status_histories`. Test: `full_lifecycle_records_every_transition` |
| Completion requires a date; cancellation requires a reason | ✅ | Service + DB CHECK. Tests: `cancellation_requires_a_reason`, `completion_requires_a_valid_completion_date`, `database_rejects_cancelled_without_reason_and_completed_without_date` |
| Unauthorized users cannot change status | ✅ | Per-target permissions. Tests: `viewer_cannot_change_status`, `unauthorized_livewire_status_request_is_rejected` |

## §17 Deadline management

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Upcoming, Due Today, Overdue, Completed On Time, Completed Late; Normal / Due Soon indicators | ✅ | `DeadlineCalculator`, `DeadlineState`. Test: `deadline_states`, `completed_on_time_and_late` |
| Text, not only color | ✅ | Badges always include a label and icon |
| UTC storage; America/New_York display (configurable); DST | ✅ | DB session `+00:00`; `BusinessClock`. Tests: `today_is_evaluated_in_the_business_timezone_not_utc`, `daylight_saving_change_does_not_shift_deadlines` |

## §18 Work order details page

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Header: number, status, customer, vendor, seller, play type, deadline | ✅ | `work-order-show` header |
| Sections: customer, service address, vendor, seller, package, services, pricing, dates, status timeline, internal notes, attachments, activity history | ✅ | Cards: Customer & service address, Package & services, Pricing (snapshot), Dates, Handling, Status timeline, Internal notes, Attachments, Activity history (audit entries) |
| Actions by status and authorization: edit, submit, schedule, hold, complete, cancel, note, upload, print | ✅ | `WorkOrderShow::availableTransitions()` + policies; print route |

## §19 Reporting

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| 11 reports: customer, vendor, seller, package, service type, play type, work order, deadline, status, completed, overdue | ✅ | `app/Reports/*`, `ReportRegistry` |
| Filters: dates, customer, vendor, seller, package, service, play type, status, state, ZIP, reseller/sub-reseller | ✅ | `ReportFilters::KEYS` (also customer status and deadline state) |
| View, print, CSV/Excel; PDF if needed | ✅ | Screen, print view (browser *Save as PDF*), CSV (UTF-8 BOM, Excel-friendly) |
| Respect tenant boundaries and permissions | ✅ | Tests: `reports_respect_tenant_boundaries`, `export_and_print_require_export_permission`, `seller_logins_cannot_open_reports` |

## §20 User management · §21 RBAC

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Users separate from business records | ✅ | `users` vs `sellers`/`customers` |
| Fields: name, e-mail, phone, password, organization, role, status, last login, created | ✅ | `UserForm`, `user-index` |
| Username if required | ➖ | Not required: sign-in uses the unique e-mail address |
| List, add, edit, activate, deactivate, reset password, force reset, role assignment | ✅ | `UserIndex`/`UserForm`: reset link or temporary password with forced change |
| No modification of higher-privilege users | ✅ | Level checks. Tests: `admin_cannot_modify_equal_or_higher_privileged_users`, `sub_reseller_admin_cannot_escalate_by_assigning_a_higher_role`, `delegated_role_managers_cannot_grant_or_revoke_permissions_they_do_not_hold` |
| Roles: Super Admin, Main Reseller Admin, Sub-Reseller Admin, Operations, Seller, Viewer/Auditor | ✅ | `DefaultRoles`; matrix in [ROLES_AND_PERMISSIONS.md](ROLES_AND_PERMISSIONS.md) |
| Granular permissions (customer.*, vendor.*, package.*, seller.*, work-order.*, report.view, user.manage, role.manage, audit.view) | ✅ | 42 permissions in `App\Enums\Permission` (all examples included) |
| Do not rely on role names | ✅ | Code checks permissions only |

## §22 Database design · §39 Migration rules

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Normalized tables (users, organizations, roles, permissions, vendors, customers, customer_addresses, sellers, services, packages, package_services, price history, work_orders, work_order_services, histories, notes, attachments, audit_logs, settings, notifications) | ✅ | [DATABASE.md](DATABASE.md) |
| `resellers` table | ➖ | Folded into `organizations` (type main/sub) to avoid two parallel hierarchies |
| `role_user` / `model_has_roles` | ➖ | One role per user (`users.role_id`); `permission_role` pivot |
| `package_service_details` | ✅ | `package_services.details` JSON, validated per category |
| `login_histories` / `security_events` | ✅ | `audit_logs` (auth/security categories), `users.last_login_at/ip`, `security` log channel |
| PKs, FKs, unique and composite indexes, CHECK constraints, timestamps | ✅ | 8 migration files (`database/migrations`) |
| Soft deletes only where appropriate; never on audit history | ✅ | Customers and attachments only; audit log has none |
| No accidental cascade of business history; block deletes of referenced records; deactivate masters | ✅ | RESTRICT FKs. Tests: `customer_with_work_orders_cannot_be_deleted`, `vendor_with_packages_cannot_be_deleted_only_deactivated`, `seller_with_customers_cannot_be_deleted` |
| utf8mb4, DECIMAL money, no FLOAT | ✅ | `DECIMAL(10,2)` everywhere |
| Migrations for every change, reversible `down()`, FKs, indexes, correct nullability, seeders, factories | ✅ | `database/migrations`, `database/seeders`, `database/factories` |
| Destructive production migrations strategy | ➖ | None exist in v1.0 |

## §24 Security (ASVS L2) · §37 Security testing

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Argon2id, no plaintext, secure expiring reset, no enumeration, login + account throttling, session regeneration, logout invalidation | ✅ | [SECURITY.md](SECURITY.md) §2–3. Tests in `AuthenticationTest`, `PasswordResetTest` |
| 2FA for administrators (optional) | ✅ | TOTP with recovery codes, enforceable. Tests: `TwoFactorTest` |
| Server-side authorization (policies, gates, middleware, tenant scoping) against IDOR, BOLA, horizontal and vertical escalation | ✅ | SECURITY.md §4; access tests |
| CSRF on all state-changing requests | ✅ | Test: `csrf_protection_is_enabled_for_all_web_routes_without_exceptions` |
| SQL injection: Eloquent, bindings, no concatenated raw SQL | ✅ | Code review: raw fragments static or bound; whitelisted sort. Test: `unknown_reports_and_malicious_filters_are_rejected` |
| XSS: escaped output, no unescaped user content | ✅ | Test: `stored_script_in_notes_is_rendered_escaped`; `html_is_rejected_in_names_and_addresses` |
| Mass assignment: `$fillable`, validated data only | ✅ | Test: `tenant_and_system_fields_are_not_mass_assignable`, `locked_livewire_state_cannot_be_tampered_with` |
| Validation of required values, lengths, formats, enums, references, ownership, dates, prices, files, transitions | ✅ | Services + components |
| Secure upload (allowlist, size, server names, outside web root, block executables, ownership check, controlled download, path traversal, malware hook) | ✅ | `AttachmentService`; storage paths are generated, never user-supplied. Tests: `AttachmentSecurityTest` |
| Secure, HttpOnly, SameSite cookies; HTTPS; lifetime, idle timeout, invalidation; no session IDs in logs | ✅ | Tests: `SessionSecurityTest`, `session_cookie_is_http_only`, `role_change_signs_the_user_out_everywhere` |
| Rate limits: login, reset, sensitive actions, search, uploads | ✅ | SECURITY.md §9. Test: `livewire_requests_are_rate_limited`, `reset_requests_are_rate_limited` |
| Sensitive data: never logged; encryption for reversible secrets; `.env` not committed; `.env.example` | ✅ | Test: `audit_trail_never_stores_passwords_or_secrets`; `.gitignore` excludes `.env` |
| `APP_DEBUG=false`; no traces, SQL, secrets or paths; pages for 403, 404, 419, 422, 429, 500 | ✅ | `resources/views/errors` (+401, 503). Test: `errors_never_expose_debug_details` |
| CSP, nosniff, Referrer-Policy, Permissions-Policy, frame-ancestors, HSTS | ✅ | `SecurityHeaders`. Tests: `security_headers_are_present`, `hsts_is_sent_over_https_when_enabled` |
| Avoid unsafe-inline JavaScript | ✅ | Nonce-based scripts, no `'unsafe-inline'` for scripts. `'unsafe-eval'` is needed by Alpine (documented trade-off). |
| `composer audit`, `npm audit`, minimal dependencies | ✅ | Both clean at release. Runtime additions: `livewire/livewire`, `bacon/bacon-qr-code`, `chart.js`, Inter font. |
| §37: SQLi, XSS, CSRF, IDOR, access control, escalation, mass assignment, open redirect, uploads, path traversal, brute force, session fixation and invalidation, data leakage, debug exposure, direct download, duplicate submission, manipulated IDs and hidden fields, date and price tampering | ✅ | Automated tests listed in [TESTING.md](TESTING.md) (`AttackSurfaceTest`, `WorkOrderAccessTest`, `AttachmentSecurityTest`, `AuthenticationTest`, `stale_status_is_rejected_to_prevent_double_submission`, `client_supplied_prices_services_and_organization_are_ignored`). An external penetration test is recommended before go-live. |

## §25 Audit logging · §41 Logging

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Events: login success and failure, logout, customer create/update/status, vendor, seller, package create/update, price change, work order create/update/status/cancel/complete, user created, role and permission changes, settings, attachments | ✅ | Full event list in SECURITY.md §11 |
| Captures user, event, entity type and ID, timestamp, old and new values, IP, user agent, organization | ✅ | `audit_logs` columns |
| No passwords, secrets or session IDs; not editable | ✅ | Redaction; model guards + DB triggers. Test: `audit_logs_are_append_only` |
| Laravel logging for critical exceptions, failed jobs, security events; no unnecessary PII; business audit separate from technical logs | ✅ | Daily logs, `failed_jobs`, `security` channel (e-mails hashed) |

## §26 Front-end / UX · §27 SPA navigation · §28 Accessibility · §43 Error and empty states

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Modern, consistent enterprise UI (typography, spacing, radius, shadows, inputs, buttons, tables, badges, empty states, tooltips, icons) | ✅ | Design tokens and component classes in `resources/css/app.css`; `components/ui/*` |
| Collapsible sidebar, header, breadcrumbs, title, primary action; mobile drawer | ✅ | `layouts/app`, `partials/sidebar`, `ui/page-header` |
| Header: organization, user avatar, notifications, profile, logout | ✅ | `partials/header` |
| Consistent status colors, never color alone | ✅ | `StatusBadge`/`DeadlineBadge`: color + icon + text |
| Forms: grouped, required markers, inline errors, preserved values, double-submit protection, loading state, feedback, focus first invalid field, keyboard support | ✅ | `ui/field`, `ui/button` (`wire:loading.attr="disabled"`), toasts, `resources/js/forms.js` |
| Tables: search, filters, sort, server pagination, reset filters, empty state, row actions, responsive | ✅ | `WithTable` concern; `table-wrap` horizontal scroll |
| SPA: shell preserved, progress bar, no flicker, lazy components, forms, modals and filters without reload, back/forward, titles, server-side route protection | ✅ | `wire:navigate`, `@persist('sidebar')`, `#[Lazy]` charts, `#[Url]` filters, `->title()` |
| WCAG 2.2 AA: semantic HTML, labels, keyboard, focus, contrast, headings, ARIA, modal focus, accessible validation, icon labels | ✅ | Skip link, `aria-describedby` errors, `aria-live` toasts, focus-trapped modals, `sr-only` labels. Assistive-technology testing is still to be done (see §50). |
| Empty, no-results, no-permission, inactive related record, DB error, unexpected error, expired session, validation states; no raw exceptions | ✅ | `ui/empty-state` with filter-aware text; 403/404/419/500 pages; `errors.js` for Livewire failures; inactive badges on historical records |

## §29 Performance · §31 Search and filtering

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| No N+1 (eager loading), pagination, selected columns, optimized dashboard, lazy heavy components, minified Vite build, small JS, cached settings | ✅ | `Model::preventLazyLoading()` outside production; aggregate queries; Chart.js lazy; settings cached |
| Indexes on organization, numbers, FKs, status, dates, deadline, state, ZIP, created_at; composite where useful | ✅ | DATABASE.md §4 |
| Server-side, debounced search; filter persistence | ✅ | `wire:model.live.debounce`; URL-bound filters |
| Customer filters: status, seller, vendor, package, state, ZIP | ✅ | `CustomerIndex` (+ type, organization) |
| Work order filters: status, vendor, seller, customer, package, play type, date range, deadline range, overdue, organization | ✅ | `WorkOrderIndex` (+ service, priority, assignee, completion timeliness) |
| Reports use the same rules as lists | ✅ | Shared `WorkOrderQuery`/`CustomerQuery`. Test: `report_totals_match_transaction_data` |

## §30 Business rules

| # | Rule | Status | Evidence (tests) |
|---|---|:-:|---|
| 1 | Customer belongs to an authorized organization | ✅ | `customer_of_another_sub_reseller_cannot_be_used` |
| 2 | Seller belongs to the reseller | ✅ | `seller_must_belong_to_the_customers_organization` |
| 3 | Vendor available to the reseller | ✅ | `vendor_not_permitted_for_the_sub_reseller_is_rejected` |
| 4 | Package belongs to the vendor | ✅ | `package_must_belong_to_the_selected_vendor` |
| 5 | Services belong to the package | ✅ | Services are copied from the package on the server; client service lists are ignored (`client_supplied_prices_services_and_organization_are_ignored`) |
| 6 | Play type = service count | ✅ | §10 tests + DB CHECK |
| 7 | Price/service snapshot | ✅ | §14 tests |
| 8 | Valid deadline relative to the work order date | ✅ | `deadline_cannot_be_before_the_work_order_date` + DB CHECK |
| 9 | Completion requires a date | ✅ | §16 tests |
| 10 | Cancellation requires a reason | ✅ | §16 tests |
| 11 | Inactive package not selectable | ✅ | `inactive_package_and_vendor_cannot_be_ordered` |
| 12 | Historical orders with inactive packages stay viewable | ✅ | `history_stays_viewable_after_renaming_and_deactivating_master_records`: the order page, print view, list and customer page still show it with its original vendor and package names |
| 13 | Inactive vendors, sellers and customers don't disappear from history | ✅ | Same test (vendor, package, seller and customer deactivated); RESTRICT FKs prevent deletion |
| 14 | Unauthorized users can't change status | ✅ | §16 tests |
| 15 | No cross-organization access | ✅ | §4 tests |

## §32 Notifications · §33 Settings · §34 USA defaults · §35 Seeding

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| In-app: approaching deadline, overdue, assigned, completed, important status change | ✅ | `WorkOrderNotifier`, `work-orders:deadline-alerts`. Tests in `DeadlineTest` |
| E-mail notifications | ⏳ | Optional in the prompt ("can be added"). Notifications use Laravel's channel system, so adding `mail` is a small change. Password-reset e-mails already use Laravel Mail. |
| No SMS or payment integrations | ✅ | None |
| Settings: name, logo, timezone, currency, date format, warning days, WO and customer prefixes, pagination, session duration, password rules, login attempts; admins only; audited | ✅ | `SettingsRegistry` (+ seller prefix, default deadline days, 2FA enforcement). Test: `settings_are_validated_saved_and_audited` |
| USD, America/New_York, UTC storage, US address structure | ✅ | Defaults in `config/portal.php` and Settings |
| Seed: admin, main reseller, 2 sub-resellers, vendors, Internet/TV/Phone, Single/Double/Triple packages, sellers, customers, work orders in many statuses; no real PII; local-only credentials | ✅ | `DemoDataSeeder`, `DemoCatalogSeeder`, `DemoOperationsSeeder` (`example.com` addresses, 555-01xx numbers); refuses to run in production |

## §36 Testing requirements

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Authentication, authorization, isolation, customer/vendor/seller/package CRUD, service selection, play type, work order creation, transitions, deadlines, price snapshot, validation, unauthorized access, URL manipulation, IDOR, cross-reseller access, upload security, search, filters, reports, soft delete/inactivation | ✅ | 210 PHPUnit tests; mapping in [TESTING.md](TESTING.md) |
| Named cases: B's customer URL → 404; 3 services → Triple; price change keeps old price; Completed → Draft rejected; unauthorized status POST rejected; cancel without reason rejected | ✅ | `sub_reseller_cannot_access_another_sub_resellers_customer`, `three_services_are_automatically_triple_play`, `package_price_change_never_alters_existing_work_orders`, `completed_work_order_cannot_return_to_draft_or_any_status`, `unauthorized_livewire_status_request_is_rejected`, `cancellation_requires_a_reason` |

## §38 Code quality

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| PSR-12 / Laravel conventions, SOLID, DRY, KISS | ✅ | Laravel Pint (PSR-12-based preset), clean |
| No fat controllers or components, duplicate logic, magic strings, hard-coded permissions, reseller IDs or statuses | ✅ | Enums (`WorkOrderStatus`, `Permission`, `PlayType`, …), services, shared queries; no IDs hard-coded |
| Enums, services, policies, form requests, reusable components, config constants | ✅ | `app/Enums`, `app/Services`, `app/Policies`, `components/ui`, `config/portal.php` |

## §40 Local setup · §42 Backup · §46 Documentation

| Requirement | Status | Implementation / evidence |
|---|:-:|---|
| Standard setup steps; no Docker, Redis, VM, cloud or paid API | ✅ | [INSTALLATION.md](INSTALLATION.md) §2 |
| Backup of database, uploads and secrets documented; no "download backup" feature | ✅ | INSTALLATION.md §5, DATABASE.md §6 |
| README, INSTALLATION, DATABASE, ARCHITECTURE, SECURITY, ROLES_AND_PERMISSIONS, WORK_ORDER_FLOW, TESTING, CHANGELOG | ✅ | All present, plus this file |

## §45 Future-ready, not future-heavy

| Item | Status | Notes |
|---|:-:|---|
| Mobile / Quad Play | ⏳ | Supported by the model (enum, CHECK, service details); activate the Mobile service when needed |
| Vendor API integration, address availability lookup, electronic order submission | ⏳ | New services behind `WorkOrderService`; `external_order_number` already stored |
| E-mail/SMS notifications | ⏳ | Notification channels |
| Advanced analytics, commission, customer portal, e-signature | ⏳ | Out of scope by the prompt; separate modules |

## §47–49, §54 Process

| Requirement | Status | Notes |
|---|:-:|---|
| Phased execution (1 → 10), each phase verified before the next | ✅ | Built in the prescribed order; migrations and tests run after each phase |
| Per-module analysis (purpose, users, permissions, data, rules, UI, audit, tests) | ✅ | Reflected in ARCHITECTURE.md, ROLES_AND_PERMISSIONS.md, WORK_ORDER_FLOW.md |
| §49 Modifying an existing project | ➖ | New project (empty folder) |
| §54 Working rules (no CSRF or authorization bypass, no secrets in code, no unnecessary packages, no silent rule changes) | ✅ | Deviations (Laravel 12, MariaDB) documented, not silent |

## §50 QA / SQA review · §51 Release checklist

| Requirement | Status | Notes |
|---|:-:|---|
| Pages, CRUD, permissions, direct URLs, workflow, deadline/price/package logic, cross-tenant security, error pages | ✅ | Automated suite + HTTP smoke test of every page per demo role (200 / 403 / 404 as expected, CSP nonce on all scripts, zero server errors) |
| Visual checks in **Chrome, Firefox, Edge**; responsive widths; browser console; spelling and alignment by eye | ⏳ | No real browser was available in the build environment. Responsive layouts and SPA behavior were built and reviewed in code but **not observed in a browser**. Run the checklist in [TESTING.md](TESTING.md) §4 before go-live. |
| Release checklist: architecture, normalization, migrations, seeders, auth, authorization, isolation, modules, play logic, deadlines, reports, audit, CSRF, XSS, SQLi, IDOR, uploads, rate limits, headers, debug guidance, audits, tests, docs, no hard-coded credentials, no `.env`, no Redis/Docker/payment, no unnecessary features | ✅ | Verified as above. Demo passwords exist only in local/testing seeders and factories. The project folder is not yet a git repository; `.gitignore` excludes `.env`. |
| "Responsive UI verified", "SPA navigation verified" | ⏳ | Implemented; browser verification pending (see the row above) |

## §52 Final acceptance flow

| Step | Status | Evidence |
|---|:-:|---|
| Login → dashboard → seller → customer → service address → vendor → package → services loaded → play type → price → work order → number → date → deadline → submit → track → schedule → complete → history → report | ✅ | Seeded as **WO-2026-000001** (John Smith, Michael Brown in Forest Hills, Queens; Example Telecom Provider → Home Connect Plus; Internet + TV + Home Phone = Triple Play; $129.99/month; 10/15/2026 → 10/20/2026). Replayed by `test_specification_acceptance_flow`. The prompt's sample number `WO-2026-000125` is illustrative; numbers come from the live sequence. |

## §55 Final self-review

This document is the requirement-to-implementation comparison. The final multi-perspective review (COO,
front-end, Laravel, database, security, SQA) is in the release summary delivered with v1.0.0 and summarised in
[CHANGELOG.md](CHANGELOG.md) (decisions, fixes found in review, known limitations).
