# Yemen Snack Food Laravel Migration Plan

The field-by-field model schema, relationships, and legacy-to-canonical rename reference are maintained in [MODEL_SCHEMA_PLAN.md](MODEL_SCHEMA_PLAN.md).

## 1. Goal and scope

Migrate the legacy pure-PHP Yemen Snack Food platform into this Laravel 13 / Filament 5 application while preserving the current client and driver behavior, improving data integrity, and providing a maintainable versioned API.

The target system has three surfaces:

1. A Filament administration panel for operations, catalogue, customers, providers, drivers, orders, payments, marketing, loyalty, configuration, and audit data.
2. A versioned customer API under `/api/v1`.
3. A versioned driver/provider API under `/api/v1/driver` and `/api/v1/provider`.

The legacy source is `/Users/mac/Downloads/yemensnackfood`. `/Users/mac/Downloads/ysf.sql` is schema-only. The populated snapshot `/Users/mac/Downloads/platformdgtl_yemensnackfood.sql` contains the same 75 `CREATE TABLE` declarations (including five compatibility views) and the current data used for planning. A new production export is still required for rehearsal and cutover because this snapshot will become stale.

## 2. Migration principles

- Keep legacy IDs in the first migration so references, uploaded files, and support records remain traceable.
- Use canonical Laravel table and column names in the new schema. Preserve legacy names only in import staging and explicit `legacy_id` / snapshot fields.
- Import into staging tables first, validate, then transform into the canonical schema in dependency order.
- Use InnoDB, UTF-8 (`utf8mb4`), foreign keys, unique constraints, decimal money fields, JSON columns, and Laravel timestamps.
- Convert Unix integer timestamps to nullable `datetime` values. Keep the raw legacy timestamp in staging, not in domain tables.
- Replace integer magic values with PHP enums. Confirm every legacy value against `$GLOBALS` configuration and API branches before fixing enum cases.
- Preserve order, coupon, currency, product, address, and payment snapshots as JSON because historical orders must not change when catalogue data changes.
- Do not expose Eloquent models directly from APIs. Use Form Requests, policies, API Resources, service/action classes, database transactions, and stable response envelopes.
- Treat orders, payments, wallets, loyalty points, and stock as transactional domains with idempotency and immutable ledger/audit records.
- Use the existing separate `Admin` guard for Filament. Customer, provider, and driver API authentication should use the `User` model plus account-type abilities/permissions.

## 2.1 Current populated-data profile

The September 2, 2026 snapshot is small enough for a single maintenance-window import, but importers should remain resumable and chunked. Important populated tables are:

| Domain | Current rows |
|---|---:|
| Countries / country languages / cities / zones | 239 / 984 / 4,095 / 160 |
| Admins / users / user devices / addresses | 5 / 11 / 16 / 10 |
| Sections / products / favourites | 8 / 40 / 12 |
| Orders / order items / deliveries / status events | 36 / 113 / 20 / 123 |
| Ratings / rating answers / questions | 8 / 24 / 3 |
| Payment methods / coupons / coupon redemptions | 27 / 6 / 7 |
| Wallet entries | 27 |
| Notifications / recipients / scheduled notifications | 76 / 22 / 10 |
| Admin logs | 299 |

The checked core references have no orphans: addresses-to-users, orders-to-users, items-to-orders/products, deliveries-to-orders/drivers, favourites-to-users/products, and coupon redemptions-to-coupons/orders all resolve. Foreign keys should still be added only after the complete import audit.

Observed values that must be mapped to named enums after confirming their meanings in legacy globals:

- Users: account types `1` (9 rows) and `2` (2 rows); all use legacy `active=0`; 10 of 11 are activated.
- Orders: statuses `1` (5), `5` (15), `6` (16); payment types `1` (28), `6` (8); payment statuses `1` (28), `3` (5), `4` (3); all 36 are multi-product orders.
- Deliveries: 11 accepted and 9 unaccepted; 20 total assignments.
- Products: all 40 are published and use legacy `active=0`; 38 are marked for home display; none are marked chosen under value `1`.
- Coupons: types `1`, `2`, and `3` are present; all six are published and use legacy `active=0`.
- Wallet: types `1` (19) and `2` (8); deserved statuses `1` (21) and `2` (6).
- All 10 scheduled notifications are pending `OrderDeliveryReview` notifications.

The following feature tables are empty in this snapshot and should not drive the first release: brands; product images/variants/related products; provider-product/provider-section mappings; loyalty definitions and ledgers; electronic-payment transaction tables; temporary checkout tables; payment-location pivots; contact messages; welcome sliders; diagnostics/search analytics. Generate these only when their feature is confirmed as required, while retaining their model/API plan below for completeness.

