@actual-app/api 25.1.0 → 25.2.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.
Files changed (61) hide show
  1. package/@types/loot-core/client/constants.d.ts +0 -11
  2. package/@types/loot-core/client/state-types/index.d.ts +0 -14
  3. package/@types/loot-core/client/state-types/modals.d.ts +7 -2
  4. package/@types/loot-core/mocks/index.d.ts +9 -20
  5. package/@types/loot-core/platform/client/fetch/index.d.ts +10 -1
  6. package/@types/loot-core/platform/client/undo/index.d.ts +8 -4
  7. package/@types/loot-core/platform/server/connection/index.d.ts +1 -1
  8. package/@types/loot-core/server/accounts/sync.d.ts +1 -1
  9. package/@types/loot-core/server/accounts/transaction-rules.d.ts +0 -3
  10. package/@types/loot-core/server/accounts/transactions.d.ts +0 -3
  11. package/@types/loot-core/server/accounts/transfer.d.ts +0 -11
  12. package/@types/loot-core/server/admin/app.d.ts +9 -4
  13. package/@types/loot-core/server/admin/types/handlers.d.ts +4 -2
  14. package/@types/loot-core/server/app.d.ts +14 -4
  15. package/@types/loot-core/server/aql/compiler.d.ts +4 -6
  16. package/@types/loot-core/server/aql/schema/index.d.ts +3 -0
  17. package/@types/loot-core/server/budget/actions.d.ts +6 -0
  18. package/@types/loot-core/server/budget/app.d.ts +10 -5
  19. package/@types/loot-core/server/budget/types/handlers.d.ts +4 -0
  20. package/@types/loot-core/server/dashboard/app.d.ts +9 -4
  21. package/@types/loot-core/server/filters/app.d.ts +9 -4
  22. package/@types/loot-core/server/importers/ynab4-types.d.ts +0 -2
  23. package/@types/loot-core/server/importers/ynab5-types.d.ts +0 -2
  24. package/@types/loot-core/server/main-app.d.ts +10 -5
  25. package/@types/loot-core/server/main.d.ts +2 -2
  26. package/@types/loot-core/server/notes/app.d.ts +9 -4
  27. package/@types/loot-core/server/preferences/app.d.ts +9 -4
  28. package/@types/loot-core/server/reports/app.d.ts +11 -4
  29. package/@types/loot-core/server/rules/app.d.ts +9 -4
  30. package/@types/loot-core/server/rules/types/handlers.d.ts +4 -4
  31. package/@types/loot-core/server/schedules/app.d.ts +10 -7
  32. package/@types/loot-core/server/schedules/types/handlers.d.ts +5 -5
  33. package/@types/loot-core/server/tools/app.d.ts +9 -4
  34. package/@types/loot-core/server/tools/types/handlers.d.ts +2 -1
  35. package/@types/loot-core/shared/schedules.d.ts +5 -31
  36. package/@types/loot-core/shared/transactions.d.ts +101 -17
  37. package/@types/loot-core/shared/util.d.ts +23 -9
  38. package/@types/loot-core/types/api-handlers.d.ts +3 -0
  39. package/@types/loot-core/types/models/dashboard.d.ts +1 -1
  40. package/@types/loot-core/types/models/payee.d.ts +1 -0
  41. package/@types/loot-core/types/models/reports.d.ts +4 -0
  42. package/@types/loot-core/types/models/rule.d.ts +6 -4
  43. package/@types/loot-core/types/models/schedule.d.ts +5 -18
  44. package/@types/loot-core/types/models/simplefin.d.ts +2 -0
  45. package/@types/loot-core/types/models/transaction.d.ts +6 -0
  46. package/@types/loot-core/types/prefs.d.ts +3 -2
  47. package/@types/loot-core/types/server-events.d.ts +80 -17
  48. package/@types/loot-core/types/server-handlers.d.ts +19 -7
  49. package/@types/loot-core/types/util.d.ts +5 -0
  50. package/@types/methods.d.ts +4 -4
  51. package/dist/app/bundle.api.js +334 -187
  52. package/dist/methods.js +4 -1
  53. package/dist/migrations/1736640000000__custom_report_sorting.sql +7 -0
  54. package/dist/migrations/1737158400000_add_learn_categories_to_payees.sql +5 -0
  55. package/dist/migrations/1738491452000__sorting_rename.sql +13 -0
  56. package/dist/package.json +1 -1
  57. package/package.json +1 -1
  58. package/@types/loot-core/client/actions/types.d.ts +0 -6
  59. package/@types/loot-core/client/state-types/account.d.ts +0 -27
  60. package/@types/loot-core/client/state-types/app.d.ts +0 -42
  61. package/@types/loot-core/client/state-types/queries.d.ts +0 -76
@@ -31418,9 +31418,13 @@
31418
31418
  });
31419
31419
  }
31420
31420
  function importTransactions(accountId, transactions) {
31421
+ var opts = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {
31422
+ defaultCleared: true
31423
+ };
31421
31424
  return send('api/transactions-import', {
31422
31425
  accountId: accountId,
31423
- transactions: transactions
31426
+ transactions: transactions,
31427
+ opts: opts
31424
31428
  });
31425
31429
  }
31426
31430
  function getTransactions(accountId, startDate, endDate) {
@@ -33870,7 +33874,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
33870
33874
  });
33871
33875
  /* harmony import */ var _db__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../db */ "./packages/loot-core/src/server/db/index.ts");
33872
33876
  // @ts-strict-ignore
33873
- /* eslint-disable import/no-unused-modules */ async function createPayee(description) {
33877
+ async function createPayee(description) {
33874
33878
  // Check to make sure no payee already exists with exactly the same
33875
33879
  // name
33876
33880
  const row = await _db__WEBPACK_IMPORTED_MODULE_0__.first(`SELECT id FROM payees WHERE UNICODE_LOWER(name) = ? AND tombstone = 0`, [
@@ -34014,10 +34018,16 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
34014
34018
  /* harmony export */ rankRules: () => ( /* binding */rankRules)
34015
34019
  /* harmony export */
34016
34020
  });
34017
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/isValid/index.js");
34018
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/parseISO/index.js");
34019
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/subDays/index.js");
34020
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/addDays/index.js");
34021
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/isValid/index.js");
34022
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/parseISO/index.js");
34023
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/subDays/index.js");
34024
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/addDays/index.js");
34025
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/addMonths/index.js");
34026
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/subMonths/index.js");
34027
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/addWeeks/index.js");
34028
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/subWeeks/index.js");
34029
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/addYears/index.js");
34030
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/subYears/index.js");
34021
34031
  /* harmony import */ var handlebars__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! handlebars */ "./node_modules/handlebars/lib/index.js");
34022
34032
  /* harmony import */ var handlebars__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/ __webpack_require__.n(handlebars__WEBPACK_IMPORTED_MODULE_0__);
34023
34033
  /* harmony import */ var _shared_months__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../shared/months */ "./packages/loot-core/src/shared/months.ts");
@@ -34037,8 +34047,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
34037
34047
  return b.map(Number).reduce(fn, Number(a));
34038
34048
  };
34039
34049
  }
