@actual-app/api 6.6.0 → 6.7.0

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.
@@ -12051,6 +12051,47 @@
12051
12051
  }
12052
12052
  /***/
12053
12053
  }),
12054
+ /***/ "./node_modules/date-fns/esm/differenceInMilliseconds/index.js":
12055
+ /*!*********************************************************************!*\
12056
+ !*** ./node_modules/date-fns/esm/differenceInMilliseconds/index.js ***!
12057
+ \*********************************************************************/
12058
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
12059
+ "use strict";
12060
+ __webpack_require__.r(__webpack_exports__);
12061
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
12062
+ /* harmony export */ "default": () => ( /* binding */differenceInMilliseconds)
12063
+ /* harmony export */
12064
+ });
12065
+ /* harmony import */ var _toDate_index_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../toDate/index.js */ "./node_modules/date-fns/esm/toDate/index.js");
12066
+ /* harmony import */ var _lib_requiredArgs_index_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../_lib/requiredArgs/index.js */ "./node_modules/date-fns/esm/_lib/requiredArgs/index.js");
12067
+ /**
12068
+ * @name differenceInMilliseconds
12069
+ * @category Millisecond Helpers
12070
+ * @summary Get the number of milliseconds between the given dates.
12071
+ *
12072
+ * @description
12073
+ * Get the number of milliseconds between the given dates.
12074
+ *
12075
+ * @param {Date|Number} dateLeft - the later date
12076
+ * @param {Date|Number} dateRight - the earlier date
12077
+ * @returns {Number} the number of milliseconds
12078
+ * @throws {TypeError} 2 arguments required
12079
+ *
12080
+ * @example
12081
+ * // How many milliseconds are between
12082
+ * // 2 July 2014 12:30:20.600 and 2 July 2014 12:30:21.700?
12083
+ * const result = differenceInMilliseconds(
12084
+ * new Date(2014, 6, 2, 12, 30, 21, 700),
12085
+ * new Date(2014, 6, 2, 12, 30, 20, 600)
12086
+ * )
12087
+ * //=> 1100
12088
+ */
12089
+ function differenceInMilliseconds(dateLeft, dateRight) {
12090
+ (0, _lib_requiredArgs_index_js__WEBPACK_IMPORTED_MODULE_0__["default"])(2, arguments);
12091
+ return (0, _toDate_index_js__WEBPACK_IMPORTED_MODULE_1__["default"])(dateLeft).getTime() - (0, _toDate_index_js__WEBPACK_IMPORTED_MODULE_1__["default"])(dateRight).getTime();
12092
+ }
12093
+ /***/
12094
+ }),
12054
12095
  /***/ "./node_modules/date-fns/esm/endOfMonth/index.js":
12055
12096
  /*!*******************************************************!*\
12056
12097
  !*** ./node_modules/date-fns/esm/endOfMonth/index.js ***!
@@ -27249,6 +27290,7 @@
27249
27290
  /* harmony export */ getBudgetMonth: () => ( /* binding */getBudgetMonth),
27250
27291
  /* harmony export */ getBudgetMonths: () => ( /* binding */getBudgetMonths),
27251
27292
  /* harmony export */ getCategories: () => ( /* binding */getCategories),
27293
+ /* harmony export */ getCategoryGroups: () => ( /* binding */getCategoryGroups),
27252
27294
  /* harmony export */ getPayees: () => ( /* binding */getPayees),
27253
27295
  /* harmony export */ getTransactions: () => ( /* binding */getTransactions),
27254
27296
  /* harmony export */ importTransactions: () => ( /* binding */importTransactions),
@@ -27660,6 +27702,9 @@
27660
27702
  id: id
27661
27703
  });
27662
27704
  }
