Files
mini-erp/mini-frontend/docs/flujo-socios-negocio.md
T
2026-08-17 11:58:38 -05:00

9.5 KiB

Flujo de Procesamiento — Socios de Negocio (Clientes / Proveedores)

Arquitectura General

graph TB
  subgraph "Frontend React (sap-frontend)"
    RT[Router App.tsx]
    CLIST[CustomersList.tsx]
    SLIST[SuppliersList.tsx]
    CVIEW[Customers.tsx]
    SVIEW[Suppliers.tsx]
    BPFORM[BusinessPartnerForm.tsx]
    LIST_TMPL[ListViewTemplate.tsx]
    UI_TABLE[DenseTable.tsx]
    API[api.ts]
  end

  subgraph "Backend FastAPI (mini-backend)"
    ROUTER[routers/business_partners.py<br/>make_crud_router]
    CORE[core/crud_router.py]
    SAP_CORE[core/sap.py]
    SUNAT[Sunat/BusinessPartners/DocumentType]
  end

  subgraph "SAP Business One"
    SL[Service Layer<br/>BusinessPartners]
  end

  RT --> CLIST
  RT --> SLIST
  RT --> CVIEW
  RT --> SVIEW
  CLIST --> LIST_TMPL
  SLIST --> LIST_TMPL
  CVIEW --> BPFORM
  SVIEW --> BPFORM
  LIST_TMPL --> UI_TABLE
  LIST_TMPL --> API
  BPFORM --> API

  API -->|GET/POST/PATCH BusinessPartners| ROUTER
  API -->|GET DocumentType| SUNAT
  ROUTER --> CORE
  CORE --> SAP_CORE
  SAP_CORE -->|B1SESSION| SL

Un solo formulario para Clientes y Proveedores

A diferencia de otros módulos, acá no hay un componente por tipo de socio: BusinessPartnerForm.tsx es un único formulario genérico parametrizado por props, y Customers.tsx / Suppliers.tsx son wrappers finos que solo le pasan configuración distinta:

Prop Customers.tsx Suppliers.tsx
cardType "cCustomer" "cSupplier"
entityLabel "Cliente" "Proveedor"
listPath /socios/clientes /socios/proveedores
codePlaceholder Ej. C00001 Ej. P00001
extraFields [] (sin campos extra hoy) U_EXX_TIPODOCU (Tipo de Documento SUNAT, select)

La diferenciación entre "es cliente" o "es proveedor" para el propio dato de SAP la hace el campo CardType (cCustomer / cSupplier), que viaja fijo en el body del POST/PATCH según qué wrapper se use — el formulario en sí no lo deja editar.

El mecanismo extraFields (tipo ExtraFieldConfig[], definido en BusinessPartnerForm.tsx) permite agregar campos exclusivos de un tipo de socio sin tocar el formulario base: cada entrada define name, label, type (text | email | select), y opcionalmente loadOptions (una función async que trae las opciones del backend). Hoy el único caso real es el campo U_EXX_TIPODOCU en Proveedores, que carga sus opciones desde GET /api/Sunat/BusinessPartners/DocumentType (catálogo SUNAT expuesto vía SAP).

Flujo Vista de Lista → Editar Socio (caso Clientes)

sequenceDiagram
  actor User as Usuario
  participant Router as App.tsx
  participant ListView as CustomersList
  participant LVT as ListViewTemplate
  participant DTable as DenseTable
  participant DetailView as Customers.tsx
  participant Form as BusinessPartnerForm
  participant API as api.ts
  participant Backend as backend business_partners.py
  participant SAP as SAP Service Layer

  User->>Router: /socios/clientes
  Router->>ListView: render
  ListView->>LVT: <ListViewTemplate endpoint="BusinessPartners" baseFilter="CardType eq 'cCustomer'" .../>
  LVT->>API: GET /api/BusinessPartners?...&$filter=CardType eq 'cCustomer'
  API->>Backend: fetch con X-Session-Token
  Backend->>SAP: GET BusinessPartners (con B1SESSION)
  SAP-->>Backend: JSON con datos
  Backend-->>API: JSON response
  API-->>LVT: json.value[]
  LVT->>DTable: render rows
  DTable-->>User: tabla de clientes

  User->>DTable: clic en editar (CardCode "C00001")
  DTable->>Router: navigate("/socios/clientes/mantenimiento?code=C00001")
  Router->>DetailView: render con searchParams.code
  DetailView->>Form: <BusinessPartnerForm cardType="cCustomer" .../>
  Form->>API: GET /api/BusinessPartners/C00001
  API->>Backend: fetch
  Backend->>SAP: GET BusinessPartners('C00001')
  SAP-->>Backend: {CardCode, CardName, FederalTaxID, BPAddresses[], ...}
  Backend-->>API: JSON raw SAP
  API-->>Form: data
  Form->>Form: setFormData(data) — sin mapeo de nombres, usa los campos SAP tal cual
  Form-->>User: formulario con pestañas "General" / "Direcciones"

  User->>Form: edita campos y clic "Actualizar Cliente"
  Form->>API: PATCH /api/BusinessPartners/C00001
  Note over Form,API: body: {CardName, FederalTaxID, Phone1, EmailAddress, BPAddresses[], ...extraFields}
  API->>Backend: fetch PATCH
  Backend->>SAP: PATCH BusinessPartners('C00001')
  SAP-->>Backend: 204 No Content
  Backend-->>API: 204
  API-->>Form: ok
  Form-->>User: "Cliente actualizado correctamente"