34040
- const helpers = {
34041
- regex: (value, regex, replace) => {
34050
+ function regexHelper(mapRegex, mapNonRegex, apply) {
34051
+ return (value, regex, replace) => {
34042
34052
  if (value == null) {
34043
34053
  return null;
34044
34054
  }
@@ -34049,13 +34059,18 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
34049
34059
  const match = regexTest.exec(regex);
34050
34060
  // Regex is in format /regex/flags
34051
34061
  if (match) {
34052
- regexp = new RegExp(match[1], match[2]);
34062
+ regexp = mapRegex(match[1], match[2]);
34053
34063
  }
34054
34064
  else {
34055
- regexp = new RegExp(regex);
34065
+ regexp = mapNonRegex(regex);
34056
34066
  }
34057
- return String(value).replace(regexp, replace);
34058
- },
34067
+ return apply(String(value), regexp, replace);
34068
+ };
34069
+ }
34070
+ const helpers = {
34071
+ regex: regexHelper((regex, flags) => new RegExp(regex, flags), (value) => new RegExp(value), (value, regex, replace) => value.replace(regex, replace)),
34072
+ replace: regexHelper((regex, flags) => new RegExp(regex, flags), (value) => value, (value, regex, replace) => value.replace(regex, replace)),
34073
+ replaceAll: regexHelper((regex, flags) => new RegExp(regex, flags), (value) => value, (value, regex, replace) => value.replaceAll(regex, replace)),
34059
34074
  add: mathHelper((a, b) => a + b),
34060
34075
  sub: mathHelper((a, b) => a - b),
34061
34076
  div: mathHelper((a, b) => a / b),
@@ -34072,9 +34087,56 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
34072
34087
  month: (date) => date && (0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.format)(date, 'M'),
34073
34088
  year: (date) => date && (0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.format)(date, 'yyyy'),
34074
34089
  format: (date, f) => date && f && (0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.format)(date, f),
34090
+ addDays: (date, days) => {
34091
+ if (!days || !days)
34092
+ return date;
34093
+ return (0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.format)((0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.addDays)(date, days), 'yyyy-MM-dd');
34094
+ },
34095
+ subDays: (date, days) => {
34096
+ if (!days || !days)
34097
+ return date;
34098
+ return (0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.format)((0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.subDays)(date, days), 'yyyy-MM-dd');
34099
+ },
34100
+ addMonths: (date, months) => {
34101
+ if (!months || !months)
34102
+ return date;
34103
+ return (0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.format)((0, date_fns__WEBPACK_IMPORTED_MODULE_8__["default"])((0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.parseDate)(date), months), 'yyyy-MM-dd');
34104
+ },
34105
+ subMonths: (date, months) => {
34106
+ if (!months || !months)
34107
+ return date;
34108
+ return (0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.format)((0, date_fns__WEBPACK_IMPORTED_MODULE_9__["default"])((0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.parseDate)(date), months), 'yyyy-MM-dd');
34109
+ },
34110
+ addWeeks: (date, weeks) => {
34111
+ if (!weeks || !weeks)
34112
+ return date;
34113
+ return (0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.format)((0, date_fns__WEBPACK_IMPORTED_MODULE_10__["default"])((0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.parseDate)(date), weeks), 'yyyy-MM-dd');
34114
+ },
34115
+ subWeeks: (date, weeks) => {
34116
+ if (!weeks || !weeks)
34117
+ return date;
34118
+ return (0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.format)((0, date_fns__WEBPACK_IMPORTED_MODULE_11__["default"])((0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.parseDate)(date), weeks), 'yyyy-MM-dd');
34119
+ },
34120
+ addYears: (date, years) => {
34121
+ if (!years || !years)
34122
+ return date;
34123
+ return (0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.format)((0, date_fns__WEBPACK_IMPORTED_MODULE_12__["default"])((0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.parseDate)(date), years), 'yyyy-MM-dd');
34124
+ },
34125
+ subYears: (date, years) => {
34126
+ if (!years || !years)
34127
+ return date;
34128
+ return (0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.format)((0, date_fns__WEBPACK_IMPORTED_MODULE_13__["default"])((0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.parseDate)(date), years), 'yyyy-MM-dd');
34129
+ },
34130
+ setDay: (date, day) => {
34131
+ if (!date)
34132
+ return date;
34133
+ const actualDay = Number((0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.format)(date, 'd'));
34134
+ return (0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.format)((0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.addDays)(date, day - actualDay), 'yyyy-MM-dd');
34135
+ },
34075
34136
  debug: (value) => {
34076
34137
  console.log(value);
34077
- }
34138
+ },
34139
+ concat: (...args) => args.slice(0, -1).join('')
34078
34140
  };
34079
34141
  for (const [name, fn] of Object.entries(helpers)) {
34080
34142
  handlebars__WEBPACK_IMPORTED_MODULE_0__.registerHelper(name, fn);
@@ -34110,7 +34172,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
34110
34172
  }
