@actual-app/core 26.4.0 → 26.5.0-nightly.20260407
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/package.json +2 -5
- package/src/server/dashboard/app.ts +1 -0
- package/src/server/importers/ynab4.ts +1 -2
- package/src/server/update.ts +2 -2
- package/src/shared/currencies.ts +1 -0
- package/src/shared/transactions.test.ts +34 -0
- package/src/shared/transactions.ts +2 -1
- package/src/types/models/dashboard.ts +15 -2
- package/src/types/prefs.ts +2 -1
- package/vite.config.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@actual-app/core",
|
|
3
|
-
"version": "26.
|
|
3
|
+
"version": "26.5.0-nightly.20260407",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"typesVersions": {
|
|
@@ -90,11 +90,9 @@
|
|
|
90
90
|
"date-fns": "^4.1.0",
|
|
91
91
|
"handlebars": "^4.7.9",
|
|
92
92
|
"lru-cache": "^11.2.6",
|
|
93
|
-
"md5": "^2.3.0",
|
|
94
93
|
"memoize-one": "^6.0.0",
|
|
95
94
|
"mitt": "^3.0.1",
|
|
96
95
|
"promise-retry": "^2.0.1",
|
|
97
|
-
"slash": "5.1.0",
|
|
98
96
|
"typescript-strict-plugin": "^2.4.4",
|
|
99
97
|
"ua-parser-js": "^2.0.9",
|
|
100
98
|
"uuid": "^13.0.0"
|
|
@@ -107,7 +105,6 @@
|
|
|
107
105
|
"@types/emscripten": "^1.41.5",
|
|
108
106
|
"@types/jlongster__sql.js": "npm:@types/sql.js@latest",
|
|
109
107
|
"@types/node": "^22.19.15",
|
|
110
|
-
"@types/pegjs": "^0.10.6",
|
|
111
108
|
"@typescript/native-preview": "^7.0.0-dev.20260309.1",
|
|
112
109
|
"assert": "^2.1.0",
|
|
113
110
|
"browserify-zlib": "^0.2.0",
|
|
@@ -129,7 +126,7 @@
|
|
|
129
126
|
"ts-node": "^10.9.2",
|
|
130
127
|
"util": "^0.12.5",
|
|
131
128
|
"vite": "^8.0.0",
|
|
132
|
-
"vite-plugin-node-polyfills": "^0.
|
|
129
|
+
"vite-plugin-node-polyfills": "^0.26.0",
|
|
133
130
|
"vite-plugin-peggy-loader": "^2.0.1",
|
|
134
131
|
"vitest": "^4.1.0",
|
|
135
132
|
"yargs": "^18.0.0"
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// @ts-strict-ignore
|
|
2
2
|
import AdmZip from 'adm-zip';
|
|
3
|
-
import normalizePathSep from 'slash';
|
|
4
3
|
import { v4 as uuidv4 } from 'uuid';
|
|
5
4
|
|
|
6
5
|
import { logger } from '../../platform/server/log';
|
|
@@ -396,7 +395,7 @@ export async function doImport(data: YNAB4.YFull) {
|
|
|
396
395
|
}
|
|
397
396
|
|
|
398
397
|
export function getBudgetName(filepath) {
|
|
399
|
-
let unixFilepath =
|
|
398
|
+
let unixFilepath = filepath.replace(/\\/g, '/');
|
|
400
399
|
|
|
401
400
|
if (!/\.zip/.test(unixFilepath)) {
|
|
402
401
|
return null;
|
package/src/server/update.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @ts-strict-ignore
|
|
2
|
-
import
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
3
|
|
|
4
4
|
import { makeViews, schema, schemaConfig } from './aql';
|
|
5
5
|
import * as db from './db';
|
|
@@ -20,7 +20,7 @@ async function updateViews() {
|
|
|
20
20
|
const { value: hash } = row || {};
|
|
21
21
|
|
|
22
22
|
const views = makeViews(schema, schemaConfig);
|
|
23
|
-
const currentHash = md5(views);
|
|
23
|
+
const currentHash = createHash('md5').update(views).digest('hex');
|
|
24
24
|
|
|
25
25
|
if (hash !== currentHash) {
|
|
26
26
|
db.execQuery(views);
|
package/src/shared/currencies.ts
CHANGED
|
@@ -28,6 +28,7 @@ export const currencies: Currency[] = [
|
|
|
28
28
|
{ code: 'BYN', name: 'Belarusian Ruble', symbol: 'Br', decimalPlaces: 2, numberFormat: 'space-comma', symbolFirst: false },
|
|
29
29
|
{ code: 'CAD', name: 'Canadian Dollar', symbol: 'CA$', decimalPlaces: 2, numberFormat: 'comma-dot', symbolFirst: true },
|
|
30
30
|
{ code: 'CHF', name: 'Swiss Franc', symbol: 'Fr.', decimalPlaces: 2, numberFormat: 'apostrophe-dot', symbolFirst: true },
|
|
31
|
+
{ code: 'CLP', name: 'Chilean Peso', symbol: 'CLP$', decimalPlaces: 2, numberFormat: 'dot-comma', symbolFirst: true },
|
|
31
32
|
{ code: 'CNY', name: 'Yuan Renminbi', symbol: '¥', decimalPlaces: 2, numberFormat: 'comma-dot', symbolFirst: true },
|
|
32
33
|
{ code: 'COP', name: 'Colombian Peso', symbol: 'Col$', decimalPlaces: 2, numberFormat: 'dot-comma', symbolFirst: true },
|
|
33
34
|
{ code: 'CRC', name: 'Costa Rican Colón', symbol: '₡', decimalPlaces: 2, numberFormat: 'space-comma', symbolFirst: true },
|
|
@@ -201,6 +201,40 @@ describe('Transactions', () => {
|
|
|
201
201
|
expect(data.length).toBe(5);
|
|
202
202
|
});
|
|
203
203
|
|
|
204
|
+
test('partially updating a split parent preserves amount and does not set error', () => {
|
|
205
|
+
const transactions = [
|
|
206
|
+
makeTransaction({ amount: 2001 }),
|
|
207
|
+
...makeSplitTransaction({ id: 't1', amount: 2500 }, [
|
|
208
|
+
{ id: 't2', amount: 2000 },
|
|
209
|
+
{ id: 't3', amount: 500 },
|
|
210
|
+
]),
|
|
211
|
+
makeTransaction({ amount: 3002 }),
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
// Simulate a partial update (only `notes`) on the parent — this is
|
|
215
|
+
// how `api.updateTransaction(id, { notes: '...' })` calls it in
|
|
216
|
+
// `api.ts`: `updateTransaction(transactions, { id, ...fields })`.
|
|
217
|
+
const { data, diff } = updateTransaction(transactions, {
|
|
218
|
+
id: 't1',
|
|
219
|
+
notes: 'updated note',
|
|
220
|
+
} as TransactionEntity);
|
|
221
|
+
|
|
222
|
+
// The parent should get the updated notes without an error
|
|
223
|
+
const parent = data.find(d => d.id === 't1');
|
|
224
|
+
expect(parent?.notes).toBe('updated note');
|
|
225
|
+
expect(parent?.amount).toBe(2500);
|
|
226
|
+
expect(parent?.error).toBeNull();
|
|
227
|
+
|
|
228
|
+
// Children should be unchanged
|
|
229
|
+
expect(data.filter(t => t.parent_id === 't1').length).toBe(2);
|
|
230
|
+
|
|
231
|
+
expect(diff).toEqual({
|
|
232
|
+
added: [],
|
|
233
|
+
deleted: [],
|
|
234
|
+
updated: [expect.objectContaining({ id: 't1', notes: 'updated note' })],
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
204
238
|
test('deleting a split transaction works', () => {
|
|
205
239
|
const transactions = [
|
|
206
240
|
makeTransaction({ amount: 2001 }),
|
|
@@ -262,7 +262,8 @@ export function updateTransaction(
|
|
|
262
262
|
) {
|
|
263
263
|
return replaceTransactions(transactions, transaction.id, trans => {
|
|
264
264
|
if (trans.is_parent) {
|
|
265
|
-
const parent =
|
|
265
|
+
const parent =
|
|
266
|
+
trans.id === transaction.id ? { ...trans, ...transaction } : trans;
|
|
266
267
|
const originalSubtransactions =
|
|
267
268
|
parent.subtransactions ?? trans.subtransactions;
|
|
268
269
|
const sub = originalSubtransactions?.map(t => {
|
|
@@ -114,13 +114,13 @@ type SpecializedWidget =
|
|
|
114
114
|
| MarkdownWidget
|
|
115
115
|
| SummaryWidget
|
|
116
116
|
| CalendarWidget
|
|
117
|
-
| FormulaWidget
|
|
117
|
+
| FormulaWidget
|
|
118
|
+
| SankeyWidget;
|
|
118
119
|
export type DashboardWidgetEntity = SpecializedWidget | CustomReportWidget;
|
|
119
120
|
export type NewDashboardWidgetEntity = Omit<
|
|
120
121
|
DashboardWidgetEntity,
|
|
121
122
|
'id' | 'tombstone' | 'dashboard_page_id'
|
|
122
123
|
>;
|
|
123
|
-
|
|
124
124
|
// Exported/imported (json) widget definition
|
|
125
125
|
export type ExportImportCustomReportWidget = Omit<
|
|
126
126
|
CustomReportWidget,
|
|
@@ -197,3 +197,16 @@ export type FormulaWidget = AbstractWidget<
|
|
|
197
197
|
>;
|
|
198
198
|
} | null
|
|
199
199
|
>;
|
|
200
|
+
|
|
201
|
+
export type SankeyWidget = AbstractWidget<
|
|
202
|
+
'sankey-card',
|
|
203
|
+
{
|
|
204
|
+
name?: string;
|
|
205
|
+
conditions?: RuleConditionEntity[];
|
|
206
|
+
conditionsOp?: 'and' | 'or';
|
|
207
|
+
timeFrame?: TimeFrame;
|
|
208
|
+
mode?: 'budgeted' | 'spent';
|
|
209
|
+
topNcategories?: number;
|
|
210
|
+
categorySort?: 'per-group' | 'global' | 'budget-order';
|
|
211
|
+
} | null
|
|
212
|
+
>;
|
package/src/types/prefs.ts
CHANGED