Files
mini-erp/mini-frontend/docs/flujo-documentos-marketing.md
T
GeorgeandClaude Sonnet 5 f58f4292c3 Backport de mejoras genéricas de frontend (desde cliente/tagumedica)
- Nuevo Icon.tsx (reemplaza emojis en toda la app), FieldTooltip, ConfirmModal, PageLoader, SecondaryButton, hook useSapCatalog, utils/errors (getErrorMessage).
- App.tsx: code-splitting con lazy()+Suspense+PageLoader para todas las rutas.
- MainLayout.tsx: menú usa Icon.tsx en vez de emojis.
- FormattedField/SelectField: soporte de dbField+tooltip, multiline.
- DenseTable/FilterBar/ListViewTemplate: onClear, mejoras varias.
- PrimaryButton: variante de tamaño sm/md.
- Renombres *View.tsx para uniformar el naming de vistas de detalle/mantenimiento (Dashboard, Items, BatchNumberDetails, Customers, Suppliers, Invoices, Orders, PurchaseOrders, PurchaseInvoices) y SapLogin -> SapLoginForm.
- BatchNumberDetailsList: filtros y columna de descripción de artículo.
- Configs de reportes e iconos de módulos actualizados a Icon.tsx.
- Ajustes de paleta en los 4 temas CSS.
- Docs actualizadas.

Se excluyó explícitamente del backport (queda solo en cliente/tagumedica, no es reusable sin adaptar a los UDFs propios de cada cliente):
- Vistas InmovilizarLotesView, LiberarLotesView, ConfirmarLiberacionLoteView y sus rutas/ítems de menú.
- types/sapInventoryLock.ts (contrato con los UDFs U_SIS_MOVINV/U_EXX_TIPOOPER/U_SIS_TIPDESP/U_SIS_MOTALM de Tagumedica).
- En BatchForm.tsx: los 3 campos U_SIS_MOTEST/U_SIS_COMEST/U_SIS_ACCTOM (Motivo/Comentario/Acción), manteniendo el resto de mejoras genéricas del archivo y reactivando el campo estándar "Detalles/Observaciones" que en tagumedica había quedado comentado sin justificación genérica.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 14:28:29 -05:00