34111
34173
  else if (str.length === 10) {
34112
34174
  // YYYY-MM-DD
34113
- if (!date_fns__WEBPACK_IMPORTED_MODULE_8__["default"](date_fns__WEBPACK_IMPORTED_MODULE_9__["default"](str))) {
34175
+ if (!date_fns__WEBPACK_IMPORTED_MODULE_14__["default"](date_fns__WEBPACK_IMPORTED_MODULE_15__["default"](str))) {
34114
34176
  return null;
34115
34177
  }
34116
34178
  return {
@@ -34120,7 +34182,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
34120
34182
  }
34121
34183
  else if (str.length === 7) {
34122
34184
  // YYYY-MM
34123
- if (!date_fns__WEBPACK_IMPORTED_MODULE_8__["default"](date_fns__WEBPACK_IMPORTED_MODULE_9__["default"](str + '-01'))) {
34185
+ if (!date_fns__WEBPACK_IMPORTED_MODULE_14__["default"](date_fns__WEBPACK_IMPORTED_MODULE_15__["default"](str + '-01'))) {
34124
34186
  return null;
34125
34187
  }
34126
34188
  return {
@@ -34130,7 +34192,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
34130
34192
  }
34131
34193
  else if (str.length === 4) {
34132
34194
  // YYYY
34133
- if (!date_fns__WEBPACK_IMPORTED_MODULE_8__["default"](date_fns__WEBPACK_IMPORTED_MODULE_9__["default"](str + '-01-01'))) {
34195
+ if (!date_fns__WEBPACK_IMPORTED_MODULE_14__["default"](date_fns__WEBPACK_IMPORTED_MODULE_15__["default"](str + '-01-01'))) {
34134
34196
  return null;
34135
34197
  }
34136
34198
  return {
@@ -34284,13 +34346,16 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
34284
34346
  }
34285
34347
  eval(object) {
34286
34348
  let fieldValue = object[this.field];
34349
+ const type = this.type;
34350
+ if (type === 'string') {
34351
+ fieldValue ??= '';
34352
+ }
34287
34353
  if (fieldValue === undefined) {
34288
34354
  return false;
34289
34355
  }
34290
34356
  if (typeof fieldValue === 'string') {
34291
34357
  fieldValue = fieldValue.toLowerCase();
34292
34358
  }
34293
- const type = this.type;
34294
34359
  if (type === 'number' && this.options) {
34295
34360
  if (this.options.outflow) {
34296
34361
  if (fieldValue > 0) {
@@ -34316,7 +34381,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
34316
34381
  const { schedule } = this.value;
34317
34382
  if (this.op === 'isapprox') {
34318
34383
  const fieldDate = (0, _shared_months__WEBPACK_IMPORTED_MODULE_1__.parseDate)(fieldValue);
34319
- return schedule.occursBetween(date_fns__WEBPACK_IMPORTED_MODULE_10__["default"](fieldDate, 2), date_fns__WEBPACK_IMPORTED_MODULE_11__["default"](fieldDate, 2));
34384
+ return schedule.occursBetween(date_fns__WEBPACK_IMPORTED_MODULE_16__["default"](fieldDate, 2), date_fns__WEBPACK_IMPORTED_MODULE_17__["default"](fieldDate, 2));
34320
34385
  }
34321
34386
  else {
34322
34387
  return schedule.occursOn({
@@ -34612,7 +34677,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
34612
34677
  lastNonFixedTransactionIndex = Math.max(lastNonFixedTransactionIndex, splitTransactionIndex);
34613
34678
  });
34614
34679
  // The last remainder split will be adjusted for any leftovers from rounding.
34615
- newTransactions[lastNonFixedTransactionIndex].amount -= getSplitRemainder(newTransactions);
34680
+ newTransactions[lastNonFixedTransactionIndex].amount += getSplitRemainder(newTransactions);
34616
34681
  }
34617
34682
  // The split index 0 (transaction index 1) is reserved for "Apply to all" actions.
34618
34683
  // Remove that entry from the transaction list.
@@ -35252,7 +35317,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
35252
35317
  }
35253
35318
  });
35254
35319
  }
35255
- async function reconcileTransactions(acctId, transactions, isBankSyncAccount = false, strictIdChecking = true, isPreview = false) {
35320
+ async function reconcileTransactions(acctId, transactions, isBankSyncAccount = false, strictIdChecking = true, isPreview = false, defaultCleared = true) {
35256
35321
  console.log('Performing transaction reconciliation');
35257
35322
  const updated = [];
35258
35323
  const added = [];
@@ -35283,7 +35348,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
35283
35348
  category: existing.category || trans.category || null,
35284
35349
  imported_payee: trans.imported_payee || null,
35285
35350
  notes: existing.notes || trans.notes || null,
35286
- cleared: trans.cleared != null ? trans.cleared : true
35351
+ cleared: trans.cleared ?? existing.cleared
35287
35352
  };
35288
35353
  if ((0, _shared_util__WEBPACK_IMPORTED_MODULE_4__.hasFieldsChanged)(existing, updates, Object.keys(updates))) {
35289
35354
  updated.push({
@@ -35326,7 +35391,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
35326
35391
  ...newTrans,
35327
35392
  id: (0, uuid__WEBPACK_IMPORTED_MODULE_16__["default"])(),
35328
35393
  category: trans.category || null,
35329
- cleared: trans.cleared != null ? trans.cleared : true
35394
+ cleared: trans.cleared ?? defaultCleared
35330
35395
  };
35331
35396
  if (subtransactions && subtransactions.length > 0) {
35332
35397
  added.push(...makeSplitTransaction(finalTransaction, subtransactions));
@@ -36633,7 +36698,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
36633
36698
  // (this might change when we think about scheduled transactions)
36634
36699
  const register = await _db__WEBPACK_IMPORTED_MODULE_5__.all(`SELECT t.* FROM v_transactions t
36635
36700
  LEFT JOIN accounts a ON a.id = t.account
36636
- WHERE date >= ? AND date <= ? AND is_parent = 0 AND a.closed = 0
36701
+ LEFT JOIN payees p ON p.id = t.payee
36702
+ WHERE date >= ? AND date <= ? AND is_parent = 0 AND a.closed = 0 AND p.learn_categories = 1
36637
36703
  ORDER BY date DESC`, [
36638
36704
  (0, _models__WEBPACK_IMPORTED_MODULE_8__.toDateRepr)(oldestDate),
36639
36705
  (0, _models__WEBPACK_IMPORTED_MODULE_8__.toDateRepr)((0, _shared_months__WEBPACK_IMPORTED_MODULE_0__.addDays)((0, _shared_months__WEBPACK_IMPORTED_MODULE_0__.currentDay)(), 180))
@@ -36961,32 +37027,6 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
36961
37027
  const { id: fromPayee } = await _db__WEBPACK_IMPORTED_MODULE_0__.first('SELECT id FROM payees WHERE transfer_acct = ?', [
36962
37028
  transaction.account
36963
37029
  ]);
36964
- // We need to enforce certain constraints with child transaction transfers
36965
- if (transaction.parent_id) {
36966
- const row = await _db__WEBPACK_IMPORTED_MODULE_0__.first(`
36967
- SELECT p.id, p.transfer_acct FROM v_transactions t
36968
- LEFT JOIN payees p ON p.id = t.payee
36969
- WHERE t.id = ?
36970
- `, [
36971
- transaction.parent_id
36972
- ]);
36973
- if (row.transfer_acct) {
36974
- if (row.id !== transaction.payee) {
36975
- // This child transaction is trying to use a transfer payee,
36976
- // but the parent is already using a different transfer payee.
36977
- // This is not allowed, so not only do we do nothing, we clear
36978
- // the payee of the child transaction to make it clear
36979
- await _db__WEBPACK_IMPORTED_MODULE_0__.updateTransaction({
36980
- id: transaction.id,
36981
- payee: null
36982
- });
36983
- return {
36984
- id: transaction.id,
36985
- payee: null
36986
- };
36987
- }
36988
- }
36989
- }
36990
37030
  const id = await _db__WEBPACK_IMPORTED_MODULE_0__.insertTransaction({
36991
37031
  account: transferredAccount,
36992
37032
  amount: -transaction.amount,
@@ -38189,10 +38229,6 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
38189
38229
  /* harmony import */ var mitt__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! mitt */ "./node_modules/mitt/dist/mitt.mjs");
38190
38230
  /* harmony import */ var _platform_exceptions__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../platform/exceptions */ "./packages/loot-core/src/platform/exceptions/index.electron.ts");
38191
38231
  // @ts-strict-ignore
38192
- // This is a simple helper abstraction for defining methods exposed to
38193
- // the client. It doesn't do much, but checks for naming conflicts and
38194
- // makes it cleaner to combine methods. We call a group of related
38195
- // methods an "app".
38196
38232
  class App {
38197
38233
  constructor() {
38198
38234
  this.handlers = {};
@@ -39050,7 +39086,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
39050
39086
  return compileAnd(state, cond);
39051
39087
  }
39052
39088
  else if (field === '$or') {
39053
- if (!cond) {
39089
+ if (!cond || Array.isArray(cond) && cond.length === 0) {
39054
39090
  return null;
39055
39091
  }
39056
39092
  return compileOr(state, cond);
@@ -40060,6 +40096,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
40060
40096
  group_by: f('string', {
40061
40097
  default: 'Category'
40062
40098
  }),
40099
+ sort_by: f('string', {
40100
+ default: 'desc'
40101
+ }),
40063
40102
  balance_type: f('string', {
40064
40103
  default: 'Expense'
40065
40104
  }),
@@ -40102,7 +40141,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
40102
40141
  zero_budgets: {
40103
40142
  id: f('id'),
40104
40143
  month: f('integer'),
40105
- category: f('string'),
40144
+ category: f('string', {
40145
+ ref: 'categories'
40146
+ }),
40106
40147
  amount: f('integer'),
40107
40148
  carryover: f('integer'),
40108
40149
  goal: f('integer'),
@@ -40644,7 +40685,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
40644
40685
  /* harmony export */ holdForNextMonth: () => ( /* binding */holdForNextMonth),
40645
40686
  /* harmony export */ isReflectBudget: () => ( /* binding */isReflectBudget),
40646
40687
  /* harmony export */ resetHold: () => ( /* binding */resetHold),
40688
+ /* harmony export */ set12MonthAvg: () => ( /* binding */set12MonthAvg),
40647
40689
  /* harmony export */ set3MonthAvg: () => ( /* binding */set3MonthAvg),
40690
+ /* harmony export */ set6MonthAvg: () => ( /* binding */set6MonthAvg),
40648
40691
  /* harmony export */ setBudget: () => ( /* binding */setBudget),
40649
40692
  /* harmony export */ setBuffer: () => ( /* binding */setBuffer),
40650
40693
  /* harmony export */ setCategoryCarryover: () => ( /* binding */setCategoryCarryover),
@@ -40861,6 +40904,36 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
40861
40904
  }
40862
40905
  });
40863
40906
  }
40907
+ async function set12MonthAvg({ month }) {
40908
+ const categories = await _db__WEBPACK_IMPORTED_MODULE_2__.all('SELECT * FROM v_categories WHERE tombstone = 0');
40909
+ await (0, _sync__WEBPACK_IMPORTED_MODULE_4__.batchMessages)(async () => {
40910
+ for (const cat of categories) {
40911
+ if (cat.is_income === 1 && !isReflectBudget()) {
40912
+ continue;
40913
+ }
40914
+ setNMonthAvg({
40915
+ month,
40916
+ N: 12,
40917
+ category: cat.id
40918
+ });
40919
+ }
40920
+ });
40921
+ }
40922
+ async function set6MonthAvg({ month }) {
40923
+ const categories = await _db__WEBPACK_IMPORTED_MODULE_2__.all('SELECT * FROM v_categories WHERE tombstone = 0');
40924
+ await (0, _sync__WEBPACK_IMPORTED_MODULE_4__.batchMessages)(async () => {
40925
+ for (const cat of categories) {
40926
+ if (cat.is_income === 1 && !isReflectBudget()) {
40927
+ continue;
40928
+ }
40929
+ setNMonthAvg({
40930
+ month,
40931
+ N: 6,
40932
+ category: cat.id
40933
+ });
40934
+ }
40935
+ });
40936
+ }
40864
40937
  async function setNMonthAvg({ month, N, category }) {
40865
40938
  const categoryFromDb = await _db__WEBPACK_IMPORTED_MODULE_2__.first('SELECT is_income FROM v_categories WHERE id = ?', [
40866
40939
  category
@@ -41043,6 +41116,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
41043
41116
  app.method('budget/copy-single-month', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_actions__WEBPACK_IMPORTED_MODULE_3__.copySinglePreviousMonth)));
41044
41117
  app.method('budget/set-zero', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_actions__WEBPACK_IMPORTED_MODULE_3__.setZero)));
41045
41118
  app.method('budget/set-3month-avg', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_actions__WEBPACK_IMPORTED_MODULE_3__.set3MonthAvg)));
41119
+ app.method('budget/set-6month-avg', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_actions__WEBPACK_IMPORTED_MODULE_3__.set6MonthAvg)));
41120
+ app.method('budget/set-12month-avg', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_actions__WEBPACK_IMPORTED_MODULE_3__.set12MonthAvg)));
41046
41121
  app.method('budget/set-n-month-avg', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_actions__WEBPACK_IMPORTED_MODULE_3__.setNMonthAvg)));
41047
41122
  app.method('budget/check-templates', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_goaltemplates__WEBPACK_IMPORTED_MODULE_5__.runCheckTemplates)));
41048
41123
  app.method('budget/apply-goal-template', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_goaltemplates__WEBPACK_IMPORTED_MODULE_5__.applyTemplate)));
@@ -41542,57 +41617,61 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
41542
41617
  let scheduleFlag = false;
41543
41618
  // switch on template type and calculate the amount for the line
41544
41619
  for (let i = 0; i < t.length; i++) {
41620
+ let newBudget = 0;
41545
41621
  switch (t[i].type) {
41546
41622
  case 'simple':
41547
41623
  {
41548
- toBudget += this.runSimple(t[i], this.limitAmount);
41624
+ newBudget = this.runSimple(t[i], this.limitAmount);
41549
41625
  break;
41550
41626
  }
41551
41627
  case 'copy':
41552
41628
  {
41553
- toBudget += await this.runCopy(t[i]);
41629
+ newBudget = await this.runCopy(t[i]);
41554
41630
  break;
41555
41631
  }
41556
41632
  case 'week':
41557
41633
  {
41558
- toBudget += this.runWeek(t[i]);
41634
+ newBudget = this.runWeek(t[i]);
41559
41635
  break;
41560
41636
  }
41561
41637
  case 'spend':
41562
41638
  {
41563
- toBudget += await this.runSpend(t[i]);
41639
+ newBudget = await this.runSpend(t[i]);
41564
41640
  break;
41565
41641
  }
41566
41642
  case 'percentage':
41567
41643
  {
41568
- toBudget += await this.runPercentage(t[i], availStart);
41644
+ newBudget = await this.runPercentage(t[i], availStart);
41569
41645
  break;
41570
41646
  }
41571
41647
  case 'by':
41572
41648
  {
41573
41649
  //TODO add the logic to run all of these at once or whatever is needed
41574
41650
  const ret = this.runBy(t[i], first, remainder);
41575
- toBudget += ret.ret;
41651
+ newBudget = ret.ret;
41576
41652
  remainder = ret.remainder;
41577
41653
  first = false;
41578
41654
  break;
41579
41655
  }
41580
41656
  case 'schedule':
41581
41657
  {
41582
- const budgeted = await (0, _actions__WEBPACK_IMPORTED_MODULE_3__.getSheetValue)(_shared_months__WEBPACK_IMPORTED_MODULE_0__.sheetForMonth(this.month), `leftover-${this.category.id}`);
41658
+ const budgeted = this.fromLastMonth + toBudget;
41583
41659
  const ret = await (0, _goalsSchedule__WEBPACK_IMPORTED_MODULE_4__.goalsSchedule)(scheduleFlag, t, this.month, budgeted, remainder, this.fromLastMonth, toBudget, [], this.category);
41584
- toBudget = ret.to_budget;
41660
+ // Schedules assume that its to budget value is the whole thing so this
41661
+ // needs to remove the previous funds so they aren't double counted
41662
+ newBudget = ret.to_budget - toBudget;
41585
41663
  remainder = ret.remainder;
41586
41664
  scheduleFlag = ret.scheduleFlag;
41587
41665
  break;
41588
41666
  }
41589
41667
  case 'average':
41590
41668
  {
41591
- toBudget += await this.runAverage(t[i]);
41669
+ newBudget = await this.runAverage(t[i]);
41592
41670
  break;
41593
41671
  }
41594
41672
  }
41595
- available = available - toBudget;
41673
+ available = available - newBudget;
41674
+ toBudget += newBudget;
41596
41675
  }
