# Project Architecture

## 1. Purpose And Scope

This document describes the current architecture of the Jetstream-based vehicle marketplace application in this repository. It is written from the code and migrations that exist now, not from older planning notes. Where the codebase contains legacy structures, compatibility layers, or incomplete refactors, those are called out explicitly.

The application is a Laravel 12 + Jetstream + Inertia + Vue 3 system for:

- public vehicle discovery and filtering
- seller and admin vehicle intake and listing management
- auction purchase intake and conversion into saleable vehicles
- customer cart, checkout, purchase, payment, and shipment tracking
- shipping, deposit, and pricing configuration

At a high level, the system combines three architectural styles:

1. classic relational commerce tables for users, orders, payments, shipping, and logistics
2. a hybrid inventory model for vehicles that mixes normalized catalog references with legacy direct columns
3. an EAV-style attribute layer for dynamic vehicle specifications and public filtering

## 2. Technology Stack

### Backend

- Laravel 12
- PHP 8.2
- Eloquent ORM
- Jetstream and Fortify for authentication scaffolding
- Sanctum for API token support
- Inertia server responses for SPA-style page delivery

### Frontend

- Vue 3
- Inertia.js
- Tailwind CSS
- Vite

### Architectural Characteristics

- role-based route partitioning using a `users.role` column
- service-layer orchestration for purchase, payment, shipping, and filtering logic
- Eloquent model events for derived lifecycle synchronization
- intentional denormalization in orders and vehicles to preserve business snapshots and support backward compatibility

## 3. Runtime Architecture

### 3.1 Entry Surfaces

The application has four main request surfaces.

#### Public Web

- `/` renders the public catalog listing
- `/vehicles/{vehicle}` renders the public vehicle detail page
- `/vehicle-catalog/...` exposes JSON endpoints for model lists, attribute metadata, and model-code options used by the public catalog UI

#### Authenticated Web

Authenticated routes are grouped under `auth:sanctum`, Jetstream session auth, and `verified` middleware. Within that authenticated surface the app is segmented by `role` middleware.

#### Admin Surface

The admin area owns configuration and operational control:

- countries, ports, shipping rates
- bank accounts, pricing rules, deposit settings
- users, makes, models, attributes, attribute values
- auction data-entry intake
- vehicle creation and editing
- order and shipment administration
- payment review and manual payment approval

#### Seller Surface

The seller area is narrower and focused on inventory and order visibility:

- seller dashboard
- seller vehicle CRUD
- seller document management
- seller order visibility and limited order updates

#### Customer Surface

The customer area supports the buyer lifecycle:

- cart and cart count
- multi-item checkout
- single-vehicle purchase flow
- order history and order detail pages
- payments and payment history
- consignee and remitter address-book management
- customer-facing document downloads

#### API Surface

The API routes are lightweight and primarily support dynamic frontend behavior:

- model attributes lookup
- model code lookup
- model default metadata
- country and port lookup
- bank account lookup
- shipping calculations

### 3.2 Layering

The backend is not formally hexagonal or DDD-based, but it does follow a practical separation of concerns.

#### Controllers

Controllers shape request validation, select a service or model action, and return Inertia pages or JSON. Business logic is only partially embedded in controllers; the heavier transactional work has been moved into services.

#### Services

Key services centralize business behavior:

- `CustomerPurchaseService` orchestrates checkout and single-vehicle purchasing
- `PaymentService` owns payment creation, reconciliation, order balance sync, and refund handling
- `ShippingService` resolves shipping rates and logistics totals
- `PricingService` calculates deposit and checkout pricing structures
- `VehicleFilterService` builds filter queries and facet data for the public catalog
- `VehicleCatalogAttributeService` resolves make/model attribute metadata and defaults
- `PurchaseTimelineService` builds display timelines for orders
- `VehicleFormOptionsService` provides admin/seller vehicle-form option data

#### Models

Models contain a mixture of relationships, convenience helpers, compatibility accessors, and lifecycle hooks. `Order`, `Vehicle`, and `AuctionCar` are especially important because they contain business-state helpers, derived fields, and synchronization logic.

### 3.3 Role Model

The application uses a simple role string on `users.role` instead of a separate roles table. The effective roles are:

- `admin`
- `seller`
- `customer`

This is distinct from Jetstream teams. Team tables exist, but the app’s primary access-control model is role-based rather than team-scoped.

## 4. Domain Modules

### 4.1 Identity And Access

Identity is Jetstream-based with the application-specific extension that `users` also acts as the root actor table for admins, sellers, and customers.

