# Organization isolation across admin, backend, and Flutter

## Delivery workflow

Implementation is split into review gates. Complete and review one stage before
starting the next. Each stage must include its focused automated tests and must
leave the application runnable.

- [x] **Stage 1 — Admin access foundation:** make
  super-admin bypass explicit, require organization access whenever data-scope
  permissions are assigned, and intersect division/sales-office limits with
  that organization. Preserve legacy visibility for roles with no data-scope
  assignments during the staged rollout.
- [x] **Stage 2 — Persistent admin organization context:** add the organization
  selector, configure role organization assignments, enable deny-by-default
  enforcement, and apply it to resources, direct record access, widgets,
  reports, exports, actions, and relationship fields.
- [x] **Stage 3 — Explicit business-record ownership:** add organization ownership
  to drivers, authorization requests, money transfers, signup requests, and
  account-stop requests, with validation and safe legacy backfill/review.
- [x] **Stage 4 — Organization content and configuration:** scope business
  settings, numbering, images, terms, about content, ads, and notifications;
  retain technical app controls as global settings.
- [x] **Stage 5 — Flutter organization propagation:** send organization context,
  partition caches, reload organization content on switching, and fix clearing
  and stale-response behavior.
- [x] **Stage 6 — Report regression and acceptance:** verify the contract delivery
  filter, sales-office permissions, all report screens, and the two-company
  end-to-end acceptance matrix.

### Stage review record

For every completed stage, record the commit, migrations (if any), tests run,
manual checks, known limitations, and reviewer approval here before proceeding.

#### Stage 1

- Status: implemented; awaiting reviewer approval before Stage 2.
- Commit: not created.
- Migrations: none.
- Automated checks: organization access scope tests and existing organization
  model tests pass (9 tests, 19 assertions).
- Rollout safeguard: roles with no `view_data:*` permissions retain their prior
  visibility until Stage 2 configures organization assignments and enables
  strict enforcement.
- Known limitation: PHP 8.5 reports the existing deprecated
  `PDO::MYSQL_ATTR_SSL_CA` constant from `config/database.php`.
- Full-suite baseline: 34 existing failures remain in unrelated Filament
  authorization/component tests, legacy cascade expectations, division API
  assertions, and user-agreement schema/factory tests. Stage 1 focused tests
  pass independently.

#### Stage 2 — Part 1

- Status: implemented; awaiting review before organization context is applied
  to resource queries and actions.
- Added a persistent session organization context and an admin user-menu
  selector restricted to the organizations assigned through
  `view_data:organization:*` permissions. A single available organization is
  selected automatically, and stale or unauthorized selections are cleared.
- Strict deny-by-default resource enforcement remains disabled until the next
  reviewed part.

#### Stage 2 — Part 2

- Status: implemented; awaiting review before expanding enforcement to
  resources that do not use customer sales-area relationships.
- The current tenant now constrains all ten resources using the shared customer
  sales-area scope. Unassigned admins are denied by default, and super admins
  can access every tenant through Filament's tenant switcher.

#### Stage 2 — Built-in tenancy revision

- Replaced the temporary session context and custom sidebar selector with
  Filament's built-in Organization tenancy, tenant switcher, tenant URL access
  checks, and default-tenant handling.
- Tenancy is opt-in for resources until direct organization ownership is added.
  The ten shared-customer resources continue to use their custom sales-area
  scope, now driven by Filament's current tenant.
- Admins with no assigned organizations are securely signed out and redirected
  to login with an access message instead of becoming trapped on a tenant 404.

#### Stage 2 — Part 3

- Status: implemented; awaiting review.
- The Customer Usage report, displayed organization badges, and exported rows
  are constrained to the active Filament tenant. Widgets for records without
  explicit organization ownership remain scheduled for Stage 3.

#### Stage 3 — Part 1

- Status: schema foundation implemented; awaiting review before API enforcement.
- Added many-to-many organization ownership for drivers. Every historical
  driver is assigned to all valid organizations present in that driver's
  customer sales areas.