27705
+ function getCategoryGroups() {
27706
+ return send("api/category-groups-get");
27707
+ }
27663
27708
  function createCategoryGroup(group) {
27664
27709
  return send("api/category-group-create", {
27665
27710
  group: group
@@ -31564,7 +31609,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
31564
31609
  errors,
31565
31610
  transactions: data.transactions.map((trans) => {
31566
31611
  return {
31567
- amount: trans.amount,
31612
+ amount: Number(trans.amount),
31568
31613
  imported_id: trans.fitId,
31569
31614
  date: trans.date,
31570
31615
  payee_name: trans.name || (useMemoFallback ? trans.memo : null),
@@ -32559,9 +32604,10 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
32559
32604
  /* harmony export */ syncExternalAccount: () => ( /* binding */syncExternalAccount)
32560
32605
  /* harmony export */
32561
32606
  });
32562
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/max/index.js");
32563
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/format/index.js");
32564
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/parseISO/index.js");
32607
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/differenceInMilliseconds/index.js");
32608
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/parseISO/index.js");
32609
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/max/index.js");
32610
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/format/index.js");
32565
32611
  /* harmony import */ var uuid__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! uuid */ "./node_modules/uuid/dist/esm-node/v4.js");
32566
32612
  /* harmony import */ var _platform_server_asyncStorage__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../platform/server/asyncStorage */ "./packages/loot-core/src/platform/server/asyncStorage/index.electron.ts");
32567
32613
  /* harmony import */ var _shared_months__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../shared/months */ "./packages/loot-core/src/shared/months.ts");
@@ -32699,6 +32745,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
32699
32745
  const userToken = await _platform_server_asyncStorage__WEBPACK_IMPORTED_MODULE_0__.getItem('user-token');
32700
32746
  if (!userToken)
32701
32747
  return;
32748
+ console.log('Pulling transactions from GoCardless');
32702
32749
  const res = await (0, _post__WEBPACK_IMPORTED_MODULE_6__.post)((0, _server_config__WEBPACK_IMPORTED_MODULE_7__.getServer)().GOCARDLESS_SERVER + '/transactions', {
32703
32750
  userId,
32704
32751
  key: userKey,
@@ -32712,6 +32759,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
32712
32759
  throw BankSyncError(res.error_type, res.error_code);
32713
32760
  }
32714
32761
  const { transactions: { all }, balances, startingBalance } = res;
32762
+ console.log('Response:', res);
32715
32763
  return {
32716
32764
  transactions: all,
32717
32765
  accountBalance: balances,
@@ -32722,6 +32770,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
32722
32770
  const userToken = await _platform_server_asyncStorage__WEBPACK_IMPORTED_MODULE_0__.getItem('user-token');
32723
32771
  if (!userToken)
32724
32772
  return;
32773
+ console.log('Pulling transactions from SimpleFin');
32725
32774
  const res = await (0, _post__WEBPACK_IMPORTED_MODULE_6__.post)((0, _server_config__WEBPACK_IMPORTED_MODULE_7__.getServer)().SIMPLEFIN_SERVER + '/transactions', {
32726
32775
  accountId: acctId,
32727
32776
  startDate: since
@@ -32732,6 +32781,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
32732
32781
  throw BankSyncError(res.error_type, res.error_code);
32733
32782
  }
32734
32783
  const { transactions: { all }, balances, startingBalance } = res;
32784
+ console.log('Response:', res);
32735
32785
  return {
32736
32786
  transactions: all,
32737
32787
  accountBalance: balances,
@@ -32883,6 +32933,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
32883
32933
  });
32884
32934
  }
32885
32935
  async function reconcileExternalTransactions(acctId, transactions) {
32936
+ console.log('Performing transaction reconciliation');
32886
32937
  const hasMatched = new Set();
32887
32938
  const updated = [];
32888
32939
  const added = [];
@@ -32919,6 +32970,15 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
32919
32970
  trans.amount || 0,
32920
32971
  acctId
32921
32972
  ]);
32973
+ // Sort the matched transactions according to the distance from the original
32974
+ // transactions date. i.e. if the original transaction is in 21-02-2024 and
32975
+ // the matched transactions are: 20-02-2024, 21-02-2024, 29-02-2024 then
32976
+ // the resulting data-set should be: 21-02-2024, 20-02-2024, 29-02-2024.
32977
+ fuzzyDataset = fuzzyDataset.sort((a, b) => {
32978
+ const aDistance = Math.abs(date_fns__WEBPACK_IMPORTED_MODULE_14__["default"](date_fns__WEBPACK_IMPORTED_MODULE_15__["default"](trans.date), date_fns__WEBPACK_IMPORTED_MODULE_15__["default"](_db__WEBPACK_IMPORTED_MODULE_4__.fromDateRepr(a.date))));
32979
+ const bDistance = Math.abs(date_fns__WEBPACK_IMPORTED_MODULE_14__["default"](date_fns__WEBPACK_IMPORTED_MODULE_15__["default"](trans.date), date_fns__WEBPACK_IMPORTED_MODULE_15__["default"](_db__WEBPACK_IMPORTED_MODULE_4__.fromDateRepr(b.date))));
32980
+ return aDistance > bDistance ? 1 : -1;
32981
+ });
32922
32982
  }