## 3. Decisions required before implementation

Resolve these in Phase 0; they materially affect schema and compatibility:

1. **API compatibility:** confirm whether existing mobile apps must continue sending `POST action=...` requests. Recommended: build REST endpoints as canonical APIs and temporarily add a legacy adapter that translates old actions into the same application services.
2. **Authentication:** choose Laravel Sanctum personal access tokens unless existing clients cannot change their token format. Social/Firebase login still needs provider token verification.
3. **User roles:** confirm the numeric meanings of `accounts_type` and whether one person can be both provider and driver. Recommended: one `users` table plus a backed `AccountType` enum initially; introduce role pivot tables only if multi-role accounts exist.
4. **Product ownership:** confirm whether products are global catalogue entries with provider-specific prices/availability (the schema suggests this) or provider-owned products.
5. **Variants:** `products_variants.type_variant_id` points to `products_variants_sections`; rename it to `variant_section_id` and confirm whether multiple variant selections can belong to one order item.
6. **Order status/payment status:** extract all numeric values and legal transitions from legacy globals and PHP branches before creating enums.
7. **Geography:** use `world_countrys`, `world_citys`, and `world_zones` as the canonical sources. The singular tables and governorate/district tables are SQL compatibility views, not separate models.
8. **Coupons:** `platform_cobons*` is unused legacy spelling while `platform_coupons*` is active. Import only `coupons*` unless a production export proves that old tables contain unique records.
9. **Content/configuration:** decide whether About is a singleton and whether configuration remains one row. Recommended: singleton Filament pages/resources with guarded keys.
10. **External services:** inventory live credentials and contracts for Firebase/FCM, OneSignal, WhatsApp, Floosak/other payment providers, Google/Apple/Facebook login, email, and Odoo sync.

## 4. Target domain model plan

### 4.1 Identity, access, and audit

| Model | Canonical table | Main relationships / behavior | Filament |
|---|---|---|---|
| `Admin` | `admins` | Authenticates with admin guard; uses Spatie Permission and Activitylog | Full CRUD restricted by Spatie permissions; password reset/change; never show password |
| `User` | `users` | Customer/provider/driver identity; has addresses, sessions/devices, orders, wallet entries, notifications, favourites, provider catalogue | Separate Customer, Provider, Driver resources using scoped queries; close/reactivate actions |
| `UserDevice` | `user_devices` | Renamed from `users_login_info`; belongs to user; stores FCM/OneSignal identifiers, token hash, last seen and location | Relation manager/read-only operational fields; revoke action |
| `UserEvent` | `user_events` | Diagnostic request/event data; sensitive JSON and network metadata | Read-only, tightly authorized, retention policy |
| `SystemError` | `system_errors` | Application/import errors | Read-only with resolution metadata; Laravel logging remains primary |

Notes:

- Keep the new existing `admins` schema and transform legacy admin usernames/emails/passwords. Map the legacy `super_admin` flag to a Spatie role rather than a model field. Legacy password hashes must be identified; force reset if they cannot be safely verified and rehashed.
- Replace page/department numeric permissions with policies and named permissions managed by `spatie/laravel-permission`. Do not create a custom AdminPermission model/table.
- Use `spatie/laravel-activitylog` instead of a custom AdminAuditLog model/table. Import resolvable legacy `platform_logs` records into the package activity table and expose them through a permission-protected read-only Filament viewer.
- Sensitive tokens must be encrypted at rest or hashed when only comparison is needed.

### 4.2 Geography, addresses, delivery, and currency

| Model | Canonical table | Main relationships / behavior | Filament |
|---|---|---|---|
| `Country` | `countries` | Has cities; belongs to optional currency; delivery settings | CRUD; searchable Arabic/English names; registration/address visibility toggles |
| `City` | `cities` | Belongs to country and optional currency; has zones | CRUD; country filter; driver availability and delivery settings |
| `Zone` | `zones` | Belongs to city and optional currency; location and delivery settings | CRUD; city filter; map coordinates |
| `CountryLanguage` | `country_languages` | Composite country/language data | Usually read-only relation manager; no standalone navigation |
| `AddressType` | `address_types` | Has addresses; bilingual content | CRUD with sort order and status |
| `Address` | `addresses` | Belongs to user, country, city, zone, type; coordinates and audit metadata | User relation manager plus global read/edit resource |
| `DeliveryTime` | `delivery_times` | Self-referencing parent/children; time window and type | CRUD/tree ordering; validate non-overlapping windows if required |
| `Currency` | `currencies` | Has exchange rates, countries/cities/zones and orders | CRUD; enforce one main currency |
| `CurrencyExchangeRate` | `currency_exchange_rates` | Belongs to currency; rate for date/time | CRUD/history; unique currency/date |