- Driver authorization requests derive their organization visibility from the
  linked driver's organization assignments rather than storing duplicate
  organization ownership.
- No mobile request contract changed in this part.

#### Stage 3 — Part 2

- Status: implemented; awaiting review.
- Driver API requests accept an optional `organization_code`. New clients are
  restricted to that customer organization; legacy clients that omit it retain
  customer-wide behavior and assign/query all organizations from the
  customer's sales areas.
- Driver authorization creation validates visibility through the selected
  driver's organization assignments.

#### Stage 3 — Part 3

- Status: implemented; awaiting review.
- Money transfers store one originating organization and use Filament's direct
  tenant scoping. Legacy requests infer ownership only for single-organization
  customers; ambiguous legacy requests must select an organization.
- Historical money transfers that could not be inferred because they had no
  organization are assigned to the agreed legacy organization `7100`, ensuring
  they remain visible in that tenant after rollout.

#### Stage 3 — Part 4

- Status: implemented; awaiting review.
- Signup requests support many-to-many organization ownership through a pivot
  table while retaining organization-less existing requests.
  New organization-owned requests, pending duplicate checks,
  admin resources, dashboard widgets, and notification links/recipients are
  tenant-scoped. Legacy unassigned requests are retained for later review.

#### Stage 3 — Part 5

- Status: implemented; awaiting review.
- Suspension requests belong to one selected customer organization and pending
  duplicate checks are scoped to that organization. Whole-account deletion
  requests remain central. Historical suspensions are backfilled only for
  customers with one valid organization; ambiguous records remain available
  only to super admins for review.

#### Stage 4 — Part 1

- Status: schema foundation implemented; awaiting review before wiring the
  Filament settings page and API.
- Added organization-specific storage for business text, feature controls, and
  reference numbering. Existing values are copied to every organization while
  technical controls such as app versions, update enforcement, and maintenance
  state remain in the global settings table.

#### Stage 4 — Part 2

- Status: implemented; awaiting review.
- The Filament settings page reads and writes business settings for the active
  organization. Maintenance and app-version controls remain global and are
  available only to super admins. The app-settings API accepts an organization
  code and returns that organization's business configuration; omitted context
  temporarily returns the legacy global values until the Flutter rollout.

#### Stage 4 — Part 3

- Status: implemented; awaiting review.
- Sale-contract and sale-order numbers use independent organization templates,
  serial lengths, and transactionally incremented sequences. The existing SAP
  request field names remain unchanged, and both organization membership and
  the organization/division sales-area combination are validated.

#### Stage 4 — Part 4

- Status: implemented; awaiting review.
- Images are owned and uniquely identified by organization, type, and code.
  Filament image records and uploads are tenant-scoped, and new files use
  organization-specific storage paths. Existing global files remain untouched;
  their references are copied to each organization and retained as a temporary
  API fallback until Flutter sends organization context.
- Added the idempotent `images:copy-to-organizations` Artisan command to copy
  legacy files into organization directories and update tenant records. It
  supports `--dry-run` previews and leaves global fallback records unchanged.

#### Stage 4 — Part 5

- Status: implemented; awaiting review.
- About pages and terms are tenant-scoped in Filament. Home banners and popups
  belong to organizations, while login banners remain global because they are
  shown before organization selection and are managed only by super admins.
  Omitted context returns preserved global content during Flutter rollout.
  Terms reads and acceptance validate customer membership in the owning
  organization.
- Removed the redundant organization tabs from About Pages and Terms of Use;
  Filament's active tenant is now the single organization context.
- Newly created organizations are automatically provisioned with baseline
  business settings and legacy image/non-login-ad references. Provisioning is
  idempotent and excludes global login banners and push notifications.

#### Stage 5 — Part 1

