feat(api): add CFDI API endpoints (list, detail, resumen)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
128
apps/api/src/services/cfdi.service.ts
Normal file
128
apps/api/src/services/cfdi.service.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { prisma } from '../config/database.js';
|
||||
import type { Cfdi, CfdiFilters, CfdiListResponse } from '@horux/shared';
|
||||
|
||||
export async function getCfdis(schema: string, filters: CfdiFilters): Promise<CfdiListResponse> {
|
||||
const page = filters.page || 1;
|
||||
const limit = filters.limit || 20;
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
let whereClause = 'WHERE 1=1';
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
if (filters.tipo) {
|
||||
whereClause += ` AND tipo = $${paramIndex++}`;
|
||||
params.push(filters.tipo);
|
||||
}
|
||||
|
||||
if (filters.estado) {
|
||||
whereClause += ` AND estado = $${paramIndex++}`;
|
||||
params.push(filters.estado);
|
||||
}
|
||||
|
||||
if (filters.fechaInicio) {
|
||||
whereClause += ` AND fecha_emision >= $${paramIndex++}`;
|
||||
params.push(filters.fechaInicio);
|
||||
}
|
||||
|
||||
if (filters.fechaFin) {
|
||||
whereClause += ` AND fecha_emision <= $${paramIndex++}`;
|
||||
params.push(filters.fechaFin);
|
||||
}
|
||||
|
||||
if (filters.rfc) {
|
||||
whereClause += ` AND (rfc_emisor ILIKE $${paramIndex} OR rfc_receptor ILIKE $${paramIndex++})`;
|
||||
params.push(`%${filters.rfc}%`);
|
||||
}
|
||||
|
||||
if (filters.search) {
|
||||
whereClause += ` AND (uuid_fiscal ILIKE $${paramIndex} OR nombre_emisor ILIKE $${paramIndex} OR nombre_receptor ILIKE $${paramIndex++})`;
|
||||
params.push(`%${filters.search}%`);
|
||||
}
|
||||
|
||||
const countResult = await prisma.$queryRawUnsafe<[{ count: number }]>(`
|
||||
SELECT COUNT(*) as count FROM "${schema}".cfdis ${whereClause}
|
||||
`, ...params);
|
||||
|
||||
const total = Number(countResult[0]?.count || 0);
|
||||
|
||||
params.push(limit, offset);
|
||||
const data = await prisma.$queryRawUnsafe<Cfdi[]>(`
|
||||
SELECT
|
||||
id, uuid_fiscal as "uuidFiscal", tipo, serie, folio,
|
||||
fecha_emision as "fechaEmision", fecha_timbrado as "fechaTimbrado",
|
||||
rfc_emisor as "rfcEmisor", nombre_emisor as "nombreEmisor",
|
||||
rfc_receptor as "rfcReceptor", nombre_receptor as "nombreReceptor",
|
||||
subtotal, descuento, iva, isr_retenido as "isrRetenido",
|
||||
iva_retenido as "ivaRetenido", total, moneda,
|
||||
tipo_cambio as "tipoCambio", metodo_pago as "metodoPago",
|
||||
forma_pago as "formaPago", uso_cfdi as "usoCfdi",
|
||||
estado, xml_url as "xmlUrl", pdf_url as "pdfUrl",
|
||||
created_at as "createdAt"
|
||||
FROM "${schema}".cfdis
|
||||
${whereClause}
|
||||
ORDER BY fecha_emision DESC
|
||||
LIMIT $${paramIndex++} OFFSET $${paramIndex}
|
||||
`, ...params);
|
||||
|
||||
return {
|
||||
data,
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
};
|
||||
}
|
||||
|
||||
export async function getCfdiById(schema: string, id: string): Promise<Cfdi | null> {
|
||||
const result = await prisma.$queryRawUnsafe<Cfdi[]>(`
|
||||
SELECT
|
||||
id, uuid_fiscal as "uuidFiscal", tipo, serie, folio,
|
||||
fecha_emision as "fechaEmision", fecha_timbrado as "fechaTimbrado",
|
||||
rfc_emisor as "rfcEmisor", nombre_emisor as "nombreEmisor",
|
||||
rfc_receptor as "rfcReceptor", nombre_receptor as "nombreReceptor",
|
||||
subtotal, descuento, iva, isr_retenido as "isrRetenido",
|
||||
iva_retenido as "ivaRetenido", total, moneda,
|
||||
tipo_cambio as "tipoCambio", metodo_pago as "metodoPago",
|
||||
forma_pago as "formaPago", uso_cfdi as "usoCfdi",
|
||||
estado, xml_url as "xmlUrl", pdf_url as "pdfUrl",
|
||||
created_at as "createdAt"
|
||||
FROM "${schema}".cfdis
|
||||
WHERE id = $1
|
||||
`, id);
|
||||
|
||||
return result[0] || null;
|
||||
}
|
||||
|
||||
export async function getResumenCfdis(schema: string, año: number, mes: number) {
|
||||
const result = await prisma.$queryRawUnsafe<[{
|
||||
total_ingresos: number;
|
||||
total_egresos: number;
|
||||
count_ingresos: number;
|
||||
count_egresos: number;
|
||||
iva_trasladado: number;
|
||||
iva_acreditable: number;
|
||||
}]>(`
|
||||
SELECT
|
||||
COALESCE(SUM(CASE WHEN tipo = 'ingreso' THEN total ELSE 0 END), 0) as total_ingresos,
|
||||
COALESCE(SUM(CASE WHEN tipo = 'egreso' THEN total ELSE 0 END), 0) as total_egresos,
|
||||
COUNT(CASE WHEN tipo = 'ingreso' THEN 1 END) as count_ingresos,
|
||||
COUNT(CASE WHEN tipo = 'egreso' THEN 1 END) as count_egresos,
|
||||
COALESCE(SUM(CASE WHEN tipo = 'ingreso' THEN iva ELSE 0 END), 0) as iva_trasladado,
|
||||
COALESCE(SUM(CASE WHEN tipo = 'egreso' THEN iva ELSE 0 END), 0) as iva_acreditable
|
||||
FROM "${schema}".cfdis
|
||||
WHERE estado = 'vigente'
|
||||
AND EXTRACT(YEAR FROM fecha_emision) = $1
|
||||
AND EXTRACT(MONTH FROM fecha_emision) = $2
|
||||
`, año, mes);
|
||||
|
||||
const r = result[0];
|
||||
return {
|
||||
totalIngresos: Number(r?.total_ingresos || 0),
|
||||
totalEgresos: Number(r?.total_egresos || 0),
|
||||
countIngresos: Number(r?.count_ingresos || 0),
|
||||
countEgresos: Number(r?.count_egresos || 0),
|
||||
ivaTrasladado: Number(r?.iva_trasladado || 0),
|
||||
ivaAcreditable: Number(r?.iva_acreditable || 0),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user