Responsibilities:

- authentication and verification
- role assignment
- profile data reused in checkout snapshots
- optional Jetstream team membership
- personal access tokens through Sanctum

### 4.2 Vehicle Inventory And Listing Management

This module manages the inventory that the public catalog exposes. It includes:

- the core `vehicles` table
- listing media and documents
- seller/admin CRUD flows
- publication state and stock state
- optional linkage to an auction intake record

The vehicle model is intentionally hybrid. It stores normalized `make_id` and `model_id`, but it still also stores text `make` and `model` columns for compatibility with older flows and UI assumptions.

### 4.3 Catalog Normalization And Dynamic Attributes

This module provides a normalized catalog and an EAV-like attribute system for vehicle specifications.

It is built from:

- `makes`
- `models`
- `attributes`
- `attribute_values`
- `model_attributes`
- `make_attributes`
- `model_attribute_defaults`
- `vehicle_attribute_values`

This layer supports:

- make and model normalization
- configurable make-level and model-level specification definitions
- allowed values and default values
- public filter facets and ranges
- migration away from older vehicle direct columns such as `vehicle_type`, `fuel_type`, `color`, and `transmission`

### 4.4 Commerce And Orders

This module covers the customer buying lifecycle:

- carts
- checkout
- direct single-vehicle purchase
- order creation
- order items
- order-level financial snapshotting
- customer status progression

The `orders` table is the main snapshot boundary of the system. It intentionally duplicates customer party information and pricing data so an order remains historically stable even if the user profile, consignee/remitter records, or pricing settings change later.

### 4.5 Payments And Financial Operations

This module covers:

- payment creation
- manual bank-transfer evidence storage
- payment approval/reconciliation
- payment-to-order allocation
- refund recording
- order balance synchronization
- deposit configuration
- pricing rules for inspection and insurance

### 4.6 Logistics And Shipping

This module covers:

- shipping country and port configuration
- shipping rate lookup by destination, vehicle type, and shipping method
- shipment records attached to orders and vehicles
- route-specific inspection and insurance cost rules

### 4.7 Party Profiles

Customers can store two separate address-book style record types:

- `consignees`: receiving party
- `remitters`: paying/sending party

These are operational profiles, not historical truth. Historical truth lives on the order snapshot columns.

### 4.8 Auction Intake And Vehicle Creation Pipeline

This is the newest domain module. It introduces a pre-sale operational workflow:

`Auction Purchase -> Data Entry -> Vehicle Creation -> Ready For Sale`

It uses:

- `auction_cars` as the intake/source table
- `vehicles.auction_car_id` as the one-to-one linkage point
- `vehicles.is_ready_for_sale` as the visibility/readiness flag derived from auction completion state

The design keeps auction acquisition data separate from the public vehicle listing until an admin creates the corresponding vehicle record.

## 5. Database Architecture

## 5.1 Database Summary

The current schema consists of 39 tables across framework, identity, catalog, commerce, logistics, payments, and auction intake domains.

The schema is not purely normalized. Important denormalizations are deliberate:

- orders store immutable party and pricing snapshots
- vehicles retain compatibility columns during catalog normalization
- auction cars store wide cost-component columns rather than line-item child rows

## 5.2 Table Catalog

### A. Framework And Infrastructure Tables

#### `cache`

Purpose:

- database-backed cache storage

Key structure:

- primary key on `key`
- expiration index

Notes:

- Laravel infrastructure table, not business-domain data

#### `cache_locks`

Purpose:

- database-backed cache locking

Key structure:

- primary key on `key`
- expiration index

Notes:

- Laravel infrastructure table

#### `jobs`

Purpose:

- queued job storage

Key structure:

- queue index

Notes:

- exists even if the app is not heavily using queued workflows yet

#### `job_batches`

Purpose:

- grouped batch tracking for queued jobs

Key structure:

- primary key on `id`

Notes:

- Laravel infrastructure table

#### `failed_jobs`

Purpose:

- failed queue job storage

Key structure:

- unique `uuid`

Notes:

- operational/debug table rather than business-domain data

### B. Identity, Authentication, And Team Tables

#### `users`

Purpose:

- root actor table for admins, sellers, and customers

Important columns:

- identity and auth: `name`, `email`, `password`, `email_verified_at`, `remember_token`
- Jetstream/team linkage: `current_team_id`, `profile_photo_path`
- app-specific extensions from later migrations: `role`, `phone`, `address`, `country`, order-related profile fields

Relationships:

