n8n-nodes-ifood-completo 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.23
|
|
4
|
+
|
|
5
|
+
- Corrige o node `iFood Analise Conciliacao` para nao usar eventos logisticos/operacionais em `diferencas`.
|
|
6
|
+
- Passa a montar `diferencas` apenas com lancamentos financeiros com valor numerico, priorizando `billingSummary.billingEntries`.
|
|
7
|
+
- Completa o valor bruto com `saleGrossValue` ou `ORDER_PAYMENT` e calcula `liquidoVenda` por `billingEntries` quando `saleBalance` nao vier na resposta.
|
|
8
|
+
- Melhora a leitura de lote, valor e status do repasse a partir de `closingItems`, priorizando o item `REPASSE`.
|
|
9
|
+
|
|
10
|
+
## 0.1.22
|
|
11
|
+
|
|
12
|
+
- Altera a saida do node `iFood Analise Conciliacao` para um unico item JSON consolidado com `merchantId`, `periodo`, `vendas` e `repasses`.
|
|
13
|
+
- Remove os payloads brutos de origem e campos extras da resposta, mantendo apenas o modelo sintetico de conciliacao solicitado.
|
|
14
|
+
|
|
3
15
|
## 0.1.21
|
|
4
16
|
|
|
5
17
|
- Ajusta `Financial Events` para paginar a partir de `page=1` e consultar o periodo semanal do corte, respeitando o limite da API.
|
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ O node `iFood Authentication` nao exige credencial nem recebe `Access Token`; el
|
|
|
60
60
|
|
|
61
61
|
`iFood Produtos do Catalogo` gera o token internamente por `client_credentials`. Informe somente `Client ID`, `Client Secret`, `Merchant ID` e `Catalog ID`; a saída contém um item n8n por produto, com categoria, código externo, valor, situação, pausa e grupos de complementos.
|
|
62
62
|
|
|
63
|
-
`iFood Analise Conciliacao` tambem gera o token internamente por `client_credentials`. Informe `Client ID`, `Client Secret`, `Merchant ID da Loja` e `Data` no formato `yyyy-mm-dd`; o node consulta vendas do dia, eventos financeiros e repasses relacionados, retornando um
|
|
63
|
+
`iFood Analise Conciliacao` tambem gera o token internamente por `client_credentials`. Informe `Client ID`, `Client Secret`, `Merchant ID da Loja` e `Data` no formato `yyyy-mm-dd`; o node consulta vendas do dia, eventos financeiros e repasses relacionados, retornando um unico JSON consolidado com `merchantId`, `periodo`, `vendas` e `repasses`.
|
|
64
64
|
|
|
65
65
|
## Tutorial
|
|
66
66
|
|
package/TUTORIAL.md
CHANGED
|
@@ -421,7 +421,8 @@ Reconciliation Arquivo:
|
|
|
421
421
|
|
|
422
422
|
O node `iFood Analise Conciliacao` executa o fluxo financeiro consolidado para uma
|
|
423
423
|
data especifica. Ele recebe os dados de credenciamento manualmente, gera o token
|
|
424
|
-
OAuth internamente e retorna um item n8n
|
|
424
|
+
OAuth internamente e retorna um unico item n8n consolidado com as vendas e repasses
|
|
425
|
+
da data informada.
|
|
425
426
|
|
|
426
427
|
Parametros obrigatorios:
|
|
427
428
|
|
|
@@ -436,41 +437,54 @@ Fluxo executado:
|
|
|
436
437
|
|
|
437
438
|
1. Gera o token em `/authentication/v1.0/oauth/token`.
|
|
438
439
|
2. Consulta vendas em `/v3/sales` no dia informado.
|
|
439
|
-
3. Consulta eventos financeiros em `/v3/financial-events`
|
|
440
|
+
3. Consulta eventos financeiros em `/v3/financial-events` quando disponivel para vincular impactos e repasse.
|
|
440
441
|
4. Consulta repasses em `/financial/v3.0/merchants/{merchantId}/settlements` para identificar lote, valor e data de pagamento.
|
|
441
|
-
5.
|
|
442
|
+
5. Monta `diferencas` apenas com lancamentos financeiros com valor, priorizando `billingSummary.billingEntries` da venda.
|
|
442
443
|
|
|
443
|
-
Modelo de saida
|
|
444
|
+
Modelo de saida consolidada:
|
|
444
445
|
|
|
445
446
|
```json
|
|
446
447
|
{
|
|
447
448
|
"merchantId": "MERCHANT_ID",
|
|
448
|
-
"
|
|
449
|
-
|
|
450
|
-
"orderId": "ORDER_ID",
|
|
451
|
-
"shortId": "1234",
|
|
452
|
-
"statusFinal": "CONCLUDED",
|
|
453
|
-
"dataVenda": "2026-09-06T18:40:00Z"
|
|
454
|
-
},
|
|
455
|
-
"corteSemanal": {
|
|
456
|
-
"inicio": "2026-08-31",
|
|
449
|
+
"periodo": {
|
|
450
|
+
"inicio": "2026-09-06",
|
|
457
451
|
"fim": "2026-09-06"
|
|
458
452
|
},
|
|
459
|
-
"
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
453
|
+
"vendas": [
|
|
454
|
+
{
|
|
455
|
+
"orderId": "ORDER_ID",
|
|
456
|
+
"shortId": "1234",
|
|
457
|
+
"statusFinal": "CONCLUDED",
|
|
458
|
+
"dataVenda": "2026-09-06T18:40:00Z",
|
|
459
|
+
"corteSemanal": {
|
|
460
|
+
"inicio": "2026-08-31",
|
|
461
|
+
"fim": "2026-09-06"
|
|
462
|
+
},
|
|
463
|
+
"repasse": {
|
|
464
|
+
"dataPrevista": "2026-09-16",
|
|
465
|
+
"dataPagamento": "2026-09-16",
|
|
466
|
+
"loteId": "LOTE_ID",
|
|
467
|
+
"valorLote": 14082.41,
|
|
468
|
+
"status": "SUCCEED"
|
|
469
|
+
},
|
|
470
|
+
"valores": {
|
|
471
|
+
"bruto": 100,
|
|
472
|
+
"descontos": 10,
|
|
473
|
+
"acrescimos": 0,
|
|
474
|
+
"liquidoVenda": 90,
|
|
475
|
+
"liquidoRepasse": 72.5
|
|
476
|
+
},
|
|
477
|
+
"diferencas": []
|
|
478
|
+
}
|
|
479
|
+
],
|
|
480
|
+
"repasses": [
|
|
481
|
+
{
|
|
482
|
+
"dataPagamento": "2026-09-16",
|
|
483
|
+
"loteId": "LOTE_ID",
|
|
484
|
+
"valor": 14082.41,
|
|
485
|
+
"vendas": ["ORDER_ID"]
|
|
486
|
+
}
|
|
487
|
+
]
|
|
474
488
|
}
|
|
475
489
|
```
|
|
476
490
|
|
|
@@ -69,6 +69,8 @@ class IfoodCompletoAnaliseConciliacao {
|
|
|
69
69
|
|
|
70
70
|
const eventsByOrder = groupFinancialEventsByOrder(financialEvents);
|
|
71
71
|
const settlementsIndex = indexSettlements(settlements);
|
|
72
|
+
const vendas = [];
|
|
73
|
+
const repassesMap = {};
|
|
72
74
|
|
|
73
75
|
for (const sale of sales) {
|
|
74
76
|
const saleId = saleIdentifier(sale);
|
|
@@ -77,10 +79,19 @@ class IfoodCompletoAnaliseConciliacao {
|
|
|
77
79
|
const orderEvents = mergeEvents(saleId && eventsByOrder[saleId] ? eventsByOrder[saleId] : [], salesOrderEvents);
|
|
78
80
|
const corte = weekCut(saleDate.slice(0, 10));
|
|
79
81
|
const settlement = matchSettlement(orderEvents, settlementsIndex, corte);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
const venda = buildSaleConciliation({ sale, saleId, saleDate, orderEvents, corte, settlement });
|
|
83
|
+
vendas.push(venda);
|
|
84
|
+
addRepasseSale(repassesMap, venda);
|
|
83
85
|
}
|
|
86
|
+
|
|
87
|
+
returnData.push({
|
|
88
|
+
json: {
|
|
89
|
+
merchantId,
|
|
90
|
+
periodo: { inicio: data, fim: data },
|
|
91
|
+
vendas,
|
|
92
|
+
repasses: Object.keys(repassesMap).map((key) => repassesMap[key]),
|
|
93
|
+
},
|
|
94
|
+
});
|
|
84
95
|
}
|
|
85
96
|
|
|
86
97
|
return [returnData];
|
|
@@ -215,11 +226,8 @@ function groupFinancialEventsByOrder(events) {
|
|
|
215
226
|
|
|
216
227
|
function normalizeSaleOrderEvents(sale) {
|
|
217
228
|
const events = [];
|
|
218
|
-
for (const event of arrayFrom(sale, ['orderEvents'])) {
|
|
219
|
-
events.push(event);
|
|
220
|
-
}
|
|
221
229
|
for (const entry of arrayFrom(sale, ['billingEntries', 'billingSummary.billingEntries'])) {
|
|
222
|
-
events.push(Object.assign({ hasTransferImpact: true }, entry));
|
|
230
|
+
events.push(Object.assign({ hasTransferImpact: true, source: 'billingEntries' }, entry));
|
|
223
231
|
}
|
|
224
232
|
return events;
|
|
225
233
|
}
|
|
@@ -290,33 +298,34 @@ function periodMatches(settlement, corte) {
|
|
|
290
298
|
}
|
|
291
299
|
|
|
292
300
|
function settlementSummary(settlement, matchedId) {
|
|
301
|
+
const closingItem = settlementClosingItem(settlement);
|
|
293
302
|
return {
|
|
294
|
-
loteId: matchedId || firstText(settlement, ['id', 'settlementId', 'transactionId', 'closingItemId']),
|
|
295
|
-
dataPagamento: firstText(settlement, ['paymentDate', 'paidAt', 'date', 'settlementDate']),
|
|
296
|
-
valorLote: firstNumber(settlement, ['balance', 'amount', 'value', 'totalAmount']),
|
|
297
|
-
status: firstText(settlement, ['status', 'situation']),
|
|
298
|
-
banco: objectValue(settlement, ['accountDetails', 'bankAccount', 'bank']),
|
|
299
|
-
origem: settlement,
|
|
303
|
+
loteId: matchedId || firstText(closingItem, ['id', 'settlementId', 'transactionId', 'closingItemId']) || firstText(settlement, ['id', 'settlementId', 'transactionId', 'closingItemId']),
|
|
304
|
+
dataPagamento: firstText(closingItem, ['paymentDate', 'paidAt', 'date', 'settlementDate']) || firstText(settlement, ['paymentDate', 'paidAt', 'date', 'settlementDate']),
|
|
305
|
+
valorLote: firstNumber(closingItem, ['balance', 'amount', 'value', 'totalAmount']) || firstNumber(settlement, ['balance', 'amount', 'value', 'totalAmount']),
|
|
306
|
+
status: firstText(closingItem, ['status', 'situation']) || firstText(settlement, ['status', 'situation']),
|
|
300
307
|
};
|
|
301
308
|
}
|
|
302
309
|
|
|
310
|
+
function settlementClosingItem(settlement) {
|
|
311
|
+
const items = arrayFrom(settlement, ['closingItems', 'items']);
|
|
312
|
+
if (!items.length) return {};
|
|
313
|
+
return items.find((item) => firstText(item, ['type']).toUpperCase() === 'REPASSE') || items[0];
|
|
314
|
+
}
|
|
315
|
+
|
|
303
316
|
function buildSaleConciliation(ctx) {
|
|
304
|
-
const {
|
|
317
|
+
const { sale, saleId, saleDate, orderEvents, corte, settlement } = ctx;
|
|
305
318
|
const valoresVenda = saleValues(sale);
|
|
306
|
-
const diferencas = orderEvents.map(normalizeFinancialEvent);
|
|
319
|
+
const diferencas = orderEvents.map(normalizeFinancialEvent).filter(isFinancialDifference);
|
|
307
320
|
const liquidoRepasse = calculateTransferNet(diferencas, valoresVenda.liquidoVenda);
|
|
308
321
|
const descontosEventos = sumBySignAndType(diferencas, 'desconto');
|
|
309
322
|
const acrescimosEventos = sumBySignAndType(diferencas, 'acrescimo');
|
|
310
323
|
|
|
311
324
|
return {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
shortId: firstText(sale, ['shortId', 'displayId', 'order.displayId']),
|
|
317
|
-
statusFinal: firstText(sale, ['currentStatus', 'status', 'order.status']),
|
|
318
|
-
dataVenda: saleDate,
|
|
319
|
-
},
|
|
325
|
+
orderId: saleId,
|
|
326
|
+
shortId: firstText(sale, ['shortId', 'displayId', 'order.displayId']),
|
|
327
|
+
statusFinal: firstText(sale, ['currentStatus', 'status', 'order.status']),
|
|
328
|
+
dataVenda: saleDate,
|
|
320
329
|
corteSemanal: corte,
|
|
321
330
|
repasse: settlement ? {
|
|
322
331
|
dataPrevista: firstTextFromEvents(orderEvents, ['settlement.expectedDate', 'expectedPaymentDate', 'paymentDate']) || settlement.dataPagamento,
|
|
@@ -324,14 +333,12 @@ function buildSaleConciliation(ctx) {
|
|
|
324
333
|
loteId: settlement.loteId,
|
|
325
334
|
valorLote: settlement.valorLote,
|
|
326
335
|
status: settlement.status,
|
|
327
|
-
banco: settlement.banco,
|
|
328
336
|
} : {
|
|
329
337
|
dataPrevista: firstTextFromEvents(orderEvents, ['settlement.expectedDate', 'expectedPaymentDate', 'paymentDate']),
|
|
330
338
|
dataPagamento: '',
|
|
331
339
|
loteId: '',
|
|
332
340
|
valorLote: null,
|
|
333
341
|
status: '',
|
|
334
|
-
banco: null,
|
|
335
342
|
},
|
|
336
343
|
valores: {
|
|
337
344
|
bruto: valoresVenda.bruto,
|
|
@@ -341,37 +348,104 @@ function buildSaleConciliation(ctx) {
|
|
|
341
348
|
liquidoRepasse,
|
|
342
349
|
},
|
|
343
350
|
diferencas,
|
|
344
|
-
repassesRelacionados: settlement ? [settlement.origem] : [],
|
|
345
|
-
origem: {
|
|
346
|
-
sale,
|
|
347
|
-
financialEvents: orderEvents,
|
|
348
|
-
},
|
|
349
351
|
};
|
|
350
352
|
}
|
|
351
353
|
|
|
354
|
+
function addRepasseSale(map, venda) {
|
|
355
|
+
const repasse = venda && venda.repasse ? venda.repasse : null;
|
|
356
|
+
if (!repasse || !repasse.loteId) return;
|
|
357
|
+
const key = `${repasse.dataPagamento}|${repasse.loteId}`;
|
|
358
|
+
if (!map[key]) {
|
|
359
|
+
map[key] = {
|
|
360
|
+
dataPagamento: repasse.dataPagamento,
|
|
361
|
+
loteId: repasse.loteId,
|
|
362
|
+
valor: repasse.valorLote,
|
|
363
|
+
vendas: [],
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
if (venda.orderId && map[key].vendas.indexOf(venda.orderId) === -1) {
|
|
367
|
+
map[key].vendas.push(venda.orderId);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
352
371
|
function saleValues(sale) {
|
|
372
|
+
const billingEntries = arrayFrom(sale, ['billingEntries', 'billingSummary.billingEntries']);
|
|
373
|
+
const bruto = saleGrossValue(sale) || billingEntryValue(billingEntries, 'ORDER_PAYMENT');
|
|
374
|
+
const liquidoVenda = firstNumber(sale, ['billingSummary.saleBalance', 'billingSummary.netValue', 'netValue', 'saleBalance']);
|
|
353
375
|
return {
|
|
354
|
-
bruto: firstNumber(sale, ['billingSummary.grossValue', 'billingSummary.total', 'total.orderAmount', 'total.price', 'orderAmount', 'amount']),
|
|
355
|
-
descontos:
|
|
356
|
-
acrescimos: firstNumber(sale, ['billingSummary.additionalFees', 'billingSummary.addition', 'total.additionalFees', 'additionalFees', 'additions']),
|
|
357
|
-
liquidoVenda:
|
|
376
|
+
bruto: bruto !== null ? bruto : firstNumber(sale, ['billingSummary.grossValue', 'billingSummary.total', 'total.orderAmount', 'total.price', 'orderAmount', 'amount']),
|
|
377
|
+
descontos: saleDiscountValue(sale),
|
|
378
|
+
acrescimos: firstNumber(sale, ['billingSummary.additionalFees', 'billingSummary.addition', 'total.additionalFees', 'additionalFees', 'additions', 'additionalFee']),
|
|
379
|
+
liquidoVenda: liquidoVenda !== null ? liquidoVenda : billingEntriesBalance(billingEntries),
|
|
358
380
|
};
|
|
359
381
|
}
|
|
360
382
|
|
|
383
|
+
function billingEntryValue(entries, name) {
|
|
384
|
+
for (const entry of entries) {
|
|
385
|
+
if (firstText(entry, ['name', 'type']).toUpperCase() !== name) continue;
|
|
386
|
+
const value = firstNumber(entry, ['value', 'amount.value', 'amount', 'total']);
|
|
387
|
+
if (value !== null) return value;
|
|
388
|
+
}
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function billingEntriesBalance(entries) {
|
|
393
|
+
if (!entries.length) return null;
|
|
394
|
+
let total = 0;
|
|
395
|
+
let found = false;
|
|
396
|
+
for (const entry of entries) {
|
|
397
|
+
if (firstText(entry, ['name', 'type']).toUpperCase() === 'STORE_SUBSIDY') continue;
|
|
398
|
+
const value = firstNumber(entry, ['value', 'amount.value', 'amount', 'total']);
|
|
399
|
+
if (value !== null) {
|
|
400
|
+
total += value;
|
|
401
|
+
found = true;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return found ? roundCurrency(total) : null;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function saleGrossValue(sale) {
|
|
408
|
+
const total = firstNumber(sale, ['saleGrossValue.total', 'saleGrossValue.grossValue', 'saleGrossValue.value']);
|
|
409
|
+
if (total !== null) return total;
|
|
410
|
+
const bag = firstNumber(sale, ['saleGrossValue.bag']);
|
|
411
|
+
const deliveryFee = firstNumber(sale, ['saleGrossValue.deliveryFee']);
|
|
412
|
+
const serviceFee = firstNumber(sale, ['saleGrossValue.serviceFee']);
|
|
413
|
+
if (bag === null && deliveryFee === null && serviceFee === null) return null;
|
|
414
|
+
return roundCurrency((bag || 0) + (deliveryFee || 0) + (serviceFee || 0));
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function saleDiscountValue(sale) {
|
|
418
|
+
const direct = firstNumber(sale, ['billingSummary.discount', 'billingSummary.discounts', 'total.discount', 'discount', 'discounts']);
|
|
419
|
+
if (direct !== null) return direct;
|
|
420
|
+
let total = 0;
|
|
421
|
+
let found = false;
|
|
422
|
+
for (const benefit of arrayFrom(sale, ['benefits', 'benefits.items', 'benefits.delivery'])) {
|
|
423
|
+
const value = firstNumber(benefit, ['value', 'amount', 'discount', 'sponsorshipValues.IFood', 'sponsorshipValues.MERCHANT', 'sponsorshipValues.CHAIN']);
|
|
424
|
+
if (value !== null) {
|
|
425
|
+
total += Math.abs(value);
|
|
426
|
+
found = true;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return found ? roundCurrency(total) : null;
|
|
430
|
+
}
|
|
431
|
+
|
|
361
432
|
function normalizeFinancialEvent(event) {
|
|
362
|
-
const tipo = firstText(event, ['type', 'eventType', 'code', 'classification']);
|
|
363
|
-
const valor = firstNumber(event, ['
|
|
433
|
+
const tipo = firstText(event, ['name', 'type', 'eventType', 'code', 'classification']);
|
|
434
|
+
const valor = firstNumber(event, ['value', 'amount.value', 'amount', 'total']);
|
|
364
435
|
return {
|
|
365
436
|
tipo,
|
|
366
437
|
descricao: firstText(event, ['description', 'name', 'title', 'detail.description']),
|
|
367
438
|
valor,
|
|
368
439
|
impactaRepasse: Boolean(valueAt(event, 'hasTransferImpact')),
|
|
369
|
-
dataEvento: firstText(event, ['eventDate', 'date', 'createdAt', 'paymentDate']),
|
|
370
|
-
loteId: firstText(event, ['settlement.id', 'settlementId', 'payment.id', 'paymentId', 'closingItem.id', 'closingItemId']),
|
|
371
|
-
origem: event,
|
|
372
440
|
};
|
|
373
441
|
}
|
|
374
442
|
|
|
443
|
+
function isFinancialDifference(event) {
|
|
444
|
+
if (!event || typeof event.valor !== 'number') return false;
|
|
445
|
+
if (!event.tipo && !event.descricao) return false;
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
|
|
375
449
|
function calculateTransferNet(events, fallback) {
|
|
376
450
|
const impacting = events.filter((event) => event.impactaRepasse && typeof event.valor === 'number');
|
|
377
451
|
if (!impacting.length) return fallback;
|
|
@@ -422,14 +496,6 @@ function valueAt(object, path) {
|
|
|
422
496
|
}, object);
|
|
423
497
|
}
|
|
424
498
|
|
|
425
|
-
function objectValue(object, paths) {
|
|
426
|
-
for (const path of paths) {
|
|
427
|
-
const value = valueAt(object, path);
|
|
428
|
-
if (value && typeof value === 'object') return value;
|
|
429
|
-
}
|
|
430
|
-
return null;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
499
|
function arrayFrom(object, paths) {
|
|
434
500
|
for (const path of paths) {
|
|
435
501
|
const value = valueAt(object, path);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-ifood-completo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"description": "Nodes n8n modulares para Merchant API iFood: autenticação, lojas, eventos, pedidos, financeiro, análise de conciliação, catálogo, produtos de catálogo, logística, shipping e avaliações.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|