Schema decisions:

- Rename legacy `area_id` to `zone_id` and `address_type` to `address_type_id`.
- Use `decimal(10,7)` for latitude/longitude, never float. Use `decimal` for exchange rates and delivery fees.
- Store `is_active` with normal semantics (`true` means active); transform legacy `active=0` accordingly.
- Recreate legacy singular geography views only if old clients or reports query them directly.

### 4.3 Catalogue and provider availability

| Model | Canonical table | Main relationships / behavior | Filament |
|---|---|---|---|
| `Section` | `sections` | Self-referencing category tree; has products and providers | CRUD/tree; image/icon; home visibility; reorder |
| `Brand` | `brands` | Has products | CRUD; image and bilingual fields |
| `Product` | `products` | Belongs to section/brand; has images, variants, related products, providers, favourites, order items | Full CRUD; stock/status actions; media; filters; relation managers |
| `ProductImage` | `product_images` | Belongs to product; main image flag and ordering | Product relation manager; enforce one main image |
| `VariantSection` | `variant_sections` | Groups variants (size, pack, etc.) | CRUD; bilingual names and ordering |
| `ProductVariant` | `product_variants` | Belongs to product and variant section | Product relation manager; price/stock/status |
| `RelatedProduct` | `related_products` | Self-referencing product pivot; optional admin/user provenance | Product relation manager; unique pair and prevent self-reference |
| `ProviderProduct` | `provider_products` | Belongs to provider and product; provider price, margin, availability, admin moderation | Provider and Product relation managers; bulk enable/disable |
| `ProviderSection` | `provider_sections` | Provider-to-section pivot | Provider relation manager |
| `Favorite` | `favorites` | User-to-product pivot | Read-only relation manager; no standalone admin CRUD unless support needs it |

Schema decisions:

- Convert prices and app percentages to fixed decimals. Define whether base `products.price` is retail, wholesale, or fallback provider price.
- Replace cached counters (`visits`, `orders`, provider collection counts) with computed/cached metrics; do not import `users_provider_products_all_collection_info` as a writable model.
- Add indexes for published catalogue queries: section, brand, active/status, home/chosen flags, order, and searchable names.
- Preserve uploaded files under deterministic Laravel disk paths and produce a manifest mapping old path to new path, checksum, owner, and import status.

### 4.4 Orders, fulfilment, reviews, and payments

| Model | Canonical table | Main relationships / behavior | Filament |
|---|---|---|---|
| `Order` | `orders` | Customer, provider, address, currency, payment method, coupon, loyalty coupon; has items, delivery assignment, payments, status history, ratings | Primary operations resource; tabs/filters; guarded state actions; detail infolist |
| `OrderItem` | `order_items` | Belongs to order, product, variant, provider; contains immutable product/price snapshots | Order relation manager; edit only while status permits |
| `OrderDelivery` | `order_deliveries` | Belongs to order and driver; assignment/acceptance lifecycle | Relation manager plus assign/reassign/accept/start/deliver actions |
| `OrderStatusEvent` | `order_status_events` | Belongs to order; actor morph/type, status, note, timestamp | Read-only timeline on Order |
| `OrderAuditEvent` | `order_audit_events` | From `order_items_admin_events_log`; before/after JSON, actor and request metadata | Read-only relation manager |
| `OrderPayment` | `order_payments` | Consolidates electronic payment info; request/response JSON, provider status, amount/reference | Read-only/update status actions; retry only through service |
| `PaymentMethod` | `payment_methods` | Has availability areas and orders; electronic account config | CRUD; secrets masked/encrypted; availability relation managers |
| `PaymentMethodAvailability` | `payment_method_availabilities` | Polymorphic or explicit country/city/zone availability | Relation manager; replace five legacy pivot tables |
| `OrderRating` | `order_ratings` | Customer rates driver/order; has answers | Review resource; moderation and order link |
| `RatingQuestion` | `rating_questions` | Has rating answers | CRUD/order/status |
| `RatingAnswer` | `rating_answers` | Belongs to rating and question | Read-only relation manager |
| `OrderEvaluation` | `order_evaluations` | Legacy general order feedback, if distinct from driver rating | Read-only/moderation; merge only after confirming semantics |

Order implementation rules:

- `orders.total` must be persisted from explicit components: subtotal, discount, loyalty discount, delivery fee, gift wrapping, tax if any, and grand total. Never trust client totals.
- Create orders inside a transaction with row locks or atomic stock decrements. Generate `public_id`/UUID for clients and preserve `legacy_id`/`SysUniqueId` for traceability.
- Define an `OrderStatus` state-transition map. Only action classes may change status; direct Filament/API field editing is forbidden.
- Make checkout idempotent using a client idempotency key; payment callbacks require provider signature verification and unique external transaction/reference constraints.
- Temporary order/item/payment tables should become a `CheckoutSession`/`PaymentAttempt` workflow with expiry and cleanup, not three duplicated permanent schemas. Import unfinished legacy rows into quarantine for manual review.
- Preserve legacy `PostOrderJsonInfo`, product, address, currency, coupon, and payment snapshots as JSON audit fields where they are needed for support/legal history.

### 4.5 Coupons, loyalty, and wallet

| Model | Canonical table | Main relationships / behavior | Filament |
|---|---|---|---|
| `Coupon` | `coupons` | Has redemptions; code, type, limits, minimum order and deadline | CRUD; code uniqueness; usage/expiry filters |
| `CouponRedemption` | `coupon_redemptions` | Belongs to coupon, user and order; stores applied snapshot/amount | Read-only relation manager/resource |
| `LoyaltyCondition` | `loyalty_conditions` | Defines earning rules | CRUD; typed configuration and status |
| `LoyaltyCoupon` | `loyalty_coupons` | Defines point redemption rewards | CRUD; points/currency value/status |
| `LoyaltyEntry` | `loyalty_entries` | Immutable credit/debit ledger; user, order, condition/coupon, points and monetary snapshot | Read-only; corrective entry action, never edit/delete |
| `WalletEntry` | `wallet_entries` | Immutable monetary ledger; user/order/payment/actor, type, amount and status | Read-only; controlled credit/debit action with reason |

Schema decisions:

- Consolidate loyalty deposit, coupon withdrawal, and trigger tables into one ledger plus an idempotency key/source reference. Preserve original payload snapshots.
- Calculate wallet and loyalty balances from ledger entries. Cached balances on `users` may remain only as verified projections updated transactionally.
- Use signed decimals for money and integers for points. Never use float or varchar for monetary values.
- Enforce unique coupon codes and appropriate unique redemption rules per coupon/user/order.

### 4.6 Content, marketing, notifications, and system settings

| Model | Canonical table | Main relationships / behavior | Filament |
|---|---|---|---|
| `AboutPage` | `about_pages` or singleton `about_page` | Bilingual content and media | Singleton edit page/resource |
| `Advertisement` | `advertisements` | Optional product, section, provider; target app, URL, deadline | CRUD; placement, audience, expiry filters |
| `WelcomeSlider` | `welcome_sliders` | Bilingual content, image, audience, link target | CRUD/reorder |
| `ContactMessage` | `contact_messages` | Optional user; device/request metadata | Read-only queue; status, assignment, reply action |
| `Notification` | `notifications` | Sender, type, target URL/item, payload; has recipients | Compose/send resource; preview and queue status |
| `NotificationRecipient` | `notification_recipients` | User/account target; read/new timestamps | Notification relation manager; read-only |
| `ScheduledNotification` | `scheduled_notifications` | Optional user/order, payload, scheduled time, attempts/status | CRUD before dispatch; retry/cancel actions |
| `Configuration` | `configurations` | Singleton feature, app version, delivery, payment, contact and social settings | Grouped singleton settings page; secret fields encrypted/masked |
| `SearchLog` | `search_logs` | Search term and request metadata | Read-only analytics; retention/anonymization |
| `OnlineSession` | cache/session store | Legacy `online` table replacement | Dashboard widget only; no Eloquent model required |

Implementation rules:

- Dispatch notifications through queued jobs with database records as the source of truth. Scheduler processes due rows with locking and idempotency.
- Split the 100+ configuration columns into typed sections or dedicated settings objects only after confirming how frequently values change. Avoid a generic unrestricted key/value API.
- Do not migrate obsolete `days` or `news_users_events` tables unless code/data discovery demonstrates a live feature.

## 5. Filament resource architecture

### 5.1 Navigation groups

1. **Dashboard:** order/revenue/status/driver/stock/customer metrics, failed payments, scheduled notifications.
2. **Orders:** Orders, deliveries, payments, ratings.
3. **Catalogue:** Sections, products, brands, variants, provider catalogue.
4. **Accounts:** Customers, providers, drivers, admins.
5. **Locations & Delivery:** Countries, cities, zones, address types, delivery times.
6. **Payments & Finance:** Payment methods, wallet ledger, currency/rates.
7. **Promotions & Loyalty:** Coupons, loyalty conditions/coupons/ledger.
8. **Content & Messaging:** Ads, sliders, About, contact messages, notifications.
9. **System:** Configuration, permissions, Spatie activity log, import failures, system errors.

