From 558197e870fe4e4959aa1a1477b3e06f3e695832 Mon Sep 17 00:00:00 2001 From: Yuri Argolo Date: Sat, 13 Jun 2026 15:54:27 -0300 Subject: [PATCH] feat: mapeamento automatico de categoria na importacao de planilhas --- .../transactions/components/import/import-page.tsx | 13 +++++++++++-- src/shared/lib/import/types.ts | 1 + src/shared/lib/import/xls-parser.ts | 11 +++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/features/transactions/components/import/import-page.tsx b/src/features/transactions/components/import/import-page.tsx index 4f33bd0..f9a3ab5 100644 --- a/src/features/transactions/components/import/import-page.tsx +++ b/src/features/transactions/components/import/import-page.tsx @@ -108,9 +108,18 @@ export function ImportPage({ setRows( stmt.transactions.map((t) => { - const mappedCategoryId = + let mappedCategoryId = categoryMappings[normalizeDescriptionKey(t.description)] ?? null; + if (t.categoryRaw) { + const matchedOption = categoryOptions.find( + (opt) => opt.label.toLowerCase() === t.categoryRaw?.toLowerCase() + ); + if (matchedOption) { + mappedCategoryId = matchedOption.value; + } + } + return { ...t, isDuplicate: t.externalId ? duplicates.has(t.externalId) : false, @@ -129,7 +138,7 @@ export function ImportPage({ setIsChecking(false); } }, - [isCategoryCompatible, payerId], + [isCategoryCompatible, payerId, categoryOptions], ); // Pré-seleciona cartão ou conta com base no tipo detectado no OFX diff --git a/src/shared/lib/import/types.ts b/src/shared/lib/import/types.ts index dca9b37..f9e8510 100644 --- a/src/shared/lib/import/types.ts +++ b/src/shared/lib/import/types.ts @@ -4,6 +4,7 @@ export type ImportedTransaction = { amount: number; // positivo = receita, negativo = despesa description: string; // MEMO ou NAME transactionType: "income" | "expense"; + categoryRaw?: string | null; }; export type ImportStatement = { diff --git a/src/shared/lib/import/xls-parser.ts b/src/shared/lib/import/xls-parser.ts index 4897fe1..48ecfc6 100644 --- a/src/shared/lib/import/xls-parser.ts +++ b/src/shared/lib/import/xls-parser.ts @@ -99,6 +99,7 @@ export async function parseXls(buffer: ArrayBuffer): Promise { const typeRaw = values[4] != null ? String(values[4]).toLowerCase().trim() : ""; const transactionType = typeRaw === "receita" ? "income" : "expense"; + const categoryRaw = values[5] != null ? String(values[5]).trim() : null; if (!date || !description || amount === null || amount <= 0) return; @@ -108,6 +109,7 @@ export async function parseXls(buffer: ArrayBuffer): Promise { amount, description, transactionType, + categoryRaw, }); }); @@ -132,16 +134,17 @@ export async function generateXlsTemplate(): Promise { const ws = workbook.addWorksheet("Lançamentos"); ws.addRows([ - ["Data", "Descrição", "Valor", "Tipo"], - ["01/03/2026", "Ingressos São Januário", 160, "despesa"], - ["01/03/2026", "Salário", 3000.0, "receita"], - ["01/03/2026", "Posto do Vasco da Gama", 89.9, "despesa"], + ["Data", "Descrição", "Valor", "Tipo", "Categoria"], + ["01/03/2026", "Ingressos São Januário", 160, "despesa", "Lazer"], + ["01/03/2026", "Salário", 3000.0, "receita", "Salário"], + ["01/03/2026", "Posto do Vasco da Gama", 89.9, "despesa", "Transporte"], ]); ws.getColumn(1).width = 14; ws.getColumn(2).width = 32; ws.getColumn(3).width = 12; ws.getColumn(4).width = 10; + ws.getColumn(5).width = 24; // Dropdown para coluna Tipo (D2:D100) for (let i = 2; i <= 100; i++) {