# Vehicle & Auction Architecture Analysis

## 1. Tables Overview

### 1.1 vehicles table

Current table shape is derived from these migrations:

- `2026_04_07_195624_create_vehicles_table.php`
- `2026_04_07_222633_add_extra_fields_to_vehicles_table.php`
- `2026_04_08_170327_add_discount_fields_to_vehicles_table.php`
- `2026_04_08_171020_add_type_and_stock_country_to_vehicles_table.php`
- `2026_04_09_000001_add_status_and_features_to_vehicles_table.php`
- `2026_04_10_000002_create_vehicle_catalog_tables.php`
- `2026_04_10_000003_change_engine_capacity_type_on_vehicles_table.php`
- `2026_04_15_000007_add_model_code_to_vehicles_table.php`
- `2026_04_15_000009_make_vehicle_seller_nullable.php`
- `2026_04_15_000011_drop_features_json_from_vehicles_table.php`
- `2026_04_16_000003_add_auction_fields_to_vehicles_table.php`
- `2026_04_21_000001_add_country_id_to_vehicles_table.php`

Historical note:

- `features_json` existed temporarily, but was dropped by `2026_04_15_000011_drop_features_json_from_vehicles_table.php`, so it is not part of the current table.

| Column | Purpose based on code | Status |
| --- | --- | --- |
| `id` | Primary key for all vehicle reads, route binding, carts, order items, seller/admin/public listing payloads. | ✅ actively used |
| `rec_no` | Generated reference number in `Vehicle::boot()`, used in seller search and public/admin detail display. | ✅ actively used |
| `seller_id` | Ownership field for seller inventory, seller authorization checks, seller dashboard counts, admin seller assignment. | ✅ actively used |
| `auction_car_id` | One-to-one link from vehicle to auction intake; used in create/update validation, joined price queries, eager loads, and lifecycle sync. | ✅ actively used |
| `make_id` | Normalized make key used by forms, validation, filters, facets, and create-from-auction checks. | ✅ actively used |
| `model_id` | Normalized model key used by forms, validation, filters, facets, and create-from-auction checks. | ✅ actively used |
| `make` | Denormalized make string used in search, display, payload building, and backfilled from `make_id` on save. | ✅ actively used |
| `model` | Denormalized model string used in search, display, payload building, and backfilled from `model_id` on save. | ✅ actively used |
| `model_code` | Used in admin filters, public search ordering, model-code facets, form selection, and seller/admin search. | ✅ actively used |
| `chassis` | Used in auction-link validation, search, display, and admin auction payloads for linked vehicles. | ✅ actively used |
| `grade` | Used in search and displayed on public, seller, admin, and customer order views. | ✅ actively used |
| `registration_year` | Stored through forms and displayed on public and customer order detail views. | ✅ actively used |
| `manufacture_year` | Stored through forms and displayed on public and customer order detail views. | ✅ actively used |
| `year` | Core listing/search/filter field across public, seller, and admin flows. | ✅ actively used |
| `mileage` | Core listing/filter/display field across public, seller, admin, and purchase flows. | ✅ actively used |
| `price` | Compatibility price column; used directly in forms and as fallback in `COALESCE(...)` queries and accessor-backed reads. | ⚠️ partially used |
| `discount_percentage` | Validated and fillable, but no active runtime calculation or UI path was found using it in the audited controllers/services/Vue flows. | ❌ unused / redundant |
| `discount_amount` | Used by `price_with_discount`, cart, checkout, order snapshots, and public/admin/seller UI discount display. | ✅ actively used |
| `is_featured` | Used in admin listing counts and filters; stored through vehicle write flows. | ✅ actively used |
| `is_clearance` | Used in admin listing counts and filters; stored through vehicle write flows. | ✅ actively used |
| `condition` | Used in forms and displayed on public detail and listing cards. | ✅ actively used |
| `vehicle_type` | Used by shipping calculations and public filters, but also duplicated via dynamic `Body Type` attribute access. | ⚠️ partially used |
| `color` | Stored directly, validated directly, but accessor prefers dynamic attribute value when available. | ⚠️ partially used |
| `transmission` | Stored directly, validated directly, but accessor prefers dynamic attribute value when available. | ⚠️ partially used |
| `fuel_type` | Stored directly, validated directly, but accessor prefers dynamic attribute value when available. | ⚠️ partially used |
| `engine_capacity` | Stored directly, validated directly, but accessor and sync logic treat dynamic `Engine CC` as preferred source. | ⚠️ partially used |
| `length_m` | Stored in forms and displayed in public and customer order detail views. | ✅ actively used |
| `width_m` | Stored in forms and displayed in public and customer order detail views. | ✅ actively used |
| `height_m` | Stored in forms and displayed in public and customer order detail views. | ✅ actively used |
| `volume_m3` | Stored in forms and displayed in public and customer order detail views. | ✅ actively used |
| `seats` | Stored in forms and displayed in public and customer order detail views. | ✅ actively used |
| `doors` | Stored in forms and displayed in public and customer order detail views. | ✅ actively used |
| `location` | Used in forms, public listing cards, checkout/purchase location display, and fallback display when country is missing. | ✅ actively used |
| `country_id` | Used in normalization, listing filters, country hydration, admin auction filters, and vehicle form submission. | ✅ actively used |
| `stock_country` | Legacy fallback used in public display, checkout/purchase display, order snapshot source, and country normalization fallback. | ⚠️ partially used |
| `primary_image` | Used for uploads, media display, and `primary_image_url` generation. | ✅ actively used |
| `stock_status` | Primary availability field for public listing, seller/admin counts, and purchase gating. | ✅ actively used |
| `status` | Primary listing lifecycle field for publication, seller/admin filtering, and reservation state. | ✅ actively used |
| `is_ready_for_sale` | Synced from auction completion and surfaced in admin auction intake UI as linked-vehicle readiness. | ✅ actively used |
| `created_at` | Used for admin/seller/public/API listing sort order and implicit `latest()` calls on non-joined vehicle relations. | ✅ actively used |
| `updated_at` | Maintained automatically by Eloquent, but not used materially in the audited vehicle flows. | ⚠️ partially used |

