# Vehicle & Auction System Analysis

## 1. Overview

The current system uses two different records to represent one commercial vehicle lifecycle:

- `vehicles` is the listing-facing record. It drives public browsing, seller inventory, admin vehicle management, cart, checkout, order items, shipping calculations, and most UI payloads.
- `auction_cars` is the purchase-intake record. It stores auction-side costs, sale readiness inputs, and the intended selling price for auction-linked inventory.

In actual runtime behavior, the application starts most read queries from `vehicles` and then selectively joins or eager-loads `auction_cars` when pricing or auction metadata is needed.

The current design is not a clean split between listing data and auction data:

- `vehicles` still stores legacy pricing and descriptive columns directly.
- `auction_cars` stores the cost stack and `selling_price`.
- `Vehicle::getPriceAttribute()` prefers `auction_cars.selling_price` over `vehicles.price` when a linked auction record exists.
- vehicle create and update flows still write `price` into `vehicles`, even for auction-linked vehicles.

The result is a compatibility-oriented hybrid model:

- `auction_cars` acts as the effective source of truth for auction-linked selling price in most read paths.
- `vehicles` remains the record that almost every business flow actually queries, updates, and serializes.

---

## 2. Database Structure

### 2.1 vehicles Table

#### Purpose

The `vehicles` table is the main listing table. It started as the original inventory table and then accumulated additional concerns over time:

- seller ownership
- searchable catalog fields
- pricing and discount fields
- public listing state
- logistics context
- auction linkage
- backward-compatible copies of catalog data now also stored in attributes

#### Important columns explained

- `id`: primary key.
- `seller_id`: owner of the listing; nullable in newer schema usage, but still central to seller flows.
- `auction_car_id`: optional one-to-one link to `auction_cars.id`; unique, so one auction intake can only back one vehicle.
- `make_id`, `model_id`: normalized catalog foreign keys.
- `make`, `model`: denormalized display/search strings still stored directly.
- `model_code`: stored directly on vehicle and used in search/filter UI.
- `rec_no`: generated listing/reference number.
- `chassis`: listing chassis number; must match `auction_cars.chassis_no` for auction-linked creation.
- `year`, `mileage`: primary search and listing fields.
- `price`: legacy listing price column; still validated and written, but accessor logic can override reads with auction selling price.
- `discount_percentage`, `discount_amount`: listing-level discount fields.
- `stock_status`: inventory availability, typically `available` or `sold`.
- `status`: listing lifecycle, typically `draft`, `published`, `reserved`, or `sold`.
- `is_ready_for_sale`: sync flag derived from linked auction completion state.
- `country_id`: normalized stock country foreign key to `shipping_countries`.
- `stock_country`: legacy text country column kept in sync with `country_id`.
- `location`: location label used by listing and forms.
- `vehicle_type`, `color`, `transmission`, `fuel_type`, `engine_capacity`: legacy physical columns now partially backed by dynamic attributes.
- `length_m`, `width_m`, `height_m`, `volume_m3`, `seats`, `doors`: direct physical spec fields.
- `primary_image`: primary media path.
- `is_featured`, `is_clearance`: merchandising flags.

#### Categorized fields

##### Identity

- `id`
- `seller_id`
- `auction_car_id`
- `make_id`
- `model_id`
- `make`
- `model`
- `model_code`
- `rec_no`
- `chassis`
- `grade`
- `registration_year`
- `manufacture_year`
- `year`

##### Pricing

- `price`
- `discount_percentage`
- `discount_amount`
- `is_ready_for_sale`

##### Logistics

- `location`
- `country_id`
- `stock_country`
- `stock_status`

##### Display/UI

- `status`
- `is_featured`
- `is_clearance`
- `primary_image`
- `mileage`
- `condition`

##### Technical/legacy

- `vehicle_type`
- `color`
- `transmission`
- `fuel_type`
- `engine_capacity`
- `length_m`
- `width_m`
- `height_m`
- `volume_m3`
- `seats`
- `doors`
- `created_at`
- `updated_at`

Notes on legacy behavior:

- `vehicle_type`, `color`, `transmission`, and `fuel_type` are still physical columns, but `Vehicle` accessors now resolve them from dynamic attribute rows first.
- `engine_capacity` is also synchronized from the dynamic `Engine CC` attribute into the legacy column.

### 2.2 auction_cars Table

#### Purpose

The `auction_cars` table is the auction intake and purchase-cost record. It represents a purchased or imported unit before or alongside listing publication.

Its current responsibilities are:

