@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.
Files changed (358) hide show
  1. package/.swcrc +11 -0
  2. package/bin/build-browser +40 -0
  3. package/bin/copy-migrations +9 -0
  4. package/db.sqlite +0 -0
  5. package/default-db.sqlite +0 -0
  6. package/migrations/.force-copy-windows +0 -0
  7. package/migrations/1548957970627_remove-db-version.sql +5 -0
  8. package/migrations/1550601598648_payees.sql +23 -0
  9. package/migrations/1555786194328_remove_category_group_unique.sql +25 -0
  10. package/migrations/1561751833510_indexes.sql +7 -0
  11. package/migrations/1567699552727_budget.sql +38 -0
  12. package/migrations/1582384163573_cleared.sql +6 -0
  13. package/migrations/1597756566448_rules.sql +10 -0
  14. package/migrations/1608652596043_parent_field.sql +13 -0
  15. package/migrations/1608652596044_trans_views.sql +56 -0
  16. package/migrations/1612625548236_optimize.sql +7 -0
  17. package/migrations/1614782639336_trans_views2.sql +33 -0
  18. package/migrations/1615745967948_meta.sql +10 -0
  19. package/migrations/1616167010796_accounts_order.sql +5 -0
  20. package/migrations/1618975177358_schedules.sql +28 -0
  21. package/migrations/1632571489012_remove_cache.js +136 -0
  22. package/migrations/1679728867040_rules_conditions.sql +5 -0
  23. package/migrations/1681115033845_add_schedule_name.sql +5 -0
  24. package/migrations/1682974838138_remove_payee_rules.sql +5 -0
  25. package/migrations/1685007876842_add_category_hidden.sql +6 -0
  26. package/migrations/1686139660866_remove_account_type.sql +5 -0
  27. package/migrations/1688749527273_transaction_filters.sql +10 -0
  28. package/migrations/1688841238000_add_account_type.sql +5 -0
  29. package/migrations/1691233396000_add_schedule_next_date_tombstone.sql +5 -0
  30. package/migrations/1694438752000_add_goal_targets.sql +7 -0
  31. package/migrations/1697046240000_add_reconciled.sql +5 -0
  32. package/migrations/1704572023730_add_account_sync_source.sql +5 -0
  33. package/migrations/1704572023731_add_missing_goCardless_sync_source.sql +9 -0
  34. package/migrations/1707267033000_reports.sql +28 -0
  35. package/migrations/1712784523000_unhide_input_group.sql +8 -0
  36. package/migrations/1716359441000_include_current.sql +5 -0
  37. package/migrations/1720310586000_link_transfer_schedules.sql +19 -0
  38. package/migrations/1720664867241_add_payee_favorite.sql +5 -0
  39. package/migrations/1720665000000_goal_context.sql +6 -0
  40. package/migrations/1722717601000_reports_move_selected_categories.js +55 -0
  41. package/migrations/1722804019000_create_dashboard_table.js +69 -0
  42. package/migrations/1723665565000_prefs.js +59 -0
  43. package/migrations/1730744182000_fix_dashboard_table.sql +7 -0
  44. package/migrations/1736640000000_custom_report_sorting.sql +7 -0
  45. package/migrations/1737158400000_add_learn_categories_to_payees.sql +5 -0
  46. package/migrations/1738491452000_sorting_rename.sql +13 -0
  47. package/migrations/1739139550000_bank_sync_page.sql +7 -0
  48. package/migrations/1740506588539_add_last_reconciled_at.sql +5 -0
  49. package/migrations/1745425408000_update_budgetType_pref.sql +7 -0
  50. package/migrations/1749799110000_add_tags.sql +10 -0
  51. package/migrations/1749799110001_tags_tombstone.sql +5 -0
  52. package/migrations/1754611200000_add_category_template_settings.sql +5 -0
  53. package/migrations/1759260219000_add_trim_interval_report_setting.sql +6 -0
  54. package/migrations/1759842823172_add_isGlobal_to_preferences.sql +1 -0
  55. package/migrations/1762178745667_rename_csv_skip_lines_pref.sql +8 -0
  56. package/migrations/1765518577215_multiple_dashboards.js +30 -0
  57. package/migrations/1768872504000_add_payee_locations.sql +21 -0
  58. package/package.json +128 -0
  59. package/src/mocks/arbitrary-schema.ts +162 -0
  60. package/src/mocks/budget.ts +901 -0
  61. package/src/mocks/files/8859-1.qfx +63 -0
  62. package/src/mocks/files/best.data-ever$.QFX +124 -0
  63. package/src/mocks/files/big.data.QiF +91 -0
  64. package/src/mocks/files/budgets/.commit-to-git +0 -0
  65. package/src/mocks/files/camt/camt.053.payee-memo.xml +127 -0
  66. package/src/mocks/files/camt/camt.053.xml +463 -0
  67. package/src/mocks/files/credit-card.ofx +11 -0
  68. package/src/mocks/files/data-multi-decimal.ofx +64 -0
  69. package/src/mocks/files/data-payee-memo.ofx +75 -0
  70. package/src/mocks/files/data-payee-memo.qif +17 -0
  71. package/src/mocks/files/data.ofx +124 -0
  72. package/src/mocks/files/data.qfx +124 -0
  73. package/src/mocks/files/data.qif +91 -0
  74. package/src/mocks/files/default-budget-template/db.sqlite +0 -0
  75. package/src/mocks/files/default-budget-template/metadata.json +6 -0
  76. package/src/mocks/files/html-vals.qfx +17 -0
  77. package/src/mocks/index.ts +221 -0
  78. package/src/mocks/migrations/1508717984291_up_add-poop.sql +13 -0
  79. package/src/mocks/migrations/1508718036311_up_modify-poop.sql +2 -0
  80. package/src/mocks/migrations/1508727787513_remove-is_income.sql +15 -0
  81. package/src/mocks/random.ts +16 -0
  82. package/src/mocks/setup.ts +180 -0
  83. package/src/mocks/spreadsheet.ts +101 -0
  84. package/src/mocks/util.ts +82 -0
  85. package/src/platform/client/connection/README.md +3 -0
  86. package/src/platform/client/connection/__mocks__/index.ts +67 -0
  87. package/src/platform/client/connection/index-types.ts +95 -0
  88. package/src/platform/client/connection/index.browser.ts +213 -0
  89. package/src/platform/client/connection/index.ts +155 -0
  90. package/src/platform/client/undo/index.ts +59 -0
  91. package/src/platform/exceptions/__mocks__/index.ts +7 -0
  92. package/src/platform/exceptions/index.ts +9 -0
  93. package/src/platform/server/asyncStorage/__mocks__/index.ts +50 -0
  94. package/src/platform/server/asyncStorage/index-types.ts +35 -0
  95. package/src/platform/server/asyncStorage/index.api.ts +2 -0
  96. package/src/platform/server/asyncStorage/index.electron.ts +88 -0
  97. package/src/platform/server/asyncStorage/index.ts +126 -0
  98. package/src/platform/server/connection/README.md +3 -0
  99. package/src/platform/server/connection/__mocks__/index.ts +15 -0
  100. package/src/platform/server/connection/index-types.ts +20 -0
  101. package/src/platform/server/connection/index.api.ts +13 -0
  102. package/src/platform/server/connection/index.electron.ts +102 -0
  103. package/src/platform/server/connection/index.ts +154 -0
  104. package/src/platform/server/fetch/__mocks__/index.ts +3 -0
  105. package/src/platform/server/fetch/index.api.ts +1 -0
  106. package/src/platform/server/fetch/index.electron.ts +18 -0
  107. package/src/platform/server/fetch/index.ts +20 -0
  108. package/src/platform/server/fs/index.api.ts +198 -0
  109. package/src/platform/server/fs/index.electron.ts +208 -0
  110. package/src/platform/server/fs/index.test.ts +117 -0
  111. package/src/platform/server/fs/index.ts +416 -0
  112. package/src/platform/server/fs/path-join.api.ts +1 -0
  113. package/src/platform/server/fs/path-join.electron.ts +1 -0
  114. package/src/platform/server/fs/path-join.ts +97 -0
  115. package/src/platform/server/fs/shared.ts +33 -0
  116. package/src/platform/server/indexeddb/index.ts +115 -0
  117. package/src/platform/server/log/index.ts +43 -0
  118. package/src/platform/server/sqlite/index.api.ts +2 -0
  119. package/src/platform/server/sqlite/index.electron.ts +134 -0
  120. package/src/platform/server/sqlite/index.test.ts +108 -0
  121. package/src/platform/server/sqlite/index.ts +241 -0
  122. package/src/platform/server/sqlite/normalise.ts +9 -0
  123. package/src/platform/server/sqlite/unicodeLike.test.ts +58 -0
  124. package/src/platform/server/sqlite/unicodeLike.ts +31 -0
  125. package/src/server/__mocks__/post.ts +9 -0
  126. package/src/server/__snapshots__/main.test.ts.snap +199 -0
  127. package/src/server/__snapshots__/sheet.test.ts.snap +9 -0
  128. package/src/server/accounts/__snapshots__/sync.test.ts.snap +136 -0
  129. package/src/server/accounts/app-bank-sync.test.ts +136 -0
  130. package/src/server/accounts/app.ts +1294 -0
  131. package/src/server/accounts/link.ts +25 -0
  132. package/src/server/accounts/payees.ts +36 -0
  133. package/src/server/accounts/sync.test.ts +679 -0
  134. package/src/server/accounts/sync.ts +1168 -0
  135. package/src/server/accounts/title/index.ts +60 -0
  136. package/src/server/accounts/title/lower-case.ts +93 -0
  137. package/src/server/accounts/title/specials.ts +21 -0
  138. package/src/server/admin/app.ts +241 -0
  139. package/src/server/api-models.ts +244 -0
  140. package/src/server/api.test.ts +36 -0
  141. package/src/server/api.ts +1030 -0
  142. package/src/server/app.ts +91 -0
  143. package/src/server/aql/compiler.test.ts +966 -0
  144. package/src/server/aql/compiler.ts +1222 -0
  145. package/src/server/aql/exec.test.ts +289 -0
  146. package/src/server/aql/exec.ts +128 -0
  147. package/src/server/aql/index.ts +41 -0
  148. package/src/server/aql/schema/executors.test.ts +420 -0
  149. package/src/server/aql/schema/executors.ts +345 -0
  150. package/src/server/aql/schema/index.test.ts +67 -0
  151. package/src/server/aql/schema/index.ts +409 -0
  152. package/src/server/aql/schema-helpers.test.ts +242 -0
  153. package/src/server/aql/schema-helpers.ts +208 -0
  154. package/src/server/aql/views.test.ts +62 -0
  155. package/src/server/aql/views.ts +57 -0
  156. package/src/server/auth/app.ts +387 -0
  157. package/src/server/bench.ts +29 -0
  158. package/src/server/budget/actions.ts +686 -0
  159. package/src/server/budget/app.ts +469 -0
  160. package/src/server/budget/base.test.ts +340 -0
  161. package/src/server/budget/base.ts +339 -0
  162. package/src/server/budget/category-template-context.test.ts +1658 -0
  163. package/src/server/budget/category-template-context.ts +862 -0
  164. package/src/server/budget/cleanup-template.pegjs +27 -0
  165. package/src/server/budget/cleanup-template.ts +408 -0
  166. package/src/server/budget/envelope.ts +403 -0
  167. package/src/server/budget/goal-template.pegjs +110 -0
  168. package/src/server/budget/goal-template.ts +309 -0
  169. package/src/server/budget/report.ts +308 -0
  170. package/src/server/budget/schedule-template.test.ts +184 -0
  171. package/src/server/budget/schedule-template.ts +351 -0
  172. package/src/server/budget/statements.ts +60 -0
  173. package/src/server/budget/template-notes.test.ts +393 -0
  174. package/src/server/budget/template-notes.ts +323 -0
  175. package/src/server/budget/util.ts +25 -0
  176. package/src/server/budgetfiles/__snapshots__/backups.test.ts.snap +101 -0
  177. package/src/server/budgetfiles/app.ts +672 -0
  178. package/src/server/budgetfiles/backups.test.ts +79 -0
  179. package/src/server/budgetfiles/backups.ts +251 -0
  180. package/src/server/cloud-storage.ts +467 -0
  181. package/src/server/dashboard/app.ts +373 -0
  182. package/src/server/db/__snapshots__/index.test.ts.snap +271 -0
  183. package/src/server/db/index.test.ts +300 -0
  184. package/src/server/db/index.ts +855 -0
  185. package/src/server/db/mappings.ts +59 -0
  186. package/src/server/db/sort.ts +58 -0
  187. package/src/server/db/types/index.ts +342 -0
  188. package/src/server/db/util.ts +36 -0
  189. package/src/server/encryption/app.ts +133 -0
  190. package/src/server/encryption/encryption-internals.api.ts +2 -0
  191. package/src/server/encryption/encryption-internals.electron.ts +89 -0
  192. package/src/server/encryption/encryption-internals.ts +109 -0
  193. package/src/server/encryption/encryption.test.ts +19 -0
  194. package/src/server/encryption/index.test.ts +19 -0
  195. package/src/server/encryption/index.ts +89 -0
  196. package/src/server/errors.ts +110 -0
  197. package/src/server/filters/app.ts +191 -0
  198. package/src/server/importers/actual.ts +49 -0
  199. package/src/server/importers/index.ts +58 -0
  200. package/src/server/importers/ynab4-types.ts +163 -0
  201. package/src/server/importers/ynab4.ts +470 -0
  202. package/src/server/importers/ynab5-types.ts +290 -0
  203. package/src/server/importers/ynab5.ts +1193 -0
  204. package/src/server/main-app.ts +25 -0
  205. package/src/server/main.test.ts +392 -0
  206. package/src/server/main.ts +336 -0
  207. package/src/server/migrate/__snapshots__/migrations.test.ts.snap +17 -0
  208. package/src/server/migrate/cli.ts +100 -0
  209. package/src/server/migrate/migrations.test.ts +81 -0
  210. package/src/server/migrate/migrations.ts +192 -0
  211. package/src/server/models.ts +184 -0
  212. package/src/server/mutators.ts +139 -0
  213. package/src/server/notes/app.ts +18 -0
  214. package/src/server/payees/app.ts +351 -0
  215. package/src/server/polyfills.ts +26 -0
  216. package/src/server/post.ts +219 -0
  217. package/src/server/preferences/app.ts +249 -0
  218. package/src/server/prefs.ts +91 -0
  219. package/src/server/reports/app.ts +187 -0
  220. package/src/server/rules/action.ts +344 -0
  221. package/src/server/rules/app.ts +193 -0
  222. package/src/server/rules/condition.ts +436 -0
  223. package/src/server/rules/customFunctions.ts +61 -0
  224. package/src/server/rules/formula-action.test.ts +175 -0
  225. package/src/server/rules/handlebars-helpers.ts +131 -0
  226. package/src/server/rules/index.test.ts +1095 -0
  227. package/src/server/rules/index.ts +22 -0
  228. package/src/server/rules/rule-indexer.ts +89 -0
  229. package/src/server/rules/rule-utils.ts +274 -0
  230. package/src/server/rules/rule.ts +193 -0
  231. package/src/server/schedules/app.test.ts +502 -0
  232. package/src/server/schedules/app.ts +644 -0
  233. package/src/server/schedules/find-schedules.ts +391 -0
  234. package/src/server/server-config.ts +59 -0
  235. package/src/server/sheet.test.ts +101 -0
  236. package/src/server/sheet.ts +280 -0
  237. package/src/server/spreadsheet/__snapshots__/spreadsheet.test.ts.snap +5 -0
  238. package/src/server/spreadsheet/app.ts +54 -0
  239. package/src/server/spreadsheet/globals.ts +13 -0
  240. package/src/server/spreadsheet/graph-data-structure.ts +165 -0
  241. package/src/server/spreadsheet/scratch +60 -0
  242. package/src/server/spreadsheet/spreadsheet.test.ts +191 -0
  243. package/src/server/spreadsheet/spreadsheet.ts +523 -0
  244. package/src/server/spreadsheet/util.ts +15 -0
  245. package/src/server/sql/init.sql +88 -0
  246. package/src/server/sync/__snapshots__/sync.test.ts.snap +31 -0
  247. package/src/server/sync/app.ts +29 -0
  248. package/src/server/sync/encoder.ts +129 -0
  249. package/src/server/sync/index.ts +820 -0
  250. package/src/server/sync/make-test-message.ts +19 -0
  251. package/src/server/sync/migrate.test.ts +169 -0
  252. package/src/server/sync/migrate.ts +48 -0
  253. package/src/server/sync/repair.ts +39 -0
  254. package/src/server/sync/reset.ts +91 -0
  255. package/src/server/sync/sync.property.test.ts +385 -0
  256. package/src/server/sync/sync.test.ts +349 -0
  257. package/src/server/sync/utils.ts +3 -0
  258. package/src/server/tags/app.ts +101 -0
  259. package/src/server/tests/mockData.json +9352 -0
  260. package/src/server/tests/mockSyncServer.ts +119 -0
  261. package/src/server/tools/app.ts +152 -0
  262. package/src/server/transactions/__snapshots__/transaction-rules.test.ts.snap +173 -0
  263. package/src/server/transactions/__snapshots__/transfer.test.ts.snap +655 -0
  264. package/src/server/transactions/app.ts +136 -0
  265. package/src/server/transactions/export/export-to-csv.ts +132 -0
  266. package/src/server/transactions/import/__snapshots__/parse-file.test.ts.snap +1582 -0
  267. package/src/server/transactions/import/ofx2json.test.ts +33 -0
  268. package/src/server/transactions/import/ofx2json.ts +157 -0
  269. package/src/server/transactions/import/parse-file.test.ts +224 -0
  270. package/src/server/transactions/import/parse-file.ts +286 -0
  271. package/src/server/transactions/import/qif2json.ts +110 -0
  272. package/src/server/transactions/import/xmlcamt2json.ts +168 -0
  273. package/src/server/transactions/index.ts +196 -0
  274. package/src/server/transactions/merge.test.ts +370 -0
  275. package/src/server/transactions/merge.ts +139 -0
  276. package/src/server/transactions/transaction-rules.test.ts +994 -0
  277. package/src/server/transactions/transaction-rules.ts +1038 -0
  278. package/src/server/transactions/transfer.test.ts +221 -0
  279. package/src/server/transactions/transfer.ts +173 -0
  280. package/src/server/undo.ts +271 -0
  281. package/src/server/update.ts +37 -0
  282. package/src/server/util/budget-name.ts +61 -0
  283. package/src/server/util/custom-sync-mapping.ts +48 -0
  284. package/src/server/util/rschedule.ts +9 -0
  285. package/src/shared/__mocks__/platform.ts +7 -0
  286. package/src/shared/__snapshots__/months.test.ts.snap +21 -0
  287. package/src/shared/arithmetic.test.ts +112 -0
  288. package/src/shared/arithmetic.ts +170 -0
  289. package/src/shared/async.test.ts +135 -0
  290. package/src/shared/async.ts +76 -0
  291. package/src/shared/constants.ts +5 -0
  292. package/src/shared/currencies.ts +70 -0
  293. package/src/shared/dashboard.ts +260 -0
  294. package/src/shared/environment.ts +18 -0
  295. package/src/shared/errors.ts +195 -0
  296. package/src/shared/locale.ts +27 -0
  297. package/src/shared/location-utils.test.ts +69 -0
  298. package/src/shared/location-utils.ts +49 -0
  299. package/src/shared/months.test.ts +5 -0
  300. package/src/shared/months.ts +485 -0
  301. package/src/shared/normalisation.ts +6 -0
  302. package/src/shared/platform.electron.ts +21 -0
  303. package/src/shared/platform.ts +20 -0
  304. package/src/shared/query.ts +176 -0
  305. package/src/shared/rules.test.ts +56 -0
  306. package/src/shared/rules.ts +371 -0
  307. package/src/shared/schedules.test.ts +570 -0
  308. package/src/shared/schedules.ts +560 -0
  309. package/src/shared/test-helpers.ts +156 -0
  310. package/src/shared/transactions.test.ts +275 -0
  311. package/src/shared/transactions.ts +433 -0
  312. package/src/shared/transfer.test.ts +75 -0
  313. package/src/shared/transfer.ts +16 -0
  314. package/src/shared/user.ts +4 -0
  315. package/src/shared/util.test.ts +240 -0
  316. package/src/shared/util.ts +633 -0
  317. package/src/types/api-handlers.ts +287 -0
  318. package/src/types/budget.ts +8 -0
  319. package/src/types/file.ts +47 -0
  320. package/src/types/handlers.ts +46 -0
  321. package/src/types/models/account.ts +24 -0
  322. package/src/types/models/bank-sync.ts +23 -0
  323. package/src/types/models/bank.ts +6 -0
  324. package/src/types/models/category-group.ts +11 -0
  325. package/src/types/models/category.ts +13 -0
  326. package/src/types/models/dashboard.ts +199 -0
  327. package/src/types/models/gocardless.ts +84 -0
  328. package/src/types/models/import-transaction.ts +56 -0
  329. package/src/types/models/index.ts +23 -0
  330. package/src/types/models/nearby-payee.ts +7 -0
  331. package/src/types/models/note.ts +4 -0
  332. package/src/types/models/openid.ts +8 -0
  333. package/src/types/models/payee-location.ts +8 -0
  334. package/src/types/models/payee.ts +10 -0
  335. package/src/types/models/pluggyai.ts +19 -0
  336. package/src/types/models/reports.ts +144 -0
  337. package/src/types/models/rule.ts +174 -0
  338. package/src/types/models/schedule.ts +49 -0
  339. package/src/types/models/simplefin.ts +28 -0
  340. package/src/types/models/tags.ts +6 -0
  341. package/src/types/models/templates.ts +135 -0
  342. package/src/types/models/transaction-filter.ts +9 -0
  343. package/src/types/models/transaction.ts +39 -0
  344. package/src/types/models/user-access.ts +10 -0
  345. package/src/types/models/user.ts +25 -0
  346. package/src/types/prefs.ts +167 -0
  347. package/src/types/server-events.ts +86 -0
  348. package/src/types/server-handlers.ts +27 -0
  349. package/src/types/util.ts +26 -0
  350. package/tsconfig.json +34 -0
  351. package/typings/pegjs.ts +1 -0
  352. package/typings/process-worker.ts +12 -0
  353. package/typings/vite-plugin-peggy-loader.ts +1 -0
  354. package/typings/window.ts +62 -0
  355. package/vite.config.ts +109 -0
  356. package/vite.desktop.config.ts +59 -0
  357. package/vitest.config.ts +43 -0
  358. package/vitest.web.config.ts +38 -0