41597
41676
  //check limit
41598
41677
  if (this.limitCheck) {
@@ -42464,7 +42543,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
42464
42543
  // Unless the current category is relevant to the schedule, target the post-rule amount.
42465
42544
  const sign = category.is_income ? 1 : -1;
42466
42545
  const target = sign * (categorySubtransactions?.length ? categorySubtransactions.reduce((acc, t) => acc + t.amount, 0) : postRuleAmount ?? scheduleAmount);
42467
- const next_date_string = (0, _schedules_app__WEBPACK_IMPORTED_MODULE_3__.getNextDate)(dateConditions, _shared_months__WEBPACK_IMPORTED_MODULE_0__._parse(current_month));
42546
+ const next_date_string = (0, _shared_schedules__WEBPACK_IMPORTED_MODULE_1__.getNextDate)(dateConditions, _shared_months__WEBPACK_IMPORTED_MODULE_0__._parse(current_month));
42468
42547
  const target_interval = dateConditions.value.interval ? dateConditions.value.interval : 1;
42469
42548
  const target_frequency = dateConditions.value.frequency;
42470
42549
  const isRepeating = Object(dateConditions.value) === dateConditions.value && 'frequency' in dateConditions.value;
@@ -42490,14 +42569,14 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
42490
42569
  if (isRepeating) {
42491
42570
  let monthlyTarget = 0;
42492
42571
  const nextMonth = _shared_months__WEBPACK_IMPORTED_MODULE_0__.addMonths(current_month, t[t.length - 1].num_months + 1);
42493
- let nextBaseDate = (0, _schedules_app__WEBPACK_IMPORTED_MODULE_3__.getNextDate)(dateConditions, _shared_months__WEBPACK_IMPORTED_MODULE_0__._parse(current_month), true);
42494
- let nextDate = dateConditions.value.skipWeekend ? _shared_months__WEBPACK_IMPORTED_MODULE_0__.dayFromDate((0, _schedules_app__WEBPACK_IMPORTED_MODULE_3__.getDateWithSkippedWeekend)(_shared_months__WEBPACK_IMPORTED_MODULE_0__._parse(nextBaseDate), dateConditions.value.weekendSolveMode)) : nextBaseDate;
42572
+ let nextBaseDate = (0, _shared_schedules__WEBPACK_IMPORTED_MODULE_1__.getNextDate)(dateConditions, _shared_months__WEBPACK_IMPORTED_MODULE_0__._parse(current_month), true);
42573
+ let nextDate = dateConditions.value.skipWeekend ? _shared_months__WEBPACK_IMPORTED_MODULE_0__.dayFromDate((0, _shared_schedules__WEBPACK_IMPORTED_MODULE_1__.getDateWithSkippedWeekend)(_shared_months__WEBPACK_IMPORTED_MODULE_0__._parse(nextBaseDate), dateConditions.value.weekendSolveMode)) : nextBaseDate;
42495
42574
  while (nextDate < nextMonth) {
42496
42575
  monthlyTarget += -target;
42497
42576
  const currentDate = nextBaseDate;
42498
42577
  const oneDayLater = _shared_months__WEBPACK_IMPORTED_MODULE_0__.addDays(nextBaseDate, 1);
42499
- nextBaseDate = (0, _schedules_app__WEBPACK_IMPORTED_MODULE_3__.getNextDate)(dateConditions, _shared_months__WEBPACK_IMPORTED_MODULE_0__._parse(oneDayLater), true);
42500
- nextDate = dateConditions.value.skipWeekend ? _shared_months__WEBPACK_IMPORTED_MODULE_0__.dayFromDate((0, _schedules_app__WEBPACK_IMPORTED_MODULE_3__.getDateWithSkippedWeekend)(_shared_months__WEBPACK_IMPORTED_MODULE_0__._parse(nextBaseDate), dateConditions.value.weekendSolveMode)) : nextBaseDate;
42578
+ nextBaseDate = (0, _shared_schedules__WEBPACK_IMPORTED_MODULE_1__.getNextDate)(dateConditions, _shared_months__WEBPACK_IMPORTED_MODULE_0__._parse(oneDayLater), true);
42579
+ nextDate = dateConditions.value.skipWeekend ? _shared_months__WEBPACK_IMPORTED_MODULE_0__.dayFromDate((0, _shared_schedules__WEBPACK_IMPORTED_MODULE_1__.getDateWithSkippedWeekend)(_shared_months__WEBPACK_IMPORTED_MODULE_0__._parse(nextBaseDate), dateConditions.value.weekendSolveMode)) : nextBaseDate;
42501
42580
  const diffDays = _shared_months__WEBPACK_IMPORTED_MODULE_0__.differenceInCalendarDays(nextBaseDate, currentDate);
42502
42581
  if (!diffDays) {
42503
42582
  break;
@@ -43026,13 +43105,13 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
43026
43105
  const schedules = await (0, _statements__WEBPACK_IMPORTED_MODULE_2__.getActiveSchedules)();
43027
43106
  const scheduleNames = schedules.map(({ name }) => name);
43028
43107
  const errors = [];
43029
- categoryWithTemplates.forEach(({ id, name, templates }) => {
43108
+ categoryWithTemplates.forEach(({ name, templates }) => {
43030
43109
  templates.forEach((template) => {
43031
43110
  if (template.type === 'error') {
43032
43111
  errors.push(`${name}: ${template.line}`);
43033
43112
  }
43034
43113
  else if (template.type === 'schedule' && !scheduleNames.includes(template.name)) {
43035
- errors.push(`${id}: Schedule “${template.name}” does not exist`);
43114
+ errors.push(`${name}: Schedule “${template.name}” does not exist`);
43036
43115
  }
43037
43116
  });
43038
43117
  });
@@ -43057,7 +43136,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
43057
43136
  }
43058
43137
  const parsedTemplates = [];
43059
43138
  note.split('\n').forEach((line) => {
43060
- const trimmedLine = line.trim();
43139
+ const trimmedLine = line.substring(line.indexOf('#')).trim();
43061
43140
  if (!trimmedLine.startsWith(TEMPLATE_PREFIX) && !trimmedLine.startsWith(GOAL_PREFIX)) {
43062
43141
  return;
43063
43142
  }
@@ -45862,8 +45941,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
45862
45941
  /* harmony import */ var _app__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./app */ "./packages/loot-core/src/server/app.ts");
45863
45942
  // Main app
45864
45943
  const app = (0, _app__WEBPACK_IMPORTED_MODULE_1__.createApp)();
45865
- app.events.on('sync', (info) => {
45866
- _platform_server_connection__WEBPACK_IMPORTED_MODULE_0__.send('sync-event', info);
45944
+ app.events.on('sync', (event) => {
45945
+ _platform_server_connection__WEBPACK_IMPORTED_MODULE_0__.send('sync-event', event);
45867
45946
  });
45868
45947
  /***/
45869
45948
  }),
@@ -46916,13 +46995,13 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
46916
46995
  console.groupEnd();
46917
46996
  return retVal;
46918
46997
  };
46919
- handlers['transactions-import'] = (0, _mutators__WEBPACK_IMPORTED_MODULE_36__.mutator)(function ({ accountId, transactions, isPreview }) {
46998
+ handlers['transactions-import'] = (0, _mutators__WEBPACK_IMPORTED_MODULE_36__.mutator)(function ({ accountId, transactions, isPreview, opts }) {
46920
46999
  return (0, _undo__WEBPACK_IMPORTED_MODULE_51__.withUndo)(async () => {
46921
47000
  if (typeof accountId !== 'string') {
46922
47001
  throw (0, _errors__WEBPACK_IMPORTED_MODULE_32__.APIError)('transactions-import: accountId must be an id');
46923
47002
  }
46924
47003
  try {
46925
- return await _accounts_sync__WEBPACK_IMPORTED_MODULE_18__.reconcileTransactions(accountId, transactions, false, true, isPreview);
47004
+ return await _accounts_sync__WEBPACK_IMPORTED_MODULE_18__.reconcileTransactions(accountId, transactions, false, true, isPreview, opts?.defaultCleared);
46926
47005
  }
46927
47006
  catch (err) {
46928
47007
  if (err instanceof _errors__WEBPACK_IMPORTED_MODULE_32__.TransactionError) {
@@ -47004,6 +47083,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
47004
47083
  if ('floatingSidebar' in prefs) {
47005
47084
  await _platform_server_asyncStorage__WEBPACK_IMPORTED_MODULE_5__.setItem('floating-sidebar', '' + prefs.floatingSidebar);
47006
47085
  }
47086
+ if ('language' in prefs) {
47087
+ await _platform_server_asyncStorage__WEBPACK_IMPORTED_MODULE_5__.setItem('language', prefs.language);
47088
+ }
47007
47089
  if ('theme' in prefs) {
47008
47090
  await _platform_server_asyncStorage__WEBPACK_IMPORTED_MODULE_5__.setItem('theme', prefs.theme);
47009
47091
  }
@@ -47016,11 +47098,12 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
47016
47098
  return 'ok';
47017
47099
  };
47018
47100
  handlers['load-global-prefs'] = async function () {
47019
- const [[, floatingSidebar], [, maxMonths], [, documentDir], [, encryptKey], [, theme], [, preferredDarkTheme], [, serverSelfSignedCert]] = await _platform_server_asyncStorage__WEBPACK_IMPORTED_MODULE_5__.multiGet([
47101
+ const [[, floatingSidebar], [, maxMonths], [, documentDir], [, encryptKey], [, language], [, theme], [, preferredDarkTheme], [, serverSelfSignedCert]] = await _platform_server_asyncStorage__WEBPACK_IMPORTED_MODULE_5__.multiGet([
47020
47102
  'floating-sidebar',
47021
47103
  'max-months',
47022
47104
  'document-dir',
47023
47105
  'encrypt-key',
47106
+ 'language',
47024
47107
  'theme',
47025
47108
  'preferred-dark-theme',
47026
47109
  'server-self-signed-cert'
@@ -47030,6 +47113,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
47030
47113
  maxMonths: (0, _shared_util__WEBPACK_IMPORTED_MODULE_13__.stringToInteger)(maxMonths || ''),
47031
47114
  documentDir: documentDir || getDefaultDocumentDir(),
47032
47115
  keyId: encryptKey && JSON.parse(encryptKey).id,
47116
+ language,
47033
47117
  theme: theme === 'light' || theme === 'dark' || theme === 'auto' || theme === 'development' || theme === 'midnight' ? theme : 'auto',
47034
47118
  preferredDarkTheme: preferredDarkTheme === 'dark' || preferredDarkTheme === 'midnight' ? preferredDarkTheme : 'dark',
47035
47119
  serverSelfSignedCert: serverSelfSignedCert || undefined
@@ -47646,7 +47730,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
47646
47730
  }
47647
47731
  }
47648
47732
  catch { } // Ignore cleanup errors
47649
- throw new Error(`Failed to duplicate budget: ${error.message}`);
47733
+ throw new Error(`Failed to duplicate budget file: ${error.message}`);
47650
47734
  }
47651
47735
  // load in and validate
47652
47736
  const { error } = await loadBudget(newId);
@@ -48063,7 +48147,6 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
48063
48147
  await ensureExists(documentDir);
48064
48148
  _platform_server_fs__WEBPACK_IMPORTED_MODULE_7__._setDocumentDir(documentDir);
48065
48149
  }
48066
- // eslint-disable-next-line import/no-unused-modules
48067
48150
  async function initApp(isDev, socketName) {
48068
48151
  await _platform_server_sqlite__WEBPACK_IMPORTED_MODULE_9__.init();
48069
48152
  await Promise.all([
@@ -48100,7 +48183,6 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
48100
48183
  global.$setSyncingMode = _sync__WEBPACK_IMPORTED_MODULE_48__.setSyncingMode;
48101
48184
  }
48102
48185
  }
48103
- // eslint-disable-next-line import/no-unused-modules
48104
48186
  async function init(config) {
48105
48187
  // Get from build
48106
48188
  let dataDir, serverURL;
@@ -48139,7 +48221,6 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
48139
48221
  return lib;
48140
48222
  }
48141
48223
  // Export a few things required for the platform
48142
- // eslint-disable-next-line import/no-unused-modules
48143
48224
  const lib = {
48144
48225
  getDataDir: _platform_server_fs__WEBPACK_IMPORTED_MODULE_7__.getDataDir,
48145
48226
  sendMessage: (msg, args) => _platform_server_connection__WEBPACK_IMPORTED_MODULE_6__.send(msg, args),
@@ -48936,6 +49017,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
48936
49017
  dateRange: row.date_range,
48937
49018
  mode: row.mode,
48938
49019
  groupBy: row.group_by,
49020
+ sortBy: row.sort_by,
48939
49021
  interval: row.interval,
48940
49022
  balanceType: row.balance_type,
48941
49023
  showEmpty: row.show_empty === 1,
@@ -48958,6 +49040,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
48958
49040
  date_range: report.dateRange,
48959
49041
  mode: report.mode,
48960
49042
  group_by: report.groupBy,
49043
+ sort_by: report.sortBy,
48961
49044
  interval: report.interval,
48962
49045
  balance_type: report.balanceType,
48963
49046
  show_empty: report.showEmpty ? 1 : 0,
@@ -49156,22 +49239,17 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49156
49239
  /* harmony export */ app: () => ( /* binding */app),
49157
49240
  /* harmony export */ createSchedule: () => ( /* binding */createSchedule),
49158
49241
  /* harmony export */ deleteSchedule: () => ( /* binding */deleteSchedule),
49159
- /* harmony export */ getDateWithSkippedWeekend: () => ( /* binding */getDateWithSkippedWeekend),
49160
- /* harmony export */ getNextDate: () => ( /* binding */getNextDate),
49161
49242
  /* harmony export */ getRuleForSchedule: () => ( /* binding */getRuleForSchedule),
49162
49243
  /* harmony export */ setNextDate: () => ( /* binding */setNextDate),
49163
49244
  /* harmony export */ updateConditions: () => ( /* binding */updateConditions),
49164
49245
  /* harmony export */ updateSchedule: () => ( /* binding */updateSchedule)
49165
49246
  /* harmony export */
49166
49247
  });
49167
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/startOfDay/index.js");
49168
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/addDays/index.js");
49169
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/isWeekend/index.js");
49170
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/nextMonday/index.js");
49171
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/previousFriday/index.js");
49248
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/addDays/index.js");
49249
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/startOfDay/index.js");
49172
49250
  /* harmony import */ var deep_equal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! deep-equal */ "./node_modules/deep-equal/index.js");
49173
49251
  /* harmony import */ var deep_equal__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/ __webpack_require__.n(deep_equal__WEBPACK_IMPORTED_MODULE_0__);
49174
- /* harmony import */ var uuid__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! uuid */ "./node_modules/uuid/dist/esm-node/v4.js");
49252
+ /* harmony import */ var uuid__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! uuid */ "./node_modules/uuid/dist/esm-node/v4.js");
49175
49253
  /* harmony import */ var _platform_exceptions__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../platform/exceptions */ "./packages/loot-core/src/platform/exceptions/index.electron.ts");
49176
49254
  /* harmony import */ var _platform_server_connection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../platform/server/connection */ "./packages/loot-core/src/platform/server/connection/index.api.ts");
49177
49255
  /* harmony import */ var _shared_months__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../shared/months */ "./packages/loot-core/src/shared/months.ts");
@@ -49213,36 +49291,6 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49213
49291
  const added = replacements.filter((x) => x[0] == null && x[1] != null).map((x) => x[1]);
49214
49292
  return updated.concat(added);
49215
49293
  }
49216
- function getNextDate(dateCond, start = new Date((0, _shared_months__WEBPACK_IMPORTED_MODULE_3__.currentDay)()), noSkipWeekend = false) {
49217
- start = date_fns__WEBPACK_IMPORTED_MODULE_19__["default"](start);
49218
- const cond = new _accounts_rules__WEBPACK_IMPORTED_MODULE_6__.Condition(dateCond.op, 'date', dateCond.value, null);
49219
- const value = cond.getValue();
49220
- if (value.type === 'date') {
49221
- return value.date;
49222
- }
49223
- else if (value.type === 'recur') {
49224
- let dates = value.schedule.occurrences({
49225
- start,
49226
- take: 1
49227
- }).toArray();
49228
- if (dates.length === 0) {
49229
- // Could be a schedule with limited occurrences, so we try to
49230
- // find the last occurrence
49231
- dates = value.schedule.occurrences({
49232
- reverse: true,
49233
- take: 1
49234
- }).toArray();
49235
- }
49236
- if (dates.length > 0) {
49237
- let date = dates[0].date;
49238
- if (value.schedule.data.skipWeekend && !noSkipWeekend) {
49239
- date = getDateWithSkippedWeekend(date, value.schedule.data.weekendSolve);
49240
- }
49241
- return (0, _shared_months__WEBPACK_IMPORTED_MODULE_3__.dayFromDate)(date);
49242
- }
49243
- }
49244
- return null;
49245
- }
49246
49294
  async function getRuleForSchedule(id) {
49247
49295
  if (id == null) {
49248
49296
  throw new Error('Schedule not attached to a rule');
@@ -49303,7 +49351,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49303
49351
  }).calculate('next_date'));
49304
49352
  // Only do this if a date condition exists
49305
49353
  if (dateCond) {
49306
- const newNextDate = getNextDate(dateCond, start ? start(nextDate) : new Date());
49354
+ const newNextDate = (0, _shared_schedules__WEBPACK_IMPORTED_MODULE_5__.getNextDate)(dateCond, start ? start(nextDate) : new Date());
49307
49355
  if (newNextDate !== nextDate) {
49308
49356
  // Our `update` functon requires the id of the item and we don't
49309
49357
  // have it, so we need to query it
@@ -49336,7 +49384,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49336
49384
  return true;
49337
49385
  }
49338
49386
  async function createSchedule({ schedule = null, conditions = [] } = {}) {
49339
- const scheduleId = schedule?.id || (0, uuid__WEBPACK_IMPORTED_MODULE_20__["default"])();
49387
+ const scheduleId = schedule?.id || (0, uuid__WEBPACK_IMPORTED_MODULE_19__["default"])();
49340
49388
  const { date: dateCond } = (0, _shared_schedules__WEBPACK_IMPORTED_MODULE_5__.extractScheduleConds)(conditions);
49341
49389
  if (dateCond == null) {
49342
49390
  throw new Error('A date condition is required to create a schedule');
@@ -49344,7 +49392,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49344
49392
  if (dateCond.value == null) {
49345
49393
  throw new Error('Date is required');
49346
49394
  }
49347
- const nextDate = getNextDate(dateCond);
49395
+ const nextDate = (0, _shared_schedules__WEBPACK_IMPORTED_MODULE_5__.getNextDate)(dateCond);
49348
49396
  const nextDateRepr = nextDate ? (0, _models__WEBPACK_IMPORTED_MODULE_12__.toDateRepr)(nextDate) : null;
49349
49397
  if (schedule) {
49350
49398
  if (schedule.name) {
@@ -49437,6 +49485,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49437
49485
  }
49438
49486
  await _db__WEBPACK_IMPORTED_MODULE_11__.updateWithSchema('schedules', schedule);
49439
49487
  });
49488
+ return schedule.id;
49440
49489
  }
49441
49490
  async function deleteSchedule({ id }) {
49442
49491
  const { data: ruleId } = await (0, _aql__WEBPACK_IMPORTED_MODULE_10__.runQuery)((0, _shared_query__WEBPACK_IMPORTED_MODULE_4__.q)('schedules').filter({
@@ -49451,7 +49500,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49451
49500
  return setNextDate({
49452
49501
  id,
49453
49502
  start: (nextDate) => {
49454
- return date_fns__WEBPACK_IMPORTED_MODULE_21__["default"]((0, _shared_months__WEBPACK_IMPORTED_MODULE_3__.parseDate)(nextDate), 1);
49503
+ return date_fns__WEBPACK_IMPORTED_MODULE_20__["default"]((0, _shared_months__WEBPACK_IMPORTED_MODULE_3__.parseDate)(nextDate), 1);
49455
49504
  }
49456
49505
  });
49457
49506
  }
@@ -49465,9 +49514,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49465
49514
  rrules: rules
49466
49515
  });
49467
49516
  return schedule.occurrences({
49468
- start: date_fns__WEBPACK_IMPORTED_MODULE_19__["default"](new Date()),
49517
+ start: date_fns__WEBPACK_IMPORTED_MODULE_21__["default"](new Date()),
49469
49518
  take: count
49470
- }).toArray().map((date) => config.skipWeekend ? getDateWithSkippedWeekend(date.date, config.weekendSolveMode) : date.date).map((date) => (0, _shared_months__WEBPACK_IMPORTED_MODULE_3__.dayFromDate)(date));
49519
+ }).toArray().map((date) => config.skipWeekend ? (0, _shared_schedules__WEBPACK_IMPORTED_MODULE_5__.getDateWithSkippedWeekend)(date.date, config.weekendSolveMode) : date.date).map((date) => (0, _shared_months__WEBPACK_IMPORTED_MODULE_3__.dayFromDate)(date));
49471
49520
  }
49472
49521
  catch (err) {
49473
49522
  (0, _platform_exceptions__WEBPACK_IMPORTED_MODULE_1__.captureBreadcrumb)(config);
@@ -49551,9 +49600,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49551
49600
  const { data: upcomingLength } = await (0, _aql__WEBPACK_IMPORTED_MODULE_10__.runQuery)((0, _shared_query__WEBPACK_IMPORTED_MODULE_4__.q)('preferences').filter({
49552
49601
  id: 'upcomingScheduledTransactionLength'
49553
49602
  }).select('value'));
49554
- const upcomingLengthValue = upcomingLength[0]?.value ?? '7'; // Default to 7 days if not set
49555
49603
  for (const schedule of schedules) {
49556
- const status = (0, _shared_schedules__WEBPACK_IMPORTED_MODULE_5__.getStatus)(schedule.next_date, schedule.completed, hasTrans.has(schedule.id), upcomingLengthValue);
49604
+ const status = (0, _shared_schedules__WEBPACK_IMPORTED_MODULE_5__.getStatus)(schedule.next_date, schedule.completed, hasTrans.has(schedule.id), upcomingLength[0]?.value ?? '7');
49557
49605
  if (status === 'paid') {
49558
49606
  if (schedule._date) {
49559
49607
  // Move forward recurring schedules
@@ -49595,9 +49643,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49595
49643
  }
49596
49644
  }
49597
49645
  if (failedToPost.length > 0) {
49598
- _platform_server_connection__WEBPACK_IMPORTED_MODULE_2__.send('schedules-offline', {
49599
- payees: failedToPost
49600
- });
49646
+ _platform_server_connection__WEBPACK_IMPORTED_MODULE_2__.send('schedules-offline');
49601
49647
  }
49602
49648
  else if (didPost) {
49603
49649
  // This forces a full refresh of transactions because it
@@ -49609,7 +49655,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49609
49655
  tables: [
49610
49656
  'transactions'
49611
49657
  ],
49612
- syncDisabled: 'false'
49658
+ syncDisabled: false
49613
49659
  });
49614
49660
  }
49615
49661
  }
@@ -49636,20 +49682,6 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
49636
49682
  }
49637
49683
  }
49638
49684
  });