- storing purchase-side costs
- storing shipping-side cost inputs captured during intake
- storing the intended selling price
- determining whether an auction-backed vehicle is ready for sale
- linking one auction intake row to at most one vehicle listing

#### Important columns explained

- `id`: primary key.
- `make_id`, `model_id`: normalized catalog keys.
- `chassis_no`: unique auction-side chassis identifier.
- `purchase_date`: intake/purchase date.
- `auction_price`: mandatory purchase price.
- `auction_fee`, `inspection`, `transportation`, `h_charge`, `van`, `insurance`, `freight`, `extra`, `shipper`, `storage`: cost inputs.
- `total`: derived total calculated in the model on save.
- `selling_price`: intended selling price for linked listing flows.
- `vessel`, `sailing_date`, `region`, `city`, `auction_house`: auction/logistics metadata.
- `admin_notes`: freeform notes.
- `status`: derived auction completion state, `pending` or `completed`.

#### Categorized fields

##### Pricing

- `selling_price`

##### Cost breakdown

- `auction_price`
- `auction_fee`
- `inspection`
- `transportation`
- `h_charge`
- `van`
- `insurance`
- `freight`
- `extra`
- `shipper`
- `storage`
- `total`

##### Auction metadata

- `make_id`
- `model_id`
- `chassis_no`
- `purchase_date`
- `auction_house`
- `admin_notes`
- `status`

##### Logistics

- `vessel`
- `sailing_date`
- `region`
- `city`

Important actual behavior:

- `AuctionCar::syncDerivedFields()` recalculates `status` and `total` every time the model is saved.
- `status` becomes `completed` only when the auction car has a linked vehicle and `selling_price` is not null.
- `total` is calculated from `auction_price`, `auction_fee`, `inspection`, `transportation`, `h_charge`, `van`, `insurance`, `freight`, and `extra`.
- `shipper` and `storage` are stored, cast, validated, and shown in UI, but they are not included in `calculateTotal()`.

### 2.3 Supporting Tables

#### vehicle_attribute_values

Purpose:

- Stores the actual attribute selections for each vehicle.
- Acts as the pivot between a vehicle and its dynamic specifications/features.

Key behavior:

- Each row belongs to one vehicle and one attribute.
- `value_id` references a predefined option in `attribute_values` when the attribute is select-based.
- `raw_value` stores numeric, boolean, or freeform values when needed.
- `Vehicle::syncDynamicAttributes()` deletes and recreates these rows during vehicle save flows.

#### attributes

Purpose:

- Master definition table for dynamic vehicle attributes.

Key behavior:

- Defines attribute name and type.
- Supported types are `select`, `multi_select`, `range`, and `boolean`.
- Used by vehicle forms, filters, facet generation, and boolean feature labels.

#### attribute_values

Purpose:

- Option dictionary for select and multi-select attributes.

Key behavior:

- Each row belongs to one attribute.
- Used to resolve human-readable labels from `vehicle_attribute_values.value_id`.
- Also used in filter facets and admin attribute configuration.

Context around the attribute system:

- `model_attributes` defines which attributes apply to each vehicle model.
- `make_attributes` provides make-level fallback attribute configuration.
- These supporting mapping tables do not store vehicle data directly, but they determine which attributes the vehicle form and filters expose.

---

## 3. Model Relationships

### Vehicle -> auctionCar

- Relationship type: `belongsTo(AuctionCar::class, 'auction_car_id')`
- Foreign key owner: `vehicles.auction_car_id`
- Ownership of data:
  - `vehicles` owns the link.
  - `auction_cars` owns purchase-side costs and `selling_price`.
  - `vehicles` still stores duplicated listing price and descriptive fields.
- Intended usage:
  - One vehicle listing can be backed by one auction intake record.
  - Auction-linked vehicles should inherit make, model, chassis, and selling price from intake.
- Actual usage:
  - Many reads start from `vehicles` and then prefer `auctionCar.selling_price` for price.
  - Admin create flow can create a vehicle from an auction intake record.
  - Store and update requests copy `auction_cars.selling_price` back into `vehicles.price` for compatibility.
  - `Vehicle::syncAuctionCarLifecycle()` recalculates linked auction completion status after vehicle create, update, or delete.

### Vehicle -> attributes

- Relationship type: `belongsToMany` through `vehicle_attribute_values`
- Supporting direct relation: `attributeValues()` is a `hasMany` to `VehicleAttributeValue`
- Ownership of data:
  - The actual selected values live in `vehicle_attribute_values`.
  - Definitions live in `attributes` and `attribute_values`.