- one-to-many with `vehicles` through `seller_id`
- one-to-many with `orders`
- one-to-many with `consignees`
- one-to-many with `remitters`
- optional approval actor for payments through `payments.approved_by`

Rules and notes:

- `email` is unique
- role-based authorization is driven from this table, not from a separate roles table

#### `password_reset_tokens`

Purpose:

- password reset workflow storage

Important columns:

- primary key `email`
- `token`
- `created_at`

#### `sessions`

Purpose:

- session persistence

Important columns:

- primary key `id`
- optional `user_id`
- `payload`
- `last_activity`

Relationships:

- optional link to `users`

#### `personal_access_tokens`

Purpose:

- Sanctum token storage

Important columns:

- morph columns for token owner
- unique token hash
- expiration support

Notes:

- supports API authentication if expanded in the future

#### `teams`

Purpose:

- Jetstream team/workspace model

Relationships:

- owner-like relationship back to `users`
- many-to-many with users through `team_user`

Notes:

- the table exists, but the main app behavior is not team-scoped today

#### `team_user`

Purpose:

- pivot table connecting users to teams

Relationships:

- `team_id -> teams.id`
- `user_id -> users.id`

Rules:

- unique composite membership per team/user pair

#### `team_invitations`

Purpose:

- Jetstream team invitation workflow

Relationships:

- `team_id -> teams.id`

Rules:

- unique composite pair of team and invited email

### C. Catalog And Attribute Tables

#### `makes`

Purpose:

- normalized manufacturer master data

Relationships:

- one-to-many with `models`
- one-to-many with `vehicles`
- many-to-many style attribute mapping through `make_attributes`

Rules:

- make names are unique

#### `models`

Purpose:

- normalized vehicle model records under a make

Relationships:

- `make_id -> makes.id`
- one-to-many with `vehicles`
- many-to-many style attribute mapping through `model_attributes`

Rules:

- unique composite of make and model name

Notes:

- represented in code by the `VehicleModel` model class because `Model` is reserved in Eloquent terminology

#### `attributes`

Purpose:

- master definition of dynamic catalog attributes

Important columns:

- `name`
- `type`

Supported types in code:

- `select`
- `multi_select`
- `range`
- `boolean`

Relationships:

- one-to-many with `attribute_values`
- many-to-many style configuration with makes and models
- one-to-many with `vehicle_attribute_values`

Notes:

- represented in code by `VehicleAttribute` even though the table name is `attributes`

#### `attribute_values`

Purpose:

- allowed selectable values for select and multi-select attributes

Relationships:

- `attribute_id -> attributes.id`

Rules:

- unique composite of `attribute_id` and `value`

#### `make_attributes`

Purpose:

- make-level attribute availability and default metadata

Relationships:

- `make_id -> makes.id`
- `attribute_id -> attributes.id`
- optional `default_value_id -> attribute_values.id`

Important metadata:

- `min_value`
- `max_value`
- `default_value_id`
- `default_raw_value`

Rules:

- unique composite of `make_id` and `attribute_id`

#### `model_attributes`

Purpose:

- model-level attribute configuration, allowed values, numeric bounds, and defaults

Relationships:

- `model_id -> models.id`
- `attribute_id -> attributes.id`
- optional `default_value_id -> attribute_values.id`

Important metadata:

- `min_numeric_value`, `max_numeric_value`
- `min_value`, `max_value`
- `default_value_id`
- `default_raw_value`
- `default_value` JSON payload for allowed-value and default selection metadata

Rules:

- unique composite of `model_id` and `attribute_id`

Notes:

- this is the main source of truth for model-specific attribute configuration

#### `model_attribute_defaults`

Purpose:

- read-oriented persistence of model-level default values by attribute

Relationships:

- `model_id -> models.id`
- `attribute_id -> attributes.id`
- optional `value_id -> attribute_values.id`

Notes:

- this table is actively written from the admin model-attribute mapping flow
- it is not the main configuration source; the main editable payload lives on `model_attributes.default_value`
- its existence reflects some duplication in the attribute-default design

#### `vehicle_attribute_values`

Purpose:

- per-vehicle assignment of dynamic attributes

Relationships:

- `vehicle_id -> vehicles.id`
- `attribute_id -> attributes.id`
- optional `value_id -> attribute_values.id`

Important columns:

- `raw_value` for numeric, boolean, or free-form persisted values

Rules:

- uniqueness across `vehicle_id`, `attribute_id`, `value_id`, and `raw_value`
- indexes were added to support public filtering performance

### D. Vehicle Inventory Tables

#### `vehicles`

Purpose:

- the main inventory and listing table

Relationships:

- optional `seller_id -> users.id`
- optional `auction_car_id -> auction_cars.id`
- optional `make_id -> makes.id`
- optional `model_id -> models.id`
- one-to-many with `vehicle_images`
- one-to-many with `vehicle_documents`
- one-to-many with `vehicle_attribute_values`
- referenced from `order_items` and `shipments`

Important columns:

- normalized catalog references: `make_id`, `model_id`
- compatibility strings: `make`, `model`
- catalog identity: `model_code`, `rec_no`, `chassis`, `grade`
- years and usage: `registration_year`, `manufacture_year`, `year`, `mileage`
- pricing and promotion: `price`, `discount_percentage`, `discount_amount`, `is_featured`, `is_clearance`
- publication and availability: `status`, `stock_status`, `is_ready_for_sale`
- compatibility attributes retained from pre-EAV design: `vehicle_type`, `color`, `transmission`, `fuel_type`, `engine_capacity`
- physical/logistics fields: `location`, `stock_country`, dimensions, seats, doors

Rules and notes:

- `auction_car_id` is unique, making the auction-to-vehicle linkage effectively one-to-one
- `seller_id` is nullable, allowing admin-created inventory
- model events keep text `make` and `model` synchronized from normalized relations when IDs are set
- several direct spec columns are marked deprecated in code but remain live for compatibility and fallback

#### `vehicle_images`

Purpose:

- listing image gallery

Relationships:

- `vehicle_id -> vehicles.id`

Notes:

- used by public and back-office vehicle pages

#### `vehicle_documents`

Purpose:

- documents associated with a vehicle

Relationships:

- `vehicle_id -> vehicles.id`
- `uploaded_by -> users.id`

Important uses:

- admin and seller uploads
- customer document visibility through order-linked download pages

### E. Shopping, Orders, And Shipment Tables

#### `carts`

Purpose:

- temporary cart items before checkout

Relationships:

- `user_id -> users.id`
- `vehicle_id -> vehicles.id`

Rules:

- unique composite of `user_id` and `vehicle_id`

Notes:

- each row represents one vehicle selection per user rather than duplicated cart lines

#### `orders`

Purpose:

- root purchase/order record and the main historical snapshot boundary

Relationships:

- `user_id -> users.id`
- optional `consignee_id -> consignees.id`
- optional `remitter_id -> remitters.id`
- optional `shipping_country_id -> shipping_countries.id`
- optional `shipping_port_id -> ports.id`
- one-to-many with `order_items`
- one-to-many with `payments`
- one-to-many with `shipments`
- one-to-many with `payment_allocations` through payments

Important column groups:

- pricing snapshot: `total_price`, `vehicle_price`, `shipping_cost`, `freight_cost`, `inspection_fee`, `insurance_fee`, `cif_total`, `discount_amount`
- deposit snapshot: `deposit_percentage`, `min_deposit_amount`, `min_ship_deposit_percentage`
- payment state: `paid_amount`, `remaining_balance`
- shipping metadata: `shipping_country_id`, `shipping_port_id`, `shipping_type`, `shipping_address`, `from_port`, `arrival_port`, `tracking_number`
- dates and lifecycle timestamps: `order_date`, `buy_date`, `shipped_at`, `departure_date`, `arrival_date`, `dhl_date`, `delivered_at`
- party snapshots: `consignee_*` and `remitter_*`
- lifecycle/status: `status`, `customer_status`, `type`, `currency`, `notes`

Business behavior:

- the order captures locked pricing and party data at the time of purchase
- the `Order` model exposes helpers such as `financialTotal()`, `logisticsTotal()`, `resolvedPurchaseStatus()`, `consigneeDisplay()`, and `remitterDisplay()`
- status progression is richer in code than in the raw schema and includes states such as `reserved`, `payment_pending`, `paid`, `shipment_pending`, `shipped`, `in_transit`, `arrived`, `delivered`, and `completed`

#### `order_items`

Purpose:

- per-vehicle line items attached to an order

Relationships:

- `order_id -> orders.id`
- `vehicle_id -> vehicles.id`

Important columns:

- `quantity`
- `price`
- `discount_applied`

Notes:

- these rows preserve point-in-time line pricing independent of later vehicle changes

#### `shipments`

Purpose:

- shipment tracking records for purchased vehicles

Relationships:

- `order_id -> orders.id`
- `vehicle_id -> vehicles.id`

Important behavior:

- used by the admin shipment workflow
- contributes to purchase timeline rendering
- overlaps with some order-level shipping/date fields, which is a known source-of-truth tension in the current model

