Skip to content

ABM de Clientes - Documentacion Tecnica Frontend

Modulo: ventas Feature: ABM de Clientes Fecha: 2026-02-09

DOCUMENTACION RETROSPECTIVA - Generada a partir de codigo implementado el 2026-02-09.


Referencia de Negocio


Estado de Migracion

El frontend del ABM de Clientes se encuentra en un estado de migracion parcial:

FuncionalidadEstadoTecnologia
Formulario CRUD (crear/editar)LegacyVanilla JS (no migrado a React)
Tabla de listado (DataTables SSR)LegacyVanilla JS + DataTables jQuery
Hook de datos de clientesMigradoReact + TypeScript + Axios
Informe Lista de ClientesMigradoReact + TypeScript + Zod + RHF
Informe Comprobante por ConceptosMigradoReact + TypeScript + Zod + RHF

Formulario CRUD (Vanilla JS Legacy)

El formulario principal de alta y modificacion de clientes esta implementado en vanilla JS, no en React. Es un componente modularizado y reutilizable.

Archivos del Formulario CRUD

ArchivoProposito
view/mod-ventas/form-cliente.phpTemplate PHP con layout AdminLTE (card, breadcrumb, sidebar)
js/view/mod-ventas/form-cliente.jsInicializador: carga el componente createFormCliente, determina modo (alta/edicion)
js/components/forms/general/cliente.jsComponente reutilizable createFormCliente - logica del formulario completo

Flujo del Formulario

  1. El template PHP (form-cliente.php) carga el layout AdminLTE con un contenedor vacio #form_container
  2. El script form-cliente.js se ejecuta como modulo ES6
  3. Extrae el parametro id de la URL para determinar si es alta o modificacion
  4. Invoca createFormCliente(updateData, callback) del componente reutilizable
  5. El componente carga su HTML desde php/components/mod-venta/forms/cliente via fetch
  6. Monta el formulario en el contenedor y configura event listeners

Componente createFormCliente

Ubicacion: js/components/forms/general/cliente.jsProposito: Componente reutilizable de formulario de clientes Patron: Funcion factory que crea y retorna un elemento DOM con todo el formulario

Parametros:

  • updateData (object): Si incluye id, opera en modo edicion; vacio para alta
  • options (object): { inModal: false, isShowing: false } para uso en modales
  • onSubmitCallback (function): Callback post-submit (por defecto redirige a listado)

Estructura de datos interna (objeto Cliente):

  • id, nombre, domicilio1, domicilio2, email
  • localidad (objeto), condicion_iva (objeto), iibb (objeto), vendedor (objeto), cartera (objeto)
  • identificacion, comision, nro_iibb
  • tiene_ctacte (boolean), margen_credito, margen_fact_imp
  • rel_lista ('S'/'N'), lista

Metodos del objeto Cliente:

  • setCondicionIva(condicion) - Configura condicion IVA y ajusta requerimiento de CUIT
  • setIngresosBrutos(iibb) - Configura IIBB y habilita/deshabilita campo numero IIBB
  • permiteCtaCte(flag) - Muestra/oculta panel de cuenta corriente (jQuery collapse)
  • relacionaLista(flag) - Habilita/deshabilita campo de lista de precios propia
  • getData() - Retorna solo propiedades de datos (excluye metodos)

Campos del formulario:

  • Nombre (input text, required)
  • Email (input email, configurado con setInputEmail)
  • Domicilio 1 y 2 (input text)
  • Localidad (autocomplete jQuery UI)
  • Telefono 1 y 2 (input, configurado con setInputTelefono)
  • Condicion IVA (select)
  • Identificacion DNI/CUIT (input, configurado con setInputIdentificacion)
  • Ingresos Brutos tipo (select)
  • Numero IIBB (input, habilitado condicionalmente)
  • Vendedor (autocomplete)
  • Comision (input numerico con badge)
  • Cartera (componente React CarteraSelect montado via mountComponent)
  • Checkbox cuenta corriente (toggle panel CtaCte)
  • Margen credito, Saldo cliente, Margen facturas impagas (campos CtaCte)
  • Checkbox lista de precios propia + Numero de lista
  • Fecha alta y Fecha ultimo movimiento (solo lectura en edicion)