- Intended usage:
  - Dynamic specifications and filters should move out of rigid vehicle columns into configurable attributes.
- Actual usage:
  - Forms load model-specific attributes from the catalog service.
  - Save flows write `vehicle_attribute_values` rows.
  - Public filters use joins to `vehicle_attribute_values` for attribute filtering and facets.
  - Legacy columns such as `vehicle_type`, `color`, `transmission`, `fuel_type`, and `engine_capacity` are still physically present and partially synchronized from attributes.

### AuctionCar -> vehicle

- Relationship type: `hasOne(Vehicle::class, 'auction_car_id')`
- Foreign key owner: still `vehicles`, not `auction_cars`
- Ownership of data:
  - `auction_cars` owns the intake record.
  - `vehicle` is the downstream listing created from that intake.
- Intended usage:
  - One auction intake should produce at most one listing.
- Actual usage:
  - The unique index on `vehicles.auction_car_id` enforces one-to-one linkage.
  - `AuctionCar::canCreateVehicle()` gates vehicle creation until make, model, and chassis are present and no vehicle is linked.
  - `AuctionCar::syncLinkedVehicleSaleReadiness()` writes `vehicles.is_ready_for_sale` based on auction completion state.

---

## 4. Read Flow Analysis

### 4.1 Public Listing

Primary controller:

- `app/Http/Controllers/PublicController.php`

Read path:

1. Request filters are normalized by `VehicleFilterService`.
2. `VehicleFilterService::buildListingQuery()` starts from `Vehicle::query()`.
3. Base constraints always apply to `vehicles.stock_status = available`.
4. For public pages, `vehicles.status = published` is also required.
5. Price filters join `auction_cars` only when needed and use `COALESCE(listing_auction_cars.selling_price, vehicles.price)`.
6. Attribute filters join `vehicle_attribute_values`.
7. Final list eager-loads:
   - `auctionCar`
   - `images`
   - `attributeValues.attribute`
   - `attributeValues.value`
8. Country relations are hydrated separately from `country_id` or `stock_country`.

Data source by concern:

- Listing identity: `vehicles`
- Availability/status: `vehicles`
- Price filtering: `auction_cars.selling_price` first, fallback `vehicles.price`
- Display price: public Vue templates use `vehicle.auction_car?.selling_price ?? vehicle.price`
- Attribute data: `vehicle_attribute_values`, `attributes`, `attribute_values`
- Country/currency display: `shipping_countries` and `currencies` via `country` relation hydration

### 4.2 Admin Panel

#### Vehicle listing

Primary controller:

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

Read path:

- Starts from `Vehicle::query()`.
- Left-joins `auction_cars as listing_auction_cars`.
- Selects `vehicles.*`.
- Eager-loads `auctionCar`, `vehicleMake`, and `vehicleModel`.
- Search, status filters, and year filters all operate on `vehicles` columns.
- Price range filters use `COALESCE(listing_auction_cars.selling_price, vehicles.price)`.
- Payload exposes both `price` and nested `auction_car.selling_price`.

#### Vehicle detail

- `Admin\VehicleController@show` loads:
  - `auctionCar`
  - `seller`
  - `images`
  - `attributeValues.attribute`
  - `attributeValues.value`

Actual source split:

- Core vehicle page data comes from `vehicles`.
- Auction pricing metadata comes from `auction_cars`.
- Dynamic specification display comes from attribute tables.

Additional admin auction read surface:

- `app/Http/Controllers/Admin/AuctionCarController.php` is the main intake-side listing and edit surface.
- That controller reads `auction_cars` with `makeRelation`, `modelRelation`, and linked `vehicle`.
- Admin auction list filters can also filter by linked vehicle country via `whereHas('vehicle')`.

### 4.3 Seller Panel

Primary controllers:

- `app/Http/Controllers/Seller/VehicleController.php`
- `app/Http/Controllers/Seller/DashboardController.php`

Seller vehicle listing:

- Starts from the seller's `vehicles`.
- Left-joins `auction_cars as listing_auction_cars` for price sorting.
- Selects `vehicles.*`.
- Eager-loads `auctionCar`, `images`, and dynamic attributes.
- Search and status filtering use `vehicles` columns.
- Price sort uses `COALESCE(listing_auction_cars.selling_price, vehicles.price)`.

Seller dashboard:

- Reads counts from the seller's `vehicles()` relation.
- Loads recent vehicles with `auctionCar` and `images`.
- Revenue stats come from `orders`, not directly from vehicle price columns.

UI behavior:

- Seller pages commonly render price as `vehicle.auction_car?.selling_price ?? vehicle.price`.

