import type { Pool } from 'pg'; export interface DocumentoExtra { id: number; contribuyenteId: string | null; nombre: string; descripcion: string | null; categoria: string | null; pdfFilename: string; subidoPor: string | null; createdAt: string; } interface CreateExtraInput { contribuyenteId?: string | null; nombre: string; descripcion?: string | null; categoria?: string | null; pdfBase64: string; pdfFilename: string; subidoPor: string; } function sanitizeUuid(id: string): string { return id.replace(/[^a-f0-9-]/gi, ''); } export async function createExtra( pool: Pool, data: CreateExtraInput, ): Promise { const pdfBuffer = Buffer.from(data.pdfBase64, 'base64'); const contribuyenteId = data.contribuyenteId ? sanitizeUuid(data.contribuyenteId) : null; const { rows } = await pool.query( `INSERT INTO documentos_extras (contribuyente_id, nombre, descripcion, categoria, pdf, pdf_filename, subido_por) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, contribuyente_id AS "contribuyenteId", nombre, descripcion, categoria, pdf_filename AS "pdfFilename", subido_por AS "subidoPor", created_at AS "createdAt"`, [ contribuyenteId, data.nombre, data.descripcion ?? null, data.categoria ?? null, pdfBuffer, data.pdfFilename, data.subidoPor, ], ); const r = rows[0]; return { ...r, createdAt: r.createdAt?.toISOString?.() ?? r.createdAt }; } export async function listExtras( pool: Pool, contribuyenteId?: string | null, categoria?: string | null, ): Promise { const params: any[] = []; const where: string[] = []; if (contribuyenteId) { params.push(sanitizeUuid(contribuyenteId)); where.push(`contribuyente_id = $${params.length}`); } if (categoria) { params.push(categoria); where.push(`categoria = $${params.length}`); } const whereClause = where.length ? `WHERE ${where.join(' AND ')}` : ''; const { rows } = await pool.query( `SELECT id, contribuyente_id AS "contribuyenteId", nombre, descripcion, categoria, pdf_filename AS "pdfFilename", subido_por AS "subidoPor", created_at AS "createdAt" FROM documentos_extras ${whereClause} ORDER BY created_at DESC LIMIT 500`, params, ); return rows.map((r: any) => ({ ...r, createdAt: r.createdAt?.toISOString?.() ?? r.createdAt, })); } export async function getExtraPdf( pool: Pool, id: number, ): Promise<{ buffer: Buffer; filename: string; nombre: string } | null> { const { rows } = await pool.query( `SELECT pdf, pdf_filename AS "pdfFilename", nombre FROM documentos_extras WHERE id = $1`, [id], ); if (rows.length === 0) return null; return { buffer: rows[0].pdf, filename: rows[0].pdfFilename, nombre: rows[0].nombre, }; } export async function deleteExtra(pool: Pool, id: number): Promise { const { rowCount } = await pool.query( `DELETE FROM documentos_extras WHERE id = $1`, [id], ); return (rowCount ?? 0) > 0; } export async function listCategorias( pool: Pool, contribuyenteId?: string | null, ): Promise { const params: any[] = []; let where = `WHERE categoria IS NOT NULL AND categoria != ''`; if (contribuyenteId) { params.push(sanitizeUuid(contribuyenteId)); where += ` AND contribuyente_id = $${params.length}`; } const { rows } = await pool.query( `SELECT DISTINCT categoria FROM documentos_extras ${where} ORDER BY categoria`, params, ); return rows.map((r: any) => r.categoria); }