Integracion hibrida vanilla JS + React: El campo de Cartera usa un componente React (CarteraSelect) montado dentro del formulario vanilla JS usando la funcion mountComponent de dist/main.js. Esto demuestra el patron de migracion progresiva donde componentes React se insertan en contextos legacy.

Dependencias legacy:

  • ApiRequest (js/middleware/API.js) - Comunicacion con backend
  • getPermisosModulos (js/auth/permisos.js) - Permisos del usuario
  • empresaSingleton (js/util/empresaSingleton.js) - Datos de empresa
  • jQuery UI Autocomplete - Campos de localidad y vendedor
  • jQuery Bootstrap Collapse - Panel de cuenta corriente
  • SweetAlert2 - Popups de carga y error
  • DataTables jQuery - Tabla de listado (en la vista de listado, no en este formulario)

Componentes Implementados (React/TypeScript)

ListaClientesApp

Ubicacion: ts/ventas/informes/ListaClientesApp.tsxProposito: Aplicacion React standalone para el informe de lista de clientes Tipo: Aplicacion montable (no mountable component)

Estructura:

  • Componente funcional simple que renderiza ListaClientesForm dentro de un container fluid
  • Se auto-inicializa buscando el elemento #lista-clientes-app en el DOM al cargar la pagina
  • Usa la funcion mountApp de main.tsx para montar la aplicacion React

Montaje en PHP/HTML:

html
<div id="lista-clientes-app"></div>
<script type="module" src="dist/ventas/informes/lista-clientes.js"></script>

ListaClientesForm

Ubicacion: ts/ventas/components/ListaClientesForm.tsxProposito: Formulario de parametros para generar el informe de lista de clientes Lineas: 245

Dependencias:

  • react-hook-form + @hookform/resolvers/zod para manejo de formulario
  • CarteraSelect de ts/ctacte/components/ para seleccion de carteras
  • useCarteraData de ts/ctacte/hooks/ para datos de carteras
  • FormField, RangeSelector de ts/core/components/form/
  • generarInforme de js/middleware/informes.js (legacy)
  • ApiRequest de js/middleware/API.js (legacy)
  • getPermisosModulos de js/auth/permisos.js (legacy)

Estado Local (useState):

VariableTipoProposito
ctacteEnabledbooleanControla visibilidad del filtro de cuenta corriente
carteraEnabledbooleanControla visibilidad del filtro de cartera

Configuracion del Formulario (React Hook Form):

CampoTipoDefaultDescripcion
codReportenumber6Codigo fijo del reporte
carteraDesdenumber | nullnullID de cartera desde
carteraHastanumber | nullnullID de cartera hasta
ordenamientonumber (1-3)11=Codigo, 2=Alfabeto, 3=Vendedor
filtroCtaCtenumber (1-3)31=Tienen CtaCte, 2=No tienen, 3=Todos
ordenarPorLocalidadesbooleanfalseOrdenar resultado por localidad

Logica de inicializacion:

  1. Al montar, obtiene permisos del usuario via getPermisosModulos() (legacy)
  2. Obtiene datos de empresa via ApiRequest.get('empres') (legacy)
  3. Habilita filtro de cuenta corriente si permisos.modulo_ctacte === 1
  4. Habilita filtro de cartera si empres.cartera === true

UI Condicional:

  • El selector de rango de carteras (RangeSelector + CarteraSelect) solo se muestra si carteraEnabled
  • Los radio buttons de filtro de cuenta corriente solo se muestran si ctacteEnabled
  • El boton de submit muestra "Generando..." con spinner durante el envio

Submit: Invoca generarInforme(data) del sistema legacy de informes.

