.
This commit is contained in:
parent
3182771455
commit
97cafb3818
@ -46,6 +46,7 @@ Este directorio reúne la documentación técnica, operativa y de apoyo del ERP.
|
||||
- [README de `modules/identity`](../modules/identity/README.md)
|
||||
- [README de `modules/companies`](../modules/companies/README.md)
|
||||
- [README de `modules/auth`](../modules/auth/README.md)
|
||||
- [API pública de selección y alta rápida de clientes](./customers/customer-selection-and-quick-create.md)
|
||||
|
||||
#### Guías y estándares
|
||||
|
||||
|
||||
392
docs/customers/customer-selection-and-quick-create.md
Normal file
392
docs/customers/customer-selection-and-quick-create.md
Normal file
@ -0,0 +1,392 @@
|
||||
# Customer Selection y Quick Create
|
||||
|
||||
## Objetivo
|
||||
|
||||
`modules/customers/src/common/features` es la superficie pública del módulo `customers` para colaboración entre módulos frontend del ERP.
|
||||
|
||||
Estas features permiten que un consumidor:
|
||||
|
||||
- seleccione un cliente existente;
|
||||
- cree un cliente de alta rápida;
|
||||
- reciba siempre un contrato público homogéneo para aplicar el resultado en su propio formulario.
|
||||
|
||||
La dependencia deseada es:
|
||||
|
||||
```text
|
||||
consumer module
|
||||
│
|
||||
▼
|
||||
@erp/customers o @erp/customers/common
|
||||
│
|
||||
▼
|
||||
customers/common/features
|
||||
│
|
||||
├── customer-selection
|
||||
└── quick-create-customer
|
||||
```
|
||||
|
||||
Un consumidor no debe resolver estos flujos importando implementación interna desde:
|
||||
|
||||
- `modules/customers/src/web/create`
|
||||
- `modules/customers/src/web/update`
|
||||
- `modules/customers/src/web/shared`
|
||||
|
||||
Esos directorios siguen siendo responsabilidad interna del módulo `customers`.
|
||||
|
||||
## Features públicas
|
||||
|
||||
### `customer-selection`
|
||||
|
||||
Resuelve el problema de buscar y seleccionar un cliente desde otro módulo, exponiendo un flujo reutilizable con diálogo de selección y un contrato público estable para devolver el cliente elegido.
|
||||
|
||||
API pública real exportada por `modules/customers/src/common/features/customer-selection/index.ts`:
|
||||
|
||||
- `useCustomerSelectionFlow`
|
||||
- `CustomerSelectionCriteria`
|
||||
- `CustomerSelectionOption`
|
||||
- `SelectCustomerDialog`
|
||||
- `buildCustomerSelectionOption`
|
||||
|
||||
Responsabilidades principales:
|
||||
|
||||
- búsqueda y paginación de clientes;
|
||||
- apertura y cierre del selector;
|
||||
- transformación del resultado de listado al contrato público `CustomerSelectionOption`;
|
||||
- coordinación con alta rápida cuando el consumidor usa el flow público;
|
||||
- entrega del cliente seleccionado al callback del consumidor.
|
||||
|
||||
No pertenece a esta feature:
|
||||
|
||||
- construir `CreateCustomerParams`;
|
||||
- conocer el formulario completo de create/update de clientes;
|
||||
- invalidar cachés externas del consumidor;
|
||||
- decidir qué hace el consumidor con el cliente ya seleccionado.
|
||||
|
||||
### `quick-create-customer`
|
||||
|
||||
Resuelve el problema de crear un cliente mínimo y embebible sin exponer al consumidor los detalles internos del CRUD de `customers`.
|
||||
|
||||
API pública real exportada por `modules/customers/src/common/features/quick-create-customer/index.ts`:
|
||||
|
||||
- `useQuickCreateCustomerFlow`
|
||||
- `QuickCustomerCreateResult`
|
||||
- `QuickCreateCustomerDialog`
|
||||
|
||||
Responsabilidades principales:
|
||||
|
||||
- formulario reducido de alta rápida;
|
||||
- validación del formulario;
|
||||
- apertura y cierre del diálogo;
|
||||
- creación mediante la mutation propia de `customers`;
|
||||
- adaptación del resultado de creación a `CustomerSelectionOption`;
|
||||
- gestión de toasts, errores y estado del diálogo;
|
||||
- aplicación de defaults internos y contexto opcional del consumidor.
|
||||
|
||||
No pertenece a esta feature:
|
||||
|
||||
- que el consumidor construya `CreateCustomerParams`;
|
||||
- que el consumidor importe `useCustomerCreateMutation`;
|
||||
- que el consumidor gestione manualmente la caché de `customers`.
|
||||
|
||||
## Contrato público: `CustomerSelectionOption`
|
||||
|
||||
`CustomerSelectionOption` representa la proyección pública que `customers` entrega a otros módulos cuando el resultado del flujo es “ya tienes un cliente seleccionable y aplicable”.
|
||||
|
||||
No es la entity completa del CRUD de `customers`. Es un shape orientado a integración y UI.
|
||||
|
||||
Campos reales actuales:
|
||||
|
||||
- `id`
|
||||
- `status`
|
||||
- `isCompany`
|
||||
- `name`
|
||||
- `tradeName`
|
||||
- `tin`
|
||||
- `street`
|
||||
- `street2`
|
||||
- `city`
|
||||
- `province`
|
||||
- `postalCode`
|
||||
- `country`
|
||||
- `primaryEmail`
|
||||
- `primaryPhone`
|
||||
- `primaryMobile`
|
||||
|
||||
Su objetivo es permitir que el consumidor:
|
||||
|
||||
- persista el identificador funcional (`customerId`);
|
||||
- muestre un resumen útil del cliente;
|
||||
- trate de la misma forma un cliente seleccionado y uno recién creado.
|
||||
|
||||
## Convergencia entre selección y creación
|
||||
|
||||
La convergencia es intencional.
|
||||
|
||||
`modules/customers/src/common/features/quick-create-customer/entities/quick-customer-create-result.entity.ts` define:
|
||||
|
||||
```ts
|
||||
type QuickCustomerCreateResult = CustomerSelectionOption;
|
||||
```
|
||||
|
||||
Eso significa que, para el consumidor, no hay dos contratos distintos:
|
||||
|
||||
```text
|
||||
seleccionar cliente existente ───────┐
|
||||
├──► CustomerSelectionOption
|
||||
crear cliente rápidamente ───────────┘
|
||||
```
|
||||
|
||||
Una vez terminada la operación, el consumidor puede usar el mismo callback para ambos casos. En el flow público actual, ese callback es `onCustomerSelected(customer)`.
|
||||
|
||||
## `useCustomerSelectionFlow`
|
||||
|
||||
`useCustomerSelectionFlow` es el punto de composición pública recomendado cuando una pantalla necesita:
|
||||
|
||||
- seleccionar un cliente existente;
|
||||
- abrir alta rápida desde el selector;
|
||||
- abrir alta rápida directamente desde la pantalla;
|
||||
- recibir siempre un `CustomerSelectionOption`.
|
||||
|
||||
Parámetros públicos actuales:
|
||||
|
||||
- `onCustomerSelected(customer: CustomerSelectionOption)`
|
||||
- `defaultLanguageCode?`
|
||||
- `defaultCurrencyCode?`
|
||||
|
||||
Estado y artefactos que coordina:
|
||||
|
||||
- `selectCtrl` para el selector;
|
||||
- `createCtrl` para el diálogo de alta rápida;
|
||||
- `SelectCustomerDialog`;
|
||||
- `QuickCreateCustomerDialog`;
|
||||
- entrypoints de apertura para creación rápida en distintos contextos.
|
||||
|
||||
El consumidor no debería reproducir manualmente esta coordinación si ya consume este flow.
|
||||
|
||||
## Dos entrypoints de creación rápida
|
||||
|
||||
La implementación actual diferencia dos contextos y su semántica importa.
|
||||
|
||||
### `openQuickCreateDialog`
|
||||
|
||||
Uso típico:
|
||||
|
||||
- botón `Nuevo cliente` de una pantalla consumidora.
|
||||
|
||||
Comportamiento:
|
||||
|
||||
```text
|
||||
abre creación rápida directamente
|
||||
→ cancelar
|
||||
→ simplemente se cierra el diálogo
|
||||
```
|
||||
|
||||
No intenta reabrir el selector porque no presupone que el usuario viniera de ahí.
|
||||
|
||||
### `openQuickCreateFromSelection`
|
||||
|
||||
Uso típico:
|
||||
|
||||
- botón `Nuevo cliente` dentro de `SelectCustomerDialog`.
|
||||
|
||||
Comportamiento:
|
||||
|
||||
```text
|
||||
cierra selector
|
||||
→ abre creación rápida
|
||||
→ cancelar
|
||||
→ vuelve a abrir selector
|
||||
```
|
||||
|
||||
Esta diferencia evita que el consumidor tenga que gestionar manualmente el retorno al selector.
|
||||
|
||||
## Diagrama del flujo
|
||||
|
||||
```text
|
||||
useCustomerSelectionFlow
|
||||
│
|
||||
├── selectCtrl.openDialog()
|
||||
│ │
|
||||
│ ▼
|
||||
│ SelectCustomerDialog
|
||||
│ │
|
||||
│ ├── confirmar selección
|
||||
│ │ │
|
||||
│ │ ▼
|
||||
│ │ onCustomerSelected(CustomerSelectionOption)
|
||||
│ │
|
||||
│ └── Nuevo cliente
|
||||
│ │
|
||||
│ ▼
|
||||
│ openQuickCreateFromSelection()
|
||||
│ │
|
||||
│ ▼
|
||||
│ QuickCreateCustomerDialog
|
||||
│ │
|
||||
│ ├── cancelar
|
||||
│ │ │
|
||||
│ │ ▼
|
||||
│ │ reabrir selector
|
||||
│ │
|
||||
│ └── crear
|
||||
│ │
|
||||
│ ▼
|
||||
│ onCustomerSelected(CustomerSelectionOption)
|
||||
│
|
||||
└── openQuickCreateDialog()
|
||||
│
|
||||
▼
|
||||
QuickCreateCustomerDialog
|
||||
│
|
||||
├── cancelar
|
||||
│ │
|
||||
│ ▼
|
||||
│ cerrar diálogo
|
||||
│
|
||||
└── crear
|
||||
│
|
||||
▼
|
||||
onCustomerSelected(CustomerSelectionOption)
|
||||
```
|
||||
|
||||
## Detalle de `quick-create-customer`
|
||||
|
||||
### Formulario reducido
|
||||
|
||||
La alta rápida no intenta sustituir al CRUD completo de `customers`.
|
||||
|
||||
El schema público de formulario rápido actual (`QuickCustomerCreateFormSchema`) expone solo:
|
||||
|
||||
- `isCompany`
|
||||
- `name`
|
||||
- `tin`
|
||||
- `primaryEmail`
|
||||
|
||||
### Defaults internos y contexto opcional
|
||||
|
||||
La feature reutiliza `buildCustomerCreateDefault()` del área interna de create y resuelve por defecto:
|
||||
|
||||
- `country = ES`
|
||||
- `languageCode = es`
|
||||
- `currencyCode = EUR`
|
||||
|
||||
Además, `useQuickCreateCustomerFlow` y `useQuickCreateCustomerDialogController` admiten contexto opcional:
|
||||
|
||||
- `defaultCountry?`
|
||||
- `defaultLanguageCode?`
|
||||
- `defaultCurrencyCode?`
|
||||
|
||||
Regla conceptual:
|
||||
|
||||
```text
|
||||
consumidor
|
||||
→ aporta contexto opcional
|
||||
|
||||
customers
|
||||
→ sigue construyendo, validando y ejecutando la creación
|
||||
```
|
||||
|
||||
El consumidor no debe construir `CreateCustomerParams`.
|
||||
|
||||
### Validación, errores y toasts
|
||||
|
||||
La responsabilidad de validación y feedback está dentro de `quick-create-customer`:
|
||||
|
||||
- `QuickCustomerCreateFormSchema` valida el formulario;
|
||||
- `useQuickCreateCustomerDialogController` aplica errores de validación al form;
|
||||
- la feature muestra `showSuccessToast`, `showWarningToast` y `showErrorToast`;
|
||||
- el diálogo mantiene `submitError` y el estado `isSubmitting`.
|
||||
|
||||
Por tanto, un consumidor normal no debería duplicar:
|
||||
|
||||
- `try/catch` del submit;
|
||||
- toasts de éxito/error de creación;
|
||||
- `setError` sobre el formulario rápido.
|
||||
|
||||
### Caché
|
||||
|
||||
La creación usa `useCustomerCreateMutation()` desde el módulo `customers`.
|
||||
|
||||
La sincronización de React Query y su estrategia de caché siguen siendo responsabilidad de `customers`. El consumidor no debe invalidar manualmente queries de `customers` tras una creación rápida salvo un caso excepcional y explícitamente justificado.
|
||||
|
||||
## Ejemplo real: `proformas/update`
|
||||
|
||||
Caso real actual:
|
||||
|
||||
- `modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-page-controller.ts`
|
||||
- `modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-controller.ts`
|
||||
- `modules/customer-invoices/src/web/proformas/update/ui/pages/proforma-update-page.tsx`
|
||||
|
||||
Integración conceptual actual:
|
||||
|
||||
```text
|
||||
useCustomerSelectionFlow(...)
|
||||
│
|
||||
├── seleccionar cliente existente
|
||||
└── crear cliente rápido
|
||||
│
|
||||
▼
|
||||
onCustomerSelected(customer)
|
||||
│
|
||||
▼
|
||||
updateCtrl.setCustomer(customer)
|
||||
│
|
||||
▼
|
||||
form.setValue("customerId", customer.id)
|
||||
```
|
||||
|
||||
En `proformas/update`:
|
||||
|
||||
- el page controller compone `useCustomerSelectionFlow`;
|
||||
- la pantalla usa `selectCustomerCtrl.openQuickCreateDialog` para el botón principal `Nuevo cliente`;
|
||||
- `SelectCustomerDialog` usa `selectCustomerCtrl.openQuickCreateFromSelection` para `Nuevo cliente` dentro del selector;
|
||||
- ambos caminos convergen en `updateCtrl.setCustomer(customer)`.
|
||||
|
||||
## Rehidratación y snapshots externos
|
||||
|
||||
`CustomerSelectionOption` es el contrato ideal que `customers` entrega a consumidores.
|
||||
|
||||
Eso no implica que cualquier otro recurso del ERP tenga que persistir exactamente el mismo shape. Por ejemplo, una proforma puede contener un snapshot suficiente para representar a su cliente, pero no necesariamente todos los campos disponibles en `CustomerSelectionOption`.
|
||||
|
||||
La integración correcta consiste en:
|
||||
|
||||
- usar `CustomerSelectionOption` cuando el dato proviene del flujo público de `customers`;
|
||||
- permitir que recursos externos reconstruyan su propio resumen cuando trabajan con snapshots parciales.
|
||||
|
||||
## Qué debe importar un consumidor
|
||||
|
||||
Imports recomendados:
|
||||
|
||||
```ts
|
||||
import { useCustomerSelectionFlow } from "@erp/customers/common";
|
||||
import type { CustomerSelectionOption } from "@erp/customers";
|
||||
import { QuickCreateCustomerDialog, SelectCustomerDialog } from "@erp/customers";
|
||||
```
|
||||
|
||||
La ruta concreta puede variar según el barrel elegido, pero debe salir de la superficie pública exportada por `@erp/customers` o `@erp/customers/common`.
|
||||
|
||||
## Qué no debe importar un consumidor
|
||||
|
||||
No debe depender directamente de piezas internas del CRUD como:
|
||||
|
||||
- `CreateCustomerParams`
|
||||
- `CustomerCreateForm`
|
||||
- `useCustomerCreateMutation`
|
||||
- `buildCreateCustomerParams`
|
||||
- componentes o hooks desde `modules/customers/src/web/create/*`
|
||||
- detalles internos de `modules/customers/src/web/shared/*`
|
||||
|
||||
## Respuestas rápidas de contrato
|
||||
|
||||
1. `customer-selection` resuelve búsqueda, selección y entrega homogénea de cliente.
|
||||
2. `quick-create-customer` resuelve alta mínima embebible sin exponer el CRUD interno.
|
||||
3. La API pública real está definida por los barrels de cada feature, no por sus internals.
|
||||
4. El consumidor debe importar desde `@erp/customers` o `@erp/customers/common`.
|
||||
5. No debe importar internals de `web/create`, `web/update` o `web/shared`.
|
||||
6. `CustomerSelectionOption` es la proyección pública reutilizable de cliente seleccionado.
|
||||
7. `QuickCustomerCreateResult` converge en el mismo contrato para no bifurcar la integración.
|
||||
8. `openQuickCreateDialog` es apertura directa; `openQuickCreateFromSelection` preserva el retorno al selector.
|
||||
9. La cancelación y la reapertura del selector las gestiona el flow de `customers`.
|
||||
10. La creación, validación, errores y caché pertenecen a `customers`.
|
||||
11. El consumidor integra el resultado aplicando `CustomerSelectionOption` a su propio caso de uso.
|
||||
12. `proformas/update` lo hace actualmente con `updateCtrl.setCustomer(customer)`.
|
||||
@ -24,11 +24,19 @@ export const useCustomerSelectionFlow = (options: {
|
||||
defaultCurrencyCode: options.defaultCurrencyCode,
|
||||
});
|
||||
|
||||
/**
|
||||
* Se usa cuando la creación rápida nace dentro del selector.
|
||||
* Si el usuario cancela, `onCancel` reabre el selector.
|
||||
*/
|
||||
const openQuickCreateFromSelection = () => {
|
||||
selectCtrl.closeDialog();
|
||||
quickCreateFlow.createCtrl.openDialog();
|
||||
};
|
||||
|
||||
/**
|
||||
* Se usa cuando el usuario inicia la creación rápida desde una pantalla consumidora.
|
||||
* Si cancela, el diálogo simplemente se cierra.
|
||||
*/
|
||||
const openQuickCreateDialog = () => {
|
||||
quickCreateFlow.createCtrl.openDialog();
|
||||
};
|
||||
|
||||
@ -2,15 +2,34 @@
|
||||
|
||||
Feature pública reutilizable del módulo `customers` para alta rápida embebida.
|
||||
|
||||
La documentación de diseño e integración entre módulos está consolidada en:
|
||||
|
||||
- [docs/customers/customer-selection-and-quick-create.md](../../../../../../docs/customers/customer-selection-and-quick-create.md)
|
||||
|
||||
## API pública
|
||||
|
||||
```ts
|
||||
import {
|
||||
QuickCreateCustomerDialog,
|
||||
QuickCustomerCreateResult,
|
||||
useQuickCreateCustomerFlow,
|
||||
} from "@erp/customers/common";
|
||||
```
|
||||
|
||||
## Responsabilidad
|
||||
|
||||
- exponer un diálogo reutilizable de alta rápida;
|
||||
- validar el formulario mínimo;
|
||||
- crear el cliente usando la mutation propia de `customers`;
|
||||
- devolver el resultado como `CustomerSelectionOption`.
|
||||
|
||||
El consumidor no debería importar detalles internos como:
|
||||
|
||||
- `CreateCustomerParams`
|
||||
- `CustomerCreateForm`
|
||||
- `useCustomerCreateMutation`
|
||||
- `buildCreateCustomerParams`
|
||||
|
||||
## Ejemplo mínimo
|
||||
|
||||
```tsx
|
||||
@ -29,3 +48,24 @@ const quickCreate = useQuickCreateCustomerFlow({
|
||||
- Éxito: devuelve un `CustomerSelectionOption`, listo para reutilizarse en flujos de selección.
|
||||
- Cancelación: no crea nada, cierra el diálogo y dispara `onCancel` si el consumidor lo ha proporcionado.
|
||||
- Integración con `customer-selection`: puede abrirse desde el selector y devolver el cliente para dejarlo seleccionado automáticamente.
|
||||
|
||||
## Defaults y contexto
|
||||
|
||||
El formulario rápido actual pide solo:
|
||||
|
||||
- `isCompany`
|
||||
- `name`
|
||||
- `tin`
|
||||
- `primaryEmail`
|
||||
|
||||
Los defaults base de create se resuelven dentro de `customers`. Actualmente la feature conserva:
|
||||
|
||||
- `country = ES`
|
||||
- `languageCode = es`
|
||||
- `currencyCode = EUR`
|
||||
|
||||
Opcionalmente, el consumidor puede aportar contexto mediante:
|
||||
|
||||
- `defaultCountry`
|
||||
- `defaultLanguageCode`
|
||||
- `defaultCurrencyCode`
|
||||
|
||||
Loading…
Reference in New Issue
Block a user