# Análisis de lógica de negocio – Sistema SCV (Ventas, Caseta y Almacén)

> Documento alineado con el código vigente del repositorio (backend Laravel + frontend Vue 3).  
> Última revisión: mayo 2026.

---

## 1. Contexto general y tipos de sucursal

El sistema SCV administra **ventas y salidas de materiales de construcción** con dos perfiles operativos definidos por la sucursal activa en `ConfiguracionEmpresa`:

| Tipo sucursal | Perfil front (`perfilInterfaz`) | Rol operativo |
|---------------|----------------------------------|---------------|
| `venta` | `VENTA` | Villahermosa — mostrador, ticket + QR |
| `venta_almacen` | `VENTA_ALMACEN` | Macuspana — caseta, oficina, inventario, entregas |

- **Sucursal tipo `venta` (Villahermosa)**  
  - Genera ventas en mostrador (`POST /api/ventas`) con ticket y QR.  
  - **No descuenta stock** local al vender.  
  - Requiere **caja abierta** (`middleware caja.abierta`).  
  - Flujo: venta → ticket/QR → despacho en almacén (Macuspana).

- **Sucursal tipo `venta_almacen` (Macuspana)**  
  - Caseta (Vigilante), Oficina (validación de pedidos), inventario y entregas.  
  - **Descuenta stock** en ventas, importaciones e importación silenciosa por QR.  
  - Cobros en oficina con `registrarPagos`; donativos con observaciones obligatorias.

> La sucursal activa: `ConfiguracionEmpresa::obtenerConfiguracion()->sucursal`.  
> El front deriva `perfilInterfaz` en Vuex y bloquea rutas por perfil en `router/index.js`.

---

## 2. Ventas y pagos (módulo común)

### 2.1 Creación de ventas (`VentaController::store`)

- Ruta: `POST /api/ventas` con middleware `sucursal.tipo:venta` y `caja.abierta`.
- Si no se envía `sucursal_id`, se usa la sucursal de `ConfiguracionEmpresa`.
- Cliente por defecto: `Cliente::clienteMostrador()` cuando `cliente_id` es nulo.
- Se valida caja abierta en la sucursal: `Caja::where('sucursal_id', $sucursal->id)->where('estatus', 'abierta')`.
- Campos clave:
  - `tipo`: `'venta'` o `'donativo'`.
  - `es_donativo`, `observaciones`.
  - `estatus`: `pendiente`, `pendiente_pago`, `pagado`, `parcial`, `entregado`, `cancelado`.
  - `total` = suma de `precio_unitario * cantidad_pedida` por detalle.
- Los `PagoDetalle` creados en el mismo request llevan `caja_id` de la caja abierta (trazabilidad por turno).

### 2.2 Regla de inventario por sucursal

| Sucursal | ¿Descuenta stock al crear/importar venta? |
|----------|---------------------------------------------|
| `venta` | **No** |
| `venta_almacen` | **Sí** (ventas normales y donativos) |

Implementado en `VentaController::store`, `importarPedido` e `importarPedidoSilencio` vía `Producto::reducirStock()`.

### 2.3 Pagos divididos (`pago_detalles`) y donativos

- Tabla `pago_detalles`: `venta_id`, `metodo_pago`, `monto`, `referencia_pago`, `caja_id` (nullable, FK a `cajas`).
- **Ventas normales**: suma de `pagos[]` debe coincidir con `total` en `store` y en `registrarPagos`.
- **Donativos** (`tipo === 'donativo'` o `es_donativo = true`):
  - Observaciones obligatorias.
  - Pueden ir sin pagos (sin ingreso monetario).
  - En Macuspana **sí consumen inventario**.
  - En caja y reportes **no suman a ingresos**; se reportan como valor referencial de material.

### 2.4 Inventario y movimientos

- **Modelo `Producto`**: `precio_unitario`, `stock_actual`, `stock_minimo`, `unidad_medida`, `activo`.
- **Tabla `inventario_movimientos`**: `producto_id`, `tipo`, `cantidad`, `stock_anterior`, `stock_nuevo`, `motivo`, `usuario_id`.
- Movimientos automáticos en: venta/donativo, importar pedido, importación silenciosa, cancelación (devolución), ajuste manual (`InventarioController::ajustar`).

### 2.5 Listado y filtros de ventas (`VentaController::index`)