### 4.4 Vehicle Detail Page

Primary public detail controller:

- `PublicController@show`

Loaded data:

- `vehicle`
- `auctionCar`
- `images`
- `attributeValues.attribute`
- `attributeValues.value`
- hydrated `country` relation

Displayed data sources on the page:

- Heading and identity: `vehicles`
- Price card: `auction_cars.selling_price` first, fallback `vehicle.price`
- Discount line: `vehicles.discount_amount`
- Current selling price line: `vehicle.price_with_discount` first, then `auction_car.selling_price`, then `vehicle.price`
- Feature badges: `Vehicle::feature_labels`, derived from boolean dynamic attributes
- Detail grid: mostly direct `vehicles` columns, with country shown from normalized relation when available

### 4.5 Filters & Search

Primary service:

- `app/Services/VehicleFilterService.php`

#### Price filters

- Implemented with a conditional join to `auction_cars`.
- Expression used throughout listing and facets:
  - `COALESCE(listing_auction_cars.selling_price, vehicles.price)`
- This same expression is used for:
  - public listing min/max price filters
  - public price range facets
  - admin vehicle min/max price filters
  - seller sort by price
  - model price insights in `VehicleCatalogAttributeService`

#### Status filters

- Public listing uses `vehicles.stock_status` and optionally `vehicles.status = published`.
- Admin and seller inventory filters use `vehicles.status` and `vehicles.stock_status`.
- `auction_cars.status` is separate and only used in auction intake screens.

#### Attribute filters

- Implemented by joining `vehicle_attribute_values` once per active attribute filter.
- Select and multi-select filters match `value_id`.
- Range and boolean filters match `raw_value`.
- Vehicle type filter is hybrid:
  - checks `vehicles.vehicle_type`
  - or attribute rows for the `Body Type` attribute

---

## 5. Write Flow Analysis

### Vehicle create

Primary write paths:

- `app/Http/Controllers/Admin/VehicleController.php`
- `app/Http/Controllers/Seller/VehicleController.php`
- legacy path: `app/Http/Controllers/VehicleController.php`

Admin/seller flow:

1. `VehicleForm.vue` posts vehicle fields plus `dynamic_attributes`.
2. `StoreVehicleRequest` validates and normalizes input.
3. Make/model names are re-derived from `make_id` and `model_id`.
4. Country is normalized into `country_id` and `stock_country`.
5. If `auction_car_id` is present:
   - request loads `AuctionCar`
   - enforces make/model/chassis match
   - blocks linking if another vehicle already exists
   - copies `auction_cars.selling_price` into `vehicles.price`
6. Controller creates the `vehicles` row.
7. Controller writes `vehicle_attribute_values` via `syncDynamicAttributes()`.
8. Controller writes image rows.
9. Vehicle model hooks call `syncAuctionCarLifecycle()`, which updates linked auction readiness state.

Which table gets which data:

- `vehicles`: almost all listing fields, including copied price
- `vehicle_attribute_values`: dynamic specs/features
- `vehicle_images`: media
- `auction_cars`: not created in this flow, only linked and read

### Vehicle update

Primary write paths:

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

Behavior:

- `UpdateVehicleRequest` normalizes make/model/country and dynamic attributes.
- If the existing vehicle is auction-linked, request loads `AuctionCar` and copies `selling_price` back into `vehicles.price`.
- Controller updates `vehicles`.
- Dynamic attribute rows are deleted and recreated.
- Additional images may be added.
- Vehicle save hooks resync linked auction readiness.

### Auction import / purchase flow

Primary write path:

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

Behavior:

1. Admin intake UI edits auction rows in `AuctionIntakeTable.vue` or `AuctionCarForm.vue`.
2. `StoreAuctionCarRequest` or `UpdateAuctionCarRequest` validates auction-side fields.
3. `auction_cars` row is created or updated.
4. `AuctionCar` model recalculates:
   - `total`
   - `status`
5. If a vehicle is linked, `syncLinkedVehicleSaleReadiness()` updates `vehicles.is_ready_for_sale`.

Which table gets which data:

- `auction_cars`: purchase-side costs, selling price, auction metadata, logistics metadata
- `vehicles`: only indirectly updated through `is_ready_for_sale`

### Where duplication occurs

- Auction-linked create/update flows copy `auction_cars.selling_price` into `vehicles.price`.
- `make` and `model` exist both as normalized foreign keys and as direct strings on `vehicles`.
- `stock_country` and `country_id` represent the same concept in two forms.
- Some catalog values live both in `vehicle_attribute_values` and legacy columns on `vehicles`.