El flujo de Proveedores es idéntico, cambiando el baseFilter a CardType eq 'cSupplier' en SuppliersList.tsx y usando Suppliers.tsx como vista de detalle.

Para un socio nuevo (isEditMode = false), el submit hace POST /api/BusinessPartners en vez de PATCH, y tras la respuesta exitosa navega de vuelta al listado (listPath) después de 1.5s.

Árbol de Componentes

App.tsx (BrowserRouter)
└── / (ProtectedRoute → MainLayout)
    └── /socios
        ├── /clientes
        │   └── CustomersList.tsx
        │       └── ListViewTemplate.tsx
        │           ├── SearchInput.tsx
        │           ├── PrimaryButton.tsx
        │           ├── DenseTable.tsx
        │           └── PaginationBar.tsx
        ├── /clientes/mantenimiento
        │   └── Customers.tsx
        │       └── BusinessPartnerForm.tsx (cardType="cCustomer")
        ├── /proveedores
        │   └── SuppliersList.tsx
        │       └── ListViewTemplate.tsx (mismo árbol que CustomersList)
        └── /proveedores/mantenimiento
            └── Suppliers.tsx
                └── BusinessPartnerForm.tsx (cardType="cSupplier", extraFields=[U_EXX_TIPODOCU])

Campos del Formulario

A diferencia del módulo de Lotes, acá no hay mapeo de nombres SAP → amigable: el formulario usa los nombres de campo de SAP directamente como name de cada input (CardCode, CardName, FederalTaxID, Phone1, EmailAddress).

Campo SAP Pestaña Tipo Notas
CardCode General input text (solo lectura en edición) Llave primaria en SAP; no editable una vez creado el socio
CardName General input text Razón social / nombre
CardType General fijo, no editable en UI cCustomer o cSupplier según el wrapper usado
FederalTaxID General input text RUC / documento de identidad
Phone1 General input text Teléfono principal
EmailAddress General input email Correo
U_EXX_TIPODOCU General select (dinámico, solo Proveedores) Catálogo SUNAT vía /api/Sunat/BusinessPartners/DocumentType
BPAddresses[].Street, City, County, GlobalLocationNumber Direcciones input text Dos direcciones por defecto: bo_BillTo (Facturación) y bo_ShipTo (Despacho)

Archivos Clave

Archivo Ruta Propósito
BusinessPartnerForm.tsx src/components/ Formulario genérico único para Clientes y Proveedores, parametrizable
Customers.tsx src/views/socios/ Wrapper que configura BusinessPartnerForm para cCustomer
Suppliers.tsx src/views/socios/ Wrapper que configura BusinessPartnerForm para cSupplier + campo SUNAT
CustomersList.tsx src/views/socios/ Lista de clientes, filtra CardType eq 'cCustomer'
SuppliersList.tsx src/views/socios/ Lista de proveedores, filtra CardType eq 'cSupplier'
ListViewTemplate.tsx src/components/ui/ Template genérico de listado con paginación/búsqueda/filtros
api.ts src/utils/ Cliente HTTP con sesión, token, timeout
business_partners.py mini-backend/routers/ Router CRUD genérico (make_crud_router("BusinessPartners", key_field="CardCode"))
crud_router.py mini-backend/core/ Factory que genera GET/POST/PATCH proxificados hacia SAP
sap.py mini-backend/core/ Proxy universal a SAP Service Layer