Files
ofrangao-ops/supabase-sheet-vendas/workflow.js
T

188 lines
7.0 KiB
JavaScript

import { workflow, trigger, node } from 'n8n-workflow-sdk';
export default workflow('Supabase → Sheet VENDAS', {
description: 'Busca fechamentos_web do Supabase e append na Sheet VENDAS (incremental por atualizado_em UTC)',
nodes: [
// ── TRIGGER: 20h BRT (23h UTC) ──────────────────────────────────
trigger('Schedule Daily 20h', {
type: 'n8n-nodes-base.scheduleTrigger',
typeVersion: 1.2,
position: [100, 300],
parameters: {
rule: { interval: [{ field: 'cron', expression: '0 20 * * *' }] }
}
}),
// ── READ LAST SYNC from Sheet Sincronizacao A1 ───────────────────
node('Read Last Sync', {
type: 'n8n-nodes-base.googleSheets',
typeVersion: 4.2,
position: [340, 300],
credentials: { googleSheetsOAuth2Api: { id: 'qVVlfm46BZ6tX5VX', name: 'Google Sheets account' } },
parameters: {
operation: 'read',
documentId: { __rl: true, mode: 'id', value: '1RGa7Qigu5kWJGCOx3kJ3Oa8wTTJeD-CzGjBYazrjsWk' },
sheetName: { __rl: true, mode: 'list', value: 'Sincronizacao' },
cellSelection: { __rl: true, mode: 'list', value: 'A1' },
options: { cellFormat: 'USER_ENTERED' }
}
}),
// ── FETCH fechamentos_web from Supabase ──────────────────────────
node('Fetch from Supabase', {
type: 'n8n-nodes-base.httpRequest',
typeVersion: 4.2,
position: [600, 300],
credentials: { supabaseApi: { id: 'Tw9r35a9Iz3P9q7x', name: 'Supabase account 2 (self-hosted)' } },
parameters: {
method: 'GET',
url: '=https://supabase.ofrangao.com.br/rest/v1/fechamentos_web',
queryParameters: {
parameters: [
{
name: 'select',
value: '=id_fechamento,data,loja,turno,operador,fechamento_dinheiro,fechamento_cartoes,total_sangrias,total_despesas,total_vales,total_pixcnpj,saldo_pixcnpj,total_areceber,total_cartao_credito,total_cartao_debito,total_cartao_alimentacao,total_cancelamentos,diferenca,status_diferenca,atualizado_em'
},
{ name: 'order', value: '=atualizado_em.asc' },
{ name: 'limit', value: '=500' }
]
}
}
}),
// ── CODE: Filter by lastSync + Map to Sheet columns ─────────────
node('Filter + Map', {
type: 'n8n-nodes-base.code',
typeVersion: 2,
position: [860, 300],
parameters: {
jsCode: `
// 1. lastSync da Sheet
const lastSyncRaw = $input.first().json.value || null;
const lastSync = lastSyncRaw ? new Date(lastSyncRaw).toISOString() : null;
// 2. Todas as rows do Supabase
const allRows = $input.all().map(item => item.json);
// 3. Filtrar por lastSync
let filtered = lastSync ? allRows.filter(r => r.atualizado_em && r.atualizado_em > lastSync) : allRows;
if (filtered.length === 0) {
// Sem linhas novas —marker o sync point com lastSync antigo para não re-buscar
return [{
json: {
_action: 'noop',
_lastSync: lastSync,
_max_updated: lastSync,
DATA:'', EMPRESA:'', SANGRIA:0, DESPESAS:0, VALES:0,
DINHEIRO:0, PIX_TAXA:0, PIX_LOJA:0, PRAZO:0, P2:0,
CREDITO:0, DEBITO:0, VOUCHER:0, CARTAO:0,
T_VENDA:0, TX_CAIXA:0, CLIENTES:0, CANCELADOS:0,
SISTEMA:0, DIFERENCA:0, CAIXA:'', PG:false
}
}];
}
// 4. MAX(atualizado_em) do batch
const maxUpdated = filtered.reduce((m, r) =>
r.atualizado_em > m ? r.atualizado_em : m, filtered[0].atualizado_em);
// 5. Mapear para colunas Sheet
const LOJA_MAP = { alianca: 'Aliança', uniao: 'União' };
return filtered.map(r => ({
json: {
DATA: r.data,
EMPRESA: LOJA_MAP[r.loja] || r.loja,
SANGRIA: r.total_sangrias || 0,
DESPESAS: r.total_despesas || 0,
VALES: r.total_vales || 0,
DINHEIRO: r.fechamento_dinheiro || 0,
PIX_TAXA: r.total_pixcnpj || 0,
PIX_LOJA: r.saldo_pixcnpj || 0,
PRAZO: r.total_areceber || 0,
P2: 0,
CREDITO: r.total_cartao_credito || 0,
DEBITO: r.total_cartao_debito || 0,
VOUCHER: r.total_cartao_alimentacao || 0,
CARTAO: r.fechamento_cartoes || 0,
T_VENDA: 0, // fórmula na Sheet
TX_CAIXA: 0, // fórmula na Sheet
CLIENTES: 0,
CANCELADOS: r.total_cancelamentos || 0,
SISTEMA: 0, // fórmula na Sheet
DIFERENCA: r.diferenca || 0,
CAIXA: r.operador || '',
PG: r.status_diferenca === 'ok',
_action: 'append',
_max_updated: maxUpdated
}
}));
`
}
}),
// ── APPEND to Sheet VENDAS ──────────────────────────────────────
node('Append to VENDAS', {
type: 'n8n-nodes-base.googleSheets',
typeVersion: 4.2,
position: [1100, 200],
credentials: { googleSheetsOAuth2Api: { id: 'qVVlfm46BZ6tX5VX', name: 'Google Sheets account' } },
parameters: {
operation: 'append',
documentId: { __rl: true, mode: 'id', value: '1RGa7Qigu5kWJGCOx3kJ3Oa8wTTJeD-CzGjBYazrjsWk' },
sheetName: { __rl: true, mode: 'list', value: 'VENDAS' },
options: { cellFormat: 'USER_ENTERED' }
}
}),
// ── UPDATE LAST SYNC in Sincronizacao A1 ───────────────────────
// Recebe o primeiro item (com _max_updated) de Filter+Map
node('Update Last Sync', {
type: 'n8n-nodes-base.googleSheets',
typeVersion: 4.2,
position: [1100, 440],
credentials: { googleSheetsOAuth2Api: { id: 'qVVlfm46BZ6tX5VX', name: 'Google Sheets account' } },
parameters: {
operation: 'update',
documentId: { __rl: true, mode: 'id', value: '1RGa7Qigu5kWJGCOx3kJ3Oa8wTTJeD-CzGjBYazrjsWk' },
sheetName: { __rl: true, mode: 'list', value: 'Sincronizacao' },
cellSelection: { __rl: true, mode: 'list', value: 'A1' },
valueInputOption: 'USER_ENTERED',
options: { cellFormat: 'USER_ENTERED' }
}
}),
],
connections: {
'Schedule Daily 20h': {
main: [[{ node: 'Read Last Sync', type: 'main', index: 0 }]]
},
'Read Last Sync': {
main: [[{ node: 'Fetch from Supabase', type: 'main', index: 0 }]]
},
'Fetch from Supabase': {
main: [[{ node: 'Filter + Map', type: 'main', index: 0 }]]
},
'Filter + Map': {
main: [
// Branch 0: dados para Sheet
[{ node: 'Append to VENDAS', type: 'main', index: 0 }],
// Branch 1: marker sync (vai directo para Update Last Sync)
[{ node: 'Update Last Sync', type: 'main', index: 0 }]
]
},
'Append to VENDAS': {
main: [[{ node: 'Update Last Sync', type: 'main', index: 0 }]]
}
},
settings: {
executionOrder: 'v1',
saveExecutionProgress: true,
saveDataSuccessExecution: 'all',
saveDataErrorExecution: 'all'
}
});