- Status: implemented in Flutter; awaiting review.
- Organization selection now reloads organization-specific app settings and
  image updates. Settings caches and image update timestamps are partitioned by
  organization, stale settings responses are discarded after switching, and
  nullable state copying now genuinely clears the selected organization.
- Push-notification behavior remains unchanged by request.

#### Stage 5 — Part 2

- Status: implemented in Flutter; awaiting review.
- The selected organization is now sent when listing, creating, updating, or
  deleting temporary drivers; when submitting temporary-driver authorization
  requests; and when creating permanent-driver and money-transfer requests.
- Company suspension requests include the selected organization. Whole-account
  deletion remains global and intentionally omits organization context.
- Flutter blocks these organization-owned operations when no organization is
  selected. Push-notification behavior remains unchanged by request.

#### Stage 5 — Part 3

- Status: implemented; awaiting review.
- Added a lightweight API-key-protected organization-options endpoint for the
  public account-request form. The Flutter signup screen now requires selecting
  one or more organizations and submits them as `organization_codes`.
- Existing organization-less signup records remain unchanged, and push
  notifications remain outside the organization-separation work by request.

#### Stage 5 — Part 4

- Status: implemented in Flutter; awaiting review.
- Home banners and popup ads are cleared and reloaded when the selected
  organization changes. In-flight responses from a previously selected
  organization are discarded so they cannot overwrite the active company's
  content. Organization-owned ads are not loaded without an organization.
- Login banners remain global and push notifications remain unchanged.

#### Stage 5 — Part 5

- Status: implemented in Flutter; awaiting review.
- Temporary-driver state now records the organization it belongs to. Switching
  organizations immediately clears the previous list and reloads the selected
  organization's drivers. Delayed list responses and completed create, update,
  or delete operations cannot mutate the visible list after an organization
  switch.
- Permanent SAP driver/truck retrieval remains customer-based at the existing
  SAP integration boundary. Push notifications remain unchanged.

#### Stage 5 — Part 6

- Status: implemented; awaiting review.
- Temporary-driver creation and editing now let the customer select one or
  more of their available organizations. Flutter submits repeated
  `organization_codes[]` multipart fields, and Laravel validates every code
  against the customer's sales-area organizations before synchronizing the
  driver's organization assignments.
- The existing single `organization_code` request remains supported for older
  application versions.

#### Shared permanent-driver revocations

- Status: implemented; awaiting review.
- SAP permanent drivers and their revocation requests are customer-wide rather
  than organization-owned. Revocations are visible from every Filament tenant
  in both the dashboard widget and resource list, and direct view links use the
  same rule. Temporary-driver authorization requests remain scoped through the
  linked driver's organizations and the admin's division/sales-office limits.
- Flutter no longer sends an unused organization code when submitting a
  permanent-driver revocation request.

#### Stage 5 — Part 7

- Status: implemented; awaiting review.
- Temporary-driver creation and editing reject duplicates for the same
  customer by mobile number, national ID, or the combined vehicle number and
  separator. Soft-deleted drivers do not block a replacement record.
- The Flutter drivers page now provides one local search field for both tabs.
  It searches loaded temporary-driver identity and vehicle fields and loaded
  permanent SAP driver, truck, and customer fields.

#### Stage 5 — Part 8

- Status: implemented; awaiting review.
- Flutter code-based division, brand, and product image URLs now include the
  selected organization directory, matching the server path
  `uploads/images/{organization}/{type}/{code}.png`.
- Every division, brand, product, offer, cart, and add-to-cart image call site
  passes its active sales organization, preventing one organization's static
  images from being reused for another organization.
- Replacing a code-based image now always touches its database record so the
  image-update API detects the replacement. Admin previews use an
  `updated_at` cache-busting query and image responses require revalidation,
  preventing browsers and intermediary caches from showing the old file.

#### Stage 6 — Part 1