32923
32983
  transactionsStep1.push({
32924
32984
  payee_name,
@@ -32949,7 +33009,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
32949
33009
  });
32950
33010
  // The final fuzzy matching pass. This is the lowest fidelity
32951
33011
  // matching: it just find the first transaction that hasn't been
32952
- // matched yet. Remember the the dataset only contains transactions
33012
+ // matched yet. Remember the dataset only contains transactions
32953
33013
  // around the same date with the same amount.
32954
33014
  const transactionsStep3 = transactionsStep2.map((data) => {
32955
33015
  if (!data.match && data.fuzzyDataset) {
@@ -33028,6 +33088,13 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
33028
33088
  added,
33029
33089
  updated
33030
33090
  });
33091
+ console.log('Debug data for the operations:', {
33092
+ transactionsStep1,
33093
+ transactionsStep2,
33094
+ transactionsStep3,
33095
+ added,
33096
+ updated
33097
+ });
33031
33098
  return {
33032
33099
  added: added.map((trans) => trans.id),
33033
33100
  updated: updated.map((trans) => trans.id)
@@ -33070,6 +33137,15 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
33070
33137
  trans.amount || 0,
33071
33138
  acctId
33072
33139
  ]);
33140
+ // Sort the matched transactions according to the distance from the original
33141
+ // transactions date. i.e. if the original transaction is in 21-02-2024 and
33142
+ // the matched transactions are: 20-02-2024, 21-02-2024, 29-02-2024 then
33143
+ // the resulting data-set should be: 21-02-2024, 20-02-2024, 29-02-2024.
33144
+ fuzzyDataset = fuzzyDataset.sort((a, b) => {
33145
+ const aDistance = Math.abs(date_fns__WEBPACK_IMPORTED_MODULE_14__["default"](date_fns__WEBPACK_IMPORTED_MODULE_15__["default"](trans.date), date_fns__WEBPACK_IMPORTED_MODULE_15__["default"](_db__WEBPACK_IMPORTED_MODULE_4__.fromDateRepr(a.date))));
33146
+ const bDistance = Math.abs(date_fns__WEBPACK_IMPORTED_MODULE_14__["default"](date_fns__WEBPACK_IMPORTED_MODULE_15__["default"](trans.date), date_fns__WEBPACK_IMPORTED_MODULE_15__["default"](_db__WEBPACK_IMPORTED_MODULE_4__.fromDateRepr(b.date))));
33147
+ return aDistance > bDistance ? 1 : -1;
33148
+ });
33073
33149
  }
33074
33150
  transactionsStep1.push({
33075
33151
  payee_name,
@@ -33100,7 +33176,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
33100
33176
  });
33101
33177
  // The final fuzzy matching pass. This is the lowest fidelity
33102
33178
  // matching: it just find the first transaction that hasn't been
33103
- // matched yet. Remember the the dataset only contains transactions
33179
+ // matched yet. Remember the dataset only contains transactions
33104
33180
  // around the same date with the same amount.
