FactuGES_ERP/AGENTS.md
david eba766e7eb feat(payment-terms): add payment terms management components and utilities
- Implemented PaymentTermRowActions component for editing, toggling, and deleting payment terms.
- Created PaymentTermSegmentedBar for visual representation of payment term segments.
- Added PaymentTermsEmptyState and PaymentTermsNoResultsState for handling empty and no results scenarios.
- Developed PaymentTermsSettingsSkeleton for loading state representation.
- Introduced utility functions for building payment term forms and parameters.
- Added criteria building functions for payment terms list filtering.
- Implemented error handling view model for payment terms settings page.
- Created tests for payment terms list criteria and due rules utilities.
- Added shell script to check development environment setup.
2026-08-16 17:12:07 +02:00

17 KiB

AGENTS.md

Ámbito

Estas instrucciones se aplican a todo el monorepo, salvo que exista un archivo AGENTS.md más específico en un subdirectorio.

Los archivos AGENTS.md descendientes deben añadir o restringir reglas para su ámbito, no duplicar innecesariamente este documento.

Objetivo

Trabajar sobre el ERP mediante cambios:

  • correctos;
  • mínimos;
  • comprobables;
  • coherentes con la arquitectura existente;
  • fáciles de entender y mantener por otros desarrolladores.

Antes de modificar código, revisar las implementaciones y patrones existentes en el área afectada. No introducir patrones nuevos si ya existe una solución equivalente en el proyecto.

Entorno de ejecución

El repositorio canónico es:

/home/rodax/Documentos/uecko-erp

Todos los comandos de desarrollo deben ejecutarse en Linux.

No ejecutar Node.js, PNPM, Turborepo, TypeScript, Biome ni tests desde rutas Windows, unidades Z: ni rutas UNC.

Antes de trabajar comprobar:

pwd
uname -s
git rev-parse --show-toplevel

Se espera:

Linux
/home/rodax/Documentos/uecko-erp

Stack principal

  • Node.js >= 22
  • TypeScript
  • PNPM
  • Turborepo
  • ExpressJS
  • React
  • MariaDB
  • Sequelize v6
  • Zod v4
  • Biome

Principios obligatorios

Aplicar de forma pragmática:

  • SOLID;
  • Clean Code;
  • DRY;
  • KISS;
  • YAGNI;
  • SSOT;
  • Fail Fast;
  • programación defensiva;
  • inmutabilidad cuando resulte adecuada;
  • idempotencia;
  • principio de menor privilegio;
  • observabilidad en APIs y servicios.

Priorizar siempre:

claridad y explicitud
>
abstracción prematura

No introducir capas, adaptadores, factories, genéricos, eventos, contenedores DI o abstracciones sin una necesidad real y actual.

Idioma y convenciones de nombres

  • Código e identificadores en inglés.
  • Comentarios y TSDoc en castellano.
  • Ficheros y directorios en kebab-case.
  • Clases, tipos e interfaces en PascalCase.
  • Variables, funciones y métodos en camelCase.
  • Constantes globales en UPPER_SNAKE_CASE solo cuando realmente sean constantes globales.
  • Mantener la convención de prefijo I para interfaces cuando el área existente ya la utilice.

Comentarios y documentación

Los comentarios deben explicar:

  • por qué existe una decisión;
  • qué riesgo evita;
  • qué contrato debe respetarse;
  • qué efectos laterales o errores son relevantes.

No deben repetir lo que ya expresa el código.

Documentar mediante TSDoc las APIs públicas cuando aporten información sobre:

  • propósito;
  • parámetros;
  • valor devuelto;
  • errores;
  • efectos laterales;
  • invariantes o restricciones.

Después de un refactor, actualizar o eliminar los comentarios que hayan quedado obsoletos.

Disciplina de cambios

  • Realizar cambios incrementales.
  • No modificar código no relacionado con la tarea.
  • No renombrar, mover o reformatear archivos sin necesidad.
  • No introducir dependencias nuevas sin una justificación concreta.
  • No copiar código si ya existe una abstracción adecuada y estable.
  • No crear una abstracción nueva para eliminar una duplicación pequeña y local.
  • Mantener compatibilidad con los contratos existentes salvo que la tarea exija explícitamente romperlos.
  • No afirmar que una validación ha pasado si no se ha ejecutado correctamente.
  • No ocultar fallos de compilación, lint o tests.
  • No añadir supresiones de TypeScript, Biome o ESLint sin justificar el motivo en el código o en la respuesta final.

Uso de Result, Maybe y Collection

