feat: ecosistema FRASA — apps Cotizador, Alta Clientes, Cobranza y Landing

- Cotizador (/quote): 4 propuestas con formulas validadas del Excel, comparador,
  combobox de unidades, vigencia, PDF ANEXO A
- Alta Clientes (/client): wizard 8 pasos, validacion CURP RENAPO, OCR de INE,
  checklist de expediente, firma digital, 5 PDFs legales
- Cobranza (/pay): planes de enganche, mora, vista cobranza del dia, recibos PDF,
  bitacora, dashboard con filtros
- Landing (/): entrada al ecosistema
- Design system StudioBlank (monocromo, acento #1B4332) en las 4 apps
- 123 tests vitest
This commit is contained in:
2026-08-21 22:27:36 +00:00
commit ecd4edac02
147 changed files with 27345 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
import { describe, expect, it } from 'vitest'
import type { PlanPago } from '../../types'
import { generarCalendario, registrarAbono } from '../cobranza'
import { crearDocumento, folioRecibo } from './reciboAbono'
function planConAbono(): PlanPago {
const { mensualidad, pagos } = generarCalendario('2026-01-15', 100000, 3)
const plan: PlanPago = {
id: 'plan-fixture',
ventaRef: 'VTA-2026-001',
clienteNombre: 'Cliente Fixture',
clienteTelefono: '55 0000 0000',
unidadCodigo: 'U-101',
proyecto: 'SENDA',
fechaInicio: '2026-01-15',
engancheTotal: 100000,
numPagos: 3,
mensualidad,
status: 'ACTIVO',
pagos,
notas: [],
createdAt: '2026-01-01T00:00:00.000Z',
}
return registrarAbono(plan, {
pagoId: plan.pagos[0].id,
fechaPago: '2026-01-15',
monto: 5000,
referencia: 'SPEI-001',
})
}
describe('reciboAbono PDF', () => {
it('el folio son los primeros 8 chars del id de la aplicación en mayúsculas', () => {
const plan = planConAbono()
const aplicacion = plan.pagos[0].aplicaciones[0]
expect(folioRecibo(aplicacion)).toBe(aplicacion.id.slice(0, 8).toUpperCase())
expect(folioRecibo({ ...aplicacion, id: 'abcdef12-3456-7890' })).toBe('ABCDEF12')
})
it('genera el documento con nombre de archivo correcto', () => {
const plan = planConAbono()
const pago = plan.pagos[0]
const aplicacion = pago.aplicaciones[0]
const { doc, nombreArchivo } = crearDocumento(plan, pago, aplicacion, new Date('2026-02-20T12:00:00'))
expect(nombreArchivo).toBe(`recibo_U-101_${folioRecibo(aplicacion)}.pdf`)
expect(doc.getNumberOfPages()).toBeGreaterThanOrEqual(1)
// smoke: el contenido interno es un PDF válido
expect(doc.output('datauristring')).toContain('application/pdf')
})
})