- Status: implemented; awaiting review.
- Added a direct-record isolation regression covering drivers, signup
  requests, money transfers, images, ads, about pages, terms of use, and
  organization suspension requests. For each resource, a record owned by the
  active tenant resolves normally while an ID owned by another tenant resolves
  to no record, protecting view/edit URLs in addition to list queries.
- The previously stale pre-tenancy Filament URLs/component namespaces,
  soft-delete expectations, division response assertions, root redirect, and
  obsolete user-agreement schema tests have been updated. The complete backend
  suite now passes with 117 tests and 377 assertions.

#### Stage 6 — Part 2

- Status: implemented; awaiting review.
- The Filament tenant switcher is now the only company selector on business
  resource tables. Redundant organization filters were removed, while customer,
  division, and sales-office filters remain as within-tenant refinements.
- Customer filter options and customer relationship selectors now use one
  shared tenant scope that requires the active organization and intersects its
  division and sales-office permissions. Division and sales-office filter
  options are limited to values that exist in the active organization.
- Driver authorization forms limit temporary-driver choices to drivers linked
  to the active organization, and terms-acceptance forms limit terms choices to
  the active organization. Drivers created from Filament are automatically
  attached to the active organization.
- Manual customer creation is disabled because it cannot establish the
  required SAP sales-area ownership. Full customer synchronization from SAP is
  visible only to super admins. Global reference/configuration resources and
  push notifications remain intentionally shared.

#### Stage 6 — Part 3

- Status: completed and manually accepted.
- Added an explicit regression for an ordinary admin assigned to two
  organizations. Switching the active Filament tenant exposes only the
  selected organization's customers. Together with the existing tests, the
  access matrix now covers one organization, both organizations, neither,
  super admin, division restrictions, and sales-office restrictions.
- Verified direct record isolation for tenant-owned Filament resources. The
  focused backend matrix passes with 13 tests and 36 assertions.
- Updated stale soft-delete and pre-tenancy URL assertions to match the current
  recoverable-delete and Filament tenant URL behavior. The broader organization
  and tenant suite now passes with 68 tests and 211 assertions.
- Verified all required Flutter contract-delivery scenarios: an open delivery
  remains incomplete even when no contract quantity remains; all completed
  deliveries with no remaining quantity are complete; unallocated quantity is
  incomplete; and a contract with no deliveries is incomplete. All 5 focused
  tests pass.
- The Flutter test harness now mounts the real application shell with a
  dependency-free test home and disables notification callbacks only for that
  test. The manual API diagnostic requires explicit `MANUAL_API_BASE_URL` and
  `MANUAL_API_KEY` dart defines, so normal tests cannot contact production and
  no API key remains hard-coded in its source.
- The complete Flutter suite passes with 11 tests. Static analysis completes
  without a fatal analyzer error and reports 104 pre-existing warning/info
  items that are outside this organization-isolation rollout.
- Manual acceptance: the customer reviewed the admin panel and Flutter app in
  the real environment and confirmed the organization-aware behavior. This
  completes the two-company manual acceptance gate.

#### Stage 6 — Part 4

- Status: implemented and verified.
- Customer groups are now owned by an organization using `organization_id` and
  are scoped through Filament's built-in tenant ownership. The same group name
  may be used independently by different organizations.
- Existing groups are safely distributed to the organizations represented by
  their assigned customers' sales areas. Groups without customer assignments
  are copied to every existing organization to preserve availability.
- Customer edit forms load and update only the active tenant's group
  memberships. Memberships belonging to other tenants are explicitly
  preserved, preventing edits in one company from changing another company.
- Customer-group choices used for push-notification audiences are restricted
  to the active tenant. Single-group delivery now resolves group devices
  directly, avoiding stale topic IDs after legacy groups are copied.
- The complete backend suite passes with 120 tests and 386 assertions.

## Summary

Reviewed the 12-page customer report and the backend and Flutter project at `Desktop/flutter_projects/platform/alsaeed_app`. Source: :codex-file-citation{path="/Users/mac/Downloads/Check Mobile App.pdf" purpose="source"}.