196 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
## Arquitectura General
```mermaid
graph TB
subgraph "Frontend React (sap-frontend)"
OL[OrdersList.tsx]
O[Orders.tsx]
IL[InvoicesList.tsx]
I[Invoices.tsx]
POL[PurchaseOrdersList.tsx]
PO[PurchaseOrders.tsx]
PIL[PurchaseInvoicesList.tsx]
PI[PurchaseInvoices.tsx]
FORM[MarketingDocumentForm.tsx]
COPY[CopyFromModal.tsx]
SEARCH[ModalSearchTemplate.tsx]
BADGE[StatusBadge.tsx]
LVT[ListViewTemplate.tsx]
TYPES[types/sapMarketing.ts]
API[api.ts]
end
subgraph "Backend FastAPI (mini-backend)"
SALES[routers/sales.py]
PURCH[routers/purchases.py]
CORE[core/sap.py]
end
subgraph "SAP Business One"
SL[Service Layer<br/>Orders / Invoices / PurchaseOrders / PurchaseInvoices]
end
OL --> LVT
IL --> LVT
POL --> LVT
PIL --> LVT
LVT --> BADGE
O --> FORM
I --> FORM
PO --> FORM
PI --> FORM
FORM --> COPY
FORM --> SEARCH
FORM --> TYPES
FORM --> API
API -->|GET/POST/PATCH| SALES
API -->|GET/POST/PATCH| PURCH
SALES --> CORE
PURCH --> CORE
CORE --> SL
```
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`.
## Flujo: Crear una Factura de Venta copiando una Orden
```mermaid
sequenceDiagram
actor User as Usuario
participant List as InvoicesList
participant View as Invoices.tsx
participant Form as MarketingDocumentForm
participant Copy as CopyFromModal
participant API as api.ts
participant Backend as backend sales.py
participant SAP as SAP Service Layer
User->>List: /ventas/facturas → "Nueva Factura"
List->>View: navigate("/ventas/facturas/mantenimiento")
View->>Form: <MarketingDocumentForm endpoint="Invoices" copyFromSources=[Orden, Entrega] />
Form->>API: GET /api/Currencies
API->>Backend: fetch
Backend->>SAP: GET Currencies
SAP-->>Form: lista de monedas
User->>Form: busca y selecciona Cliente (ícono de lupa)
Form->>View: onBuscarSocio() abre ModalSearchTemplate(BusinessPartners)
View-->>Form: setDoc({CardCode, CardName})
User->>Form: clic "Copiar de" → "Orden de Venta"
Form->>Copy: <CopyFromModal source={Orden} cardCode docType />
Copy->>API: GET /api/Orders?filtro por CardCode/BaseType
API->>Backend: fetch
Backend->>SAP: GET Orders
SAP-->>Copy: documentos abiertos del cliente
User->>Copy: selecciona una orden
Copy-->>Form: onConfirm(lines, sourceLabel)
Note over Form: Si ya hay líneas, confirm() pregunta reemplazar o agregar
Form->>Form: setDoc({DocumentLines: lines}) — cada línea trae BaseType/BaseEntry/BaseLine
User->>Form: clic "Crear Documento"
Form->>API: POST /api/Invoices (doc + DocumentLines, sin BaseRef)
API->>Backend: fetch POST
Backend->>SAP: POST Invoices (con líneas BaseEntry → SAP cierra la orden origen)
SAP-->>Backend: 201 Created
Backend-->>Form: ok
Form-->>User: "Documento creado exitosamente en SAP"
```
El mismo patrón aplica a Facturas de Proveedor copiando desde Órdenes de Compra, solo cambian `endpoint` y `copyFromSources`.
## Árbol de Componentes
```
App.tsx (BrowserRouter)
└── / (ProtectedRoute → MainLayout)
├── /ventas
│ ├── /ordenes → OrdersList.tsx → ListViewTemplate → StatusBadge
│ ├── /ordenes/mantenimiento → Orders.tsx → MarketingDocumentForm
│ ├── /entrega → stub "Entregas / Guías" (sin vista real, ver Hallazgos)
│ └── /facturas
│ ├── (lista) → InvoicesList.tsx → ListViewTemplate → StatusBadge
│ └── /mantenimiento → Invoices.tsx → MarketingDocumentForm
└── /compras
├── /ordenes → PurchaseOrdersList.tsx → ListViewTemplate → StatusBadge
├── /ordenes/mantenimiento → PurchaseOrders.tsx → MarketingDocumentForm
├── /recepcion → stub "Recepciones de Compra" (sin vista real, ver Hallazgos)
└── /facturas
├── (lista) → PurchaseInvoicesList.tsx → ListViewTemplate → StatusBadge
└── /mantenimiento → PurchaseInvoices.tsx → MarketingDocumentForm (+ Retención/Detracción)
MarketingDocumentForm.tsx
├── FormattedField.tsx / SelectField.tsx (cabecera, con FieldTooltip por campo)
├── PrimaryButton.tsx / SecondaryButton.tsx (submit / cancelar)
├── Icon.tsx (íconos SVG: buscar, copiar, catálogo, eliminar)
├── PageLoader.tsx ("Cargando documento..." mientras hace fetch)
├── CopyFromModal.tsx (botón "Copiar de")
└── (por vista) ModalSearchTemplate.tsx ×4 (Socios, Artículos, Almacén, Código de Impuesto)
```
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`).
## Archivos Clave
| Archivo | Ruta | Propósito |
|---|---|---|
| `MarketingDocumentForm.tsx` | `src/components/` | Formulario genérico: cabecera, grilla de líneas, totales, copiar-de, submit GET/POST/PATCH |
| `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 |
| `sapMarketing.ts` | `src/types/` | `MarketingDocument`, `DocumentLine`, `DocumentConfigProps`, `CopyFromSource`, `LineColumnConfig` (`key`/`name` tipados como `keyof DocumentLine`/`keyof MarketingDocument`, no `string` suelto) |
| `sunat.ts` | `src/utils/` | `DETRACCION_CODIGOS` (porcentajes SUNAT usados en Facturas de Proveedor) |
| `Icon.tsx` | `src/components/ui/` | Registro de íconos SVG inline (reemplaza los emojis que se usaban antes en botones y menú) |
| `FieldTooltip.tsx` | `src/components/ui/` | Ícono ⓘ con el nombre técnico SAP del campo, usado por `FormattedField`/`SelectField` |
| `errors.ts` | `src/utils/` | `getErrorMessage(err, fallback)` — evita `catch (err: any)` en cada formulario |
| `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.