### F. Payment And Pricing Tables

#### `payments`

Purpose:

- payment transaction records for orders

Relationships:

- `order_id -> orders.id`
- optional `bank_account_id -> bank_accounts.id`
- optional `approved_by -> users.id`

Important columns:

- `payment_method`
- `transaction_id`
- `amount`
- `status`
- `payment_data` JSON
- `payment_proof`
- `bank_reference`
- `paid_at`

Rules and notes:

- `transaction_id` is unique when present
- `PaymentService` is the primary coordinator for creation, reconciliation, refunds, and order-balance updates
- the current system records payment intent and outcome in this table even when manual transfer approval is involved

#### `payment_allocations`

Purpose:

- allocation records linking payments to order balances

Relationships:

- `payment_id -> payments.id`
- `order_id -> orders.id`

Notes:

- supports allocation-style accounting even though many flows are still one payment to one order
- this adds flexibility, but it also increases complexity in payment reasoning

#### `payment_histories`

Purpose:

- historical payment receipt tracking

Relationships:

- `user_id -> users.id`
- `order_id -> orders.id`

Notes:

- the active payment flows are primarily built around `payments` and `payment_allocations`
- this table exists as additional financial history storage, but it is less central to the current application path than `payments`

#### `bank_accounts`

Purpose:

- admin-managed receiving bank accounts for transfer payments

Relationships:

- referenced from `payments`

Important behavior:

- customer checkout and payment pages only expose active bank accounts

#### `pricing_rules`

Purpose:

- configurable rules for inspection and insurance pricing

Relationships:

- optional `country_id -> shipping_countries.id`
- optional `port_id -> ports.id`

Important behavior:

- `ShippingService` resolves the most specific active rule available for a country, port, and vehicle type combination
- rules can be global or scoped based on nullable fields

#### `deposit_settings`

Purpose:

- global configuration for deposit requirements

Important behavior:

- the app treats this table as a singleton-style configuration source with one active record at a time
- order creation snapshots the percentage and required amount so later admin changes do not rewrite history

### G. Shipping Configuration Tables

#### `shipping_countries`

Purpose:

- destination country configuration for shipping calculations

Important columns:

- `name`
- `code`
- `is_active`

Relationships:

- one-to-many with `ports`
- one-to-many with `shipping_rates`
- referenced by `orders`

Notes:

- in code, the model class is named `Country`, but it maps to the `shipping_countries` table

#### `ports`

Purpose:

- destination ports under a shipping country

Relationships:

- `country_id -> shipping_countries.id`
- one-to-many with `shipping_rates`
- referenced by `orders`

Rules:

- unique composite of `country_id` and port name

#### `shipping_rates`

Purpose:

- base freight lookup table

Relationships:

- `country_id -> shipping_countries.id`
- `port_id -> ports.id`

Important columns:

- `vehicle_type`
- `shipping_type`
- `price`
- `currency`

Business behavior:

- `ShippingService` resolves shipping quotes from this table and then layers inspection and insurance rules on top
- uniqueness constraints and indexing are used to protect duplicate rate definitions for the same route/type combination

### H. Party Profile Tables

#### `consignees`

Purpose:

- user-maintained receiving-party profiles

Relationships:

- `user_id -> users.id`
- optionally referenced by `orders.consignee_id`

Important behavior:

- one record can be flagged as default
- the order does not rely on this table as historical truth after checkout; it snapshots values into order columns

#### `remitters`

Purpose:

- user-maintained paying-party profiles

Relationships:

- `user_id -> users.id`
- optionally referenced by `orders.remitter_id`

Important behavior:

- mirrors the consignee pattern with independent default handling

### I. Auction Intake Tables

#### `auction_cars`

Purpose:

- pre-listing auction intake records used by admin data-entry operations

Relationships:

- optional `make_id -> makes.id`
- optional `model_id -> models.id`
- one-to-one style reverse link from `vehicles.auction_car_id`

Important columns:

- intake identity: `chassis_no`, `purchase_date`
- acquisition pricing: `auction_price`, `auction_fee`, `inspection`, `transportation`, `h_charge`, `van`, `insurance`, `freight`, `extra`, `total`, `selling_price`
- logistics/location metadata: `vessel`, `sailing_date`, `region`, `city`, `area`
- operations metadata: `admin_notes`, `status`

Rules and business behavior:

