194 lines
6.0 KiB
TypeScript
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();
|