### 5.2 Resource rules

- Generate resources using Filament 5 commands after checking command options and official versioned docs.
- Use dedicated `Schemas`, `Tables`, `Pages`, and `RelationManagers` in the Filament 5 generated structure.
- Use bilingual Arabic/English fields side by side; Arabic inputs use RTL. Add locale-aware display helpers without hiding the source fields.
- Use enums for badges, filters, icons, and permitted transitions.
- Use policies for view/create/update/delete/restore and separate abilities for operational actions such as assign driver, cancel order, refund, credit wallet, or send notification.
- Disable delete for financial, order, notification delivery, and audit records. Use archive/status changes where needed.
- Public uploads explicitly use public visibility; sensitive payment/contact attachments use private storage with authorized temporary URLs.
- Eager-load every relationship displayed in tables/infolists and add indexes supporting default filters/sorts.
- Build custom pages/actions for workflows; do not expose raw status fields or secret configuration in generic CRUD forms.

### 5.3 Resource delivery priority

- **P0 (populated/live):** Admin, Customer/Provider/Driver scoped users, Country/City/Zone, AddressType/Address, Section, Product, Order, OrderDelivery, PaymentMethod, Coupon, WalletEntry, Rating, Notification, Configuration.
- **P1 (populated/supporting):** Currency/ExchangeRate, Ads, notification scheduling, Spatie activity-log viewer/import, import monitoring.
- **P2 (currently empty; confirm before generating):** Brand, product variants/images/related products, provider catalogue pivots, loyalty, contact messages, welcome sliders, payment-location pivots, payment-attempt history, error/search analytics.

## 6. API plan

### 6.1 API foundation

- Add `routes/api.php` through Laravel bootstrap routing and prefix routes with `/api/v1`.
- Use Sanctum or the approved compatible token guard, rate limiting by endpoint class, JSON-only exception rendering, pagination, stable error codes, and a request/correlation ID.
- Use controllers grouped by domain, Form Requests, API Resources/collections, policies, enums, and application actions/services shared with Filament.
- Default response shapes: resource/collection under `data`, pagination under `meta`/`links`, and validation/errors under a documented `message`, `code`, and `errors` contract.
- Support `Accept-Language: ar|en` while optionally returning both language fields where clients require them.
- Produce an OpenAPI contract once endpoint payloads are confirmed; use it as the compatibility checklist.

### 6.2 Public/bootstrap endpoints

| Method and path | Purpose | Legacy modules replaced |
|---|---|---|
| `GET /api/v1/bootstrap` | App configuration subset, versions, feature flags, main currency | `configurations.php`, `update.php`, parts of `main.php` |
| `GET /api/v1/about` | About content | `about.php` |
| `GET /api/v1/sliders` | Active audience-targeted sliders | `welcome_sliders.php` |
| `GET /api/v1/advertisements` | Main/offers placements | `ads.php` actions |
| `GET /api/v1/currencies` | Active currencies and current rates | `currencies.php` and currency helpers |
| `GET /api/v1/locations/countries` | Registration/address countries | `places.php`, address country actions |
| `GET /api/v1/locations/countries/{country}/cities` | Cities | legacy `get_cities` |
| `GET /api/v1/locations/cities/{city}/zones` | Zones/areas | legacy `get_areas` |
| `GET /api/v1/address-types` | Active address types | `addresses-types.php` |
| `GET /api/v1/delivery-times` | Active delivery windows | `delivery-times.php` |
| `POST /api/v1/contact-messages` | Submit contact/support message | `contacts.php`, `contact_Send_Mail.php` |

### 6.3 Authentication and account endpoints

| Method and path | Purpose | Notes |
|---|---|---|
| `POST /auth/register` | Mobile/email customer registration | Validate unique normalized mobile/email; issue token after required verification |
| `POST /auth/login` | Password login | Rate limit; rehash legacy password after successful compatibility check |
| `POST /auth/mobile/request-code` | Send mobile OTP | Rate limit by mobile/IP/device; do not reveal account existence |
| `POST /auth/mobile/verify` | Verify OTP/login | Single-use, expiring hashed code |
| `POST /auth/social/{provider}` | Google/Apple/Facebook/Firebase login | Verify provider token server-side; link identities safely |
| `POST /auth/forgot-password` | Start reset | Uniform response |
| `POST /auth/reset-password` | Verify code/token and reset | Revoke existing tokens as policy requires |
| `POST /auth/logout` | Revoke current token | Replaces GET logout |
| `GET /me` | Current profile and balances | Derived balances, account capabilities |
| `PATCH /me` | Update allowed profile fields | Strict allow-list |
| `PUT /me/password` | Change password | Require current password unless verified reset flow |
| `DELETE /me/avatar` | Remove avatar | Storage transaction/cleanup |
| `POST /me/close` | Close account | Capture reason; revoke tokens; business eligibility checks |
| `GET/POST/PATCH/DELETE /me/addresses` | Address CRUD | Ownership policy and location consistency |
| `PUT /me/devices/{device}` | Register/update push token | Unique device/user constraints |

