From 540c4109573fdf8cff30fb672430a9b98abff719 Mon Sep 17 00:00:00 2001
From: Stefan Hall <stefan-hall@hotmail.com>
Date: Sat, 13 Jan 2024 09:59:31 +1300
Subject: [PATCH]  fixed:issue->#1968 Import from nYNAB fails with unknown
 error (#2191)

* Allow case insensitive ynab5 import for special 'starting balance' payee

* set upcoming release number to related github issue

* extract string comparison into separate function

and reuse when checking starting balance/s on ynab4 import

* make all category group checks case insensitive

when importing from ynab5 to make the check strategy consistent when importing from ynab5

* extract findById into sreusable function

to 'simplify' usage

* Add null check

Co-authored-by: Joel Jeremy Marquez <joeljeremy.marquez@gmail.com>

---------

Co-authored-by: Joel Jeremy Marquez <joeljeremy.marquez@gmail.com>
---
 .../loot-core/src/server/importers/ynab5.ts   | 60 ++++++++++++-------
 upcoming-release-notes/2191.md                |  6 ++
 2 files changed, 44 insertions(+), 22 deletions(-)
 create mode 100644 upcoming-release-notes/2191.md

diff --git a/packages/loot-core/src/server/importers/ynab5.ts b/packages/loot-core/src/server/importers/ynab5.ts
index 0653a1f95..8ea7ac630 100644
--- a/packages/loot-core/src/server/importers/ynab5.ts
+++ b/packages/loot-core/src/server/importers/ynab5.ts
@@ -39,25 +39,26 @@ async function importCategories(
   // so it's already handled.
 
   const categories = await actual.getCategories();
-  const incomeCatId = categories.find(cat => cat.name === 'Income').id;
+  const incomeCatId = findIdByName(categories, 'Income');
   const ynabIncomeCategories = ['To be Budgeted', 'Inflow: Ready to Assign'];
 
   function checkSpecialCat(cat) {
     if (
       cat.category_group_id ===
-      data.category_groups.find(
-        group => group.name === 'Internal Master Category',
-      ).id
+      findIdByName(data.category_groups, 'Internal Master Category')
     ) {
-      if (ynabIncomeCategories.includes(cat.name)) {
+      if (
+        ynabIncomeCategories.some(ynabIncomeCategory =>
+          equalsIgnoreCase(cat.name, ynabIncomeCategory),
+        )
+      ) {
         return 'income';
       } else {
         return 'internal';
       }
     } else if (
       cat.category_group_id ===
-      data.category_groups.find(group => group.name === 'Credit Card Payments')
-        .id
+      findIdByName(data.category_groups, 'Credit Card Payments')
     ) {
       return 'creditCard';
     }
@@ -70,8 +71,8 @@ async function importCategories(
       let groupId;
       // Ignores internal category and credit cards
       if (
-        group.name !== 'Internal Master Category' &&
-        group.name !== 'Credit Card Payments'
+        !equalsIgnoreCase(group.name, 'Internal Master Category') &&
+        !equalsIgnoreCase(group.name, 'Credit Card Payments')
       ) {
         groupId = await actual.createCategoryGroup({
           name: group.name,
@@ -132,13 +133,10 @@ async function importTransactions(
 ) {
   const payees = await actual.getPayees();
   const categories = await actual.getCategories();
-  const incomeCatId = categories.find(cat => cat.name === 'Income').id;
-  const startingBalanceCatId = categories.find(
-    cat => cat.name === 'Starting Balances',
-  ).id; //better way to do it?
-  const startingPayeeYNAB = data.payees.find(
-    payee => payee.name === 'Starting Balance',
-  ).id;
+  const incomeCatId = findIdByName(categories, 'Income');
+  const startingBalanceCatId = findIdByName(categories, 'Starting Balances'); //better way to do it?
+
+  const startingPayeeYNAB = findIdByName(data.payees, 'Starting Balance');
 
   const transactionsGrouped = groupBy(data.transactions, 'account_id');
   const subtransactionsGrouped = groupBy(
@@ -258,12 +256,14 @@ async function importBudgets(
 
   const budgets = sortByKey(data.months, 'month');
 
-  const internalCatIdYnab = data.category_groups.find(
-    group => group.name === 'Internal Master Category',
-  ).id;
-  const creditcardCatIdYnab = data.category_groups.find(
-    group => group.name === 'Credit Card Payments',
-  ).id;
+  const internalCatIdYnab = findIdByName(
+    data.category_groups,
+    'Internal Master Category',
+  );
+  const creditcardCatIdYnab = findIdByName(
+    data.category_groups,
+    'Credit Card Payments',
+  );
 
   await actual.batchBudgetUpdates(async () => {
     for (const budget of budgets) {
@@ -327,3 +327,19 @@ export function parseFile(buffer: Buffer): YNAB5.Budget {
 export function getBudgetName(_filepath: string, data: YNAB5.Budget) {
   return data.budget_name || data.name;
 }
+
+function equalsIgnoreCase(stringa: string, stringb: string): boolean {
+  return (
+    stringa.localeCompare(stringb, undefined, {
+      sensitivity: 'base',
+    }) === 0
+  );
+}
+
+function findByNameIgnoreCase(categories: YNAB5.CategoryGroup[], name: string) {
+  return categories.find(cat => equalsIgnoreCase(cat.name, name));
+}
+
+function findIdByName(categories: YNAB5.CategoryGroup[], name: string) {
+  return findByNameIgnoreCase(categories, name)?.id;
+}
diff --git a/upcoming-release-notes/2191.md b/upcoming-release-notes/2191.md
new file mode 100644
index 000000000..810ec3727
--- /dev/null
+++ b/upcoming-release-notes/2191.md
@@ -0,0 +1,6 @@
+---
+category: Bugfix
+authors: [Marethyu1]
+---
+
+Allow case insensitive ynab5 import for special 'starting balance' payee
-- 
GitLab