33105
33181
  const transactionsStep3 = transactionsStep2.map((data) => {
33106
33182
  if (!data.match && data.fuzzyDataset) {
@@ -33239,7 +33315,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
33239
33315
  id
33240
33316
  ]);
33241
33317
  const startingDate = _shared_months__WEBPACK_IMPORTED_MODULE_1__.parseDate(_db__WEBPACK_IMPORTED_MODULE_4__.fromDateRepr(startingTransaction.date));
33242
- const startDate = _shared_months__WEBPACK_IMPORTED_MODULE_1__.dayFromDate(date_fns__WEBPACK_IMPORTED_MODULE_14__["default"]([
33318
+ const startDate = _shared_months__WEBPACK_IMPORTED_MODULE_1__.dayFromDate(date_fns__WEBPACK_IMPORTED_MODULE_16__["default"]([
33243
33319
  // Many GoCardless integrations do not support getting more than 90 days
33244
33320
  // worth of data, so make that the earliest possible limit.
33245
33321
  _shared_months__WEBPACK_IMPORTED_MODULE_1__.parseDate(_shared_months__WEBPACK_IMPORTED_MODULE_1__.subDays(_shared_months__WEBPACK_IMPORTED_MODULE_1__.currentDay(), 90)),
@@ -33359,7 +33435,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
33359
33435
  // Otherwise, download transaction for the last few days if it's an
33360
33436
  // on-budget account, or for the past 30 days if off-budget
33361
33437
  const startingDay = _shared_months__WEBPACK_IMPORTED_MODULE_1__.subDays(_shared_months__WEBPACK_IMPORTED_MODULE_1__.currentDay(), acctRow.offbudget === 0 ? 1 : 30);
33362
- const { transactions } = await downloadTransactions(userId, userKey, acctId, bankId, date_fns__WEBPACK_IMPORTED_MODULE_15__["default"](date_fns__WEBPACK_IMPORTED_MODULE_16__["default"](startingDay), 'yyyy-MM-dd'));
33438
+ const { transactions } = await downloadTransactions(userId, userKey, acctId, bankId, date_fns__WEBPACK_IMPORTED_MODULE_17__["default"](date_fns__WEBPACK_IMPORTED_MODULE_15__["default"](startingDay), 'yyyy-MM-dd'));
33363
33439
  // We need to add a transaction that represents the starting
33364
33440
  // balance for everything to balance out. In order to get balance
33365
33441
  // before the first imported transaction, we need to get the
@@ -35252,6 +35328,10 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
35252
35328
  const result = await handlers['get-categories']();
35253
35329
  return grouped ? result.grouped.map(_api_models__WEBPACK_IMPORTED_MODULE_8__.categoryGroupModel.toExternal) : result.list.map(_api_models__WEBPACK_IMPORTED_MODULE_8__.categoryModel.toExternal);
35254
35330
  };
35331
+ handlers['api/category-groups-get'] = async function () {
35332
+ checkFileOpen();
35333
+ return handlers['get-category-groups']();
35334
+ };
35255
35335
  handlers['api/category-group-create'] = withMutation(async function ({ group }) {
35256
35336
  checkFileOpen();
35257
35337
  return handlers['category-group-create']({
@@ -38601,38 +38681,6 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
38601
38681
  });
38602
38682
  }
38603
38683
  }
38604
- //fund rollover categories after non-rollover categories
38605
- for (let c = 0; c < categories.length; c++) {
38606
- const category = categories[c];
38607
- const budgetAvailable = await (0, _actions__WEBPACK_IMPORTED_MODULE_2__.getSheetValue)(sheetName, `to-budget`);
38608
- const balance = await (0, _actions__WEBPACK_IMPORTED_MODULE_2__.getSheetValue)(sheetName, `leftover-${category.id}`);
38609
- const budgeted = await (0, _actions__WEBPACK_IMPORTED_MODULE_2__.getSheetValue)(sheetName, `budget-${category.id}`);
38610
- const to_budget = budgeted + Math.abs(balance);
38611
- const categoryId = category.id;
38612
- let carryover = await _db__WEBPACK_IMPORTED_MODULE_1__.first(`SELECT carryover FROM zero_budgets WHERE month = ? and category = ?`, [
38613
- db_month,
38614
- categoryId
38615
- ]);
38616
- if (carryover === null) {
38617
- carryover = {
38618
- carryover: 0
38619
- };
38620
- }
38621
- if (balance < 0 && Math.abs(balance) <= budgetAvailable && !category.is_income && carryover.carryover === 1) {
38622
- await (0, _actions__WEBPACK_IMPORTED_MODULE_2__.setBudget)({
38623
- category: category.id,
38624
- month,
38625
- amount: to_budget
38626
- });
38627
- }
38628
- else if (balance < 0 && !category.is_income && carryover.carryover === 1 && Math.abs(balance) > budgetAvailable) {
38629
- await (0, _actions__WEBPACK_IMPORTED_MODULE_2__.setBudget)({
38630
- category: category.id,
38631
- month,
38632
- amount: budgeted + budgetAvailable
38633
- });
38634
- }
38635
- }
38636
38684
  const budgetAvailable = await (0, _actions__WEBPACK_IMPORTED_MODULE_2__.getSheetValue)(sheetName, `to-budget`);
38637
38685
  if (budgetAvailable <= 0) {
38638
38686
  warnings.push('No funds are available to reallocate.');
@@ -39019,8 +39067,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
39019
39067
  //in the case of multiple templates per category, schedules may have wrong priority level
39020
39068
  const t = await createScheduleList(template, current_month, category);
39021
39069
  errors = errors.concat(t.errors);
39022
- const t_payMonthOf = t.t.filter((c) => c.full || c.target_frequency === 'monthly' && c.target_interval === 1 && c.num_months === 0 || c.target_frequency === 'weekly' && c.target_interval >= 0 && c.num_months === 0 || c.target_frequency === 'daily' || (0, _actions__WEBPACK_IMPORTED_MODULE_4__.isReflectBudget)());
39023
- const t_sinking = t.t.filter((c) => !c.full && c.target_frequency === 'monthly' && c.target_interval > 1 || !c.full && c.target_frequency === 'monthly' && c.num_months > 0 && c.target_interval === 1 || !c.full && c.target_frequency === 'yearly' || !c.full && c.target_frequency === undefined).sort((a, b) => a.next_date_string.localeCompare(b.next_date_string));
39070
+ const isPayMonthOf = (c) => c.full || c.target_frequency === 'monthly' && c.target_interval === 1 && c.num_months === 0 || c.target_frequency === 'weekly' && c.target_interval >= 0 && c.num_months === 0 || c.target_frequency === 'daily' || (0, _actions__WEBPACK_IMPORTED_MODULE_4__.isReflectBudget)();
39071
+ const t_payMonthOf = t.t.filter(isPayMonthOf);
39072
+ const t_sinking = t.t.filter((c) => !isPayMonthOf(c)).sort((a, b) => a.next_date_string.localeCompare(b.next_date_string));
39024
39073
  const totalPayMonthOf = await getPayMonthOfTotal(t_payMonthOf);
39025
39074
  const totalSinking = await getSinkingTotal(t_sinking);
39026
39075
  const totalSinkingBaseContribution = await getSinkingBaseContributionTotal(t_sinking);
@@ -39029,7 +39078,12 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
39029
39078
  }
39030
39079
  else {
39031
39080
  const totalSinkingContribution = await getSinkingContributionTotal(t_sinking, remainder, last_month_balance);
39032
- to_budget += Math.round(totalPayMonthOf + totalSinkingContribution);
39081
+ if (t_sinking.length === 0) {
39082
+ to_budget += Math.round(totalPayMonthOf + totalSinkingContribution) - last_month_balance;
39083
+ }
39084
+ else {
39085
+ to_budget += Math.round(totalPayMonthOf + totalSinkingContribution);
39086
+ }
39033
39087
  }
39034
39088
  }
39035
39089
  return {
@@ -41942,7 +41996,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
41942
41996
  category: isOffBudget(entityIdMap.get(accountId)) ? null : getCategory(transaction.categoryId),
41943
41997
  date: transaction.date,
41944
41998
  notes: transaction.memo || null,
41945
- cleared: transaction.cleared === 'Cleared',
41999
+ cleared: transaction.cleared === 'Cleared' || transaction.cleared === 'Reconciled',
42000
+ reconciled: transaction.cleared === 'Reconciled',
41946
42001
  ...transferProperties(transaction),
41947
42002
  subtransactions: transaction.subTransactions && transaction.subTransactions.map((t) => {
41948
42003
  return {
@@ -42271,6 +42326,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
42271
42326
  'cleared',
42272
42327
  'reconciled'
42273
42328
  ].includes(transaction.cleared),
42329
+ reconciled: transaction.cleared === 'reconciled',
42274
42330
  notes: transaction.memo || null,
42275
42331
  imported_id: transaction.import_id || null,
42276
42332
  transfer_id: entityIdMap.get(transaction.transfer_transaction_id) || null,
@@ -42730,6 +42786,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
42730
42786
  return result;
42731
42787
  });
42732
42788
  });
42789
+ handlers['get-category-groups'] = async function () {
42790
+ return await _db__WEBPACK_IMPORTED_MODULE_27__.getCategoriesGrouped();
42791
+ };
42733
42792
  handlers['category-group-create'] = (0, _mutators__WEBPACK_IMPORTED_MODULE_34__.mutator)(async function ({ name, isIncome }) {
42734
42793
  return (0, _undo__WEBPACK_IMPORTED_MODULE_48__.withUndo)(async () => {
42735
42794
  return _db__WEBPACK_IMPORTED_MODULE_27__.insertCategoryGroup({
@@ -43474,12 +43533,11 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
43474
43533
  'user-id',
43475
43534
  'user-key'
43476
43535
  ]);
43477
- let accounts = await _db__WEBPACK_IMPORTED_MODULE_27__.runQuery(`SELECT a.*, b.bank_id as bankId FROM accounts a
43536
+ const accounts = await _db__WEBPACK_IMPORTED_MODULE_27__.runQuery(`SELECT a.*, b.bank_id as bankId FROM accounts a
43478
43537
  LEFT JOIN banks b ON a.bank = b.id
43479
- WHERE a.tombstone = 0 AND a.closed = 0`, [], true);
43480
- if (id) {
43481
- accounts = accounts.filter((acct) => acct.id === id);
43482
- }
43538
+ WHERE a.tombstone = 0 AND a.closed = 0 AND a.id = ?`, [
43539
+ id
43540
+ ], true);
43483
43541
  const errors = [];
43484
43542
  let newTransactions = [];
43485
43543
  let matchedTransactions = [];
@@ -43488,7 +43546,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
43488
43546
  const acct = accounts[i];
43489
43547
  if (acct.bankId) {
43490
43548
  try {
43549
+ console.group('Bank Sync operation');
43491
43550
  const res = await _accounts_sync__WEBPACK_IMPORTED_MODULE_18__.syncExternalAccount(userId, userKey, acct.id, acct.account_id, acct.bankId);
43551
+ console.groupEnd();
43492
43552
  const { added, updated } = res;
43493
43553
  newTransactions = newTransactions.concat(added);
43494
43554
  matchedTransactions = matchedTransactions.concat(updated);
@@ -49918,6 +49978,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49918
49978
  /* harmony export */ deleteTransaction: () => ( /* binding */deleteTransaction),
49919
49979
  /* harmony export */ groupTransaction: () => ( /* binding */groupTransaction),
49920
49980
  /* harmony export */ isPreviewId: () => ( /* binding */isPreviewId),
49981
+ /* harmony export */ isTemporaryId: () => ( /* binding */isTemporaryId),
49921
49982
  /* harmony export */ makeChild: () => ( /* binding */makeChild),
49922
49983
  /* harmony export */ realizeTempTransactions: () => ( /* binding */realizeTempTransactions),
49923
49984
  /* harmony export */ recalculateSplit: () => ( /* binding */recalculateSplit),
@@ -49929,7 +49990,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49929
49990
  });
49930
49991
  /* harmony import */ var uuid__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! uuid */ "./node_modules/uuid/dist/esm-node/v4.js");
49931
49992
  /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ "./packages/loot-core/src/shared/util.ts");
49932
- // @ts-strict-ignore
49993
+ function isTemporaryId(id) {
49994
+ return id.indexOf('temp') !== -1;
49995
+ }
49933
49996
  function isPreviewId(id) {
49934
49997
  return id.indexOf('preview/') !== -1;
49935
49998
  }
@@ -49950,8 +50013,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49950
50013
  return {
49951
50014
  amount: 0,
49952
50015
  ...data,
49953
- payee: data.payee || parent.payee,
49954
- id: data.id ? data.id : prefix + (0, uuid__WEBPACK_IMPORTED_MODULE_1__["default"])(),
50016
+ payee: 'payee' in data ? data.payee : parent.payee,
50017
+ id: 'id' in data ? data.id : prefix + (0, uuid__WEBPACK_IMPORTED_MODULE_1__["default"])(),
49955
50018
  account: parent.account,
49956
50019
  date: parent.date,
49957
50020
  cleared: parent.cleared != null ? parent.cleared : null,
@@ -49964,7 +50027,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49964
50027
  function recalculateSplit(trans) {
49965
50028
  // Calculate the new total of split transactions and make sure
49966
50029
  // that it equals the parent amount
49967
- const total = trans.subtransactions.reduce((acc, t) => acc + num(t.amount), 0);
50030
+ const total = (trans.subtransactions || []).reduce((acc, t) => acc + num(t.amount), 0);
49968
50031
  return {
49969
50032
  ...trans,
49970
50033
  error: total === num(trans.amount) ? null : SplitTransactionError(total, trans)
@@ -50059,7 +50122,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
50059
50122
  updated: []
50060
50123
  };
50061
50124
  grouped = {
50062
- id: split[0].id,
50125
+ ...split[0],
50063
50126
  _deleted: true
50064
50127
  };
50065
50128
  transactionsCopy.splice(parentIndex, split.length);
@@ -50084,7 +50147,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
50084
50147
  return {
50085
50148
  data: transactionsCopy,
50086
50149
  newTransaction: grouped || {
50087
- id: trans.id,
50150
+ ...trans,
50088
50151
  _deleted: true
50089
50152
  },
50090
50153
  diff: (0, _util__WEBPACK_IMPORTED_MODULE_0__.diffItems)([
@@ -50144,7 +50207,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
50144
50207
  else if (trans.subtransactions?.length === 1) {
50145
50208
  return {
50146
50209
  ...trans,
50147
- subtransactions: null,
50210
+ subtransactions: undefined,
50148
50211
  is_parent: false,
50149
50212
  error: null
50150
50213
  };
@@ -50181,9 +50244,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
50181
50244
  });
50182
50245
  }
50183
50246
  function realizeTempTransactions(transactions) {
50184
- let parent = transactions.find((t) => !t.is_child);
50185
- parent = {
50186
- ...parent,
50247
+ const parent = {
50248
+ ...transactions.find((t) => !t.is_child),
50187
50249
  id: (0, uuid__WEBPACK_IMPORTED_MODULE_1__["default"])()
50188
50250
  };
50189
50251
  const children = transactions.filter((t) => t.is_child);
@@ -50535,7 +50597,14 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
50535
50597
  // currencies. We extract out the numbers and just ignore separators.
50536
50598
  function looselyParseAmount(amount) {
50537
50599
  function safeNumber(v) {
50538
- return isNaN(v) ? null : v;
50600
+ if (isNaN(v)) {
50601
+ return null;
50602
+ }
50603
+ const value = v * 100;
50604
+ if (value > MAX_SAFE_NUMBER || value < MIN_SAFE_NUMBER) {
50605
+ return null;
50606
+ }
50607
+ return v;
50539
50608
  }
50540
50609
  function extractNumbers(v) {
50541
50610
  return v.replace(/[^0-9-]/g, '');
package/dist/methods.js CHANGED
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.deletePayee = exports.updatePayee = exports.createPayee = exports.getPayees = exports.deleteCategory = exports.updateCategory = exports.createCategory = exports.getCategories = exports.deleteCategoryGroup = exports.updateCategoryGroup = exports.createCategoryGroup = exports.deleteAccount = exports.reopenAccount = exports.closeAccount = exports.updateAccount = exports.createAccount = exports.getAccounts = exports.deleteTransaction = exports.updateTransaction = exports.getTransactions = exports.importTransactions = exports.addTransactions = exports.setBudgetCarryover = exports.setBudgetAmount = exports.getBudgetMonth = exports.getBudgetMonths = exports.runQuery = exports.batchBudgetUpdates = exports.sync = exports.downloadBudget = exports.loadBudget = exports.runImport = exports.q = void 0;
26
+ exports.deletePayee = exports.updatePayee = exports.createPayee = exports.getPayees = exports.deleteCategory = exports.updateCategory = exports.createCategory = exports.getCategories = exports.deleteCategoryGroup = exports.updateCategoryGroup = exports.createCategoryGroup = exports.getCategoryGroups = exports.deleteAccount = exports.reopenAccount = exports.closeAccount = exports.updateAccount = exports.createAccount = exports.getAccounts = exports.deleteTransaction = exports.updateTransaction = exports.getTransactions = exports.importTransactions = exports.addTransactions = exports.setBudgetCarryover = exports.setBudgetAmount = exports.getBudgetMonth = exports.getBudgetMonths = exports.runQuery = exports.batchBudgetUpdates = exports.sync = exports.downloadBudget = exports.loadBudget = exports.runImport = exports.q = void 0;
27
27
  const injected = __importStar(require("./injected"));
28
28
  var query_1 = require("./app/query");
29
29
  Object.defineProperty(exports, "q", { enumerable: true, get: function () { return query_1.q; } });
@@ -137,6 +137,10 @@ function deleteAccount(id) {
137
137
  return send('api/account-delete', { id });
138
138
  }
139
139
  exports.deleteAccount = deleteAccount;
140
+ function getCategoryGroups() {
141
+ return send('api/category-groups-get');
142
+ }
143
+ exports.getCategoryGroups = getCategoryGroups;
140
144
  function createCategoryGroup(group) {
141
145
  return send('api/category-group-create', { group });
142
146
  }
@@ -61,10 +61,38 @@ describe('API CRUD operations', () => {
61
61
  // load test budget
62
62
  await api.loadBudget(budgetName);
63
63
  });
64
- // apis: createCategoryGroup, updateCategoryGroup, deleteCategoryGroup
64
+ // apis: getCategoryGroups, createCategoryGroup, updateCategoryGroup, deleteCategoryGroup
65
65
  test('CategoryGroups: successfully update category groups', async () => {
66
66
  const month = '2023-10';
67
67
  global.currentMonth = month;
68
+ // get existing category groups
69
+ const groups = await api.getCategoryGroups();
70
+ expect(groups).toEqual(expect.arrayContaining([
71
+ expect.objectContaining({
72
+ hidden: 0,
73
+ id: 'fc3825fd-b982-4b72-b768-5b30844cf832',
74
+ is_income: 0,
75
+ name: 'Usual Expenses',
76
+ sort_order: 16384,
77
+ tombstone: 0,
78
+ }),
79
+ expect.objectContaining({
80
+ hidden: 0,
81
+ id: 'a137772f-cf2f-4089-9432-822d2ddc1466',
82
+ is_income: 0,
83
+ name: 'Investments and Savings',
84
+ sort_order: 32768,
85
+ tombstone: 0,
86
+ }),
87
+ expect.objectContaining({
88
+ hidden: 0,
89
+ id: '2E1F5BDB-209B-43F9-AF2C-3CE28E380C00',
90
+ is_income: 1,
91
+ name: 'Income',
92
+ sort_order: 32768,
93
+ tombstone: 0,
94
+ }),
95
+ ]));
68
96
  // create our test category group
69
97
  const mainGroupId = await api.createCategoryGroup({
70
98
  name: 'test-group',
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actual-app/api",
3
- "version": "6.6.0",
3
+ "version": "6.7.0",
4
4
  "license": "MIT",
5
5
  "description": "An API for Actual",
6
6
  "engines": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actual-app/api",
3
- "version": "6.6.0",
3
+ "version": "6.7.0",
4
4
  "license": "MIT",
5
5
  "description": "An API for Actual",
6
6
  "engines": {