### Conflicts between vehicle and auction data

- The application treats `auction_cars.selling_price` as the preferred read source in many places, but forms and validations still require `vehicles.price`.
- Auction completion state is computed from linked vehicle existence plus selling price, so sale readiness is partly controlled by data in both tables.
- UI and service layers do not always use the same pricing expression in the same order.

---

## 6. Pricing Flow (CRITICAL)

### Where price is coming from

#### Listing

- Public listing filters and range calculations use `COALESCE(auction_cars.selling_price, vehicles.price)`.
- Admin/seller list sorting and filtering use the same `COALESCE(...)` pattern.
- `Vehicle::getPriceAttribute()` returns `auction_cars.selling_price` when available, otherwise `vehicles.price`.
- Public/admin/seller Vue pages often display `vehicle.auction_car?.selling_price ?? vehicle.price`.

Practical result:

- For auction-linked vehicles with a non-null selling price, the effective listing price is usually auction-backed.
- For non-linked vehicles or linked vehicles without selling price, reads fall back to `vehicles.price`.

#### Cart

Primary path:

- `app/Http/Controllers/Customer/CartController.php`

Behavior:

- Loads `vehicle.auctionCar`.
- Uses `vehicle.price_with_discount` for line totals.
- `price_with_discount` is computed from `Vehicle::price`, which itself prefers `auction_cars.selling_price`.

Important actual behavior:

- Cart total is already based on discounted price.
- Cart discount is also summed separately from `discount_amount`.
- Cart subtotal is then calculated as `total - discount`, which subtracts the discount a second time.

#### Checkout

Single-vehicle purchase path:

- `CustomerPurchaseService::getPurchasePageData()` shows `auctionCar.selling_price ?? vehicle.price_with_discount`.
- `CustomerPurchaseService::createVehiclePurchase()` locks price using `lockedVehicle->price_with_discount`.

Multi-vehicle checkout path:

- `CheckoutController@index` computes UI subtotal from `vehicle.price_with_discount`.
- `PricingService::calculateCheckoutTotal()` also sums discounted selling price.
- Order items are stored with `price = vehicle.price_with_discount`.

Implication:

- Checkout locking logic is based on discounted price, not raw auction selling price.
- However, the single-vehicle purchase page preview prefers raw `auctionCar.selling_price` before discounted price.

#### Orders

Order creation paths:

- `CustomerPurchaseService::createVehiclePurchase()` stores `vehicle_price` from `price_with_discount`.
- `CheckoutController@store` stores each order item from `price_with_discount`.
- `Customer\OrderController@store` uses `auctionCar.selling_price ?? price_with_discount`.
- `Api\OrderController@store` uses `auctionCar.selling_price ?? price_with_discount`.

This means order creation is not fully uniform across all flows.

### Comparing the price fields

#### vehicles.price

Current role:

- Physical compatibility column
- fallback price when no auction selling price exists
- still required by create/update validation
- still stored on vehicle create and update

Actual read behavior:

- direct raw column is often bypassed by the `Vehicle` price accessor
- still used explicitly in `COALESCE(..., vehicles.price)` expressions

#### auction_cars.selling_price

Current role:

- preferred selling price for auction-linked vehicles
- used in model accessor, pricing service, list filters, list sorts, auction UI, and several order paths
- also acts as one of the conditions for auction completion and vehicle sale readiness

#### auction cost fields

Fields:

- `auction_price`
- `auction_fee`
- `inspection`
- `transportation`
- `h_charge`
- `van`
- `insurance`
- `freight`
- `extra`
- `shipper`
- `storage`
- `total`

Current role:

- stored in `auction_cars`
- mainly used in admin intake, admin/seller/public eager-load payloads, and informational display
- not directly used to build customer order price snapshots except where shipping quote logic independently computes logistics costs

### Current source of truth

For selling price:

- Effective runtime source of truth for auction-linked vehicles is `auction_cars.selling_price`.
- Compatibility fallback is `vehicles.price`.

For order lock price:

- In the main purchase service and multi-item checkout, the locked value is `price_with_discount`, which resolves through the vehicle accessor and discount amount.

For auction purchase economics:

- `auction_cars` is the only source of auction cost breakdown.

### Conflicts

- `vehicles.price` still exists and is still written even when `auction_cars.selling_price` is intended to lead.
- Single-vehicle purchase preview uses raw auction selling price first, while locked purchase logic uses discounted price.
- Simple customer/API order flows use `auctionCar.selling_price ?? price_with_discount`, while the main purchase service uses `price_with_discount`.
- `AuctionCar::total` excludes `shipper` and `storage` even though those fields are stored and displayed.

