FactuGES_ERP/modules/companies/README.md

297 lines
5.2 KiB
Markdown
Raw Normal View History

2026-07-06 15:47:05 +00:00
# `modules/companies`
`companies` es el módulo independiente y fuente de verdad de la ficha operativa de empresa.
## Responsabilidades
Gestiona la entidad `Company` funcional del ERP:
- datos legales
- datos operativos básicos
- `defaultPaymentTermId`
2026-07-06 15:47:05 +00:00
- `slug`
- `status`
Separación arquitectónica:
- `modules/identity`: autenticación, cuentas, memberships, acceso account-company
- `modules/companies`: ficha real de empresa y validación de company activa
## Tabla `companies`
Campos actuales:
```txt
id
legal_name
trade_name
tin
slug
email
phone
website
default_payment_term_id
2026-07-06 15:47:05 +00:00
status
created_at
updated_at
```
Índices esperados:
```txt
UNIQUE(slug)
INDEX(status)
INDEX(tin)
```
## Servicios públicos
El módulo expone:
```txt
companies:general
```
Responsabilidad:
- validar existencia y estado activo de la company
- devolver datos públicos/operativos de empresa
No debe asumir:
- memberships
- autenticación por cuenta
- roles/permisos de identity
## Dependencias
`modules/companies` depende de `identity` y `catalogs`.
2026-07-06 15:47:05 +00:00
Uso actual:
- sus endpoints usan `requireIdentityAuthenticated(params)`
- `GET /companies/available` no usa `requireIdentityTenant(params)`
- `GET /companies/current` y `PATCH /companies/current` usan auth de `identity` + `requireCompanyContext(...)` cableado con `companyAccessChecker` de `identity` y `publicFinder` propio de `companies`
- los endpoints legacy por `:company_id` siguen tenant-scoped y comparan `req.user.companyId` con `:company_id`
## Endpoints relevantes
```http
POST /companies
GET /companies/available
GET /companies/current
PATCH /companies/current
GET /companies/:company_id
PATCH /companies/:company_id
POST /companies/:company_id/enable
POST /companies/:company_id/disable
```
### Settings de empresa activa
`/settings/company/details` consume:
```http
GET /companies/current
PATCH /companies/current
```
Resolución real del target:
```txt
Authorization
+ X-Company-Id
+ requireCompanyContext(...)
=> req.user.companyId
```
El cliente no selecciona la empresa a editar mediante `companyId` en URL, body ni query.
## Política V1 de autorización
En la V1 cualquier miembro activo de la compañía puede editar sus datos generales mediante:
```http
PATCH /companies/current
```
Política real:
```txt
usuario autenticado
+ X-Company-Id válido
+ membership activa en esa company
+ company activa
```
No existe todavía enforcement real de RBAC ni permisos granulares en runtime.
`identity:companies:update` puede existir como código futuro de catálogo, pero no representa autorización aplicada hoy.
## Campos editables actuales
`PATCH /companies/current` admite solo:
```txt
legal_name
trade_name
tin
email
phone
website
default_payment_term_id
```
En frontend, la feature:
```txt
/settings/company/details
```
modela esos mismos campos como ficha editable de la company activa.
La UI actual agrupa la edición en tres secciones:
```txt
Datos generales
→ legalName
→ tradeName
→ tin
Datos de contacto
→ email
→ phone
→ website
Condiciones comerciales
→ defaultPaymentTermId
```
Semántica PATCH esperada en la pantalla:
```txt
propiedad omitida -> conservar valor actual
propiedad presente con valor -> actualizar
propiedad nullable presente con null -> limpiar
```
Flujo de guardado esperado en frontend:
```txt
GET /companies/current
→ hydrata formulario
editar campos dirty
→ construir PATCH parcial
PATCH /companies/current
→ respuesta servidor como SSOT
→ updateActiveCompany(...)
→ reset del formulario con la respuesta
```
Política de errores frontend en esta pantalla:
```txt
fallo inicial sin datos utilizables
→ PageErrorState
error de refetch con datos previos
→ mantener formulario
→ ErrorAlert inline
cancelled
→ sin pantalla bloqueante
422 con paths mapeables
→ errores de campo en RHF
error general de guardado
→ toast
→ sin reset del formulario
```
La UI no debe seleccionar el target mediante `companyId`.
Fuera de alcance de esta API:
```txt
slug
status
fiscalidad avanzada
VeriFactu
document series
payment methods
branding
bank accounts
defaultPaymentMethodId
```
## `defaultPaymentTermId`
Contrato actual:
```txt
omitido -> mantener
null -> desvincular
UUID -> validar dentro de la company activa
```
La referencia es:
```txt
nullable
company-scoped
validada mediante catalogs
```
Semántica HTTP actual:
```txt
UUID activo de la misma company -> 200
UUID de otra company -> 422
UUID inactivo -> 422
```
El error de asignación es deliberadamente opaco:
```txt
Default payment term is not assignable to the active company.
```
No expone si el problema concreto es:
- inexistente
- cross-company
- inactivo
El PATCH es transaccional:
- si falla `default_payment_term_id`
- no se persisten otros cambios del mismo request
## Limitaciones V1 de `Company Details`
Fuera del alcance funcional actual:
- RBAC granular
- address
- country
- language
- currency
- timezone
- branding
- logo
- bank accounts
- fiscalidad avanzada
- VeriFactu
- series documentales
- `defaultPaymentMethodId`
2026-07-06 15:47:05 +00:00
## Reglas
- `CompanyMembership` vive en `identity`.
- No mover memberships a `companies`.
- No usar `customers` para representar la empresa tenant.
- `companySlug` no debe venir de token, `X-Company-Slug` ni `req.user.companySlug`.