Usar los patrones existentes del proyecto:

  • Result para operaciones que pueden fallar;
  • Maybe para ausencia semántica;
  • Collection para conjuntos de objetos del mismo tipo cuando el dominio requiera comportamiento propio.

No sustituirlos por excepciones, null, arrays o tipos ad hoc si el área existente ya utiliza estos patrones.

No usar excepciones para flujo de negocio esperado salvo que el diseño actual del módulo lo establezca expresamente.

Gestión de operaciones asíncronas

El proyecto aplica la regla de Biome:

lint/complexity/noVoid

Está prohibido usar el operador void para descartar el resultado de una promesa.

No generar:

void handleDuplicate();
void handleDownload();
void asyncOperation();

Toda Promise debe gestionarse explícitamente mediante una de estas opciones:

Esperar la operación

await handleDuplicate();

Usar esta opción cuando la función actual pueda ser async y el flujo deba esperar el resultado.

Devolver la promesa

return handleDuplicate();

Usar esta opción cuando el llamador deba controlar el ciclo de vida de la operación.

Gestionar expresamente el error

handleDuplicate().catch(handleUnexpectedError);

Usar esta opción solo cuando la operación sea deliberadamente no bloqueante.

Reglas adicionales:

  • No eliminar void dejando una promesa flotante.
  • No silenciar errores mediante .catch(() => undefined) o equivalentes sin justificación explícita.
  • Antes de convertir un callback en async, comprobar si su contrato acepta Promise<void> o exige void.
  • Si el contrato exige void, encapsular la operación asíncrona y gestionar explícitamente su error.
  • No usar void como mecanismo para indicar intención.

TypeScript

Imports de tipos

Usar siempre imports de tipos explícitos en la cabecera del fichero mediante import type:

import type { UniqueID } from "@repo/rdx-ddd";
import type { Result } from "@repo/rdx-utils";

No usar referencias de tipo inline mediante import("...").Tipo:

// Prohibido
companyId: import("@repo/rdx-ddd").UniqueID;

Aplicar estas reglas:

  • Usar import type { ... } from "..." para toda dependencia utilizada exclusivamente como tipo.
  • Mantener los imports de tipos en la cabecera del fichero, junto con el resto de imports.
  • No mezclar imports de tipo y de runtime cuando puedan separarse con claridad.
  • No usar import("...").Tipo en código fuente ordinario.
  • Solo se admite un tipo inline en declaraciones generadas, archivos .d.ts, module augmentation o casos excepcionales técnicamente justificados y documentados.
  • No introducir imports de runtime para símbolos usados únicamente como tipos.

Ejemplo correcto:

import type { UniqueID } from "@repo/rdx-ddd";
import type { Result } from "@repo/rdx-utils";

export interface IIdentityTenantCompanyFinder {
  existsActiveById(params: {
    companyId: UniqueID;
    transaction?: unknown;
  }): Promise<Result<boolean, Error>>;
}
  • Evitar any. Usar unknown cuando el tipo todavía no sea conocido.
  • No usar type assertions salvo que exista una garantía verificable que el compilador no pueda inferir.
  • Evitar ! salvo que la invariante esté demostrada localmente.
  • Preferir tipos semánticos frente a primitivas genéricas.
  • Modelar conjuntos cerrados mediante unions o enums existentes.
  • Mantener exhaustividad en switch cuando el tipo represente un conjunto cerrado.
  • No añadir ramas default que oculten casos no contemplados cuando pueda usarse comprobación exhaustiva.
  • No duplicar tipos derivados de schemas Zod.
  • No usar Partial<T> si el schema o el tipo de entrada ya representa explícitamente la opcionalidad.
  • Mantener inmutabilidad en valores y estructuras cuando no exista necesidad de mutación.

Zod y DTOs

Zod es la fuente única de verdad para los contratos HTTP.

Reglas:

  • Inferir tipos con z.infer<typeof Schema>.
  • No duplicar DTOs mediante interfaces manuales equivalentes.
  • Cada endpoint debe tener sus DTOs de request y response.
  • Reutilizar subschemas solo cuando sean realmente estables y compartidos.
  • No mezclar validación de transporte con lógica de negocio.
  • Evitar .default() y .transform() en DTOs de negocio salvo necesidad justificada.
  • Diferenciar explícitamente entre campo omitido, null, cadena vacía y colección vacía.
  • Usar schemas semánticos para UUID, fechas, timestamps, porcentajes, importes y cantidades.
  • Los valores decimales exactos no deben viajar como number.

Arquitectura backend

