# Flujo de Procesamiento — Documentos de Marketing (Ventas / Compras)
Cubre los documentos comerciales de SAP Business One que la app gestiona: Órdenes de Venta, Facturas de Venta, Órdenes de Compra y Facturas de Proveedor. Los cuatro comparten un único formulario genérico (`MarketingDocumentForm.tsx`) parametrizado por vista.
Cada vista de mantenimiento (`Orders.tsx`, `Invoices.tsx`, `PurchaseOrders.tsx`, `PurchaseInvoices.tsx`) es una capa delgada: mantiene el estado del documento (`doc`, tipado como `MarketingDocument` en `types/sapMarketing.ts`) y le pasa a `MarketingDocumentForm` la configuración específica de ese tipo de documento — el componente de formulario en sí no sabe si está editando una orden o una factura, solo renderiza lo que recibe por props.
## Cómo un solo formulario sirve a los 4 documentos
`MarketingDocumentForm` recibe por props todo lo que varía entre documentos:
| Prop | Qué controla | Ejemplo |
|---|---|---|
| `endpoint` | Endpoint del backend a consultar/enviar (`GET/POST/PATCH /api/{endpoint}`) | `"Orders"`, `"Invoices"`, `"PurchaseOrders"`, `"PurchaseInvoices"` |
| `tipoDocumento` | Etiqueta tipada (`OrdenVenta`, `FacturaVenta`, `OrdenCompra`, `FacturaProveedor`) declarada en `DocumentConfigProps` | — no se usa dentro de la lógica del propio formulario en el código leído; parece ser solo metadata para quien lo invoca |
| `titulo` | Encabezado visible del formulario | `"Factura de Ventas (OINV)"` |
| `headerExtraFields` | Campos de cabecera exclusivos de ese documento | Facturas: `FolioPrefixString`, `FolioNumber`, `GroupNum` |
| `lineColumns` | Columnas de la grilla de líneas, cada una con un `show(doc)` opcional para ocultar/mostrar según `DocType` | Compras oculta `ItemCode`/`Almacén` si `DocType` es de servicio |
| `copyFromSources` | Documentos origen habilitados para el botón "Copiar de" (ícono de portapapeles) | Facturas de Venta: Orden de Venta y Entrega; Facturas de Proveedor: Orden de Compra |
| `retencionConfig` / `detraccionConfig` | Solo presentes en `PurchaseInvoices.tsx` — habilitan los bloques de Retención (3%/6%) y Detracción (SUNAT) en el resumen de totales | No se pasan en Órdenes ni en Facturas de Venta |
| `onBuscarSocio`, `onBuscarArticulos`, `onCellSearch` | Callbacks que abren los `ModalSearchTemplate` de Socios, Artículos, Almacén y Código de Impuesto | Cada vista instancia sus propios modales y decide qué endpoint/filtro usar |
El cálculo de totales (subtotal, impuesto 18% fijo, retención, detracción, total general) vive dentro del propio `MarketingDocumentForm`, no en las vistas.
Los íconos de acción (búsqueda, copiar de, catálogo, eliminar línea) son SVG inline definidos en `src/components/ui/Icon.tsx` — no hay emojis ni dependencias externas de iconografía en el proyecto.
### Campo técnico en tooltip, no en el label
Cada `CampoConfig`/`HeaderExtraFieldConfig` de `genericHeaderFields` acepta un `dbField` (= su propio `name`, ya que ambos son literalmente el nombre del campo en `MarketingDocument`) que `FormattedField`/`SelectField` muestran como un ícono ⓘ junto al label; al pasar el mouse aparece el nombre técnico SAP (ej. `DocEntry`, `CardCode`). Antes el label concatenaba ambos textos ("Documento (DocEntry)"), lo que a veces partía el campo en dos líneas y deformaba la grilla — ver `FieldTooltip.tsx`.
### Campos de solo lectura (`readOnly` / `readOnlyOnUpdate`)
`CampoConfig` soporta dos flags que `MarketingDocumentForm` traduce a `mode="display"` (o `disabled` en los `SelectField`):
| Campo | Flag | Comportamiento |
|---|---|---|
| `DocEntry` | `readOnly: true` | Nunca editable (ni al crear ni al actualizar) — lo asigna SAP |
| `DocNum` | `readOnlyOnUpdate: true` | Editable al crear, bloqueado al actualizar |
| `CardCode` | `readOnlyOnUpdate: true` | Editable al crear, bloqueado al actualizar; además oculta el botón de búsqueda de socio cuando está bloqueado |
`readOnlyOnUpdate` se evalúa contra `isEditMode` (`!!docEntry`), definido dentro del propio `MarketingDocumentForm`.
Cada vista de mantenimiento (`Orders`, `Invoices`, `PurchaseOrders`, `PurchaseInvoices`) se carga con `React.lazy()` desde `App.tsx`: el JS de un módulo no se descarga hasta que el usuario navega a él (ver `docs/index.md`).
| `Orders.tsx` / `Invoices.tsx` | `src/views/ventas/` | Configuran el formulario para Órdenes y Facturas de venta |
| `PurchaseOrders.tsx` / `PurchaseInvoices.tsx` | `src/views/compras/` | Configuran el formulario para Compras; Facturas agrega retención/detracción |
| `OrdersList.tsx`, `InvoicesList.tsx`, `PurchaseOrdersList.tsx`, `PurchaseInvoicesList.tsx` | `src/views/ventas/`, `src/views/compras/` | Listados vía `ListViewTemplate`, con `StatusBadge` por estado del documento |
| `CopyFromModal.tsx` | `src/components/ui/` | Modal para copiar líneas desde un documento base (Orden→Entrega→Factura) |
| `ModalSearchTemplate.tsx` | `src/components/ui/` | Modal de búsqueda genérico reutilizado para Socios, Artículos, Almacenes, Códigos de Impuesto |
| `sales.py` / `purchases.py` | `mini-backend/routers/` | Endpoints REST que proxean a SAP Service Layer |
## Hallazgos (código tal cual está hoy, sin corregir)
- **`ventas/entrega` y `compras/recepcion` son stubs**: en `App.tsx` son componentes en línea que solo muestran un título ("Entregas / Guías" / "Recepciones de Compra"), sin listado ni formulario real todavía.
- **`DeliveryNotesList.tsx` existe pero no está enrutado**: el archivo `src/views/ventas/DeliveryNotesList.tsx` está escrito pero ninguna ruta en `App.tsx` lo importa o lo usa — no es alcanzable desde la navegación actual.
- El endpoint `PurchaseDeliveryNotes` (documentado en `mini-backend/docs/api-reference.md`) tampoco tiene vista de frontend correspondiente todavía.