49639
- function getDateWithSkippedWeekend(date, solveMode) {
49640
- if (date_fns__WEBPACK_IMPORTED_MODULE_22__["default"](date)) {
49641
- if (solveMode === 'after') {
49642
- return date_fns__WEBPACK_IMPORTED_MODULE_23__["default"](date);
49643
- }
49644
- else if (solveMode === 'before') {
49645
- return date_fns__WEBPACK_IMPORTED_MODULE_24__["default"](date);
49646
- }
49647
- else {
49648
- throw new Error('Unknown weekend solve mode, this should not happen!');
49649
- }
49650
- }
49651
- return date;
49652
- }
49653
49685
  /***/
49654
49686
  }),
49655
49687
  /***/ "./packages/loot-core/src/server/schedules/find-schedules.ts":
@@ -52021,10 +52053,30 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
52021
52053
  const subValue = t.subtransactions.reduce((acc, st) => acc + st.amount, 0);
52022
52054
  return subValue !== t.amount;
52023
52055
  });
52056
+ // 5. Fix transfers that should not have categories
52057
+ const brokenTransfers = await _db__WEBPACK_IMPORTED_MODULE_4__.all(`
52058
+ SELECT t1.id
52059
+ FROM v_transactions_internal t1
52060
+ JOIN accounts a1 ON t1.account = a1.id
52061
+ JOIN v_transactions_internal t2 ON t1.transfer_id = t2.id
52062
+ JOIN accounts a2 ON t2.account = a2.id
52063
+ WHERE a1.offbudget = a2.offbudget
52064
+ AND t1.category IS NOT NULL
52065
+ `);
52066
+ await (0, _mutators__WEBPACK_IMPORTED_MODULE_5__.runMutator)(async () => {
52067
+ const updated = brokenTransfers.map((row) => ({
52068
+ id: row.id,
52069
+ category: null
52070
+ }));
52071
+ await (0, _accounts_transactions__WEBPACK_IMPORTED_MODULE_1__.batchUpdateTransactions)({
52072
+ updated
52073
+ });
52074
+ });
52024
52075
  return {
52025
52076
  numBlankPayees: blankPayeeRows.length,
52026
52077
  numCleared: clearedRows.length,
52027
52078
  numDeleted: deletedRows.length,
52079
+ numTransfersFixed: brokenTransfers.length,
52028
52080
  mismatchedSplits
52029
52081
  };
