@actual-app/api 6.8.0 → 6.8.2

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.
@@ -4,6 +4,7 @@ import type {
4
4
  CategoryEntity,
5
5
  CategoryGroupEntity,
6
6
  GoCardlessToken,
7
+ TransactionEntity,
7
8
  } from '../../types/models';
8
9
  import type { NewRuleEntity, RuleEntity } from '../../types/models/rule';
9
10
  import type { EmptyObject, StripNever } from '../../types/util';
@@ -121,7 +122,7 @@ type FinanceModals = {
121
122
  month: string;
122
123
  };
123
124
 
124
- 'schedule-edit': { id: string } | null;
125
+ 'schedule-edit': { id: string; transaction?: TransactionEntity } | null;
125
126
 
126
127
  'schedule-link': { transactionIds: string[] } | null;
127
128
 
@@ -201,6 +202,7 @@ type FinanceModals = {
201
202
  'rollover-summary-to-budget-menu': {
202
203
  month: string;
203
204
  onTransfer: () => void;
205
+ onCover: () => void;
204
206
  onHoldBuffer: () => void;
205
207
  onResetHoldBuffer: () => void;
206
208
  };
@@ -217,8 +219,9 @@ type FinanceModals = {
217
219
  showToBeBudgeted?: boolean;
218
220
  };
219
221
  cover: {
220
- categoryId: string;
222
+ title: string;
221
223
  month: string;
224
+ showToBeBudgeted?: boolean;
222
225
  onSubmit: (fromCategoryId: string) => void;
223
226
  };
224
227
  'hold-buffer': {
@@ -1,10 +1,10 @@
1
- import { type Database } from 'better-sqlite3';
1
+ import { type Database } from '@jlongster/sql.js';
2
2
 
3
- export async function init(): unknown;
3
+ export async function init(): Promise<void>;
4
4
 
5
5
  export function _getModule(): SqlJsStatic;
6
6
 
7
- export function prepare(db, sql): unknown;
7
+ export function prepare(db: Database, sql: string): string;
8
8
 
9
9
  export function runQuery(
10
10
  db: Database,
@@ -1,20 +1,8 @@
1
- import { TransactionEntity } from '../../types/models';
1
+ import { NewTransactionEntity, TransactionEntity } from '../../types/models';
2
2
  export declare function batchUpdateTransactions({ added, deleted, updated, learnCategories, detectOrphanPayees, runTransfers, }: {
3
- added?: Array<{
4
- id: string;
5
- payee: unknown;
6
- category: unknown;
7
- }>;
8
- deleted?: Array<{
9
- id: string;
10
- payee: unknown;
11
- }>;
12
- updated?: Array<{
13
- id: string;
14
- payee?: unknown;
15
- account?: unknown;
16
- category?: unknown;
17
- }>;
3
+ added?: Array<Partial<NewTransactionEntity | TransactionEntity>>;
4
+ deleted?: Array<Partial<NewTransactionEntity | TransactionEntity>>;
5
+ updated?: Array<Partial<NewTransactionEntity | TransactionEntity>>;
18
6
  learnCategories?: boolean;
19
7
  detectOrphanPayees?: boolean;
20
8
  runTransfers?: boolean;
@@ -271,6 +271,9 @@ export declare const schema: {
271
271
  show_uncategorized: {
272
272
  type: string;
273
273
  };
274
+ include_current: {
275
+ type: string;
276
+ };
274
277
  selected_categories: {
275
278
  type: string;
276
279
  };
@@ -50,6 +50,10 @@ export declare function transferAvailable({ month, amount, category, }: {
50
50
  amount: number;
51
51
  category: string;
52
52
  }): Promise<void>;
53
+ export declare function coverOverbudgeted({ month, category, }: {
54
+ month: string;
55
+ category: string;
56
+ }): Promise<void>;
53
57
  export declare function transferCategory({ month, amount, from, to, }: {
54
58
  month: string;
55
59
  amount: number;
@@ -4,7 +4,7 @@ export declare const app: {
4
4
  handlers: BudgetHandlers;
5
5
  services: any;
6
6
  unlistenServices: any;
7
- method<Name extends "budget/budget-amount" | "budget/copy-previous-month" | "budget/set-zero" | "budget/set-3month-avg" | "budget/check-templates" | "budget/apply-goal-template" | "budget/overwrite-goal-template" | "budget/cleanup-goal-template" | "budget/hold-for-next-month" | "budget/reset-hold" | "budget/cover-overspending" | "budget/transfer-available" | "budget/transfer-category" | "budget/set-carryover" | "budget/apply-single-template" | "budget/set-n-month-avg" | "budget/copy-single-month">(name: Name, func: BudgetHandlers[Name]): void;
7
+ method<Name extends "budget/budget-amount" | "budget/copy-previous-month" | "budget/set-zero" | "budget/set-3month-avg" | "budget/check-templates" | "budget/apply-goal-template" | "budget/overwrite-goal-template" | "budget/cleanup-goal-template" | "budget/hold-for-next-month" | "budget/reset-hold" | "budget/cover-overspending" | "budget/transfer-available" | "budget/cover-overbudgeted" | "budget/transfer-category" | "budget/set-carryover" | "budget/apply-single-template" | "budget/set-n-month-avg" | "budget/copy-single-month">(name: Name, func: BudgetHandlers[Name]): void;
8
8
  service(func: any): void;
9
9
  combine(...apps: any[]): void;
10
10
  startServices(): void;
@@ -46,6 +46,11 @@ export interface BudgetHandlers {
46
46
  category: string;
47
47
  }) => Promise<void>;
48
48
 
49
+ 'budget/cover-overbudgeted': (arg: {
50
+ month: string;
51
+ category: string;
52
+ }) => Promise<void>;
53
+
49
54
  'budget/transfer-category': (arg: {
50
55
  month: string;
51
56
  amount: number;
@@ -1,16 +1,56 @@
1
+ import { Database } from '@jlongster/sql.js';
1
2
  import { CategoryEntity, CategoryGroupEntity } from '../../types/models';
2
3
  export { toDateRepr, fromDateRepr } from '../models';
3
- export declare function getDatabasePath(): any;
4
- export declare function openDatabase(id?: any): Promise<void>;
5
- export declare function reopenDatabase(): Promise<void>;
4
+ export declare function getDatabasePath(): string;
5
+ export declare function openDatabase(id?: string): Promise<void>;
6
6
  export declare function closeDatabase(): Promise<void>;
7
- export declare function setDatabase(db_: any): void;
8
- export declare function getDatabase(): any;
7
+ export declare function setDatabase(db_: Database): void;
8
+ export declare function getDatabase(): {
9
+ close(): void;
10
+ create_function(name: string, func: (...args: any[]) => any): any;
11
+ each(sql: string, params: import("@jlongster/sql.js").BindParams, callback: import("@jlongster/sql.js").ParamsCallback, done: () => void): any;
12
+ each(sql: string, callback: import("@jlongster/sql.js").ParamsCallback, done: () => void): any;
13
+ exec(sql: string, params?: import("@jlongster/sql.js").BindParams): import("@jlongster/sql.js").QueryExecResult[];
14
+ export(): Uint8Array;
15
+ getRowsModified(): number;
16
+ handleError(): null;
17
+ iterateStatements(sql: string): {
18
+ getRemainingSql(): string;
19
+ next(): import("@jlongster/sql.js").StatementIteratorResult;
20
+ [Symbol.iterator](): Iterator<{
21
+ bind(values?: import("@jlongster/sql.js").BindParams): boolean;
22
+ free(): boolean;
23
+ freemem(): void;
24
+ get(params?: import("@jlongster/sql.js").BindParams): import("@jlongster/sql.js").SqlValue[];
25
+ getAsObject(params?: import("@jlongster/sql.js").BindParams): import("@jlongster/sql.js").ParamsObject;
26
+ getColumnNames(): string[];
27
+ getNormalizedSQL(): string;
28
+ getSQL(): string;
29
+ reset(): void;
30
+ run(values?: import("@jlongster/sql.js").BindParams): void;
31
+ step(): boolean;
32
+ }, any, undefined>;
33
+ };
34
+ prepare(sql: string, params?: import("@jlongster/sql.js").BindParams): {
35
+ bind(values?: import("@jlongster/sql.js").BindParams): boolean;
36
+ free(): boolean;
37
+ freemem(): void;
38
+ get(params?: import("@jlongster/sql.js").BindParams): import("@jlongster/sql.js").SqlValue[];
39
+ getAsObject(params?: import("@jlongster/sql.js").BindParams): import("@jlongster/sql.js").ParamsObject;
40
+ getColumnNames(): string[];
41
+ getNormalizedSQL(): string;
42
+ getSQL(): string;
43
+ reset(): void;
44
+ run(values?: import("@jlongster/sql.js").BindParams): void;
45
+ step(): boolean;
46
+ };
47
+ run(sql: string, params?: import("@jlongster/sql.js").BindParams): any;
48
+ };
9
49
  export declare function loadClock(): Promise<void>;
10
50
  export declare function runQuery(sql: string, params?: Array<string | number>, fetchAll?: false): any;
11
51
  export declare function runQuery(sql: string, params: Array<string | number> | undefined, fetchAll: true): any;
12
- export declare function execQuery(sql: any): void;
13
- export declare function cache(sql: any): any;
52
+ export declare function execQuery(sql: string): void;
53
+ export declare function cache(sql: string): any;
14
54
  export declare function transaction(fn: () => void): void;
15
55
  export declare function asyncTransaction(fn: () => Promise<void>): Promise<void>;
16
56
  export declare function all(sql: any, params?: (string | number)[]): Promise<any>;
@@ -4,7 +4,7 @@ export declare const app: {
4
4
  handlers: Handlers;
5
5
  services: any;
6
6
  unlistenServices: any;
7
- method<Name extends "transaction-update" | "undo" | "redo" | "transactions-batch-update" | "transaction-add" | "transaction-delete" | "transactions-parse-file" | "transactions-export" | "transactions-export-query" | "get-categories" | "get-earliest-transaction" | "get-budget-bounds" | "rollover-budget-month" | "report-budget-month" | "budget-set-type" | "category-create" | "category-update" | "category-move" | "category-delete" | "category-group-create" | "category-group-update" | "category-group-move" | "category-group-delete" | "must-category-transfer" | "payee-create" | "payees-get" | "payees-get-rule-counts" | "payees-merge" | "payees-batch-change" | "payees-check-orphaned" | "payees-get-rules" | "make-filters-from-conditions" | "getCell" | "getCells" | "getCellNamesInSheet" | "debugCell" | "create-query" | "query" | "account-update" | "accounts-get" | "account-properties" | "gocardless-accounts-link" | "simplefin-accounts-link" | "account-create" | "account-close" | "account-reopen" | "account-move" | "secret-set" | "secret-check" | "gocardless-poll-web-token" | "gocardless-status" | "simplefin-status" | "simplefin-accounts" | "gocardless-get-banks" | "gocardless-poll-web-token-stop" | "gocardless-create-web-token" | "accounts-bank-sync" | "transactions-import" | "account-unlink" | "save-global-prefs" | "load-global-prefs" | "save-prefs" | "load-prefs" | "sync-reset" | "sync-repair" | "key-make" | "key-test" | "get-did-bootstrap" | "subscribe-needs-bootstrap" | "subscribe-bootstrap" | "subscribe-get-user" | "subscribe-change-password" | "subscribe-sign-in" | "subscribe-sign-out" | "get-server-version" | "get-server-url" | "set-server-url" | "sync" | "get-budgets" | "get-remote-files" | "reset-budget-cache" | "upload-budget" | "download-budget" | "sync-budget" | "load-budget" | "create-demo-budget" | "close-budget" | "delete-budget" | "create-budget" | "import-budget" | "export-budget" | "upload-file-web" | "backups-get" | "backup-load" | "backup-make" | "get-last-opened-backup" | "app-focused" | "api/batch-budget-start" | "api/batch-budget-end" | "api/load-budget" | "api/download-budget" | "api/start-import" | "api/finish-import" | "api/abort-import" | "api/query" | "api/budget-months" | "api/budget-month" | "api/budget-set-amount" | "api/budget-set-carryover" | "api/transactions-export" | "api/transactions-import" | "api/transactions-add" | "api/transactions-get" | "api/transaction-update" | "api/transaction-delete" | "api/sync" | "api/bank-sync" | "api/accounts-get" | "api/account-create" | "api/account-update" | "api/account-close" | "api/account-reopen" | "api/account-delete" | "api/categories-get" | "api/category-groups-get" | "api/category-group-create" | "api/category-group-update" | "api/category-group-delete" | "api/category-create" | "api/category-update" | "api/category-delete" | "api/payees-get" | "api/payee-create" | "api/payee-update" | "api/payee-delete" | "api/rules-get" | "api/payee-rules-get" | "api/rule-create" | "api/rule-update" | "api/rule-delete" | "budget/budget-amount" | "budget/copy-previous-month" | "budget/set-zero" | "budget/set-3month-avg" | "budget/check-templates" | "budget/apply-goal-template" | "budget/overwrite-goal-template" | "budget/cleanup-goal-template" | "budget/hold-for-next-month" | "budget/reset-hold" | "budget/cover-overspending" | "budget/transfer-available" | "budget/transfer-category" | "budget/set-carryover" | "budget/apply-single-template" | "budget/set-n-month-avg" | "budget/copy-single-month" | "filter-create" | "filter-update" | "filter-delete" | "notes-save" | "report/create" | "report/update" | "report/delete" | "rule-validate" | "rule-add" | "rule-update" | "rule-delete" | "rule-delete-all" | "rule-apply-actions" | "rule-add-payee-rename" | "rules-get" | "rule-get" | "rules-run" | "schedule/create" | "schedule/update" | "schedule/delete" | "schedule/skip-next-date" | "schedule/post-transaction" | "schedule/force-run-service" | "schedule/discover" | "schedule/get-upcoming-dates" | "tools/fix-split-transactions">(name: Name, func: Handlers[Name]): void;
7
+ method<Name extends "transaction-update" | "undo" | "redo" | "transactions-batch-update" | "transaction-add" | "transaction-delete" | "transactions-parse-file" | "transactions-export" | "transactions-export-query" | "get-categories" | "get-earliest-transaction" | "get-budget-bounds" | "rollover-budget-month" | "report-budget-month" | "budget-set-type" | "category-create" | "category-update" | "category-move" | "category-delete" | "category-group-create" | "category-group-update" | "category-group-move" | "category-group-delete" | "must-category-transfer" | "payee-create" | "payees-get" | "payees-get-rule-counts" | "payees-merge" | "payees-batch-change" | "payees-check-orphaned" | "payees-get-rules" | "make-filters-from-conditions" | "getCell" | "getCells" | "getCellNamesInSheet" | "debugCell" | "create-query" | "query" | "account-update" | "accounts-get" | "account-properties" | "gocardless-accounts-link" | "simplefin-accounts-link" | "account-create" | "account-close" | "account-reopen" | "account-move" | "secret-set" | "secret-check" | "gocardless-poll-web-token" | "gocardless-status" | "simplefin-status" | "simplefin-accounts" | "gocardless-get-banks" | "gocardless-poll-web-token-stop" | "gocardless-create-web-token" | "accounts-bank-sync" | "transactions-import" | "account-unlink" | "save-global-prefs" | "load-global-prefs" | "save-prefs" | "load-prefs" | "sync-reset" | "sync-repair" | "key-make" | "key-test" | "get-did-bootstrap" | "subscribe-needs-bootstrap" | "subscribe-bootstrap" | "subscribe-get-user" | "subscribe-change-password" | "subscribe-sign-in" | "subscribe-sign-out" | "get-server-version" | "get-server-url" | "set-server-url" | "sync" | "get-budgets" | "get-remote-files" | "reset-budget-cache" | "upload-budget" | "download-budget" | "sync-budget" | "load-budget" | "create-demo-budget" | "close-budget" | "delete-budget" | "create-budget" | "import-budget" | "export-budget" | "upload-file-web" | "backups-get" | "backup-load" | "backup-make" | "get-last-opened-backup" | "app-focused" | "api/batch-budget-start" | "api/batch-budget-end" | "api/load-budget" | "api/download-budget" | "api/start-import" | "api/finish-import" | "api/abort-import" | "api/query" | "api/budget-months" | "api/budget-month" | "api/budget-set-amount" | "api/budget-set-carryover" | "api/transactions-export" | "api/transactions-import" | "api/transactions-add" | "api/transactions-get" | "api/transaction-update" | "api/transaction-delete" | "api/sync" | "api/bank-sync" | "api/accounts-get" | "api/account-create" | "api/account-update" | "api/account-close" | "api/account-reopen" | "api/account-delete" | "api/categories-get" | "api/category-groups-get" | "api/category-group-create" | "api/category-group-update" | "api/category-group-delete" | "api/category-create" | "api/category-update" | "api/category-delete" | "api/payees-get" | "api/payee-create" | "api/payee-update" | "api/payee-delete" | "api/rules-get" | "api/payee-rules-get" | "api/rule-create" | "api/rule-update" | "api/rule-delete" | "budget/budget-amount" | "budget/copy-previous-month" | "budget/set-zero" | "budget/set-3month-avg" | "budget/check-templates" | "budget/apply-goal-template" | "budget/overwrite-goal-template" | "budget/cleanup-goal-template" | "budget/hold-for-next-month" | "budget/reset-hold" | "budget/cover-overspending" | "budget/transfer-available" | "budget/cover-overbudgeted" | "budget/transfer-category" | "budget/set-carryover" | "budget/apply-single-template" | "budget/set-n-month-avg" | "budget/copy-single-month" | "filter-create" | "filter-update" | "filter-delete" | "notes-save" | "report/create" | "report/update" | "report/delete" | "rule-validate" | "rule-add" | "rule-update" | "rule-delete" | "rule-delete-all" | "rule-apply-actions" | "rule-add-payee-rename" | "rules-get" | "rule-get" | "rules-run" | "schedule/create" | "schedule/update" | "schedule/delete" | "schedule/skip-next-date" | "schedule/post-transaction" | "schedule/force-run-service" | "schedule/discover" | "schedule/get-upcoming-dates" | "tools/fix-split-transactions">(name: Name, func: Handlers[Name]): void;
8
8
  service(func: any): void;
9
9
  combine(...apps: any[]): void;
10
10
  startServices(): void;
@@ -1,4 +1,4 @@
1
- import { Database } from 'better-sqlite3';
1
+ import { Database } from '@jlongster/sql.js';
2
2
  export declare function withMigrationsDir(dir: string, func: () => Promise<void>): Promise<void>;
3
3
  export declare function getMigrationsDir(): string;
4
4
  export declare function getUpMigration(id: any, names: any): any;
@@ -1,3 +1,3 @@
1
- export declare function post(url: any, data: any, headers?: {}): Promise<any>;
1
+ export declare function post(url: any, data: any, headers?: {}, timeout?: any): Promise<any>;
2
2
  export declare function postBinary(url: any, data: any, headers: any): Promise<any>;
3
3
  export declare function get(url: any, opts?: any): Promise<string>;
@@ -31,7 +31,7 @@ export declare const ALLOCATION_METHODS: {
31
31
  remainder: string;
32
32
  };
33
33
  export declare function mapField(field: any, opts?: any): any;
34
- export declare function friendlyOp(op: any, type?: any): "" | "is" | "contains" | "one of" | "not one of" | "is not" | "is approx" | "is between" | "does not contain" | "is after" | "is greater than" | "is after or equals" | "is greater than or equals" | "is before" | "is less than" | "is before or equals" | "is less than or equals" | "is true" | "is false" | "set" | "allocate" | "link schedule" | "and" | "or";
34
+ export declare function friendlyOp(op: any, type?: any): "" | "is" | "contains" | "matches" | "one of" | "not one of" | "is not" | "is approx" | "is between" | "does not contain" | "is after" | "is greater than" | "is after or equals" | "is greater than or equals" | "is before" | "is less than" | "is before or equals" | "is less than or equals" | "is true" | "is false" | "set" | "allocate" | "link schedule" | "and" | "or";
35
35
  export declare function deserializeField(field: any): {
36
36
  field: string;
37
37
  options: {
@@ -12,7 +12,7 @@ declare function SplitTransactionError(total: number, parent: TransactionEntity)
12
12
  difference: number;
13
13
  };
14
14
  type GenericTransactionEntity = NewTransactionEntity | TransactionEntity | TransactionEntityWithError;
15
- export declare function makeChild<T extends GenericTransactionEntity>(parent: T, data: object): T;
15
+ export declare function makeChild<T extends GenericTransactionEntity>(parent: T, data?: object): T;
16
16
  export declare function recalculateSplit(trans: TransactionEntity): TransactionEntityWithError;
17
17
  export declare function ungroupTransactions(transactions: TransactionEntity[]): TransactionEntity[];
18
18
  export declare function groupTransaction(split: TransactionEntity[]): TransactionEntity;
@@ -54,7 +54,7 @@ export declare function deleteTransaction(transactions: TransactionEntity[], id:
54
54
  newTransaction: TransactionEntityWithError | TransactionEntity;
55
55
  diff: any;
56
56
  };
57
- export declare function splitTransaction(transactions: TransactionEntity[], id: string): {
57
+ export declare function splitTransaction(transactions: TransactionEntity[], id: string, createSubtransactions?: (parentTransaction: TransactionEntity) => TransactionEntity[]): {
58
58
  data: any[];
59
59
  diff: {
60
60
  deleted: any[];
@@ -88,4 +88,29 @@ export declare function realizeTempTransactions(transactions: TransactionEntity[
88
88
  starting_balance_flag?: boolean;
89
89
  transfer_id?: string;
90
90
  }[];
91
+ export declare function makeAsNonChildTransactions(childTransactionsToUpdate: TransactionEntity[], transactions: TransactionEntity[]): {
92
+ updated: TransactionEntity[];
93
+ deleted: {
94
+ amount: number;
95
+ id: string;
96
+ account: import("../types/models").AccountEntity;
97
+ category?: import("../types/models").CategoryEntity;
98
+ payee?: import("../types/models").PayeeEntity;
99
+ schedule?: import("../types/models").ScheduleEntity;
100
+ subtransactions?: TransactionEntity[];
101
+ sort_order?: number;
102
+ tombstone?: boolean;
103
+ imported_payee?: string;
104
+ date: string;
105
+ notes?: string;
106
+ cleared?: boolean;
107
+ reconciled?: boolean;
108
+ is_parent?: boolean;
109
+ is_child?: boolean;
110
+ parent_id?: string;
111
+ imported_id?: string;
112
+ starting_balance_flag?: boolean;
113
+ transfer_id?: string;
114
+ }[];
115
+ };
91
116
  export {};
@@ -15,6 +15,7 @@ export interface CustomReportEntity {
15
15
  showEmpty: boolean;
16
16
  showOffBudget: boolean;
17
17
  showHiddenCategories: boolean;
18
+ includeCurrentInterval: boolean;
18
19
  showUncategorized: boolean;
19
20
  selectedCategories?: CategoryEntity[];
20
21
  graphType: string;
@@ -24,6 +25,13 @@ export interface CustomReportEntity {
24
25
  tombstone?: boolean;
25
26
  }
26
27
 
28
+ export type balanceTypeOpType =
29
+ | 'totalAssets'
30
+ | 'totalDebts'
31
+ | 'totalTotals'
32
+ | 'netAssets'
33
+ | 'netDebts';
34
+
27
35
  export type SpendingMonthEntity = Record<
28
36
  string | number,
29
37
  {
@@ -49,6 +57,7 @@ export interface SpendingEntity {
49
57
  average: number;
50
58
  thisMonth: number;
51
59
  lastMonth: number;
60
+ lastYear: number;
52
61
  }[];
53
62
  startDate?: string;
54
63
  endDate?: string;
@@ -66,6 +75,8 @@ export interface DataEntity {
66
75
  endDate?: string;
67
76
  totalDebts: number;
68
77
  totalAssets: number;
78
+ netAssets: number;
79
+ netDebts: number;
69
80
  totalTotals: number;
70
81
  }
71
82
 
@@ -79,8 +90,11 @@ export type IntervalEntity = {
79
90
  date?: string;
80
91
  change?: number;
81
92
  intervalStartDate?: string;
93
+ intervalEndDate?: string;
82
94
  totalAssets: number;
83
95
  totalDebts: number;
96
+ netAssets: number;
97
+ netDebts: number;
84
98
  totalTotals: number;
85
99
  };
86
100
 
@@ -92,6 +106,8 @@ export interface GroupedEntity {
92
106
  totalAssets: number;
93
107
  totalDebts: number;
94
108
  totalTotals: number;
109
+ netAssets: number;
110
+ netDebts: number;
95
111
  categories?: GroupedEntity[];
96
112
  }
97
113
 
@@ -112,6 +128,7 @@ export interface CustomReportData {
112
128
  show_empty: number;
113
129
  show_offbudget: number;
114
130
  show_hidden: number;
131
+ include_current: number;
115
132
  show_uncategorized: number;
116
133
  selected_categories?: CategoryEntity[];
117
134
  graph_type: string;
@@ -24,7 +24,8 @@ export type RuleConditionOp =
24
24
  | 'lt'
25
25
  | 'lte'
26
26
  | 'contains'
27
- | 'doesNotContain';
27
+ | 'doesNotContain'
28
+ | 'matches';
28
29
 
29
30
  export interface RuleConditionEntity {
30
31
  field?: string;
@@ -3,10 +3,9 @@ import { type numberFormats } from '../shared/util';
3
3
  export type FeatureFlag =
4
4
  | 'reportBudget'
5
5
  | 'goalTemplatesEnabled'
6
- | 'customReports'
7
6
  | 'spendingReport'
8
7
  | 'simpleFinSync'
9
- | 'splitsInRules';
8
+ | 'iterableTopologicalSort';
10
9
 
11
10
  export type LocalPrefs = Partial<
12
11
  {
@@ -32,7 +32,7 @@ export interface ServerHandlers {
32
32
  Parameters<typeof batchUpdateTransactions>[0],
33
33
  'detectOrphanPayees'
34
34
  >,
35
- ) => Promise<Awaited<ReturnType<typeof batchUpdateTransactions>>['updated']>;
35
+ ) => Promise<Awaited<ReturnType<typeof batchUpdateTransactions>>>;
36
36
 
37
37
  'transaction-add': (transaction) => Promise<EmptyObject>;
38
38
 
@@ -25745,6 +25745,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
25745
25745
  }
25746
25746
  }
25747
25747
  }
25748
+ function regexp(regex, text) {
25749
+ return new RegExp(regex).test(text) ? 1 : 0;
25750
+ }
25748
25751
  function openDatabase(pathOrBuffer) {
25749
25752
  const db = new (better_sqlite3__WEBPACK_IMPORTED_MODULE_0___default())(pathOrBuffer);
25750
25753
  // Define Unicode-aware LOWER and UPPER implementation.
@@ -25757,6 +25760,10 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
25757
25760
  db.function('UNICODE_UPPER', {
25758
25761
  deterministic: true
25759
25762
  }, (arg) => arg?.toUpperCase());
25763
+ // @ts-expect-error @types/better-sqlite3 does not support setting strict 3rd argument
25764
+ db.function('REGEXP', {
25765
+ deterministic: true
25766
+ }, regexp);
25760
25767
  return db;
25761
25768
  }
25762
25769
  function closeDatabase(db) {
@@ -26370,18 +26377,17 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
26370
26377
  /* harmony export */ rankRules: () => ( /* binding */rankRules)
26371
26378
  /* harmony export */
26372
26379
  });
26373
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/isValid/index.js");
26374
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/parseISO/index.js");
26375
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/subDays/index.js");
26376
- /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/addDays/index.js");
26380
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/isValid/index.js");
26381
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/parseISO/index.js");
26382
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/subDays/index.js");
26383
+ /* harmony import */ var date_fns__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/addDays/index.js");
26377
26384
  /* harmony import */ var _shared_months__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../shared/months */ "./packages/loot-core/src/shared/months.ts");
26378
26385
  /* harmony import */ var _shared_rules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../shared/rules */ "./packages/loot-core/src/shared/rules.ts");
26379
26386
  /* harmony import */ var _shared_schedules__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../shared/schedules */ "./packages/loot-core/src/shared/schedules.ts");
26380
26387
  /* harmony import */ var _shared_transactions__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../shared/transactions */ "./packages/loot-core/src/shared/transactions.ts");
26381
26388
  /* harmony import */ var _shared_util__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../shared/util */ "./packages/loot-core/src/shared/util.ts");
26382
26389
  /* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../errors */ "./packages/loot-core/src/server/errors.ts");
26383
- /* harmony import */ var _prefs__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../prefs */ "./packages/loot-core/src/server/prefs.ts");
26384
- /* harmony import */ var _util_rschedule__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../util/rschedule */ "./packages/loot-core/src/server/util/rschedule.ts");
26390
+ /* harmony import */ var _util_rschedule__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../util/rschedule */ "./packages/loot-core/src/server/util/rschedule.ts");
26385
26391
  // @ts-strict-ignore
26386
26392
  function assert(test, type, msg) {
26387
26393
  if (!test) {
@@ -26393,7 +26399,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
26393
26399
  const rules = (0, _shared_schedules__WEBPACK_IMPORTED_MODULE_2__.recurConfigToRSchedule)(desc);
26394
26400
  return {
26395
26401
  type: 'recur',
26396
- schedule: new _util_rschedule__WEBPACK_IMPORTED_MODULE_7__.Schedule({
26402
+ schedule: new _util_rschedule__WEBPACK_IMPORTED_MODULE_6__.Schedule({
26397
26403
  rrules: rules,
26398
26404
  data: {
26399
26405
  skipWeekend: desc.skipWeekend,
@@ -26412,7 +26418,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
26412
26418
  }
26413
26419
  else if (str.length === 10) {
26414
26420
  // YYYY-MM-DD
26415
- if (!date_fns__WEBPACK_IMPORTED_MODULE_8__["default"](date_fns__WEBPACK_IMPORTED_MODULE_9__["default"](str))) {
26421
+ if (!date_fns__WEBPACK_IMPORTED_MODULE_7__["default"](date_fns__WEBPACK_IMPORTED_MODULE_8__["default"](str))) {
26416
26422
  return null;
26417
26423
  }
26418
26424
  return {
@@ -26422,7 +26428,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
26422
26428
  }
26423
26429
  else if (str.length === 7) {
26424
26430
  // YYYY-MM
26425
- if (!date_fns__WEBPACK_IMPORTED_MODULE_8__["default"](date_fns__WEBPACK_IMPORTED_MODULE_9__["default"](str + '-01'))) {
26431
+ if (!date_fns__WEBPACK_IMPORTED_MODULE_7__["default"](date_fns__WEBPACK_IMPORTED_MODULE_8__["default"](str + '-01'))) {
26426
26432
  return null;
26427
26433
  }
26428
26434
  return {
@@ -26432,7 +26438,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
26432
26438
  }
26433
26439
  else if (str.length === 4) {
26434
26440
  // YYYY
26435
- if (!date_fns__WEBPACK_IMPORTED_MODULE_8__["default"](date_fns__WEBPACK_IMPORTED_MODULE_9__["default"](str + '-01-01'))) {
26441
+ if (!date_fns__WEBPACK_IMPORTED_MODULE_7__["default"](date_fns__WEBPACK_IMPORTED_MODULE_8__["default"](str + '-01-01'))) {
26436
26442
  return null;
26437
26443
  }
26438
26444
  return {
@@ -26481,6 +26487,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
26481
26487
  ops: [
26482
26488
  'is',
26483
26489
  'contains',
26490
+ 'matches',
26484
26491
  'oneOf',
26485
26492
  'isNot',
26486
26493
  'doesNotContain',
@@ -26499,6 +26506,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
26499
26506
  ops: [
26500
26507
  'is',
26501
26508
  'contains',
26509
+ 'matches',
26502
26510
  'oneOf',
26503
26511
  'isNot',
26504
26512
  'doesNotContain',
@@ -26510,7 +26518,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
26510
26518
  assert(Array.isArray(value), 'no-empty-array', `oneOf must have an array value (field: ${fieldName}): ${JSON.stringify(value)}`);
26511
26519
  return value.filter(Boolean).map((val) => val.toLowerCase());
26512
26520
  }
26513
- if (op === 'contains' || op === 'doesNotContain') {
26521
+ if (op === 'contains' || op === 'matches' || op === 'doesNotContain') {
26514
26522
  assert(typeof value === 'string' && value.length > 0, 'no-empty-string', `contains must have non-empty string (field: ${fieldName})`);
26515
26523
  }
26516
26524
  return value.toLowerCase();
@@ -26611,7 +26619,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
26611
26619
  const { schedule } = this.value;
26612
26620
  if (this.op === 'isapprox') {
26613
26621
  const fieldDate = (0, _shared_months__WEBPACK_IMPORTED_MODULE_0__.parseDate)(fieldValue);
26614
- return schedule.occursBetween(date_fns__WEBPACK_IMPORTED_MODULE_10__["default"](fieldDate, 2), date_fns__WEBPACK_IMPORTED_MODULE_11__["default"](fieldDate, 2));
26622
+ return schedule.occursBetween(date_fns__WEBPACK_IMPORTED_MODULE_9__["default"](fieldDate, 2), date_fns__WEBPACK_IMPORTED_MODULE_10__["default"](fieldDate, 2));
26615
26623
  }
26616
26624
  else {
26617
26625
  return schedule.occursOn({
@@ -26797,7 +26805,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
26797
26805
  const childActions = actions.filter((action) => action.options?.splitIndex);
26798
26806
  const totalSplitCount = actions.reduce((prev, cur) => Math.max(prev, cur.options?.splitIndex ?? 0), 0) + 1;
26799
26807
  let update = execNonSplitActions(parentActions, transaction);
26800
- if (!_prefs__WEBPACK_IMPORTED_MODULE_6__.getPrefs()?.['flags.splitsInRules'] || totalSplitCount === 1) {
26808
+ if (totalSplitCount === 1) {
26809
+ // No splits, no need to do anything else.
26801
26810
  return update;
26802
26811
  }
26803
26812
  if (update.is_child) {
@@ -27008,7 +27017,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
27008
27017
  lt: 1,
27009
27018
  lte: 1,
27010
27019
  contains: 0,
27011
- doesNotContain: 0
27020
+ doesNotContain: 0,
27021
+ matches: 0
27012
27022
  };
27013
27023
  function computeScore(rule) {
27014
27024
  const initialScore = rule.conditions.reduce((score, condition) => {
@@ -27298,7 +27308,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
27298
27308
  startDate: since
27299
27309
  }, {
27300
27310
  'X-ACTUAL-TOKEN': userToken
27301
- });
27311
+ }, 60000);
27302
27312
  if (res.error_code) {
27303
27313
  throw BankSyncError(res.error_type, res.error_code);
27304
27314
  }
@@ -28459,6 +28469,10 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
28459
28469
  // Running contains with id will automatically reach into
28460
28470
  // the `name` of the referenced table and do a string match
28461
28471
  return apply(type === 'id' ? field + '.name' : field, '$like', '%' + value + '%');
28472
+ case 'matches':
28473
+ // Running contains with id will automatically reach into
28474
+ // the `name` of the referenced table and do a regex match
28475
+ return apply(type === 'id' ? field + '.name' : field, '$regexp', value);
28462
28476
  case 'doesNotContain':
28463
28477
  // Running contains with id will automatically reach into
28464
28478
  // the `name` of the referenced table and do a string match
@@ -30685,6 +30699,17 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
30685
30699
  ]);
30686
30700
  return `${left} LIKE ${right}`;
30687
30701
  }
30702
+ case '$regexp':
30703
+ {
30704
+ const [left, right] = valArray(state, [
30705
+ lhs,
30706
+ rhs
30707
+ ], [
30708
+ 'string',
30709
+ 'string'
30710
+ ]);
30711
+ return `REGEXP(${right}, ${left})`;
30712
+ }
30688
30713
  case '$notlike':
30689
30714
  {
30690
30715
  const [left, right] = valArray(state, [
@@ -31739,6 +31764,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
31739
31764
  show_uncategorized: f('integer', {
31740
31765
  default: 0
31741
31766
  }),
31767
+ include_current: f('integer', {
31768
+ default: 0
31769
+ }),
31742
31770
  selected_categories: f('json'),
31743
31771
  graph_type: f('string', {
31744
31772
  default: 'BarGraph'
@@ -32276,6 +32304,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
32276
32304
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
32277
32305
  /* harmony export */ copyPreviousMonth: () => ( /* binding */copyPreviousMonth),
32278
32306
  /* harmony export */ copySinglePreviousMonth: () => ( /* binding */copySinglePreviousMonth),
32307
+ /* harmony export */ coverOverbudgeted: () => ( /* binding */coverOverbudgeted),
32279
32308
  /* harmony export */ coverOverspending: () => ( /* binding */coverOverspending),
32280
32309
  /* harmony export */ getBudget: () => ( /* binding */getBudget),
32281
32310
  /* harmony export */ getSheetValue: () => ( /* binding */getSheetValue),
@@ -32480,16 +32509,22 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
32480
32509
  const spent1 = await getSheetValue(_shared_months__WEBPACK_IMPORTED_MODULE_0__.sheetForMonth(prevMonth1), 'sum-amount-' + cat.id);
32481
32510
  const spent2 = await getSheetValue(_shared_months__WEBPACK_IMPORTED_MODULE_0__.sheetForMonth(prevMonth2), 'sum-amount-' + cat.id);
32482
32511
  const spent3 = await getSheetValue(_shared_months__WEBPACK_IMPORTED_MODULE_0__.sheetForMonth(prevMonth3), 'sum-amount-' + cat.id);
32483
- const avg = Math.round((spent1 + spent2 + spent3) / 3);
32512
+ let avg = Math.round((spent1 + spent2 + spent3) / 3);
32513
+ if (cat.is_income === 0) {
32514
+ avg *= -1;
32515
+ }
32484
32516
  setBudget({
32485
32517
  category: cat.id,
32486
32518
  month,
32487
- amount: -avg
32519
+ amount: avg
32488
32520
  });
32489
32521
  }
32490
32522
  });
32491
32523
  }
32492
32524
  async function setNMonthAvg({ month, N, category }) {
32525
+ const categoryFromDb = await _db__WEBPACK_IMPORTED_MODULE_2__.first('SELECT is_income FROM v_categories WHERE id = ?', [
32526
+ category
32527
+ ]);
32493
32528
  let prevMonth = _shared_months__WEBPACK_IMPORTED_MODULE_0__.prevMonth(month);
32494
32529
  let sumAmount = 0;
32495
32530
  for (let l = 0; l < N; l++) {
@@ -32497,11 +32532,14 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
32497
32532
  prevMonth = _shared_months__WEBPACK_IMPORTED_MODULE_0__.prevMonth(prevMonth);
32498
32533
  }
32499
32534
  await (0, _sync__WEBPACK_IMPORTED_MODULE_5__.batchMessages)(async () => {
32500
- const avg = Math.round(sumAmount / N);
32535
+ let avg = Math.round(sumAmount / N);
32536
+ if (categoryFromDb.is_income === 0) {
32537
+ avg *= -1;
32538
+ }
32501
32539
  setBudget({
32502
32540
  category,
32503
32541
  month,
32504
- amount: -avg
32542
+ amount: avg
32505
32543
  });
32506
32544
  });
32507
32545
  }
@@ -32556,6 +32594,16 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
32556
32594
  amount: budgeted + amount
32557
32595
  });
32558
32596
  }
32597
+ async function coverOverbudgeted({ month, category }) {
32598
+ const sheetName = _shared_months__WEBPACK_IMPORTED_MODULE_0__.sheetForMonth(month);
32599
+ const toBudget = await getSheetValue(sheetName, 'to-budget');
32600
+ const categoryBudget = await getSheetValue(sheetName, 'budget-' + category);
32601
+ await setBudget({
32602
+ category,
32603
+ month,
32604
+ amount: categoryBudget + toBudget
32605
+ });
32606
+ }
32559
32607
  async function transferCategory({ month, amount, from, to }) {
32560
32608
  const sheetName = _shared_months__WEBPACK_IMPORTED_MODULE_0__.sheetForMonth(month);
32561
32609
  const fromBudgeted = await getSheetValue(sheetName, 'budget-' + from);
@@ -32619,6 +32667,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
32619
32667
  app.method('budget/reset-hold', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_actions__WEBPACK_IMPORTED_MODULE_3__.resetHold)));
32620
32668
  app.method('budget/cover-overspending', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_actions__WEBPACK_IMPORTED_MODULE_3__.coverOverspending)));
32621
32669
  app.method('budget/transfer-available', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_actions__WEBPACK_IMPORTED_MODULE_3__.transferAvailable)));
32670
+ app.method('budget/cover-overbudgeted', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_actions__WEBPACK_IMPORTED_MODULE_3__.coverOverbudgeted)));
32622
32671
  app.method('budget/transfer-category', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_actions__WEBPACK_IMPORTED_MODULE_3__.transferCategory)));
32623
32672
  app.method('budget/set-carryover', (0, _mutators__WEBPACK_IMPORTED_MODULE_1__.mutator)((0, _undo__WEBPACK_IMPORTED_MODULE_2__.undoable)(_actions__WEBPACK_IMPORTED_MODULE_3__.setCategoryCarryover)));
32624
32673
  /***/
@@ -35207,7 +35256,6 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
35207
35256
  /* harmony export */ moveCategory: () => ( /* binding */moveCategory),
35208
35257
  /* harmony export */ moveCategoryGroup: () => ( /* binding */moveCategoryGroup),
35209
35258
  /* harmony export */ openDatabase: () => ( /* binding */openDatabase),
35210
- /* harmony export */ reopenDatabase: () => ( /* binding */reopenDatabase),
35211
35259
  /* harmony export */ run: () => ( /* binding */run),
35212
35260
  /* harmony export */ runQuery: () => ( /* binding */runQuery),
35213
35261
  /* harmony export */ select: () => ( /* binding */select),
@@ -35239,8 +35287,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
35239
35287
  /* harmony import */ var _sync__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../sync */ "./packages/loot-core/src/server/sync/index.ts");
35240
35288
  /* harmony import */ var _sort__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./sort */ "./packages/loot-core/src/server/db/sort.ts");
35241
35289
  // @ts-strict-ignore
35242
- let dbPath;
35243
- let db;
35290
+ let dbPath = null;
35291
+ let db = null;
35244
35292
  // Util
35245
35293
  function getDatabasePath() {
35246
35294
  return dbPath;
@@ -35253,10 +35301,6 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
35253
35301
  setDatabase(await _platform_server_sqlite__WEBPACK_IMPORTED_MODULE_3__.openDatabase(dbPath));
35254
35302
  // await execQuery('PRAGMA journal_mode = WAL');
35255
35303
  }
35256
- async function reopenDatabase() {
35257
- await _platform_server_sqlite__WEBPACK_IMPORTED_MODULE_3__.closeDatabase(db);
35258
- setDatabase(await _platform_server_sqlite__WEBPACK_IMPORTED_MODULE_3__.openDatabase(dbPath));
35259
- }
35260
35304
  async function closeDatabase() {
35261
35305
  if (db) {
35262
35306
  await _platform_server_sqlite__WEBPACK_IMPORTED_MODULE_3__.closeDatabase(db);
@@ -35659,7 +35703,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
35659
35703
  SELECT p.*, COALESCE(a.name, p.name) AS name FROM payees p
35660
35704
  LEFT JOIN accounts a ON (p.transfer_acct = a.id AND a.tombstone = 0)
35661
35705
  WHERE p.tombstone = 0 AND (p.transfer_acct IS NULL OR a.id IS NOT NULL)
35662
- ORDER BY p.transfer_acct IS NULL DESC, p.name COLLATE NOCASE
35706
+ ORDER BY p.transfer_acct IS NULL DESC, p.name COLLATE NOCASE, a.offbudget, a.sort_order
35663
35707
  `);
35664
35708
  }
35665
35709
  function syncGetOrphanedPayees() {
@@ -36867,6 +36911,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
36867
36911
  else if (cat.category_group_id === findIdByName(data.category_groups, 'Credit Card Payments')) {
36868
36912
  return 'creditCard';
36869
36913
  }
36914
+ else if (cat.category_group_id === findIdByName(data.category_groups, 'Income')) {
36915
+ return 'income';
36916
+ }
36870
36917
  }
36871
36918
  // Can't be done in parallel to have
36872
36919
  // correct sort order.
@@ -36874,13 +36921,17 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
36874
36921
  if (!group.deleted) {
36875
36922
  let groupId;
36876
36923
  // Ignores internal category and credit cards
36877
- if (!equalsIgnoreCase(group.name, 'Internal Master Category') && !equalsIgnoreCase(group.name, 'Credit Card Payments')) {
36924
+ if (!equalsIgnoreCase(group.name, 'Internal Master Category') && !equalsIgnoreCase(group.name, 'Credit Card Payments') && !equalsIgnoreCase(group.name, 'Income')) {
36878
36925
  groupId = await _actual_app_api_methods__WEBPACK_IMPORTED_MODULE_0__.createCategoryGroup({
36879
36926
  name: group.name,
36880
36927
  is_income: false
36881
36928
  });
36882
36929
  entityIdMap.set(group.id, groupId);
36883
36930
  }
36931
+ if (equalsIgnoreCase(group.name, 'Income')) {
36932
+ groupId = incomeCatId;
36933
+ entityIdMap.set(group.id, groupId);
36934
+ }
36884
36935
  const cats = data.categories.filter((cat) => cat.category_group_id === group.id);
36885
36936
  for (const cat of cats.reverse()) {
36886
36937
  if (!cat.deleted) {
@@ -37304,8 +37355,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
37304
37355
  deleted,
37305
37356
  learnCategories
37306
37357
  });
37307
- // Return all data updates to the frontend
37308
- return result.updated;
37358
+ return result;
37309
37359
  });
37310
37360
  });
37311
37361
  handlers['transaction-add'] = (0, _mutators__WEBPACK_IMPORTED_MODULE_34__.mutator)(async function (transaction) {
@@ -37781,7 +37831,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
37781
37831
  const institution = {
37782
37832
  name: externalAccount.institution ?? 'Unknown'
37783
37833
  };
37784
- const bank = await _accounts_link__WEBPACK_IMPORTED_MODULE_15__.findOrCreateBank(institution, externalAccount.orgDomain);
37834
+ const bank = await _accounts_link__WEBPACK_IMPORTED_MODULE_15__.findOrCreateBank(institution, externalAccount.orgDomain ?? externalAccount.orgId);
37785
37835
  if (upgradingId) {
37786
37836
  const accRow = await _db__WEBPACK_IMPORTED_MODULE_27__.first('SELECT * FROM accounts WHERE id = ?', [
37787
37837
  upgradingId
@@ -38063,9 +38113,16 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
38063
38113
  error: 'unauthorized'
38064
38114
  };
38065
38115
  }
38066
- return (0, _post__WEBPACK_IMPORTED_MODULE_37__.post)((0, _server_config__WEBPACK_IMPORTED_MODULE_42__.getServer)().SIMPLEFIN_SERVER + '/accounts', {}, {
38067
- 'X-ACTUAL-TOKEN': userToken
38068
- });
38116
+ try {
38117
+ return await (0, _post__WEBPACK_IMPORTED_MODULE_37__.post)((0, _server_config__WEBPACK_IMPORTED_MODULE_42__.getServer)().SIMPLEFIN_SERVER + '/accounts', {}, {
38118
+ 'X-ACTUAL-TOKEN': userToken
38119
+ }, 60000);
38120
+ }
38121
+ catch (error) {
38122
+ return {
38123
+ error_code: 'TIMED_OUT'
38124
+ };
38125
+ }
38069
38126
  };
38070
38127
  handlers['gocardless-get-banks'] = async function (country) {
38071
38128
  const userToken = await _platform_server_asyncStorage__WEBPACK_IMPORTED_MODULE_5__.getItem('user-token');
@@ -38149,7 +38206,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
38149
38206
  else if (err instanceof _errors__WEBPACK_IMPORTED_MODULE_30__.PostError && err.reason !== 'internal') {
38150
38207
  errors.push({
38151
38208
  accountId: acct.id,
38152
- message: `Account “${acct.name}” is not linked properly. Please link it again`
38209
+ message: err.reason ? err.reason : `Account “${acct.name}” is not linked properly. Please link it again.`
38153
38210
  });
38154
38211
  }
38155
38212
  else {
@@ -39632,18 +39689,23 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
39632
39689
  throw new _errors__WEBPACK_IMPORTED_MODULE_1__.PostError(text);
39633
39690
  }
39634
39691
  }
39635
- async function post(url, data, headers = {}) {
39692
+ async function post(url, data, headers = {}, timeout = null) {
39636
39693
  let text;
39637
39694
  let res;
39638
39695
  try {
39696
+ const controller = new AbortController();
39697
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
39698
+ const signal = timeout ? controller.signal : null;
39639
39699
  res = await (0, _platform_server_fetch__WEBPACK_IMPORTED_MODULE_0__.fetch)(url, {
39640
39700
  method: 'POST',
39641
39701
  body: JSON.stringify(data),
39702
+ signal,
39642
39703
  headers: {
39643
39704
  ...headers,
39644
39705
  'Content-Type': 'application/json'
39645
39706
  }
39646
39707
  });
39708
+ clearTimeout(timeoutId);
39647
39709
  text = await res.text();
39648
39710
  }
39649
39711
  catch (err) {
@@ -39842,6 +39904,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
39842
39904
  showOffBudget: row.show_offbudget === 1,
39843
39905
  showHiddenCategories: row.show_hidden === 1,
39844
39906
  showUncategorized: row.show_uncategorized === 1,
39907
+ includeCurrentInterval: row.include_current === 1,
39845
39908
  selectedCategories: row.selected_categories,
39846
39909
  graphType: row.graph_type,
39847
39910
  conditions: row.conditions,
@@ -39865,6 +39928,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
39865
39928
  show_offbudget: report.showOffBudget ? 1 : 0,
39866
39929
  show_hidden: report.showHiddenCategories ? 1 : 0,
39867
39930
  show_uncategorized: report.showUncategorized ? 1 : 0,
39931
+ include_current: report.includeCurrentInterval ? 1 : 0,
39868
39932
  selected_categories: report.selectedCategories,
39869
39933
  graph_type: report.graphType,
39870
39934
  conditions: report.conditions,
@@ -41202,6 +41266,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
41202
41266
  /* harmony export */ Graph: () => ( /* binding */Graph)
41203
41267
  /* harmony export */
41204
41268
  });
41269
+ /* harmony import */ var _prefs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../prefs */ "./packages/loot-core/src/server/prefs.ts");
41205
41270
  // @ts-strict-ignore
41206
41271
  function Graph() {
41207
41272
  const graph = {
@@ -41272,27 +41337,84 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
41272
41337
  }
41273
41338
  return graph;
41274
41339
  }
41275
- function topologicalSortUntil(name, visited, sorted) {
41340
+ function topologicalSort(sourceNodes) {
41341
+ const visited = new Set();
41342
+ const sorted = [];
41343
+ const prefs = (0, _prefs__WEBPACK_IMPORTED_MODULE_0__.getPrefs)();
41344
+ const iterableTopologicalSort = prefs != null ? prefs['flags.iterableTopologicalSort'] : false;
41345
+ sourceNodes.forEach((name) => {
41346
+ if (!visited.has(name)) {
41347
+ if (iterableTopologicalSort) {
41348
+ topologicalSortIterable(name, visited, sorted);
41349
+ }
41350
+ else {
41351
+ topologicalSortUntil(name, visited, sorted, 0);
41352
+ }
41353
+ }
41354
+ });
41355
+ return sorted;
41356
+ }
41357
+ function topologicalSortUntil(name, visited, sorted, level) {
41276
41358
  visited.add(name);
41359
+ if (level > 2500) {
41360
+ console.error('Limit of recursions reached while sorting budget: 2500');
41361
+ return;
41362
+ }
41277
41363
  const iter = adjacent(name).values();
41278
41364
  let cur = iter.next();
41279
41365
  while (!cur.done) {
41280
41366
  if (!visited.has(cur.value)) {
41281
- topologicalSortUntil(cur.value, visited, sorted);
41367
+ topologicalSortUntil(cur.value, visited, sorted, level + 1);
41282
41368
  }
41283
41369
  cur = iter.next();
41284
41370
  }
41285
41371
  sorted.unshift(name);
41286
41372
  }
41287
- function topologicalSort(sourceNodes) {
41288
- const visited = new Set();
41289
- const sorted = [];
41290
- sourceNodes.forEach((name) => {
41291
- if (!visited.has(name)) {
41292
- topologicalSortUntil(name, visited, sorted);
41373
+ function topologicalSortIterable(name, visited, sorted) {
41374
+ const stackTrace = [];
41375
+ stackTrace.push({
41376
+ count: -1,
41377
+ value: name,
41378
+ parent: '',
41379
+ level: 0
41380
+ });
41381
+ while (stackTrace.length > 0) {
41382
+ const current = stackTrace.slice(-1)[0];
41383
+ const adjacents = adjacent(current.value);
41384
+ if (current.count === -1) {
41385
+ current.count = adjacents.size;
41386
+ }
41387
+ if (current.count > 0) {
41388
+ const iter = adjacents.values();
41389
+ let cur = iter.next();
41390
+ while (!cur.done) {
41391
+ if (!visited.has(cur.value)) {
41392
+ stackTrace.push({
41393
+ count: -1,
41394
+ parent: current.value,
41395
+ value: cur.value,
41396
+ level: current.level + 1
41397
+ });
41398
+ }
41399
+ else {
41400
+ current.count--;
41401
+ }
41402
+ cur = iter.next();
41403
+ }
41293
41404
  }
41294
- });
41295
- return sorted;
41405
+ else {
41406
+ if (!visited.has(current.value)) {
41407
+ visited.add(current.value);
41408
+ sorted.unshift(current.value);
41409
+ }
41410
+ const removed = stackTrace.pop();
41411
+ for (let i = 0; i < stackTrace.length; i++) {
41412
+ if (stackTrace[i].value === removed.parent) {
41413
+ stackTrace[i].count--;
41414
+ }
41415
+ }
41416
+ }
41417
+ }
41296
41418
  }
41297
41419
  function generateDOT() {
41298
41420
  const edgeStrings = [];
@@ -41302,9 +41424,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
41302
41424
  }
41303
41425
  });
41304
41426
  return `
41305
- digraph G {
41306
- ${edgeStrings.join('\n').replace(/!/g, '_')}
41307
- }
41427
+ digraph G {
41428
+ ${edgeStrings.join('\n').replace(/!/g, '_')}
41429
+ }
41308
41430
  `;
41309
41431
  }
41310
41432
  return graph;
@@ -43735,7 +43857,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
43735
43857
  function _yearRange(start, end, inclusive = false) {
43736
43858
  const years = [];
43737
43859
  let year = yearFromDate(start);
43738
- while (date_fns__WEBPACK_IMPORTED_MODULE_14__["default"](_parse(year), _parse(end))) {
43860
+ const endYear = yearFromDate(end);
43861
+ while (date_fns__WEBPACK_IMPORTED_MODULE_14__["default"](_parse(year), _parse(endYear))) {
43739
43862
  years.push(year);
43740
43863
  year = addYears(year, 1);
43741
43864
  }
@@ -43766,7 +43889,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
43766
43889
  function _range(start, end, inclusive = false) {
43767
43890
  const months = [];
43768
43891
  let month = monthFromDate(start);
43769
- while (date_fns__WEBPACK_IMPORTED_MODULE_14__["default"](_parse(month), _parse(end))) {
43892
+ const endMonth = monthFromDate(end);
43893
+ while (date_fns__WEBPACK_IMPORTED_MODULE_14__["default"](_parse(month), _parse(endMonth))) {
43770
43894
  months.push(month);
43771
43895
  month = addMonths(month, 1);
43772
43896
  }
@@ -44073,6 +44197,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
44073
44197
  ops: [
44074
44198
  'is',
44075
44199
  'contains',
44200
+ 'matches',
44076
44201
  'oneOf',
44077
44202
  'isNot',
44078
44203
  'doesNotContain',
@@ -44088,6 +44213,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
44088
44213
  ops: [
44089
44214
  'is',
44090
44215
  'contains',
44216
+ 'matches',
44091
44217
  'oneOf',
44092
44218
  'isNot',
44093
44219
  'doesNotContain',
@@ -44130,7 +44256,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
44130
44256
  }));
44131
44257
  const ALLOCATION_METHODS = {
44132
44258
  'fixed-amount': 'a fixed amount',
44133
- 'fixed-percent': 'a fixed percent',
44259
+ 'fixed-percent': 'a fixed percent of the remainder',
44134
44260
  remainder: 'an equal portion of the remainder'
44135
44261
  };
44136
44262
  function mapField(field, opts) {
@@ -44170,6 +44296,8 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
44170
44296
  return 'is between';
44171
44297
  case 'contains':
44172
44298
  return 'contains';
44299
+ case 'matches':
44300
+ return 'matches';
44173
44301
  case 'doesNotContain':
44174
44302
  return 'does not contain';
44175
44303
  case 'gt':
@@ -44428,7 +44556,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
44428
44556
  $and: {
44429
44557
  schedule: schedule.id,
44430
44558
  date: {
44431
- $gte: dateCond && dateCond.op === 'is' ? schedule.next_date : _months__WEBPACK_IMPORTED_MODULE_0__.subDays(schedule.next_date, 7)
44559
+ $gte: dateCond && dateCond.op === 'is' ? schedule.next_date : _months__WEBPACK_IMPORTED_MODULE_0__.subDays(schedule.next_date, 2)
44432
44560
  }
44433
44561
  }
44434
44562
  };
@@ -44672,6 +44800,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
44672
44800
  /* harmony export */ groupTransaction: () => ( /* binding */groupTransaction),
44673
44801
  /* harmony export */ isPreviewId: () => ( /* binding */isPreviewId),
44674
44802
  /* harmony export */ isTemporaryId: () => ( /* binding */isTemporaryId),
44803
+ /* harmony export */ makeAsNonChildTransactions: () => ( /* binding */makeAsNonChildTransactions),
44675
44804
  /* harmony export */ makeChild: () => ( /* binding */makeChild),
44676
44805
  /* harmony export */ realizeTempTransactions: () => ( /* binding */realizeTempTransactions),
44677
44806
  /* harmony export */ recalculateSplit: () => ( /* binding */recalculateSplit),
@@ -44701,7 +44830,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
44701
44830
  difference
44702
44831
  };
44703
44832
  }
44704
- function makeChild(parent, data) {
44833
+ function makeChild(parent, data = {}) {
44705
44834
  const prefix = parent.id === 'temp' ? 'temp' : '';
44706
44835
  return {
44707
44836
  amount: 0,
@@ -44719,6 +44848,18 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
44719
44848
  error: null
44720
44849
  };
44721
44850
  }
44851
+ function makeNonChild(parent, data) {
44852
+ return {
44853
+ amount: 0,
44854
+ ...data,
44855
+ cleared: parent.cleared != null ? parent.cleared : null,
44856
+ reconciled: parent.reconciled != null ? parent.reconciled : null,
44857
+ sort_order: parent.sort_order || null,
44858
+ starting_balance_flag: null,
44859
+ is_child: false,
44860
+ parent_id: null
44861
+ };
44862
+ }
44722
44863
  function recalculateSplit(trans) {
44723
44864
  // Calculate the new total of split transactions and make sure
44724
44865
  // that it equals the parent amount
@@ -44920,21 +45061,22 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
44920
45061
  }
44921
45062
  });
44922
45063
  }
44923
- function splitTransaction(transactions, id) {
45064
+ function splitTransaction(transactions, id, createSubtransactions) {
44924
45065
  return replaceTransactions(transactions, id, (trans) => {
44925
45066
  if (trans.is_parent || trans.is_child) {
44926
45067
  return trans;
44927
45068
  }
45069
+ const subtransactions = createSubtransactions?.(trans) || [
45070
+ makeChild(trans)
45071
+ ];
44928
45072
  return {
44929
45073
  ...trans,
44930
45074
  is_parent: true,
44931
45075
  error: num(trans.amount) === 0 ? null : SplitTransactionError(0, trans),
44932
- subtransactions: [
44933
- makeChild(trans, {
44934
- amount: 0,
44935
- sort_order: -1
44936
- })
44937
- ]
45076
+ subtransactions: subtransactions.map((t) => ({
45077
+ ...t,
45078
+ sort_order: t.sort_order || -1
45079
+ }))
44938
45080
  };
44939
45081
  });
44940
45082
  }
@@ -44953,6 +45095,35 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
44953
45095
  }))
44954
45096
  ];
44955
45097
  }
45098
+ function makeAsNonChildTransactions(childTransactionsToUpdate, transactions) {
45099
+ const [parentTransaction, ...childTransactions] = transactions;
45100
+ const newNonChildTransactions = childTransactionsToUpdate.map((t) => makeNonChild(parentTransaction, t));
45101
+ const remainingChildTransactions = childTransactions.filter((t) => !newNonChildTransactions.some((updatedTrans) => updatedTrans.id === t.id));
45102
+ const nonChildTransactionsToUpdate = remainingChildTransactions.length === 1 ? [
45103
+ ...newNonChildTransactions,
45104
+ makeNonChild(parentTransaction, remainingChildTransactions[0])
45105
+ ] : newNonChildTransactions;
45106
+ const deleteParentTransaction = remainingChildTransactions.length <= 1;
45107
+ const updatedParentTransaction = {
45108
+ ...parentTransaction,
45109
+ ...!deleteParentTransaction ? {
45110
+ amount: remainingChildTransactions.map((t) => t.amount).reduce((total, amount) => total + amount, 0)
45111
+ } : {}
45112
+ };
45113
+ return {
45114
+ updated: [
45115
+ ...!deleteParentTransaction ? [
45116
+ updatedParentTransaction
45117
+ ] : [],
45118
+ ...nonChildTransactionsToUpdate
45119
+ ],
45120
+ deleted: [
45121
+ ...deleteParentTransaction ? [
45122
+ updatedParentTransaction
45123
+ ] : []
45124
+ ]
45125
+ };
45126
+ }
44956
45127
  /***/
44957
45128
  }),
44958
45129
  /***/ "./packages/loot-core/src/shared/util.ts":
@@ -45331,9 +45502,9 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
45331
45502
  if (amount.startsWith('(') && amount.endsWith(')')) {
45332
45503
  amount = amount.replace('(', '-').replace(')', '');
45333
45504
  }
45334
- //look for a decimal marker, then look for either 5 or 1-2 decimal places.
45505
+ // Look for a decimal marker, then look for either 1-2 or 5-9 decimal places.
45335
45506
  // This avoids matching against 3 places which may not actually be decimal
45336
- const m = amount.match(/[.,]([^.,]{5}|[^.,]{1,2})$/);
45507
+ const m = amount.match(/[.,]([^.,]{5,9}|[^.,]{1,2})$/);
45337
45508
  if (!m || m.index === undefined || m.index === 0) {
45338
45509
  return safeNumber(parseFloat(extractNumbers(amount)));
45339
45510
  }
@@ -0,0 +1,5 @@
1
+ BEGIN TRANSACTION;
2
+
3
+ ALTER TABLE custom_reports ADD COLUMN include_current INTEGER DEFAULT 0;
4
+
5
+ COMMIT;
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actual-app/api",
3
- "version": "6.8.0",
3
+ "version": "6.8.2",
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.8.0",
3
+ "version": "6.8.2",
4
4
  "license": "MIT",
5
5
  "description": "An API for Actual",
6
6
  "engines": {
@@ -22,7 +22,7 @@
22
22
  "clean": "rm -rf dist @types"
23
23
  },
24
24
  "dependencies": {
25
- "@actual-app/crdt": "workspace:^",
25
+ "@actual-app/crdt": "^2.1.0",
26
26
  "better-sqlite3": "^9.6.0",
27
27
  "compare-versions": "^6.1.0",
28
28
  "node-fetch": "^3.3.2",
@@ -37,4 +37,4 @@
37
37
  "tsc-alias": "^1.8.8",
38
38
  "typescript": "^5.0.2"
39
39
  }
40
- }
40
+ }