Additional observations from code usage:

- `Vehicle::getPriceAttribute()` prefers `auction_cars.selling_price` over raw `vehicles.price` when `auction_car_id` is set.
- `Vehicle::getPriceWithDiscountAttribute()` subtracts only `discount_amount`; `discount_percentage` is not used in that calculation.
- `vehicle_type`, `color`, `transmission`, `fuel_type`, and `engine_capacity` are in a hybrid state: still stored on `vehicles`, but also read from the attribute system via accessors.

### 1.2 auction_cars table

Current table shape is derived from these migrations:

- `2026_04_16_000002_create_auction_cars_table.php`
- `2026_04_16_000004_add_additional_fields_to_auction_cars_table.php`
- `2026_04_16_000005_refine_auction_cars_business_fields.php`
- `2026_04_17_000001_update_auction_house_and_cost_fields_on_auction_cars_table.php`

Historical note:

- `make` and `model` string columns originally existed on `auction_cars`, but were removed by `2026_04_16_000005_refine_auction_cars_business_fields.php`.
- `vehicle_cost` was renamed to `selling_price` in the same migration.

| Column | Purpose based on code | Status |
| --- | --- | --- |
| `id` | Primary key for intake rows, linking to vehicles, batch updates, create-from-auction flow, and route model binding. | ✅ actively used |
| `make_id` | Intake-side normalized make key used by validation, linkage eligibility, filters, and vehicle creation constraints. | ✅ actively used |
| `model_id` | Intake-side normalized model key used by validation, linkage eligibility, filters, and vehicle creation constraints. | ✅ actively used |
| `chassis_no` | Unique intake chassis field used for validation, linking rules, create-vehicle gating, and auction filters. | ✅ actively used |
| `purchase_date` | Stored and serialized in admin auction create/edit/list flows. | ✅ actively used |
| `auction_price` | Required intake purchase amount, included in total derivation and admin auction UI. | ✅ actively used |
| `auction_fee` | Intake cost component used in total derivation and admin auction UI. | ✅ actively used |
| `inspection` | Intake cost component used in total derivation and admin auction UI. | ✅ actively used |
| `transportation` | Intake cost component used in total derivation and admin auction UI. | ✅ actively used |
| `h_charge` | Intake cost component used in total derivation and admin auction UI. | ✅ actively used |
| `van` | Intake cost component used in total derivation and admin auction UI. | ✅ actively used |
| `insurance` | Intake cost component used in total derivation and admin auction UI. | ✅ actively used |
| `freight` | Intake cost component used in total derivation, eager-loaded into vehicle payloads, and surfaced in admin/public/seller detail payloads. | ✅ actively used |
| `extra` | Intake cost component used in total derivation and admin auction UI. | ✅ actively used |
| `shipper` | Stored, validated, and shown in admin auction UI, but not included in `AuctionCar::calculateTotal()`. | ⚠️ partially used |
| `storage` | Stored, validated, and shown in admin auction UI, but not included in `AuctionCar::calculateTotal()`. | ⚠️ partially used |
| `total` | Derived intake total used in filters, serialized payloads, and eager-loaded vehicle detail payloads. | ✅ actively used |
| `selling_price` | Current selling-price field used by vehicle accessor, pricing service, public/admin/seller UI, filters, and auction completion state. | ✅ actively used |
| `vessel` | Stored and serialized in admin auction create/edit/list flows. | ✅ actively used |
| `sailing_date` | Used in validation, serialized payloads, and admin auction date filters. | ✅ actively used |
| `region` | Stored and filtered in admin auction list flow. | ✅ actively used |
| `city` | Stored and serialized in admin auction list/edit payloads. | ✅ actively used |
| `auction_house` | Stored and filtered in admin auction list flow. | ✅ actively used |
| `admin_notes` | Stored and serialized in admin auction flows. | ✅ actively used |
| `status` | Derived `pending/completed` field used in intake summary counts and linked sale-readiness logic. | ✅ actively used |
| `created_at` | Used by `latest()` ordering in `Admin\AuctionCarController@index` and serialized in payloads. | ✅ actively used |
| `updated_at` | Serialized in auction payloads, but not used as a major query/filter field in audited flows. | ⚠️ partially used |