- Solo ventas de la sucursal activa (`ConfiguracionEmpresa`), salvo `sucursal_id` explícito si no hay sucursal configurada.
- **Query params**: `fecha_desde`, `fecha_hasta`, `estatus` (comma-separated), `folio` (LIKE), `per_page` (1–100, default 20), `page`.
- Front: `ventas/historial.vue` con `ListFiltersBar` (rango de fechas, estatus, folio); default últimos **7 días**.

---

## 3. Punto de venta – Villahermosa (`/ventas`)

### 3.1 Interfaz (`ventas/index.vue`)

- Requiere caja abierta (`useCaja`, alerta + link a `/cajas` si no hay apertura).
- **Catálogo vía modal** `PosProductosModal.vue`:
  - Búsqueda por nombre.
  - Controles +/- cantidad en m³.
  - Badge “En carrito” si el producto ya está en `detalles`.
  - El área principal muestra CTA “Agregar productos” (no grid de tarjetas en pantalla).
- Carrito lateral: líneas, total, finalizar venta (modal pagos/donativo).
- **No muestra** ventas recientes en el POS (eliminado del layout).
- Productos en POS para perfil `VENTA_ALMACEN` (si accediera): solo Polvo, Rezaga, Balastre — en la práctica Macuspana no usa esta ruta (`blockedByVentaAlmacen` en router).

### 3.2 Finalizar venta

- Modal Bootstrap: donativo, pagos múltiples, cambio en efectivo.
- Tras éxito en sucursal `tipo_sucursal === "venta"`: `VentaTicketPreview` con impresión.

---

## 4. Flujo Caseta → Oficina (solo Macuspana)

### 4.1 Generación de pedido en caseta (`VentaController::generarQrVigilanteLocal`)

- Sucursal `venta_almacen`; productos permitidos: **Polvo, Rezaga, Balastre** (`CasetaService::isProductAllowedForLocalQr`).
- Sin `venta_id`: crea `Venta` con `estatus = pendiente_pago`, `viajes_permitidos`, `viajes_usados = 0`.
- Crea `VigilanteQr` con payload QR plano (`uuid|idSucursal|idProd|cant|...`).

### 4.2 Escaneo y accesos

- `validarQrVigilante`: QR local vs externo; control de viajes.
- `EntregaController::validarYRegistrarAcceso`: foto obligatoria; importación silenciosa si QR externo sin venta local.

### 4.3 Cobro en Oficina (`cajas/validar-pedidos.vue`)

- `GET /api/ventas/pedidos-pendientes-pago` — filtro opcional `folio`.
- `POST /api/ventas/{venta}/registrar-pagos` — requiere `caja.abierta`.
- Pagos múltiples o donativo; `estatus → pagado`; genera `qr_payload` si faltaba.

---

## 5. Entregas parciales

- `EntregaController::registrar`: cantidad vs `cantidad_pedida - cantidad_entregada`; actualiza estatus `parcial` / `entregado`.
- `validarYRegistrarAcceso`: entregas por QR con `uuid_qr` y foto.

---

## 6. Caja, gastos, ingresos y corte X/Z

### 6.1 Modelo de datos

| Tabla / columna | Uso |
|-----------------|-----|
| `cajas` | Turnos: `monto_inicial`, `monto_final`, `estatus`, fechas |
| `cajas.reporte_x`, `cajas.reporte_z` | JSON persistido al cerrar (historial de cortes) |
| `gastos` | Salidas de efectivo del turno (`caja_id`, descripción, monto) |
| `ingresos` | Entradas manuales de efectivo (depósitos, fondos adicionales) |
| `pago_detalles.caja_id` | Trazabilidad de cobros al turno |

### 6.2 Servicio (`CajaService`)

- **`calcularResumen(Caja, ?monto_final_simulado)`** — usado en caja abierta y vista previa de corte:
  - Ventas del período (apertura → cierre o `now`).
  - Separa ingreso vs donativo.
  - `total_cobrado` y `pagos_por_metodo` solo de ventas **no donativo**.
  - **Efectivo esperado** = `monto_inicial + total_efectivo + total_ingresos_manuales - total_gastos`.
- **`procesarCierre`**: cierra caja, calcula reportes, **persiste** `reporte_x` y `reporte_z` en BD.

### 6.3 API (`CajaController`, `GastoController`, `IngresoController`)