---

## 7. Attribute System Usage

### Which vehicle fields are already using attributes

The `Vehicle` model now resolves these legacy columns from dynamic attributes when available:

- `vehicle_type` from the `Body Type` attribute
- `color` from the `Color` attribute
- `transmission` from the `Transmission` attribute
- `fuel_type` from the `Fuel Type` attribute
- `engine_capacity` from the `Engine CC` attribute

Additional attribute-driven behavior:

- Boolean attributes power `feature_labels` for UI badges.
- Public filters and facets use attribute rows directly.
- Vehicle forms load attribute definitions from make/model metadata and submit selected values as `dynamic_attributes`.

### Which fields are still stored directly in vehicles table

Still stored directly and used directly:

- `make`, `model`, `make_id`, `model_id`, `model_code`
- `price`, `discount_percentage`, `discount_amount`
- `year`, `mileage`
- `condition`
- `grade`, `registration_year`, `manufacture_year`
- `location`, `country_id`, `stock_country`
- `status`, `stock_status`, `is_featured`, `is_clearance`, `is_ready_for_sale`
- `length_m`, `width_m`, `height_m`, `volume_m3`, `seats`, `doors`
- `primary_image`

Still physically stored even though attributes also exist:

- `vehicle_type`
- `color`
- `transmission`
- `fuel_type`
- `engine_capacity`

### Where attributes are used in UI

- `VehicleForm.vue` renders dynamic specification fields and boolean feature fields.
- Public listing filters are driven by attribute facets via `VehicleCatalogController` and `VehicleFilterService`.
- Public vehicle detail shows `feature_labels` and attribute-backed legacy fields.
- Admin and seller show pages load attribute values for display.

---

## 8. Data Duplication & Design Issues

### Duplicate fields between vehicles and auction_cars

- Pricing meaning overlap:
  - `vehicles.price`
  - `auction_cars.selling_price`
- Make/model meaning overlap:
  - `vehicles.make_id` and `vehicles.model_id`
  - `auction_cars.make_id` and `auction_cars.model_id`
- Chassis meaning overlap:
  - `vehicles.chassis`
  - `auction_cars.chassis_no`

### Fields that should NOT be in vehicles if the goal is strict separation

Based on current usage, these are purchase-side or auction-side concepts, not listing-native concepts:

- mirrored selling price in `vehicles.price` for auction-linked stock
- auction linkage readiness flag dependencies tied to intake completion

However, these fields cannot be removed safely today because the code still depends on them:

- `vehicles.price`
- `vehicles.chassis`
- `vehicles.make`
- `vehicles.model`

### Fields that should be moved to attributes if the goal is full catalog normalization

Already partially migrated but still duplicated on `vehicles`:

- `vehicle_type`
- `color`
- `transmission`
- `fuel_type`
- `engine_capacity`

Potential future attribute candidates based on current rigid storage:

- `seats`
- `doors`
- possibly dimensional fields if model-specific catalog management is desired

These are not currently attribute-backed in the codebase.

### Any inconsistencies

- `vehicles.price` is still required on forms and requests, even though auction-linked reads prefer `auction_cars.selling_price`.
- Single-vehicle purchase preview and locked purchase pricing do not use the same precedence order.
- Cart pricing labels and calculations are inconsistent because subtotal subtracts discount after already using discounted price.
- `AuctionCar::calculateTotal()` ignores `shipper` and `storage`, but those fields are validated, stored, and displayed.
- There is still a legacy generic `VehicleController` write path that creates and updates vehicles without the newer catalog/auction constraints used by admin and seller controllers.
- `stock_country` remains a live fallback in multiple read paths even after `country_id` normalization.

---

## 9. Dependency Map

### vehicles table is used:

#### Controllers

- `app/Http/Controllers/PublicController.php`: public listing and vehicle detail start from `vehicles`.
- `app/Http/Controllers/Api/VehicleController.php`: API listing/detail start from `vehicles`.
- `app/Http/Controllers/Admin/VehicleController.php`: admin CRUD and admin inventory listing.
- `app/Http/Controllers/Seller/VehicleController.php`: seller CRUD and seller inventory listing.
- `app/Http/Controllers/Seller/DashboardController.php`: seller vehicle counts and recent inventory.
- `app/Http/Controllers/Admin/DashboardController.php`: admin inventory counts and body-type aggregation.
- `app/Http/Controllers/VehicleCatalogController.php`: model codes, attribute facets, model insights derived from vehicles.
- `app/Http/Controllers/Customer/CartController.php`: cart items reference vehicles.
- `app/Http/Controllers/Customer/CheckoutController.php`: checkout loads vehicle-backed cart items and writes order items from vehicles.
- `app/Http/Controllers/Customer/OrderController.php`: simple order create and order display read vehicles.
- `app/Http/Controllers/Api/OrderController.php`: API order creation uses vehicles.
- `app/Http/Controllers/Api/ShippingCalculationController.php`: shipping quote endpoints load vehicles.
- `app/Http/Controllers/VehicleController.php`: legacy direct vehicle create/update/delete flow.