Additional observations from code usage:

- `AuctionCar::syncDerivedFields()` recalculates `status` and `total` on every save.
- `status` becomes `completed` only when the row has a linked vehicle and `selling_price` is not null.
- `shipper` and `storage` are stored and validated, but are not part of `calculateTotal()`.

## 2. Relationship Mapping

### `vehicles -> auction_cars`

Foreign key:

- `vehicles.auction_car_id -> auction_cars.id`

Eloquent relations:

- `Vehicle::auctionCar(): BelongsTo`
- `AuctionCar::vehicle(): HasOne`

How they are loaded:

- Eager-loaded in public list/detail: `PublicController` with `auctionCar:id,selling_price,total,freight,auction_price`
- Eager-loaded in API list/detail: `Api\VehicleController`
- Eager-loaded in admin vehicle list/show/edit: `Admin\VehicleController`
- Eager-loaded in seller vehicle list/show/edit and seller dashboard: `Seller\VehicleController`, `Seller\DashboardController`
- Eager-loaded in cart and checkout through nested relation: `vehicle.auctionCar`
- Lazy-loaded inside `Vehicle::getPriceAttribute()` when not already loaded, using `select(['id', 'selling_price'])`

Actual runtime role:

- `vehicles` remains the root record for most queries.
- `auction_cars` is attached when pricing or intake metadata is needed.
- For auction-linked vehicles, most price reads prefer `auction_cars.selling_price`.

### `vehicles -> attributes`

Foreign keys and pivot structure:

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

Eloquent relations:

- `Vehicle::attributeValues(): HasMany`
- `Vehicle::attributes(): BelongsToMany`
- `VehicleAttributeValue::vehicle(): BelongsTo`
- `VehicleAttributeValue::attribute(): BelongsTo`
- `VehicleAttributeValue::value(): BelongsTo`

How they are loaded:

- Runtime display paths usually eager-load `attributeValues.attribute` and `attributeValues.value`
- Public list/detail: eager-loaded in `PublicController`
- API list/detail: eager-loaded in `Api\VehicleController`
- Admin show/edit: eager-loaded in `Admin\VehicleController`
- Seller show/edit: eager-loaded in `Seller\VehicleController`

Important actual behavior:

- The `attributes()` many-to-many relation exists, but the audited runtime code more often uses `attributeValues()` with nested `attribute` and `value` relations.
- Dynamic attributes are the actual stored source for configurable specs and boolean features.
- Several legacy columns on `vehicles` still duplicate attribute-backed data.

### `vehicles -> orders / cart / checkout`

Direct and indirect links found in code:

- `Cart::vehicle(): BelongsTo(Vehicle::class)`
- `OrderItem::vehicle(): BelongsTo(Vehicle::class)`
- `Vehicle::orders(): HasMany(Order::class)` exists in the model

Actual schema/runtime note:

- `orders.vehicle_id` was removed by `2026_04_08_180433_remove_vehicle_id_from_orders_table.php`.
- The current normalized purchase flow uses `order_items.vehicle_id`, not `orders.vehicle_id`.
- `Vehicle::orders()` therefore does not match the current schema shape.
- `Customer\OrderController@store` and `Api\OrderController@store` still attempt to write `vehicle_id` directly onto `orders`, which is inconsistent with the migration history.

How vehicle data is loaded in cart/checkout/order flows:

- Cart: `user->cart()->with('vehicle.auctionCar', 'vehicle.images')`
- Checkout: `user->cart()->with('vehicle.auctionCar')`
- Order detail: `orders -> items -> vehicle -> attributeValues / images / documents`
- Purchase service: locks `Vehicle::query()->with('auctionCar')->lockForUpdate()`

## 3. Data Flow Analysis

### 3.1 Vehicle Creation Flow

Where vehicles are created:

- `Admin\VehicleController@store`
- `Seller\VehicleController@store`
- legacy path: `VehicleController@store`

What fields are filled in current main flow:

- Seller/admin forms submit vehicle base fields from `VehicleForm.vue`
- Core fields include: seller, make/model ids, make/model strings, model_code, year, price, mileage, chassis, condition, country, location, status, stock status, feature flags, images, and dynamic attributes
- Additional optional direct fields include grade, registration/manufacture year, color, transmission, fuel type, dimensions, seats, and doors

Whether data comes from auction or manually:

- Manual create: fields are entered directly into the vehicle form
- Auction-linked create: admin enters via vehicle form, but `auction_car_id` preloads and locks:
  - `make_id`
  - `model_id`
  - `make`
  - `model`
  - `chassis`
  - initial `price` seeded from `auction_cars.selling_price`

Main flow behavior from code:

1. `StoreVehicleRequest` validates request data.
2. Request normalizes `make`, `model`, `country_id`, `stock_country`, and `dynamic_attributes`.
3. If `auction_car_id` is present, request loads the auction row and enforces make/model/chassis consistency.
4. If `auction_cars.selling_price` exists, request copies it into `vehicles.price`.
5. Controller stores uploaded primary image and additional images.
6. `Vehicle::create()` writes the row.
7. `syncDynamicAttributes()` replaces the vehicle's attribute rows.
8. Vehicle save hooks trigger auction lifecycle sync.

Legacy flow note:

- `app/Http/Controllers/VehicleController.php` still provides a simpler create/update path using direct vehicle fields only, without the newer catalog and auction-link rules used in admin/seller controllers.

### 3.2 Auction Flow

How `auction_cars` is created:

- `Admin\AuctionCarController@store`
- `Admin\AuctionCarController@batchUpdate` for spreadsheet-like intake entry
- `Admin\AuctionCarController@update`

How it is linked to vehicles:

- Admin vehicle create page accepts `auction_car_id` as input
- `Admin\VehicleController@create` loads the auction row and blocks vehicle creation if:
  - a vehicle already exists
  - make/model/chassis data is incomplete
- When a vehicle is created with `auction_car_id`, the unique constraint on `vehicles.auction_car_id` enforces one-to-one linkage

What data `auction_cars` owns in actual code:

- Purchase price and cost stack:
  - `auction_price`
  - `auction_fee`
  - `inspection`
  - `transportation`
  - `h_charge`
  - `van`
  - `insurance`
  - `freight`
  - `extra`
  - `shipper`
  - `storage`
  - `total`
- Selling-side price:
  - `selling_price`
- Logistics and intake metadata:
  - `purchase_date`
  - `vessel`
  - `sailing_date`
  - `region`
  - `city`
  - `auction_house`
  - `admin_notes`
- Sale-readiness state:
  - `status`

Linkage side effects:

- `AuctionCar::syncLinkedVehicleSaleReadiness()` updates `vehicles.is_ready_for_sale`
- `Vehicle::syncAuctionCarLifecycle()` recalculates auction `status` and `total` after vehicle changes

## 4. Runtime Usage (CRITICAL SECTION)

### 4.1 Admin Panel

#### Vehicle listing

Controller:

- `app/Http/Controllers/Admin/VehicleController.php`

Fields actually used in listing/filter/sort payloads:

- Search: `vehicles.make`, `vehicles.model`, `vehicles.model_code`, `vehicles.chassis`
- Filters: `vehicles.make_id`, `vehicles.model_id`, `vehicles.model_code`, `vehicles.stock_status`, `vehicles.status`, `vehicles.year`
- Price filter: `COALESCE(listing_auction_cars.selling_price, vehicles.price)`
- Status counts: `vehicles.stock_status`, `vehicles.status`, `vehicles.is_featured`, `vehicles.is_clearance`
- Sort: `vehicles.make`, `vehicles.model`, `vehicles.created_at`
- Returned display fields: `make`, `model`, `make_id`, `model_id`, `model_code`, `chassis`, `year`, `mileage`, `stock_status`, `status`, `is_featured`, `is_clearance`, `price`, `auction_car.selling_price`

#### Vehicle detail

Controller:

- `Admin\VehicleController@show`

Loaded relations:

- `auctionCar`
- `seller`
- `images`
- `attributeValues.attribute`
- `attributeValues.value`

Fields actually used in admin vehicle pages:

- Identity: make, model, model_code, rec_no, chassis, year
- Media: `primary_image`, `images`
- Listing state: `status`, `stock_status`, `is_featured`, `is_clearance`
- Pricing: `auction_car.selling_price`, `price`, `discount_amount`, `price_with_discount`
- Location: `stock_country`, `country`
- Specs: grade, vehicle_type, color, transmission, fuel_type, engine_capacity, dimensions, seats, doors
- Dynamic attributes and feature labels

#### Filters

Admin listing filters rely on:

- direct `vehicles` columns for text/state/year filters
- joined `auction_cars` for price range only

#### Forms (create/edit)

Form component:

- `resources/js/Components/Vehicles/VehicleForm.vue`

Actual admin form fields:

- Seller assignment
- Make, model, model code
- Year, price, mileage, chassis
- Condition
- Country and location
- Listing status and stock status
- Featured and clearance flags
- Dynamic specification attributes
- Boolean feature attributes
- Primary image and additional images

Auction-linked admin create behavior:

- Creates from `auction_car_id`
- Locks make, model, chassis, and price input behavior in the form
- Displays intake summary on create page

### 4.2 Seller Side

Primary files:

- `app/Http/Controllers/Seller/VehicleController.php`
- `app/Http/Controllers/Seller/DashboardController.php`
- seller vehicle Vue pages

What seller sees from vehicle data:

- Seller inventory list from `vehicles`
- Seller detail page from `vehicle` with relations
- Dashboard recent vehicles from `user->vehicles()`

What comes from `vehicles` vs `auction_cars`:

- `vehicles` supplies ownership, stock state, make/model/year/chassis, images, specs, and most display fields
- `auction_cars` supplies `selling_price`, `total`, `freight`, and `auction_price` when eager-loaded

Seller list/detail price behavior:

- Seller list UI displays `vehicle.auction_car?.selling_price ?? vehicle.price`
- Seller detail UI displays both auction-backed base price and `price_with_discount`
- Seller controller sort-by-price uses `COALESCE(listing_auction_cars.selling_price, vehicles.price)`

Seller status behavior:

- Seller listing filters use `vehicles.status` and `vehicles.stock_status`
- Seller dashboard order counts use `orders.status`, not vehicle status

### 4.3 Customer / Public Side

#### Listing page

Primary files:

- `app/Http/Controllers/PublicController.php`
- `resources/js/Pages/Public/Home.vue`

Actual fields used:

- Listing cards show `primary_image_url`, year, country/location, make, model, location, price, `discount_amount`, mileage, engine_capacity, condition, vehicle_type, fuel_type, transmission
- Filters use make/model/model_code/country/price/mileage/year plus attribute facets

Pricing source:

- UI display uses `vehicle.auction_car?.selling_price ?? vehicle.price`
- Range/filter query uses `COALESCE(listing_auction_cars.selling_price, vehicles.price)`

#### Detail page

Primary files:

- `PublicController@show`
- `resources/js/Pages/Public/VehicleShow.vue`

Actual fields used:

- Price header uses `auction_car.selling_price ?? price`
- Current selling price line uses `price_with_discount || auction_car.selling_price || price`
- Discount display uses `discount_amount`
- Detail grid uses grade, rec_no, chassis, registration_year, manufacture_year, condition, vehicle_type, color, transmission, fuel_type, engine_capacity, seats, doors, stock country, location, length, width, height, volume, stock status
- Feature labels come from boolean attributes

#### Checkout / cart

Primary files:

- `Customer\CartController`
- `Customer\CheckoutController`
- `CustomerPurchaseService`
- `PricingService`
- `resources/js/Pages/Customer/Cart/Index.vue`
- `resources/js/Pages/Customer/Checkout/Index.vue`
- `resources/js/Pages/Customer/Purchase/Show.vue`

Actual pricing source:

- Cart total uses `vehicle.price_with_discount`
- Cart line display uses `vehicle.auction_car?.selling_price ?? vehicle.price`
- Checkout line calculation in Vue uses `price_with_discount ?? auction_car.selling_price ?? price`
- `PricingService` resolves selling price from auction price first, then `vehicle.price`
- `CustomerPurchaseService::createVehiclePurchase()` locks to `lockedVehicle->price_with_discount`
- `CustomerPurchaseService::getPurchasePageData()` previews `auctionCar.selling_price ?? vehicle.price_with_discount`

## 5. Pricing & Financial Source of Truth

### Where `vehicles.price` is used

Direct and fallback usage found in code:

- `Vehicle::getPriceAttribute()` fallback when no auction selling price exists
- `VehicleFilterService` price filter and range expression: `COALESCE(listing_auction_cars.selling_price, vehicles.price)`
- `VehicleCatalogAttributeService::modelInsights()` with `COALESCE(pricing_auction_cars.selling_price, vehicles.price)`
- Seller vehicle sort by price
- Admin vehicle min/max price filters
- Public/admin/seller/customer Vue price fallbacks
- Vehicle create/update forms still validate and submit `price`
- Store/update requests copy auction selling price back into `price` for linked vehicles

### Where `auction_cars.selling_price` is used

- `Vehicle::getPriceAttribute()` preferred source
- `PricingService::resolveSellingPrice()` preferred source
- `CustomerPurchaseService::getPurchasePageData()` preview source
- `Customer\OrderController@store` and `Api\OrderController@store` first price source
- Public/admin/seller/API eager-loaded payloads
- Seller/admin sorting and filtering through `COALESCE(...)`
- Admin auction intake validation, payloads, and filters
- Public/admin/seller/customer Vue display expressions

### Where each price source is used by category

#### Filters

- `VehicleFilterService`: `COALESCE(listing_auction_cars.selling_price, vehicles.price)`
- `Admin\VehicleController`: same `COALESCE(...)` for min/max price
- `VehicleCatalogAttributeService`: same `COALESCE(...)` for model price insights
- `Admin\AuctionCarController`: direct `selling_price` range filters on `auction_cars`

#### Checkout

- `PricingService` and purchase flows primarily work from `price_with_discount`
- `price_with_discount` is derived from `Vehicle::price`, which itself may resolve to `auction_cars.selling_price`

#### UI display

- Most public/admin/seller/customer UI shows base price as `auction_car?.selling_price ?? price`
- Discounted price display uses `price_with_discount`

### Conflicts or duplication

- The same sell-side price exists in two places for auction-linked vehicles:
  - `vehicles.price`
  - `auction_cars.selling_price`
