# Identity and Access Models

## Permission architecture: Spatie Permission

Use `spatie/laravel-permission`. Do not create an `AdminPermission` model or custom `admin_permissions` table.

- Use package `Role` and `Permission` models/tables and its standard pivot tables.
- Use the `admin` guard and add Spatie's `HasRoles` trait to `Admin`.
- Filament authorization remains policy-based; roles/permissions provide abilities.
- Convert legacy department/page permissions to named abilities only after extracting the legacy menu/check map.
- The populated dump has no permission rows, so seed an approved role matrix instead of importing empty data.
- Map legacy `platform_admins.super_admin = 'yes'` to an approved Spatie super-admin role during import. Do not retain a boolean super-admin field.

The package is not installed. Adding it is a separate implementation step requiring dependency approval and version/documentation verification.

## Activity logging: Spatie Activitylog

Use `spatie/laravel-activitylog`. Do not create an `AdminAuditLog` model or custom `admin_audit_logs` table.

- Add the package's `LogsActivity` behavior only to approved auditable models.
- Configure explicit logged attributes and meaningful event descriptions; do not log passwords, tokens, secrets, or unnecessarily large payloads.
- Use the package `Activity` model and `activity_log` table for the Filament read-only activity viewer.
- Map the 299 legacy `platform_logs` records into package activity records where actor, action, subject, and timestamp can be resolved. Preserve unresolved legacy context in activity properties.
- Define retention and pruning rules before enabling request metadata logging.

The package is not currently installed. Installation/configuration is a separate implementation step requiring dependency approval and compatible-version documentation review.

## `Admin` → `admins`

| Field | Type / constraints | Legacy reference |
|---|---|---|
| `id` | bigint PK | `platform_admins.id` |
| `username` | varchar(50), unique | unchanged |
| `name` | varchar(255) | unchanged |
| `email` | varchar(255), unique; temporarily nullable for import | unchanged |
| `password` | varchar(255), hidden | detect legacy hash |
| `notes` | text nullable | unchanged |
| `is_active` | boolean | `auth → is_active` |
| `last_login_at` | datetime nullable | `last_login → last_login_at` |
| `remember_token` | nullable | new |
| timestamps | datetime | `date_created → created_at`; `updated_at` new |

Relationships/traits: Spatie Permission `HasRoles`; Spatie Activitylog `LogsActivity` when Admin changes must be audited. Any super-admin behavior must be based on the designated Spatie role and tested through authorization policies.

## `User` → `users`

Fields:

- Identity: `id`, unique nullable `referral_code`, `account_type`, `name`, nullable `display_name`, `email`, `mobile`, `country_dial_code`, `password`.
- External identity: nullable `firebase_uid`, `facebook_id`, `external_provider`, `external_account_id`; encrypted external token only if unavoidable.
- Geography: nullable `country_id`, `city_id`, `zone_id`.
- Profile: `avatar_path`, phone/social fields, `job`, `gender`, `birth_date`, `address_text`, `bio`.
- Preferences: `is_public`, `accepts_newsletters`, `show_name`, decimal `app_percentage`.
- Verification/lifecycle: `mobile_verified_at`, `email_verified_at`, `activated_at`, `last_login_at`, `is_active`, `closed_at`, `close_type`, `close_reason`.
- Optional projections: decimal `wallet_balance`, integer `loyalty_points_balance`; ledgers are authoritative.
- Laravel: `remember_token`, timestamps.

Relationships: belongs to optional country/city/zone; has many addresses, devices, orders, favorites, wallet/loyalty entries, coupon redemptions and notification recipients. Provider aliases: providerProducts/providerSections/providerOrders. Driver aliases: deliveries/driverRatings.

Renames: `accounts_type → account_type`, `accounts_type_access_id → external_account_id`, `public_show → is_public`, `accept_newsletters → accepts_newsletters`, `country_code → country_dial_code`, `area_id → zone_id`, `confirm_mobile/email → mobile_verified_at/email_verified_at`, `image/picture → avatar_path`, `birthdate → birth_date`, `address → address_text`, `active → is_active` (inverted), `close_account/close_date → closed_at`, `activated → activated_at`, `last_login → last_login_at`, `date/date_last_change → created_at/updated_at`.

Never permanently migrate plaintext temporary passwords, OTPs, reset tokens, or provider tokens.

## `UserDevice` → `user_devices`

Fields: `id`, `user_id`, `device_identifier`, `device_name`, `platform`, `push_provider`, encrypted `push_token`, optional `legacy_auth_token_hash`, `is_activated`, `is_online`, `last_seen_at`, `last_login_at`, nullable coordinate decimals, `ip_address`, `user_agent`, timestamps.

Relationship: belongs to user; optional registeredByAdmin. Unique `user_id + device_identifier`.

Renames: `mobile_id → device_identifier`, `player_id/fcm_token → push_token + push_provider`, `token → legacy_auth_token_hash`, `activated → is_activated`, `online → is_online`, `last_online → last_seen_at`, `ipaddress → ip_address`, `date → created_at`.

## Intentionally absent

No application `AdminPermission` or `AdminAuditLog` model. Spatie owns permission and activity models. Password/OTP challenge data should use framework tokens/cache unless a database-backed expiring challenge is explicitly selected.