- `auction_price` is required
- `chassis_no` is unique when present
- `total` is derived from cost components in the model and is not intended to be manually authored as the source of truth
- `status` is derived, not manually selected
- the record becomes `completed` only when it has a linked vehicle and a `selling_price`
- `AuctionCar::saved` and `Vehicle::saved/deleted` synchronize readiness state between the auction record and the linked vehicle

Notes:

- earlier free-text `make` and `model` columns were removed during the refinement migration; current linkage is through normalized IDs only

## 5.3 Relationship Map

### Identity And Access

- a user can own many orders
- a user can act as a seller for many vehicles
- a user can store many consignees and remitters
- a user can approve payments
- users can belong to teams, but teams are not the main access model in current business flows

### Inventory And Catalog

- a make has many models
- a make can expose many make-level attribute mappings
- a model belongs to a make and can expose many model-level attribute mappings
- a vehicle belongs to an optional seller, make, model, and auction car
- a vehicle has many images, documents, and attribute values

### Commerce

- a customer has many cart rows
- a cart row belongs to one vehicle
- an order belongs to one customer and has many order items, payments, and shipments
- each order item points at one vehicle
- each shipment points at one order and one vehicle

### Payments And Shipping

- a payment belongs to an order and may optionally belong to a receiving bank account and approving admin
- a payment can have allocation rows
- a shipping country has many ports and shipping rates
- an order may reference a shipping country and port snapshot through foreign keys plus copied display fields

### Auction Pipeline

- an auction car may become exactly one vehicle
- a vehicle may originate from at most one auction car

## 6. Core Business Flows

### 6.1 Public Catalog Flow

1. The public home page calls `VehicleFilterService` with normalized query parameters.
2. The service builds a base listing query constrained to `stock_status = available` and optionally `status = published`.
3. The service computes make, model, stock-country, numeric-range, and dynamic-attribute facets from the same dataset.
4. The public UI refines filters by calling lightweight catalog endpoints for models, attributes, and model codes.
5. Vehicle detail pages only render published and available vehicles.

Important architectural point:

- filtering is server-driven and based on the actual dataset rather than a static filter registry

### 6.2 Seller/Admin Vehicle Creation Flow

1. Admins and sellers create or edit vehicles through their respective back-office pages.
2. The form uses catalog metadata, option services, and attribute metadata to present dynamic fields.
3. The vehicle save path stores both normalized references (`make_id`, `model_id`) and compatibility strings (`make`, `model`).
4. Dynamic attributes are stored in `vehicle_attribute_values`.
5. Vehicle publication and stock state determine whether the public catalog can surface the record.

### 6.3 Auction Intake To Vehicle Flow

1. An admin creates an `auction_cars` record in the data-entry queue.
2. The intake stores auction and logistics cost components plus location metadata.
3. The admin later creates a vehicle from the auction record using `auction_car_id`.
4. Make, model, and chassis are locked during that linked vehicle creation flow to preserve auction-source integrity.
5. Once the linked vehicle exists and `selling_price` is set, the auction record derives `status = completed`.
6. The linked vehicle’s `is_ready_for_sale` flag is synchronized from that derived auction status.

Important architectural point:

- the queue logic is separate from public listing visibility; linking alone is not enough, because the vehicle also needs readiness and normal publication state

### 6.4 Multi-Item Checkout Flow

1. A customer adds vehicles to `carts`.
2. Checkout loads cart items, active bank accounts, shipping options, and default consignee/remitter drafts.
3. The customer selects or edits consignee/remitter details; these are prepared as order snapshots rather than live references only.
4. `ShippingService::calculateForItems()` computes aggregate freight, inspection, and insurance.
5. `PricingService` computes the checkout total and deposit requirement.
6. The order is created with locked pricing fields, shipping metadata, and party snapshots.
7. Order items are written.
8. A payment may be created immediately depending on the selected payment option.
9. The cart is cleared.

### 6.5 Single-Vehicle Purchase Flow

1. A customer opens the direct purchase page for one vehicle.
2. `CustomerPurchaseService` locks the vehicle row for update.
3. `ShippingService::calculate()` computes route-specific freight and option costs.
4. `PricingService` computes deposit requirements.
5. The system creates an order with full financial and party snapshots.
6. An order item is created for the vehicle.
7. A payment is created in `pending` or gateway-reconciled state.
8. The vehicle is moved to `stock_status = sold` and `status = reserved` immediately to prevent competing purchase flows.

### 6.6 Payment Reconciliation Flow