@@ -0,0 +1,55 @@
1
+ export default async function runMigration(db) {
2
+ const categories = db.runQuery(
3
+ 'SELECT id FROM categories WHERE tombstone = 0',
4
+ [],
5
+ true,
6
+ );
7
+
8
+ const customReports = db.runQuery(
9
+ 'SELECT id, selected_categories, conditions FROM custom_reports WHERE tombstone = 0 AND selected_categories IS NOT NULL',
10
+ [],
11
+ true,
12
+ );
13
+
14
+ // Move all `selected_categories` to `conditions` if possible.. otherwise skip
15
+ for (const report of customReports) {
16
+ const conditions = report.conditions ? JSON.parse(report.conditions) : [];
17
+ const selectedCategories = report.selected_categories
18
+ ? JSON.parse(report.selected_categories)
19
+ : [];
20
+ const selectedCategoryIds = selectedCategories.map(({ id }) => id);
21
+
22
+ const areAllCategoriesSelected = !categories.find(
23
+ ({ id }) => !selectedCategoryIds.includes(id),
24
+ );
25
+
26
+ // Do nothing if all categories are selected.. we don't need to add a new condition for that
27
+ if (areAllCategoriesSelected) {
28
+ continue;
29
+ }
30
+
31
+ // If `conditions` already has a "category" filter - skip the entry
32
+ if (conditions.find(({ field }) => field === 'category')) {
33
+ continue;
34
+ }
35
+
36
+ // Append a new condition with the selected category IDs
37
+ db.runQuery('UPDATE custom_reports SET conditions = ? WHERE id = ?', [
38
+ JSON.stringify([
39
+ ...conditions,
40
+ {
41
+ field: 'category',
42
+ op: 'oneOf',
43
+ value: selectedCategoryIds,
44
+ type: 'id',
45
+ },
46
+ ]),
47
+ report.id,
48
+ ]);
49
+ }
50
+
51
+ // Remove all the `selectedCategories` values - we don't need them anymore
52
+ db.runQuery(
53
+ 'UPDATE custom_reports SET selected_categories = NULL WHERE tombstone = 0',
54
+ );
55
+ }
@@ -0,0 +1,69 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+
3
+ import { DEFAULT_DASHBOARD_STATE } from '../src/shared/dashboard';
4
+
5
+ export default async function runMigration(db) {
6
+ db.transaction(() => {
7
+ const reports = db.runQuery(
8
+ 'SELECT id FROM custom_reports WHERE tombstone = 0 ORDER BY name COLLATE NOCASE ASC',
9
+ [],
10
+ true,
11
+ );
12
+
13
+ db.execQuery(`
14
+ CREATE TABLE dashboard
15
+ (id TEXT PRIMARY KEY,
16
+ type TEXT,
17
+ width INTEGER,
18
+ height INTEGER,
19
+ x INTEGER,
20
+ y INTEGER,
21
+ meta TEXT,
22
+ tombstone INTEGER DEFAULT 0);
23
+ `);
24
+
25
+ if (reports.length === 0) {
26
+ DEFAULT_DASHBOARD_STATE.forEach(widget => {
27
+ db.runQuery(
28
+ `INSERT INTO dashboard (id, type, width, height, x, y, meta) VALUES (?, ?, ?, ?, ?, ?, ?)`,
29
+ [
30
+ uuidv4(),
31
+ widget.type,
32
+ widget.width,
33
+ widget.height,
34
+ widget.x,
35
+ widget.y,
36
+ widget.meta !== undefined && widget.meta !== null
37
+ ? JSON.stringify(widget.meta)
38
+ : null,
39
+ ],
40
+ );
41
+ });
42
+ return;
43
+ }
44
+
45
+ db.execQuery(`
46
+ INSERT INTO dashboard (id, type, width, height, x, y)
47
+ VALUES
48
+ ('${uuidv4()}', 'net-worth-card', 8, 2, 0, 0),
49
+ ('${uuidv4()}', 'cash-flow-card', 4, 2, 8, 0),
50
+ ('${uuidv4()}', 'spending-card', 4, 2, 0, 2);
51
+ `);
52
+
53
+ // Add custom reports to the dashboard
54
+ reports.forEach((report, id) => {
55
+ db.runQuery(
56
+ `INSERT INTO dashboard (id, type, width, height, x, y, meta) VALUES (?, ?, ?, ?, ?, ?, ?)`,
57
+ [
58
+ uuidv4(),
59
+ 'custom-report',
60
+ 4,
61
+ 2,
62
+ (id * 4) % 12,
63
+ 2 + Math.floor(id / 3) * 2,
64
+ JSON.stringify({ id: report.id }),
65
+ ],
66
+ );
67
+ });
68
+ });
69
+ }
@@ -0,0 +1,59 @@
1
+ const SYNCED_PREF_KEYS = [
2
+ 'firstDayOfWeekIdx',
3
+ 'dateFormat',
4
+ 'numberFormat',
5
+ 'hideFraction',
6
+ 'isPrivacyEnabled',
7
+ /^show-extra-balances-/,
8
+ /^hide-cleared-/,
9
+ /^parse-date-/,
10
+ /^csv-mappings-/,
11
+ /^csv-delimiter-/,
12
+ /^csv-has-header-/,
13
+ /^ofx-fallback-missing-payee-/,
14
+ /^flip-amount-/,
15
+ 'budgetType',
16
+ /^flags\./,
17
+ ];
18
+
19
+ export default async function runMigration(db, { fs, fileId }) {
20
+ await db.execQuery(`
21
+ CREATE TABLE preferences
22
+ (id TEXT PRIMARY KEY,
23
+ value TEXT);
24
+ `);
25
+
26
+ try {
27
+ const budgetDir = fs.getBudgetDir(fileId);
28
+ const fullpath = fs.join(budgetDir, 'metadata.json');
29
+
30
+ const prefs = JSON.parse(await fs.readFile(fullpath));
31
+
32
+ if (typeof prefs !== 'object') {
33
+ return;
34
+ }
35
+
36
+ await Promise.all(
37
+ Object.keys(prefs).map(async key => {
38
+ // Check if the current key is of synced-keys type
39
+ if (
40
+ !SYNCED_PREF_KEYS.find(keyMatcher =>
41
+ keyMatcher instanceof RegExp
42
+ ? keyMatcher.test(key)
43
+ : keyMatcher === key,
44
+ )
45
+ ) {
46
+ return;
47
+ }
48
+
49
+ // insert the synced prefs in the new table
50
+ db.runQuery('INSERT INTO preferences (id, value) VALUES (?, ?)', [
51
+ key,
52
+ String(prefs[key]),
53
+ ]);
54
+ }),
55
+ );
56
+ } catch {
57
+ // Do nothing
58
+ }
59
+ }
@@ -0,0 +1,7 @@
1
+ BEGIN TRANSACTION;
2
+
3
+ UPDATE dashboard
4
+ SET tombstone = 1
5
+ WHERE type is NULL;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ BEGIN TRANSACTION;
2
+
3
+ ALTER TABLE custom_reports ADD COLUMN sort_by TEXT DEFAULT 'Descending';
4
+ UPDATE custom_reports SET sort_by = 'Descending';
5
+ UPDATE custom_reports SET sort_by = 'Budget' where graph_type = 'TableGraph';
6
+
7
+ COMMIT;
@@ -0,0 +1,5 @@
1
+ BEGIN TRANSACTION;
2
+
3
+ ALTER TABLE payees ADD COLUMN learn_categories BOOLEAN DEFAULT 1;
4
+
5
+ COMMIT;
@@ -0,0 +1,13 @@
1
+ BEGIN TRANSACTION;
2
+
3
+ ALTER TABLE custom_reports RENAME COLUMN sort_by TO sort_by_old;
4
+ ALTER TABLE custom_reports ADD COLUMN sort_by TEXT DEFAULT 'desc';
5
+
6
+ UPDATE custom_reports SET sort_by = 'desc' where sort_by_old = 'Descending';
7
+ UPDATE custom_reports SET sort_by = 'asc' where sort_by_old = 'Ascending';
8
+ UPDATE custom_reports SET sort_by = 'budget' where sort_by_old = 'Budget';
9
+ UPDATE custom_reports SET sort_by = 'name' where sort_by_old = 'Name';
10
+
11
+ ALTER TABLE custom_reports DROP COLUMN sort_by_old;
12
+
13
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ BEGIN TRANSACTION;
2
+
3
+ ALTER TABLE accounts ADD COLUMN last_sync text;
4
+
5
+ ALTER TABLE transactions ADD COLUMN raw_synced_data text;
6
+
7
+ COMMIT;
@@ -0,0 +1,5 @@
1
+ BEGIN TRANSACTION;
2
+
3
+ ALTER TABLE accounts ADD COLUMN last_reconciled text;
4
+
5
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ BEGIN TRANSACTION;
2
+
3
+ UPDATE preferences
4
+ SET value = CASE WHEN id = 'budgetType' AND value = 'report' THEN 'tracking' ELSE 'envelope' END
5
+ WHERE id = 'budgetType';
6
+
7
+ COMMIT;
@@ -0,0 +1,10 @@
1
+ BEGIN TRANSACTION;
2
+
3
+ CREATE TABLE tags(
4
+ id TEXT PRIMARY KEY,
5
+ tag TEXT UNIQUE,
6
+ color TEXT,
7
+ description TEXT
8
+ );
9
+
10
+ COMMIT
@@ -0,0 +1,5 @@
1
+ BEGIN TRANSACTION;
2
+
3
+ ALTER TABLE tags ADD COLUMN tombstone integer DEFAULT 0;
4
+
5
+ COMMIT;
@@ -0,0 +1,5 @@
1
+ -- Add a column to track where a category's automation templates come from
2
+ -- Allowed values for source (by convention): 'notes' | 'ui'
3
+ ALTER TABLE categories ADD COLUMN template_settings JSON DEFAULT '{"source": "notes"}';
4
+
5
+ UPDATE categories SET template_settings = '{"source": "ui"}' WHERE template_settings IS NULL;
@@ -0,0 +1,6 @@
1
+ -- Migration: Add trim_intervals column to custom_reports table
2
+ BEGIN TRANSACTION;
3
+
4
+ ALTER TABLE custom_reports ADD COLUMN trim_intervals INTEGER DEFAULT 0;
5
+
6
+ COMMIT;
@@ -0,0 +1 @@
1
+ -- Migration removed because it was not needed
@@ -0,0 +1,8 @@
1
+ BEGIN TRANSACTION;
2
+
3
+ -- Rename csv-skip-lines-* preferences to csv-skip-start-lines-*
4
+ UPDATE preferences
5
+ SET id = REPLACE(id, 'csv-skip-lines-', 'csv-skip-start-lines-')
6
+ WHERE id LIKE 'csv-skip-lines-%';
7
+
8
+ COMMIT;
@@ -0,0 +1,30 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+
3
+ export default async function runMigration(db) {
4
+ db.transaction(() => {
5
+ // 1. Create dashboards table
6
+ db.execQuery(`
7
+ CREATE TABLE dashboard_pages
8
+ (id TEXT PRIMARY KEY,
9
+ name TEXT,
10
+ tombstone INTEGER DEFAULT 0);
11
+ `);
12
+
13
+ // 2. Add dashboard_page_id to dashboard (widgets) table
14
+ db.execQuery(`
15
+ ALTER TABLE dashboard ADD COLUMN dashboard_page_id TEXT;
16
+ `);
17
+
18
+ // 3. Create a default dashboard
19
+ const defaultDashboardId = uuidv4();
20
+ db.runQuery(`INSERT INTO dashboard_pages (id, name) VALUES (?, ?)`, [
21
+ defaultDashboardId,
22
+ 'Main',
23
+ ]);
24
+
25
+ // 4. Migrate existing widgets to the default dashboard
26
+ db.runQuery(`UPDATE dashboard SET dashboard_page_id = ?`, [
27
+ defaultDashboardId,
28
+ ]);
29
+ });
30
+ }
@@ -0,0 +1,21 @@
1
+ BEGIN TRANSACTION;
2
+
3
+ CREATE TABLE IF NOT EXISTS payee_locations (
4
+ id TEXT PRIMARY KEY,
5
+ payee_id TEXT,
6
+ latitude REAL,
7
+ longitude REAL,
8
+ created_at INTEGER,
9
+ tombstone INTEGER DEFAULT 0
10
+ );
11
+
12
+ -- Create index on payee_id for faster lookups
13
+ CREATE INDEX IF NOT EXISTS idx_payee_locations_payee_id ON payee_locations (payee_id);
14
+
15
+ -- Create index on created_at for time-based queries
16
+ CREATE INDEX IF NOT EXISTS idx_payee_locations_tombstone_payee_created ON payee_locations (tombstone, payee_id, created_at);
17
+
18
+ -- Create geospatial composite index with tombstone for location-based queries
19
+ CREATE INDEX IF NOT EXISTS idx_payee_locations_geo_tombstone ON payee_locations (tombstone, latitude, longitude);
20
+
21
+ COMMIT;
package/package.json ADDED
@@ -0,0 +1,128 @@
1
+ {
2
+ "name": "@actual-app/core",
3
+ "version": "26.3.0",
4
+ "description": "",
5
+ "license": "ISC",
6
+ "imports": {
7
+ "#*": [
8
+ "./src/*.ts",
9
+ "./src/*.tsx",
10
+ "./src/*/index.ts",
11
+ "./src/*/index.tsx"
12
+ ]
13
+ },
14
+ "exports": {
15
+ "./client/accounts/*": "./src/client/accounts/*.ts",
16
+ "./client/app/*": "./src/client/app/*.ts",
17
+ "./client/budgets/*": "./src/client/budgets/*.ts",
18
+ "./client/data-hooks/schedules": "./src/client/data-hooks/schedules.tsx",
19
+ "./client/data-hooks/*": "./src/client/data-hooks/*.ts",
20
+ "./client/modals/*": "./src/client/modals/*.ts",
21
+ "./client/notifications/*": "./src/client/notifications/*.ts",
22
+ "./client/prefs/*": "./src/client/prefs/*.ts",
23
+ "./client/queries/*": "./src/client/queries/*.ts",
24
+ "./client/redux": "./src/client/redux/index.ts",
25
+ "./client/store": "./src/client/store/index.ts",
26
+ "./client/store/mock": "./src/client/store/mock.ts",
27
+ "./client/users/*": "./src/client/users/*.ts",
28
+ "./client/platform": {
29
+ "node": "./src/client/platform.electron.ts",
30
+ "default": "./src/client/platform.web.ts"
31
+ },
32
+ "./client/queries": "./src/client/queries.ts",
33
+ "./client/query-helpers": "./src/client/query-helpers.ts",
34
+ "./client/query-hooks": "./src/client/query-hooks.ts",
35
+ "./client/reports": "./src/client/reports.ts",
36
+ "./client/shared-listeners": "./src/client/shared-listeners.ts",
37
+ "./client/SpreadsheetProvider": "./src/client/SpreadsheetProvider.tsx",
38
+ "./client/transfer": "./src/client/transfer.ts",
39
+ "./client/undo": "./src/client/undo.ts",
40
+ "./mocks": "./src/mocks/index.ts",
41
+ "./platform/client/connection": {
42
+ "electron": "./src/platform/client/connection/index.ts",
43
+ "default": "./src/platform/client/connection/index.browser.ts"
44
+ },
45
+ "./platform/client/undo": "./src/platform/client/undo/index.ts",
46
+ "./platform/exceptions": "./src/platform/exceptions/index.ts",
47
+ "./platform/server/asyncStorage": "./src/platform/server/asyncStorage/index.ts",
48
+ "./platform/server/connection": "./src/platform/server/connection/index.ts",
49
+ "./platform/server/fetch": "./src/platform/server/fetch/index.ts",
50
+ "./platform/server/fs": "./src/platform/server/fs/index.ts",
51
+ "./platform/server/log": "./src/platform/server/log/index.ts",
52
+ "./platform/server/sqlite": "./src/platform/server/sqlite/index.ts",
53
+ "./server/budget/types/*": "./src/server/budget/types/*.d.ts",
54
+ "./server/*": "./src/server/*.ts",
55
+ "./shared/*": "./src/shared/*.ts",
56
+ "./types/models": "./src/types/models/index.ts",
57
+ "./types/*": "./src/types/*.ts",
58
+ "./typings/*": "./typings/*.ts",
59
+ "./lib-dist/electron/bundle.desktop.js": "./lib-dist/electron/bundle.desktop.js"
60
+ },
61
+ "scripts": {
62
+ "build:node": "cross-env NODE_ENV=production vite build --config ./vite.desktop.config.ts",
63
+ "watch:node": "cross-env NODE_ENV=development vite build --config ./vite.desktop.config.ts --watch",
64
+ "build:browser": "cross-env NODE_ENV=production ./bin/build-browser",
65
+ "watch:browser": "cross-env NODE_ENV=development ./bin/build-browser",
66
+ "generate:i18n": "i18next",
67
+ "test": "npm-run-all -cp 'test:*'",
68
+ "test:node": "ENV=node vitest --run",
69
+ "test:web": "ENV=web vitest --run -c vitest.web.config.ts",
70
+ "typecheck": "tsc -b && tsc-strict"
71
+ },
72
+ "dependencies": {
73
+ "@jlongster/sql.js": "^1.6.7",
74
+ "@reduxjs/toolkit": "^2.11.2",
75
+ "@rschedule/core": "^1.5.0",
76
+ "@rschedule/json-tools": "^1.5.0",
77
+ "@rschedule/standard-date-adapter": "^1.5.0",
78
+ "absurd-sql": "0.0.54",
79
+ "adm-zip": "patch:adm-zip@npm%3A0.5.16#~/.yarn/patches/adm-zip-npm-0.5.16-4556fea098.patch",
80
+ "better-sqlite3": "^12.6.2",
81
+ "csv-parse": "^6.1.0",
82
+ "csv-stringify": "^6.6.0",
83
+ "date-fns": "^4.1.0",
84
+ "handlebars": "^4.7.8",
85
+ "lru-cache": "^11.2.5",
86
+ "md5": "^2.3.0",
87
+ "memoize-one": "^6.0.0",
88
+ "mitt": "^3.0.1",
89
+ "promise-retry": "^2.0.1",
90
+ "slash": "5.1.0",
91
+ "typescript": "^5.9.3",
92
+ "typescript-strict-plugin": "^2.4.4",
93
+ "ua-parser-js": "^2.0.9",
94
+ "uuid": "^13.0.0"
95
+ },
96
+ "devDependencies": {
97
+ "@actual-app/crdt": "workspace:*",
98
+ "@swc/core": "^1.15.11",
99
+ "@types/adm-zip": "^0.5.7",
100
+ "@types/better-sqlite3": "^7.6.13",
101
+ "@types/emscripten": "^1.41.5",
102
+ "@types/jlongster__sql.js": "npm:@types/sql.js@latest",
103
+ "@types/node": "^22.19.10",
104
+ "@types/pegjs": "^0.10.6",
105
+ "assert": "^2.1.0",
106
+ "browserify-zlib": "^0.2.0",
107
+ "buffer": "^6.0.3",
108
+ "cross-env": "^10.1.0",
109
+ "fake-indexeddb": "^6.2.5",
110
+ "fast-check": "4.5.3",
111
+ "i18next": "^25.8.4",
112
+ "jest-diff": "^30.2.0",
113
+ "jsverify": "^0.8.4",
114
+ "mockdate": "^3.0.5",
115
+ "npm-run-all": "^4.1.5",
116
+ "path-browserify": "^1.0.1",
117
+ "peggy": "5.0.6",
118
+ "rollup-plugin-visualizer": "^6.0.5",
119
+ "stream-browserify": "^3.0.0",
120
+ "ts-node": "^10.9.2",
121
+ "typescript": "^5.9.3",
122
+ "vite": "^7.3.1",
123
+ "vite-plugin-node-polyfills": "^0.25.0",
124
+ "vite-plugin-peggy-loader": "^2.0.1",
125
+ "vitest": "^4.0.18",
126
+ "yargs": "^18.0.0"
127
+ }
128
+ }
@@ -0,0 +1,162 @@
1
+ // @ts-strict-ignore
2
+ import fc from 'fast-check';
3
+ import type { Arbitrary } from 'fast-check';
4
+
5
+ import { schema } from '../server/aql';
6
+ import { addDays } from '../shared/months';
7
+
8
+ export function typeArbitrary(typeDesc, name?) {
9
+ let arb;
10
+ switch (typeDesc.type) {
11
+ case 'id':
12
+ arb = fc.uuid();
13
+ break;
14
+ case 'boolean':
15
+ arb = fc.boolean();
16
+ break;
17
+ case 'integer':
18
+ arb = fc.integer();
19
+ break;
20
+ case 'float':
21
+ arb = fc.float();
22
+ break;
23
+ case 'string':
24
+ arb = fc.string();
25
+ break;
26
+ case 'date':
27
+ arb = fc.integer({ min: 0, max: 365 * 4 }).map(n => {
28
+ return addDays('2018-01-01', n);
29
+ });
30
+ break;
31
+ case 'json':
32
+ arb = fc.constant(null);
33
+ break;
34
+ default:
35
+ throw new Error('Unknown schema field type: ' + typeDesc.type);
36
+ }
37
+
38
+ if (!typeDesc.required && name !== 'id') {
39
+ return fc.option(arb).map(val => {
40
+ if (val == null) {
41
+ if (typeDesc.default !== undefined) {
42
+ return typeof typeDesc.default === 'function'
43
+ ? typeDesc.default()
44
+ : typeDesc.default;
45
+ } else if (typeDesc.type === 'boolean') {
46
+ return false;
47
+ }
48
+ }
49
+ return val;
50
+ });
51
+ }
52
+ return arb;
53
+ }
54
+
55
+ export function flattenSortTransactions(arr) {
56
+ const flattened = arr.reduce((list, trans) => {
57
+ const { subtransactions, ...fields } = trans;
58
+
59
+ if (subtransactions.length > 0) {
60
+ list.push({
61
+ ...fields,
62
+ is_parent: true,
63
+ is_child: false,
64
+ parent_id: null,
65
+ });
66
+ subtransactions.forEach(subtrans => {
67
+ list.push({
68
+ ...subtrans,
69
+ is_parent: false,
70
+ is_child: true,
71
+ parent_id: trans.id,
72
+ date: trans.date,
73
+ account: trans.account,
74
+ });
75
+ });
76
+ } else {
77
+ list.push({
78
+ ...fields,
79
+ is_parent: false,
80
+ is_child: false,
81
+ parent_id: null,
82
+ });
83
+ }
84
+ return list;
85
+ }, []);
86
+
87
+ return flattened.sort((t1, t2) => {
88
+ if (t1.id < t2.id) {
89
+ return -1;
90
+ } else if (t1.id > t2.id) {
91
+ return 1;
92
+ }
93
+ return 0;
94
+ });
95
+ }
96
+
97
+ function tableArbitrary<
98
+ T extends Record<string, { type: string; required?: boolean }>,
99
+ E extends Record<string, Arbitrary<unknown>>,
100
+ >(
101
+ tableSchema: T,
102
+ extraArbs?: E,
103
+ requiredKeys: Array<Extract<keyof T | keyof E, string>> = [],
104
+ ) {
105
+ const arb = fc.record(
106
+ {
107
+ ...Object.fromEntries<T>(
108
+ Object.entries(tableSchema).map(([name, field]) => {
109
+ return [name, typeArbitrary(field, name)] as const;
110
+ }),
111
+ ),
112
+ // Override the amount to make it a smaller integer
113
+ amount: fc.integer({ min: -1000000, max: 1000000 }),
114
+ ...extraArbs,
115
+ },
116
+ {
117
+ requiredKeys: [
118
+ 'id',
119
+ ...requiredKeys,
120
+ ...Object.keys(tableSchema).filter(name => tableSchema[name].required),
121
+ ],
122
+ },
123
+ );
124
+
125
+ return arb;
126
+ }
127
+
128
+ export function makeTransaction({
129
+ splitFreq = 1,
130
+ payeeIds,
131
+ }: { splitFreq?: number; payeeIds?: string[] } = {}) {
132
+ const payeeField = payeeIds
133
+ ? { payee: fc.oneof(...payeeIds.map(id => fc.constant(id))) }
134
+ : null;
135
+
136
+ const subtrans = tableArbitrary(schema.transactions, payeeField);
137
+
138
+ return tableArbitrary(
139
+ schema.transactions,
140
+ {
141
+ ...payeeField,
142
+ subtransactions: fc.oneof(
143
+ { arbitrary: fc.constant([]), weight: 100 },
144
+ { arbitrary: fc.array(subtrans), weight: splitFreq * 100 },
145
+ ),
146
+ },
147
+ ['subtransactions'],
148
+ );
149
+ }
150
+
151
+ export const makeTransactionArray = (
152
+ options: { minLength?; maxLength?; splitFreq?; payeeIds? } = {},
153
+ ) => {
154
+ const { minLength, maxLength, ...transOpts } = options;
155
+ return fc
156
+ .array(makeTransaction(transOpts), { minLength, maxLength })
157
+ .map(arr => flattenSortTransactions(arr));
158
+ };
159
+ export const payee = tableArbitrary(schema.payees);
160
+ export const account = tableArbitrary(schema.accounts);
161
+ export const category = tableArbitrary(schema.categories);
162
+ export const category_group = tableArbitrary(schema.category_groups);