import { prisma } from './prisma.js' // ── Tipos ─────────────────────────────────────────────────────────── export interface FiltrosClientesMaestro { busqueda?: string desarrollo?: string estatus?: string pagina?: number limite?: number } export type ClienteMaestroDTO = { id: string numCliente: string nombre: string apellidoPaterno: string apellidoMaterno: string | null sexo: string | null telefono: string | null email: string | null estadoCivil: string | null rangoEdad: string | null ocupacion: string | null rangoIngresos: string | null credito: string | null institucion: string | null medioContacto: string | null canal: string | null desarrollo: string | null manzana: string | null lote: string | null interior: string | null tipoCasa: string | null etapa: string | null valorTotal: number | null estatus: string | null fechaLiberacion: Date | null vendedor: string | null vendedorId: string | null importadoEn: Date } // ── Helpers ───────────────────────────────────────────────────────── function construirWhere(filtros: FiltrosClientesMaestro) { const where: Record = {} if (filtros.desarrollo) { where.desarrollo = filtros.desarrollo } if (filtros.estatus) { where.estatus = filtros.estatus } if (filtros.busqueda) { const term = filtros.busqueda.trim() where.OR = [ { nombre: { contains: term } }, { apellidoPaterno: { contains: term } }, { apellidoMaterno: { contains: term } }, { telefono: { contains: term } }, { numCliente: { contains: term } }, ] } return where } // ── Service ───────────────────────────────────────────────────────── /** * Lista clientes maestro con filtros y paginación. */ export async function listar(filtros: FiltrosClientesMaestro): Promise<{ datos: ClienteMaestroDTO[] total: number pagina: number limite: number }> { const pagina = filtros.pagina ?? 1 const limite = filtros.limite ?? 50 const skip = (pagina - 1) * limite const where = construirWhere(filtros) const [datos, total] = await Promise.all([ prisma.clienteMaestro.findMany({ where, orderBy: [{ apellidoPaterno: 'asc' }, { nombre: 'asc' }], skip, take: limite, }), prisma.clienteMaestro.count({ where }), ]) return { datos, total, pagina, limite } } /** * Obtiene un cliente por numCliente. */ export async function obtener(numCliente: string): Promise { return prisma.clienteMaestro.findUnique({ where: { numCliente } }) } /** * Cuenta clientes con filtros. */ export async function contar(filtros: Omit): Promise { const where = construirWhere(filtros) return prisma.clienteMaestro.count({ where }) } /** * Devuelve array de objetos para exportar a xlsx. */ export async function exportar( filtros: Omit, ): Promise { const where = construirWhere(filtros) return prisma.clienteMaestro.findMany({ where, orderBy: [{ apellidoPaterno: 'asc' }, { nombre: 'asc' }], take: 50000, // safety cap }) } /** * Bulk upsert: recibe un array de datos y los inserta/actualiza por numCliente. */ export async function importarDesdeExcel(datos: Array>): Promise<{ insertados: number; actualizados: number; errores: number }> { let insertados = 0 let actualizados = 0 let errores = 0 for (const fila of datos) { const numCliente = String(fila.numCliente ?? '').trim() const nombre = String(fila.nombre ?? '').trim() if (!numCliente || !nombre) { errores++ continue } try { const existente = await prisma.clienteMaestro.findUnique({ where: { numCliente } }) const data = { numCliente, nombre, apellidoPaterno: String(fila.apellidoPaterno ?? '').trim(), apellidoMaterno: fila.apellidoMaterno ? String(fila.apellidoMaterno).trim() : null, sexo: fila.sexo ? String(fila.sexo).trim() : null, telefono: fila.telefono ? String(fila.telefono).trim() : null, email: fila.email ? String(fila.email).trim() : null, estadoCivil: fila.estadoCivil ? String(fila.estadoCivil).trim() : null, rangoEdad: fila.rangoEdad ? String(fila.rangoEdad).trim() : null, ocupacion: fila.ocupacion ? String(fila.ocupacion).trim() : null, rangoIngresos: fila.rangoIngresos ? String(fila.rangoIngresos).trim() : null, credito: fila.credito ? String(fila.credito).trim() : null, institucion: fila.institucion ? String(fila.institucion).trim() : null, medioContacto: fila.medioContacto ? String(fila.medioContacto).trim() : null, canal: fila.canal ? String(fila.canal).trim() : null, desarrollo: fila.desarrollo ? String(fila.desarrollo).trim() : null, manzana: fila.manzana ? String(fila.manzana).trim() : null, lote: fila.lote ? String(fila.lote).trim() : null, interior: fila.interior ? String(fila.interior).trim() : null, tipoCasa: fila.tipoCasa ? String(fila.tipoCasa).trim() : null, etapa: fila.etapa ? String(fila.etapa).trim() : null, valorTotal: typeof fila.valorTotal === 'number' ? fila.valorTotal : null, estatus: fila.estatus ? String(fila.estatus).trim() : null, fechaLiberacion: fila.fechaLiberacion ? new Date(String(fila.fechaLiberacion)) : null, vendedor: fila.vendedor ? String(fila.vendedor).trim() : null, vendedorId: fila.vendedorId ? String(fila.vendedorId).trim() : null, } if (existente) { await prisma.clienteMaestro.update({ where: { numCliente }, data }) actualizados++ } else { await prisma.clienteMaestro.create({ data }) insertados++ } } catch { errores++ } } return { insertados, actualizados, errores } }