| Método | Ruta | Descripción |
|--------|------|-------------|
| GET | `/api/cajas` | Listado paginado. Filtros: `sucursal_id`, `estatus`, `fecha_desde`, `fecha_hasta` (sobre `fecha_cierre`), `per_page`, `page` |
| GET | `/api/cajas/caja-abierta` | Caja abierta de sucursal configurada |
| POST | `/api/cajas/apertura` | Una caja abierta por sucursal |
| POST | `/api/cajas/corte` | Cierre + reportes X/Z |
| GET | `/api/cajas/{caja}` | Detalle (incluye reportes guardados) |
| GET | `/api/cajas/{caja}/resumen` | Resumen en vivo; query `monto_final` para preview Z |
| GET/POST | `/api/cajas/{caja}/gastos` | Listar / registrar (solo caja abierta) |
| GET/POST | `/api/cajas/{caja}/ingresos` | Listar / registrar (solo caja abierta) |

### 6.4 Reporte Z (cierre)

- `efectivo_esperado`: fórmula con ingresos manuales.
- `monto_final`: efectivo contado físicamente.
- `diferencia` = `efectivo_esperado - monto_final` (sobrante/faltante/cuadre).
- Donativos: valor referencial en X/Z, **no** en `total_cobrado`.

### 6.5 Interfaz de caja (`/cajas`)

- **Gestión de caja** (`cajas/index.vue`): pestañas **Resumen | Gastos | Ingresos | Corte de caja**.
  - Resumen en vivo vía `GET .../resumen`.
  - Modales para gasto e ingreso.
  - Corte: monto contado + preview `CorteCajaReport` (reportes X y Z).
  - Tras cierre: modal con reporte completo.
- **Historial de cortes** (`/cajas/historial`): listado de cajas `estatus=cerrada`, filtros por fecha de cierre, modal “Ver corte” con reportes persistidos.
- Composable global `useCaja.js`: estado de caja abierta para header, sidebar y POS.

### 6.6 Corrección conocida (caja abierta en front)

- `getCajaAbierta()` devuelve `{ data: caja | null }`. El front debe usar `res?.data ?? null`, no `res.data ?? res`, para no tratar `{ data: null }` como caja abierta.

---

## 7. Reportes y Dashboard

### 7.1 Dashboard (`GET /api/dashboard/estadisticas`)

- Adaptado a sucursal activa y tipo.
- Incluye: boletos (legacy), ventas, y para `venta_almacen`: donativos, alertas inventario, pendientes de cobro, entregas del día, etc.

### 7.2 Reportes de salidas (boletos legacy)

- Rutas bajo `middleware sucursal.tipo:venta_almacen`: `/api/reportes/salidas`, `estadisticas`, `exportar-csv`.
- Front: `reportes/salidas.vue` con filtros fecha/estatus/folio.

### 7.3 Reportes de ventas

- `estadisticasVentas`, `exportarVentasExcel`, `exportarVentasPdf`, `exportarTicketsVillahermosaExcel`.
- Estadísticas de ingreso excluyen donativos.

---

## 8. Catálogos y administración

### 8.1 Productos

- API: `activo`, `todos` (como clientes).
- Front: filtro estado con `ListFiltersBar` en `productos/lista`.

### 8.2 Clientes

- API: `activo`, `todos`; default solo activos.
- Front: filtro Activos / Inactivos / Todos con `ListFiltersBar`.

### 8.3 Usuarios

- API `GET /api/users/all`: `q` (nombre/email), `estatus` (`activo` default).
- Front: búsqueda + estatus con `ListFiltersBar`.

### 8.4 Configuración hardware

- API: `tipo`, `activo`.
- Front: filtros tipo y estado con `ListFiltersBar`.

### 8.5 Inventario

- `GET /api/inventario?q=` — búsqueda por nombre.
- `GET /api/inventario/alertas?umbral=`
- `GET /api/inventario/movimientos?producto_id=`

---

## 9. Filtros de listados (patrón front)

Componente reutilizable: **`ListFiltersBar.vue`** + composable **`useListFilters.js`** (`fechaHoy`, `rangoUltimosDias`, `paramsLimpios`).

| Vista | Filtros servidor | Default |
|-------|------------------|---------|
| `ventas/historial` | fecha_desde/hasta, estatus, folio, paginación | 7 días |
| `cajas/historial` | fecha cierre desde/hasta, estatus=cerrada | 30 días |
| `clientes/lista` | activo / todos | Activos |
| `productos/lista` | activo / todos | Activos |
| `users/lista` | q, estatus | activo |
| `configuracion-hardware/lista` | tipo, activo | — |
| `reportes/salidas` | fecha, estatus, folio | (existente) |
| `inventario/index` | q | — |

