113 lines
3.2 KiB
TypeScript
113 lines
3.2 KiB
TypeScript
// SOE Event Bus — Dispara eventos após execução do motor
|
|
// Permite que lançamentos contábeis, alertas e automações
|
|
// sejam disparados de forma desacoplada após cada operação SOE.
|
|
|
|
import { db } from "../../db/index";
|
|
import { soeEventos } from "@shared/schema";
|
|
import { contabilHandler } from "./handlers/contabil-handler";
|
|
import { financialHandler } from "./handlers/financial-handler";
|
|
|
|
export type SoeEventType =
|
|
| "nfe_emitida"
|
|
| "nfe_cancelada"
|
|
| "venda_confirmada"
|
|
| "venda_faturada"
|
|
| "compra_recebida"
|
|
| "pagamento_realizado"
|
|
| "recebimento_realizado"
|
|
| "estoque_baixo";
|
|
|
|
export interface SoeEventPayload {
|
|
tenantId?: number;
|
|
empresaId?: number;
|
|
motorOrigem?: "plus" | "erpnext" | "local";
|
|
dados: Record<string, any>;
|
|
}
|
|
|
|
type EventHandler = (payload: SoeEventPayload) => Promise<void>;
|
|
|
|
// Mapa de handlers por evento
|
|
const HANDLERS: Record<SoeEventType, EventHandler[]> = {
|
|
nfe_emitida: [
|
|
contabilHandler.lancamentoVenda,
|
|
financialHandler.criarContaReceber,
|
|
],
|
|
nfe_cancelada: [
|
|
contabilHandler.estornoLancamentoVenda,
|
|
],
|
|
venda_confirmada: [
|
|
financialHandler.reservarEstoque,
|
|
],
|
|
venda_faturada: [
|
|
contabilHandler.lancamentoVenda,
|
|
],
|
|
compra_recebida: [
|
|
contabilHandler.lancamentoCompra,
|
|
financialHandler.criarContaPagar,
|
|
],
|
|
pagamento_realizado: [
|
|
contabilHandler.lancamentoPagamento,
|
|
financialHandler.baixarContaPagar,
|
|
],
|
|
recebimento_realizado: [
|
|
contabilHandler.lancamentoRecebimento,
|
|
financialHandler.baixarContaReceber,
|
|
],
|
|
estoque_baixo: [
|
|
financialHandler.alertaEstoqueBaixo,
|
|
],
|
|
};
|
|
|
|
export class SoeEventBus {
|
|
private static instance: SoeEventBus;
|
|
|
|
static getInstance(): SoeEventBus {
|
|
if (!SoeEventBus.instance) SoeEventBus.instance = new SoeEventBus();
|
|
return SoeEventBus.instance;
|
|
}
|
|
|
|
async emit(evento: SoeEventType, payload: SoeEventPayload): Promise<void> {
|
|
const start = Date.now();
|
|
|
|
// Registra evento no banco
|
|
let eventoId: string | undefined;
|
|
try {
|
|
const [row] = await db
|
|
.insert(soeEventos)
|
|
.values({
|
|
tenantId: payload.tenantId,
|
|
empresaId: payload.empresaId,
|
|
evento,
|
|
motorOrigem: payload.motorOrigem,
|
|
payloadEntrada: payload.dados,
|
|
status: "ok",
|
|
})
|
|
.returning({ id: soeEventos.id });
|
|
eventoId = row?.id;
|
|
} catch (e: any) {
|
|
console.error("[SoeEventBus] Erro ao registrar evento:", e.message);
|
|
}
|
|
|
|
// Dispara handlers em paralelo (falha isolada — não bloqueia o fluxo)
|
|
const handlers = HANDLERS[evento] ?? [];
|
|
const results = await Promise.allSettled(
|
|
handlers.map((h) => h({ ...payload, dados: { ...payload.dados, _eventoId: eventoId } }))
|
|
);
|
|
|
|
const erros = results
|
|
.filter((r) => r.status === "rejected")
|
|
.map((r) => (r as PromiseRejectedResult).reason?.message);
|
|
|
|
if (erros.length > 0) {
|
|
console.warn(`[SoeEventBus] ${evento}: ${erros.length} handler(s) falharam:`, erros);
|
|
}
|
|
|
|
console.log(
|
|
`[SoeEventBus] ${evento} processado em ${Date.now() - start}ms | ` +
|
|
`${handlers.length} handlers | ${erros.length} erros`
|
|
);
|
|
}
|
|
}
|
|
|
|
export const soeEventBus = SoeEventBus.getInstance();
|