feat: track average, max, and min stock additions per product in csv and n8n payload
All checks were successful
Build and Deploy / build-and-push (push) Successful in 1m4s
All checks were successful
Build and Deploy / build-and-push (push) Successful in 1m4s
This commit is contained in:
@@ -158,15 +158,15 @@ export const handleTinyStockUpdate = async (req: Request, res: Response): Promis
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Received stock webhook from Tiny. Calculating delta...');
|
||||
console.log('Received stock webhook from Tiny. Calculating delta and stats...');
|
||||
let payload = req.body || {};
|
||||
if (payload && typeof payload.dados === 'string') {
|
||||
try { payload.dados = JSON.parse(payload.dados); } catch (e) {}
|
||||
}
|
||||
|
||||
// --- SMART MEMORY LOGIC ---
|
||||
// --- SMART MEMORY & STATS LOGIC ---
|
||||
const memoryFile = path.join(process.cwd(), 'stock_memory.json');
|
||||
let memory: Record<string, number> = {};
|
||||
let memory: Record<string, any> = {};
|
||||
|
||||
// Load memory if it exists
|
||||
if (fs.existsSync(memoryFile)) {
|
||||
@@ -185,18 +185,51 @@ export const handleTinyStockUpdate = async (req: Request, res: Response): Promis
|
||||
let deltaEstoque = 0;
|
||||
|
||||
if (idProduto) {
|
||||
const estoqueAntigo = memory[idProduto];
|
||||
// Backwards compatibility for old memory format (if it was just a number)
|
||||
if (typeof memory[idProduto] === 'number') {
|
||||
memory[idProduto] = {
|
||||
saldo: memory[idProduto],
|
||||
addCount: 0,
|
||||
addTotal: 0,
|
||||
addMax: 0,
|
||||
addMin: null
|
||||
};
|
||||
}
|
||||
|
||||
const prodMem = memory[idProduto] || {
|
||||
saldo: undefined,
|
||||
addCount: 0,
|
||||
addTotal: 0,
|
||||
addMax: 0,
|
||||
addMin: null
|
||||
};
|
||||
|
||||
const estoqueAntigo = prodMem.saldo;
|
||||
|
||||
if (estoqueAntigo !== undefined) {
|
||||
// We know what it was before, calculate the difference
|
||||
// Calculate delta
|
||||
deltaEstoque = novoSaldo - estoqueAntigo;
|
||||
|
||||
// Track statistics if stock was ADDED
|
||||
if (deltaEstoque > 0) {
|
||||
prodMem.addCount += 1;
|
||||
prodMem.addTotal += deltaEstoque;
|
||||
|
||||
if (deltaEstoque > prodMem.addMax) {
|
||||
prodMem.addMax = deltaEstoque;
|
||||
}
|
||||
|
||||
if (prodMem.addMin === null || deltaEstoque < prodMem.addMin) {
|
||||
prodMem.addMin = deltaEstoque;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// First time seeing this product. Default delta to 0 so we don't alert on initial load.
|
||||
deltaEstoque = 0;
|
||||
}
|
||||
|
||||
// Save the new balance to memory
|
||||
memory[idProduto] = novoSaldo;
|
||||
// Update current balance in memory
|
||||
prodMem.saldo = novoSaldo;
|
||||
memory[idProduto] = prodMem;
|
||||
fs.writeFileSync(memoryFile, JSON.stringify(memory, null, 2));
|
||||
|
||||
console.log(`[Stock Tracker] Product ${idProduto}: Old=${estoqueAntigo ?? 'None'} -> New=${novoSaldo}. Delta=${deltaEstoque > 0 ? '+' : ''}${deltaEstoque}`);
|
||||
@@ -206,20 +239,35 @@ export const handleTinyStockUpdate = async (req: Request, res: Response): Promis
|
||||
const csvFile = path.join(process.cwd(), 'stock_log.csv');
|
||||
const timestamp = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
|
||||
const oldStockStr = estoqueAntigo !== undefined ? estoqueAntigo : 'NEW';
|
||||
const csvLine = `"${timestamp}","${idProduto}","${nomeProduto}","${oldStockStr}","${novoSaldo}","${deltaEstoque > 0 ? '+' : ''}${deltaEstoque}"\n`;
|
||||
|
||||
const adicionado = deltaEstoque > 0 ? deltaEstoque : 0;
|
||||
const vendido = deltaEstoque < 0 ? Math.abs(deltaEstoque) : 0;
|
||||
|
||||
// Calculate current average
|
||||
const mediaAdicionado = prodMem.addCount > 0 ? (prodMem.addTotal / prodMem.addCount).toFixed(2) : 0;
|
||||
const minAdicionado = prodMem.addMin !== null ? prodMem.addMin : 0;
|
||||
const maxAdicionado = prodMem.addMax;
|
||||
|
||||
const csvLine = `"${timestamp}","${idProduto}","${nomeProduto}","${oldStockStr}","${novoSaldo}","${adicionado}","${vendido}","${mediaAdicionado}","${maxAdicionado}","${minAdicionado}"\n`;
|
||||
|
||||
// Create file with headers if it doesn't exist
|
||||
if (!fs.existsSync(csvFile)) {
|
||||
fs.writeFileSync(csvFile, '"Data","ID_Produto","Nome_Produto","Estoque_Antigo","Estoque_Novo","Quantidade_Adicionada"\n');
|
||||
fs.writeFileSync(csvFile, '"Data","ID_Produto","Nome_Produto","Estoque_Antigo","Estoque_Novo","Adicionado","Vendido","Media_Adicionado","Max_Adicionado","Min_Adicionado"\n');
|
||||
}
|
||||
|
||||
fs.appendFileSync(csvFile, csvLine);
|
||||
}
|
||||
}
|
||||
|
||||
// Inject the delta into the payload so n8n can easily read it
|
||||
// Inject the delta and stats into the payload so n8n can easily read it
|
||||
if (!payload.dados) payload.dados = {};
|
||||
payload.dados.delta_estoque = deltaEstoque;
|
||||
if (idProduto && memory[idProduto]) {
|
||||
const pm = memory[idProduto];
|
||||
payload.dados.media_adicionado = pm.addCount > 0 ? (pm.addTotal / pm.addCount).toFixed(2) : 0;
|
||||
payload.dados.max_adicionado = pm.addMax;
|
||||
payload.dados.min_adicionado = pm.addMin !== null ? pm.addMin : 0;
|
||||
}
|
||||
|
||||
await sendToN8n(payload, targetUrl);
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user