- The code prefers `auction_cars.selling_price` in most reads, but still writes and validates `vehicles.price`
- Single-vehicle preview and locked purchase pricing do not use identical precedence rules:
  - preview can show raw `auction_cars.selling_price`
  - locked purchase uses `price_with_discount`
- `discount_percentage` exists but is not part of the active runtime price calculation path

## 6. Query & Filter Analysis

### Queries using `vehicles.price`

- `app/Services/VehicleFilterService.php`
  - `COALESCE(listing_auction_cars.selling_price, vehicles.price)`
- `app/Services/VehicleCatalogAttributeService.php`
  - `COALESCE(pricing_auction_cars.selling_price, vehicles.price)`
- `app/Http/Controllers/Admin/VehicleController.php`
  - min/max price filters with `COALESCE(...)`
- `app/Http/Controllers/Seller/VehicleController.php`
  - price sort with `COALESCE(...)`
- Multiple Vue files display fallback price as `auction_car?.selling_price ?? vehicle.price`

### Queries using `auction_cars.selling_price`

- `Vehicle::getPriceAttribute()` lazy-load select
- `PricingService::resolveSellingPrice()`
- `CustomerPurchaseService::getPurchasePageData()`
- `Customer\OrderController@store`
- `Api\OrderController@store`
- `Admin\AuctionCarController` selling-price filters on intake rows
- Admin/seller/public/API eager-load lists of `auctionCar:id,selling_price,total,freight,auction_price`
- Vue display and form prefill paths

### Queries using `status` relevant to the ambiguity issue

Joined vehicle + auction queries where `status` could be ambiguous if unqualified:

- `app/Http/Controllers/Admin/VehicleController.php`
  - joins `auction_cars as listing_auction_cars`
  - now filters `vehicles.status` and `vehicles.stock_status`
  - now sorts with `vehicles.created_at`
- `app/Http/Controllers/Seller/VehicleController.php`
  - joins `auction_cars as listing_auction_cars`
  - now filters `vehicles.status` and `vehicles.stock_status`
  - now sorts with `latest('vehicles.created_at')`
- `app/Services/VehicleFilterService.php`
  - when joining `auction_cars`, it already uses explicit `vehicles.status` and `vehicles.stock_status`

Other `status` uses found in audited code are not part of the vehicle/auction ambiguity surface:

- `Admin\AuctionCarController`: `status` means `auction_cars.status`
- `Seller\DashboardController`: bare `status` applies to `orders.status`
- `Seller\OrderController`: bare `status` applies to orders
- Payment services/controllers: `status` applies to payments

### Joins, where clauses, ambiguous columns

Joined vehicle/auction query sites:

- `VehicleFilterService::applySellingPriceJoin()`
  - `leftJoin('auction_cars as listing_auction_cars', 'vehicles.auction_car_id', '=', 'listing_auction_cars.id')`
- `VehicleCatalogAttributeService::modelInsights()`
  - `leftJoin('auction_cars as pricing_auction_cars', 'vehicles.auction_car_id', '=', 'pricing_auction_cars.id')`
- `Admin\VehicleController@index`
  - `leftJoin('auction_cars as listing_auction_cars', 'vehicles.auction_car_id', '=', 'listing_auction_cars.id')`
- `Seller\VehicleController@index`
  - same join

Ambiguous columns in principle when `vehicles` and `auction_cars` are joined:

- `status`
- `created_at`
- `updated_at`

Current code state in the audited joined vehicle paths:

- `Admin\VehicleController@index`: explicit table qualification is in place
- `Seller\VehicleController@index`: explicit table qualification is in place
- `VehicleFilterService`: explicit table qualification is in place

## 7. Attribute & Specs Mapping

### Specs stored directly in `vehicles`

- `vehicle_type`
- `color`
- `transmission`
- `fuel_type`
- `engine_capacity`
- `length_m`
- `width_m`
- `height_m`
- `volume_m3`
- `seats`
- `doors`
- `grade`
- `registration_year`
- `manufacture_year`

### Specs stored via attributes system

Confirmed from `Vehicle` model accessors and attribute sync logic:

- `Body Type` -> backs `vehicle_type`
- `Color` -> backs `color`
- `Transmission` -> backs `transmission`
- `Fuel Type` -> backs `fuel_type`
- `Engine CC` -> backs `engine_capacity`
- Boolean features -> surfaced via `feature_labels`

### Identified duplication

- `fuel_type`: direct column plus dynamic attribute-backed accessor
- `transmission`: direct column plus dynamic attribute-backed accessor
- `engine_capacity`: direct column plus dynamic attribute-backed accessor and sync-back
- `vehicle_type`: direct column plus dynamic `Body Type`
- `color`: direct column plus dynamic `Color`

### Dimensions

Current code stores dimensions directly in `vehicles` only:

- `length_m`
- `width_m`
- `height_m`
- `volume_m3`
- `seats`
- `doors`

No audited runtime path stores these six fields through the attributes system.

## 8. Redundancy & Risk Analysis

### Duplicate fields between `vehicles` and `auction_cars`