### 6.4 Catalogue endpoints

| Method and path | Purpose |
|---|---|
| `GET /sections` and `GET /sections/{section}` | Category tree/detail |
| `GET /brands` and `GET /brands/{brand}` | Brand list/detail |
| `GET /products` | Paginated products; filters for section, brand, provider, featured, offers, search and sort |
| `GET /products/{product}` | Product, images, variants, provider availability and related products |
| `GET /me/favorites` | Favourite products |
| `PUT /me/favorites/{product}` / `DELETE ...` | Idempotent add/remove favourite |

Search queries must allow-list filter/sort fields, escape wildcard semantics where appropriate, and use indexed/full-text search selected for the target database.

### 6.5 Checkout, orders, payment, and reviews

| Method and path | Purpose |
|---|---|
| `POST /checkout/quote` | Server-calculated prices, stock, discounts, delivery, currency and totals |
| `POST /coupons/validate` | Validate coupon for current user/cart |
| `POST /loyalty-coupons/validate` | Validate points and reward |
| `GET /payment-methods` | Methods available for selected location/order |
| `POST /orders` | Idempotent transactional checkout |
| `GET /orders` / `GET /orders/{order}` | Current user order history/detail |
| `POST /orders/{order}/cancel` | Policy/state-controlled cancellation |
| `POST /orders/{order}/reorder-quote` | Revalidate historic items before reorder |
| `POST /orders/{order}/payment-attempts` | Initialize electronic payment |
| `POST /payment-webhooks/{provider}` | Signed idempotent provider callback |
| `POST /orders/{order}/rating` | Submit driver/order rating and answers |

### 6.6 Wallet, loyalty, and notifications

| Method and path | Purpose |
|---|---|
| `GET /me/wallet` / `GET /me/wallet/entries` | Balance and paginated immutable ledger |
| `GET /me/loyalty` / `GET /me/loyalty/entries` | Points balance/history |
| `GET /loyalty/conditions` / `GET /loyalty/coupons` | Active earning/redemption information |
| `GET /me/notifications` | Paginated notifications |
| `PATCH /me/notifications/{notification}/read` | Mark one read |
| `POST /me/notifications/read-all` | Mark all read |

### 6.7 Driver and provider endpoints

Driver endpoints under `/api/v1/driver`:

- `GET /orders/available`, `GET /orders`, `GET /orders/{order}`.
- `POST /orders/{order}/accept`, `/start`, `/deliver`, and `/reject` if supported.
- `PATCH /location` and `PATCH /availability`.
- `GET /profile`, `PATCH /profile`, device registration, notification endpoints, and payment/account information required by the driver app.
- State transitions must be atomic so two drivers cannot accept one order.

Provider endpoints under `/api/v1/provider`:

- Provider profile and assigned sections.
- Product catalogue/availability, provider price and app percentage where providers are allowed to edit them.
- Provider order queue/details and only the confirmed state actions providers own.
- Provider revenue/settlement endpoints only if the legacy application actually exposes settlement data.

### 6.8 Legacy API adapter

If deployed apps cannot migrate immediately, expose the existing PHP paths or a dedicated `/api/legacy/v1/{module}` adapter. It should:

1. Validate and normalize old POST fields/action names.
2. Authenticate using the migration token bridge.
3. Call the same canonical actions used by REST controllers.
4. Translate API Resources/errors into the exact legacy response shape.
5. Log action usage by app version so endpoints can be retired safely.
6. Have a published end date; no new business logic may be implemented in the adapter.

## 7. Application service/action plan

Keep controllers and Filament actions thin. Introduce focused classes as the workflows are implemented:

- `RegisterUser`, `AuthenticateUser`, `VerifyMobileCode`, `LinkSocialIdentity`.
- `CalculateCheckoutQuote`, `ValidateCoupon`, `ValidateLoyaltyRedemption`.
- `CreateOrder`, `CancelOrder`, `CreateReorderQuote`, `ChangeOrderStatus`.
- `AssignDriver`, `AcceptDelivery`, `StartDelivery`, `CompleteDelivery`.
- `CreatePaymentAttempt`, provider-specific payment gateway contracts, `HandlePaymentWebhook`.
- `AdjustStock` with atomic operations and explicit reasons.
- `CreditWallet`, `DebitWallet`, `AwardLoyaltyPoints`, `RedeemLoyaltyPoints` with idempotency.
- `SendNotification`, `DispatchScheduledNotifications`, push-channel contracts.
- `ImportLegacyData` broken into domain-specific importers plus reconciliation reports.

