# Jetstream App — Project Master Documentation

**Document version:** 1.0  
**Generated:** June 2026  
**Audience:** Developers, QA, business stakeholders, production handover  
**Scope:** System as implemented today — no speculative features

---

## Table of Contents

1. [Project Overview](#1-project-overview)
2. [User Roles](#2-user-roles)
3. [System Modules](#3-system-modules)
4. [Vehicle Lifecycle](#4-vehicle-lifecycle)
5. [Reservation Workflow](#5-reservation-workflow)
6. [Payment Workflow](#6-payment-workflow)
7. [Invoice Workflow](#7-invoice-workflow)
8. [Remittance Workflow](#8-remittance-workflow)
9. [Auction Intake Workflow](#9-auction-intake-workflow)
10. [Access Control System](#10-access-control-system)
11. [User Override System](#11-user-override-system)
12. [Master Data System](#12-master-data-system)
13. [Exchange Rate System](#13-exchange-rate-system)
14. [User Management](#14-user-management)
15. [Database Overview](#15-database-overview)
16. [Current Business Rules](#16-current-business-rules)
17. [Known Limitations](#17-known-limitations)
18. [Testing Checklist](#18-testing-checklist)

---

# 1. Project Overview

## Project Purpose

**Jetstream App** is a Laravel + Inertia/Vue web platform for **Japanese vehicle export operations**. It supports the full commercial pipeline from auction sourcing through customer purchase, payment collection, invoicing, remittance settlement, and shipment tracking.

The platform serves multiple internal personas (admin, seller, bidder, shipping agent, data entry) and external customers who browse inventory, reserve vehicles, checkout, and pay balances.

## Business Model

| Concept | Description |
|---------|-------------|
| **Sourcing** | Vehicles enter the system via **auction intake** (`auction_cars`) with JPY cost build-up and logistics fields |
| **Inventory** | Sellable **vehicle listings** (`vehicles`) are materialized from auction rows and/or created directly; sellers own assigned stock |
| **Sales** | Sellers and admins **reserve** vehicles for customers, then **checkout** creates orders |
| **Revenue** | Customer payments (wallet, bank transfer, card) settle order balances; sellers earn on assigned sales |
| **Settlement** | **Remittances** credit customer wallets after admin verification; wallet funds allocate to orders via FIFO |
| **Fulfillment** | Orders progress through shipment milestones to delivery/completion |

Commercial activity is **reservation-driven**: stock is held for a customer before checkout converts the hold into a financial order.

## Main Goals

1. **Staged auction intake** — multiple staff roles contribute to one auction record over time (bidder → shipping → data entry enrichment).
2. **Seller-mediated sales** — sellers manage customers, reservations, checkout submissions, and remittances for assigned inventory.
3. **Admin oversight** — full platform control including checkout approval, payment marking, remittance verification, and analytics.
4. **Customer self-service** — public marketplace, CIF checkout, order tracking, payments, and invoice download.
5. **Granular access control (in progress)** — scope-based permissions with role grants and per-user overrides for staff roles.

## Technology Stack

### Backend

| Component | Version / Package |
|-----------|-------------------|
| PHP | ^8.2 |
| Laravel | ^12 |
| Laravel Jetstream | ^5.5 (Inertia stack, teams, profile photos) |
| Laravel Sanctum | ^4.0 (API/session auth) |
| Inertia Laravel | ^2.0 |
| DomPDF / Browsershot | PDF generation (invoices via Chrome headless) |
| Ziggy | Route helpers in JavaScript |

### Frontend

| Component | Version / Package |
|-----------|-------------------|
| Vue | 3 |
| Inertia Vue 3 | ^2.0 |
| Vite | ^7 |
| Tailwind CSS | ^3.4 |
| Bootstrap 5, DataTables, Chart.js | UI/data components |
| Axios | HTTP client |

### Infrastructure Patterns

- **Monolithic Laravel app** with role-prefixed route groups (`/admin`, `/seller`, `/customer`, etc.)
- **Inertia.js** for SPA-like navigation without a separate API for most flows
- **SQLite/MySQL** compatible schema (tests use SQLite in-memory)
- **Exchange rate API** — exchangerate.host for historical USD/JPY backfill

## Architecture Overview

```mermaid
flowchart TB
    subgraph Public
        P[Public Marketplace]
    end

    subgraph Intake
        AC[Auction Cars]
        AVS[AuctionVehicleSyncService]
        V[Vehicles]
    end

    subgraph Commercial
        VR[Vehicle Reservations]
        O[Orders]
        PAY[Payments]
        W[Wallet]
    end

    subgraph Finance
        R[Remittances]
        INV[Invoices]
    end

    subgraph Fulfillment
        SH[Shipments]
    end

    P --> V
    AC --> AVS --> V
    V --> VR --> O --> PAY
    PAY --> W
    R --> W
    PAY --> INV
    O --> SH
```

### Central Orchestration Services

| Service | Responsibility |
|---------|----------------|
| `AuctionVehicleSyncService` | Materialize/update vehicles from auction cars |
| `VehicleReservationService` | Reserve, release, lock vehicles, trigger reservation invoices |
| `CustomerPurchaseService` | Customer CIF checkout and balance payments |
| `CheckoutApprovalService` | Approve/hold/decline seller checkouts; bypass scopes |
| `PaymentService` | Payment ledger, allocations, order status sync |
| `WalletService` | Credit/debit ledger, FIFO remittance allocation |
| `InvoiceService` | PDF generation, storage, email delivery |
| `ExchangeRateService` | Historical rate lookup and API backfill |
| `EffectiveScopeGrantResolver` | Runtime permission resolution |
| `UserAccountStatusService` | Enable/disable users, session invalidation |

### Authentication & Middleware Stack

All authenticated routes use:

```
auth:sanctum → jetstream session → verified → user.active → log.permission
```

Staff routes add `role:{role}` middleware. Seller, bidder, shipping agent, and data entry routes additionally use `scope.{role}` middleware when enforcement is enabled (`PERMISSIONS_ENFORCE_*` env vars).

---

# 2. User Roles

The system uses a **single role per user** (`users.role`). Six roles exist:

| Role | Slug | Route Prefix |
|------|------|--------------|
| Admin | `admin` | `/admin` |
| Seller | `seller` | `/seller` |
| Customer | `customer` | `/customer` |
| Bidder | `bidder` | `/bidder` |
| Shipping Agent | `shipping_agent` | `/shipping-agent` |
| Data Entry | `data_entry` | `/data-entry` |

`GET /dashboard` redirects each role to its dashboard route.

---

## Admin

### Responsibilities

- Full platform operational control across auction intake, vehicles, reservations, orders, payments, invoices, remittances, settings, and user management
- Seller assignment on auction intake rows
- Checkout on behalf of customers (immediate completion)
- Approval/hold/decline of seller-submitted checkouts
- Remittance verification and wallet crediting
- Master data and geographic/shipping configuration
- Access control configuration (role scope grants and user overrides)

### Accessible Modules

| Module | Routes (prefix) |
|--------|-------------------|
| Dashboard | `admin.dashboard` |
| Auction Intake | `admin.auction-cars.*` |
| Vehicles & Documents | `admin.vehicles.*` |
| Reservations | `admin.reservations.*` |
| Checkout Approvals | `admin.checkout-approvals.*` |
| Orders & Shipments | `admin.orders.*` |
| Payments | `admin.payments.*` |
| Remittances | `admin.remittances.*` |
| Invoices | `admin.invoices.*` |
| Users | `admin.users.*` |
| Settings | Countries, regions, ports, auction houses, bank accounts, deposit settings, pricing rules, shipping rates, reservation/invoice settings, access control |
| Master Data | Makes, models, attributes (`admin.makes.*`, `admin.models.*`, `admin.attributes.*`) |

Admin bypasses all scope enforcement middleware and receives all staff scope keys in the permission catalog.

### Approval Powers

- **Checkout approval** — approve, hold, or decline seller checkouts (`pending_approval` / `on_hold` reservations)
- **Payment approval** — mark bank-transfer payments as paid (`admin.payments.mark-paid`)
- **Remittance verification** — verify pending remittances to credit customer wallets; reject or return pool claims

### Financial Powers

- Create manual and bulk-import remittances
- Verify remittances → wallet credit
- Mark payments completed
- Generate partial payment invoices
- View platform-wide payment and remittance analytics
- Admin checkout creates **completed** payments immediately (no approval gate)

### User Management Powers

- Create managed users: customer, seller, bidder, shipping_agent, data_entry (not admin)
- Edit profile fields, optional password reset, seller exchange rate adjustment
- Enable/disable accounts via separate active-status endpoint
- View customer oversight dashboard (orders, wallet, remittances)
- View seller oversight dashboard (sales, remittances, checkout approval queue)
- View lightweight staff profiles (bidder, data_entry, shipping_agent)
- Configure role scope grants and per-user overrides in Access Control settings

---

## Seller

### Responsibilities

- Sales operations for **assigned inventory** and **auction pool** (unlinked auction rows)
- Customer relationship management (create customers, consignees)
- Reservations and checkout submission for own customers
- Remittance submission and pool claiming
- Partial invoice generation for completed payments
- Order and shipment updates for own sales

**Not responsible for:** primary auction intake data entry (bidder/shipping stages), platform settings, admin approval gates, or seller assignment on vehicles.

### Vehicle Management

- CRUD own vehicles (`vehicles.seller_id = auth user`)
- Manage vehicle documents for own listings
- Browse **auction pool** — auction cars without linked vehicles (`seller.auction-pool.index`)
- Cannot assign seller on vehicles (locked scope `vehicles.sections.seller_assignment`)
- Cannot delete vehicles when scope enforcement blocks `vehicles.delete` (not in seller defaults)

### Customer Management

- List/search own customers (`seller.customers.*`)
- Create customer accounts with consignee profiles
- View customer wallet balance and consignees
- Customer linkage via `users.created_by` and order/reservation relationships

### Reservations

- Create reservations for own customers from assigned vehicles or auction pool
- Pricing: `seller_price = base_price + commission_amount` (commission required on seller path)
- Release own reservations (`seller.reservations.destroy`)
- Reservation expiry governed by `reservation_expiry_days` setting (default 3 days)
- Optional wallet debit at reservation time

### Checkout Flow

- **Checkout** (`seller.reservations.checkout`) creates order + **pending** payments
- Sets reservation status to **`pending_approval`**
- Requires **admin approval** before payments complete and reservation converts
- **Bypass:** if user has `checkout.bypass_approval` override, checkout auto-completes (see §11)
- **Pay balance** (`seller.reservations.pay-balance`) after reservation converted; payments pending unless `payments.bypass_approval` override

### Remittances

- Submit remittances (`origin = seller_submitted`) with deposit slip
- Browse and **claim** admin PDF imports from remittance pool
- Delete own **pending** seller-submitted remittances only (strict policy — see §8)
- Cannot verify remittances (admin-only)

### Invoices

- List and download invoices for own sales
- Generate **partial payment** invoices for completed payments (`seller.payments.invoice.generate`)
- Does **not** auto-generate reservation invoices (system does on unified reservation create)

### Orders

- View and update own orders
- Manage shipments on own orders
- Cannot mark orders paid directly (admin scope)

---

## Data Entry

### Responsibilities

- **Stage 3 vehicle catalog enrichment** — detailed specifications, images, attributes, location after intake pipeline
- Read-only auction queue browsing for context
- Master data maintenance (makes, models, attributes) within granted scopes
- **No** commercial workflows: reservations, checkout, payments, remittances, invoices

### Auction Intake Workflow

- **Read-only** auction queue (`data-entry.auction-cars.index`)
- Uses shared `AuctionList.vue` with `AuctionIntakeScope::DataEntry`
- Cannot edit intake rows, upload files, assign sellers, or add rows
- Commercial/cost columns masked in UI

### Vehicle Creation

- Can create vehicles (`data-entry.vehicles.create`) when scope granted
- Create flow uses data-entry vehicle controllers with section grant enforcement

### Vehicle Editing

- Edit enrichment fields only via `UpdateDataEntryVehicleRequest` whitelist
- **Allowed sections:** basic info, specifications, images, location/status (when granted)
- **Blocked sections:** seller assignment, pricing, stock lifecycle (`status`, `stock_status`), auction car linkage changes, lock fields
- Immutable fields on auction-linked vehicles: make, model, chassis when locked

### Master Data Management

- Batch create/update makes, models, attributes, make-attribute mappings
- **Cannot delete** master data rows (data entry batch delete blocked)
- Routes: `data-entry.master-data.*`

---

## Bidder

### Responsibilities

- **Stage 1 auction intake** — identity and base cost capture only
- No commercial, reservation, or financial access

### Auction Intake Editing

- Routes: `bidder.auction-cars.index`, batch update, file upload/delete
- Shared admin auction grid UI with `AuctionIntakeScope::Bidder`

### Editable Sections

**Editable data keys (bidder identity block):**

- Make, model, vehicle type, chassis
- Purchase date, region, city, auction house, destination country
- `auction_price`, `auction_fee`
- Inspection, transportation, and related base cost fields (per scope definition)

**Editable file types:** `as` (auction sheet) only

### Restricted Sections

- Shipping/logistics columns (freight, vessel, sailing date, storage, etc.)
- `selling_price`
- Seller assignment
- Admin control fields
- Cannot add new intake rows (shipping agent and data entry also restricted from row creation in some scopes; bidder **can** add rows)
- Seller exchange rate adjustment **masked** in UI (not visible to bidder)

---

## Shipping Agent

### Responsibilities

- **Stage 2 auction intake** — logistics, shipping costs, and selling price completion
- No commercial, reservation, or financial access

### Shipping Logistics Workflow

- Edit shipping-stage fields on existing auction rows
- Upload shipping document types
- PC preview for shipping documents

### Editable Sections

**Editable data keys (shipping logistics block):**

- Freight, BL, shipper, storage, vessel, sailing date
- `selling_price`
- Shipping-related fees and logistics fields (per `AuctionIntakeScope::ShippingAgent`)

**Editable file types:** `ec`, `coc`, `bl`, `dhl`, `keys`, `pc`, `psi`, `bill`, `inv`

### Restricted Sections

- Bidder identity block (preserved from baseline on save — cannot overwrite make/model/chassis context)
- Cannot add new intake rows
- Cannot assign sellers
- Cannot create reservations or vehicles from intake UI
- No access to reservations, orders, payments, remittances

---

## Customer

### Responsibilities

- Browse public marketplace and purchase/reserve vehicles
- Manage own orders, payments, consignees, remitters, and invoices
- Self-service dashboard for purchase progress

### Public Marketplace

- `GET /` — published vehicle listings with filters (make, price, mileage, year)
- `GET /vehicles/{vehicle}` — detail page for `published` or `reserved` vehicles with `stock_status = available`
- Purchase mode: **direct purchase** when linked auction car is `completed`; otherwise **inquiry only**
- Reserved vehicles show expiry timestamp when applicable

### Orders

- List and view own orders (`customer.orders.*`)
- Order status timeline from reservation through delivery
- Document download when order status permits (delivered/completed)

### Payments

- Pay order balance (`customer.payments.*`, `/orders/{order}/pay`)
- Payment history
- Wallet-funded payments with remittance allocation
- Stripe/card and bank transfer methods supported via `PaymentService`

### Dashboard

- Vehicle purchase summary, payment progress, reserved vehicles
- Wallet balance derived from remittance credits minus allocations

**Customer scope is frozen** — internal access-control refactors must not alter customer-facing behavior.

---

# 3. System Modules

## Dashboard

| Aspect | Detail |
|--------|--------|
| **Purpose** | Role-specific landing pages with summary metrics and quick navigation |
| **Main screens** | `Admin/Dashboard`, `Seller/Dashboard`, `Customer/Dashboard`, `Bidder/Dashboard`, `ShippingAgent/Dashboard`, `DataEntry/Dashboard` |
| **Business rules** | Widget visibility gated by analytics scopes when enforcement enabled; admin sees revenue/profit/inventory aggregates |
| **Dependencies** | Orders, vehicles, auction cars, payments, `AdminDashboardService` |

## Vehicles

| Aspect | Detail |
|--------|--------|
| **Purpose** | Customer-facing and internal vehicle catalog |
| **Main screens** | Admin/Seller/DataEntry vehicle index, create, show, edit; public home and vehicle show |
| **Business rules** | `stock_status`: available/sold; `status`: draft/published/reserved/sold; seller owns assigned stock; lock on reservation |
| **Dependencies** | Makes/models, auction cars, reservations, orders, attributes |

## Vehicle Documents

| Aspect | Detail |
|--------|--------|
| **Purpose** | Per-vehicle document storage and download |
| **Main screens** | `*/Vehicles/Documents/Index.vue` per role |
| **Business rules** | Scope `vehicles.manage_documents`; customer download gated by order status |
| **Dependencies** | Vehicles, orders, storage disk |

## Auction Intake

| Aspect | Detail |
|--------|--------|
| **Purpose** | Auction sourcing queue with staged multi-role data entry |
| **Main screens** | `Admin/DataEntry/AuctionList.vue` (shared across admin, bidder, shipping, data entry), create/edit pages |
| **Business rules** | `AuctionIntakeScope` controls editable fields/files per role; admin assigns seller; auto-syncs minimal vehicle |
| **Dependencies** | Auction houses, regions, countries, exchange rates, vehicles |

## Reservations

| Aspect | Detail |
|--------|--------|
| **Purpose** | Hold stock for a customer before checkout |
| **Main screens** | `Admin/Reservations/Index`, `Seller/Reservations/Index` |
| **Business rules** | Statuses: active, expired, converted, pending_approval, on_hold; expiry from settings; wallet debit optional |
| **Dependencies** | Vehicles, auction cars, wallet, invoices, orders |

## Orders

| Aspect | Detail |
|--------|--------|
| **Purpose** | Purchase contract after checkout |
| **Main screens** | Admin/Seller/Customer order index and show |
| **Business rules** | Status flow through payment and shipment milestones; CIF fields; consignee/remitter snapshots |
| **Dependencies** | Reservations, payments, shipments, vehicles, sellers |

## Customers

| Aspect | Detail |
|--------|--------|
| **Purpose** | Seller-managed customer accounts; admin customer oversight |
| **Main screens** | `Seller/Customers/*`, `Admin/Users/Show` (customer view) |
| **Business rules** | Seller customers linked via `created_by`, orders, reservations; consignee management |
| **Dependencies** | Users, consignees, remitters, wallet |

## Invoices

| Aspect | Detail |
|--------|--------|
| **Purpose** | PDF invoices for reservations and partial payments |
| **Main screens** | Admin/Seller/Customer invoice index |
| **Business rules** | Reservation auto-generated on unified reserve; partial payment manual generation |
| **Dependencies** | Reservations, payments, orders, settings (company/bank details), Browsershot |

## Remittances

| Aspect | Detail |
|--------|--------|
| **Purpose** | Track funds received from customers; credit wallets after verification |
| **Main screens** | `Admin/Remittances/Index`, `Seller/Remittances/Index` (submit + pool tabs) |
| **Business rules** | Statuses: pending, verified, rejected; origins: admin_pdf, admin_manual, seller_submitted; claim pool workflow |
| **Dependencies** | Wallet, users, remittance allocations |

## Payments

| Aspect | Detail |
|--------|--------|
| **Purpose** | Order payment ledger |
| **Main screens** | `Admin/Payments/Index`, `Customer/Payments/Show` |
| **Business rules** | Statuses: pending, completed, failed, refunded; admin marks bank transfers paid |
| **Dependencies** | Orders, wallet, remittance allocations, invoices |

## Wallet

| Aspect | Detail |
|--------|--------|
| **Purpose** | Customer prepaid balance from verified remittances |
| **Main screens** | Embedded in customer dashboard, admin customer show, checkout flows |
| **Business rules** | FIFO allocation from remittance credits; debits for reservations and checkout |
| **Dependencies** | Wallet transactions, remittances, payments |

## Users

| Aspect | Detail |
|--------|--------|
| **Purpose** | Admin management of non-admin accounts |
| **Main screens** | `Admin/Users/Index`, `Create`, `Edit`, `Show`, `ShowSeller`, `ShowStaff` |
| **Business rules** | Admin users excluded from list; role read-only on edit; active/disable via separate endpoint |
| **Dependencies** | Teams (Jetstream), user account status service |

## Access Control

| Aspect | Detail |
|--------|--------|
| **Purpose** | Configure role scope grants and inspect effective permissions |
| **Main screens** | `Admin/Settings/AccessControl.vue` |
| **Business rules** | Locked scopes cannot be granted; user-override-only scopes excluded from role templates |
| **Dependencies** | `role_scope_grants`, `user_scope_overrides`, config catalog |

## User Overrides

| Aspect | Detail |
|--------|--------|
| **Purpose** | Per-user grant/revoke on top of role template |
| **Main screens** | Tab within Access Control settings |
| **Business rules** | Only seller, bidder, shipping_agent, data_entry; requires reason for bypass scopes |
| **Dependencies** | `EffectiveScopeGrantResolver`, audit tables |

## Master Data

| Aspect | Detail |
|--------|--------|
| **Purpose** | Vehicle catalog hierarchy and dynamic attributes |
| **Main screens** | `Admin/Configuration/MasterData.vue`, `DataEntry/MasterData/Index` |
| **Business rules** | Batch save pattern; data entry cannot delete rows |
| **Dependencies** | Makes, models, attributes, attribute values, make/model attribute mappings |

## Settings

| Aspect | Detail |
|--------|--------|
| **Purpose** | Platform configuration |
| **Main screens** | Countries, regions, ports, auction houses, bank accounts, deposit settings, pricing rules, shipping rates, reservation settings, invoice settings |
| **Business rules** | Admin-only; settings scopes locked from role grant editing |
| **Dependencies** | Settings table, related geographic/finance models |

## Exchange Rates

| Aspect | Detail |
|--------|--------|
| **Purpose** | USD/JPY daily rates for auction cost conversion |
| **Main screens** | Used in auction intake table (no dedicated settings page) |
| **Business rules** | Lookup by `purchase_date`; API backfill; seller adjustment on admin display only |
| **Dependencies** | `exchange_rates` table, `ExchangeRateService`, exchangerate.host API |

---

# 4. Vehicle Lifecycle

## Overview Diagram

```mermaid
stateDiagram-v2
    [*] --> AuctionPending: Auction intake created
    AuctionPending --> AuctionCompleted: selling_price set + vehicle linked
    AuctionCompleted --> VehicleDraft: Seller assigned / materialized
    VehicleDraft --> VehiclePublished: Published for sale
    VehiclePublished --> VehicleReserved: Reservation created
    VehicleReserved --> OrderReserved: Checkout creates order
    OrderReserved --> PaymentPending: Partial payment
    PaymentPending --> Paid: Balance zero
    Paid --> Sold: stock_status sold
    Sold --> Shipped: Shipment milestones
    Shipped --> Completed: Delivered / completed
    VehicleReserved --> VehicleDraft: Release (no order)
```

## Stage Details

### 1. Auction Intake

| Field | Value |
|-------|-------|
| **Entity** | `auction_cars` |
| **DB status** | `pending` (default), `completed` |
| **Derived completed** | Linked vehicle exists AND `selling_price` is set |
| **Operational UI status** | `available`, `reserved`, `in_process`, `sold` (derived from reservations/orders/payments) |

Cost build-up sums JPY fields (auction price, fees, freight, storage, etc.) with 10% surcharge on selected fee components.

### 2. Vehicle Creation

| Trigger | Result |
|---------|--------|
| Admin saves auction car | `AuctionVehicleSyncService::syncMinimalVehicle()` |
| Admin assigns seller | Creates draft vehicle with `seller_id`, `status=draft`, `stock_status=available` |
| Reservation on auction-only row | `ensureForReservation()` materializes vehicle with lock |

**Vehicle statuses:** `draft`, `published`, `reserved`, `sold`  
**Stock statuses:** `available`, `sold`

### 3. Seller Assignment

- Admin-only (`assignSeller` / `auction_intake.assign_seller` — locked scope)
- Sets `vehicles.seller_id`; does not auto-publish

### 4. Reservation

- Locks vehicle: `status=reserved`, `is_locked=true`
- Reservation statuses — see §5

### 5. Checkout → Order

- Creates `orders` row linked to reservation
- Order purchase status flow:

```
reserved → payment_pending → paid → shipment_pending → shipped → in_transit → arrived → delivered → completed
```

Also supports `cancelled`.

### 6. Payments

- Multiple partial payments allowed
- First completed payment converts reservation to `converted`
- Full payment sets vehicle `stock_status=sold`, `status=sold`

### 7. Sold

- Vehicle marked sold when order fully paid
- Documents may become downloadable for customer

## Entity Relationship

```mermaid
erDiagram
    AUCTION_CARS ||--o| VEHICLES : links
    VEHICLES ||--o{ VEHICLE_RESERVATIONS : has
    VEHICLE_RESERVATIONS ||--o| ORDERS : converts
    ORDERS ||--o{ PAYMENTS : has
    ORDERS ||--o{ SHIPMENTS : has
    USERS ||--o{ VEHICLES : sells
    USERS ||--o{ ORDERS : buys
```

---

# 5. Reservation Workflow

## Status Reference

| Status | Meaning |
|--------|---------|
| `active` | Stock held for customer; not yet checked out |
| `expired` | Hold released or TTL passed |
| `converted` | Checkout completed; order exists |
| `pending_approval` | Seller checkout submitted; awaiting admin |
| `on_hold` | Admin placed checkout on hold |

## Create Reservation

### Unified path (`VehicleReservationService::reserveForCustomer`)

Used by seller and admin controllers.

1. Validate vehicle or auction car target
2. Materialize vehicle from auction if needed
3. Calculate `seller_price` (base + commission on seller path)
4. Block duplicate active/converted/pending_approval reservations
5. Lock vehicle
6. Optional wallet debit
7. **After commit:** auto-create reservation invoice via `InvoiceService::createAndSendReservationInvoice`

### Legacy path (`VehicleReservationService::reserve`)

Auction-only, seller-initiated; optional purchase-history gate; does **not** auto-generate invoice.

### Settings

| Key | Default | Effect |
|-----|---------|--------|
| `reservation_expiry_days` | 3 | TTL for active reservations |
| `reservation_require_purchase_history` | — | Gate for legacy reserve |
| `reservation_min_purchases` | — | Minimum completed purchases |

## Release Reservation

**Endpoint:** seller destroy, admin destroy → `VehicleReservationService::release()`

1. Set reservation `expired`, clear `expires_at`
2. Unlock vehicle
3. If **not converted** and **no order**: clear `seller_id`, set `status=draft`, `is_ready_for_sale=false`
4. If converted/order exists: set vehicle `status=available` instead
5. **No wallet refund** of `wallet_amount_used`

## Checkout Approval

### Seller checkout (default)

1. Seller submits checkout → order created, payments **pending**
2. Reservation → `pending_approval`
3. Admin reviews in Checkout Approvals

| Admin action | Result |
|--------------|--------|
| **Approve** | Complete pending payments; reservation → `converted` |
| **Hold** | `pending_approval` → `on_hold` |
| **Decline** | Fail payments; reverse wallet debits; soft-delete order; reservation → `active` with renewed expiry |

### Admin checkout

- Payments created **completed** immediately
- Reservation → `converted` without approval gate

## Checkout Bypass Approval

When seller has user override `checkout.bypass_approval`:

1. Normal checkout flow creates pending payments
2. `CheckoutApprovalService::approvePendingCheckoutWithBypass()` auto-completes
3. Reservation → `converted` immediately
4. Audit note appended to order; scope key logged

**Requires** base scope `reservations.checkout` — bypass alone is insufficient.

## Converted Reservations

- Reservation `status=converted` with linked `order_id`
- Vehicle remains locked through checkout
- Seller/admin can submit **pay balance** for remaining order balance
- First completed payment may have already converted reservation during partial payment flow

## Expiry Command

`php artisan reservations:expire` marks overdue `active` reservations as `expired` but **does not** call `release()` — vehicle lock/seller cleanup may not run on cron expiry alone.

---

# 6. Payment Workflow

## Payment Statuses

| Status | Meaning |
|--------|---------|
| `pending` | Awaiting completion/approval |
| `completed` | Funds applied to order |
| `failed` | Declined or reversed |
| `refunded` | Refund recorded |

## Initial Payments

Created at checkout:

| Actor | Initial payment status |
|-------|------------------------|
| Admin checkout | `completed` |
| Seller checkout | `pending` (until approval or bypass) |
| Customer checkout | Per gateway / wallet flow |

Seller checkout sets `allocate_on_create=false` until approval.

## Partial Payments

- Multiple `completed` payments allowed per order
- Order stays `payment_pending` until `remaining_balance <= 0`
- `PaymentService::syncOrderPaymentTotals()` recalculates after each completion

## Balance Payments

- **Admin pay balance:** payments created `completed`
- **Seller pay balance:** payments `pending` unless `payments.bypass_approval` override
- Routes: `admin.reservations.pay-balance`, `seller.reservations.pay-balance`

## Admin Approval

`Admin\PaymentController::markPaid`:

1. Sets `approved_by`
2. Calls `PaymentService::updatePaymentStatus(..., 'completed')`
3. Triggers allocations and order status sync

## Payment Bypass Approval

When seller has `payments.bypass_approval` override on pay balance:

- `CheckoutApprovalService::completePaymentsWithBypass()` marks pending payments completed
- Audit metadata stored in `payment_data`

## Wallet Allocation

### Credit sources

- `remittance` — verified remittance wallet credit
- `manual` — admin manual credit

### Debit sources

- `reservation` — reservation wallet hold
- `checkout` — checkout wallet payment
- `manual` — admin manual debit

### FIFO allocation

On wallet debit, `WalletService` consumes oldest remittance credits first (by `received_date`), creating `remittance_allocations` rows linking credit/debit/remittance/order.

On payment completion, `PaymentService::attachRemittanceAllocationsToPayment` links allocations via `wallet_transaction_id` in `payment_data`.

## Order Status Sync Rules

From `PaymentService::syncOrderPaymentTotals`:

| Condition | Order status |
|-----------|--------------|
| `remaining_balance <= 0` | `paid` |
| `paid_amount > 0` | `payment_pending` |
| No payments | `reserved` |

First completed payment converts linked reservation to `converted` and assigns vehicle `seller_id` from reservation.

---

# 7. Invoice Workflow

## Invoice Types

| Type | Constant | When created |
|------|----------|--------------|
| Reservation | `reservation` | Auto after unified `reserveForCustomer` (idempotent) |
| Partial payment | `partial_payment` | Manual admin/seller action on completed payment |

## Reservation Invoice

**Trigger:** `InvoiceService::createAndSendReservationInvoice` (after DB commit on reservation create)

**Number format:** zero-padded reservation ID (6 digits)

**Not created** on admin/seller checkout payment completion.

## Partial Payment Invoice

**Trigger:**

- `Admin\InvoiceController::generateForPayment`
- `Seller\InvoiceController` equivalent
- Requires payment `status = completed`

**Number format:** `KT-{n}` sequential from max existing KT number, minimum `110001`

## Invoice Generation Process

1. Build context: company/banking from `Setting` keys, consignee, vehicle, transport defaults
2. Render `resources/views/invoices/pdf.blade.php` via **Browsershot** (headless Chrome)
3. Store PDF at `invoices/Y/m/{invoice_number}.pdf` on `local` disk
4. Email customer via `CustomerInvoiceMail` if SMTP configured (`MailSettingsService`)
5. Set `email_sent_at` on success; skip gracefully if no email/SMTP

## Email Delivery

- Reservation invoices emailed automatically when SMTP is configured
- Failures logged; generation still persists PDF

## PDF Generation / Download

- `InvoiceService::downloadResponse` regenerates PDF from stored metadata before download
- Customer, seller, and admin can download per role routes

## Settings Dependencies

| Setting keys | Purpose |
|--------------|---------|
| `invoice_bill_from_*` | Company header on invoice |
| `invoice_bank_*` | Banking details |
| `invoice_transport_from` | Transport line item default |

---

# 8. Remittance Workflow

## Status & Origin Reference

| Status | Meaning |
|--------|---------|
| `pending` | Awaiting verification |
| `verified` | Admin verified; wallet credited |
| `rejected` | Rejected or returned to pool |

| Origin | Created by |
|--------|------------|
| `seller_submitted` | Seller remittance form |
| `admin_manual` | Admin manual entry |
| `admin_pdf` | Admin bulk PDF import |

## Seller Submitted Remittances

**Route:** `seller.remittances.store`

**Required:** company name, customer name, conversion, average rate, received date, received by seller, deposit slip

**Optional:** linked `customer_id` (must be seller-linked customer)

**Result:** `origin=seller_submitted`, `status=pending`

## Admin Imported Remittances

### Manual create

- Deposit slip required; optional seller/customer
- `origin=admin_manual`
- Optional `verify_on_create` → immediate verify + wallet credit

### Bulk PDF import

- Multiple PDFs + extracted metadata JSON
- `origin=admin_pdf`, `is_complete=false`, no seller/customer
- Enters **claim pool** for sellers

## Claim Process (Seller)

**Route:** `seller.remittances.claim`

1. Remittance must match `availableForClaim()` scope (admin PDF, unclaimed)
2. Seller provides customer (seller-linked), `received_by_seller`, claim slip
3. Sets seller/customer, `claimed_at`, recomputes `is_complete`
4. Stays **pending** until admin verifies

## Verification Process (Admin)

**Route:** `admin.remittances.verify`

**Requires:**

- `is_complete` (seller_id + customer_id + received_by_seller)
- `status=pending`
- Positive USD amount

**Actions:**

1. Set `verified`, `verified_at`, `verified_by`
2. Credit customer wallet: `WalletService::credit(source=remittance, reference_id=remittance.id)`
3. Idempotent — skips if credit already exists

## Reject Process

| Case | Result |
|------|--------|
| Normal pending remittance | `status=rejected` |
| Pending claim on admin PDF | Return to pool — clear seller, customer, claim slip, `claimed_at`; `is_complete=false` |

## Wallet Impact

Verified remittance → `wallet_transactions` credit → available for checkout/reservation debits → FIFO allocation on use → `remittance_allocations` link to payments

## Seller Delete-Own-Pending Rule

**Policy:** `SellerRemittanceDeletePolicy`

Deletion allowed only when **all** true:

| Rule | Check |
|------|-------|
| Ownership | `seller_id` matches requesting seller |
| Origin | `origin === seller_submitted` |
| Not claimed | `claimed_at` is null |
| Status | `status === pending` |
| Not verified | `verified_at` and `verified_by` null |
| No wallet credit | No wallet transaction for this remittance |
| No allocations | No `remittance_allocations` rows |

Admin-imported, verified, or claimed remittances **cannot** be seller-deleted.

---

# 9. Auction Intake Workflow

## Auction Creation

**Controller:** `Admin\AuctionCarController`

- Validated via `StoreAuctionCarRequest` / `UpdateAuctionCarRequest`
- On store/update: syncs minimal vehicle via `AuctionVehicleSyncService`
- Supports batch save, BL/auction file uploads
- Bidder can create rows; shipping agent and data entry cannot add rows

## Seller Assignment

- Admin-only: `assignSeller()` → `syncMinimalVehicle($auctionCar, $sellerId, assignSellerId=true)`
- Creates/updates linked vehicle with `seller_id`, `status=draft`, `stock_status=available`

## Vehicle Materialization

| Method | When | Result |
|--------|------|--------|
| `syncMinimalVehicle` | Auction save / seller assign | Draft vehicle when seller present |
| `ensureForReservation` | Reservation on auction row | Locked reserved vehicle with seller and price |

## Exchange Rate Lookup

- `AuctionCar` accessor `exchange_rate` looks up `exchange_rates.rate` where `date = purchase_date`
- `AuctionCarController` preloads rates via `ExchangeRateService::preloadRatesForDates()`
- No `exchange_rate` column stored on auction car — computed at read time

## Historical Exchange Rate Fetch

`ExchangeRateService::getRateByDate()`:

1. Memory cache
2. Database lookup
3. HTTP GET to exchangerate.host `/historical` (USD→JPY)
4. `insertOrIgnore` — does not overwrite existing DB rate

## Seller Exchange Rate Adjustment

| Aspect | Detail |
|--------|--------|
| Storage | `users.exchange_rate_adjustment` decimal(5,1), range -999.9 to 999.9 |
| Admin management | User create/edit, seller settings PATCH, seller show |
| Display | Admin auction intake table only |
| Bidder view | Adjustment masked |

## Buying Cost Calculation

### Model accessor (`AuctionCar::usd_cost`)

```
usd_cost = total / divisor
divisor = (purchase_date < 2026-05-06) ? (rate - 3) : rate
```

**Does not** include seller adjustment.

### Admin intake table display (frontend)

```
effective_rate = exchange_rate + seller.exchange_rate_adjustment  (when seller assigned)
buying_cost = total / effective_rate
```

### Profit

```
profit = selling_price - usd_cost  (model accessor uses raw rate divisor)
```

## Cost Total (JPY)

`AuctionCar::calculateTotal()` sums:

- auction_price, auction_fee, inspection, transportation, h_charge, van, insurance, freight, bl, extra, shipper, storage
- Plus 10% surcharge on (auction_fee + shipper + transportation + storage + extra)
- Storage may be auto-calculated from auction house storage pricing unless manual override

---

# 10. Access Control System

## Architecture

```mermaid
flowchart TB
    subgraph Config
        PC[config/permissions.php]
        SM[config/scope_manifest.php]
    end

    subgraph Database
        RSG[role_scope_grants]
        USO[user_scope_overrides]
    end

    subgraph Runtime
        ESR[EffectiveScopeGrantResolver]
        SRE[ScopeRouteEnforcer]
        AIS[AccessControlInspectorService]
    end

    PC --> ESR
    RSG --> ESR
    USO --> ESR
    ESR --> SRE
    ESR --> AIS
```

## Role Grants

- Stored in `role_scope_grants` table; seeded from `config/permissions.php` on migrate
- Admin UI: Access Control settings → Role Grants tab
- Service: `RoleScopeGrantService::saveRoleGrants()`, `resetRoleToConfigDefaults()`
- Audited in `role_scope_grant_audits`

### Default grant summary

| Role | Key capabilities |
|------|------------------|
| **admin** | All staff scope keys |
| **seller** | Vehicles (no delete), reservations (no approve_checkout), orders (no mark_paid), remittances (submit/claim, no verify), customers (no wallet_credit), invoices, analytics |
| **bidder** | Auction intake view/update/create_row, bidder_identity section, files, dashboard |
| **shipping_agent** | Auction intake view/update, shipping_logistics section, files, dashboard |
| **data_entry** | Vehicles CRUD (section grants), auction intake view (read-only), master data, dashboard |
| **customer** | `customer_self_service.*` scopes (documentation only for staff system) |

## User Overrides

See §11.

## Scope Enforcement

### Middleware (feature-flagged)

| Env variable | Middleware | Routes |
|--------------|------------|--------|
| `PERMISSIONS_ENFORCE_DATA_ENTRY` | `scope.data_entry` | `data-entry.*` |
| `PERMISSIONS_ENFORCE_BIDDER` | `scope.bidder` | `bidder.*` |
| `PERMISSIONS_ENFORCE_SHIPPING_AGENT` | `scope.shipping_agent` | `shipping-agent.*` |
| `PERMISSIONS_ENFORCE_SELLER` | `scope.seller` | `seller.*` (safe routes only) |

**Admin bypasses** all scope checks.

**Intentionally omitted** from seller enforcement: delete, remittance verify/reject, mark paid, wallet credit.

### Other enforcement surfaces

- Inertia shared props (`HandleInertiaRequests`) — scope booleans for UI gating
- Form request traits — vehicle section grants
- `AuctionIntakeBatchGrantEnforcer` — batch intake saves
- `DashboardWidgetGrantPolicy`, `VehicleCatalogGrantPolicy`

## Locked Scopes

Cannot be saved to role grants or user overrides (`RoleScopePolicy::LOCKED_SCOPES`):

| Scope | Category |
|-------|----------|
| `remittances.verify` | Financial |
| `payments.mark_paid` | Financial |
| `orders.mark_paid` | Financial |
| `customers.wallet_credit` | Financial |
| `auction_intake.assign_seller` | Lifecycle |
| `vehicles.sections.seller_assignment` | Lifecycle |
| `users_access.manage_scopes` | Privilege |
| `users_access.assign_roles` | Privilege |

Also locked: any `settings.*`, any `customer_self_service.*`, all `future_actions`.

## Implemented Scopes

All keys in `config/permissions.php` → `all_staff_scope_keys`, organized by module:

- `auction_intake.*` — view, create, update, assign_seller, files, export, search, sections
- `vehicles.*` — view, create, update, delete, documents, export, search, sections
- `vehicle_master_data.*` — makes, models, attributes, geo (admin)
- `reservations.*` — view, create, release, checkout, approve_checkout, pay_balance
- `orders.*` — view, update, shipments, mark_paid, export
- `payments.*` — view, mark_paid
- `invoices.*` — view, download, generate_partial
- `remittances.*` — full lifecycle scopes
- `customers.*` — view, create, search, consignees, wallet
- `analytics.*` — per-role dashboard scopes
- `users_access.*` — manage_users

## Export Permissions

Explicit export scopes (not in non-admin defaults unless granted):

- `vehicles.export`
- `auction_intake.export`, `auction_intake.export_pool`
- `orders.export`, `reservations.export`, `customers.export`, `remittances.export`

## Search Permissions

- `vehicles.search`
- `auction_intake.table_search`, `auction_intake.advanced_search`
- `reservations.search`, `orders.search`, `remittances.search`, `customers.search`
- `vehicle_master_data.search`

## Analytics Permissions

Per-role dashboard scopes, e.g.:

- `analytics.dashboard.view` (admin)
- `analytics.seller_dashboard.view` + sub-stats (seller)
- `analytics.bidder_dashboard.view` (bidder)
- `analytics.shipping_dashboard.view` (shipping agent)
- `analytics.data_entry_dashboard.view` (data entry)

## Section Permissions

### Auction intake sections

- `auction_intake.sections.bidder_identity`
- `auction_intake.sections.shipping_logistics`
- `auction_intake.sections.admin_control`

### Vehicle sections

- `vehicles.sections.basic_info`
- `vehicles.sections.specifications`
- `vehicles.sections.images`
- `vehicles.sections.seller_assignment` (locked)
- `vehicles.sections.pricing`
- `vehicles.sections.location_status`

---

# 11. User Override System

## Effects

| Effect | Behavior |
|--------|----------|
| **grant** | Adds scope on top of role grants |
| **revoke** | Removes scope even if role grants it |
| **inherit** | Deletes override row; falls back to role grants |

## Precedence

```mermaid
flowchart LR
    RG[Role Grant Template] --> BASE[Base Grants]
    BASE --> UO{User Override?}
    UO -->|grant| ADD[Scope granted]
    UO -->|revoke| REM[Scope denied]
    UO -->|inherit / none| BASE
    ADD --> EFFECTIVE[Effective Permission]
    REM --> EFFECTIVE
    BASE --> EFFECTIVE
```

**Resolution order** (`EffectiveScopeGrantResolver`):

1. **Admin** → always granted
2. **Customer** → config grants only; no overrides
3. **Editable staff** (seller, bidder, shipping_agent, data_entry):
   - Base: DB `role_scope_grants` if rows exist, else config defaults
   - Apply user overrides (grant/revoke)
   - `inherit` = remove override row

## Editable Roles

`RoleScopePolicy::EDITABLE_ROLES` = seller, bidder, shipping_agent, data_entry

Admin and customer users **cannot** receive overrides.

## User-Override-Only Scopes

Cannot be assigned in role templates — **per-user only**:

### `checkout.bypass_approval`

| Aspect | Detail |
|--------|--------|
| **Role** | Seller only |
| **Requires** | `reservations.checkout` base scope |
| **Enforced in** | `Seller\SellerCheckoutController::checkout()` |
| **Effect** | Auto-completes pending checkout payments; reservation → `converted` |
| **Audit** | Order note + log entry with scope key |

### `payments.bypass_approval`

| Aspect | Detail |
|--------|--------|
| **Role** | Seller only |
| **Requires** | `reservations.pay_balance` base scope |
| **Enforced in** | `Seller\SellerCheckoutController::payBalance()` |
| **Effect** | Auto-completes pending balance payments |
| **Audit** | Payment data metadata + log entry |

Granting override-only scopes requires a **reason** in Access Control UI.

---

# 12. Master Data System

## Makes

- **Table:** `makes`
- **Model:** `App\Models\Make`
- **Admin:** full CRUD + batch via `MakeController`
- **Data entry:** batch create/update only (no delete)
- **Scope:** `vehicle_master_data.makes_models.manage`

## Models

- **Table:** `models` (vehicle models, not Laravel models)
- **Model:** `App\Models\VehicleModel`
- **Linked to:** make (parent)
- **Admin:** CRUD + batch; model edit includes attribute assignment
- **Data entry:** batch + model attribute update routes

## Attributes

- **Table:** `attributes`
- **Model:** `App\Models\VehicleAttribute`
- **Types:** `select`, `multi_select`, `range`, `boolean`
- **Admin:** full CRUD via `AttributeController`

## Attribute Values

- **Table:** `attribute_values`
- **Model:** `App\Models\AttributeValue`
- **Admin:** nested under attributes via `AttributeValueController`

## Make Mappings

- **Table:** `make_attributes`
- **Model:** `App\Models\MakeAttribute`
- **Purpose:** Default attribute constraints per make
- **Admin:** batch via `MakeAttributeController` (`admin.make-attributes.batch`)

## Model Mappings

- **Table:** `model_attributes`
- **Model:** `App\Models\ModelAttribute`
- **Purpose:** Attribute assignment and defaults per vehicle model
- **Managed via:** model edit UI and batch routes

## Vehicle Instance Values

- **Table:** `vehicle_attribute_values`
- **Model:** `App\Models\VehicleAttributeValue`
- **Purpose:** Actual spec values on a vehicle record

## Public Catalog API

Unauthenticated helpers for forms and public site:

- `vehicle-catalog.makes.models`
- `vehicle-catalog.models.attributes`
- `vehicle-catalog.makes.attributes`
- `/api/models`, `/api/regions`, `/api/countries` (in `routes/api.php`)

---

# 13. Exchange Rate System

## Exchange Rate Storage

| Column | Type | Notes |
|--------|------|-------|
| `date` | date, unique | Purchase date key |
| `rate` | decimal(10,6) | USD/JPY rate |

**Table:** `exchange_rates`  
**Model:** `App\Models\ExchangeRate`

## Historical Lookup

`ExchangeRateService::getRateByDate($date)`:

1. In-memory cache (per request lifecycle)
2. Database row for date
3. API fetch if missing
4. `insertOrIgnore` — never overwrites existing row

Bulk: `preloadRatesForDates()` for auction intake listings.

## API Integration

| Setting | Location |
|---------|----------|
| `access_key` | `config/services.php` → `exchangerate_host` |
| `base_url` | exchangerate.host API |
| Params | `source=USD`, `currencies=JPY`, historical date |

Parses `quotes.USDJPY` or `rates.JPY` from response.

## Purchase Date Matching

- `AuctionCar.purchase_date` → lookup `exchange_rates.date`
- Accessor computed at serialization time (not stored on auction car)

## Seller Adjustment Logic

| Context | Formula |
|---------|---------|
| Model `usd_cost` | `total / divisor` (divisor = rate or rate-3 by date threshold) |
| Admin intake UI | `total / (exchange_rate + seller.adjustment)` when seller assigned |
| Bidder view | Adjustment hidden |
| Seller settings | `-999.9` to `999.9`, one decimal |

**Important:** Seller adjustment affects **admin intake display only**, not `AuctionCar::usd_cost`, dashboard profit aggregates, invoices, or customer pricing.

---

# 14. User Management

## User Creation

**Route:** `POST admin/users` (`admin.users.store`)  
**Request:** `StoreManagedUserRequest`

| Field | Rule |
|-------|------|
| name | required |
| email | required, unique |
| phone, address, country | optional |
| role | customer, seller, bidder, shipping_agent, data_entry |
| password | required, confirmed, Fortify rules |
| exchange_rate_adjustment | seller only, -999.9 to 999.9 |

**Side effects:**

- Sets `created_by` to creating admin
- Creates personal Jetstream team
- Does not explicitly set `is_active` (defaults true via DB)

**Admin role** cannot be created through this UI.

## User Editing

**Routes:** `GET admin/users/{user}/edit`, `PUT admin/users/{user}`  
**Request:** `UpdateManagedUserRequest`

| Field | Editable |
|-------|----------|
| name, email, phone, address, country | yes |
| password | optional (only updated if provided) |
| exchange_rate_adjustment | seller only |
| role | **prohibited** (read-only) |
| is_active | **prohibited** (separate endpoint) |

**Admin users** receive 404 on edit/update routes.

## User Activation / Disabling

**Route:** `PATCH admin/users/{user}/active-status`  
**Service:** `UserAccountStatusService`

| Action | Fields set |
|--------|------------|
| Disable | `is_active=false`, `disabled_at`, `disabled_by`; deletes sessions and API tokens |
| Enable | clears disabled fields |

**Guards:**

- Cannot disable own account
- Cannot disable admin users
- Non-admin callers receive 403/404

## Login Restrictions

| Layer | Behavior |
|-------|----------|
| Fortify `authenticateUsing` | Blocks login when `!$user->isActive()` |
| `EnsureUserIsActive` middleware | Logs out disabled user on next request; flash message |
| `User::isActive()` | Treats null `is_active` as true |

## Managed User Views

| Role | View page |
|------|-----------|
| customer | `Admin/Users/Show` (full oversight) |
| seller | `Admin/Users/ShowSeller` (sales dashboard) |
| bidder, data_entry, shipping_agent | `Admin/Users/ShowStaff` (lightweight profile) |

## Users Index Features

- Status column: Active / Disabled
- Role badges and filters for all managed roles
- View, Edit, Enable/Disable actions
- Customer wallet summary columns

---

# 15. Database Overview

Business purpose of important tables (not full schemas):

| Table | Business Purpose |
|-------|------------------|
| `users` | All personas; role, seller adjustment, created_by, active status |
| `teams`, `team_user` | Jetstream team membership |
| `vehicles` | Sale listings; seller, pricing, status, lock, auction link |
| `vehicle_images` | Listing photos |
| `vehicle_documents` | Per-vehicle document storage |
| `vehicle_attribute_values` | Dynamic spec values on vehicles |
| `makes`, `models` | Vehicle catalog hierarchy |
| `attributes`, `attribute_values` | Spec field definitions and options |
| `model_attributes`, `make_attributes` | Model/make attribute mappings |
| `auction_cars` | Auction intake queue; costs, logistics, selling price |
| `auction_houses` | Auction source reference and fee defaults |
| `vehicle_reservations` | Customer holds; checkout lifecycle |
| `orders` | Purchase contracts; CIF, consignee, financial snapshots |
| `order_items` | Line items per order |
| `payments` | Payment ledger and approval audit |
| `payment_allocations` | Payment-to-vehicle amount tracking |
| `payment_histories` | Payment audit trail |
| `wallet_transactions` | Customer wallet credits and debits |
| `invoices` | Generated invoice records and PDF paths |
| `shipments` | Order shipping lifecycle and dates |
| `remittances` | Funds received; claim and verification workflow |
| `remittance_allocations` | Wallet credit consumption per payment |
| `consignees`, `remitters` | Shipping and payment party profiles |
| `countries`, `regions`, `ports` | Geographic master data |
| `shipping_countries`, `shipping_rates` | Shipping pricing configuration |
| `bank_accounts`, `deposit_settings` | Payment and deposit configuration |
| `pricing_rules` | Platform pricing rules |
| `settings` | Key-value platform settings |
| `exchange_rates` | Daily USD/JPY rates by date |
| `role_scope_grants` | Persisted per-role permission templates |
| `role_scope_grant_audits` | Role grant change history |
| `user_scope_overrides` | Per-user grant/revoke overrides |
| `user_scope_override_audits` | Override change history |
| `personal_access_tokens` | Sanctum API tokens |
| `cache`, `jobs` | Laravel infrastructure |

---

# 16. Current Business Rules

## Access & Authorization

1. Single role per user; `RoleMiddleware` requires exact role match.
2. Admin bypasses all scope enforcement.
3. Customer cannot receive user overrides; customer scopes are frozen.
4. Locked scopes cannot be granted via role templates or user overrides.
5. User-override-only scopes (`checkout.bypass_approval`, `payments.bypass_approval`) require reason and seller target.
6. Bypass scopes require base scopes (`reservations.checkout`, `reservations.pay_balance`).
7. Seller cannot approve own checkout without admin or bypass override.
8. Data entry cannot delete master data batch rows.
9. Seller cannot assign seller on vehicles (locked section scope).

## User Account

10. Disabled users cannot log in (Fortify) and are logged out on next request.
11. Admin cannot disable own account or other admin accounts via active-status endpoint.
12. Admin cannot edit admin users via managed edit/update routes (404).
13. Role cannot be changed via update request tampering (`prohibited` validation).
14. Password updated only when provided on edit.
15. `is_active` cannot be changed via profile update (separate toggle endpoint).

## Auction & Vehicles

16. Auction car `completed` when linked vehicle exists and `selling_price` set.
17. Seller assignment is admin-only.
18. Vehicle materialization for seller assignment requires make, model, chassis.
19. Public listings show only `published` or `reserved` with `stock_status=available`.
20. Purchase mode `direct_purchase` only when auction car completed; else inquiry only.
21. Reservation blocks auction car legacy reserve when status `completed`.
22. USD cost divisor uses `rate - 3` for purchases before 2026-05-06.
23. Seller exchange rate adjustment affects admin intake display only, not model `usd_cost`.

## Reservations

24. One active/converted/pending_approval reservation per target (unified path).
25. Seller reservation pricing includes commission on seller path.
26. Unified reservation auto-generates reservation invoice after commit.
27. Release clears `seller_id` and sets draft when not converted and no order.
28. Release does not refund wallet debits.
29. Cron expiry marks `expired` but does not unlock vehicle.
30. Admin checkout completes payments immediately; seller checkout requires approval.

## Payments & Wallet

31. Partial payments supported; order `payment_pending` until balance zero.
32. First completed payment converts reservation to `converted`.
33. Full payment marks vehicle sold.
34. Wallet debits enforce sufficient balance.
35. Remittance credits allocate FIFO by received date.
36. Seller checkout sets `allocate_on_create=false` until approval.

## Invoices

37. Reservation invoices idempotent (existing returned if present).
38. Partial payment invoices require completed payment.
39. Invoice email skipped gracefully without SMTP.

## Remittances

40. Verification requires complete remittance (seller, customer, received_by_seller).
41. Wallet credit on verify is idempotent.
42. Seller can delete only own pending seller-submitted remittances without credits/allocations.
43. Admin PDF reject returns claim to pool.
44. Seller claim requires seller-linked customer.

## Checkout Approval

45. Approve completes all pending payments and converts reservation.
46. Decline fails payments, reverses wallet debits, soft-deletes order, restores active reservation.
47. Hold moves to `on_hold` status.

---

# 17. Known Limitations

## Functional Gaps

| Limitation | Detail |
|------------|--------|
| Reservation wallet refund | `release()` does not refund `wallet_amount_used` |
| Cron expiry incomplete | `reservations:expire` does not call `release()` for lock cleanup |
| Dual reservation APIs | Legacy `reserve()` and unified `reserveForCustomer()` coexist |
| Seller adjustment scope | Display-only in admin intake; not in model financial calculations |
| Customer payment history page | Route exists; Vue page may be missing |
| Multi-role users | Not supported — one role per user |
| Role switching | Not implemented in user management |
| User delete | No admin user deletion flow |
| Admin user management | Admin accounts excluded from managed user UI |
| API route auth | Many `/api/*` helpers unauthenticated (documented in scope catalog) |
| Future actions | Cataloged but not granted to any role (`future_actions` in config) |
| Export scopes | Not in default non-admin grants; must be explicitly granted |
| Vehicle profit nav | Admin nav placeholders disabled (no routes) |

## Access Control Maturity

| Limitation | Detail |
|------------|--------|
| Dual enforcement | Role middleware + scope middleware + controller ownership checks coexist |
| Ownership rules | `seller_id`, `customerBelongsToSeller()` not fully modeled in scope layer |
| Log-only mode | `PERMISSIONS_LOG_ONLY_ENABLED` observability without deny |
| Customer scope freeze | Internal refactors must not affect customer flows |

## Technical / Operational

| Limitation | Detail |
|------------|--------|
| Invoice PDF | Requires Chrome/Browsershot runtime |
| Exchange rate API | External dependency on exchangerate.host |
| Test isolation | Some feature tests require minimal migration subsets; schema conflicts if mixed |
| README_ECOMMERCE | References shopping cart features that may not match current reservation-driven model |

## Future Enhancements (Not Implemented)

- Full role assignment / role switching in user management
- `users_access.manage_scopes` and `users_access.assign_roles` (locked/future)
- Remittance future actions: `complete_claim`, `allocate_customer`, `approve_allocation`
- Invoice resend scope
- Consolidated seller adjustment between ShowSeller settings and Edit form
- Automated reservation expiry with full `release()` cleanup

---

# 18. Testing Checklist

## Admin QA

### Dashboard & Analytics
- [ ] Dashboard loads with inventory and financial summary
- [ ] Navigation reaches all admin modules

### Auction Intake
- [ ] Create, edit, batch save auction row
- [ ] Upload/delete auction files (all types)
- [ ] Assign seller → draft vehicle created
- [ ] Exchange rate displays for purchase date
- [ ] Seller adjustment reflected in buying cost column when seller assigned
- [ ] Operational status reflects reservations/orders

### Vehicles
- [ ] CRUD vehicles; link to auction car
- [ ] Manage vehicle documents
- [ ] Publish vehicle → appears on public site

### Reservations
- [ ] Create reservation for customer (any vehicle state with admin flag)
- [ ] Release reservation → vehicle unlocked / seller cleared per rules
- [ ] Admin checkout → immediate conversion

### Checkout Approvals
- [ ] Seller checkout appears in approval queue
- [ ] Approve → payments completed, reservation converted
- [ ] Hold → status on_hold
- [ ] Decline → order removed, reservation restored

### Orders & Shipments
- [ ] View order detail; update status fields
- [ ] Create/update shipments; milestone progression

### Payments
- [ ] List payments; mark bank transfer paid
- [ ] Partial payments update order balance

### Remittances
- [ ] Manual create; bulk PDF import
- [ ] Verify → customer wallet credited
- [ ] Reject pending; reject claim → returns to pool

### Invoices
- [ ] List/download invoices
- [ ] Generate partial payment invoice for completed payment

### Users
- [ ] List users with status column and role filters
- [ ] Create each managed role type
- [ ] Edit profile; seller adjustment; optional password
- [ ] Role tampering rejected
- [ ] Disable/enable user; disabled user cannot login
- [ ] Cannot edit/disable admin users
- [ ] Customer show: orders, wallet, remittances
- [ ] Seller show: sales, remittances, checkout queue
- [ ] Staff show: bidder, data_entry, shipping_agent profiles

### Access Control
- [ ] View role grants matrix
- [ ] Save role grants; locked scopes rejected
- [ ] User override grant/revoke/inherit
- [ ] Bypass scopes require reason; seller-only

### Settings & Master Data
- [ ] CRUD countries, regions, ports, auction houses
- [ ] Bank accounts, deposit settings, pricing rules, shipping rates
- [ ] Reservation and invoice settings
- [ ] Makes, models, attributes batch operations

---

## Seller QA

### Dashboard
- [ ] Dashboard shows inventory and order stats (when scopes granted)

### Vehicles
- [ ] List/create/edit own vehicles only
- [ ] Manage documents on own vehicles
- [ ] Cannot access other seller's vehicles

### Auction Pool
- [ ] Browse unlinked auction cars
- [ ] Reserve from pool for own customer

### Reservations
- [ ] Create reservation with commission pricing
- [ ] Release own reservation
- [ ] Reservation invoice emailed to customer

### Checkout
- [ ] Checkout creates pending payments and pending_approval
- [ ] Without bypass: awaits admin approval
- [ ] With `checkout.bypass_approval` override: auto-converts

### Pay Balance
- [ ] Pay balance on converted reservation
- [ ] Without bypass: payments pending
- [ ] With `payments.bypass_approval` override: auto-completes

### Customers
- [ ] Create customer; search; view consignees
- [ ] Wallet credit not available (admin only)

### Remittances
- [ ] Submit remittance with deposit slip
- [ ] Claim pool remittance with claim slip
- [ ] Delete own pending seller-submitted remittance
- [ ] Cannot delete verified/imported remittances

### Orders & Invoices
- [ ] View own orders; update shipment info
- [ ] Generate partial payment invoice

### Negative tests
- [ ] Cannot verify remittances
- [ ] Cannot mark payments paid
- [ ] Cannot approve own checkout without bypass
- [ ] Scope enforcement 403 when grant revoked

---

## Data Entry QA

### Dashboard
- [ ] Dashboard loads

### Auction Queue
- [ ] View auction list read-only
- [ ] Cannot edit rows, upload files, or assign seller
- [ ] Commercial fields masked

### Vehicles
- [ ] List and view all vehicles
- [ ] Create vehicle (if scope granted)
- [ ] Edit enrichment fields only
- [ ] Cannot edit seller, pricing, stock status
- [ ] Cannot change locked chassis/make/model on auction-linked vehicles
- [ ] Manage documents (if scope granted)

### Master Data
- [ ] Batch create/update makes, models, attributes
- [ ] Batch delete rejected for data entry
- [ ] Edit model attributes

### Negative tests
- [ ] No access to reservations, checkout, payments, remittances
- [ ] No admin routes
- [ ] 403 when scope revoked

---

## Bidder QA

### Dashboard
- [ ] Dashboard loads with summary stats

### Auction Intake
- [ ] View and edit bidder identity fields
- [ ] Create new intake row
- [ ] Upload/delete `as` files only
- [ ] Cannot edit shipping columns or selling_price
- [ ] Cannot assign seller
- [ ] Seller adjustment not visible

### Negative tests
- [ ] No vehicle, reservation, or financial module access
- [ ] 403 when scope revoked

---

## Shipping Agent QA

### Dashboard
- [ ] Dashboard loads

### Auction Intake
- [ ] Edit shipping/logistics fields and selling_price
- [ ] Upload shipping document types
- [ ] PC preview works
- [ ] Cannot edit bidder identity block
- [ ] Cannot add new rows
- [ ] Cannot assign seller or reserve

### Negative tests
- [ ] No commercial module access
- [ ] 403 when scope revoked

---

## Customer QA

### Public Marketplace
- [ ] Home page lists published vehicles with filters
- [ ] Vehicle detail page for published/reserved available stock
- [ ] Inquiry vs purchase mode correct per auction completion
- [ ] Reserved vehicle shows expiry when applicable

### Authentication
- [ ] Register/login
- [ ] Disabled account cannot login

### Dashboard
- [ ] Dashboard shows orders and payment summary

### Checkout & Orders
- [ ] CIF checkout for direct purchase vehicle
- [ ] Reservation checkout uses locked seller price
- [ ] Order list and detail with status timeline

### Payments
- [ ] Pay balance via wallet/card/bank
- [ ] Wallet balance reflects remittance credits minus usage

### Invoices
- [ ] List and download invoices
- [ ] Reservation invoice received by email (when SMTP configured)

### Consignees & Remitters
- [ ] CRUD consignees and remitters

### Documents
- [ ] Download documents when order status permits

### Negative tests
- [ ] Cannot access admin/seller/staff routes
- [ ] Cannot view other customers' orders

---

## Cross-Role Integration QA

- [ ] Full lifecycle: auction intake → seller assign → publish → reserve → seller checkout → admin approve → partial pay → invoice → full pay → sold → shipment milestones
- [ ] Remittance: seller submit → admin verify → wallet → customer checkout debit → allocation on payment
- [ ] Remittance pool: admin import → seller claim → admin verify
- [ ] User disable: active session logged out on next request
- [ ] Exchange rate: missing date fetched from API and stored
- [ ] Access control: revoke seller scope → route returns 403

---

## Automated Test Reference

Run targeted suites:

```bash
php artisan test tests/Feature/AdminManagedUserUpdateTest.php
php artisan test tests/Feature/AdminUserActiveStatusTest.php
php artisan test tests/Feature/SellerCheckoutBypassApprovalTest.php
php artisan test tests/Feature/SellerPaymentBypassApprovalTest.php
php artisan test tests/Feature/SellerRemittanceDeleteTest.php
php artisan test tests/Feature/SellerExchangeRateAdjustmentTest.php
php artisan test tests/Feature/UserScopeOverrideFoundationTest.php
php artisan test tests/Feature/DataEntryScopeEnforcementTest.php
php artisan test tests/Feature/SellerScopeEnforcementTest.php
php artisan test tests/Feature/ExchangeRateHistoricalFetchTest.php
```

---

## Related Documentation

| Document | Location |
|----------|----------|
| Architecture & lifecycle detail | `docs/ARCHITECTURE.md` |
| System blueprint & schema appendix | `docs/system-blueprint.md` |
| Permission scope catalog | `docs/permission-scope-catalog.md` |
| Access control module tree | `docs/ACESS CONTROL.md` |
| Vehicle auction analysis | `docs/VEHICLE_AUCTION_ANALYSIS.md` |
| QA vehicle lifecycle checklist | `docs/QA-vehicle-lifecycle-checklist.md` |
| Buying cost calculation notes | `docs/Auction Intake Buying Cost Calculation.md` |
| Shipment handling flow | `docs/shipment_handling_flow.md` |

---

*End of Project Master Documentation*