1. A payment record is created against an order.
2. If the payment is completed, `PaymentService` recalculates order totals.
3. The service updates `paid_amount`, `remaining_balance`, and purchase status.
4. When the order balance reaches zero, ordered vehicles are moved to `stock_status = sold` and `status = sold`.
5. Allocation records may be created to formalize how payments map to order balances.

### 6.7 Shipment Administration Flow

1. Admin users add shipment rows to an order.
2. Shipment data feeds the purchase timeline and process summary views.
3. Admin order updates enforce lifecycle constraints such as full payment before shipment-stage statuses.
4. Shipment dates and order dates currently overlap, so views often combine shipment and order fields when rendering the latest state.

## 7. Attribute System

## 7.1 Why The Attribute System Exists

The repository started from a direct-column vehicle model. As requirements expanded, static columns became too rigid for:

- make-specific specs
- model-specific specs
- filter facets that vary by vehicle family
- defaults and allowed value lists per model

The attribute layer solves that by separating attribute definition from vehicle instance values.

## 7.2 Structural Design

The attribute architecture has four levels.

1. `attributes` defines the attribute itself.
2. `attribute_values` defines the selectable values for select-like attributes.
3. `make_attributes` and `model_attributes` define where an attribute applies and what its defaults/bounds are.
4. `vehicle_attribute_values` stores actual values on each vehicle.

## 7.3 Resolution Rules

`VehicleCatalogAttributeService` resolves metadata in this order:

1. if a model is selected and has model-level mappings, use those
2. otherwise fall back to make-level mappings
3. deduplicate and merge metadata where both layers contribute

This resolution returns:

- attribute identity and type
- allowed option values
- min/max numeric bounds
- default selected option or raw default
- a `source` flag such as `model`, `make`, or `make_fallback`

## 7.4 Filtering Integration

`VehicleFilterService` uses dynamic attributes to build:

- option counts for select and multi-select attributes
- available min/max ranges for numeric attributes
- true-count availability for boolean attributes

The filter engine joins `vehicle_attribute_values` only when needed and excludes the current facet where necessary so counts stay meaningful.

## 7.5 Legacy Compatibility

The `Vehicle` model still exposes compatibility accessors for fields such as `vehicle_type`, `color`, `transmission`, and `fuel_type`. These are explicitly marked as deprecated catalog columns in code. They remain because:

- older data still exists in those columns
- some shipping and filtering logic still reads them
- migration to a pure attribute-only model is not yet complete

## 8. Model Code System

## 8.1 Current Source Of Truth

`model_code` is currently a column on `vehicles`. The catalog and API layers treat it as vehicle inventory metadata, not as a separate reference table.

Current behavior:

- admin/seller vehicle records can store a `model_code`
- the public catalog can facet and filter by `model_code`
- `/vehicle-catalog/models/{vehicleModel}/model-codes` returns distinct codes for vehicles in that model
- `/api/models/{model}/model-codes` returns facet counts from the published inventory dataset

## 8.2 What The System Does Not Currently Do

There is no clearly implemented app-layer mechanism that derives model code from chassis, VIN-like parsing, or a dedicated decoding service. In the current codebase, model code behaves as stored inventory metadata rather than computed catalog metadata.

That means:

- `vehicles.model_code` is the operational source of truth
- `auction_cars` does not maintain its own `model_code`
- auction intake does not appear to infer model code before vehicle creation
- any upstream decoding process, if it exists operationally, is outside the currently implemented Laravel code

## 8.3 Architectural Implication

Because model code is inventory-stored rather than normalized:

- there is no central canonical dictionary of valid model codes
- no FK guarantees consistency across vehicles of the same model
- filtering is simple and fast, but validation/governance is weaker

This is workable for now, but it is an obvious candidate for future normalization or decoding support if model code becomes a critical domain concept.

## 9. Intentional Snapshots And Derived Data

## 9.1 Order Party Snapshots

Orders store `consignee_*` and `remitter_*` fields so historical orders survive later profile edits or deletions. The display helpers on `Order` are snapshot-first and only fall back to the live relation for older or partially migrated records.

## 9.2 Locked Financial Snapshots

Orders also snapshot:

- vehicle price
- freight cost
- inspection fee
- insurance fee
- CIF total
- deposit percentage and minimum deposit amount

This keeps order history stable even when pricing rules, shipping rates, or deposit settings change.

## 9.3 Vehicle Compatibility Columns

Vehicles still carry compatibility copies of catalog identity fields and legacy attributes. This is deliberate during migration but increases long-term duplication.

## 9.4 Auction Derived Fields

`AuctionCar` derives two important fields in model events:

- `total`, calculated from the acquisition cost components
- `status`, derived from linked-vehicle presence and `selling_price`