52030
52082
  });
@@ -52571,7 +52623,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
52571
52623
  /* harmony export */ isPreviewEnvironment: () => ( /* binding */isPreviewEnvironment)
52572
52624
  /* harmony export */
52573
52625
  });
52574
- /* eslint-disable import/no-unused-modules */ function isPreviewEnvironment() {
52626
+ function isPreviewEnvironment() {
52575
52627
  return String(process.env.REACT_APP_NETLIFY) === 'true';
52576
52628
  }
52577
52629
  function isDevelopmentEnvironment() {
@@ -52675,7 +52727,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
52675
52727
  return (0, i18next__WEBPACK_IMPORTED_MODULE_0__.t)('This budget cannot be loaded with this version of the app.');
52676
52728
  }
52677
52729
  else if (error === 'budget-not-found') {
52678
- return (0, i18next__WEBPACK_IMPORTED_MODULE_0__.t)('Budget “{id}” not found. Check the id of your budget in the Advanced section of the settings page.', {
52730
+ return (0, i18next__WEBPACK_IMPORTED_MODULE_0__.t)('Budget “{{id}}” not found. Check the ID of your budget in the Advanced section of the settings page.', {
52679
52731
  id
52680
52732
  });
52681
52733
  }
@@ -52704,7 +52756,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
52704
52756
  case 'unauthorized':
52705
52757
  return (0, i18next__WEBPACK_IMPORTED_MODULE_0__.t)('You are not logged in.');
52706
52758
  case 'token-expired':
52707
- return (0, i18next__WEBPACK_IMPORTED_MODULE_0__.t)('Login expired, please login again.');
52759
+ return (0, i18next__WEBPACK_IMPORTED_MODULE_0__.t)('Login expired, please log in again.');
52708
52760
  case 'user-cant-be-empty':
52709
52761
  return (0, i18next__WEBPACK_IMPORTED_MODULE_0__.t)('Please select a user.');
52710
52762
  case 'invalid-file-id':
@@ -53816,18 +53868,28 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
53816
53868
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
53817
53869
  /* harmony export */ describeSchedule: () => ( /* binding */describeSchedule),
53818
53870
  /* harmony export */ extractScheduleConds: () => ( /* binding */extractScheduleConds),
53871
+ /* harmony export */ getDateWithSkippedWeekend: () => ( /* binding */getDateWithSkippedWeekend),
53819
53872
  /* harmony export */ getHasTransactionsQuery: () => ( /* binding */getHasTransactionsQuery),
53873
+ /* harmony export */ getNextDate: () => ( /* binding */getNextDate),
53820
53874
  /* harmony export */ getRecurringDescription: () => ( /* binding */getRecurringDescription),
53821
53875
  /* harmony export */ getScheduledAmount: () => ( /* binding */getScheduledAmount),
53822
53876
  /* harmony export */ getStatus: () => ( /* binding */getStatus),
53823
- /* harmony export */ recurConfigToRSchedule: () => ( /* binding */recurConfigToRSchedule)
53877
+ /* harmony export */ getUpcomingDays: () => ( /* binding */getUpcomingDays),
53878
+ /* harmony export */ recurConfigToRSchedule: () => ( /* binding */recurConfigToRSchedule),
53879
+ /* harmony export */ scheduleIsRecurring: () => ( /* binding */scheduleIsRecurring)
53824
53880
  /* harmony export */
53825
53881
  });
53826
- /* harmony import */ var _months__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./months */ "./packages/loot-core/src/shared/months.ts");
53827
- /* harmony import */ var _query__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./query */ "./packages/loot-core/src/shared/query.ts");
53882
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/startOfDay/index.js");
53883
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/isWeekend/index.js");
53884
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/nextMonday/index.js");
53885
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/previousFriday/index.js");
53886
+ /* harmony import */ var _server_accounts_rules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../server/accounts/rules */ "./packages/loot-core/src/server/accounts/rules.ts");
53887
+ /* harmony import */ var _months__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./months */ "./packages/loot-core/src/shared/months.ts");
53888
+ /* harmony import */ var _query__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./query */ "./packages/loot-core/src/shared/query.ts");
53828
53889
  // @ts-strict-ignore