Use events/jobs only for work that is asynchronous or fan-out (push notifications, email, analytics, projections). Core order/payment/ledger state must commit before jobs dispatch, ideally using after-commit behavior.

## 8. Database migration and import strategy

### Phase A: schema discovery and value mapping

1. Use the populated snapshot for development, then obtain a new production SQL export containing data, routines/views if needed, row counts, and uploaded media for rehearsal and again for cutover.
2. Freeze and document legacy global arrays for account types, order/payment/delivery status, coupon/loyalty types, active/status, target application, and payment type.
3. Profile nulls, orphan IDs, duplicate coupon codes, duplicate devices, invalid coordinates, impossible amounts, broken JSON, and file references.
4. Produce a signed mapping sheet for every legacy column: destination, transformation, enum mapping, default, or explicit discard reason.

### Phase B: canonical migrations

Create migrations in dependency order:

1. Identity and permissions.
2. Currency and geography.
3. Users/devices/addresses.
4. Catalogue and provider pivots.
5. Payment methods and availability.
6. Coupons, loyalty rules, and content.
7. Orders/items/deliveries/status/audit/payment/rating.
8. Wallet and loyalty ledgers.
9. Notifications/contact/diagnostics.

Add foreign keys after orphan policy is defined. Use nullable references only where the historical record is valid without the parent; otherwise quarantine bad rows rather than silently setting zero/null.

### Phase C: repeatable import

- Load the legacy dump into an isolated legacy database or prefixed staging schema.
- Implement resumable Artisan import commands per domain using chunking, explicit transactions, ID maps, and an `import_runs`/`import_failures` record.
- Preserve original primary keys when safe; otherwise maintain deterministic ID mapping tables.
- Import parents before children. Transform `0` foreign keys to null only when zero means “none”.
- Decode legacy JSON with failure capture. Never discard malformed payloads; store raw data in quarantine.
- Copy files using a manifest and checksums; report missing and duplicate media.
- Make every importer idempotent (`upsert`/source key), dry-runnable, and safe to resume.

### Phase D: reconciliation

For every table/domain compare:

- Source, imported, skipped, quarantined, and orphan counts.
- Sum of order totals, payment amounts, wallet credits/debits, loyalty credits/debits, and stock quantities.
- Counts grouped by order/payment/account status and dates.
- Sampled order snapshots reconstructed against legacy output.
- All file counts/checksums and missing paths.
- All foreign-key and unique-constraint violations.

Migration cannot proceed to cutover while unexplained financial differences remain.

## 9. Testing plan

Use Pest feature tests as the default and factories with named states for all canonical models.

### 9.1 Model and schema behavior

- Relationship, scope, cast, enum, unique constraint, cascade/restrict, and balance projection behavior.
- Factory states for account types, statuses, expired coupons, out-of-stock products, payment states, and delivery states.
- Money uses exact decimals; timestamp and active/status transformation tests use fixed known examples.

### 9.2 Filament

- Authenticate with an `Admin` before every panel test.
- Resource list/search/filter/create/edit behavior for CRUD resources.
- Full policy/permission matrix at policy level and one denied panel request/action per important boundary.
- Custom order, driver assignment, payment, wallet, notification, and status actions assert notification, validation, database state, and side effects.
- Verify private/public upload behavior and prevent unauthorized access to sensitive attachments.

### 9.3 API contracts

For each endpoint cover guest/invalid token, forbidden account type, ownership/cross-user access, invalid input, valid response shape, persisted state, and side effects. Cross-user record access should generally return 404 where existence is sensitive.

High-priority matrices:

- Registration/login/OTP/social linking/token revocation and account closure.
- Catalogue filtering/sorting/search allow-lists and hidden/inactive records.
- Coupon and loyalty expiry, minimums, per-user/global limits and insufficient points.
- Checkout price tampering, out-of-stock races, duplicate idempotency key, delivery fee and exchange-rate boundaries.
- Every allowed and forbidden order/delivery/payment transition, including concurrent driver acceptance.
- Duplicate/replayed payment callbacks, invalid signatures, gateway timeouts and retries.
- Wallet/loyalty idempotency and exact balance invariants.
- Notification recipient isolation and read state.

Use framework fakes for storage, notifications, mail, queues, events, time, and outbound HTTP. Prevent stray HTTP requests and fake exact gateway/social/push endpoints.

### 9.4 Import tests