Cancelar: Invoca la funcion global back('mv') del sistema legacy para navegar al menu de ventas.


Hook de Datos

useClienteData

Ubicacion: ts/ventas/hooks/useClienteData.tsProposito: Obtener lista de clientes del backend Lineas: 54

Firma:

typescript
function useClienteData(
  scope: string = "min",
  skip: boolean = false,
): {
  records: ClienteRecord[];
  loading: boolean;
  error: string | null;
};

Tipo exportado:

typescript
interface ClienteRecord {
  id: number;
  nombre: string;
  identificacion?: string | null;
}

Comportamiento:

  • Usa useState + useEffect para fetch de datos (patron legacy, no TanStack Query)
  • Llama a api.get('ordcon', { params: { scope } }) directamente al endpoint legacy
  • El parametro skip permite omitir la carga (util para carga condicional)
  • Retorna array vacio si la respuesta no es array
  • Maneja errores con fallback a mensaje generico

Endpoint consumido: GET backend/ordcon?scope={scope}

Nota: Este hook usa Axios (api.ts) directamente en lugar de TanStack Query. Es un patron de migracion intermedio.


Schemas de Validacion (Zod)

listaClientesSchema

Ubicacion: ts/ventas/schemas/informes.ts

Definicion:

typescript
z.object({
  codReporte: z.number(),
  carteraDesde: z.number().nullable(),
  carteraHasta: z.number().nullable(),
  ordenamiento: z.number().min(1).max(3),
  filtroCtaCte: z.number().min(1).max(3),
  ordenarPorLocalidades: z.boolean(),
});

Refinamiento custom:

  • carteraDesde no puede ser mayor que carteraHasta (validacion cruzada)
  • Mensaje: "La cartera desde no puede ser mayor a la cartera hasta"
  • Path del error: carteraDesde

Tipo generado: ListaClientesFormData = z.infer<typeof listaClientesSchema>


Integracion con Backend

Endpoints Consumidos

ComponenteMetodoEndpointProposito
useClienteDataGETbackend/ordconObtener lista de clientes
ListaClientesFormGET/session/empres (legacy API)Datos de configuracion de empresa
ListaClientesForm-getPermisosModulos() (legacy)Permisos del usuario
ListaClientesForm-generarInforme() (legacy)Generar reporte PDF

Integracion con Sistema Legacy

El componente ListaClientesForm depende fuertemente del sistema legacy:

  1. generarInforme (js/middleware/informes.js): Servicio legacy para generacion de informes PDF
  2. ApiRequest (js/middleware/API.js): Cliente HTTP legacy (FormData + fetch)
  3. getPermisosModulos (js/auth/permisos.js): Sistema legacy de permisos
  4. back('mv'): Funcion global del sistema legacy de navegacion

Estas dependencias legacy se importan con // @ts-ignore ya que no tienen tipos TypeScript.


Integracion con Otros Modulos

Modulo CtaCte (Cuentas Corrientes)

Componente/HookOrigenProposito
CarteraSelectts/ctacte/components/CarteraSelect.jsSelector de carteras
useCarteraDatats/ctacte/hooks/useCarteraData.jsDatos de carteras
CarteraRecordts/ctacte/types/carteras.jsTipo de dato de cartera

Modulo Core (Compartido)

ComponenteOrigenProposito
FormFieldts/core/components/form/Wrapper de campos con error
RangeSelectorts/core/components/form/Selector de rango (desde-hasta)

Routing

No se utiliza React Router. La navegacion se maneja de forma legacy:

  • Montaje: La app se monta como una aplicacion React standalone en un contenedor HTML
  • Navegacion: La funcion global back('mv') del sistema legacy maneja la navegacion "atras"
  • Entrada: El punto de entrada se referencia desde una plantilla PHP con <script type="module">

Estructura de Archivos

Archivos Legacy (Vanilla JS)

view/mod-ventas/
└── form-cliente.php                     # Template PHP del formulario CRUD