- Sell-side price meaning overlap:
  - `vehicles.price`
  - `auction_cars.selling_price`
- Catalog identity overlap:
  - `vehicles.make_id`, `vehicles.model_id`
  - `auction_cars.make_id`, `auction_cars.model_id`
- Human-readable identity overlap:
  - `vehicles.make`, `vehicles.model`
  - auction side now relies on relations, but the linked auction row still carries the same business identity
- Chassis overlap:
  - `vehicles.chassis`
  - `auction_cars.chassis_no`

### Fields that should NOT exist in `vehicles` if judged purely by current business ownership

These currently behave like duplicated or compatibility fields rather than clear listing-owned fields:

- `price` for auction-linked vehicles
- `vehicle_type` as a hard column alongside attribute-backed `Body Type`
- `color` as a hard column alongside attribute-backed `Color`
- `transmission` as a hard column alongside attribute-backed `Transmission`
- `fuel_type` as a hard column alongside attribute-backed `Fuel Type`
- `engine_capacity` as a hard column alongside attribute-backed `Engine CC`

### Fields that are conflicting

- `vehicles.price` vs `auction_cars.selling_price`
- `vehicles.stock_country` vs `vehicles.country_id`
- `vehicles.vehicle_type` vs dynamic `Body Type`
- `vehicles.color` vs dynamic `Color`
- `vehicles.transmission` vs dynamic `Transmission`
- `vehicles.fuel_type` vs dynamic `Fuel Type`
- `vehicles.engine_capacity` vs dynamic `Engine CC`

### Current breaking or risky inconsistencies visible from code

- `Vehicle::orders()` still assumes a direct vehicle-to-order link, but `orders.vehicle_id` was removed by migration.
- `Customer\OrderController@store` and `Api\OrderController@store` still write `vehicle_id` directly onto `orders` despite that migration.
- `discount_percentage` exists in schema and validation, but was not found in active runtime pricing logic.
- `shipper` and `storage` are part of `auction_cars`, but excluded from `AuctionCar::calculateTotal()`.
- Cart totals use `price_with_discount`, then subtract `discount_amount` again when computing `subtotal`.

## 9. Dependency Map

### Files depending on `vehicles` table

#### Controllers

- `app/Http/Controllers/PublicController.php`
- `app/Http/Controllers/Api/VehicleController.php`
- `app/Http/Controllers/Admin/VehicleController.php`
- `app/Http/Controllers/Seller/VehicleController.php`
- `app/Http/Controllers/Seller/DashboardController.php`
- `app/Http/Controllers/Admin/DashboardController.php`
- `app/Http/Controllers/VehicleCatalogController.php`
- `app/Http/Controllers/Customer/CartController.php`
- `app/Http/Controllers/Customer/CheckoutController.php`
- `app/Http/Controllers/Customer/OrderController.php`
- `app/Http/Controllers/Api/OrderController.php`
- `app/Http/Controllers/Api/ShippingCalculationController.php`
- `app/Http/Controllers/VehicleController.php`

#### Services

- `app/Services/VehicleFilterService.php`
- `app/Services/VehicleCatalogAttributeService.php`
- `app/Services/PricingService.php`
- `app/Services/CustomerPurchaseService.php`
- `app/Services/ShippingService.php`
- `app/Services/VehicleFormOptionsService.php`

#### Requests

- `app/Http/Requests/StoreVehicleRequest.php`
- `app/Http/Requests/UpdateVehicleRequest.php`

#### Filters / query builders

- `app/Services/VehicleFilterService.php`
- `app/Http/Controllers/Admin/VehicleController.php`
- `app/Http/Controllers/Seller/VehicleController.php`
- `app/Http/Controllers/VehicleCatalogController.php`

#### Vue components / pages

- `resources/js/Components/Vehicles/VehicleForm.vue`
- `resources/js/Pages/Public/Home.vue`
- `resources/js/Pages/Public/VehicleShow.vue`
- `resources/js/Pages/Admin/Vehicles/Index.vue`
- `resources/js/Pages/Admin/Vehicles/Show.vue`
- `resources/js/Pages/Admin/Vehicles/Create.vue`
- `resources/js/Pages/Seller/Vehicles/Index.vue`
- `resources/js/Pages/Seller/Vehicles/Show.vue`
- `resources/js/Pages/Seller/Vehicles/Create.vue`
- `resources/js/Pages/Seller/Vehicles/Edit.vue`
- `resources/js/Pages/Seller/Dashboard.vue`
- `resources/js/Pages/Customer/Cart/Index.vue`
- `resources/js/Pages/Customer/Checkout/Index.vue`
- `resources/js/Pages/Customer/Purchase/Show.vue`
- `resources/js/Pages/Customer/Orders/Show.vue`
- `resources/js/Pages/Admin/Sellers/Index.vue`
- legacy UI path: `resources/js/Pages/Vehicles/Create.vue`

### Files depending on `auction_cars` table

#### Controllers

