@actual-app/core 26.3.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.
- package/.swcrc +11 -0
- package/bin/build-browser +40 -0
- package/bin/copy-migrations +9 -0
- package/db.sqlite +0 -0
- package/default-db.sqlite +0 -0
- package/migrations/.force-copy-windows +0 -0
- package/migrations/1548957970627_remove-db-version.sql +5 -0
- package/migrations/1550601598648_payees.sql +23 -0
- package/migrations/1555786194328_remove_category_group_unique.sql +25 -0
- package/migrations/1561751833510_indexes.sql +7 -0
- package/migrations/1567699552727_budget.sql +38 -0
- package/migrations/1582384163573_cleared.sql +6 -0
- package/migrations/1597756566448_rules.sql +10 -0
- package/migrations/1608652596043_parent_field.sql +13 -0
- package/migrations/1608652596044_trans_views.sql +56 -0
- package/migrations/1612625548236_optimize.sql +7 -0
- package/migrations/1614782639336_trans_views2.sql +33 -0
- package/migrations/1615745967948_meta.sql +10 -0
- package/migrations/1616167010796_accounts_order.sql +5 -0
- package/migrations/1618975177358_schedules.sql +28 -0
- package/migrations/1632571489012_remove_cache.js +136 -0
- package/migrations/1679728867040_rules_conditions.sql +5 -0
- package/migrations/1681115033845_add_schedule_name.sql +5 -0
- package/migrations/1682974838138_remove_payee_rules.sql +5 -0
- package/migrations/1685007876842_add_category_hidden.sql +6 -0
- package/migrations/1686139660866_remove_account_type.sql +5 -0
- package/migrations/1688749527273_transaction_filters.sql +10 -0
- package/migrations/1688841238000_add_account_type.sql +5 -0
- package/migrations/1691233396000_add_schedule_next_date_tombstone.sql +5 -0
- package/migrations/1694438752000_add_goal_targets.sql +7 -0
- package/migrations/1697046240000_add_reconciled.sql +5 -0
- package/migrations/1704572023730_add_account_sync_source.sql +5 -0
- package/migrations/1704572023731_add_missing_goCardless_sync_source.sql +9 -0
- package/migrations/1707267033000_reports.sql +28 -0
- package/migrations/1712784523000_unhide_input_group.sql +8 -0
- package/migrations/1716359441000_include_current.sql +5 -0
- package/migrations/1720310586000_link_transfer_schedules.sql +19 -0
- package/migrations/1720664867241_add_payee_favorite.sql +5 -0
- package/migrations/1720665000000_goal_context.sql +6 -0
- package/migrations/1722717601000_reports_move_selected_categories.js +55 -0
- package/migrations/1722804019000_create_dashboard_table.js +69 -0
- package/migrations/1723665565000_prefs.js +59 -0
- package/migrations/1730744182000_fix_dashboard_table.sql +7 -0
- package/migrations/1736640000000_custom_report_sorting.sql +7 -0
- package/migrations/1737158400000_add_learn_categories_to_payees.sql +5 -0
- package/migrations/1738491452000_sorting_rename.sql +13 -0
- package/migrations/1739139550000_bank_sync_page.sql +7 -0
- package/migrations/1740506588539_add_last_reconciled_at.sql +5 -0
- package/migrations/1745425408000_update_budgetType_pref.sql +7 -0
- package/migrations/1749799110000_add_tags.sql +10 -0
- package/migrations/1749799110001_tags_tombstone.sql +5 -0
- package/migrations/1754611200000_add_category_template_settings.sql +5 -0
- package/migrations/1759260219000_add_trim_interval_report_setting.sql +6 -0
- package/migrations/1759842823172_add_isGlobal_to_preferences.sql +1 -0
- package/migrations/1762178745667_rename_csv_skip_lines_pref.sql +8 -0
- package/migrations/1765518577215_multiple_dashboards.js +30 -0
- package/migrations/1768872504000_add_payee_locations.sql +21 -0
- package/package.json +128 -0
- package/src/mocks/arbitrary-schema.ts +162 -0
- package/src/mocks/budget.ts +901 -0
- package/src/mocks/files/8859-1.qfx +63 -0
- package/src/mocks/files/best.data-ever$.QFX +124 -0
- package/src/mocks/files/big.data.QiF +91 -0
- package/src/mocks/files/budgets/.commit-to-git +0 -0
- package/src/mocks/files/camt/camt.053.payee-memo.xml +127 -0
- package/src/mocks/files/camt/camt.053.xml +463 -0
- package/src/mocks/files/credit-card.ofx +11 -0
- package/src/mocks/files/data-multi-decimal.ofx +64 -0
- package/src/mocks/files/data-payee-memo.ofx +75 -0
- package/src/mocks/files/data-payee-memo.qif +17 -0
- package/src/mocks/files/data.ofx +124 -0
- package/src/mocks/files/data.qfx +124 -0
- package/src/mocks/files/data.qif +91 -0
- package/src/mocks/files/default-budget-template/db.sqlite +0 -0
- package/src/mocks/files/default-budget-template/metadata.json +6 -0
- package/src/mocks/files/html-vals.qfx +17 -0
- package/src/mocks/index.ts +221 -0
- package/src/mocks/migrations/1508717984291_up_add-poop.sql +13 -0
- package/src/mocks/migrations/1508718036311_up_modify-poop.sql +2 -0
- package/src/mocks/migrations/1508727787513_remove-is_income.sql +15 -0
- package/src/mocks/random.ts +16 -0
- package/src/mocks/setup.ts +180 -0
- package/src/mocks/spreadsheet.ts +101 -0
- package/src/mocks/util.ts +82 -0
- package/src/platform/client/connection/README.md +3 -0
- package/src/platform/client/connection/__mocks__/index.ts +67 -0
- package/src/platform/client/connection/index-types.ts +95 -0
- package/src/platform/client/connection/index.browser.ts +213 -0
- package/src/platform/client/connection/index.ts +155 -0
- package/src/platform/client/undo/index.ts +59 -0
- package/src/platform/exceptions/__mocks__/index.ts +7 -0
- package/src/platform/exceptions/index.ts +9 -0
- package/src/platform/server/asyncStorage/__mocks__/index.ts +50 -0
- package/src/platform/server/asyncStorage/index-types.ts +35 -0
- package/src/platform/server/asyncStorage/index.api.ts +2 -0
- package/src/platform/server/asyncStorage/index.electron.ts +88 -0
- package/src/platform/server/asyncStorage/index.ts +126 -0
- package/src/platform/server/connection/README.md +3 -0
- package/src/platform/server/connection/__mocks__/index.ts +15 -0
- package/src/platform/server/connection/index-types.ts +20 -0
- package/src/platform/server/connection/index.api.ts +13 -0
- package/src/platform/server/connection/index.electron.ts +102 -0
- package/src/platform/server/connection/index.ts +154 -0
- package/src/platform/server/fetch/__mocks__/index.ts +3 -0
- package/src/platform/server/fetch/index.api.ts +1 -0
- package/src/platform/server/fetch/index.electron.ts +18 -0
- package/src/platform/server/fetch/index.ts +20 -0
- package/src/platform/server/fs/index.api.ts +198 -0
- package/src/platform/server/fs/index.electron.ts +208 -0
- package/src/platform/server/fs/index.test.ts +117 -0
- package/src/platform/server/fs/index.ts +416 -0
- package/src/platform/server/fs/path-join.api.ts +1 -0
- package/src/platform/server/fs/path-join.electron.ts +1 -0
- package/src/platform/server/fs/path-join.ts +97 -0
- package/src/platform/server/fs/shared.ts +33 -0
- package/src/platform/server/indexeddb/index.ts +115 -0
- package/src/platform/server/log/index.ts +43 -0
- package/src/platform/server/sqlite/index.api.ts +2 -0
- package/src/platform/server/sqlite/index.electron.ts +134 -0
- package/src/platform/server/sqlite/index.test.ts +108 -0
- package/src/platform/server/sqlite/index.ts +241 -0
- package/src/platform/server/sqlite/normalise.ts +9 -0
- package/src/platform/server/sqlite/unicodeLike.test.ts +58 -0
- package/src/platform/server/sqlite/unicodeLike.ts +31 -0
- package/src/server/__mocks__/post.ts +9 -0
- package/src/server/__snapshots__/main.test.ts.snap +199 -0
- package/src/server/__snapshots__/sheet.test.ts.snap +9 -0
- package/src/server/accounts/__snapshots__/sync.test.ts.snap +136 -0
- package/src/server/accounts/app-bank-sync.test.ts +136 -0
- package/src/server/accounts/app.ts +1294 -0
- package/src/server/accounts/link.ts +25 -0
- package/src/server/accounts/payees.ts +36 -0
- package/src/server/accounts/sync.test.ts +679 -0
- package/src/server/accounts/sync.ts +1168 -0
- package/src/server/accounts/title/index.ts +60 -0
- package/src/server/accounts/title/lower-case.ts +93 -0
- package/src/server/accounts/title/specials.ts +21 -0
- package/src/server/admin/app.ts +241 -0
- package/src/server/api-models.ts +244 -0
- package/src/server/api.test.ts +36 -0
- package/src/server/api.ts +1030 -0
- package/src/server/app.ts +91 -0
- package/src/server/aql/compiler.test.ts +966 -0
- package/src/server/aql/compiler.ts +1222 -0
- package/src/server/aql/exec.test.ts +289 -0
- package/src/server/aql/exec.ts +128 -0
- package/src/server/aql/index.ts +41 -0
- package/src/server/aql/schema/executors.test.ts +420 -0
- package/src/server/aql/schema/executors.ts +345 -0
- package/src/server/aql/schema/index.test.ts +67 -0
- package/src/server/aql/schema/index.ts +409 -0
- package/src/server/aql/schema-helpers.test.ts +242 -0
- package/src/server/aql/schema-helpers.ts +208 -0
- package/src/server/aql/views.test.ts +62 -0
- package/src/server/aql/views.ts +57 -0
- package/src/server/auth/app.ts +387 -0
- package/src/server/bench.ts +29 -0
- package/src/server/budget/actions.ts +686 -0
- package/src/server/budget/app.ts +469 -0
- package/src/server/budget/base.test.ts +340 -0
- package/src/server/budget/base.ts +339 -0
- package/src/server/budget/category-template-context.test.ts +1658 -0
- package/src/server/budget/category-template-context.ts +862 -0
- package/src/server/budget/cleanup-template.pegjs +27 -0
- package/src/server/budget/cleanup-template.ts +408 -0
- package/src/server/budget/envelope.ts +403 -0
- package/src/server/budget/goal-template.pegjs +110 -0
- package/src/server/budget/goal-template.ts +309 -0
- package/src/server/budget/report.ts +308 -0
- package/src/server/budget/schedule-template.test.ts +184 -0
- package/src/server/budget/schedule-template.ts +351 -0
- package/src/server/budget/statements.ts +60 -0
- package/src/server/budget/template-notes.test.ts +393 -0
- package/src/server/budget/template-notes.ts +323 -0
- package/src/server/budget/util.ts +25 -0
- package/src/server/budgetfiles/__snapshots__/backups.test.ts.snap +101 -0
- package/src/server/budgetfiles/app.ts +672 -0
- package/src/server/budgetfiles/backups.test.ts +79 -0
- package/src/server/budgetfiles/backups.ts +251 -0
- package/src/server/cloud-storage.ts +467 -0
- package/src/server/dashboard/app.ts +373 -0
- package/src/server/db/__snapshots__/index.test.ts.snap +271 -0
- package/src/server/db/index.test.ts +300 -0
- package/src/server/db/index.ts +855 -0
- package/src/server/db/mappings.ts +59 -0
- package/src/server/db/sort.ts +58 -0
- package/src/server/db/types/index.ts +342 -0
- package/src/server/db/util.ts +36 -0
- package/src/server/encryption/app.ts +133 -0
- package/src/server/encryption/encryption-internals.api.ts +2 -0
- package/src/server/encryption/encryption-internals.electron.ts +89 -0
- package/src/server/encryption/encryption-internals.ts +109 -0
- package/src/server/encryption/encryption.test.ts +19 -0
- package/src/server/encryption/index.test.ts +19 -0
- package/src/server/encryption/index.ts +89 -0
- package/src/server/errors.ts +110 -0
- package/src/server/filters/app.ts +191 -0
- package/src/server/importers/actual.ts +49 -0
- package/src/server/importers/index.ts +58 -0
- package/src/server/importers/ynab4-types.ts +163 -0
- package/src/server/importers/ynab4.ts +470 -0
- package/src/server/importers/ynab5-types.ts +290 -0
- package/src/server/importers/ynab5.ts +1193 -0
- package/src/server/main-app.ts +25 -0
- package/src/server/main.test.ts +392 -0
- package/src/server/main.ts +336 -0
- package/src/server/migrate/__snapshots__/migrations.test.ts.snap +17 -0
- package/src/server/migrate/cli.ts +100 -0
- package/src/server/migrate/migrations.test.ts +81 -0
- package/src/server/migrate/migrations.ts +192 -0
- package/src/server/models.ts +184 -0
- package/src/server/mutators.ts +139 -0
- package/src/server/notes/app.ts +18 -0
- package/src/server/payees/app.ts +351 -0
- package/src/server/polyfills.ts +26 -0
- package/src/server/post.ts +219 -0
- package/src/server/preferences/app.ts +249 -0
- package/src/server/prefs.ts +91 -0
- package/src/server/reports/app.ts +187 -0
- package/src/server/rules/action.ts +344 -0
- package/src/server/rules/app.ts +193 -0
- package/src/server/rules/condition.ts +436 -0
- package/src/server/rules/customFunctions.ts +61 -0
- package/src/server/rules/formula-action.test.ts +175 -0
- package/src/server/rules/handlebars-helpers.ts +131 -0
- package/src/server/rules/index.test.ts +1095 -0
- package/src/server/rules/index.ts +22 -0
- package/src/server/rules/rule-indexer.ts +89 -0
- package/src/server/rules/rule-utils.ts +274 -0
- package/src/server/rules/rule.ts +193 -0
- package/src/server/schedules/app.test.ts +502 -0
- package/src/server/schedules/app.ts +644 -0
- package/src/server/schedules/find-schedules.ts +391 -0
- package/src/server/server-config.ts +59 -0
- package/src/server/sheet.test.ts +101 -0
- package/src/server/sheet.ts +280 -0
- package/src/server/spreadsheet/__snapshots__/spreadsheet.test.ts.snap +5 -0
- package/src/server/spreadsheet/app.ts +54 -0
- package/src/server/spreadsheet/globals.ts +13 -0
- package/src/server/spreadsheet/graph-data-structure.ts +165 -0
- package/src/server/spreadsheet/scratch +60 -0
- package/src/server/spreadsheet/spreadsheet.test.ts +191 -0
- package/src/server/spreadsheet/spreadsheet.ts +523 -0
- package/src/server/spreadsheet/util.ts +15 -0
- package/src/server/sql/init.sql +88 -0
- package/src/server/sync/__snapshots__/sync.test.ts.snap +31 -0
- package/src/server/sync/app.ts +29 -0
- package/src/server/sync/encoder.ts +129 -0
- package/src/server/sync/index.ts +820 -0
- package/src/server/sync/make-test-message.ts +19 -0
- package/src/server/sync/migrate.test.ts +169 -0
- package/src/server/sync/migrate.ts +48 -0
- package/src/server/sync/repair.ts +39 -0
- package/src/server/sync/reset.ts +91 -0
- package/src/server/sync/sync.property.test.ts +385 -0
- package/src/server/sync/sync.test.ts +349 -0
- package/src/server/sync/utils.ts +3 -0
- package/src/server/tags/app.ts +101 -0
- package/src/server/tests/mockData.json +9352 -0
- package/src/server/tests/mockSyncServer.ts +119 -0
- package/src/server/tools/app.ts +152 -0
- package/src/server/transactions/__snapshots__/transaction-rules.test.ts.snap +173 -0
- package/src/server/transactions/__snapshots__/transfer.test.ts.snap +655 -0
- package/src/server/transactions/app.ts +136 -0
- package/src/server/transactions/export/export-to-csv.ts +132 -0
- package/src/server/transactions/import/__snapshots__/parse-file.test.ts.snap +1582 -0
- package/src/server/transactions/import/ofx2json.test.ts +33 -0
- package/src/server/transactions/import/ofx2json.ts +157 -0
- package/src/server/transactions/import/parse-file.test.ts +224 -0
- package/src/server/transactions/import/parse-file.ts +286 -0
- package/src/server/transactions/import/qif2json.ts +110 -0
- package/src/server/transactions/import/xmlcamt2json.ts +168 -0
- package/src/server/transactions/index.ts +196 -0
- package/src/server/transactions/merge.test.ts +370 -0
- package/src/server/transactions/merge.ts +139 -0
- package/src/server/transactions/transaction-rules.test.ts +994 -0
- package/src/server/transactions/transaction-rules.ts +1038 -0
- package/src/server/transactions/transfer.test.ts +221 -0
- package/src/server/transactions/transfer.ts +173 -0
- package/src/server/undo.ts +271 -0
- package/src/server/update.ts +37 -0
- package/src/server/util/budget-name.ts +61 -0
- package/src/server/util/custom-sync-mapping.ts +48 -0
- package/src/server/util/rschedule.ts +9 -0
- package/src/shared/__mocks__/platform.ts +7 -0
- package/src/shared/__snapshots__/months.test.ts.snap +21 -0
- package/src/shared/arithmetic.test.ts +112 -0
- package/src/shared/arithmetic.ts +170 -0
- package/src/shared/async.test.ts +135 -0
- package/src/shared/async.ts +76 -0
- package/src/shared/constants.ts +5 -0
- package/src/shared/currencies.ts +70 -0
- package/src/shared/dashboard.ts +260 -0
- package/src/shared/environment.ts +18 -0
- package/src/shared/errors.ts +195 -0
- package/src/shared/locale.ts +27 -0
- package/src/shared/location-utils.test.ts +69 -0
- package/src/shared/location-utils.ts +49 -0
- package/src/shared/months.test.ts +5 -0
- package/src/shared/months.ts +485 -0
- package/src/shared/normalisation.ts +6 -0
- package/src/shared/platform.electron.ts +21 -0
- package/src/shared/platform.ts +20 -0
- package/src/shared/query.ts +176 -0
- package/src/shared/rules.test.ts +56 -0
- package/src/shared/rules.ts +371 -0
- package/src/shared/schedules.test.ts +570 -0
- package/src/shared/schedules.ts +560 -0
- package/src/shared/test-helpers.ts +156 -0
- package/src/shared/transactions.test.ts +275 -0
- package/src/shared/transactions.ts +433 -0
- package/src/shared/transfer.test.ts +75 -0
- package/src/shared/transfer.ts +16 -0
- package/src/shared/user.ts +4 -0
- package/src/shared/util.test.ts +240 -0
- package/src/shared/util.ts +633 -0
- package/src/types/api-handlers.ts +287 -0
- package/src/types/budget.ts +8 -0
- package/src/types/file.ts +47 -0
- package/src/types/handlers.ts +46 -0
- package/src/types/models/account.ts +24 -0
- package/src/types/models/bank-sync.ts +23 -0
- package/src/types/models/bank.ts +6 -0
- package/src/types/models/category-group.ts +11 -0
- package/src/types/models/category.ts +13 -0
- package/src/types/models/dashboard.ts +199 -0
- package/src/types/models/gocardless.ts +84 -0
- package/src/types/models/import-transaction.ts +56 -0
- package/src/types/models/index.ts +23 -0
- package/src/types/models/nearby-payee.ts +7 -0
- package/src/types/models/note.ts +4 -0
- package/src/types/models/openid.ts +8 -0
- package/src/types/models/payee-location.ts +8 -0
- package/src/types/models/payee.ts +10 -0
- package/src/types/models/pluggyai.ts +19 -0
- package/src/types/models/reports.ts +144 -0
- package/src/types/models/rule.ts +174 -0
- package/src/types/models/schedule.ts +49 -0
- package/src/types/models/simplefin.ts +28 -0
- package/src/types/models/tags.ts +6 -0
- package/src/types/models/templates.ts +135 -0
- package/src/types/models/transaction-filter.ts +9 -0
- package/src/types/models/transaction.ts +39 -0
- package/src/types/models/user-access.ts +10 -0
- package/src/types/models/user.ts +25 -0
- package/src/types/prefs.ts +167 -0
- package/src/types/server-events.ts +86 -0
- package/src/types/server-handlers.ts +27 -0
- package/src/types/util.ts +26 -0
- package/tsconfig.json +34 -0
- package/typings/pegjs.ts +1 -0
- package/typings/process-worker.ts +12 -0
- package/typings/vite-plugin-peggy-loader.ts +1 -0
- package/typings/window.ts +62 -0
- package/vite.config.ts +109 -0
- package/vite.desktop.config.ts +59 -0
- package/vitest.config.ts +43 -0
- package/vitest.web.config.ts +38 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
// @ts-strict-ignore
|
|
2
|
+
import * as monthUtils from '../../shared/months';
|
|
3
|
+
import * as db from '../db';
|
|
4
|
+
import * as sheet from '../sheet';
|
|
5
|
+
|
|
6
|
+
import { createAllBudgets } from './base';
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
return global.emptyDatabase()();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('Base budget', () => {
|
|
13
|
+
it('Recomputes budget cells when account fields change', async () => {
|
|
14
|
+
await sheet.loadSpreadsheet(db);
|
|
15
|
+
|
|
16
|
+
await db.insertCategoryGroup({ id: 'group1', name: 'group1' });
|
|
17
|
+
await db.insertCategoryGroup({
|
|
18
|
+
id: 'group2',
|
|
19
|
+
name: 'income',
|
|
20
|
+
is_income: 1,
|
|
21
|
+
});
|
|
22
|
+
const catId = await db.insertCategory({
|
|
23
|
+
name: 'foo',
|
|
24
|
+
cat_group: 'group1',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await createAllBudgets();
|
|
28
|
+
|
|
29
|
+
// Insert a transaction referencing an account that doesn't exist
|
|
30
|
+
// yet
|
|
31
|
+
await db.insertTransaction({
|
|
32
|
+
date: '2016-12-15',
|
|
33
|
+
amount: -5000,
|
|
34
|
+
account: '29eef937-9933-49ef-80d9-71627074cf31',
|
|
35
|
+
category: catId,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Make sure that the spreadsheet finishes processing to make sure
|
|
39
|
+
// the next change doesn't get batched in with it
|
|
40
|
+
await sheet.waitOnSpreadsheet();
|
|
41
|
+
|
|
42
|
+
// The category should have nothing spent on it yet
|
|
43
|
+
expect(
|
|
44
|
+
sheet.getCellValue(
|
|
45
|
+
monthUtils.sheetForMonth('2016-12'),
|
|
46
|
+
`sum-amount-${catId}`,
|
|
47
|
+
),
|
|
48
|
+
).toBe(0);
|
|
49
|
+
|
|
50
|
+
// Create the referenced account
|
|
51
|
+
await db.insertAccount({
|
|
52
|
+
id: '29eef937-9933-49ef-80d9-71627074cf31',
|
|
53
|
+
name: 'foo',
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Make sure the spreadsheet finishes processing
|
|
57
|
+
await sheet.waitOnSpreadsheet();
|
|
58
|
+
|
|
59
|
+
// The category should see the transaction
|
|
60
|
+
expect(
|
|
61
|
+
sheet.getCellValue(
|
|
62
|
+
monthUtils.sheetForMonth('2016-12'),
|
|
63
|
+
`sum-amount-${catId}`,
|
|
64
|
+
),
|
|
65
|
+
).toBe(-5000);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('Excludes hidden categories from group totals in Report Budget', async () => {
|
|
69
|
+
await sheet.loadSpreadsheet(db);
|
|
70
|
+
sheet.get().meta().budgetType = 'tracking';
|
|
71
|
+
|
|
72
|
+
// Create a group with multiple categories
|
|
73
|
+
await db.insertCategoryGroup({ id: 'group1', name: 'Test Group' });
|
|
74
|
+
await db.insertCategoryGroup({
|
|
75
|
+
id: 'group2',
|
|
76
|
+
name: 'Income',
|
|
77
|
+
is_income: 1,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const visibleCatId = await db.insertCategory({
|
|
81
|
+
name: 'Visible Category',
|
|
82
|
+
cat_group: 'group1',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const hiddenCatId = await db.insertCategory({
|
|
86
|
+
name: 'Hidden Category',
|
|
87
|
+
cat_group: 'group1',
|
|
88
|
+
hidden: 1,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
await createAllBudgets();
|
|
92
|
+
const month = '2017-01';
|
|
93
|
+
const sheetName = monthUtils.sheetForMonth(month);
|
|
94
|
+
|
|
95
|
+
await db.insertAccount({ id: 'account1', name: 'Account 1' });
|
|
96
|
+
|
|
97
|
+
await db.insertTransaction({
|
|
98
|
+
date: '2017-01-15',
|
|
99
|
+
amount: -1000,
|
|
100
|
+
account: 'account1',
|
|
101
|
+
category: visibleCatId,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
await db.insertTransaction({
|
|
105
|
+
date: '2017-01-15',
|
|
106
|
+
amount: -2000,
|
|
107
|
+
account: 'account1',
|
|
108
|
+
category: hiddenCatId,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await sheet.waitOnSpreadsheet();
|
|
112
|
+
|
|
113
|
+
// Verify individual category amounts
|
|
114
|
+
expect(sheet.getCellValue(sheetName, `sum-amount-${visibleCatId}`)).toBe(
|
|
115
|
+
-1000,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
expect(sheet.getCellValue(sheetName, `sum-amount-${hiddenCatId}`)).toBe(
|
|
119
|
+
-2000,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// Verify group total only includes visible category
|
|
123
|
+
expect(sheet.getCellValue(sheetName, `group-sum-amount-group1`)).toBe(
|
|
124
|
+
-1000,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// Now toggle hidden status of the hidden category to make it visible
|
|
128
|
+
await db.updateCategory({
|
|
129
|
+
id: hiddenCatId,
|
|
130
|
+
name: 'Hidden Category',
|
|
131
|
+
cat_group: 'group1',
|
|
132
|
+
is_income: 0,
|
|
133
|
+
hidden: 0,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
await sheet.waitOnSpreadsheet();
|
|
137
|
+
|
|
138
|
+
// After making hidden category visible, group total should include both
|
|
139
|
+
expect(sheet.getCellValue(sheetName, `group-sum-amount-group1`)).toBe(
|
|
140
|
+
-3000,
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('Excludes hidden category groups from budget totals in Report Budget', async () => {
|
|
145
|
+
await sheet.loadSpreadsheet(db);
|
|
146
|
+
sheet.get().meta().budgetType = 'tracking';
|
|
147
|
+
|
|
148
|
+
// Create two expense groups - one visible, one hidden
|
|
149
|
+
await db.insertCategoryGroup({
|
|
150
|
+
id: 'visible-group',
|
|
151
|
+
name: 'Visible Group',
|
|
152
|
+
});
|
|
153
|
+
await db.insertCategoryGroup({
|
|
154
|
+
id: 'hidden-group',
|
|
155
|
+
name: 'Hidden Group',
|
|
156
|
+
hidden: 1,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await db.insertCategoryGroup({
|
|
160
|
+
id: 'income-group',
|
|
161
|
+
name: 'Income',
|
|
162
|
+
is_income: 1,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const visibleGroupCatId = await db.insertCategory({
|
|
166
|
+
name: 'Visible Group Category',
|
|
167
|
+
cat_group: 'visible-group',
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const hiddenGroupCatId = await db.insertCategory({
|
|
171
|
+
name: 'Hidden Group Category',
|
|
172
|
+
cat_group: 'hidden-group',
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
await createAllBudgets();
|
|
176
|
+
const month = '2017-01';
|
|
177
|
+
const sheetName = monthUtils.sheetForMonth(month);
|
|
178
|
+
|
|
179
|
+
await db.insertAccount({ id: 'account1', name: 'Account 1' });
|
|
180
|
+
|
|
181
|
+
await db.insertTransaction({
|
|
182
|
+
date: '2017-01-15',
|
|
183
|
+
amount: -1000,
|
|
184
|
+
account: 'account1',
|
|
185
|
+
category: visibleGroupCatId,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
await db.insertTransaction({
|
|
189
|
+
date: '2017-01-15',
|
|
190
|
+
amount: -2000,
|
|
191
|
+
account: 'account1',
|
|
192
|
+
category: hiddenGroupCatId,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
await sheet.waitOnSpreadsheet();
|
|
196
|
+
|
|
197
|
+
// Verify individual amounts
|
|
198
|
+
expect(
|
|
199
|
+
sheet.getCellValue(sheetName, `sum-amount-${visibleGroupCatId}`),
|
|
200
|
+
).toBe(-1000);
|
|
201
|
+
|
|
202
|
+
expect(
|
|
203
|
+
sheet.getCellValue(sheetName, `sum-amount-${hiddenGroupCatId}`),
|
|
204
|
+
).toBe(-2000);
|
|
205
|
+
|
|
206
|
+
expect(
|
|
207
|
+
sheet.getCellValue(sheetName, `group-sum-amount-visible-group`),
|
|
208
|
+
).toBe(-1000);
|
|
209
|
+
|
|
210
|
+
expect(sheet.getCellValue(sheetName, `group-sum-amount-hidden-group`)).toBe(
|
|
211
|
+
-2000,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
// Verify total spent only includes visible group
|
|
215
|
+
expect(sheet.getCellValue(sheetName, 'total-spent')).toBe(-1000);
|
|
216
|
+
|
|
217
|
+
// Now toggle hidden status of the hidden group to make it visible
|
|
218
|
+
await db.updateCategoryGroup({
|
|
219
|
+
id: 'hidden-group',
|
|
220
|
+
name: 'Hidden Group',
|
|
221
|
+
is_income: 0,
|
|
222
|
+
hidden: 0,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
await sheet.waitOnSpreadsheet();
|
|
226
|
+
|
|
227
|
+
// After making hidden group visible, total should include both
|
|
228
|
+
expect(sheet.getCellValue(sheetName, 'total-spent')).toBe(-3000);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('Includes hidden categories in group totals for Rollover Budget', async () => {
|
|
232
|
+
await sheet.loadSpreadsheet(db);
|
|
233
|
+
// Rollover is the default, but explicit for clarity
|
|
234
|
+
sheet.get().meta().budgetType = 'envelope';
|
|
235
|
+
|
|
236
|
+
// Create a group with multiple categories
|
|
237
|
+
await db.insertCategoryGroup({ id: 'group1', name: 'Test Group' });
|
|
238
|
+
await db.insertCategoryGroup({
|
|
239
|
+
id: 'group2',
|
|
240
|
+
name: 'Income',
|
|
241
|
+
is_income: 1,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const visibleCatId = await db.insertCategory({
|
|
245
|
+
name: 'Visible Category',
|
|
246
|
+
cat_group: 'group1',
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const hiddenCatId = await db.insertCategory({
|
|
250
|
+
name: 'Hidden Category',
|
|
251
|
+
cat_group: 'group1',
|
|
252
|
+
hidden: 1,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
await createAllBudgets();
|
|
256
|
+
const month = '2017-01';
|
|
257
|
+
const sheetName = monthUtils.sheetForMonth(month);
|
|
258
|
+
|
|
259
|
+
await db.insertAccount({ id: 'account1', name: 'Account 1' });
|
|
260
|
+
|
|
261
|
+
await db.insertTransaction({
|
|
262
|
+
date: '2017-01-15',
|
|
263
|
+
amount: -1000,
|
|
264
|
+
account: 'account1',
|
|
265
|
+
category: visibleCatId,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
await db.insertTransaction({
|
|
269
|
+
date: '2017-01-15',
|
|
270
|
+
amount: -2000,
|
|
271
|
+
account: 'account1',
|
|
272
|
+
category: hiddenCatId,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
await sheet.waitOnSpreadsheet();
|
|
276
|
+
|
|
277
|
+
// Verify group total includes both visible and hidden category amounts
|
|
278
|
+
expect(sheet.getCellValue(sheetName, `group-sum-amount-group1`)).toBe(
|
|
279
|
+
-3000,
|
|
280
|
+
);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('Includes hidden category groups in budget totals for Rollover Budget', async () => {
|
|
284
|
+
await sheet.loadSpreadsheet(db);
|
|
285
|
+
// Rollover is the default, but explicit for clarity
|
|
286
|
+
sheet.get().meta().budgetType = 'envelope';
|
|
287
|
+
|
|
288
|
+
// Create two expense groups - one visible, one hidden
|
|
289
|
+
await db.insertCategoryGroup({
|
|
290
|
+
id: 'visible-group',
|
|
291
|
+
name: 'Visible Group',
|
|
292
|
+
});
|
|
293
|
+
await db.insertCategoryGroup({
|
|
294
|
+
id: 'hidden-group',
|
|
295
|
+
name: 'Hidden Group',
|
|
296
|
+
hidden: 1,
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
await db.insertCategoryGroup({
|
|
300
|
+
id: 'income-group',
|
|
301
|
+
name: 'Income',
|
|
302
|
+
is_income: 1,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const visibleGroupCatId = await db.insertCategory({
|
|
306
|
+
name: 'Visible Group Category',
|
|
307
|
+
cat_group: 'visible-group',
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const hiddenGroupCatId = await db.insertCategory({
|
|
311
|
+
name: 'Hidden Group Category',
|
|
312
|
+
cat_group: 'hidden-group',
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
await createAllBudgets();
|
|
316
|
+
const month = '2017-01';
|
|
317
|
+
const sheetName = monthUtils.sheetForMonth(month);
|
|
318
|
+
|
|
319
|
+
await db.insertAccount({ id: 'account1', name: 'Account 1' });
|
|
320
|
+
|
|
321
|
+
await db.insertTransaction({
|
|
322
|
+
date: '2017-01-15',
|
|
323
|
+
amount: -1000,
|
|
324
|
+
account: 'account1',
|
|
325
|
+
category: visibleGroupCatId,
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
await db.insertTransaction({
|
|
329
|
+
date: '2017-01-15',
|
|
330
|
+
amount: -2000,
|
|
331
|
+
account: 'account1',
|
|
332
|
+
category: hiddenGroupCatId,
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
await sheet.waitOnSpreadsheet();
|
|
336
|
+
|
|
337
|
+
// Verify total spent includes both visible and hidden group amounts
|
|
338
|
+
expect(sheet.getCellValue(sheetName, 'total-spent')).toBe(-3000);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
// @ts-strict-ignore
|
|
2
|
+
import * as monthUtils from '../../shared/months';
|
|
3
|
+
import { q } from '../../shared/query';
|
|
4
|
+
import { getChangedValues } from '../../shared/util';
|
|
5
|
+
import type { CategoryGroupEntity } from '../../types/models';
|
|
6
|
+
import { aqlQuery } from '../aql';
|
|
7
|
+
import * as db from '../db';
|
|
8
|
+
import * as sheet from '../sheet';
|
|
9
|
+
import { resolveName } from '../spreadsheet/util';
|
|
10
|
+
|
|
11
|
+
import * as budgetActions from './actions';
|
|
12
|
+
import * as envelopeBudget from './envelope';
|
|
13
|
+
import * as report from './report';
|
|
14
|
+
|
|
15
|
+
export function getBudgetType() {
|
|
16
|
+
const meta = sheet.get().meta();
|
|
17
|
+
return meta.budgetType || 'envelope';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getBudgetRange(start: string, end: string) {
|
|
21
|
+
start = monthUtils.getMonth(start);
|
|
22
|
+
end = monthUtils.getMonth(end);
|
|
23
|
+
|
|
24
|
+
// The start date should never be after the end date. If that
|
|
25
|
+
// happened, the month range might be a valid range and weird
|
|
26
|
+
// things happen
|
|
27
|
+
if (start > end) {
|
|
28
|
+
start = end;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Budgets should exist 3 months before the earliest needed date
|
|
32
|
+
// (either the oldest transaction or the current month if no
|
|
33
|
+
// transactions yet), and a year from the current date. There's no
|
|
34
|
+
// need to ever have budgets outside that range.
|
|
35
|
+
start = monthUtils.subMonths(start, 3);
|
|
36
|
+
end = monthUtils.addMonths(end, 12);
|
|
37
|
+
|
|
38
|
+
return { start, end, range: monthUtils.rangeInclusive(start, end) };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function createCategory(cat, sheetName, prevSheetName, start, end) {
|
|
42
|
+
sheet.get().createDynamic(sheetName, 'sum-amount-' + cat.id, {
|
|
43
|
+
initialValue: 0,
|
|
44
|
+
run: () => {
|
|
45
|
+
// Making this sync is faster!
|
|
46
|
+
const rows = db.runQuery<{ amount: number }>(
|
|
47
|
+
`SELECT SUM(amount) as amount FROM v_transactions_internal_alive t
|
|
48
|
+
LEFT JOIN accounts a ON a.id = t.account
|
|
49
|
+
WHERE t.date >= ${start} AND t.date <= ${end}
|
|
50
|
+
AND category = '${cat.id}' AND a.offbudget = 0`,
|
|
51
|
+
[],
|
|
52
|
+
true,
|
|
53
|
+
);
|
|
54
|
+
const row = rows[0];
|
|
55
|
+
const amount = row ? row.amount : 0;
|
|
56
|
+
return amount || 0;
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (getBudgetType() === 'envelope') {
|
|
61
|
+
envelopeBudget.createCategory(cat, sheetName, prevSheetName);
|
|
62
|
+
} else {
|
|
63
|
+
void report.createCategory(cat, sheetName, prevSheetName);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function handleAccountChange(months, oldValue, newValue) {
|
|
68
|
+
if (!oldValue || oldValue.offbudget !== newValue.offbudget) {
|
|
69
|
+
const rows = db.runQuery<Pick<db.DbTransaction, 'category'>>(
|
|
70
|
+
`
|
|
71
|
+
SELECT DISTINCT(category) as category FROM transactions
|
|
72
|
+
WHERE acct = ?
|
|
73
|
+
`,
|
|
74
|
+
[newValue.id],
|
|
75
|
+
true,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
months.forEach(month => {
|
|
79
|
+
const sheetName = monthUtils.sheetForMonth(month);
|
|
80
|
+
|
|
81
|
+
rows.forEach(row => {
|
|
82
|
+
sheet
|
|
83
|
+
.get()
|
|
84
|
+
.recompute(resolveName(sheetName, 'sum-amount-' + row.category));
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function handleTransactionChange(transaction, changedFields) {
|
|
91
|
+
if (
|
|
92
|
+
(changedFields.has('date') ||
|
|
93
|
+
changedFields.has('acct') ||
|
|
94
|
+
changedFields.has('amount') ||
|
|
95
|
+
changedFields.has('category') ||
|
|
96
|
+
changedFields.has('tombstone') ||
|
|
97
|
+
changedFields.has('isParent')) &&
|
|
98
|
+
transaction.date &&
|
|
99
|
+
transaction.category
|
|
100
|
+
) {
|
|
101
|
+
const month = monthUtils.monthFromDate(db.fromDateRepr(transaction.date));
|
|
102
|
+
const sheetName = monthUtils.sheetForMonth(month);
|
|
103
|
+
|
|
104
|
+
sheet
|
|
105
|
+
.get()
|
|
106
|
+
.recompute(resolveName(sheetName, 'sum-amount-' + transaction.category));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function handleCategoryMappingChange(months, oldValue, newValue) {
|
|
111
|
+
months.forEach(month => {
|
|
112
|
+
const sheetName = monthUtils.sheetForMonth(month);
|
|
113
|
+
if (oldValue) {
|
|
114
|
+
sheet
|
|
115
|
+
.get()
|
|
116
|
+
.recompute(resolveName(sheetName, 'sum-amount-' + oldValue.transferId));
|
|
117
|
+
}
|
|
118
|
+
sheet
|
|
119
|
+
.get()
|
|
120
|
+
.recompute(resolveName(sheetName, 'sum-amount-' + newValue.transferId));
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function handleBudgetMonthChange(budget) {
|
|
125
|
+
const sheetName = monthUtils.sheetForMonth(budget.id);
|
|
126
|
+
sheet.get().set(`${sheetName}!buffered`, budget.buffered);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function handleBudgetChange(budget) {
|
|
130
|
+
if (budget.category) {
|
|
131
|
+
const sheetName = monthUtils.sheetForMonth(budget.month.toString());
|
|
132
|
+
sheet
|
|
133
|
+
.get()
|
|
134
|
+
.set(`${sheetName}!budget-${budget.category}`, budget.amount || 0);
|
|
135
|
+
sheet
|
|
136
|
+
.get()
|
|
137
|
+
.set(
|
|
138
|
+
`${sheetName}!carryover-${budget.category}`,
|
|
139
|
+
budget.carryover === 1 ? true : false,
|
|
140
|
+
);
|
|
141
|
+
sheet.get().set(`${sheetName}!goal-${budget.category}`, budget.goal);
|
|
142
|
+
sheet
|
|
143
|
+
.get()
|
|
144
|
+
.set(`${sheetName}!long-goal-${budget.category}`, budget.long_goal);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function triggerBudgetChanges(oldValues, newValues) {
|
|
149
|
+
const { createdMonths = new Set() } = sheet.get().meta();
|
|
150
|
+
const budgetType = getBudgetType();
|
|
151
|
+
sheet.startTransaction();
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
newValues.forEach((items, table) => {
|
|
155
|
+
const old = oldValues.get(table);
|
|
156
|
+
|
|
157
|
+
items.forEach(newValue => {
|
|
158
|
+
const oldValue = old && old.get(newValue.id);
|
|
159
|
+
|
|
160
|
+
if (table === 'zero_budget_months') {
|
|
161
|
+
handleBudgetMonthChange(newValue);
|
|
162
|
+
} else if (table === 'zero_budgets' || table === 'reflect_budgets') {
|
|
163
|
+
handleBudgetChange(newValue);
|
|
164
|
+
} else if (table === 'transactions') {
|
|
165
|
+
const changed = new Set(
|
|
166
|
+
Object.keys(getChangedValues(oldValue || {}, newValue) || {}),
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
if (oldValue) {
|
|
170
|
+
handleTransactionChange(oldValue, changed);
|
|
171
|
+
}
|
|
172
|
+
handleTransactionChange(newValue, changed);
|
|
173
|
+
} else if (table === 'category_mapping') {
|
|
174
|
+
handleCategoryMappingChange(createdMonths, oldValue, newValue);
|
|
175
|
+
} else if (table === 'categories') {
|
|
176
|
+
if (budgetType === 'envelope') {
|
|
177
|
+
envelopeBudget.handleCategoryChange(
|
|
178
|
+
createdMonths,
|
|
179
|
+
oldValue,
|
|
180
|
+
newValue,
|
|
181
|
+
);
|
|
182
|
+
} else {
|
|
183
|
+
report.handleCategoryChange(createdMonths, oldValue, newValue);
|
|
184
|
+
}
|
|
185
|
+
} else if (table === 'category_groups') {
|
|
186
|
+
if (budgetType === 'envelope') {
|
|
187
|
+
envelopeBudget.handleCategoryGroupChange(
|
|
188
|
+
createdMonths,
|
|
189
|
+
oldValue,
|
|
190
|
+
newValue,
|
|
191
|
+
);
|
|
192
|
+
} else {
|
|
193
|
+
report.handleCategoryGroupChange(createdMonths, oldValue, newValue);
|
|
194
|
+
}
|
|
195
|
+
} else if (table === 'accounts') {
|
|
196
|
+
handleAccountChange(createdMonths, oldValue, newValue);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
} finally {
|
|
201
|
+
sheet.endTransaction();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export async function doTransfer(categoryIds, transferId) {
|
|
206
|
+
const { createdMonths: months } = sheet.get().meta();
|
|
207
|
+
|
|
208
|
+
[...months].forEach(month => {
|
|
209
|
+
const totalValue = categoryIds
|
|
210
|
+
.map(id => {
|
|
211
|
+
return budgetActions.getBudget({ month, category: id });
|
|
212
|
+
})
|
|
213
|
+
.reduce((total, value) => total + value, 0);
|
|
214
|
+
|
|
215
|
+
const transferValue = budgetActions.getBudget({
|
|
216
|
+
month,
|
|
217
|
+
category: transferId,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
void budgetActions.setBudget({
|
|
221
|
+
month,
|
|
222
|
+
category: transferId,
|
|
223
|
+
amount: totalValue + transferValue,
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export async function createBudget(months) {
|
|
229
|
+
const { data: groups }: { data: CategoryGroupEntity[] } = await aqlQuery(
|
|
230
|
+
q('category_groups').select('*'),
|
|
231
|
+
);
|
|
232
|
+
const categories = groups.flatMap(group => group.categories);
|
|
233
|
+
|
|
234
|
+
sheet.startTransaction();
|
|
235
|
+
const meta = sheet.get().meta();
|
|
236
|
+
meta.createdMonths = meta.createdMonths || new Set();
|
|
237
|
+
|
|
238
|
+
const budgetType = getBudgetType();
|
|
239
|
+
|
|
240
|
+
if (budgetType === 'envelope') {
|
|
241
|
+
envelopeBudget.createBudget(meta, categories, months);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
months.forEach(month => {
|
|
245
|
+
if (!meta.createdMonths.has(month)) {
|
|
246
|
+
const prevMonth = monthUtils.prevMonth(month);
|
|
247
|
+
const { start, end } = monthUtils.bounds(month);
|
|
248
|
+
const sheetName = monthUtils.sheetForMonth(month);
|
|
249
|
+
const prevSheetName = monthUtils.sheetForMonth(prevMonth);
|
|
250
|
+
|
|
251
|
+
categories.forEach(cat => {
|
|
252
|
+
createCategory(cat, sheetName, prevSheetName, start, end);
|
|
253
|
+
});
|
|
254
|
+
groups.forEach(group => {
|
|
255
|
+
if (budgetType === 'envelope') {
|
|
256
|
+
envelopeBudget.createCategoryGroup(group, sheetName);
|
|
257
|
+
} else {
|
|
258
|
+
report.createCategoryGroup(group, sheetName);
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
if (budgetType === 'envelope') {
|
|
263
|
+
envelopeBudget.createSummary(
|
|
264
|
+
groups,
|
|
265
|
+
categories,
|
|
266
|
+
prevSheetName,
|
|
267
|
+
sheetName,
|
|
268
|
+
);
|
|
269
|
+
} else {
|
|
270
|
+
report.createSummary(groups, sheetName);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
meta.createdMonths.add(month);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
sheet.get().setMeta(meta);
|
|
278
|
+
sheet.endTransaction();
|
|
279
|
+
|
|
280
|
+
// Wait for the spreadsheet to finish computing. Normally this won't
|
|
281
|
+
// do anything (as values are cached) but on first run this need to
|
|
282
|
+
// show the loading screen while it initially sets up.
|
|
283
|
+
await sheet.waitOnSpreadsheet();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export async function createAllBudgets() {
|
|
287
|
+
const earliestTransaction = await db.first<db.DbTransaction>(
|
|
288
|
+
'SELECT * FROM transactions WHERE isChild=0 AND date IS NOT NULL ORDER BY date ASC LIMIT 1',
|
|
289
|
+
);
|
|
290
|
+
const earliestDate =
|
|
291
|
+
earliestTransaction && db.fromDateRepr(earliestTransaction.date);
|
|
292
|
+
const currentMonth = monthUtils.currentMonth();
|
|
293
|
+
|
|
294
|
+
// Get the range based off of the earliest transaction and the
|
|
295
|
+
// current month. If no transactions currently exist the current
|
|
296
|
+
// month is also used as the starting month
|
|
297
|
+
const { start, end, range } = getBudgetRange(
|
|
298
|
+
earliestDate || currentMonth,
|
|
299
|
+
currentMonth,
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
const meta = sheet.get().meta();
|
|
303
|
+
const createdMonths = meta.createdMonths || new Set();
|
|
304
|
+
const newMonths = range.filter(m => !createdMonths.has(m));
|
|
305
|
+
|
|
306
|
+
if (newMonths.length > 0) {
|
|
307
|
+
await createBudget(range);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return { start, end };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export async function setType(type) {
|
|
314
|
+
const meta = sheet.get().meta();
|
|
315
|
+
if (type === meta.budgetType) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
meta.budgetType = type;
|
|
320
|
+
meta.createdMonths = new Set();
|
|
321
|
+
|
|
322
|
+
// Go through and force all the cells to be recomputed
|
|
323
|
+
const nodes = sheet.get().getNodes();
|
|
324
|
+
db.transaction(() => {
|
|
325
|
+
for (const name of nodes.keys()) {
|
|
326
|
+
const [sheetName, cellName] = name.split('!');
|
|
327
|
+
if (sheetName.match(/^budget\d+/)) {
|
|
328
|
+
sheet.get().deleteCell(sheetName, cellName);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
sheet.get().startCacheBarrier();
|
|
334
|
+
void sheet.loadUserBudgets(db);
|
|
335
|
+
const bounds = await createAllBudgets();
|
|
336
|
+
sheet.get().endCacheBarrier();
|
|
337
|
+
|
|
338
|
+
return bounds;
|
|
339
|
+
}
|