js/
├── view/mod-ventas/
│   └── form-cliente.js                  # Inicializador del formulario
└── components/forms/general/
    └── cliente.js                       # Componente reutilizable createFormCliente

Archivos React/TypeScript

ts/ventas/
├── components/
│   └── ListaClientesForm.tsx            # Formulario de informe de clientes
├── hooks/
│   └── useClienteData.ts               # Hook para obtener datos de clientes
├── informes/
│   ├── ListaClientesApp.tsx             # App standalone de informe
│   └── ComprobantePorConceptosForm.tsx  # Otro informe que usa clientes
├── schemas/
│   └── informes.ts                      # Schemas Zod para informes
└── config/
    └── queryKeys.ts                     # Keys de TanStack Query (no usado en clientes aun)

Patrones Identificados

Patron de Migracion Progresiva

El ABM de Clientes muestra el patron de migracion progresiva del sistema:

  • El CRUD principal permanece en vanilla JS
  • Los nuevos componentes (informes) se implementan en React
  • Se usan imports @ts-ignore para integrar con funciones legacy
  • El hook useClienteData usa Axios directamente (migracion intermedia), no TanStack Query

Patron de App Standalone

ListaClientesApp se auto-monta al detectar el contenedor #lista-clientes-app en DOMContentLoaded. Esto permite insertar la aplicacion React en cualquier pagina PHP sin necesidad de React Router.

Patron de Habilitacion por Permisos

La visibilidad de secciones del formulario se controla dinamente segun:

  • Permisos del modulo (modulo_ctacte)
  • Configuracion de empresa (empres.cartera)

Consideraciones de Accesibilidad

  • Los inputs usan label asociados via htmlFor
  • Los radio buttons usan la estructura custom-control de AdminLTE/Bootstrap
  • Los botones deshabilitados usan el atributo disabled durante submit
  • El spinner de carga proporciona feedback visual durante la generacion

Recomendaciones de Migracion [OBSERVACIONES RETROSPECTIVAS]

Basado en el analisis del codigo implementado, se identifican las siguientes oportunidades:

  1. CRUD principal: El formulario de alta/edicion de clientes (createFormCliente) es el candidato principal para migracion a React. Actualmente opera en vanilla JS con jQuery UI autocomplete, jQuery collapse y SweetAlert2
  2. useClienteData: Migrar de useState + useEffect a TanStack Query para aprovechar cache, revalidacion y deduplicacion
  3. Dependencias legacy: Reemplazar generarInforme, ApiRequest y getPermisosModulos con equivalentes TypeScript
  4. Tipos: Crear tipos completos de Cliente (scope max) en ts/ventas/types/ para uso en todo el modulo
  5. CarteraSelect ya esta en React: El campo de cartera dentro del formulario legacy ya fue migrado a React via mountComponent, lo que demuestra viabilidad de migracion progresiva

Referencias


Actualizaciones Post-Generacion

Fecha: 2026-02-09

Informacion Validada:

  • [x] Eliminacion de clientes: No implementada (integridad referencial)
  • [x] Tabla compartida: ordcon usada por clientes (ventas) y miembros (membresias)
  • [x] Formulario CRUD: Vanilla JS legacy (form-cliente.php + form-cliente.js + cliente.js)
  • [x] Campo defecto: Valor inicial en formularios (ej: facturacion electronica)
  • [x] Validacion: Legacy (proxy frontend, backend, model/DTO), no middleware
  • [x] Concurrencia: Sin problemas actuales, mejora pendiente
  • [x] Permisos: Client-side only, no backend
  • [x] Ruta cross-module: Compartida Ventas + CtaCte
  • [x] Schema: Actualizado con migracion 20240823200741

NOTA IMPORTANTE: Esta documentacion fue generada automaticamente analizando el codigo implementado y actualizada con validaciones del equipo de desarrollo el 2026-02-09. Las secciones marcadas como [OBSERVACIONES RETROSPECTIVAS] son inferencias del analisis, no requisitos confirmados.