El backend sigue DDD pragmático, Clean Architecture y arquitectura hexagonal.

Separación obligatoria:

Domain
  no conoce Application ni Infrastructure

Application
  depende de Domain, contratos y Core
  no conoce Express, Sequelize ni modelos ORM

Infrastructure
  depende de Application, Domain y Core

No se permite:

  • Domain -> Application;
  • Domain -> Infrastructure;
  • Application -> Infrastructure;
  • importar infraestructura interna de otro módulo;
  • compartir repositorios entre módulos;
  • usar Service Locator;
  • introducir DI implícita o mágica.

Flujo de entrada

Respetar:

RequestDTO
  -> RequestMapper
  -> Command
  -> PropsMapper
  -> Aggregate o Entity

No mapear directamente:

DTO -> Aggregate

Casos de uso

Los casos de uso deben:

  • orquestar;
  • gestionar transacciones cuando corresponda;
  • delegar en servicios de aplicación;
  • devolver Result;
  • construir respuestas mediante builders o mappers de salida.

No deben:

  • contener lógica HTTP;
  • usar Sequelize directamente;
  • construir agregados desde DTOs HTTP;
  • asumir responsabilidades propias de repositorios o controllers.

Servicios de aplicación

Preferir servicios pequeños y explícitos con una responsabilidad clara.

Evitar nombres genéricos como:

  • Manager;
  • GenericService;
  • CommandService;
  • BaseRepository;
  • Helper sin semántica concreta.

Repositorios

  • Cada repositorio gestiona un agregado o una responsabilidad concreta.
  • Sequelize no debe salir de Infrastructure.
  • Los repositorios deben usar mappers de persistencia.
  • No crear repositorios base genéricos sin una necesidad demostrada.
  • Mantener aislamiento por companyId y branchId cuando corresponda.

Mappers de persistencia

Usar un mapper bidireccional por agregado:

toPersistence();
toDomain();

Infrastructure debe reconstruir entidades mediante rehydrate().

create() y rehydrate()

Mantener separadas estas operaciones:

create()
  creación funcional
  valida invariantes
  puede fallar
  devuelve Result

rehydrate()
  reconstrucción desde persistencia
  no valida reglas de creación
  no genera eventos
  no devuelve Result

Reglas:

  • Infrastructure usa rehydrate().
  • Application y Domain usan create() para nuevas instancias.
  • update() no usa create() ni rehydrate().
  • Los Value Objects no tienen rehydrate(); se reconstruyen mediante create().

Comunicación entre módulos

Los módulos solo deben colaborar mediante servicios públicos explícitos.

No exponer:

  • repositorios;
  • modelos Sequelize;
  • mappers internos;
  • servicios de escritura internos;
  • detalles de infraestructura.

El composition root de cada módulo debe construir sus dependencias explícitamente.

Snapshots y builders de respuesta

Los schemas Zod definen el contrato de salida.

Los snapshot builders solo materializan ese contrato.

Reglas:

  • Tipar el builder con el DTO inferido del schema exacto.
  • No crear interfaces I*Snapshot que dupliquen un DTO.
  • El builder debe ser determinista y no contener lógica de negocio.
  • Puede convertir Maybe a null, Value Objects a primitivas y enums internos a valores de contrato.
  • No puede acceder a repositorios ni infraestructura.
  • Si cambia el schema, el builder debe fallar en compilación si deja de cumplir el contrato.

Frontend

  • Mantener React, TanStack Query, TanStack Table, React Hook Form, Zod, shadcn/ui, Tailwind e i18n según los patrones existentes.
  • No duplicar estado remoto en estado local sin necesidad.
  • Mantener las operaciones de servidor en TanStack Query.
  • Centralizar las claves de query siguiendo la convención existente.
  • Invalidar o actualizar caché de forma explícita tras mutaciones.
  • No introducir efectos para derivar estado que pueda calcularse durante render.
  • Evitar componentes monolíticos; extraer responsabilidades cuando exista una frontera clara.
  • No extraer componentes triviales que solo oculten JSX sin aportar semántica.
  • Mantener accesibilidad, estados disabled, loading, empty y error.
  • No eliminar acciones no disponibles si la UX exige mostrarlas deshabilitadas.
  • Mantener traducciones en es y en cuando se añadan textos visibles.
  • No insertar textos de interfaz directamente si el área usa i18n.
  • Mantener tabular-nums en columnas monetarias cuando corresponda.

