8.7 KiB
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
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" | 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.
Flujo: Crear una Factura de Venta copiando una Orden
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 (🔍)
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)
├── CopyFromModal.tsx (botón "Copiar de")
└── (por vista) ModalSearchTemplate.tsx ×4 (Socios, Artículos, Almacén, Código de Impuesto)
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 |
sunat.ts |
src/utils/ |
DETRACCION_CODIGOS (porcentajes SUNAT usados en Facturas de Proveedor) |
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/entregaycompras/recepcionson stubs: enApp.tsxson componentes en línea que solo muestran un título ("Entregas / Guías" / "Recepciones de Compra"), sin listado ni formulario real todavía.DeliveryNotesList.tsxexiste pero no está enrutado: el archivosrc/views/ventas/DeliveryNotesList.tsxestá escrito pero ninguna ruta enApp.tsxlo importa o lo usa — no es alcanzable desde la navegación actual.- El endpoint
PurchaseDeliveryNotes(documentado enmini-backend/docs/api-reference.md) tampoco tiene vista de frontend correspondiente todavía.