The report identifies missing company separation for drivers, authorization requests, signup requests, account-stop requests, images, and settings.

Existing filters are insufficient for isolation:

- Admins without data filters currently see all records.
- Organization, division, and sales-office permissions use OR conditions, potentially widening access.
- Some transactions are filtered through customer memberships rather than their originating company.
- Flutter settings and image caches lack consistent organization separation.

## Admin and backend changes

- Add a persistent organization selector covering business resources, dashboard widgets, reports, exports, searches, relationship selectors, and actions. Restrict its choices to authorized organizations.
- Centralize organization authorization and apply it to record reads and writes, including direct URLs and bulk actions. Only explicit super admins receive unrestricted access; unassigned admins receive no company data.
- Require organization membership first. Apply optional division and sales-office restrictions together within the same customer sales-area record.
- Store explicit organization ownership on drivers, authorization requests, signup requests, account-stop requests, money transfers, and other business records currently relying solely on customer membership. Validate related records belong to the same organization.
- Preserve customers as shared identities with organization memberships. Company admins manage only their company’s relationships and operations.
- Add company selection to signup. Scope suspension requests and duplicate-request checks to the selected company. Keep whole-account deletion centrally managed.
- Separate business settings by organization, including business text, feature controls, and reference numbering. Retain app versions and infrastructure settings globally under super-admin control.
- Scope images by organization, type, and code, including uniqueness, storage paths, returned URLs, and update checks.
- Audit screens marked “completed” in the report for authorization, not merely visible filters. Verify existing sales-office role controls persist and enforce permissions correctly.
- Keep shared geographic/currency reference data centrally managed; organization admins may use those references without gaining global editing rights.

## API and Flutter changes

- Use `organization_code` for organization context on affected Laravel endpoints; retain existing SAP parameter names at the integration boundary.
- Validate authenticated customers’ membership server-side. Missing or unauthorized organization context must never return combined business data.
- Send the selected organization for drivers, requests, transfers, business settings, images, and other affected calls. Signup uses an explicitly selected available organization.
- Partition cached business data by customer and organization; partition public company content by organization. Reset visible data when switching companies and discard responses from previously selected companies.
- Correct organization clearing: the current nullable `copyWith` pattern retains the previous selection.
- Reload settings and images after organization selection and use organization-specific image URLs.
- Verify the report’s contract scenario: zero unallocated quantity with an open delivery remains delivery-incomplete. Preserve the existing distinction between contract status and delivery completion; validate the current implementation and regression tests before changing it.

## Migration and rollout

- Add ownership fields and organization-specific configuration storage without deleting historical records.
- Backfill from explicit existing organization fields or reliable linked records. Automatically assign only unambiguous ownership; leave uncertain records in a super-admin review queue.
- Initially copy current business settings and shared image references into each organization to preserve behavior; subsequent edits remain independent.
- Deploy backend support first, then Flutter. Legacy requests may infer organization only for a single eligible membership; ambiguous requests return an actionable organization-selection error.
- Preserve existing unrelated working-tree changes. Do not modify external SAP services as part of this implementation.

## Verification

- Test admins assigned to company A, company B, both, neither, and super admin.
- Verify isolation across lists, direct record access, forms, actions, dashboards, exports, notifications, and related-record selectors.
- Use a customer belonging to two companies to prove transactions remain attached to their originating company.
- Test forged organization values, cross-company related IDs, missing context, and ambiguous historical records.
- Verify company-specific suspension leaves the customer’s other memberships usable.
- Test independent settings, image-code collisions, numbering, organization switching, offline caches, and delayed responses.
- Verify role sales-office permissions round-trip through editing and constrain access.
- Run contract regression scenarios: zero remaining quantity with an open delivery, all deliveries completed, remaining unallocated quantity, and no deliveries.
- Complete focused Laravel and Flutter automated checks plus a manual two-company acceptance pass. Confirm the deployed app version before treating older report findings as unresolved.