APIs y errores

  • Usar errores tipados y predecibles.
  • No devolver detalles internos, SQL, stack traces o información sensible.
  • Mapear errores de dominio y aplicación a errores HTTP en Infrastructure.
  • Mantener contratos de error estables.
  • Aplicar idempotencia en operaciones que puedan repetirse.
  • Validar autorización y aislamiento tenant antes de acceder o modificar datos.
  • No confiar en identificadores de compañía enviados por el cliente sin verificar el contexto autenticado.
  • Registrar información suficiente para diagnosticar fallos sin exponer datos sensibles.

Seguridad

  • Aplicar principio de menor privilegio.
  • No introducir secretos, tokens, claves o credenciales en el repositorio.
  • No registrar datos sensibles.
  • Validar toda entrada externa.
  • Usar consultas parametrizadas y APIs seguras de Sequelize.
  • Evitar interpolación manual de SQL.
  • Revisar autorización además de autenticación.
  • Mantener aislamiento multiempresa en todas las consultas y mutaciones.

Base de datos y Sequelize

  • Mantener nombres de tablas y columnas coherentes con las convenciones existentes.
  • No introducir acceso directo a modelos Sequelize fuera de Infrastructure.
  • Ejecutar operaciones agregadas dentro de una transacción cuando deban ser atómicas.
  • Diseñar migraciones idempotentes cuando el procedimiento del proyecto lo requiera.
  • No eliminar columnas, tablas o datos legacy sin una auditoría previa de referencias.
  • No confiar únicamente en restricciones de aplicación cuando una invariante crítica pueda reforzarse en base de datos.
  • Evitar JSON.stringify para detectar cambios de dominio.
  • Comparar Value Objects semánticamente mediante sus primitivas o métodos de igualdad adecuados.

Comandos y entorno de ejecución

El código se encuentra físicamente en una máquina Ubuntu compartida, aunque pueda aparecer en Windows como unidad Z:.

Preferir comandos para Linux ejecutados mediante SSH sobre Ubuntu.

No usar rutas Windows como opción principal.

Ejemplos preferidos:

pnpm --filter <workspace> typecheck
pnpm --filter <workspace> lint
pnpm --filter <workspace> test

Usar comandos acotados al workspace afectado cuando sea suficiente.

Usar comandos globales cuando el cambio pueda afectar contratos compartidos o varios workspaces.

Validación obligatoria

Antes de finalizar un cambio, ejecutar las comprobaciones relevantes disponibles para el área modificada:

pnpm typecheck
pnpm lint
pnpm test

Cuando corresponda, ejecutar además:

pnpm check
pnpm build

Criterios:

  • Preferir validación dirigida al paquete o módulo afectado.
  • Ejecutar validación global si se modifican paquetes compartidos, contratos o configuración transversal.
  • Informar exactamente qué comandos se ejecutaron.
  • Informar claramente cualquier validación no ejecutada y su motivo.
  • No considerar completada una tarea con errores conocidos de lint, typecheck o tests salvo que el usuario haya limitado expresamente el alcance.

Tests

  • Añadir o actualizar tests cuando cambie comportamiento observable.
  • No escribir tests que dependan de detalles internos irrelevantes.
  • Cubrir casos válidos, inválidos, límites e idempotencia cuando corresponda.
  • Mantener tests deterministas.
  • No usar esperas arbitrarias.
  • No desactivar tests para hacer pasar la suite.

Refactors

Un refactor debe mantener el comportamiento salvo que se indique lo contrario.

Antes de eliminar código legacy:

  1. buscar referencias en todo el monorepo;
  2. clasificar cada referencia como activa, legacy no cableada, migración, documentación o falso positivo;
  3. revisar rutas, composition roots, DI, imports, modelos, repositorios, mappers, tests y scripts;
  4. eliminar únicamente lo demostrado como no utilizado;
  5. ejecutar typecheck, lint y tests relevantes;
  6. documentar referencias residuales y motivos.

Respuesta final del agente

Al terminar una tarea de código, indicar de forma concisa:

  • cambios realizados;
  • decisiones relevantes;
  • archivos principales modificados;
  • comandos de validación ejecutados;
  • resultado de las validaciones;
  • riesgos o pendientes reales.

No afirmar que algo compila, funciona o está probado si no se ha verificado.

Regla de precedencia

En caso de conflicto, aplicar este orden:

  1. instrucciones explícitas del usuario para la tarea actual;
  2. AGENTS.md más cercano al archivo modificado;
  3. este AGENTS.md raíz;
  4. convenciones existentes del código;
  5. preferencias generales del agente.

Si una instrucción contradice la arquitectura o genera un riesgo relevante, señalarlo antes de introducir el cambio.