- Implemented ProformaDuplicator service for duplicating proforma documents. - Added tests for duplicating proformas, including scenarios for successful duplication, audit failure propagation, and payment term adaptations. - Created ProformaStatusChanger service to handle status changes of proformas with corresponding tests. - Developed ProformaArchiver service for unarchiving proformas and added tests to verify functionality. - Introduced a test fixture for creating proforma instances for testing purposes.
235 lines
8.0 KiB
Markdown
235 lines
8.0 KiB
Markdown
# 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 <access_token>
|
|
X-Company-Id: <uuid-company>
|
|
```
|
|
|
|
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.
|