Los listados con `v-client-table` mantienen además **búsqueda en cliente** sobre filas ya cargadas.

---

## 10. Visibilidad de módulos (menú y rutas)

### 10.1 Perfil `VENTA` (Villahermosa)

| Visible | Oculto / bloqueado |
|---------|-------------------|
| Nueva venta (`/ventas`) si caja abierta | Importar pedido, Entregas, Vigilante, Salidas boletos |
| Historial de ventas | POS directo en Macuspana |
| Productos, Clientes | |
| Gestión de caja, Historial de cortes | |
| Reportes → Ventas | |

### 10.2 Perfil `VENTA_ALMACEN` (Macuspana)

| Visible | Oculto / bloqueado |
|---------|-------------------|
| Entregas, Vigilante | Nueva venta (`/ventas`), Historial ventas Villahermosa |
| Inventario, Alertas stock | |
| Gestión de caja, Historial cortes, Validación pedidos | |
| Reportes → Salidas + Ventas | |

### 10.3 Restricción productos caseta

- **Generar QR local**: solo Polvo, Rezaga, Balastre (backend + front `productosParaGenerar`).
- **Escanear QR**: cualquier material del payload (importación silenciosa).

---

## 11. Rutas front principales

| Ruta | Vista | Permisos / notas |
|------|-------|------------------|
| `/ventas` | POS | `ventas.crear` \| `ventas.ver`; solo perfil VENTA |
| `/ventas/historial` | Historial ventas | `ventas.ver`; perfil VENTA |
| `/ventas/importar-pedido` | Importar QR manual | Bloqueado perfil VENTA |
| `/entregas`, `/entregas/vigilante` | Entregas / Vigilante | Macuspana |
| `/cajas` | Gestión de caja | `cajas.abrir_cerrar` \| `gastos.registrar` |
| `/cajas/historial` | Historial cortes | Idem |
| `/cajas/validar-pedidos` | Oficina cobros | Macuspana |
| `/inventario`, `/inventario/alertas` | Inventario | Macuspana |
| `/productos/lista`, `/clientes/lista` | Catálogos | Según permiso |
| `/reportes/ventas`, `/reportes/salidas` | Reportes | Según permiso y tipo sucursal |

---

## 12. Diagrama de flujo resumido

```text
Villahermosa (VENTA):
  Apertura caja → POST /api/cajas/apertura
  POS (/ventas):
    PosProductosModal → carrito → POST /api/ventas (caja.abierta)
    → Ticket + QR; sin descuento stock
  Historial → GET /api/ventas?filtros
  Cierre → POST /api/cajas/corte → reportes X/Z guardados

Macuspana (VENTA_ALMACEN):
  Caseta:
    generarQrVigilanteLocal (Polvo/Rezaga/Balastre)
    → Venta pendiente_pago + VigilanteQr
    validarYRegistrarAcceso → Entrega + foto
    → importarPedidoSilencio si QR externo

  Oficina:
    validar-pedidos → registrarPagos (caja.abierta)
    → pagado + qr_payload

  Caja (mismo módulo que Villahermosa):
    Resumen | Gastos | Ingresos | Corte
    efectivo_esperado = inicial + efectivo_ventas + ingresos_manuales - gastos
    Historial cortes → GET /api/cajas?estatus=cerrada

  Inventario:
    ventas/importaciones ↓ stock
    ajustes → inventario_movimientos
```

---

## 13. Referencia rápida API (autenticadas)

### Ventas
- `GET /api/ventas` — filtros + paginación  
- `POST /api/ventas` — Villahermosa, caja abierta  
- `POST /api/ventas/importar-pedido` — Macuspana  
- `POST /api/ventas/{id}/registrar-pagos` — Macuspana, caja abierta  
- `POST /api/ventas/{id}/cancelar`  
- Vigilante: `generar-qr-local`, `validar-qr`, `consultar-uuid`, `definir-viajes`

### Caja
- `GET/POST` apertura, corte, caja-abierta, listado, detalle, resumen, gastos, ingresos

### Inventario
- `GET /inventario`, `POST /inventario/ajustar`, alertas, movimientos

### Catálogos
- `GET/POST/PUT/DELETE` productos, clientes  
- `GET /users/all` con `q`, `estatus`

---

Este documento refleja la lógica de negocio **actual**: sucursales y perfiles, POS con modal de productos, caja con gastos/ingresos/corte persistido e historial, filtros de listados, pagos con `caja_id`, donativos segregados en arqueo, y flujos Caseta→Oficina en Macuspana.