#### Services

- `app/Services/VehicleFilterService.php`: filtering, search, facets, and ranges start from vehicles.
- `app/Services/VehicleCatalogAttributeService.php`: model insights query vehicles.
- `app/Services/PricingService.php`: resolves selling price from vehicle payloads.
- `app/Services/CustomerPurchaseService.php`: locks and updates vehicle availability during purchase.
- `app/Services/ShippingService.php`: reads vehicle type and discounted vehicle price for quotes.
- `app/Services/VehicleFormOptionsService.php`: derives location options from vehicles.

#### Vue components

- `resources/js/Components/Vehicles/VehicleForm.vue`: creates/updates vehicle fields and dynamic attributes.
- `resources/js/Pages/Public/Home.vue`: renders vehicle listing cards and filter output.
- `resources/js/Pages/Public/VehicleShow.vue`: renders detailed vehicle page.
- `resources/js/Pages/Admin/Vehicles/Index.vue`: admin vehicle listing.
- `resources/js/Pages/Admin/Vehicles/Show.vue`: admin vehicle detail.
- `resources/js/Pages/Admin/Vehicles/Create.vue`: admin vehicle creation shell.
- `resources/js/Pages/Seller/Vehicles/Index.vue`: seller vehicle listing.
- `resources/js/Pages/Seller/Vehicles/Show.vue`: seller vehicle detail.
- `resources/js/Pages/Seller/Vehicles/Create.vue`: seller vehicle create shell.
- `resources/js/Pages/Seller/Vehicles/Edit.vue`: seller vehicle edit shell.
- `resources/js/Pages/Seller/Dashboard.vue`: recent vehicle list and counts.
- `resources/js/Pages/Customer/Cart/Index.vue`: cart vehicle display.
- `resources/js/Pages/Customer/Checkout/Index.vue`: checkout line items and pricing summaries.
- `resources/js/Pages/Admin/Sellers/Index.vue`: seller vehicle tables.

### auction_cars table is used:

#### Controllers

- `app/Http/Controllers/Admin/AuctionCarController.php`: primary intake CRUD and batch save surface.
- `app/Http/Controllers/Admin/VehicleController.php`: links auction intake to vehicles and exposes auction selling price.
- `app/Http/Controllers/Seller/VehicleController.php`: joins auction cars for price sorting and eager-loads auction data.
- `app/Http/Controllers/PublicController.php`: eager-loads auction car for public listing and detail.
- `app/Http/Controllers/Api/VehicleController.php`: eager-loads auction car in API listing/detail.
- `app/Http/Controllers/Seller/DashboardController.php`: eager-loads auction car on recent vehicles.
- `app/Http/Controllers/Customer/OrderController.php`: simple order path reads `auctionCar.selling_price`.
- `app/Http/Controllers/Api/OrderController.php`: API order path reads `auctionCar.selling_price`.

#### Services

- `app/Services/VehicleFilterService.php`: joins `auction_cars` for price filters and ranges.
- `app/Services/VehicleCatalogAttributeService.php`: joins `auction_cars` for model price insight ranges.
- `app/Services/PricingService.php`: prefers `auctionCar.selling_price`.
- `app/Services/CustomerPurchaseService.php`: loads auction car and uses auction-linked pricing behavior.

#### Vue components

