feat(sat): factura global + fecha_efectiva, fallback tenant-contribuyente, fix anio_global typo
Factura Global & fecha_efectiva: - Migracion 045_factura_global.sql: periodicidad, meses_global, año_global, fecha_efectiva - sat-parser.service.ts: extrae InformacionGlobal del XML - sat.service.ts: calcFechaEfectiva con soporte bimestral (periodicidad 05) - metricas-compute, dashboard, impuestos, cfdi, export, conciliacion, alertas: reemplaza fecha_emision-1h por COALESCE(fecha_efectiva, fecha_emision-1h) - Script recalc-metricas.ts para recalculo manual Fallback datos fiscales tenant → contribuyente: - contribuyente.service.ts: fetchTenantFiscalData + mergeContribuyenteWithTenant rellena regimenFiscal, codigoPostal y domicilio cuando el contribuyente tiene el mismo RFC que el tenant y sus campos estan vacios - contribuyente.controller.ts y contribuyente-config.controller.ts: pasan req.user!.tenantId al servicio Fix critico SAT sync: - sat.service.ts: anio_global → año_global en INSERT/UPDATE de CFDIs (la migracion creo 'año_global' con tilde; el codigo usaba 'anio_global', causando fallo en 100% de inserciones de CFDI) - determineChunkMonths: salta sondeo si existe job previo con requestIds - MAX_POLL_ATTEMPTS: 45 → 500 (~8h) para syncs iniciales grandes Docs: - docs/sessions/2026-05-22-factura-global-contribuyente-fallback.md
This commit is contained in:
@@ -421,7 +421,7 @@ export default function CfdiPage() {
|
||||
}
|
||||
|
||||
const exportData = allRows.map(cfdi => ({
|
||||
'Fecha Emisión': new Date(cfdi.fechaEmision).toLocaleDateString('es-MX'),
|
||||
'Fecha Emisión': formatCfdiDate(cfdi.fechaEmision),
|
||||
'Tipo Comprobante': formatTipoComprobante(cfdi.tipoComprobante),
|
||||
'Uso CFDI': (cfdi as any).usoCfdi || '',
|
||||
'Serie': cfdi.serie || '',
|
||||
@@ -442,9 +442,7 @@ export default function CfdiPage() {
|
||||
// vacío en Excel para no confundir "0 = pagado" con "no aplica".
|
||||
'Saldo Pendiente': cfdi.saldoPendienteMxn ?? '',
|
||||
'Estatus': cfdi.status === 'Vigente' || cfdi.status === '1' ? 'Vigente' : 'Cancelado',
|
||||
'Fecha Cancelación': cfdi.fechaCancelacion
|
||||
? new Date(cfdi.fechaCancelacion).toLocaleDateString('es-MX')
|
||||
: '',
|
||||
'Fecha Cancelación': formatCfdiDate(cfdi.fechaCancelacion),
|
||||
'UUID': cfdi.uuid,
|
||||
}));
|
||||
|
||||
@@ -509,7 +507,7 @@ export default function CfdiPage() {
|
||||
if (key.endsWith('_mxn') || key === 'id' || key === 'cfdi_id') continue;
|
||||
// Formatear fecha si aplica
|
||||
if (key === 'fechaEmision' && typeof val === 'string') {
|
||||
out['Fecha Emisión'] = new Date(val).toLocaleDateString('es-MX');
|
||||
out['Fecha Emisión'] = formatCfdiDate(val);
|
||||
} else {
|
||||
out[key] = val;
|
||||
}
|
||||
@@ -539,7 +537,7 @@ export default function CfdiPage() {
|
||||
|
||||
const exportSingleCfdiToExcel = (cfdi: Cfdi) => {
|
||||
const row = {
|
||||
'Fecha Emisión': new Date(cfdi.fechaEmision).toLocaleDateString('es-MX'),
|
||||
'Fecha Emisión': formatCfdiDate(cfdi.fechaEmision),
|
||||
'Tipo Comprobante': formatTipoComprobante(cfdi.tipoComprobante),
|
||||
'Uso CFDI': (cfdi as any).usoCfdi || '',
|
||||
'Serie': cfdi.serie || '',
|
||||
@@ -560,9 +558,7 @@ export default function CfdiPage() {
|
||||
// vacío en Excel para no confundir "0 = pagado" con "no aplica".
|
||||
'Saldo Pendiente': cfdi.saldoPendienteMxn ?? '',
|
||||
'Estatus': cfdi.status === 'Vigente' || cfdi.status === '1' ? 'Vigente' : 'Cancelado',
|
||||
'Fecha Cancelación': cfdi.fechaCancelacion
|
||||
? new Date(cfdi.fechaCancelacion).toLocaleDateString('es-MX')
|
||||
: '',
|
||||
'Fecha Cancelación': formatCfdiDate(cfdi.fechaCancelacion),
|
||||
'UUID': cfdi.uuid,
|
||||
};
|
||||
|
||||
@@ -935,12 +931,22 @@ export default function CfdiPage() {
|
||||
currency: 'MXN',
|
||||
}).format(value);
|
||||
|
||||
const formatDate = (dateString: string) =>
|
||||
new Date(dateString).toLocaleDateString('es-MX', {
|
||||
const formatDate = (dateString: string) => {
|
||||
const d = new Date(dateString);
|
||||
d.setHours(d.getHours() - 1);
|
||||
return d.toLocaleDateString('es-MX', {
|
||||
day: '2-digit',
|
||||
month: 'short',
|
||||
year: 'numeric',
|
||||
});
|
||||
};
|
||||
|
||||
const formatCfdiDate = (dateString: string | null | undefined) => {
|
||||
if (!dateString) return '-';
|
||||
const d = new Date(dateString);
|
||||
d.setHours(d.getHours() - 1);
|
||||
return d.toLocaleDateString('es-MX');
|
||||
};
|
||||
|
||||
const generateUUID = () => {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
@@ -1697,7 +1703,7 @@ export default function CfdiPage() {
|
||||
<tbody className="text-sm text-center">
|
||||
{conceptosQuery.data.data.map((row, idx) => (
|
||||
<tr key={`${row.cfdi_id}-${row.id}-${idx}`} className="border-b hover:bg-muted/50">
|
||||
<td className="py-2">{new Date(row.fechaEmision).toLocaleDateString('es-MX')}</td>
|
||||
<td className="py-2">{formatCfdiDate(row.fechaEmision)}</td>
|
||||
<td className="py-2 font-mono text-xs" title={row.uuid}>{row.uuid?.substring(0, 8) || '-'}</td>
|
||||
<td className="py-2 font-mono text-xs">{row.clave_prod_serv || '-'}</td>
|
||||
<td className="py-2 text-left max-w-[280px] truncate" title={row.descripcion}>{row.descripcion}</td>
|
||||
|
||||
Reference in New Issue
Block a user