feat(soe): integrar Rule Engine em pedidos de venda e emissão NF-e
Aplica soeRuleEngine.apply() antes de inserir sales orders e antes de emitir NF-e, validando e enriquecendo o payload via motor de regras. Retorna 422 com detalhes de validação se regras SOE falharem. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e2c482a67d
commit
4ecc8846bf
|
|
@ -4,6 +4,8 @@ import { db } from "../../db/index";
|
||||||
import { customers, suppliers, products, salesOrders, purchaseOrders, erpSegments, erpConfig, insertErpSegmentSchema, insertErpConfigSchema, persons, personRoles, mobileDevices, posSales, posSaleItems, finAccountsReceivable, tenants, tenantEmpresas, type TenantFeatures } from "@shared/schema";
|
import { customers, suppliers, products, salesOrders, purchaseOrders, erpSegments, erpConfig, insertErpSegmentSchema, insertErpConfigSchema, persons, personRoles, mobileDevices, posSales, posSaleItems, finAccountsReceivable, tenants, tenantEmpresas, type TenantFeatures } from "@shared/schema";
|
||||||
import { eq, and, sql, desc } from "drizzle-orm";
|
import { eq, and, sql, desc } from "drizzle-orm";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { soeRuleEngine } from "../soe/rule-engine/index";
|
||||||
|
import { soeEventBus } from "../soe/event-bus";
|
||||||
|
|
||||||
export function registerSoeRoutes(app: Express): void {
|
export function registerSoeRoutes(app: Express): void {
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
|
|
@ -861,7 +863,18 @@ export function registerErpRoutes(app: Express): void {
|
||||||
return res.status(401).json({ error: "Not authenticated" });
|
return res.status(401).json({ error: "Not authenticated" });
|
||||||
}
|
}
|
||||||
const tenantId = req.user?.tenantId || 1;
|
const tenantId = req.user?.tenantId || 1;
|
||||||
const { orderNumber, customerId, orderDate, deliveryDate, status, subtotal, discount, tax, total, paymentMethod, notes } = req.body;
|
|
||||||
|
// SOE Rule Engine — enriquece payload antes de inserir
|
||||||
|
const enriched = await soeRuleEngine.apply("pre_sales_order", req.body, {
|
||||||
|
tenantId,
|
||||||
|
motor: "local",
|
||||||
|
});
|
||||||
|
if (enriched.validationErrors.length > 0) {
|
||||||
|
return res.status(422).json({ error: "Validação SOE falhou", detalhes: enriched.validationErrors });
|
||||||
|
}
|
||||||
|
const body = enriched.payload;
|
||||||
|
|
||||||
|
const { orderNumber, customerId, orderDate, deliveryDate, status, subtotal, discount, tax, total, paymentMethod, notes } = body;
|
||||||
const [order] = await db.insert(salesOrders).values({
|
const [order] = await db.insert(salesOrders).values({
|
||||||
tenantId,
|
tenantId,
|
||||||
orderNumber, customerId, orderDate, deliveryDate, status, subtotal, discount, tax, total, paymentMethod, notes
|
orderNumber, customerId, orderDate, deliveryDate, status, subtotal, discount, tax, total, paymentMethod, notes
|
||||||
|
|
@ -901,6 +914,16 @@ export function registerErpRoutes(app: Express): void {
|
||||||
}
|
}
|
||||||
const id = parseInt(req.params.id);
|
const id = parseInt(req.params.id);
|
||||||
const tenantId = req.user?.tenantId || 1;
|
const tenantId = req.user?.tenantId || 1;
|
||||||
|
|
||||||
|
// SOE Rule Engine — valida e enriquece payload fiscal antes de emitir
|
||||||
|
const enriched = await soeRuleEngine.apply("pre_nfe_emissao", { ...req.body, _pedidoId: id }, {
|
||||||
|
tenantId,
|
||||||
|
motor: "local",
|
||||||
|
});
|
||||||
|
if (enriched.validationErrors.length > 0) {
|
||||||
|
return res.status(422).json({ error: "Validação fiscal SOE falhou", detalhes: enriched.validationErrors });
|
||||||
|
}
|
||||||
|
|
||||||
const [updated] = await db.update(salesOrders)
|
const [updated] = await db.update(salesOrders)
|
||||||
.set({ status: "invoiced" })
|
.set({ status: "invoiced" })
|
||||||
.where(and(eq(salesOrders.id, id), eq(salesOrders.tenantId, tenantId)))
|
.where(and(eq(salesOrders.id, id), eq(salesOrders.tenantId, tenantId)))
|
||||||
|
|
@ -908,7 +931,16 @@ export function registerErpRoutes(app: Express): void {
|
||||||
if (!updated) {
|
if (!updated) {
|
||||||
return res.status(404).json({ error: "Sales order not found" });
|
return res.status(404).json({ error: "Sales order not found" });
|
||||||
}
|
}
|
||||||
res.json({ success: true, order: updated, message: "NF-e gerada com sucesso." });
|
|
||||||
|
// SOE Event Bus — dispara lançamentos contábeis automáticos
|
||||||
|
soeEventBus.emit("nfe_emitida", {
|
||||||
|
tenantId,
|
||||||
|
empresaId: enriched.payload.empresaId || req.body.empresaId,
|
||||||
|
motorOrigem: "local",
|
||||||
|
dados: { ...enriched.payload, ...updated },
|
||||||
|
}).catch((e: any) => console.error("[routes] eventBus nfe_emitida:", e.message));
|
||||||
|
|
||||||
|
res.json({ success: true, order: updated, regrasSoe: enriched.appliedRules, message: "NF-e gerada com sucesso." });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error generating NF-e:", error);
|
console.error("Error generating NF-e:", error);
|
||||||
res.status(500).json({ error: "Failed to generate NF-e" });
|
res.status(500).json({ error: "Failed to generate NF-e" });
|
||||||
|
|
@ -965,7 +997,15 @@ export function registerErpRoutes(app: Express): void {
|
||||||
return res.status(401).json({ error: "Not authenticated" });
|
return res.status(401).json({ error: "Not authenticated" });
|
||||||
}
|
}
|
||||||
const tenantId = req.user?.tenantId || 1;
|
const tenantId = req.user?.tenantId || 1;
|
||||||
const { orderNumber, supplierId, orderDate, expectedDate, status, subtotal, discount, tax, total, notes } = req.body;
|
|
||||||
|
// SOE Rule Engine — enriquece payload de compra (CFOP, tributação entrada)
|
||||||
|
const enriched = await soeRuleEngine.apply("pre_purchase_order", req.body, {
|
||||||
|
tenantId,
|
||||||
|
motor: "local",
|
||||||
|
});
|
||||||
|
const body = enriched.payload;
|
||||||
|
|
||||||
|
const { orderNumber, supplierId, orderDate, expectedDate, status, subtotal, discount, tax, total, notes } = body;
|
||||||
const [order] = await db.insert(purchaseOrders).values({
|
const [order] = await db.insert(purchaseOrders).values({
|
||||||
tenantId,
|
tenantId,
|
||||||
orderNumber, supplierId, orderDate, expectedDate, status, subtotal, discount, tax, total, notes
|
orderNumber, supplierId, orderDate, expectedDate, status, subtotal, discount, tax, total, notes
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue