- Archived historical documents for DTO design and naming conventions. - Added normative documentation for HTTP request flow and module boundaries. - Introduced testing guidelines for backend layers. - Updated onboarding module documentation and clarified onboarding requirements. - Deprecated backend prompt base and prompt auth documents. - Adjusted paths in testing documentation for consistency.
1348 lines
21 KiB
Markdown
1348 lines
21 KiB
Markdown
# DOCUMENTO DE DISEÑO — EVOLUCIÓN DE `Company` CON DIRECCIÓN EMPRESARIAL
|
|
|
|
## 1. Objetivo
|
|
|
|
Definir la evolución del agregado `Company` para incorporar una dirección empresarial utilizable por el ERP, especialmente por configuración, onboarding y reporting.
|
|
|
|
Esta fase consolida exclusivamente:
|
|
|
|
- dirección empresarial;
|
|
- país mediante código ISO 3166-1 alpha-2.
|
|
|
|
Quedan expresamente fuera de esta evolución:
|
|
|
|
- idioma por defecto de empresa;
|
|
- moneda por defecto de empresa;
|
|
- logo de empresa;
|
|
- múltiples direcciones;
|
|
- sedes/sucursales;
|
|
- snapshot del emisor en facturas emitidas;
|
|
- internacionalización completa de códigos postales.
|
|
|
|
El diseño debe mantenerse:
|
|
|
|
```txt
|
|
explícito
|
|
simple
|
|
compatible con empresas existentes
|
|
coherente con DDD pragmático
|
|
sin sobreingeniería
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Contexto arquitectónico
|
|
|
|
`Company` pertenece al módulo:
|
|
|
|
```txt
|
|
modules/companies
|
|
```
|
|
|
|
La arquitectura general mantiene:
|
|
|
|
```txt
|
|
Domain
|
|
↓
|
|
Application
|
|
↓
|
|
Infrastructure
|
|
```
|
|
|
|
con las reglas ya consolidadas del ERP:
|
|
|
|
- `Domain` no conoce HTTP, Sequelize ni Infrastructure;
|
|
- `Application` no conoce Express ni modelos ORM;
|
|
- `Infrastructure` implementa persistencia, transporte y DI;
|
|
- Zod v4 es la SSOT de los contratos HTTP;
|
|
- otros módulos consumen `companies` mediante servicios públicos explícitos;
|
|
- no se exponen repositorios ni infraestructura interna.
|
|
|
|
La dirección empresarial no debe vivir en `onboarding`, `customer-invoices`, `reports` ni ningún contenedor genérico de settings. Su SSOT debe ser `Company`.
|
|
|
|
---
|
|
|
|
## 3. Estado actual de `Company`
|
|
|
|
Campos actuales relevantes:
|
|
|
|
```txt
|
|
id
|
|
legalName
|
|
tradeName
|
|
tin
|
|
slug
|
|
email
|
|
phone
|
|
website
|
|
defaultPaymentTermId
|
|
status
|
|
createdAt
|
|
updatedAt
|
|
```
|
|
|
|
No existen actualmente en `Company`:
|
|
|
|
```txt
|
|
address
|
|
countryCode
|
|
defaultLanguage
|
|
defaultCurrency
|
|
logo
|
|
```
|
|
|
|
Esto provoca que consumidores como el perfil de informes de proformas no puedan obtener una dirección empresarial real desde `companies`.
|
|
|
|
---
|
|
|
|
## 4. Decisión principal
|
|
|
|
Se incorpora a `Company` una única propiedad:
|
|
|
|
```ts
|
|
address: Maybe<PostalAddress>
|
|
```
|
|
|
|
La dirección será:
|
|
|
|
- propiedad del agregado `Company`;
|
|
- única en V1;
|
|
- embebida en la tabla `companies`;
|
|
- nullable para compatibilidad con empresas existentes;
|
|
- completa cuando esté presente.
|
|
|
|
No se crea:
|
|
|
|
```txt
|
|
CompanyAddress
|
|
FiscalAddress
|
|
CompanyPostalAddress
|
|
company_addresses
|
|
CompanyProfile
|
|
SettingsBag
|
|
```
|
|
|
|
La existencia de una sola dirección empresarial no justifica una entidad ni una tabla independiente.
|
|
|
|
---
|
|
|
|
## 5. Naming
|
|
|
|
El nombre definitivo es:
|
|
|
|
```txt
|
|
address
|
|
```
|
|
|
|
No:
|
|
|
|
```txt
|
|
fiscalAddress
|
|
registeredAddress
|
|
legalAddress
|
|
```
|
|
|
|
Motivo:
|
|
|
|
- actualmente solo existe una dirección empresarial;
|
|
- el reporting ya utiliza conceptualmente `address`;
|
|
- no existe hoy una necesidad real de diferenciar dirección fiscal, postal, sede o envío;
|
|
- introducir esa distinción ahora anticiparía requisitos no existentes.
|
|
|
|
Si en el futuro aparecen varios conceptos de dirección, deberán modelarse entonces de forma explícita.
|
|
|
|
---
|
|
|
|
## 6. Value Object `PostalAddress`
|
|
|
|
Se reutilizará:
|
|
|
|
```txt
|
|
@repo/rdx-ddd
|
|
PostalAddress
|
|
```
|
|
|
|
No debe importarse una dirección propia de `customers` ni `suppliers`.
|
|
|
|
`PostalAddress` pertenece a un paquete compartido de dominio y es reutilizable legítimamente desde `companies`.
|
|
|
|
### 6.1 Evolución necesaria
|
|
|
|
Actualmente el país del VO usa un concepto textual equivalente a:
|
|
|
|
```ts
|
|
country: Maybe<Country>
|
|
```
|
|
|
|
Debe evolucionar a:
|
|
|
|
```ts
|
|
countryCode: Maybe<CountryCode>
|
|
```
|
|
|
|
La representación definitiva será:
|
|
|
|
```ts
|
|
interface PostalAddressProps {
|
|
street: Maybe<Street>;
|
|
street2: Maybe<Street>;
|
|
city: Maybe<City>;
|
|
postalCode: Maybe<PostalCode>;
|
|
province: Maybe<Province>;
|
|
countryCode: Maybe<CountryCode>;
|
|
}
|
|
```
|
|
|
|
Motivo:
|
|
|
|
```txt
|
|
HTTP → CountryCode ISO 3166-1 alpha-2
|
|
Dominio actual → Country textual libre
|
|
```
|
|
|
|
La evolución evita perpetuar esa inconsistencia en `Company`.
|
|
|
|
---
|
|
|
|
## 7. País
|
|
|
|
El país se representa mediante:
|
|
|
|
```txt
|
|
CountryCode
|
|
```
|
|
|
|
y debe utilizar:
|
|
|
|
```txt
|
|
ISO 3166-1 alpha-2
|
|
```
|
|
|
|
Ejemplos:
|
|
|
|
```txt
|
|
ES
|
|
PT
|
|
FR
|
|
```
|
|
|
|
### Contratos
|
|
|
|
Dominio:
|
|
|
|
```ts
|
|
CountryCode
|
|
```
|
|
|
|
HTTP:
|
|
|
|
```ts
|
|
CountryCodeSchema
|
|
```
|
|
|
|
Persistencia:
|
|
|
|
```txt
|
|
CHAR(2)
|
|
```
|
|
|
|
No se crea un catálogo de países.
|
|
|
|
Las listas frontend de países son auxiliares de presentación y no son SSOT del dominio.
|
|
|
|
---
|
|
|
|
## 8. Estructura de la dirección
|
|
|
|
Campos definitivos:
|
|
|
|
```txt
|
|
street
|
|
street2
|
|
city
|
|
postalCode
|
|
province
|
|
countryCode
|
|
```
|
|
|
|
Semántica:
|
|
|
|
| Campo | Dirección presente | Onboarding | Observaciones |
|
|
|---|---:|---:|---|
|
|
| `street` | obligatorio | obligatorio | texto |
|
|
| `street2` | opcional | opcional | texto adicional |
|
|
| `city` | obligatorio | obligatorio | texto |
|
|
| `postalCode` | obligatorio | obligatorio | VO existente |
|
|
| `province` | obligatorio | obligatorio | texto |
|
|
| `countryCode` | obligatorio | obligatorio | ISO 3166-1 alpha-2 |
|
|
|
|
No se incorporan:
|
|
|
|
```txt
|
|
regionCode
|
|
state
|
|
locality
|
|
building
|
|
county
|
|
district
|
|
```
|
|
|
|
porque no existe un consumidor actual que los justifique.
|
|
|
|
---
|
|
|
|
## 9. Invariantes
|
|
|
|
### 9.1 Invariante de `Company`
|
|
|
|
Una `Company` puede existir sin dirección:
|
|
|
|
```txt
|
|
address = Maybe.none()
|
|
```
|
|
|
|
Esto es necesario para:
|
|
|
|
- empresas ya existentes;
|
|
- seeds;
|
|
- fixtures;
|
|
- transición progresiva;
|
|
- compatibilidad con integraciones actuales.
|
|
|
|
### 9.2 Invariante cuando existe dirección
|
|
|
|
Si `address` está presente, debe ser completa.
|
|
|
|
Válido:
|
|
|
|
```txt
|
|
address = none
|
|
```
|
|
|
|
Válido:
|
|
|
|
```txt
|
|
street
|
|
city
|
|
postalCode
|
|
province
|
|
countryCode
|
|
street2 opcional
|
|
```
|
|
|
|
Inválido:
|
|
|
|
```txt
|
|
street presente
|
|
city ausente
|
|
countryCode ausente
|
|
```
|
|
|
|
No se permiten direcciones parcialmente válidas dentro de `Company`.
|
|
|
|
---
|
|
|
|
## 10. Diferencia entre `Company` y onboarding
|
|
|
|
La dirección no es obligatoria como invariante universal de `Company`.
|
|
|
|
El onboarding exige una dirección completa al crear inicialmente la empresa.
|
|
|
|
Regla:
|
|
|
|
```txt
|
|
Company existente
|
|
→ puede tener address = none
|
|
|
|
Company creada mediante onboarding
|
|
→ debe recibir una dirección completa
|
|
```
|
|
|
|
La obligatoriedad del onboarding pertenece al caso de uso de creación inicial, no al agregado de forma universal.
|
|
|
|
---
|
|
|
|
## 11. Código postal
|
|
|
|
En esta evolución se mantiene el `PostalCode` actual.
|
|
|
|
Limitación conocida:
|
|
|
|
```txt
|
|
PostalCode actual
|
|
→ cinco dígitos
|
|
```
|
|
|
|
Esto es suficiente para el alcance actual español, pero no constituye soporte internacional completo.
|
|
|
|
No se amplía ahora el VO para formatos dependientes de `countryCode`.
|
|
|
|
Ese trabajo se pospone a una evolución específica de internacionalización de direcciones.
|
|
|
|
---
|
|
|
|
## 12. Persistencia
|
|
|
|
La dirección se persiste embebida en `companies`.
|
|
|
|
Columnas conceptuales:
|
|
|
|
```txt
|
|
address_street
|
|
address_street_2
|
|
address_city
|
|
address_postal_code
|
|
address_province
|
|
address_country_code
|
|
```
|
|
|
|
Tipos orientativos:
|
|
|
|
```txt
|
|
address_street VARCHAR(255) NULL
|
|
address_street_2 VARCHAR(255) NULL
|
|
address_city VARCHAR(255) NULL
|
|
address_postal_code VARCHAR(...) NULL
|
|
address_province VARCHAR(255) NULL
|
|
address_country_code CHAR(2) NULL
|
|
```
|
|
|
|
No se añade:
|
|
|
|
```txt
|
|
company_addresses
|
|
```
|
|
|
|
No se crean índices nuevos.
|
|
|
|
Actualmente no existen consultas que justifiquen índices por:
|
|
|
|
```txt
|
|
street
|
|
postalCode
|
|
province
|
|
countryCode
|
|
```
|
|
|
|
---
|
|
|
|
## 13. Rehidratación
|
|
|
|
El mapper de persistencia debe respetar estas reglas.
|
|
|
|
### Todas las columnas de dirección a `NULL`
|
|
|
|
Resultado:
|
|
|
|
```txt
|
|
Maybe.none<PostalAddress>()
|
|
```
|
|
|
|
### Dirección completa
|
|
|
|
Resultado:
|
|
|
|
```txt
|
|
PostalAddress
|
|
→ Maybe.some(...)
|
|
```
|
|
|
|
### Estado parcial en persistencia
|
|
|
|
Ejemplo:
|
|
|
|
```txt
|
|
address_street != NULL
|
|
address_city = NULL
|
|
```
|
|
|
|
Debe considerarse estado persistido inconsistente.
|
|
|
|
Regla:
|
|
|
|
```txt
|
|
Fail Fast
|
|
```
|
|
|
|
No:
|
|
|
|
```txt
|
|
rellenar con ""
|
|
ignorar campos
|
|
convertir silenciosamente a none
|
|
rehidratar una dirección parcial
|
|
```
|
|
|
|
---
|
|
|
|
## 14. `Company.create()`
|
|
|
|
`Company.create()` debe admitir:
|
|
|
|
```ts
|
|
address: Maybe<PostalAddress>
|
|
```
|
|
|
|
No debe exigir dirección universalmente.
|
|
|
|
El flujo HTTP general de creación debe continuar siendo compatible con clientes actuales.
|
|
|
|
---
|
|
|
|
## 15. `Company.rehydrate()`
|
|
|
|
Debe restaurar:
|
|
|
|
```ts
|
|
address: Maybe<PostalAddress>
|
|
```
|
|
|
|
sin aplicar reglas propias de creación funcional.
|
|
|
|
La infraestructura es responsable de detectar estados persistidos incoherentes antes de entregar las props internas al agregado.
|
|
|
|
---
|
|
|
|
## 16. `Company.update()`
|
|
|
|
`CompanyPatchProps` incorporará conceptualmente:
|
|
|
|
```ts
|
|
address?: Maybe<PostalAddress>
|
|
```
|
|
|
|
Semántica:
|
|
|
|
```txt
|
|
omitido
|
|
→ no modificar
|
|
|
|
Maybe.none()
|
|
→ eliminar dirección
|
|
|
|
Maybe.some(address)
|
|
→ reemplazar dirección completa
|
|
```
|
|
|
|
No se implementará patch granular de campos internos de dirección.
|
|
|
|
No:
|
|
|
|
```txt
|
|
actualizar solo city
|
|
actualizar solo province
|
|
```
|
|
|
|
La dirección funciona como una unidad coherente.
|
|
|
|
---
|
|
|
|
## 17. Contratos HTTP
|
|
|
|
Se definirá un schema compartido de dirección dentro de `companies`.
|
|
|
|
Ubicación conceptual:
|
|
|
|
```txt
|
|
modules/companies/src/common/dto/shared/company-address.dto.ts
|
|
```
|
|
|
|
Shape:
|
|
|
|
```ts
|
|
export const CompanyAddressSchema = z.object({
|
|
street: z.string().trim().min(1).max(255),
|
|
street_2: z.string().trim().max(255).nullable(),
|
|
city: z.string().trim().min(1).max(255),
|
|
postal_code: PostalCodeSchema,
|
|
province: z.string().trim().min(1).max(255),
|
|
country_code: CountryCodeSchema,
|
|
});
|
|
|
|
export type CompanyAddressDTO = z.infer<typeof CompanyAddressSchema>;
|
|
```
|
|
|
|
Zod continúa siendo la SSOT.
|
|
|
|
No se crearán interfaces manuales paralelas.
|
|
|
|
---
|
|
|
|
## 18. CREATE HTTP
|
|
|
|
El request general de creación admitirá:
|
|
|
|
```ts
|
|
address: CompanyAddressSchema.nullable().optional()
|
|
```
|
|
|
|
Semántica:
|
|
|
|
```txt
|
|
omitido
|
|
→ sin dirección
|
|
|
|
null
|
|
→ sin dirección
|
|
|
|
objeto
|
|
→ dirección completa
|
|
```
|
|
|
|
Esto evita romper consumidores actuales de `POST /companies`.
|
|
|
|
El onboarding utiliza un contrato más estricto donde la dirección es obligatoria.
|
|
|
|
---
|
|
|
|
## 19. PATCH HTTP
|
|
|
|
Para la actualización de Company:
|
|
|
|
```ts
|
|
address: CompanyAddressSchema.nullable().optional()
|
|
```
|
|
|
|
Semántica:
|
|
|
|
```txt
|
|
omitido
|
|
→ no modificar
|
|
|
|
null
|
|
→ eliminar dirección
|
|
|
|
objeto
|
|
→ sustituir dirección completa
|
|
```
|
|
|
|
No se utilizarán defaults Zod.
|
|
|
|
No se transformará una dirección parcial en completa.
|
|
|
|
---
|
|
|
|
## 20. Response DTO
|
|
|
|
Las respuestas de `Company` deben incorporar:
|
|
|
|
```ts
|
|
address: CompanyAddressSchema.nullable()
|
|
```
|
|
|
|
Una empresa sin dirección debe devolver:
|
|
|
|
```json
|
|
{
|
|
"address": null
|
|
}
|
|
```
|
|
|
|
No omitir el campo en responses de detalle/configuración.
|
|
|
|
---
|
|
|
|
## 21. Application
|
|
|
|
Se ampliarán conceptualmente:
|
|
|
|
```txt
|
|
ICompanyCreateProps
|
|
CompanyInternalProps
|
|
CompanyPatchProps
|
|
```
|
|
|
|
con:
|
|
|
|
```ts
|
|
address: Maybe<PostalAddress>
|
|
```
|
|
|
|
o:
|
|
|
|
```ts
|
|
address?: Maybe<PostalAddress>
|
|
```
|
|
|
|
según corresponda.
|
|
|
|
Los mappings HTTP/Application deben crear los Value Objects necesarios.
|
|
|
|
No se pasará directamente el objeto DTO al agregado.
|
|
|
|
---
|
|
|
|
## 22. Public API de `companies`
|
|
|
|
`CompanyPublicModel` debe exponer la dirección.
|
|
|
|
No se crea otro finder.
|
|
|
|
El finder público actual seguirá siendo la capacidad de lectura entre módulos.
|
|
|
|
Otros módulos podrán consumir:
|
|
|
|
```txt
|
|
company.address
|
|
```
|
|
|
|
sin importar:
|
|
|
|
```txt
|
|
Company aggregate
|
|
SequelizeCompanyRepository
|
|
CompanyModel
|
|
```
|
|
|
|
---
|
|
|
|
## 23. Settings de Company
|
|
|
|
La pantalla actual debe incorporar una sección:
|
|
|
|
```txt
|
|
Dirección fiscal
|
|
```
|
|
|
|
Distribución recomendada:
|
|
|
|
```txt
|
|
Información general
|
|
-------------------
|
|
Razón social
|
|
Nombre comercial
|
|
NIF
|
|
|
|
Contacto
|
|
--------
|
|
Email
|
|
Teléfono
|
|
Web
|
|
|
|
Dirección fiscal
|
|
----------------
|
|
Dirección
|
|
Dirección adicional
|
|
Código postal
|
|
Ciudad
|
|
Provincia
|
|
País
|
|
|
|
Condiciones comerciales
|
|
-----------------------
|
|
Plazo de pago predeterminado
|
|
```
|
|
|
|
Se reutilizarán:
|
|
|
|
```txt
|
|
RdxPanel
|
|
RdxFormGrid
|
|
TextField
|
|
SelectField
|
|
```
|
|
|
|
No se importará:
|
|
|
|
```txt
|
|
CustomerAddressEditor
|
|
```
|
|
|
|
desde `customers`.
|
|
|
|
Eso crearía una dependencia funcional indebida:
|
|
|
|
```txt
|
|
companies → customers
|
|
```
|
|
|
|
---
|
|
|
|
## 24. Opciones de país en frontend
|
|
|
|
No se importará directamente:
|
|
|
|
```txt
|
|
COUNTRY_OPTIONS
|
|
```
|
|
|
|
desde `customers`.
|
|
|
|
Si la misma lista es necesaria en varios módulos, podrá extraerse a una ubicación frontend compartida apropiada.
|
|
|
|
Antes debe revisarse que la lista actual es parcial y no representa todos los países ISO.
|
|
|
|
La lista visual no debe confundirse con la validación del backend.
|
|
|
|
---
|
|
|
|
## 25. Reporting de proformas
|
|
|
|
Existe un consumidor real inmediato:
|
|
|
|
```txt
|
|
CompanyReportProfileFinder
|
|
```
|
|
|
|
Actualmente la dirección empresarial se resuelve como ausencia.
|
|
|
|
Después de esta evolución:
|
|
|
|
```txt
|
|
Company
|
|
↓
|
|
CompanyPublicModel.address
|
|
↓
|
|
CompanyReportProfileFinder
|
|
↓
|
|
CompanyReportProfile.address
|
|
↓
|
|
ProformaReportSnapshot
|
|
```
|
|
|
|
La proforma es un documento mutable, por lo que puede continuar utilizando información empresarial viva mientras permanezca en esa naturaleza.
|
|
|
|
---
|
|
|
|
## 26. Facturas emitidas
|
|
|
|
`IssuedInvoice` no materializa actualmente un snapshot del emisor.
|
|
|
|
Sí materializa otros datos históricos:
|
|
|
|
```txt
|
|
recipient
|
|
items
|
|
taxes
|
|
payment information
|
|
fiscal information
|
|
VeriFactu
|
|
```
|
|
|
|
pero no:
|
|
|
|
```txt
|
|
issuer
|
|
```
|
|
|
|
Además, el reporting actual de facturas emitidas mantiene datos empresariales y slug hardcoded en algunos puntos.
|
|
|
|
### Decisión
|
|
|
|
No conectar directamente:
|
|
|
|
```txt
|
|
IssuedInvoice report
|
|
→ Company viva
|
|
```
|
|
|
|
como solución a esta evolución.
|
|
|
|
Hacerlo permitiría que modificar la dirección de Company cambiara posteriormente la representación de una factura histórica regenerada.
|
|
|
|
Esto sería contrario al principio:
|
|
|
|
```txt
|
|
Snapshot models must not recalculate.
|
|
```
|
|
|
|
La evolución del emisor de `IssuedInvoice` queda como trabajo separado.
|
|
|
|
---
|
|
|
|
## 27. Snapshot futuro del emisor
|
|
|
|
Debe quedar registrada como deuda de diseño:
|
|
|
|
```txt
|
|
IssuedInvoice
|
|
issuer snapshot
|
|
```
|
|
|
|
Probablemente deberá materializar en emisión, como mínimo:
|
|
|
|
```txt
|
|
legalName
|
|
tradeName
|
|
tin
|
|
address
|
|
```
|
|
|
|
y cualquier otro dato empresarial que deba permanecer históricamente congelado.
|
|
|
|
No se diseña ni implementa en esta fase.
|
|
|
|
---
|
|
|
|
## 28. Idioma
|
|
|
|
Decisión:
|
|
|
|
```txt
|
|
POSPONER
|
|
```
|
|
|
|
No se añade:
|
|
|
|
```txt
|
|
Company.defaultLanguage
|
|
```
|
|
|
|
SSOT actuales:
|
|
|
|
```txt
|
|
Account
|
|
→ idioma UI
|
|
|
|
Customer
|
|
→ idioma del cliente
|
|
|
|
Proforma
|
|
→ idioma del documento
|
|
|
|
IssuedInvoice
|
|
→ idioma materializado
|
|
```
|
|
|
|
No existe actualmente una cadena funcional clara:
|
|
|
|
```txt
|
|
Company.defaultLanguage
|
|
→ Customer
|
|
→ Proforma
|
|
```
|
|
|
|
Añadir el campo ahora introduciría configuración sin consumidor estable.
|
|
|
|
---
|
|
|
|
## 29. Moneda
|
|
|
|
Decisión:
|
|
|
|
```txt
|
|
POSPONER
|
|
```
|
|
|
|
No se añade:
|
|
|
|
```txt
|
|
Company.defaultCurrency
|
|
```
|
|
|
|
Actualmente:
|
|
|
|
```txt
|
|
Customer
|
|
→ currencyCode
|
|
|
|
Proforma
|
|
→ currencyCode
|
|
|
|
IssuedInvoice
|
|
→ currencyCode
|
|
```
|
|
|
|
La creación de documentos maneja actualmente su propia moneda y existen defaults frontend.
|
|
|
|
No existe una precedencia empresarial consolidada.
|
|
|
|
Si se desea en el futuro:
|
|
|
|
```txt
|
|
Company → Customer → Proforma
|
|
```
|
|
|
|
debe diseñarse explícitamente.
|
|
|
|
---
|
|
|
|
## 30. Logo
|
|
|
|
Decisión:
|
|
|
|
```txt
|
|
POSPONER
|
|
```
|
|
|
|
No se añade:
|
|
|
|
```txt
|
|
Company.logo
|
|
Company.logoUrl
|
|
Company.logoAssetId
|
|
```
|
|
|
|
Todavía no existe infraestructura empresarial adecuada para:
|
|
|
|
```txt
|
|
upload
|
|
asset
|
|
storage
|
|
authorization
|
|
replacement
|
|
delete
|
|
lifecycle
|
|
```
|
|
|
|
Los assets actuales de Handlebars pertenecen a las plantillas y no constituyen almacenamiento de medios de Company.
|
|
|
|
---
|
|
|
|
## 31. Slug y creación desde onboarding
|
|
|
|
El onboarding no debe pedir:
|
|
|
|
```txt
|
|
slug
|
|
```
|
|
|
|
El slug es un detalle técnico de `companies`.
|
|
|
|
La futura capability pública de creación inicial debe poder recibir conceptualmente:
|
|
|
|
```ts
|
|
{
|
|
legalName,
|
|
tradeName,
|
|
tin,
|
|
email,
|
|
phone,
|
|
website,
|
|
address,
|
|
transaction
|
|
}
|
|
```
|
|
|
|
y `companies` debe resolver internamente:
|
|
|
|
```txt
|
|
companyId
|
|
slug
|
|
status
|
|
timestamps
|
|
```
|
|
|
|
El módulo `companies` será responsable de:
|
|
|
|
```txt
|
|
generar slug
|
|
normalizarlo
|
|
comprobar unicidad
|
|
desambiguar colisiones
|
|
```
|
|
|
|
La política exacta de desambiguación se cerrará al diseñar esa capability.
|
|
|
|
---
|
|
|
|
## 32. Campos del onboarding
|
|
|
|
La evolución de `Company` permite fijar el bloque empresarial del onboarding.
|
|
|
|
### Empresa
|
|
|
|
```txt
|
|
Razón social *
|
|
Nombre comercial
|
|
NIF
|
|
Email
|
|
Teléfono
|
|
Web
|
|
```
|
|
|
|
### Dirección fiscal
|
|
|
|
```txt
|
|
Dirección *
|
|
Dirección adicional
|
|
Código postal *
|
|
Ciudad *
|
|
Provincia *
|
|
País *
|
|
```
|
|
|
|
El onboarding no debe mostrar:
|
|
|
|
```txt
|
|
companyId
|
|
slug
|
|
status
|
|
timestamps
|
|
idioma
|
|
moneda
|
|
logo
|
|
IVA por defecto
|
|
retención por defecto
|
|
recargo por defecto
|
|
```
|
|
|
|
---
|
|
|
|
## 33. Compatibilidad y transición
|
|
|
|
Las nuevas columnas comenzarán nullable.
|
|
|
|
Las empresas existentes podrán continuar temporalmente con:
|
|
|
|
```txt
|
|
address = none
|
|
```
|
|
|
|
Settings permitirá completar la información posteriormente.
|
|
|
|
El onboarding de nuevas empresas impondrá una regla más estricta:
|
|
|
|
```txt
|
|
address obligatorio
|
|
```
|
|
|
|
No se diseñan migraciones productivas en esta fase.
|
|
|
|
La estrategia de migraciones continuará tratándose de forma independiente.
|
|
|
|
---
|
|
|
|
## 34. Tests de una futura implementación
|
|
|
|
### Domain
|
|
|
|
Debe verificarse:
|
|
|
|
```txt
|
|
create sin dirección
|
|
create con dirección
|
|
update set
|
|
update replace
|
|
update clear
|
|
rehydrate sin dirección
|
|
```
|
|
|
|
### Shared domain
|
|
|
|
Debe verificarse:
|
|
|
|
```txt
|
|
PostalAddress con CountryCode
|
|
normalización de CountryCode
|
|
comparación semántica
|
|
serialización
|
|
```
|
|
|
|
### Application
|
|
|
|
Debe verificarse:
|
|
|
|
```txt
|
|
mapeo create
|
|
mapeo patch
|
|
omitido
|
|
null
|
|
objeto
|
|
public model
|
|
```
|
|
|
|
### Persistence
|
|
|
|
Debe verificarse:
|
|
|
|
```txt
|
|
none → columnas null
|
|
address → columnas
|
|
columnas null → none
|
|
columnas completas → address
|
|
estado parcial → fail fast
|
|
```
|
|
|
|
### HTTP
|
|
|
|
Debe verificarse:
|
|
|
|
```txt
|
|
POST sin address
|
|
POST con address
|
|
PATCH omitido
|
|
PATCH null
|
|
PATCH objeto
|
|
GET address null
|
|
GET address completo
|
|
```
|
|
|
|
### Frontend
|
|
|
|
Debe verificarse:
|
|
|
|
```txt
|
|
hydrate
|
|
validación
|
|
dirty state
|
|
payload completo
|
|
clear address
|
|
country selector
|
|
```
|
|
|
|
### Reports
|
|
|
|
Debe verificarse:
|
|
|
|
```txt
|
|
CompanyReportProfile recibe address
|
|
proforma snapshot recibe address
|
|
```
|
|
|
|
---
|
|
|
|
## 35. Riesgos conocidos
|
|
|
|
### RELEVANTE — `PostalAddress` y país
|
|
|
|
Actualmente `PostalAddress` utiliza un concepto textual de país mientras los contratos modernos usan código ISO.
|
|
|
|
Debe corregirse antes de consolidar Company.address.
|
|
|
|
### RELEVANTE — consumidores existentes de `PostalAddress`
|
|
|
|
La evolución a `CountryCode` puede afectar:
|
|
|
|
```txt
|
|
customers
|
|
suppliers
|
|
tests
|
|
mappers
|
|
DTO mappings
|
|
```
|
|
|
|
Debe implementarse de forma transversal y comprobada.
|
|
|
|
### RELEVANTE — reporting histórico de facturas
|
|
|
|
`IssuedInvoice` no materializa emisor.
|
|
|
|
No debe resolverse leyendo Company viva sin diseño específico.
|
|
|
|
### RELEVANTE — templates con información empresarial estática
|
|
|
|
Existen plantillas que todavía contienen assets o datos empresariales hardcoded.
|
|
|
|
`Company.address` no resuelve por sí sola esa deuda.
|
|
|
|
### MENOR — código postal internacional
|
|
|
|
`PostalCode` actual no es compatible con todos los países representables por `CountryCode`.
|
|
|
|
Queda como limitación conocida.
|
|
|
|
---
|
|
|
|
## 36. Decisiones consolidadas
|
|
|
|
1. `Company` tendrá una única propiedad `address`.
|
|
2. Su tipo será `Maybe<PostalAddress>`.
|
|
3. No se creará `CompanyAddress`.
|
|
4. No se creará una tabla `company_addresses`.
|
|
5. `PostalAddress` compartido evolucionará de `Country` a `CountryCode`.
|
|
6. País se representará mediante ISO 3166-1 alpha-2.
|
|
7. `Company.address` podrá estar ausente.
|
|
8. Cuando exista, la dirección deberá ser completa.
|
|
9. `street2` será el único campo opcional dentro de una dirección presente.
|
|
10. Onboarding exigirá una dirección completa.
|
|
11. Las empresas existentes podrán continuar sin dirección durante la transición.
|
|
12. La dirección se almacenará embebida en `companies`.
|
|
13. No se crearán índices nuevos para campos de dirección.
|
|
14. PATCH reemplazará la dirección completa.
|
|
15. PATCH con `null` eliminará la dirección.
|
|
16. `CompanyPublicModel` expondrá `address`.
|
|
17. El finder público existente seguirá siendo suficiente para lectura.
|
|
18. Settings incorporará una sección de Dirección fiscal.
|
|
19. `companies` no dependerá de componentes ni dominio de `customers`.
|
|
20. Reporting de proformas podrá utilizar la dirección viva de Company.
|
|
21. Reporting de facturas emitidas no utilizará Company viva como solución histórica.
|
|
22. El snapshot del emisor de `IssuedInvoice` se diseñará por separado.
|
|
23. Idioma se pospone.
|
|
24. Moneda se pospone.
|
|
25. Logo se pospone.
|
|
26. El slug no será un campo del onboarding.
|
|
27. La futura capability de creación inicial de Company resolverá internamente ID, slug, status y timestamps.
|
|
28. Se mantiene temporalmente la restricción actual de `PostalCode` como limitación conocida.
|
|
|
|
---
|
|
|
|
## 37. Fuera de alcance
|
|
|
|
Fuera del alcance de esta evolución original:
|
|
|
|
```txt
|
|
snapshot issuer de IssuedInvoice
|
|
defaultLanguage
|
|
defaultCurrency
|
|
logo
|
|
asset management
|
|
company branches
|
|
multiple company addresses
|
|
international postal code engine
|
|
country catalog
|
|
migration runner
|
|
template versioning
|
|
```
|
|
|
|
---
|
|
|
|
## 38. Secuencia recomendada de implementación futura
|
|
|
|
```txt
|
|
P1
|
|
Evolucionar PostalAddress → CountryCode
|
|
|
|
P2
|
|
Añadir Company.address en dominio y persistencia
|
|
|
|
P3
|
|
Actualizar DTOs / Application / PublicModel
|
|
|
|
P4
|
|
Actualizar Settings
|
|
|
|
P5
|
|
Conectar Company.address al reporting de proformas
|
|
|
|
P6
|
|
Crear capability pública de creación inicial de Company para onboarding — completado
|
|
|
|
P7
|
|
Diseñar por separado issuer snapshot de IssuedInvoice
|
|
|
|
P8
|
|
Cerrar e implementar onboarding — completado
|
|
```
|
|
|
|
Esta secuencia mantiene cambios incrementales y evita mezclar `Company`, reporting histórico y onboarding en una única modificación.
|
|
|
|
---
|
|
|
|
## 39. Regla final
|
|
|
|
La dirección empresarial debe tener una única fuente de verdad:
|
|
|
|
```txt
|
|
Company.address
|
|
```
|
|
|
|
El onboarding la configura.
|
|
|
|
Settings la mantiene.
|
|
|
|
Los consumidores la leen mediante la API pública de `companies`.
|
|
|
|
Las facturas emitidas no deben depender de su valor vivo para preservar histórico.
|
|
|
|
La evolución debe priorizar:
|
|
|
|
```txt
|
|
SSOT
|
|
claridad semántica
|
|
compatibilidad
|
|
KISS
|
|
```
|
|
|
|
por encima de cualquier abstracción futura no necesaria.
|