53829
53890
  function getStatus(nextDate, completed, hasTrans, upcomingLength) {
53830
- const today = _months__WEBPACK_IMPORTED_MODULE_0__.currentDay();
53891
+ const upcomingDays = getUpcomingDays(upcomingLength);
53892
+ const today = _months__WEBPACK_IMPORTED_MODULE_1__.currentDay();
53831
53893
  if (completed) {
53832
53894
  return 'completed';
53833
53895
  }
@@ -53837,7 +53899,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
53837
53899
  else if (nextDate === today) {
53838
53900
  return 'due';
53839
53901
  }
53840
- else if (nextDate > today && nextDate <= _months__WEBPACK_IMPORTED_MODULE_0__.addDays(today, parseInt(upcomingLength ?? '7'))) {
53902
+ else if (nextDate > today && nextDate <= _months__WEBPACK_IMPORTED_MODULE_1__.addDays(today, upcomingDays)) {
53841
53903
  return 'upcoming';
53842
53904
  }
53843
53905
  else if (nextDate < today) {
@@ -53854,12 +53916,12 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
53854
53916
  $and: {
53855
53917
  schedule: schedule.id,
53856
53918
  date: {
53857
- $gte: dateCond && dateCond.op === 'is' ? schedule.next_date : _months__WEBPACK_IMPORTED_MODULE_0__.subDays(schedule.next_date, 2)
53919
+ $gte: dateCond && dateCond.op === 'is' ? schedule.next_date : _months__WEBPACK_IMPORTED_MODULE_1__.subDays(schedule.next_date, 2)
53858
53920
  }
53859
53921
  }
53860
53922
  };
53861
53923
  });
53862
- return (0, _query__WEBPACK_IMPORTED_MODULE_1__.q)('transactions').options({
53924
+ return (0, _query__WEBPACK_IMPORTED_MODULE_2__.q)('transactions').options({
53863
53925
  splits: 'all'
53864
53926
  }).filter({
53865
53927
  $or: filters
@@ -53873,7 +53935,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
53873
53935
  function makeNumberSuffix(num) {
53874
53936
  // Slight abuse of date-fns to turn a number like "1" into the full
53875
53937
  // form "1st" but formatting a date with that number
53876
- return _months__WEBPACK_IMPORTED_MODULE_0__.format(new Date(2020, 0, num, 12), 'do');
53938
+ return _months__WEBPACK_IMPORTED_MODULE_1__.format(new Date(2020, 0, num, 12), 'do');
53877
53939
  }
53878
53940
  function prettyDayName(day) {
53879
53941
  const days = {
@@ -53900,7 +53962,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
53900
53962
  }
53901
53963
  break;
53902
53964
  case 'on_date':
53903
- endModeSuffix = `, until ${_months__WEBPACK_IMPORTED_MODULE_0__.format(config.endDate, dateFormat)}`;
53965
+ endModeSuffix = `, until ${_months__WEBPACK_IMPORTED_MODULE_1__.format(config.endDate, dateFormat)}`;
53904
53966
  break;
53905
53967
  default:
53906
53968
  }
@@ -53917,7 +53979,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
53917
53979
  {
53918
53980
  let desc = 'Every ';
53919
53981
  desc += interval !== 1 ? `${interval} weeks` : 'week';
53920
- desc += ' on ' + _months__WEBPACK_IMPORTED_MODULE_0__.format(config.start, 'EEEE');
53982
+ desc += ' on ' + _months__WEBPACK_IMPORTED_MODULE_1__.format(config.start, 'EEEE');
53921
53983
  return desc + suffix;
53922
53984
  }
53923
53985
  case 'monthly':
@@ -53979,7 +54041,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
53979
54041
  }
53980
54042
  }
53981
54043
  else {
53982
- desc += ' on the ' + _months__WEBPACK_IMPORTED_MODULE_0__.format(config.start, 'do');
54044
+ desc += ' on the ' + _months__WEBPACK_IMPORTED_MODULE_1__.format(config.start, 'do');
53983
54045
  }
53984
54046
  return desc + suffix;
53985
54047
  }
@@ -53987,7 +54049,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
53987
54049
  {
53988
54050
  let desc = 'Every ';
53989
54051
  desc += interval !== 1 ? `${interval} years` : 'year';
53990
- desc += ' on ' + _months__WEBPACK_IMPORTED_MODULE_0__.format(config.start, 'LLL do');
54052
+ desc += ' on ' + _months__WEBPACK_IMPORTED_MODULE_1__.format(config.start, 'LLL do');
53991
54053
  return desc + suffix;
53992
54054
  }
53993
54055
  default:
@@ -53996,7 +54058,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
53996
54058
  }
53997
54059
  function recurConfigToRSchedule(config) {
53998
54060
  const base = {
53999
- start: _months__WEBPACK_IMPORTED_MODULE_0__.parseDate(config.start),
54061
+ start: _months__WEBPACK_IMPORTED_MODULE_1__.parseDate(config.start),
54000
54062
  // @ts-ignore: issues with https://gitlab.com/john.carroll.p/rschedule/-/issues/86
54001
54063
  frequency: config.frequency.toUpperCase(),
54002
54064
  byHourOfDay: [
@@ -54012,7 +54074,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
54012
54074
  base.count = config.endOccurrences;
54013
54075
  break;
54014
54076
  case 'on_date':
54015
- base.end = _months__WEBPACK_IMPORTED_MODULE_0__.parseDate(config.endDate);
54077
+ base.end = _months__WEBPACK_IMPORTED_MODULE_1__.parseDate(config.endDate);
54016
54078
  break;
54017
54079
  default:
54018
54080
  }
@@ -54068,6 +54130,50 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
54068
54130
  date: conditions.find((cond) => (cond.op === 'is' || cond.op === 'isapprox') && cond.field === 'date') || null
54069
54131
  };
54070
54132
  }