The readiness flag on the linked vehicle is then synchronized from that derived state.

## 10. Current Issues And Technical Debt

### 10.1 Hybrid Vehicle Schema

The vehicle layer still mixes:

- normalized make/model IDs
- text make/model copies
- dynamic attributes
- deprecated direct spec columns

This improves compatibility today but weakens the clarity of the true source of truth for several fields.

### 10.2 Order And Shipment Overlap

Both orders and shipments hold overlapping logistics and date concepts. The UI compensates by merging them when presenting status, but the domain boundary is not as clean as it should be.

### 10.3 Payment Complexity

The combination of `payments`, `payment_allocations`, and `payment_histories` is more flexible than the current customer workflows strictly require. That may be justified if broader accounting features are planned, but today it increases reasoning complexity.

### 10.4 Team Tables Without Team-Centric Business Logic

Jetstream team tables are present, but the actual business app is role-based. This is not harmful, but it is dead weight unless team-scoped business logic is introduced.

### 10.5 Attribute Default Duplication

Model defaults are stored in both `model_attributes.default_value` JSON and `model_attribute_defaults`. That duplication is understandable from a UI/read-optimization angle, but the design would be stronger with one canonical storage approach.

### 10.6 Model Code Governance Gap

`vehicles.model_code` is useful for filtering, but there is no canonical registry, decoder, or validation source beyond the stored value itself.

### 10.7 Auction Cost Structure Is Wide Rather Than Normalized

`auction_cars` stores each cost component as a separate column. This is straightforward, but it makes extension harder than a normalized expense-line design would.

### 10.8 Missing Cross-Cutting Auditability

The current system has no general audit-log table for:

- financial edits
- shipment-status changes
- admin configuration changes
- auction-to-vehicle lifecycle decisions

## 11. Data Flow Summary

### Public Discovery

`vehicles + vehicle_attribute_values + makes + models -> VehicleFilterService -> public catalog pages and filter endpoints`

### Vehicle Authoring

`admin/seller forms -> vehicles + vehicle_images + vehicle_documents + vehicle_attribute_values`

### Auction Intake

`auction data-entry form -> auction_cars -> linked vehicle creation -> vehicles.is_ready_for_sale`

### Checkout And Purchase

`cart or direct purchase -> ShippingService + PricingService -> CustomerPurchaseService -> orders + order_items + payments`

### Payment Lifecycle

`payments -> PaymentService -> payment_allocations -> order paid_amount / remaining_balance / status`

### Shipment Lifecycle

`admin shipment updates -> shipments -> order timeline and process summary`

## 12. Future Improvements

### 12.1 Finish Vehicle Catalog Normalization

- make dynamic attributes the only source of truth for deprecated direct spec columns
- reduce dependence on text `make` and `model` copies once all consuming code is normalized

### 12.2 Clarify Order Vs Shipment Ownership

- decide which table is authoritative for departure, arrival, and local-dispatch milestones
- collapse or formalize overlapping fields

### 12.3 Simplify Payment Architecture If Broader Allocation Is Not Needed

- either fully lean into allocation/accounting workflows or reduce duplicate financial tables

### 12.4 Normalize Model Code If It Becomes Strategic

- introduce canonical model-code validation or a decode layer
- decide whether model code belongs at the inventory level, catalog level, or both

### 12.5 Normalize Auction Expenses

- replace wide acquisition-cost columns with child expense rows if auction accounting needs to expand

### 12.6 Add Operational Audit Logging

- log financial approvals, shipment edits, status changes, and critical admin changes

### 12.7 Revisit Teams

- either remove unused team scaffolding from the business narrative or adopt it deliberately for organization-level access boundaries

## 13. Final Architectural Assessment

This codebase is not a toy scaffold anymore. It has grown into a real business application with distinct subdomains and genuine operational complexity. Its strongest architectural characteristics are:

- practical service-layer orchestration for purchase, payment, shipping, and filtering
- a flexible catalog system that supports evolving vehicle metadata
- strong order snapshotting to preserve historical correctness
- a cleanly separated auction intake pipeline that does not corrupt the live inventory path

Its main architectural risks are not lack of capability, but overlap and duplication:

- hybrid vehicle sources of truth
- overlapping logistics fields across orders and shipments
- a payment model that is more complex than the current UI workflows suggest
- a few partially completed normalization steps

Even with those issues, the repository has a clear direction: it is moving from an earlier, more ad hoc CRUD marketplace design toward a more explicit catalog, pricing, and operations platform.