# Nexus Autoparts — SaaS + Aftermarket Design **Date:** 2026-03-15 **Status:** Approved ## Overview Two features for Nexus Autoparts: 1. **SaaS user system** — auth, roles (ADMIN, OWNER, TALLER, BODEGA), warehouse inventory uploads with flexible column mapping, availability/pricing visible to authenticated talleres. 2. **Aftermarket parts cleanup** — migrate 357K AFT- prefixed parts from `parts` table into `aftermarket_parts`, link to OEM parts, fix import pipeline. Architecture: **Monolith approach** — everything in the existing PostgreSQL DB and Flask backend. --- ## Section 1: Authentication & Users ### Tables **`roles`** (existing, update names): | id_rol | name_rol | |--------|----------| | 1 | ADMIN | | 2 | OWNER | | 3 | TALLER | | 4 | BODEGA | **`users`** (existing, extend): ``` id_user (PK) name_user (VARCHAR 200) email (VARCHAR 200, UNIQUE) pass (VARCHAR 200, bcrypt hash) id_rol (FK → roles) business_name (VARCHAR 200) phone (VARCHAR 50) address (TEXT) is_active (BOOLEAN DEFAULT false) created_at (TIMESTAMP DEFAULT now()) last_login (TIMESTAMP) ``` **`sessions`** (new): ``` id_session (PK) user_id (FK → users) refresh_token (VARCHAR 500, UNIQUE) expires_at (TIMESTAMP) created_at (TIMESTAMP DEFAULT now()) ``` ### Auth Flow - Login: `POST /api/auth/login` → JWT access token (15 min) + refresh token (30 days) - Refresh: `POST /api/auth/refresh` → new access token - JWT payload: `user_id`, `role`, `business_name` - Public endpoints: catalog, landing, search, aftermarket list - Protected endpoints: pricing, inventory, admin, POS - Flask middleware validates JWT on protected routes ### Registration - Bodegas and talleres register via form - ADMIN approves accounts (is_active = false by default) - ADMIN can create accounts directly --- ## Section 2: Warehouse Inventory ### Tables **`warehouse_inventory`** (new): ``` id_inventory (PK, BIGINT) user_id (FK → users) part_id (FK → parts) price (NUMERIC 12,2) stock_quantity (INTEGER) min_order_quantity (INTEGER DEFAULT 1) warehouse_location (VARCHAR 100) updated_at (TIMESTAMP DEFAULT now()) UNIQUE (user_id, part_id, warehouse_location) ``` **`inventory_uploads`** (new): ``` id_upload (PK) user_id (FK → users) filename (VARCHAR 200) status (VARCHAR 20: pending, processing, completed, failed) rows_total (INTEGER) rows_imported (INTEGER) rows_errors (INTEGER) error_log (TEXT) created_at (TIMESTAMP DEFAULT now()) completed_at (TIMESTAMP) ``` **`inventory_column_mappings`** (new): ``` id_mapping (PK) user_id (FK → users, UNIQUE) mapping (JSONB) ``` Example JSONB mapping: ```json { "part_number": "COLUMNA_A", "price": "PRECIO_VENTA", "stock": "EXISTENCIAS", "location": "SUCURSAL" } ``` ### Upload Flow 1. Bodega uploads CSV/Excel → `POST /api/inventory/upload` 2. Backend reads file, applies JSONB mapping for that bodega 3. Matches part numbers against `parts.oem_part_number` 4. UPSERT into `warehouse_inventory` 5. Records result in `inventory_uploads` ### Catalog Display (authenticated TALLER) ``` Disponibilidad en bodegas: BODEGA CENTRAL MX | $450.00 | 12 en stock | Guadalajara REFACCIONES DEL NORTE | $485.00 | 3 en stock | Monterrey ``` - Only authenticated talleres see prices and stock - Public users see catalog without prices --- ## Section 3: Aftermarket Parts Migration ### Current Problem - 357,360 parts with `AFT-` prefix in `parts` table treated as OEM - Format: `AFT-{articleNo}-{supplierName}` (e.g., `AFT-AC191-PARTQUIP`) - Description: `"Aftermarket PARTQUIP"` - Have vehicle_parts linked - `aftermarket_parts` table exists but has only 1 record ### Migration Steps **Step 1: Parse AFT- prefix** ``` AFT-AC191-PARTQUIP → part_number: AC191, manufacturer: PARTQUIP AFT-10-0058-Airstal → part_number: 10-0058, manufacturer: Airstal ``` Logic: last segment after last `-` that matches a `manufacturers.name_manufacture` is the manufacturer. The rest (without `AFT-`) is the part number. **Step 2: Find corresponding OEM part** - Search `part_cross_references` where `cross_reference_number` = articleNo and `source_ref` = supplierName - That gives us the `part_id` of the real OEM part - If no cross-reference, search via `vehicle_parts` — OEM parts linked to same vehicles in same category **Step 3: Populate `aftermarket_parts`** ``` oem_part_id → the OEM part found manufacturer_id → FK to manufacturer (PARTQUIP, Airstal, etc.) part_number → AC191 (clean, no prefix) name_aftermarket_parts → original name_part ``` **Step 4: Migrate vehicle_parts** - vehicle_parts pointing to AFT- part get re-linked to the real OEM part - Or deleted if OEM already has that link (ON CONFLICT DO NOTHING) **Step 5: Delete AFT- parts from `parts`** - Once migrated to `aftermarket_parts` and re-linked, remove from `parts` ### Import Pipeline Changes - `import_live.py` and `import_tecdoc_parts.py` stop creating `AFT-` parts in `parts` - Instead insert directly into `aftermarket_parts` with clean manufacturer and part number - `vehicle_parts` only link to real OEM parts ### Catalog Display ``` Alternativas aftermarket: PARTQUIP AC191 | Ver disponibilidad → BOSCH 0 986 AB2 854 | Ver disponibilidad → KAWE 6497 10 | Ver disponibilidad → ``` --- ## Section 4: API Endpoints & Pages ### New Endpoints **Auth:** | Method | Route | Access | Description | |--------|-------|--------|-------------| | POST | `/api/auth/register` | Public | Register taller/bodega | | POST | `/api/auth/login` | Public | Login → JWT + refresh | | POST | `/api/auth/refresh` | Authenticated | Renew access token | | GET | `/api/auth/me` | Authenticated | User profile | **Inventory (BODEGA):** | Method | Route | Access | Description | |--------|-------|--------|-------------| | POST | `/api/inventory/upload` | BODEGA | Upload CSV/Excel | | GET | `/api/inventory/uploads` | BODEGA | Upload history | | GET | `/api/inventory/mapping` | BODEGA | View column mapping | | PUT | `/api/inventory/mapping` | BODEGA | Configure mapping | | GET | `/api/inventory/items` | BODEGA | View own inventory | | DELETE | `/api/inventory/items` | BODEGA | Clear inventory | **Availability (TALLER):** | Method | Route | Access | Description | |--------|-------|--------|-------------| | GET | `/api/parts/{id}/availability` | TALLER | Prices/stock from all bodegas | | GET | `/api/parts/{id}/aftermarket` | Public | Aftermarket alternatives list | **Admin:** | Method | Route | Access | Description | |--------|-------|--------|-------------| | GET | `/api/admin/users` | ADMIN | List users | | PUT | `/api/admin/users/{id}/activate` | ADMIN | Approve/deactivate account | ### New Pages **`login.html`** — Login/registration form. Redirects by role after login. **`bodega.html`** — Warehouse panel: - Configure column mapping - Upload CSV/Excel - View upload history with status - View current inventory with search ### Modified Pages - **`index.html` / `demo.html`** — Add "Disponibilidad en bodegas" section in part detail (TALLER only). Add "Alternativas aftermarket" section (public). - **`admin.html`** — Add "Usuarios" tab for account management. - **`nav.js`** — Add login/logout button, show username. ### Auth Middleware ``` Public: catalog, search, landing, login, aftermarket list TALLER: prices, availability, history BODEGA: upload, mapping, own inventory ADMIN/OWNER: all above + user management + admin panel ```