54133
+ function getNextDate(dateCond, start = new Date(_months__WEBPACK_IMPORTED_MODULE_1__.currentDay()), noSkipWeekend = false) {
54134
+ start = date_fns__WEBPACK_IMPORTED_MODULE_3__["default"](start);
54135
+ const cond = new _server_accounts_rules__WEBPACK_IMPORTED_MODULE_0__.Condition(dateCond.op, 'date', dateCond.value, null);
54136
+ const value = cond.getValue();
54137
+ if (value.type === 'date') {
54138
+ return value.date;
54139
+ }
54140
+ else if (value.type === 'recur') {
54141
+ let dates = value.schedule.occurrences({
54142
+ start,
54143
+ take: 1
54144
+ }).toArray();
54145
+ if (dates.length === 0) {
54146
+ // Could be a schedule with limited occurrences, so we try to
54147
+ // find the last occurrence
54148
+ dates = value.schedule.occurrences({
54149
+ reverse: true,
54150
+ take: 1
54151
+ }).toArray();
54152
+ }
54153
+ if (dates.length > 0) {
54154
+ let date = dates[0].date;
54155
+ if (value.schedule.data.skipWeekend && !noSkipWeekend) {
54156
+ date = getDateWithSkippedWeekend(date, value.schedule.data.weekendSolve);
54157
+ }
54158
+ return _months__WEBPACK_IMPORTED_MODULE_1__.dayFromDate(date);
54159
+ }
54160
+ }
54161
+ return null;
54162
+ }
54163
+ function getDateWithSkippedWeekend(date, solveMode) {
54164
+ if (date_fns__WEBPACK_IMPORTED_MODULE_4__["default"](date)) {
54165
+ if (solveMode === 'after') {
54166
+ return date_fns__WEBPACK_IMPORTED_MODULE_5__["default"](date);
54167
+ }
54168
+ else if (solveMode === 'before') {
54169
+ return date_fns__WEBPACK_IMPORTED_MODULE_6__["default"](date);
54170
+ }
54171
+ else {
54172
+ throw new Error('Unknown weekend solve mode, this should not happen!');
54173
+ }
54174
+ }
54175
+ return date;
54176
+ }
54071
54177
  function getScheduledAmount(amount, inverse = false) {
54072
54178
  // this check is temporary, and required at the moment as a schedule rule
54073
54179
  // allows the amount condition to be deleted which causes a crash
@@ -54087,6 +54193,47 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
54087
54193
  return `Next: ${schedule.next_date}`;
54088
54194
  }
54089
54195
  }
54196
+ function getUpcomingDays(upcomingLength = '7') {
54197
+ const today = _months__WEBPACK_IMPORTED_MODULE_1__.currentDay();
54198
+ const month = _months__WEBPACK_IMPORTED_MODULE_1__.getMonth(today);
54199
+ switch (upcomingLength) {
54200
+ case 'currentMonth':
54201
+ {
54202
+ const day = _months__WEBPACK_IMPORTED_MODULE_1__.getDay(today);
54203
+ const end = _months__WEBPACK_IMPORTED_MODULE_1__.getDay(_months__WEBPACK_IMPORTED_MODULE_1__.getMonthEnd(today));
54204
+ return end - day + 1;
54205
+ }
54206
+ case 'oneMonth':
54207
+ {
54208
+ return _months__WEBPACK_IMPORTED_MODULE_1__.differenceInCalendarDays(_months__WEBPACK_IMPORTED_MODULE_1__.nextMonth(month), month) + 1;
54209
+ }
54210
+ default:
54211
+ if (upcomingLength.includes('-')) {
54212
+ const [num, unit] = upcomingLength.split('-');
54213
+ const value = Math.max(1, parseInt(num, 10));
54214
+ switch (unit) {
54215
+ case 'day':
54216
+ return value;
54217
+ case 'week':
54218
+ return value * 7;
54219
+ case 'month':
54220
+ const future = _months__WEBPACK_IMPORTED_MODULE_1__.addMonths(today, value);
54221
+ return _months__WEBPACK_IMPORTED_MODULE_1__.differenceInCalendarDays(future, month) + 1;
54222
+ case 'year':
54223
+ const futureYear = _months__WEBPACK_IMPORTED_MODULE_1__.addYears(today, value);
54224
+ return _months__WEBPACK_IMPORTED_MODULE_1__.differenceInCalendarDays(futureYear, month) + 1;
54225
+ default:
54226
+ return 7;
54227
+ }
54228
+ }
54229
+ return parseInt(upcomingLength, 10);
54230
+ }
54231
+ }
54232
+ function scheduleIsRecurring(dateCond) {
54233
+ const cond = new _server_accounts_rules__WEBPACK_IMPORTED_MODULE_0__.Condition(dateCond.op, 'date', dateCond.value, null);
54234
+ const value = cond.getValue();
54235
+ return value.type === 'recur';
54236
+ }
54090
54237
  /***/
54091
54238
  }),
54092
54239
  /***/ "./packages/loot-core/src/shared/transactions.ts":
@@ -54167,8 +54314,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
54167
54314
  // Calculate the new total of split transactions and make sure
54168
54315
  // that it equals the parent amount
54169
54316
  const total = (trans.subtransactions || []).reduce((acc, t) => acc + num(t.amount), 0);
54317
+ const { error, ...rest } = trans;
54170
54318
  return {
54171
- ...trans,
54319
+ ...rest,
54172
54320
  error: total === num(trans.amount) ? null : SplitTransactionError(total, trans)
54173
54321
  };
54174
54322
  }
@@ -54348,11 +54496,10 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
54348
54496
  return null;
54349
54497
  }
54350
54498
  else if (trans.subtransactions?.length === 1) {
54499
+ const { error, subtransactions, ...rest } = trans;
54351
54500
  return {
54352
- ...trans,
54353
- subtransactions: undefined,
54354
- is_parent: false,
54355
- error: null
54501
+ ...rest,
54502
+ is_parent: false
54356
54503
  };
54357
54504
  }
54358
54505
  else {
@@ -54376,8 +54523,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
54376
54523
  const subtransactions = createSubtransactions?.(trans) || [
54377
54524
  makeChild(trans)
54378
54525
  ];
54526
+ const { error, ...rest } = trans;
54379
54527
  return {
54380
- ...trans,
54528
+ ...rest,
54381
54529
  is_parent: true,
54382
54530
  error: num(trans.amount) === 0 ? null : SplitTransactionError(0, trans),
54383
54531
  subtransactions: subtransactions.map((t) => ({
@@ -54746,7 +54894,6 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
54746
54894
  separatorRegex
54747
54895
  };
54748
54896
  }
54749
- // Number utilities
54750
54897
  // We dont use `Number.MAX_SAFE_NUMBER` and such here because those
54751
54898
  // numbers are so large that it's not safe to convert them to floats
54752
54899
  // (i.e. N / 100). For example, `9007199254740987 / 100 ===
@@ -54765,33 +54912,33 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
54765
54912
  }
54766
54913
  return value;
54767
54914
  }
54768
- function toRelaxedNumber(value) {
54769
- return integerToAmount(currencyToInteger(value) || 0);
54915
+ function toRelaxedNumber(currencyAmount) {
54916
+ return integerToAmount(currencyToInteger(currencyAmount) || 0);
54770
54917
  }
54771
- function integerToCurrency(n, formatter = getNumberFormat().formatter) {
54772
- return formatter.format(safeNumber(n) / 100);
54918
+ function integerToCurrency(integerAmount, formatter = getNumberFormat().formatter) {
54919
+ return formatter.format(safeNumber(integerAmount) / 100);
54773
54920
  }
54774
- function amountToCurrency(n) {
54775
- return getNumberFormat().formatter.format(n);
54921
+ function amountToCurrency(amount) {
54922
+ return getNumberFormat().formatter.format(amount);
54776
54923
  }
54777
- function amountToCurrencyNoDecimal(n) {
54924
+ function amountToCurrencyNoDecimal(amount) {
54778
54925
  return getNumberFormat({
54779
54926
  ...numberFormatConfig,
54780
54927
  hideFraction: true
54781
- }).formatter.format(n);
54928
+ }).formatter.format(amount);
54782
54929
  }
54783
- function currencyToAmount(str) {
54930
+ function currencyToAmount(currencyAmount) {
54784
54931
  let amount;
54785
54932
  if (getNumberFormat().separatorRegex) {
54786
- amount = parseFloat(str.replace(getNumberFormat().regex, '').replace(getNumberFormat().separatorRegex, '.'));
54933
+ amount = parseFloat(currencyAmount.replace(getNumberFormat().regex, '').replace(getNumberFormat().separatorRegex, '.'));
54787
54934
  }
54788
54935
  else {
54789
- amount = parseFloat(str.replace(getNumberFormat().regex, '').replace(getNumberFormat().separator, '.'));
54936
+ amount = parseFloat(currencyAmount.replace(getNumberFormat().regex, '').replace(getNumberFormat().separator, '.'));
54790
54937
  }
54791
54938
  return isNaN(amount) ? null : amount;
54792
54939
  }
54793
- function currencyToInteger(str) {
54794
- const amount = currencyToAmount(str);
54940
+ function currencyToInteger(currencyAmount) {
54941
+ const amount = currencyToAmount(currencyAmount);
54795
54942
  return amount == null ? null : amountToInteger(amount);
54796
54943
  }
54797
54944
  function stringToInteger(str) {
@@ -54801,11 +54948,11 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
54801
54948
  }
54802
54949
  return null;
54803
54950
  }
54804
- function amountToInteger(n) {
54805
- return Math.round(n * 100);
54951
+ function amountToInteger(amount) {
54952
+ return Math.round(amount * 100);
54806
54953
  }
54807
- function integerToAmount(n) {
54808
- return parseFloat((safeNumber(n) / 100).toFixed(2));
54954
+ function integerToAmount(integerAmount) {
54955
+ return parseFloat((safeNumber(integerAmount) / 100).toFixed(2));
54809
54956
  }
54810
54957
  // This is used when the input format could be anything (from
54811
54958
  // financial files and we don't want to parse based on the user's