Saltar a contenido

ADR-0009 · El correo es obligatorio cuando el cliente tiene RUC

  • Estado: aceptado
  • Decisores: mvaliente
  • Fecha de la decisión: 2026-09-01
  • Relacionado: análisis de clientes del exterior (reglas R1–R18 del maestro de clientes; esta es una regla general, no de clientes del exterior)

Contexto y problema

El correo del cliente era opcional en las cuatro capas: email con @IsOptional() en los DTO, notRequired() en el schema Yup, campo sin marca en el formulario y EmailAddress sin restricción en SAP.

Dos hechos que se verificaron al estudiarlo:

  1. Dentro del OMS ese campo no lo consume nadie. Se escribe en OCRD.EmailAddress y ahí termina. El único .email que el backend consume es el del proveedor, en el aviso de pago (outgoing-payments.service.ts).
  2. El consumidor real está río abajo: la facturación electrónica. El correo es el canal por el que se entrega el comprobante que nosotros emitimos. Un contribuyente sin correo cargado no tiene por dónde recibir su factura.

O sea que la exigencia no nace del maestro de clientes: es una restricción de un contexto posterior, proyectada sobre el maestro. Por eso es específica del RUC y no una obligatoriedad general del campo.

Decisión

Si el tipo de identidad es RUC (11), el correo electrónico es obligatorio — tanto al crear como al actualizar un cliente.

Alcance, en positivo y en negativo:

Caso Regla
Cliente, identidad 11 (RUC) ⛔ bloquea sin correo, en alta y en edición
Cliente, identidades 12–18 ✅ el correo sigue siendo opcional
Pestaña "Direcciones" de la edición ✅ nunca se traba
Proveedores No aplica (ver más abajo)

Por qué no aplica a proveedores

Cliente y proveedor son la misma entidad en SAP (OCRD, discriminada por CardType), así que la pregunta es legítima. Lo que cambia es la dirección de la relación: el campo EmailAddress responde siempre a ¿a dónde le mando yo algo a esta contraparte?

  • Cliente: el comprobante lo emitimos nosotros y sale hacia su correo. Tiene que estar en nuestra ficha.
  • Proveedor: la factura la emite él y llega a nuestro correo, que vive en la ficha que ellos tienen de nosotros, en su sistema. Nada de lo que carguemos acá cambia eso.

El correo del proveedor sí se usa, pero para otra cosa —avisarle un pago—, que es conveniencia operativa y no un requisito fiscal. Además, hoy el OMS no da de alta proveedores: crea siempre CardType: 'cCustomer', y de proveedores hay solo lectura (scope=supplier). La asimetría es deliberada, igual que la del estampado de fecha entre cobros y proveedores (ADR-0007).

Por qué el bloqueo también alcanza a la edición

Es la diferencia deliberada con las reglas de clasificación del análisis de clientes del exterior, que solo bloquean en el alta: ahí un cliente histórico puede tener una combinación identidad/grupo imposible que el vendedor no sabe resolver, y bloquear lo dejaría ineditable (excepción E13).

Acá la corrección es distinta: pedir un correo es una acción que quien edita puede completar. Se decidió bloquear también al editar, aceptando el costo descrito abajo.

Lo que no se toca es la pestaña "Direcciones": su payload manda solo addresses, sin identidad ni correo. Trabarla repetiría el bug M13, que dejaba sin poder guardar a los clientes cargados directamente en SAP.

Consecuencias

A favor

  • Ningún cliente contribuyente nuevo nace sin canal de entrega de su factura.
  • La regla vive en el DTO, así que la cumple cualquier consumidor del endpoint, no solo el formulario.
  • Cierra también el borrado silencioso: un email: '' llegaba a SAP y vaciaba EmailAddress sin aviso.

En contra — asumido

  • Los clientes con RUC que hoy están en SAP sin correo quedan trabados en la edición hasta que se les cargue uno. Si un vendedor entra a cambiar un teléfono, tiene que conseguir el correo primero. No se midió el universo afectado antes de decidir; conviene contar los U_CENT_TIPO_IDENT = 11 sin EmailAddress para saber el tamaño real del arrastre.
  • Riesgo de dato inventado. Toda obligatoriedad empuja al sinemail@sinemail.com cuando el correo no está a mano. Vale mirar los correos cargados a las pocas semanas: si aparecen repetidos, la regla está produciendo basura y hay que mover la fricción de lugar, no endurecerla.
  • La regla no alcanza a los clientes cargados directamente en SAP. El invariante no se puede garantizar globalmente desde el OMS: solo en sus propias superficies.

Alternativas consideradas

Alternativa Por qué no
Advertir y pedir confirmación al editar, bloquear solo en el alta Es el criterio de las reglas de clasificación (E13). Se descartó: acá la corrección está al alcance de quien edita.
Permitir el alta y bloquear recién al facturar Evita frenar una venta en el mostrador, pero mueve la validación a un flujo distinto y deja el maestro sucio mientras tanto.
Extender la obligatoriedad a la identidad 17 (tax ID del exterior) También es una empresa que recibe comprobantes, pero se prefirió el alcance literal de lo pedido. Queda como candidato natural si se amplía.
Admitir varios correos separados por ; Habitual en facturación (ventas + contabilidad). Requiere cambiar la validación en ambos extremos; hoy @IsEmail() acepta uno solo.

Cómo quedó implementado

Archivo Qué
src/modules/business-partners/business-partner.models.ts EmailRequiredForRucOnCreate y EmailRequiredForRucOnUpdate, colgados de identityType con el mismo patrón que la regla "teléfono o website". El update solo evalúa si el payload declara la identidad.
src/modules/business-partners/business-partner.dto.spec.ts 7 casos: alta sin correo, correo en blanco, alta válida, cédula sin correo, update que vacía el correo, update válido y guardado de solo direcciones.
frontend/src/validation/businessPartner.schema.ts email con .when("identityType", …). Alcanza a la edición porque clientDataSchema es el mismo objeto sin direcciones.
frontend/.../BusinessParnerForm.tsx Label Email * y required condicional; el lookup por RUC ya no pisa con vacío el correo escrito a mano.

La identidad se compara siempre como string normalizado, nunca contra el enum: GroupCode/IdentityType son string en el front y number en el back, y esa divergencia ya causó un bug entero en la clasificación por grupo.