- `resources/js/Components/Admin/AuctionCarForm.vue`: edits auction intake data directly.
- `resources/js/Components/Admin/AuctionIntakeTable.vue`: batch intake grid for auction rows.
- `resources/js/Pages/Admin/DataEntry/AuctionList.vue`: auction intake list.
- `resources/js/Pages/Admin/DataEntry/AuctionCreate.vue`: auction intake create shell.
- `resources/js/Pages/Admin/DataEntry/AuctionEdit.vue`: auction intake edit shell.
- `resources/js/Pages/Admin/Vehicles/Index.vue`: displays auction-backed price.
- `resources/js/Pages/Admin/Vehicles/Show.vue`: displays auction-backed price and auction payload.
- `resources/js/Pages/Admin/Vehicles/Create.vue`: preloads auction car when creating from intake.
- `resources/js/Pages/Seller/Vehicles/Index.vue`: displays auction-backed price.
- `resources/js/Pages/Seller/Vehicles/Show.vue`: displays auction-backed price.
- `resources/js/Pages/Seller/Dashboard.vue`: displays auction-backed price.
- `resources/js/Pages/Public/Home.vue`: displays auction-backed price.
- `resources/js/Pages/Public/VehicleShow.vue`: displays auction-backed price and discounted price.
- `resources/js/Pages/Customer/Cart/Index.vue`: displays auction-backed price fallback.

---

## 10. Risk Analysis

If `vehicles` is refactored aggressively, the following breakpoints exist.

### What will break?

Removing or bypassing `vehicles.price` will affect:

- `StoreVehicleRequest` and `UpdateVehicleRequest`, which still validate and populate `price`
- `VehicleForm.vue`, which still binds a `price` field
- `VehicleFilterService` and `VehicleCatalogAttributeService`, which use `COALESCE(..., vehicles.price)`
- legacy `VehicleController`, which directly validates and writes `price`
- any non-auction vehicles, because `vehicles.price` is still the only price source for them

Removing legacy descriptive columns from `vehicles` will affect:

- vehicle create/update validation in requests and legacy controller
- public/admin/seller views that still render `vehicle_type`, `color`, `transmission`, `fuel_type`, and `engine_capacity`
- shipping calculation, which still reads `vehicle.vehicle_type`

Removing `stock_country` too early will affect:

- `Vehicle::syncCountryFields()` fallback behavior
- public display fallbacks
- order creation fields such as `from_port`

Changing `auction_car_id` semantics will affect:

- admin create-from-auction flow
- one-to-one auction-to-vehicle assumption
- sale-readiness synchronization in both models

### Which flows depend on legacy fields?

- Vehicle forms still depend on direct vehicle columns for price, condition, color, transmission, fuel type, and other specs.
- Shipping calculation depends on `vehicle_type` being directly readable on the vehicle payload.
- Cart and checkout depend on `price_with_discount`, which is still computed from the vehicle model and its discount fields.
- Public and seller/admin UI still display many direct vehicle columns even where attribute-backed accessors exist.
- Country display and downstream order fields still use `stock_country` as a fallback.

### Safe vs risky changes

Safer changes based on current abstractions:

- shifting more reads to `auction_cars.selling_price`, because accessor and pricing service support that already
- shifting more display reads to normalized country relation, because country hydration already exists
- continuing the move of legacy descriptive fields to attributes, because accessors and sync logic already exist

Riskier changes based on current dependencies:

- removing `vehicles.price`
- removing `stock_country`
- removing legacy descriptive columns before forms, shipping logic, and templates stop referencing them
- changing the one-to-one `auction_car_id` relationship
- changing order pricing logic without first normalizing the inconsistent purchase/order paths

---

## 11. Summary

### Clear separation of responsibilities

#### vehicles

Current actual role:

- primary listing record
- primary query root for marketplace, admin, seller, cart, checkout, shipping, and order flows
- stores availability, status, seller ownership, country/location, media, and many display fields
- still stores compatibility pricing and legacy descriptive columns

#### auction_cars

Current actual role:

- intake and purchase-cost record
- stores auction-side costs, logistics metadata, and intended selling price
- determines sale readiness for linked auction-backed listings
- acts as the preferred price source for auction-linked inventory in most read paths

### Recommended source of truth

Based strictly on current behavior:

- Listing identity and lifecycle source of truth is still `vehicles`.
- Auction economics source of truth is `auction_cars`.
- Effective selling price source of truth for auction-linked vehicles is `auction_cars.selling_price`.
- Compatibility fallback source of truth remains `vehicles.price`.

### Key problems in current design

- The same commercial concept, selling price, exists in both `vehicles.price` and `auction_cars.selling_price`.
- Read precedence is partially normalized, but write paths still persist duplicated pricing into `vehicles`.
- Dynamic attributes and legacy vehicle columns coexist for the same descriptive data.
- Public, checkout, and order flows do not all lock or preview price using the same precedence order.
- Auction total calculation does not include every stored auction cost field.
- The codebase still contains both newer compatibility-aware vehicle flows and older legacy vehicle CRUD flows.

In short, the system already behaves as if `vehicles` is the listing shell and `auction_cars` is the auction economics record, but the schema and write paths still duplicate enough data that the boundary is not yet enforced.