almeira-web/transporte-cali-buenaventura/backend/src/services/local-billing-store.ts

194 lines
6.0 KiB
TypeScript

import fs from 'fs';
import path from 'path';
import crypto from 'crypto';
import { EstadoFacturacion } from '../models/CierreFacturacionMensual';
export interface LocalCierreRecord {
id: string; // UUID
conductor_id: string;
nit_o_cedula: string;
razon_social: string;
correo_electronico: string;
mes_calendario: number;
anio_calendario: number;
total_comisiones_acumuladas: number;
total_viajes_realizados: number;
estado_facturacion: EstadoFacturacion;
fecha_cierre: string; // ISO string
numero_factura?: string;
cufe?: string;
pdf_url?: string;
createdAt: string;
updatedAt: string;
}
const DATA_DIR = path.resolve(process.cwd(), 'data');
const STORE_PATH = path.join(DATA_DIR, 'pacifictrans_cierres_facturacion.json');
export class LocalBillingStore {
private static instance: LocalBillingStore;
private recordsMap = new Map<string, LocalCierreRecord>(); // key: `${conductor_id}_${mes}_${anio}`
private initialized = false;
private constructor() {
this.ensureInitialized();
}
public static getInstance(): LocalBillingStore {
if (!LocalBillingStore.instance) {
LocalBillingStore.instance = new LocalBillingStore();
}
return LocalBillingStore.instance;
}
private ensureInitialized() {
if (this.initialized) return;
try {
if (!fs.existsSync(DATA_DIR)) {
fs.mkdirSync(DATA_DIR, { recursive: true });
}
if (fs.existsSync(STORE_PATH)) {
const raw = fs.readFileSync(STORE_PATH, 'utf-8');
const list: LocalCierreRecord[] = JSON.parse(raw || '[]');
this.recordsMap.clear();
for (const r of list) {
const key = `${r.conductor_id}_${r.mes_calendario}_${r.anio_calendario}`;
this.recordsMap.set(key, r);
}
}
this.initialized = true;
} catch (err) {
console.warn('⚠️ [LocalBillingStore] Fallo al cargar cierres locales:', err);
this.initialized = true;
}
}
private persist() {
try {
if (!fs.existsSync(DATA_DIR)) {
fs.mkdirSync(DATA_DIR, { recursive: true });
}
const list = Array.from(this.recordsMap.values());
fs.writeFileSync(STORE_PATH, JSON.stringify(list, null, 2), 'utf-8');
} catch (err) {
console.error('❌ [LocalBillingStore] Error al guardar cierres locales:', err);
}
}
public upsertCierre(data: {
id?: string;
conductor_id: string;
nit_o_cedula: string;
razon_social: string;
correo_electronico: string;
mes_calendario: number;
anio_calendario: number;
total_comisiones_acumuladas: number;
total_viajes_realizados: number;
estado_facturacion?: EstadoFacturacion;
fecha_cierre?: Date | string;
}): LocalCierreRecord {
this.ensureInitialized();
const key = `${data.conductor_id}_${data.mes_calendario}_${data.anio_calendario}`;
const existing = this.recordsMap.get(key);
const now = new Date().toISOString();
const record: LocalCierreRecord = {
id: existing?.id || data.id || crypto.randomUUID(),
conductor_id: data.conductor_id,
nit_o_cedula: data.nit_o_cedula || existing?.nit_o_cedula || 'NO-REGISTRADO',
razon_social: data.razon_social || existing?.razon_social || 'Conductor Pacific Trans',
correo_electronico: data.correo_electronico || existing?.correo_electronico || 'info@pacifictrans.com',
mes_calendario: Number(data.mes_calendario),
anio_calendario: Number(data.anio_calendario),
total_comisiones_acumuladas: Number(data.total_comisiones_acumuladas || 0),
total_viajes_realizados: Number(data.total_viajes_realizados || 0),
estado_facturacion: data.estado_facturacion || existing?.estado_facturacion || 'pendiente_proceso',
fecha_cierre: data.fecha_cierre
? new Date(data.fecha_cierre).toISOString()
: existing?.fecha_cierre || now,
createdAt: existing?.createdAt || now,
updatedAt: now,
};
this.recordsMap.set(key, record);
this.persist();
return record;
}
public findByMesAnio(mes: number, anio: number): LocalCierreRecord[] {
this.ensureInitialized();
const results: LocalCierreRecord[] = [];
for (const r of this.recordsMap.values()) {
if (Number(r.mes_calendario) === Number(mes) && Number(r.anio_calendario) === Number(anio)) {
results.push({ ...r });
}
}
return results.sort((a, b) => b.total_comisiones_acumuladas - a.total_comisiones_acumuladas);
}
public findById(id: string): LocalCierreRecord | null {
this.ensureInitialized();
for (const r of this.recordsMap.values()) {
if (r.id === id) return { ...r };
}
return null;
}
public marcarComoFacturados(ids: string[]): { updatedCount: number; records: LocalCierreRecord[] } {
this.ensureInitialized();
const updated: LocalCierreRecord[] = [];
const idSet = new Set(ids);
for (const [key, r] of this.recordsMap.entries()) {
if (idSet.has(r.id)) {
const mod: LocalCierreRecord = {
...r,
estado_facturacion: 'facturado_manual',
updatedAt: new Date().toISOString(),
};
this.recordsMap.set(key, mod);
updated.push(mod);
}
}
if (updated.length > 0) {
this.persist();
}
return { updatedCount: updated.length, records: updated };
}
public actualizarFacturaDian(
id: string,
info: { numero_factura: string; cufe: string; pdf_url: string }
): LocalCierreRecord | null {
this.ensureInitialized();
for (const [key, r] of this.recordsMap.entries()) {
if (r.id === id) {
const mod: LocalCierreRecord = {
...r,
estado_facturacion: 'enviado_api' as any,
numero_factura: info.numero_factura,
cufe: info.cufe,
pdf_url: info.pdf_url,
updatedAt: new Date().toISOString(),
};
this.recordsMap.set(key, mod);
this.persist();
return mod;
}
}
return null;
}
public getAll(): LocalCierreRecord[] {
this.ensureInitialized();
return Array.from(this.recordsMap.values());
}
}
export const localBillingStore = LocalBillingStore.getInstance();