- Fixture SQL/source rows for every transformation and enum mapping.
- Orphan, duplicate, malformed JSON, missing file, zero-FK, invalid timestamp, and legacy password cases.
- Import rerun produces no duplicates.
- Reconciliation reports expected counts and monetary sums.
- A sanitized production sample migration is rehearsed before full-volume testing.

### 9.5 Performance and acceptance

- Query-count tests for product lists, order tables/details, notifications, and dashboard widgets.
- Load tests for bootstrap/catalogue/search/checkout and driver available-order polling.
- User acceptance scripts for admin order lifecycle, customer checkout/cancel/reorder, driver delivery lifecycle, provider catalogue/order workflow, wallet/loyalty, and payment recovery.

## 10. Security and operations checklist

- Rotate all secrets found in legacy PHP/configuration files; never copy credentials into Git.
- Hash passwords with Laravel's configured hasher; encrypt payment/WhatsApp/provider tokens; mask secrets in Filament.
- Validate uploaded MIME/content/size and use randomized filenames; private disk for sensitive files.
- Rate limit login, OTP, contact, search, checkout, coupon validation, and payment initialization.
- Verify payment and social identity tokens server-side; verify webhook signatures and timestamp/replay windows.
- Protect mass assignment with explicit attributes and Form Requests; authorize every record operation.
- Minimize and expire diagnostic data containing sessions, cookies, request dumps, IPs, and user agents.
- Add queue monitoring, failed-job handling, scheduled-task overlap locks, payment/notification alerting, health checks, and structured correlation IDs.
- Back up the legacy database and media before every rehearsal/cutover; define rollback and read-only maintenance modes.

## 11. Delivery phases and exit criteria

### Phase 0 — discovery and contract freeze

Deliverables: populated SQL/media export, enum/value dictionary, legacy endpoint/action catalogue, external integration inventory, signed schema mapping, compatibility decision.

Exit: every active legacy page/API action and database column has an owner and disposition.

### Phase 1 — foundation

Deliverables: API routing/auth, enums, policies/permissions, shared response/error contract, storage layout, core migrations, factories, import framework.

Exit: admin/customer authentication and authorization tests pass; importer can run and report failures.

### Phase 2 — locations, accounts, catalogue

Deliverables: related models/migrations, P0 Filament resources, bootstrap/location/catalogue/account APIs, media importer.

Exit: applications can browse catalogue and manage profiles/addresses against migrated sample data.

### Phase 3 — orders, delivery, and payments

Deliverables: checkout/order/payment services, Order Filament workflow, customer and driver APIs, gateway adapters and webhooks.

Exit: end-to-end order lifecycle passes with exact totals, concurrency, idempotency, and failure recovery tests.

### Phase 4 — promotions, wallet, loyalty, messaging

Deliverables: coupon/loyalty/wallet ledgers, notifications/scheduling, contact/content resources and APIs.

Exit: ledger reconciliation and notification retry/idempotency suites pass.

### Phase 5 — migration rehearsal and compatibility

Deliverables: full import, reconciliation report, legacy API adapter if required, performance results, UAT fixes.

Exit: unexplained financial variance is zero; orphan/malformed records have approved disposition; mobile/client contract tests pass.

### Phase 6 — cutover

1. Announce freeze window and put legacy writes into maintenance/read-only mode.
2. Take final database and media backup/export.
3. Run delta/final import and reconciliation.
4. Switch API/admin traffic, monitor errors/queues/payments/orders, and retain rollback capability.
5. Keep legacy system read-only for the agreed audit period.

Exit: operational owners approve metrics, payments, orders, authentication, and support workflows.

### Phase 7 — retirement

Remove the compatibility adapter only after usage telemetry shows supported app versions no longer call it. Archive legacy code/data securely, expire old credentials, remove temporary staging data, and finalize retention rules.

## 12. Recommended generation order

Do not generate all files at once. Implement and verify one coherent slice at a time:

1. Enums and shared concerns.
2. Currency + Country + City + Zone.
3. User + device + address + scoped Customer/Provider/Driver Filament resources.
4. Section + Brand + Product + images/variants/provider pivots.
5. PaymentMethod + availability.
6. Coupon + loyalty definitions.
7. Order + items + delivery + state history.
8. Payments and gateway contracts.
9. Wallet/loyalty ledgers.
10. Ratings, notifications, content, contact, and diagnostics.
11. Importers and reconciliation in the same domain order.
12. REST API endpoints, with the legacy adapter added only where telemetry/compatibility requires it.

For each slice, the definition of done is: migration, model, enum/casts/relationships, factory, policy, Filament resource where applicable, Form Requests, API Resource/controller/routes where applicable, importer mapping, focused Pest tests, formatting/static checks, and updated API contract.