- `app/Http/Controllers/Admin/AuctionCarController.php`
- `app/Http/Controllers/Admin/VehicleController.php`
- `app/Http/Controllers/Seller/VehicleController.php`
- `app/Http/Controllers/Seller/DashboardController.php`
- `app/Http/Controllers/PublicController.php`
- `app/Http/Controllers/Api/VehicleController.php`
- `app/Http/Controllers/Customer/OrderController.php`
- `app/Http/Controllers/Api/OrderController.php`

#### Services

- `app/Services/VehicleFilterService.php`
- `app/Services/VehicleCatalogAttributeService.php`
- `app/Services/PricingService.php`
- `app/Services/CustomerPurchaseService.php`

#### Requests

- `app/Http/Requests/StoreAuctionCarRequest.php`
- `app/Http/Requests/UpdateAuctionCarRequest.php`
- `app/Http/Requests/StoreVehicleRequest.php`
- `app/Http/Requests/UpdateVehicleRequest.php`

#### Filters / query builders

- `app/Http/Controllers/Admin/AuctionCarController.php`
- `app/Services/VehicleFilterService.php`
- `app/Services/VehicleCatalogAttributeService.php`
- `app/Http/Controllers/Admin/VehicleController.php`
- `app/Http/Controllers/Seller/VehicleController.php`

#### Vue components / pages

- `resources/js/Components/Admin/AuctionCarForm.vue`
- `resources/js/Components/Admin/AuctionIntakeTable.vue`
- `resources/js/Components/Vehicles/VehicleForm.vue`
- `resources/js/Pages/Admin/DataEntry/AuctionList.vue`
- `resources/js/Pages/Admin/DataEntry/AuctionCreate.vue`
- `resources/js/Pages/Admin/DataEntry/AuctionEdit.vue`
- `resources/js/Pages/Admin/Vehicles/Index.vue`
- `resources/js/Pages/Admin/Vehicles/Show.vue`
- `resources/js/Pages/Admin/Vehicles/Create.vue`
- `resources/js/Pages/Admin/Sellers/Index.vue`
- `resources/js/Pages/Seller/Vehicles/Index.vue`
- `resources/js/Pages/Seller/Vehicles/Show.vue`
- `resources/js/Pages/Seller/Dashboard.vue`
- `resources/js/Pages/Public/Home.vue`
- `resources/js/Pages/Public/VehicleShow.vue`
- `resources/js/Pages/Customer/Cart/Index.vue`
- `resources/js/Pages/Customer/Checkout/Index.vue`

## 10. Final Summary (NO FIXES YET)

### What `vehicles` table is ACTUALLY responsible for

Based on the audited code, `vehicles` is currently the operational root record for:

- public marketplace listings
- seller inventory management
- admin vehicle management
- cart and checkout item identity
- shipping lookup input via vehicle-facing fields like `vehicle_type`
- order item linkage
- media, listing state, and most user-facing display data

It is also still carrying compatibility fields that overlap with newer systems:

- compatibility price in `price`
- legacy country text in `stock_country`
- legacy descriptive/spec fields that now overlap with dynamic attributes

### What `auction_cars` table is ACTUALLY responsible for

Based on the audited code, `auction_cars` is currently the intake and commercial-cost record for:

- purchase-side price and cost breakdown
- intended selling price for auction-backed listings
- sale-readiness completion state
- auction logistics metadata
- admin batch intake and edit flows

It is not the root query table for marketplace runtime. Instead, it is attached to vehicles when pricing or intake details are needed.

### Where the system is inconsistent

- Selling price is duplicated across `vehicles.price` and `auction_cars.selling_price`.
- Auction-linked read paths mostly prefer `auction_cars.selling_price`, but write paths still persist `vehicles.price`.
- Several vehicle spec fields exist both as physical columns and dynamic attributes.
- `stock_country` and `country_id` represent the same business concept in two different forms.
- There is a schema/model/controller mismatch around direct vehicle-to-order linkage after `orders.vehicle_id` was removed.
- Ambiguity risk exists whenever `vehicles` joins `auction_cars` because both tables contain `status`, `created_at`, and `updated_at`; the audited vehicle-list query paths now qualify those columns explicitly.

### What belongs to `vehicles` vs `auction_cars` based on actual runtime behavior

- `vehicles` currently owns listing identity, listing state, seller ownership, media, public display fields, and transactional item identity.
- `auction_cars` currently owns intake economics, selling price for auction-backed stock, and readiness metadata.

### What appears removable later based on current duplication only

These are not recommendations yet, only duplication candidates visible from actual code:

- `vehicles.price` for auction-linked vehicles
- `vehicles.stock_country` where `country_id` is present
- direct catalog/spec columns duplicated by dynamic attributes:
  - `vehicle_type`
  - `color`
  - `transmission`
  - `fuel_type`
  - `engine_capacity`

### What is currently breaking or duplicated

- Direct vehicle-to-order assumptions remain in code after the schema moved to `order_items`
- sell-side price exists in both core tables
- attribute-backed specs are still duplicated on `vehicles`
- `discount_percentage` exists without active runtime pricing use in the audited flows