Add workflow: Supabase → Sheet VENDAS (incremental sync)
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
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'
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user