From fa7015e642ba74d9f0d0c9a5d992ec806abf1533 Mon Sep 17 00:00:00 2001 From: consultoria-as Date: Thu, 2 Apr 2026 03:38:10 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20documentacion=20completa=20=E2=80=94=20?= =?UTF-8?q?README,=20guia=20de=20uso,=20instalacion,=20API=20POS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 304 +++++---- docs/API-POS.md | 1432 +++++++++++++++++++++++++++++++++++++++++++ docs/GUIA-DE-USO.md | 377 ++++++++++++ docs/INSTALACION.md | 272 ++++++++ 4 files changed, 2222 insertions(+), 163 deletions(-) create mode 100644 docs/API-POS.md create mode 100644 docs/GUIA-DE-USO.md create mode 100644 docs/INSTALACION.md diff --git a/README.md b/README.md index 13160a7..9d359c7 100644 --- a/README.md +++ b/README.md @@ -1,205 +1,183 @@ # Nexus Autoparts -**Sistema de catalogo de autopartes con navegacion jerarquica, similar a 7zap.com/RockAuto.** +**POS + Catalogo de autopartes para refaccionarias mexicanas.** -Plataforma SaaS que conecta talleres con bodegas/distribuidores. Permite buscar partes OEM y aftermarket por vehiculo (marca, modelo, ano, motor), gestionar inventario de bodegas, y consultar disponibilidad y precios en tiempo real. +Sistema integral que combina un catalogo publico de autopartes con un Punto de Venta multi-tenant, disenado para refaccionarias independientes. Incluye inventario, facturacion CFDI 4.0, contabilidad, y caja registradora. + +--- + +## Dos Sistemas + +| Sistema | Puerto | Descripcion | +|---------|--------|-------------| +| **Web publica** | 5000 | Landing page + catalogo publico con navegacion por vehiculo | +| **POS multi-tenant** | 5001 | Sistema de punto de venta completo, instalable como PWA | + +### Web publica (puerto 5000) + +- Landing page para talleres y refaccionarias +- Catalogo publico: Marca > Modelo > Ano > Motor > Categoria > Partes +- Selector de regiones: Mexico/USA/Canada, Europa, Asia, Todos +- Busqueda combinada vehiculo + parte +- VIN decoder via NHTSA API +- Diagramas explosionados con hotspots + +### POS multi-tenant (puerto 5001) + +- **Multi-tenant**: base de datos aislada por cliente +- **PWA**: instalable en tablets/celulares, modo offline +- **10 pantallas**: Login, Catalogo, Inventario, POS, Clientes, Facturacion, Contabilidad, Dashboard, Reportes, Configuracion +- **81+ endpoints API** organizados en 9 blueprints +- **2 temas**: Industrial oscuro + Moderno claro (toggle en sidebar) +- **Auth por PIN** con JWT + rate limiting + bloqueo por dispositivo +- **5 roles**: Dueno, Admin, Cajero, Almacenista, Contador + +#### Modulos del POS + +| Modulo | Funcionalidad | +|--------|--------------| +| Catalogo | Navegacion TecDoc por vehiculo, busqueda inteligente, stock local, alternativas, carrito | +| Inventario | CRUD productos, compras, ajustes, transferencias, toma fisica, alertas, reportes (ABC, valoracion, sin movimiento) | +| Punto de Venta | Ventas, cotizaciones, apartados, credito, atajos F1-F6, metodos de pago mixtos | +| Clientes | Datos fiscales, 3 tiers de precio, credito con limites, vehiculos, historial | +| Facturacion | CFDI 4.0 (Ingreso, Egreso, Pago), cola de timbrado, cancelacion SAT | +| Contabilidad | Polizas automaticas, catalogo SAT, balanza, estado de resultados, balance general, antiguedad | +| Caja Registradora | Apertura, movimientos, corte X, corte Z, multi-caja | +| Dashboard | Ventas del dia vs meta, cajas activas, alertas | +| Reportes | Financieros y operativos | +| Configuracion | Negocio, sucursales, empleados, roles, temas | + +--- ## Tech Stack | Componente | Tecnologia | |------------|-----------| | Backend | Python 3, Flask | -| Base de datos | PostgreSQL | -| ORM / SQL | SQLAlchemy (`text()` raw SQL) | -| Autenticacion | JWT (PyJWT) + bcrypt | -| Data import | TecDoc via Apify, NHTSA VIN API | +| Base de datos | PostgreSQL (master + tenant DBs) | +| SQL | psycopg2 raw SQL (sin ORM) | +| Auth | JWT (PyJWT) + bcrypt PIN hashing | +| CFDI | lxml (XML builder CFDI 4.0) | | Frontend | HTML/CSS/JS vanilla (sin framework) | -| Dependencias extra | openpyxl (Excel), csv (CSV import) | +| Estilos | CSS custom properties (design tokens) | +| PWA | Service Worker + manifest | +| Data import | TecDoc via Apify, NHTSA VIN API | -## Estadisticas de la Base de Datos +--- -- **1.4M+** partes OEM -- **300K+** partes aftermarket -- **13M+** cross-references (numeros alternos, supersesiones, intercambios) -- **12B+** vehicle-part links (fitment) -- **100+** marcas, miles de modelos, anos 1956-2026 +## Base de Datos -## Features +| Metrica | Cantidad | +|---------|----------| +| Partes OEM | 1.5M+ | +| Partes aftermarket | 304K+ | +| Cross-references | 15.8M+ | +| Marcas TecDoc importadas | 6 (Toyota, Nissan, Renault, Ford, VW, Honda) | +| Rango de anos | 1956-2026 | -- **Catalogo de autopartes** con navegacion jerarquica: Marca > Modelo > Ano > Motor > Categoria > Grupo > Parte -- **TecDoc integration** (via Apify) para importar datos OEM y aftermarket de Europa/Mexico -- **SaaS multi-tenant** con roles: `ADMIN`, `OWNER`, `TALLER`, `BODEGA` -- **JWT authentication** con access tokens (15 min) y refresh tokens (30 dias) -- **Gestion de inventario** para bodegas con mapeo flexible de columnas CSV/Excel -- **Disponibilidad de partes** en multiples bodegas con precios comparativos -- **Alternativas aftermarket** con cross-references por cada parte OEM -- **Panel de administracion** con gestion de usuarios, import/export CSV, CRUD de categorias/grupos/partes/fabricantes/fitment -- **Busqueda full-text** en el catalogo de partes (PostgreSQL `tsvector`) -- **Busqueda combinada** vehiculo + parte (e.g., "Toyota Corolla 2020 frenos") -- **VIN decoder** via NHTSA API con cache en base de datos -- **Diagramas explosionados** con hotspots clickeables -- **Vehicle-to-part linking** (12B+ vehicle_parts links) +--- ## Quick Start -### Requisitos previos +### Requisitos -- Python 3.8+ -- PostgreSQL con la base `nexus_autoparts` +- Python 3.11+ +- PostgreSQL 15+ +- Linux (Debian/Ubuntu/Raspberry Pi OS) -### Instalacion - -```bash -cd /home/Autopartes -pip install -r requirements.txt -``` - -### Ejecutar el servidor +### Web publica (catalogo) ```bash cd /home/Autopartes/dashboard +pip install -r ../requirements.txt python3 server.py +# Acceder: http://localhost:5000 ``` -El servidor arranca en `http://localhost:5000`. - -### Importar datos de TecDoc +### POS ```bash -# Fase 1: descargar datos de TecDoc a JSON +cd /home/Autopartes/pos +pip install -r requirements.txt + +# Crear primer tenant +python3 -c " +from services.tenant_manager import provision_tenant +result = provision_tenant('Mi Refaccionaria', rfc='RFC000000AAA', owner_name='Admin', owner_pin='1234') +print(result) +" + +# Iniciar +python3 app.py +# Acceder: http://localhost:5001/pos/login +# PIN: 1234 +``` + +### Importar datos TecDoc + +```bash +# Descargar datos de TecDoc a JSON python3 scripts/import_tecdoc.py download -# Fase 2: importar JSON a PostgreSQL +# Importar JSON a PostgreSQL python3 scripts/import_tecdoc.py import # Ver progreso python3 scripts/import_tecdoc.py status ``` -### Importar partes y linkar vehiculos +--- -```bash -# Importar partes TecDoc (OEM + aftermarket) -python3 scripts/import_tecdoc_parts.py - -# Importar datos en vivo desde TecDoc API -python3 scripts/import_live.py - -# Crear links vehiculo-parte (fitment masivo) -python3 scripts/link_vehicle_parts.py - -# Migrar datos aftermarket -python3 scripts/migrate_aftermarket.py - -# Aplicar schema SaaS (roles, users, inventory tables) -python3 scripts/migrate_saas_schema.py -``` - -## Paginas del Dashboard - -| Ruta | Archivo | Descripcion | -|------|---------|-------------| -| `/login.html` | `login.html` | Login con JWT | -| `/demo.html` | `demo.html` | Catalogo publico / demo | -| `/admin` | `admin.html` | Panel de administracion (ADMIN/OWNER) | -| `/bodega.html` | `bodega.html` | Gestion de inventario para bodegas | -| `/tienda.html` | `tienda.html` | Vista de tienda/catalogo para talleres | -| `/pos.html` | `pos.html` | Punto de venta | -| `/captura.html` | `captura.html` | Captura de partes | -| `/cuentas.html` | `cuentas.html` | Gestion de cuentas | - -## API Overview - -Documentacion completa en [`docs/API.md`](docs/API.md). - -### Auth (`/api/auth/`) -- `POST /api/auth/register` - Registrar usuario (TALLER/BODEGA) -- `POST /api/auth/login` - Login, retorna access + refresh tokens -- `POST /api/auth/refresh` - Renovar access token -- `GET /api/auth/me` - Info del usuario autenticado - -### Catalogo (`/api/`) -- `GET /api/brands` - Listar marcas -- `GET /api/models?brand=X` - Modelos por marca -- `GET /api/years?brand=X&model=Y` - Anos disponibles -- `GET /api/engines?brand=X&model=Y&year=Z` - Motores disponibles -- `GET /api/categories` - Categorias de partes (arbol jerarquico) -- `GET /api/parts?group_id=X` - Partes por grupo -- `GET /api/parts/{id}/alternatives` - Alternativas aftermarket -- `GET /api/parts/{id}/cross-references` - Cross-references -- `GET /api/search?q=...` - Busqueda combinada (vehiculos + partes + aftermarket) - -### Inventario (`/api/inventory/`) -- `GET/PUT /api/inventory/mapping` - Mapeo de columnas CSV -- `POST /api/inventory/upload` - Subir CSV/Excel de inventario -- `GET /api/inventory/items` - Listar inventario propio -- `DELETE /api/inventory/items` - Limpiar inventario - -### Disponibilidad y Aftermarket -- `GET /api/parts/{id}/availability` - Bodegas con stock (auth: TALLER/ADMIN/OWNER) -- `GET /api/parts/{id}/aftermarket` - Alternativas aftermarket + cross-refs (publico) - -### Admin (`/api/admin/`) -- `GET /api/admin/users` - Listar usuarios (auth: ADMIN/OWNER) -- `PUT /api/admin/users/{id}/activate` - Activar/desactivar usuario -- `GET /api/admin/stats` - Estadisticas del catalogo -- CRUD completo: categories, groups, parts, manufacturers, aftermarket, crossref, fitment -- Import/Export CSV: `POST /api/admin/import/{type}`, `GET /api/admin/export/{type}` - -### VIN Decoder -- `GET /api/vin/decode/{vin}` - Decodificar VIN via NHTSA API -- `GET /api/vin/{vin}/parts` - Partes para un VIN decodificado -- `GET /api/vin/{vin}/match?mye_id=X` - Vincular VIN manualmente a vehiculo - -## Scripts - -| Script | Funcion | -|--------|---------| -| `import_tecdoc.py` | Descarga datos de TecDoc API (vehiculos, modelos, marcas) a JSON | -| `import_tecdoc_parts.py` | Importa partes OEM y aftermarket desde TecDoc | -| `import_live.py` | Importacion en vivo desde TecDoc API | -| `link_vehicle_parts.py` | Genera links vehiculo-parte (fitment masivo) | -| `migrate_aftermarket.py` | Migra datos aftermarket a la estructura normalizada | -| `migrate_saas_schema.py` | Crea tablas SaaS: sessions, warehouse_inventory, roles, etc. | -| `import_phase1.py` | Importacion inicial fase 1 | -| `run_all_brands.sh` | Script auxiliar para importar todas las marcas | - -## Configuracion - -Archivo principal: [`config.py`](config.py) - -| Variable | Default | Descripcion | -|----------|---------|-------------| -| `DATABASE_URL` | `postgresql://nexus:...@localhost/nexus_autoparts` | PostgreSQL connection string | -| `JWT_SECRET` | `nexus-saas-secret-change-in-prod-2026` | Secreto para firmar tokens JWT | -| `JWT_ACCESS_EXPIRES` | `900` (15 min) | Duracion del access token en segundos | -| `JWT_REFRESH_EXPIRES` | `2592000` (30 dias) | Duracion del refresh token en segundos | - -## Arquitectura - -Documentacion detallada en [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md). +## Estructura del Proyecto ``` - +------------------+ - | TecDoc (Apify) | - +--------+---------+ - | - download/import - | - v -+----------+ +--------+---------+ +----------------+ -| Frontend |<--->| Flask Server |<--->| PostgreSQL | -| (HTML/JS)| | (server.py) | | nexus_autoparts| -+----------+ +--------+---------+ +----------------+ - | - JWT auth (PyJWT) - | - +------------+------------+ - | | | - TALLER BODEGA ADMIN - (consulta) (inventario) (gestion) +/home/Autopartes/ + dashboard/ # Web publica (puerto 5000) + server.py # Flask server principal + shared.css # CSS compartido + nav.js # Navegacion compartida + *.html # Paginas del dashboard + pos/ # POS multi-tenant (puerto 5001) + app.py # Flask app factory + config.py # Configuracion + tenant_db.py # Conexiones por tenant + middleware.py # Auth + permisos + blueprints/ # 9 blueprints (auth, config, inventory, catalog, pos, customers, cashregister, invoicing, accounting) + services/ # Logica de negocio (pos_engine, inventory_engine, cfdi_builder, accounting_engine, etc.) + templates/ # 10 HTML templates + static/ # CSS, JS, assets + migrations/ # SQL migrations + seed/ # Datos semilla + scripts/ # Import TecDoc, migraciones, utilidades + vehicle_database/ # Schema SQL maestro + docs/ # Documentacion + data/ # Datos TecDoc descargados (JSON) ``` --- -**Nexus Autoparts** - Tu conexion directa con las partes que necesitas +## Deployment + +El sistema puede correr en: + +- **Servidor dedicado** o **VPS** (recomendado para produccion) +- **Raspberry Pi 5** (8GB RAM + SSD NVMe, ideal para refaccionaria individual) + +Ver [docs/INSTALACION.md](docs/INSTALACION.md) para instrucciones detalladas. + +--- + +## Documentacion + +| Documento | Descripcion | +|-----------|-------------| +| [docs/GUIA-DE-USO.md](docs/GUIA-DE-USO.md) | Guia de uso del POS (espanol) | +| [docs/INSTALACION.md](docs/INSTALACION.md) | Guia de instalacion (espanol) | +| [docs/API-POS.md](docs/API-POS.md) | Referencia completa de la API del POS | +| [docs/API.md](docs/API.md) | API del catalogo publico | +| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | Arquitectura del sistema | +| [docs/DATABASE.md](docs/DATABASE.md) | Esquema de base de datos | + +--- + +**Nexus Autoparts** -- Tu conexion directa con las partes que necesitas diff --git a/docs/API-POS.md b/docs/API-POS.md new file mode 100644 index 0000000..8c48f38 --- /dev/null +++ b/docs/API-POS.md @@ -0,0 +1,1432 @@ +# API Reference -- Nexus Autoparts POS + +Base URL: `http://[IP]:5001` + +Todas las rutas empiezan con `/pos/api/`. Salvo indicacion contraria, todos los endpoints requieren autenticacion via header `Authorization: Bearer `. + +--- + +## Autenticacion + +Todos los endpoints (excepto login y employees list) requieren un JWT token obtenido via `/pos/api/auth/login`. El token se envia en el header: + +``` +Authorization: Bearer eyJhbGciOi... +``` + +El token contiene: `tenant_id`, `employee_id`, `name`, `role`, `branch_id`, `permissions`, `device_id`. + +Duracion por defecto: 8 horas. + +--- + +## 1. Auth (3 endpoints) + +Prefix: `/pos/api/auth` + +### POST /login + +Login con PIN. No requiere autenticacion. + +**Request body:** +```json +{ + "tenant_id": 1, + "pin": "1234", + "device_id": "tablet-01", + "branch_id": 1 +} +``` + +**Response 200:** +```json +{ + "token": "eyJhbGciOi...", + "employee": { + "id": 1, + "name": "Admin", + "role": "owner", + "branch_id": 1, + "max_discount_pct": 100.0 + }, + "permissions": ["pos.sell", "pos.discount", "..."] +} +``` + +**Errores:** 400 (faltan campos), 401 (PIN incorrecto), 404 (tenant no encontrado), 429 (rate limit) + +### GET /employees/:tenant_id + +Lista empleados para la pantalla de login. No requiere autenticacion. + +**Response 200:** +```json +{ + "data": [ + {"id": 1, "name": "Admin", "initials": "AD", "role": "owner", "role_label": "Dueno"} + ] +} +``` + +### GET /me + +Info del empleado actual desde el token. + +**Response 200:** +```json +{ + "employee_id": 1, + "name": "Admin", + "role": "owner", + "tenant_id": 1, + "branch_id": 1, + "permissions": ["pos.sell", "..."] +} +``` + +--- + +## 2. Config (5 endpoints) + +Prefix: `/pos/api/config` + +### GET /branches + +Lista sucursales del tenant. + +**Permiso:** cualquier empleado autenticado + +**Response 200:** +```json +{ + "data": [ + {"id": 1, "name": "Sucursal Centro", "address": "Av. Juarez 100", "phone": "555-0001", "is_active": true} + ] +} +``` + +### POST /branches + +Crear sucursal. + +**Permiso:** `config.edit` + +**Request body:** +```json +{ + "name": "Sucursal Norte", + "address": "Blvd. Independencia 200", + "phone": "555-0002" +} +``` + +**Response 201:** +```json +{"id": 2, "message": "Branch created"} +``` + +### GET /employees + +Lista empleados con detalle. + +**Permiso:** `config.view` + +**Response 200:** +```json +{ + "data": [ + { + "id": 1, "name": "Admin", "email": null, "phone": null, + "role": "owner", "branch_id": 1, "branch_name": "Sucursal Centro", + "max_discount_pct": 100.0, "is_active": true + } + ] +} +``` + +### POST /employees + +Crear empleado. + +**Permiso:** `config.edit` + +**Request body:** +```json +{ + "name": "Juan Lopez", + "role": "cashier", + "pin": "5678", + "email": "juan@correo.com", + "phone": "555-1234", + "branch_id": 1, + "max_discount_pct": 10 +} +``` + +Roles validos: `admin`, `cashier`, `warehouse`, `accountant` + +**Response 201:** +```json +{"id": 2, "message": "Employee created"} +``` + +### GET /theme + +Obtener tema actual del tenant. + +**Permiso:** cualquier empleado autenticado + +**Response 200:** +```json +{ + "theme": "default", + "variables": { + "--color-primary": "#1a73e8", + "--color-secondary": "#5f6368", + "--color-accent": "#ff6b35", + "--color-bg": "#ffffff", + "--color-surface": "#f8f9fa", + "--color-text": "#202124", + "--color-border": "#dadce0", + "--font-display": "'Sora', sans-serif", + "--font-body": "'Plus Jakarta Sans', sans-serif", + "--font-mono": "'JetBrains Mono', monospace", + "--radius": "8px" + } +} +``` + +--- + +## 3. Inventory (22 endpoints) + +Prefix: `/pos/api/inventory` + +### GET /items + +Lista productos con stock. Soporta busqueda y paginacion. + +**Permiso:** `inventory.view` + +**Query params:** +- `q` (string) -- buscar por nombre, part_number o barcode +- `page` (int, default 1) +- `per_page` (int, default 50, max 200) +- `branch_id` (int) -- filtrar por sucursal +- `category_id` (int) -- filtrar por categoria +- `low_stock` (bool) -- solo productos bajo minimo + +**Response 200:** +```json +{ + "data": [ + { + "id": 1, "part_number": "04465-02220", "barcode": "7501234567890", + "name": "Pastillas de freno delanteras", "description": "...", + "branch_id": 1, "branch_name": "Sucursal Centro", + "category_id": 5, "brand": "Toyota", "unit": "PZA", + "cost": 150.00, "price_1": 350.00, "price_2": 300.00, "price_3": 280.00, + "tax_rate": 0.16, "min_stock": 5, "max_stock": 50, "location": "A-3-2", + "image_url": null, "catalog_part_id": 12345, "stock": 23 + } + ], + "pagination": {"page": 1, "per_page": 50, "total": 1234, "total_pages": 25} +} +``` + +### GET /items/:item_id + +Detalle de un producto con stock e historial de movimientos. + +**Permiso:** `inventory.view` + +### POST /items + +Crear producto. Genera codigo de barras automaticamente si no se proporciona. + +**Permiso:** `inventory.create` + +**Request body:** +```json +{ + "part_number": "04465-02220", + "name": "Pastillas de freno delanteras", + "description": "Pastillas ceramicas originales Toyota", + "branch_id": 1, + "category_id": 5, + "brand": "Toyota", + "unit": "PZA", + "cost": 150.00, + "price_1": 350.00, + "price_2": 300.00, + "price_3": 280.00, + "tax_rate": 0.16, + "min_stock": 5, + "max_stock": 50, + "location": "A-3-2", + "initial_stock": 10, + "catalog_part_id": 12345 +} +``` + +**Response 201:** +```json +{"id": 1, "barcode": "7501234567890", "message": "Item created"} +``` + +### PUT /items/:item_id + +Actualizar datos de un producto. + +**Permiso:** `inventory.edit` + +**Request body:** Mismos campos que POST (parcial, solo campos a actualizar). + +### POST /purchase + +Registrar compra a proveedor. Incrementa stock. + +**Permiso:** `inventory.create` + +**Request body:** +```json +{ + "supplier": "Distribuidora Central", + "invoice_number": "FAC-001", + "items": [ + {"inventory_id": 1, "quantity": 20, "unit_cost": 145.00} + ], + "notes": "Compra semanal" +} +``` + +### POST /adjustment + +Ajuste manual de inventario. + +**Permiso:** `inventory.adjust` + +**Request body:** +```json +{ + "inventory_id": 1, + "quantity": -2, + "reason": "Merma por dano en almacen" +} +``` + +### POST /transfer + +Transferir stock entre sucursales. + +**Permiso:** `inventory.transfer` + +**Request body:** +```json +{ + "from_branch_id": 1, + "to_branch_id": 2, + "items": [ + {"inventory_id": 1, "quantity": 5} + ], + "notes": "Reabasto sucursal norte" +} +``` + +### POST /return + +Registrar devolucion de producto. Incrementa stock. + +**Permiso:** `inventory.create` + +**Request body:** +```json +{ + "sale_id": 123, + "items": [ + {"inventory_id": 1, "quantity": 1} + ], + "reason": "Parte equivocada" +} +``` + +### POST /physical-count/start + +Iniciar toma fisica de inventario. + +**Permiso:** `inventory.adjust` + +**Request body:** +```json +{ + "branch_id": 1, + "items": [ + {"inventory_id": 1, "counted_quantity": 22} + ] +} +``` + +**Response 201:** +```json +{"count_id": 1, "items_counted": 1, "message": "Physical count started"} +``` + +### POST /physical-count/approve + +Aprobar toma fisica y aplicar ajustes. + +**Permiso:** `inventory.adjust` + +**Request body:** +```json +{ + "count_id": 1 +} +``` + +### GET /alerts + +Obtener alertas de inventario (stock cero, bajo minimo, sobre maximo). + +**Permiso:** `inventory.view` + +**Response 200:** +```json +{ + "zero_stock": [...], + "low_stock": [...], + "over_stock": [...] +} +``` + +### GET /items/:item_id/history + +Historial de movimientos de un producto. + +**Permiso:** `inventory.view` + +**Query params:** +- `limit` (int, default 50) + +### GET /reports/valuation + +Reporte de valorizacion del inventario. + +**Permiso:** `inventory.view` + +**Query params:** +- `branch_id` (int, opcional) + +**Response 200:** +```json +{ + "total_cost_value": 125000.00, + "total_sale_value": 350000.00, + "item_count": 450, + "items": [...] +} +``` + +### GET /reports/abc + +Clasificacion ABC de productos por volumen de venta. + +**Permiso:** `inventory.view` + +**Query params:** +- `branch_id` (int, opcional) +- `days` (int, default 90) + +### GET /reports/no-movement + +Productos sin movimiento en un periodo. + +**Permiso:** `inventory.view` + +**Query params:** +- `branch_id` (int, opcional) +- `days` (int, default 60) + +### GET /reports/low-stock + +Productos por debajo del stock minimo. + +**Permiso:** `inventory.view` + +**Query params:** +- `branch_id` (int, opcional) + +### GET /reports/branch-comparison + +Comparativo de stock entre sucursales. + +**Permiso:** `inventory.view` + +**Query params:** +- `inventory_id` (int, opcional) -- para un producto especifico + +### GET /categories + +Lista categorias de inventario. + +**Permiso:** `inventory.view` + +### GET /brands + +Lista marcas de productos en inventario. + +**Permiso:** `inventory.view` + +### POST /generate-barcode + +Generar codigo de barras para un producto. + +**Permiso:** `inventory.create` + +**Response 200:** +```json +{"barcode": "7501234567891"} +``` + +--- + +## 4. Catalog (9 endpoints) + +Prefix: `/pos/api/catalog` + +Estos endpoints consultan la base maestra TecDoc y enriquecen con stock local del tenant. + +### GET /brands + +Lista marcas de vehiculos con partes. + +**Permiso:** `catalog.view` + +**Response 200:** +```json +{ + "data": [ + {"id": 1, "name": "TOYOTA", "part_count": 245000} + ] +} +``` + +### GET /models + +Modelos por marca. + +**Permiso:** `catalog.view` + +**Query params:** +- `brand_id` (int, requerido) + +### GET /years + +Anos disponibles por modelo. + +**Permiso:** `catalog.view` + +**Query params:** +- `model_id` (int, requerido) + +### GET /engines + +Motorizaciones por modelo + ano. + +**Permiso:** `catalog.view` + +**Query params:** +- `model_id` (int, requerido) +- `year_id` (int, requerido) + +### GET /categories + +Categorias de partes para un vehiculo. + +**Permiso:** `catalog.view` + +**Query params:** +- `mye_id` (int, requerido) -- ID de model_year_engine + +### GET /groups + +Subcategorias (grupos) de partes. + +**Permiso:** `catalog.view` + +**Query params:** +- `mye_id` (int, requerido) +- `category_id` (int, requerido) + +### GET /parts + +Partes con enriquecimiento de stock local. + +**Permiso:** `catalog.view` + +**Query params:** +- `mye_id` (int, requerido) +- `group_id` (int, requerido) +- `page` (int, default 1) +- `per_page` (int, default 30) +- `branch_id` (int, opcional) + +**Response 200:** +```json +{ + "data": [ + { + "id": 12345, "part_number": "04465-02220", "name": "Pastillas de freno", + "manufacturer": "TOYOTA", "local_stock": 5, "local_price": 350.00, + "alternatives_count": 3 + } + ], + "pagination": {"page": 1, "per_page": 30, "total": 15, "total_pages": 1} +} +``` + +### GET /part/:part_id + +Detalle completo: stock local, stock en bodegas, alternativas aftermarket, cross-references. + +**Permiso:** `catalog.view` + +**Query params:** +- `branch_id` (int, opcional) + +### GET /search + +Busqueda inteligente por numero de parte o texto. + +**Permiso:** `catalog.view` + +**Query params:** +- `q` (string, requerido, minimo 2 caracteres) +- `limit` (int, default 50) +- `branch_id` (int, opcional) + +--- + +## 5. POS / Sales (16 endpoints) + +Prefix: `/pos/api` + +### POST /sales + +Crear venta. + +**Permiso:** `pos.sell` + +**Request body:** +```json +{ + "items": [ + {"inventory_id": 1, "quantity": 2, "unit_price": 350.00, "discount_pct": 0, "tax_rate": 0.16} + ], + "customer_id": null, + "payment_method": "efectivo", + "sale_type": "cash", + "register_id": 1, + "amount_paid": 812.00, + "payment_details": [], + "notes": "" +} +``` + +`sale_type`: `cash` (contado), `credit` (credito), `mixed` + +`payment_method`: `efectivo`, `transferencia`, `tarjeta`, `mixto` + +Para pagos mixtos, `payment_details` es un array: +```json +[ + {"method": "efectivo", "amount": 500.00}, + {"method": "tarjeta", "amount": 312.00, "reference": "AUTH-12345"} +] +``` + +**Response 201:** +```json +{ + "sale_id": 1, + "folio": "V-0001", + "total": 812.00, + "change": 0.00, + "items_count": 1 +} +``` + +### GET /sales + +Lista ventas con filtros. + +**Permiso:** `pos.view` + +**Query params:** +- `date_from` (YYYY-MM-DD) +- `date_to` (YYYY-MM-DD) +- `employee_id` (int) +- `customer_id` (int) +- `status` (string: `completed`, `cancelled`, `returned`) +- `register_id` (int) +- `page` (int, default 1) +- `per_page` (int, default 50, max 200) + +### GET /sales/:sale_id + +Detalle de una venta con items, pagos y datos del cliente. + +**Permiso:** `pos.view` + +### PUT /sales/:sale_id/cancel + +Cancelar una venta. Restaura inventario. + +**Permiso:** `pos.cancel` + +**Request body:** +```json +{ + "reason": "Cliente cambio de opinion" +} +``` + +### GET /sales/last + +Obtener la ultima venta (para reimprimir ticket). + +**Permiso:** `pos.sell` + +### POST /quotations + +Crear cotizacion (venta sin cobrar). + +**Permiso:** `pos.sell` + +**Request body:** +```json +{ + "items": [ + {"inventory_id": 1, "quantity": 2, "unit_price": 350.00, "discount_pct": 0, "tax_rate": 0.16} + ], + "customer_id": 5, + "notes": "Cotizacion para taller Perez", + "valid_days": 15 +} +``` + +### GET /quotations + +Lista cotizaciones. + +**Permiso:** `pos.view` + +**Query params:** +- `status` (string: `active`, `converted`, `expired`, `cancelled`) +- `customer_id` (int) +- `page` (int, default 1) +- `per_page` (int, default 50) + +### GET /quotations/:quot_id + +Detalle de una cotizacion. + +**Permiso:** `pos.view` + +### POST /quotations/:quot_id/convert + +Convertir cotizacion a venta. Aplica los mismos campos que POST /sales. + +**Permiso:** `pos.sell` + +**Request body:** +```json +{ + "payment_method": "efectivo", + "register_id": 1, + "amount_paid": 812.00 +} +``` + +### PUT /quotations/:quot_id/cancel + +Cancelar cotizacion. + +**Permiso:** `pos.cancel` + +### POST /layaways + +Crear apartado con pago parcial. + +**Permiso:** `pos.sell` + +**Request body:** +```json +{ + "items": [ + {"inventory_id": 1, "quantity": 1, "unit_price": 5000.00, "discount_pct": 0, "tax_rate": 0.16} + ], + "customer_id": 5, + "down_payment": 2000.00, + "payment_method": "efectivo", + "register_id": 1, + "notes": "Apartado motor de arranque", + "due_date": "2026-04-30" +} +``` + +### GET /layaways + +Lista apartados. + +**Permiso:** `pos.view` + +**Query params:** +- `status` (string: `active`, `completed`, `cancelled`) +- `customer_id` (int) +- `page`, `per_page` + +### GET /layaways/:layaway_id + +Detalle de un apartado con items y pagos. + +**Permiso:** `pos.view` + +### POST /layaways/:layaway_id/payment + +Registrar abono a un apartado. + +**Permiso:** `pos.sell` + +**Request body:** +```json +{ + "amount": 1500.00, + "payment_method": "efectivo", + "register_id": 1 +} +``` + +### POST /layaways/:layaway_id/complete + +Completar apartado (cuando el saldo llega a cero). Descuenta inventario. + +**Permiso:** `pos.sell` + +### PUT /layaways/:layaway_id/cancel + +Cancelar apartado. + +**Permiso:** `pos.cancel` + +**Request body:** +```json +{ + "reason": "Cliente no completo pagos" +} +``` + +--- + +## 6. Customers (7 endpoints) + +Prefix: `/pos/api/customers` + +### GET / + +Lista clientes con busqueda. + +**Permiso:** `customers.view` + +**Query params:** +- `q` (string) -- busca por nombre, RFC, telefono, razon social +- `page` (int, default 1) +- `per_page` (int, default 50, max 200) +- `branch_id` (int) + +### GET /:customer_id + +Detalle de un cliente. + +**Permiso:** `customers.view` + +### POST / + +Crear cliente. + +**Permiso:** `customers.create` + +**Request body:** +```json +{ + "name": "Taller Perez", + "rfc": "XAXX010101000", + "razon_social": "Taller Mecanico Perez SA de CV", + "regimen_fiscal": "601", + "codigo_postal": "06600", + "uso_cfdi": "G03", + "email": "taller@correo.com", + "phone": "555-9876", + "address": "Calle Reforma 50", + "price_tier": "taller", + "credit_limit": 50000.00, + "branch_id": 1 +} +``` + +`price_tier`: `mostrador` (default), `taller`, `mayoreo` + +### PUT /:customer_id + +Actualizar cliente. + +**Permiso:** `customers.edit` + +### GET /:customer_id/statement + +Estado de cuenta del cliente. + +**Permiso:** `customers.view` + +**Query params:** +- `date_from` (YYYY-MM-DD) +- `date_to` (YYYY-MM-DD) + +**Response 200:** +```json +{ + "customer": {"id": 5, "name": "Taller Perez", "credit_limit": 50000.00, "balance": 12500.00}, + "transactions": [ + {"date": "2026-03-28", "type": "sale", "reference": "V-0045", "amount": 5000.00, "balance": 12500.00}, + {"date": "2026-03-25", "type": "payment", "reference": "PAG-012", "amount": -3000.00, "balance": 7500.00} + ] +} +``` + +### GET /:customer_id/vehicles + +Vehiculos asociados al cliente. + +**Permiso:** `customers.view` + +### POST /:customer_id/payment + +Registrar pago/abono del cliente. + +**Permiso:** `customers.edit` + +**Request body:** +```json +{ + "amount": 5000.00, + "payment_method": "transferencia", + "reference": "SPEI-20260401-001", + "notes": "Abono parcial" +} +``` + +--- + +## 7. Cash Register (7 endpoints) + +Prefix: `/pos/api/register` + +### POST /open + +Abrir caja registradora. + +**Permiso:** `pos.sell` + +**Request body:** +```json +{ + "register_number": 1, + "opening_amount": 2000.00 +} +``` + +**Response 201:** +```json +{ + "register_id": 1, + "register_number": 1, + "opened_at": "2026-04-01T08:00:00", + "opening_amount": 2000.00 +} +``` + +**Errores:** 400 (ya tiene caja abierta) + +### GET /current + +Obtener la caja abierta del empleado actual. + +**Permiso:** `pos.sell` + +**Response 200:** +```json +{ + "register_id": 1, + "register_number": 1, + "opened_at": "2026-04-01T08:00:00", + "opening_amount": 2000.00, + "current_cash": 5430.00, + "sales_count": 12, + "sales_total": 8750.00 +} +``` + +### POST /movement + +Registrar entrada o salida de efectivo. + +**Permiso:** `pos.sell` + +**Request body:** +```json +{ + "register_id": 1, + "type": "out", + "amount": 500.00, + "reason": "Compra de papeleria" +} +``` + +`type`: `in` (entrada) o `out` (salida) + +### GET /cut-x + +Corte X (consulta sin cerrar caja). + +**Permiso:** `pos.sell` + +**Response 200:** +```json +{ + "register_id": 1, + "opening_amount": 2000.00, + "sales_cash": 3500.00, + "sales_card": 2100.00, + "sales_transfer": 1150.00, + "movements_in": 0.00, + "movements_out": 500.00, + "expected_cash": 5000.00, + "sales_count": 12, + "total_sales": 6750.00 +} +``` + +### POST /cut-z + +Corte Z (cierre de caja). + +**Permiso:** `pos.sell` + +**Request body:** +```json +{ + "register_id": 1, + "counted_amount": 4980.00 +} +``` + +**Response 200:** +```json +{ + "register_id": 1, + "expected_cash": 5000.00, + "counted_amount": 4980.00, + "difference": -20.00, + "closed_at": "2026-04-01T22:00:00", + "summary": {"..."} +} +``` + +### GET /history + +Historial de cortes. + +**Permiso:** `pos.sell` + +**Query params:** +- `date_from` (YYYY-MM-DD) +- `date_to` (YYYY-MM-DD) +- `page`, `per_page` + +### GET /daily-summary + +Resumen consolidado del dia (todas las cajas). + +**Permiso:** `pos.sell` + +**Query params:** +- `date` (YYYY-MM-DD, default hoy) + +--- + +## 8. Invoicing (6 endpoints) + +Prefix: `/pos/api/invoicing` + +### POST /invoice + +Generar factura CFDI 4.0 desde una venta. + +**Permiso:** `invoicing.create` + +**Request body:** +```json +{ + "sale_id": 45, + "tipo_comprobante": "I", + "uso_cfdi": "G03", + "forma_pago": "01", + "metodo_pago": "PUE" +} +``` + +`tipo_comprobante`: `I` (Ingreso), `E` (Egreso), `P` (Pago) + +`metodo_pago`: `PUE` (Pago en Una sola Exhibicion) o `PPD` (Pago en Parcialidades o Diferido) + +**Response 201:** +```json +{ + "cfdi_id": 1, + "uuid": null, + "status": "pending", + "xml_preview": "