fix(6-mejoras): subcategoría+apartado libre+PDF+coacreditado+docs+dedup
1. Subcategoría en modal descarte (catálogo completo + select) 2. Apartado sin monto mínimo (eliminada validación de 0,000) 3. PDF solo con propuesta elegida (no las 4) 4. Co-acreditado: checkbox 'no tiene' valida sin campos obligatorios 5. Documentos apartado: previsualizar + descargar (endpoint + frontend) 6. Oportunidades: sin duplicados, orden Confirmar→Afluencia→Cotizar→Aparto(verde)→Descartar
This commit is contained in:
@@ -35,6 +35,7 @@ const generalesValidos: DatosGenerales = {
|
||||
tipoVivienda: 'PROPIA',
|
||||
tipoCompra: 'VIVIR_AHI',
|
||||
actividadOcupacion: 'AGENTE DEL MINISTERIO PUBLICO FEDERAL',
|
||||
usuarioEnkontrol: '',
|
||||
}
|
||||
|
||||
describe('regex de identificadores', () => {
|
||||
@@ -128,6 +129,16 @@ describe('coacreditadoSchema', () => {
|
||||
expect(r.success).toBe(true)
|
||||
})
|
||||
|
||||
it('acepta datos ocultos a medias cuando el toggle está apagado', () => {
|
||||
const conBasura = {
|
||||
...base,
|
||||
tieneCoacreditado: false as const,
|
||||
generales: { ...base.generales, fecha: '', email: 'no-es-correo', codigoPostal: 'abc' },
|
||||
}
|
||||
const r = coacreditadoSchema.safeParse(conBasura)
|
||||
expect(r.success).toBe(true)
|
||||
})
|
||||
|
||||
it('exige el nombre del co-acreditado cuando el toggle está activo', () => {
|
||||
const r = coacreditadoSchema.safeParse({ ...base, tieneCoacreditado: true })
|
||||
expect(r.success).toBe(false)
|
||||
@@ -141,6 +152,19 @@ describe('coacreditadoSchema', () => {
|
||||
})
|
||||
expect(r.success).toBe(true)
|
||||
})
|
||||
|
||||
it('no exige usuarioEnkontrol del co-acreditado (no se captura en el bloque compacto)', () => {
|
||||
const sinUsuarioEnkontrol = JSON.parse(
|
||||
JSON.stringify({
|
||||
...base,
|
||||
tieneCoacreditado: true,
|
||||
generales: { ...base.generales, nombreCompleto: 'MARIA LOPEZ PEREZ' },
|
||||
}),
|
||||
) as typeof base & { tieneCoacreditado: true }
|
||||
delete (sinUsuarioEnkontrol.generales as Record<string, unknown>).usuarioEnkontrol
|
||||
const r = coacreditadoSchema.safeParse(sinUsuarioEnkontrol)
|
||||
expect(r.success).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('referenciasSchema', () => {
|
||||
|
||||
@@ -112,33 +112,47 @@ export const economicosSchema = z.object({
|
||||
})
|
||||
|
||||
// Datos generales del co-acreditado: el nombre se exige vía superRefine solo
|
||||
// cuando el toggle está activo; el resto se valida solo si se captura
|
||||
// cuando el toggle está activo; el resto se valida solo si se captura. La
|
||||
// fecha y usuarioEnkontrol no se capturan en el bloque compacto y no bloquean.
|
||||
const coacreditadoGeneralesSchema = datosGeneralesSchema.innerType().extend({
|
||||
nombreCompleto: z.string().trim().max(200),
|
||||
curp: curpOpcional,
|
||||
rfc: rfcOpcional,
|
||||
email: emailOpcional,
|
||||
fecha: textoOpcional,
|
||||
fechaNacimiento: textoOpcional,
|
||||
celular: textoOpcional,
|
||||
usuarioEnkontrol: textoOpcional.optional(),
|
||||
})
|
||||
|
||||
export const coacreditadoSchema = z
|
||||
.object({
|
||||
tieneCoacreditado: z.boolean(),
|
||||
generales: coacreditadoGeneralesSchema,
|
||||
empleo: empleoSchema,
|
||||
economicos: economicosSchema,
|
||||
})
|
||||
.superRefine((datos, ctx) => {
|
||||
// Los datos del co-acreditado solo se exigen si el toggle está activo
|
||||
if (datos.tieneCoacreditado && datos.generales.nombreCompleto.trim().length < 3) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ['generales', 'nombreCompleto'],
|
||||
message: 'El nombre del co-acreditado es requerido',
|
||||
})
|
||||
}
|
||||
})
|
||||
export const coacreditadoSchema = z.union([
|
||||
// Checkbox "El cliente no tiene co-acreditado" MARCADO: los bloques están
|
||||
// ocultos y pueden quedar a medias (o faltar); NO se valida nada.
|
||||
z.object({
|
||||
tieneCoacreditado: z.literal(false),
|
||||
generales: z.any(),
|
||||
empleo: z.any(),
|
||||
economicos: z.any(),
|
||||
}),
|
||||
// Checkbox SIN marcar (hay co-acreditado): se validan los bloques; el único
|
||||
// campo obligatorio es el nombre (el resto es opcional).
|
||||
z
|
||||
.object({
|
||||
tieneCoacreditado: z.literal(true),
|
||||
generales: coacreditadoGeneralesSchema,
|
||||
empleo: empleoSchema,
|
||||
economicos: economicosSchema,
|
||||
})
|
||||
.superRefine((datos, ctx) => {
|
||||
if (datos.generales.nombreCompleto.trim().length < 3) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ['generales', 'nombreCompleto'],
|
||||
message: 'El nombre del co-acreditado es requerido',
|
||||
})
|
||||
}
|
||||
}),
|
||||
])
|
||||
|
||||
const referenciaSchema = z.object({
|
||||
nombre: textoOpcional,
|
||||
|
||||
Reference in New Issue
Block a user