# Requisitos de `companies` Estado documental: `CURRENT` Fecha de actualizacion: 2026-08-26 ## Proposito y fuente de verdad `companies` es el bounded context y la fuente de verdad de la ficha operativa de cada empresa tenant del ERP. Este documento define el contrato funcional vigente del contexto. La autenticacion, las cuentas y las memberships no pertenecen a `companies`. Esas responsabilidades corresponden a `identity`. Para las decisiones de integracion entre ambos contextos, prevalece [`identity-and-companies.md`](../architecture/identity-and-companies.md). Para la resolucion del tenant, prevalece [`tenant-context.md`](../architecture/tenant-context.md). ## Alcance actual El contexto debe gestionar: - la ficha legal y operativa basica de `Company`; - el identificador funcional `slug`; - el estado operativo de la empresa (`active` o `disabled`); - el plazo de pago por defecto de la empresa; - la consulta de empresas disponibles para una cuenta autenticada; - la consulta y actualizacion de la empresa activa. El contexto no debe gestionar: - autenticacion, sesiones, refresh tokens o cuentas; - `CompanyMembership`, roles o permisos por cuenta; - datos de la empresa dentro de `identity`; - clientes como representacion de la empresa tenant; - fiscalidad avanzada, perfiles VeriFactu, series documentales, medios de pago, direccion, branding, bancos, idiomas, divisas o zona horaria. Los requisitos de VeriFactu empresarial viven en `customer-invoices` y no amplian por si mismos el aggregate `Company`. ## Modelo funcional Una empresa se identifica por un UUID y contiene los siguientes datos: | Campo | Regla vigente | | --- | --- | | `legal_name` | Obligatorio. Nombre legal de la empresa. | | `trade_name` | Opcional y nullable. Nombre comercial. | | `tin` | Opcional y nullable. Identificador fiscal. | | `slug` | Obligatorio, unico, normalizado a minusculas; solo letras, numeros y guiones, de 2 a 100 caracteres. | | `email` | Opcional y nullable; debe tener formato valido. | | `phone` | Opcional y nullable. | | `website` | Opcional y nullable; debe tener formato URL valido. | | `default_payment_term_id` | Opcional y nullable; referencia viva a un plazo de pago activo de la misma empresa. | | `status` | `active` o `disabled`. | La persistencia de `companies` debe conservar tambien `created_at` y `updated_at`. `slug` es unico; deben existir indices para `status` y `tin`. ## Separacion con `identity` - `identity` es propietario de `Account` y `CompanyMembership`. - `companies` es propietario de `Company` y de su ficha operativa. - El access token solo identifica la cuenta; no contiene `companyId`, `companySlug`, roles ni permisos. - `identity` resuelve que IDs de empresa son accesibles para una cuenta. - `companies` resuelve las fichas completas de esas empresas y valida que una empresa este activa. - No se deben crear `GET /identity/companies` ni `GET /account/companies`. - No se debe crear una dependencia `identity -> companies`. ## Contexto tenant y autorizacion V1 Las rutas tenant-scoped deben recibir: ```http Authorization: Bearer X-Company-Id: ``` El contexto tenant es valido solo si la cuenta esta autenticada, el header es un UUID valido, existe una membership `active` para esa empresa y la empresa tiene estado `active`. La empresa destino se toma de ese contexto, nunca de un `companyId` libre en body, query o ruta cuando se trate de la API actual de empresa activa. En la V1, cualquier miembro activo de una empresa activa puede actualizar los datos generales de esa empresa mediante `PATCH /companies/current`. No existe todavia enforcement runtime de RBAC ni un permiso granular efectivo como `identity:companies:update`. Una empresa deshabilitada no puede actualizarse. Los endpoints legacy con `:company_id` deben exigir que dicho ID coincida exactamente con la empresa resuelta en el contexto tenant. ## Contratos HTTP vigentes ### Alta y seleccion de empresa ```http POST /companies GET /companies/available ``` `POST /companies` crea una empresa activa. Recibe `id`, `legal_name` y `slug` como datos obligatorios; el resto de datos de ficha son opcionales. Al crearla, `default_payment_term_id` queda vacio y el estado es `active`. `GET /companies/available` requiere autenticacion, pero no `X-Company-Id`. Debe devolver solo las empresas que cumplan simultaneamente: ```txt membership activa de la cuenta + empresa activa ``` La respuesta no debe modificar la sesion ni el access token. ### Empresa activa ```http GET /companies/current PATCH /companies/current ``` Estas son las rutas canonicamente usadas por Settings para `/settings/company/details`. Requieren contexto tenant valido y operan siempre sobre la empresa resuelta mediante `X-Company-Id`. `PATCH /companies/current` admite exclusivamente: ```txt legal_name trade_name tin email phone website default_payment_term_id ``` No admite modificar `slug` ni `status`. La semantica es PATCH real: - un campo omitido conserva su valor; - un campo nullable presente con `null` limpia su valor; - un campo presente con valor valido actualiza su valor; - la respuesta del servidor es el estado autoritativo de la ficha. ### Rutas legacy por ID ```http GET /companies/:company_id PATCH /companies/:company_id POST /companies/:company_id/enable POST /companies/:company_id/disable ``` Estas rutas siguen expuestas por compatibilidad. Requieren contexto tenant valido y el `:company_id` debe ser la misma empresa activa del header. En la actualizacion legacy se permite adicionalmente modificar `slug`; su unicidad debe validarse antes de persistir. ## Plazo de pago por defecto `default_payment_term_id` es una referencia nullable y company-scoped hacia `catalogs`. Su contrato es: - omitido: conservar la asociacion actual; - `null`: desvincular el plazo de pago; - UUID: asociar solo si identifica un plazo activo de la misma empresa. Una referencia inexistente, de otra empresa o inactiva debe responder `422` con el mensaje opaco `Default payment term is not assignable to the active company.` No se debe revelar cual de esas causas se produjo. La validacion y la actualizacion deben formar una unica transaccion: si falla la referencia, no se persisten otros cambios del mismo PATCH. ## Respuesta de empresa Las consultas y mutaciones de ficha devuelven: ```ts type CompanyResponseDTO = { id: string; legal_name: string; trade_name: string | null; tin: string | null; slug: string; email: string | null; phone: string | null; website: string | null; default_payment_term_id: string | null; status: "active" | "disabled"; }; ``` La lista de `GET /companies/available` devuelve el mismo subconjunto de ficha necesario para seleccionar empresa, sin `default_payment_term_id`, y con `status: "active"`. ## Errores esperados | Situacion | Respuesta | | --- | --- | | Sin autenticacion, token invalido o cuenta no autenticable | `401` | | UUID tenant mal formado o body invalido | `400` | | Sin `X-Company-Id`, membership no activa, empresa no accesible, deshabilitada o destino distinto al tenant | `403` | | Colision de `slug` | `409` | | Campo con regla de dominio invalida o plazo de pago no asignable | `422` | | Error no controlado | `500` | ## Auditoria y consistencia Las operaciones de alta y actualizacion de empresa deben registrar sus eventos de auditoria. Las mutaciones deben ser transaccionales para no dejar la ficha y el registro de auditoria en estados divergentes. ## Fuera de alcance y evolucion futura No se deben inferir como requisitos actuales: - RBAC granular o administracion global de empresas; - cambios de `status` autorizados por un rol administrativo real; - direccion, pais, idioma, moneda, zona horaria o branding; - cuentas bancarias, logo, medios de pago o `defaultPaymentMethodId`; - fiscalidad avanzada, configuracion VeriFactu o series documentales. Estas capacidades requieren un contrato propio antes de incorporarse a `companies`. Hasta entonces, los documentos de otros bounded contexts deben enlazar esta especificacion al describir la ficha de empresa, sin duplicar ni reasignar su ownership.