# P0 DEEP AUDIT - Database Consistency & Zombie API Hunt Report **Date:** 2026-06-21 **Author:** Fast Coder (Core Developer) **Gitea Issue:** #274 **Scope:** Database schemas `vehicle`, `finance`, `fleet_finance` + Backend API endpoints --- ## 1. Database Cleanliness ### 1.1 Schema: `vehicle` (20 tables) | Table | Purpose | Status | |-------|---------|--------| | `assets` | Core vehicle assets (thick model) | ✅ Clean | | `asset_events` | Service/maintenance events | ✅ Clean | | `asset_inspections` | Inspection records | ✅ Clean | | `asset_reviews` | User reviews | ✅ Clean | | `asset_telemetry` | Telemetry data | ✅ Clean | | `auto_data_crawler_queue` | Crawler queue | ✅ Clean | | `catalog_discovery` | Catalog discovery queue | ✅ Clean | | `dict_body_types` | Body type dictionary | ✅ Clean | | `external_reference_library` | External reference data | ✅ Clean | | `feature_definitions` | Feature definitions | ✅ Clean | | `gb_catalog_discovery` | GB catalog discovery | ✅ Clean | | `model_feature_maps` | Model-feature mappings | ✅ Clean | | `motorcycle_specs` | Motorcycle specs | ✅ Clean | | `odometer_readings` | Odometer readings | ✅ Clean | | `reference_lookup` | Reference lookup | ✅ Clean | | `vehicle_catalog` | Vehicle catalog | ✅ Clean | | `vehicle_logbook` | Trip logbook | ✅ Clean | | `vehicle_model_definitions` | Master model definitions | ✅ Clean | | `vehicle_ownership_history` | Ownership history | ✅ Clean | | `vehicle_transfer_requests` | Transfer requests | ✅ Clean | | `vehicle_types` | Vehicle type dictionary | ✅ Clean | | `vehicle_user_ratings` | User ratings | ✅ Clean | ### 1.2 Schema: `fleet_finance` (6 tables) | Table | Purpose | Status | |-------|---------|--------| | `asset_costs` | Cost ledger (moved from `vehicle`) | ✅ Clean | | `asset_financials` | Purchase/financing data (moved from `vehicle`) | ✅ Clean | | `cost_categories` | Cost category hierarchy (moved from `vehicle`) | ✅ Clean | | `insurance_providers` | Insurance provider catalog | ✅ Clean | | `vehicle_insurance_policies` | Vehicle insurance policies | ✅ Clean | | `vehicle_tax_obligations` | Vehicle tax obligations | ✅ Clean | ### 1.3 Schema: `finance` (7 tables) | Table | Purpose | Status | |-------|---------|--------| | `credit_logs` | Credit transactions | ✅ Clean | | `exchange_rates` | Currency exchange rates | ✅ Clean | | `issuers` | Billing entities | ✅ Clean | | `org_subscriptions` | Organization subscriptions | ✅ Clean | | `payment_intents` | Payment intents | ✅ Clean | | `user_subscriptions` | User subscriptions | ✅ Clean | | `withdrawal_requests` | Withdrawal requests | ✅ Clean | ### 1.4 Critical Checks | Check | Result | |-------|--------| | `vehicle.vehicle_expenses` exists? | ❌ **NOT FOUND** - Correctly removed | | `vehicle.costs` (VehicleCost) exists? | ❌ **NOT FOUND** - Correctly removed | | `data` schema has leftover tables? | ❌ **NOT FOUND** - Schema is empty | | Duplicate table names between `vehicle` and `fleet_finance`? | ❌ **NONE** - No overlap | | Legacy `expense`/`cost` tables anywhere? | ❌ **NONE** - Clean | **Verdict: ✅ DATABASE IS CLEAN.** No duplicated tables, no legacy tables left behind. The migration of `AssetCost`, `CostCategory`, `AssetFinancials` from `vehicle` → `fleet_finance` schema is complete. --- ## 2. Zombie API Endpoints ### 2.1 🔴 CRITICAL: [`reports.py`](backend/app/api/v1/endpoints/reports.py) - References `vehicle.vehicle_expenses` **File:** [`backend/app/api/v1/endpoints/reports.py`](backend/app/api/v1/endpoints/reports.py:18) Two endpoints use raw SQL queries referencing the **non-existent** `vehicle.vehicle_expenses` table: 1. **`GET /reports/summary/{vehicle_id}`** (line 8-32) - Queries `FROM vehicle.vehicle_expenses` 2. **`GET /reports/trends/{vehicle_id}`** (line 34-50) - Queries `FROM vehicle.vehicle_expenses` **Impact:** These endpoints will **ALWAYS return 500 errors** because the table `vehicle.vehicle_expenses` does not exist in the database. **Router registration:** [`api.py`](backend/app/api/v1/api.py:33) - Registered as `prefix="/reports"` ### 2.2 🟡 WARNING: [`expenses.py`](backend/app/api/v1/endpoints/expenses.py) - Uses `AssetCost` from `fleet_finance` (correct) **File:** [`backend/app/api/v1/endpoints/expenses.py`](backend/app/api/v1/endpoints/expenses.py:10) Imports `AssetCost` and `CostCategory` from `app.models` which correctly resolves to `fleet_finance` schema models. **This is NOT a zombie** - it's correctly using the moved models. ### 2.3 🟡 WARNING: [`assets.py`](backend/app/api/v1/endpoints/assets.py) - Uses `AssetCost` from `fleet_finance` (correct) **File:** [`backend/app/api/v1/endpoints/assets.py`](backend/app/api/v1/endpoints/assets.py:14-15) Imports `AssetCost`, `CostCategory` from `app.models` and `AssetFinancials`, `VehicleInsurancePolicy`, `VehicleTaxObligation` from `app.models.fleet_finance`. **Correctly references the new schema.** ### 2.4 🟢 OK: [`dictionaries.py`](backend/app/api/v1/endpoints/dictionaries.py) - Uses `CostCategory` from `fleet_finance` **File:** [`backend/app/api/v1/endpoints/dictionaries.py`](backend/app/api/v1/endpoints/dictionaries.py:13) Imports `CostCategory` from `app.models.fleet_finance`. **Correct.** ### 2.5 Zombie API Summary | Endpoint File | Route Prefix | Status | Issue | |---------------|-------------|--------|-------| | `reports.py` | `/reports/summary/{id}` | 🔴 **ZOMBIE** | References `vehicle.vehicle_expenses` (DNE) | | `reports.py` | `/reports/trends/{id}` | 🔴 **ZOMBIE** | References `vehicle.vehicle_expenses` (DNE) | | `reports.py` | `/reports/summary/latest` | 🟢 OK | Returns mock data only | | `expenses.py` | `/expenses/{asset_id}` | 🟢 OK | Uses `AssetCost` from `fleet_finance` | | `assets.py` | `/assets/vehicles/{id}/costs` | 🟢 OK | Uses `AssetCost` from `fleet_finance` | | `assets.py` | `/assets/vehicles/{id}/insurance` | 🟢 OK | Uses `VehicleInsurancePolicy` | | `assets.py` | `/assets/vehicles/{id}/tax` | 🟢 OK | Uses `VehicleTaxObligation` | | `dictionaries.py` | `/dictionaries/cost-categories` | 🟢 OK | Uses `CostCategory` from `fleet_finance` | --- ## 3. InsuranceProvider & Missing CRUD Analysis ### 3.1 InsuranceProvider Model The [`InsuranceProvider`](backend/app/models/fleet_finance/models.py:180) model exists in `fleet_finance.insurance_providers` with fields: - `id` (Integer, PK) - `name` (String, unique) - `claim_phone` (String, nullable) - `claim_url` (String, nullable) - `services_offered` (JSONB) - `is_active` (Boolean) ### 3.2 Existing Insurance/Tax Endpoints in [`assets.py`](backend/app/api/v1/endpoints/assets.py) | Method | Route | Exists? | |--------|-------|---------| | `GET` | `/assets/vehicles/{asset_id}/insurance` | ✅ Yes (list) | | `POST` | `/assets/vehicles/{asset_id}/insurance` | ✅ Yes (create) | | `PUT` | `/assets/vehicles/{asset_id}/insurance/{policy_id}` | ❌ **MISSING** | | `DELETE` | `/assets/vehicles/{asset_id}/insurance/{policy_id}` | ❌ **MISSING** | | `GET` | `/assets/vehicles/{asset_id}/tax` | ✅ Yes (list) | | `POST` | `/assets/vehicles/{asset_id}/tax` | ✅ Yes (create) | | `PUT` | `/assets/vehicles/{asset_id}/tax/{tax_id}` | ❌ **MISSING** | | `DELETE` | `/assets/vehicles/{asset_id}/tax/{tax_id}` | ❌ **MISSING** | ### 3.3 InsuranceProvider CRUD (Standalone) | Method | Route | Exists? | |--------|-------|---------| | `GET` | `/insurance-providers` | ❌ **MISSING** | | `GET` | `/insurance-providers/{id}` | ❌ **MISSING** | | `POST` | `/insurance-providers` | ❌ **MISSING** | | `PUT` | `/insurance-providers/{id}` | ❌ **MISSING** | | `DELETE` | `/insurance-providers/{id}` | ❌ **MISSING** | **No schema exists for InsuranceProvider** - searched `backend/app/schemas/` for `InsuranceProvider` patterns - **0 results**. --- ## 4. Action Plan ### 4.1 🔴 P0 - Fix Zombie Endpoints in `reports.py` **Problem:** Two endpoints in [`reports.py`](backend/app/api/v1/endpoints/reports.py) reference the non-existent `vehicle.vehicle_expenses` table. **Solution:** Rewrite the raw SQL queries to use the new `fleet_finance.asset_costs` table with proper joins to `cost_categories`: ```python # Replace raw SQL with SQLAlchemy ORM queries using AssetCost + CostCategory from app.models import AssetCost, CostCategory from sqlalchemy import select, func # GET /reports/summary/{vehicle_id} stmt = select( CostCategory.code.label("category"), func.sum(AssetCost.amount_gross).label("total_amount"), func.count(AssetCost.id).label("transaction_count") ).outerjoin(CostCategory, AssetCost.category_id == CostCategory.id ).where(AssetCost.asset_id == vehicle_id ).group_by(CostCategory.code) ``` **Estimated effort:** 30 minutes ### 4.2 🟡 P1 - Add UPDATE/DELETE for Insurance Policies and Tax Obligations **Missing endpoints in [`assets.py`](backend/app/api/v1/endpoints/assets.py):** 1. `PUT /assets/vehicles/{asset_id}/insurance/{policy_id}` - Update insurance policy 2. `DELETE /assets/vehicles/{asset_id}/insurance/{policy_id}` - Delete insurance policy 3. `PUT /assets/vehicles/{asset_id}/tax/{tax_id}` - Update tax obligation 4. `DELETE /assets/vehicles/{asset_id}/tax/{tax_id}` - Delete tax obligation **Estimated effort:** 1 hour ### 4.3 🟡 P1 - Create InsuranceProvider CRUD Endpoints **Missing:** Complete CRUD for standalone `InsuranceProvider` catalog: 1. Create schema: `InsuranceProviderResponse`, `InsuranceProviderCreate`, `InsuranceProviderUpdate` 2. Create new endpoint file or add to existing router 3. Endpoints: `GET /insurance-providers`, `GET /insurance-providers/{id}`, `POST /insurance-providers`, `PUT /insurance-providers/{id}`, `DELETE /insurance-providers/{id}` **Estimated effort:** 1.5 hours ### 4.4 🟢 P2 - Add InsuranceProvider to Admin UI Once the API endpoints exist, add the InsuranceProvider management to the admin panel. **Estimated effort:** 1 hour --- ## 5. Summary | Category | Status | |----------|--------| | **Database Cleanliness** | ✅ **PASS** - No duplicates, no legacy tables | | **Zombie API Endpoints** | 🔴 **2 CRITICAL** - `reports.py` references `vehicle.vehicle_expenses` | | **InsuranceProvider CRUD** | ❌ **FULLY MISSING** - No standalone endpoints | | **Insurance Policy UPDATE/DELETE** | ❌ **MISSING** - Only GET and POST exist | | **Tax Obligation UPDATE/DELETE** | ❌ **MISSING** - Only GET and POST exist | **Total estimated fix effort:** ~4 hours