@actual-app/api 26.1.0-nightly.20251220 → 26.1.0-nightly.20251221
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.
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const captureException: (exc: Error) => void;
|
|
2
|
-
export declare const captureBreadcrumb: (
|
|
2
|
+
export declare const captureBreadcrumb: (crumb: unknown) => void;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type NavigateFunction } from 'react-router';
|
|
2
1
|
export {};
|
|
3
2
|
type FileDialogOptions = {
|
|
4
3
|
properties?: Array<'openFile' | 'openDirectory'>;
|
|
@@ -32,9 +31,6 @@ type Actual = {
|
|
|
32
31
|
startOAuthServer: () => Promise<string>;
|
|
33
32
|
};
|
|
34
33
|
declare global {
|
|
35
|
-
interface Window {
|
|
36
|
-
__navigate?: NavigateFunction;
|
|
37
|
-
}
|
|
38
34
|
var Actual: Actual;
|
|
39
35
|
var IS_TESTING: boolean;
|
|
40
36
|
var currentMonth: string | null;
|
package/dist/app/bundle.api.js
CHANGED
|
@@ -3858,7 +3858,7 @@ const getBudgetDir = (id2) => {
|
|
|
3858
3858
|
throw new Error("getDocumentDir: id is falsy: " + id2);
|
|
3859
3859
|
}
|
|
3860
3860
|
if (id2.match(/[^A-Za-z0-9\-_]/)) {
|
|
3861
|
-
throw new Error(`Invalid budget id
|
|
3861
|
+
throw new Error(`Invalid budget id "${id2}". Check the id of your budget in the Advanced section of the settings page.`);
|
|
3862
3862
|
}
|
|
3863
3863
|
return path.join(getDocumentDir(), id2);
|
|
3864
3864
|
};
|
|
@@ -9324,7 +9324,7 @@ function getNumberFormat({ format: format2 = numberFormatConfig.format, hideFrac
|
|
|
9324
9324
|
break;
|
|
9325
9325
|
case "apostrophe-dot":
|
|
9326
9326
|
locale = "de-CH";
|
|
9327
|
-
thousandsSeparator = "
|
|
9327
|
+
thousandsSeparator = "'";
|
|
9328
9328
|
decimalSeparator = ".";
|
|
9329
9329
|
break;
|
|
9330
9330
|
case "comma-dot-in":
|
|
@@ -9359,7 +9359,7 @@ function safeNumber(value) {
|
|
|
9359
9359
|
throw new Error("safeNumber: number is not an integer: " + JSON.stringify(value));
|
|
9360
9360
|
}
|
|
9361
9361
|
if (value > MAX_SAFE_NUMBER || value < MIN_SAFE_NUMBER) {
|
|
9362
|
-
throw new Error("safeNumber: can
|
|
9362
|
+
throw new Error("safeNumber: can't safely perform arithmetic with number: " + value);
|
|
9363
9363
|
}
|
|
9364
9364
|
return value;
|
|
9365
9365
|
}
|
|
@@ -11812,7 +11812,8 @@ instance.loadLanguages;
|
|
|
11812
11812
|
const captureException = function (exc) {
|
|
11813
11813
|
console.error("[Exception]", exc);
|
|
11814
11814
|
};
|
|
11815
|
-
const captureBreadcrumb = function (
|
|
11815
|
+
const captureBreadcrumb = function (crumb) {
|
|
11816
|
+
console.info("[Breadcrumb]", crumb);
|
|
11816
11817
|
};
|
|
11817
11818
|
function isPreviewEnvironment() {
|
|
11818
11819
|
return String(process.env.REACT_APP_NETLIFY) === "true";
|
|
@@ -13057,11 +13058,11 @@ function typed(value, type2, { literal = false } = {}) {
|
|
|
13057
13058
|
}
|
|
13058
13059
|
function getFieldDescription(schema2, tableName, field) {
|
|
13059
13060
|
if (schema2[tableName] == null) {
|
|
13060
|
-
throw new CompileError(`Table
|
|
13061
|
+
throw new CompileError(`Table "${tableName}" does not exist in the schema`);
|
|
13061
13062
|
}
|
|
13062
13063
|
const fieldDesc = schema2[tableName][field];
|
|
13063
13064
|
if (fieldDesc == null) {
|
|
13064
|
-
throw new CompileError(`Field
|
|
13065
|
+
throw new CompileError(`Field "${field}" does not exist in table "${tableName}"`);
|
|
13065
13066
|
}
|
|
13066
13067
|
return fieldDesc;
|
|
13067
13068
|
}
|
|
@@ -13078,7 +13079,7 @@ function makePath(state, path2) {
|
|
|
13078
13079
|
throw new CompileError(`Path error: ${tableName2} table does not exist`);
|
|
13079
13080
|
}
|
|
13080
13081
|
if (!table[field] || table[field].ref == null) {
|
|
13081
|
-
throw new CompileError(`Field not joinable on table ${tableName2}:
|
|
13082
|
+
throw new CompileError(`Field not joinable on table ${tableName2}: "${field}"`);
|
|
13082
13083
|
}
|
|
13083
13084
|
return table[field].ref;
|
|
13084
13085
|
}, initialTable);
|
|
@@ -13185,7 +13186,7 @@ function inferParam(param, type2) {
|
|
|
13185
13186
|
float: ["integer"]
|
|
13186
13187
|
};
|
|
13187
13188
|
if (existingType !== type2 && (!casts[type2] || !casts[type2].includes(existingType))) {
|
|
13188
|
-
throw new Error(`Parameter
|
|
13189
|
+
throw new Error(`Parameter "${param.paramName}" can't convert to ${type2} (already inferred as ${existingType})`);
|
|
13189
13190
|
}
|
|
13190
13191
|
}
|
|
13191
13192
|
else {
|
|
@@ -13202,7 +13203,7 @@ function castInput(state, expr, type2) {
|
|
|
13202
13203
|
}
|
|
13203
13204
|
else if (expr.type === "null") {
|
|
13204
13205
|
if (!expr.literal) {
|
|
13205
|
-
throw new CompileError("A non-literal null doesn
|
|
13206
|
+
throw new CompileError("A non-literal null doesn't make sense");
|
|
13206
13207
|
}
|
|
13207
13208
|
if (type2 === "boolean") {
|
|
13208
13209
|
return typed(0, "boolean", { literal: true });
|
|
@@ -13218,7 +13219,7 @@ function castInput(state, expr, type2) {
|
|
|
13218
13219
|
throw new CompileError("Casting string fields to dates is not supported");
|
|
13219
13220
|
}
|
|
13220
13221
|
}
|
|
13221
|
-
throw new CompileError(`Can
|
|
13222
|
+
throw new CompileError(`Can't cast ${expr.type} to date`);
|
|
13222
13223
|
}
|
|
13223
13224
|
else if (type2 === "date-month") {
|
|
13224
13225
|
let expr2;
|
|
@@ -13229,7 +13230,7 @@ function castInput(state, expr, type2) {
|
|
|
13229
13230
|
expr2 = parseMonth(expr.value) || parseDate(expr.value) || badDateFormat(expr.value, "date-month");
|
|
13230
13231
|
}
|
|
13231
13232
|
else {
|
|
13232
|
-
throw new CompileError(`Can
|
|
13233
|
+
throw new CompileError(`Can't cast ${expr.type} to date-month`);
|
|
13233
13234
|
}
|
|
13234
13235
|
if (expr2.literal) {
|
|
13235
13236
|
return typed(dateToInt(expr2.value.toString().slice(0, 6)), "date-month", { literal: true });
|
|
@@ -13247,7 +13248,7 @@ function castInput(state, expr, type2) {
|
|
|
13247
13248
|
expr2 = parseYear(expr.value) || parseMonth(expr.value) || parseDate(expr.value) || badDateFormat(expr.value, "date-year");
|
|
13248
13249
|
}
|
|
13249
13250
|
else {
|
|
13250
|
-
throw new CompileError(`Can
|
|
13251
|
+
throw new CompileError(`Can't cast ${expr.type} to date-year`);
|
|
13251
13252
|
}
|
|
13252
13253
|
if (expr2.literal) {
|
|
13253
13254
|
return typed(dateToInt(expr2.value.toString().slice(0, 4)), "date-year", {
|
|
@@ -13271,7 +13272,7 @@ function castInput(state, expr, type2) {
|
|
|
13271
13272
|
if (expr.type === "any") {
|
|
13272
13273
|
return typed(expr.value, type2, { literal: expr.literal });
|
|
13273
13274
|
}
|
|
13274
|
-
throw new CompileError(`Can
|
|
13275
|
+
throw new CompileError(`Can't convert ${expr.type} to ${type2}`);
|
|
13275
13276
|
}
|
|
13276
13277
|
function val(state, expr, type2) {
|
|
13277
13278
|
let castedExpr = expr;
|
|
@@ -13425,7 +13426,7 @@ const compileFunction = saveStack("function", (state, func) => {
|
|
|
13425
13426
|
argExprs = [argExprs];
|
|
13426
13427
|
}
|
|
13427
13428
|
if (name[0] !== "$") {
|
|
13428
|
-
throw new CompileError(`Unknown property
|
|
13429
|
+
throw new CompileError(`Unknown property "${name}." Did you mean to call a function? Try prefixing it with $`);
|
|
13429
13430
|
}
|
|
13430
13431
|
let args = argExprs;
|
|
13431
13432
|
if (name !== "$condition") {
|
|
@@ -13687,7 +13688,7 @@ function expandStar(state, expr) {
|
|
|
13687
13688
|
}
|
|
13688
13689
|
const table = state.schema[pathInfo.tableName];
|
|
13689
13690
|
if (table == null) {
|
|
13690
|
-
throw new Error(`Table
|
|
13691
|
+
throw new Error(`Table "${pathInfo.tableName}" does not exist`);
|
|
13691
13692
|
}
|
|
13692
13693
|
return Object.keys(table).map((field) => path2 ? `${path2}.${field}` : field);
|
|
13693
13694
|
}
|
|
@@ -13712,7 +13713,7 @@ const compileSelect = saveStack("select", (state, exprs, isAggregate, orders) =>
|
|
|
13712
13713
|
const [name, value] = Object.entries(expr)[0];
|
|
13713
13714
|
if (name[0] === "$") {
|
|
13714
13715
|
state.compileStack.push({ type: "value", value: expr });
|
|
13715
|
-
throw new CompileError(`Invalid field
|
|
13716
|
+
throw new CompileError(`Invalid field "${name}", are you trying to select a function? You need to name the expression`);
|
|
13716
13717
|
}
|
|
13717
13718
|
if (typeof value === "string") {
|
|
13718
13719
|
const compiled2 = compileExpr(state, "$" + value);
|
|
@@ -14054,7 +14055,7 @@ function convertInputType(value, type2) {
|
|
|
14054
14055
|
return value;
|
|
14055
14056
|
}
|
|
14056
14057
|
else {
|
|
14057
|
-
throw new Error("Can
|
|
14058
|
+
throw new Error("Can't convert to integer: " + JSON.stringify(value));
|
|
14058
14059
|
}
|
|
14059
14060
|
case "json":
|
|
14060
14061
|
return JSON.stringify(value);
|
|
@@ -14091,7 +14092,7 @@ function convertOutputType(value, type2) {
|
|
|
14091
14092
|
function conform(schema2, schemaConfig2, table, obj, { skipNull = false } = {}) {
|
|
14092
14093
|
const tableSchema = schema2[table];
|
|
14093
14094
|
if (tableSchema == null) {
|
|
14094
|
-
throw new Error(`Table
|
|
14095
|
+
throw new Error(`Table "${table}" does not exist`);
|
|
14095
14096
|
}
|
|
14096
14097
|
const views = schemaConfig2.views || {};
|
|
14097
14098
|
const fieldRef = (field) => {
|
|
@@ -14106,10 +14107,10 @@ function conform(schema2, schemaConfig2, table, obj, { skipNull = false } = {})
|
|
|
14106
14107
|
}
|
|
14107
14108
|
const fieldDesc = tableSchema[field];
|
|
14108
14109
|
if (fieldDesc == null) {
|
|
14109
|
-
throw new Error(`Field
|
|
14110
|
+
throw new Error(`Field "${field}" does not exist on table ${table}: ${JSON.stringify(obj)}`);
|
|
14110
14111
|
}
|
|
14111
14112
|
if (isRequired(field, fieldDesc) && obj[field] == null) {
|
|
14112
|
-
throw new Error(
|
|
14113
|
+
throw new Error(`"${field}" is required for table "${table}": ${JSON.stringify(obj)}`);
|
|
14113
14114
|
}
|
|
14114
14115
|
if (skipNull && obj[field] == null) {
|
|
14115
14116
|
return null;
|
|
@@ -14121,7 +14122,7 @@ function convertForInsert(schema2, schemaConfig2, table, rawObj) {
|
|
|
14121
14122
|
const obj = { ...rawObj };
|
|
14122
14123
|
const tableSchema = schema2[table];
|
|
14123
14124
|
if (tableSchema == null) {
|
|
14124
|
-
throw new Error(`Error inserting: table
|
|
14125
|
+
throw new Error(`Error inserting: table "${table}" does not exist`);
|
|
14125
14126
|
}
|
|
14126
14127
|
Object.keys(tableSchema).forEach((field) => {
|
|
14127
14128
|
const fieldDesc = tableSchema[field];
|
|
@@ -14130,7 +14131,7 @@ function convertForInsert(schema2, schemaConfig2, table, rawObj) {
|
|
|
14130
14131
|
obj[field] = typeof fieldDesc.default === "function" ? fieldDesc.default() : fieldDesc.default;
|
|
14131
14132
|
}
|
|
14132
14133
|
else if (isRequired(field, fieldDesc)) {
|
|
14133
|
-
throw new Error(
|
|
14134
|
+
throw new Error(`"${field}" is required for table "${table}": ${JSON.stringify(obj)}`);
|
|
14134
14135
|
}
|
|
14135
14136
|
}
|
|
14136
14137
|
});
|
|
@@ -14140,14 +14141,14 @@ function convertForUpdate(schema2, schemaConfig2, table, rawObj) {
|
|
|
14140
14141
|
const obj = { ...rawObj };
|
|
14141
14142
|
const tableSchema = schema2[table];
|
|
14142
14143
|
if (tableSchema == null) {
|
|
14143
|
-
throw new Error(`Error updating: table
|
|
14144
|
+
throw new Error(`Error updating: table "${table}" does not exist`);
|
|
14144
14145
|
}
|
|
14145
14146
|
return conform(schema2, schemaConfig2, table, obj);
|
|
14146
14147
|
}
|
|
14147
14148
|
function convertFromSelect(schema2, schemaConfig2, table, obj) {
|
|
14148
14149
|
const tableSchema = schema2[table];
|
|
14149
14150
|
if (tableSchema == null) {
|
|
14150
|
-
throw new Error(`Table
|
|
14151
|
+
throw new Error(`Table "${table}" does not exist`);
|
|
14151
14152
|
}
|
|
14152
14153
|
const fields = Object.keys(tableSchema);
|
|
14153
14154
|
const result = {};
|
|
@@ -14565,7 +14566,7 @@ function execTransactions(compilerState, queryState, sqlPieces, params, outputTy
|
|
|
14565
14566
|
const tableOptions = queryState.tableOptions || {};
|
|
14566
14567
|
const splitType = tableOptions.splits ? tableOptions.splits : "inline";
|
|
14567
14568
|
if (!isValidSplitsOption(splitType)) {
|
|
14568
|
-
throw new Error(`Invalid
|
|
14569
|
+
throw new Error(`Invalid "splits" option for transactions: "${splitType}"`);
|
|
14569
14570
|
}
|
|
14570
14571
|
if (splitType === "all" || splitType === "inline" || splitType === "none") {
|
|
14571
14572
|
return execTransactionsBasic(compilerState, queryState, sqlPieces, params, splitType, outputTypes);
|
|
@@ -14690,7 +14691,7 @@ async function execCategoryGroups(compilerState, queryState, sqlPieces, params,
|
|
|
14690
14691
|
const tableOptions = queryState.tableOptions || {};
|
|
14691
14692
|
const categoriesOption = tableOptions.categories ? tableOptions.categories : "all";
|
|
14692
14693
|
if (!isValidCategoriesOption(categoriesOption)) {
|
|
14693
|
-
throw new Error(`Invalid
|
|
14694
|
+
throw new Error(`Invalid "categories" option for category_groups: "${categoriesOption}"`);
|
|
14694
14695
|
}
|
|
14695
14696
|
if (categoriesOption !== "none") {
|
|
14696
14697
|
return execCategoryGroupsWithCategories(compilerState, queryState, sqlPieces, params, categoriesOption, outputTypes);
|
|
@@ -15384,6 +15385,10 @@ async function loadSpreadsheet(db2, onSheetChange2) {
|
|
|
15384
15385
|
else {
|
|
15385
15386
|
sheet = new Spreadsheet();
|
|
15386
15387
|
}
|
|
15388
|
+
captureBreadcrumb({
|
|
15389
|
+
message: "loading spreadsheet",
|
|
15390
|
+
category: "server"
|
|
15391
|
+
});
|
|
15387
15392
|
globalSheet = sheet;
|
|
15388
15393
|
globalOnChange = onSheetChange2;
|
|
15389
15394
|
if (onSheetChange2) {
|
|
@@ -15401,6 +15406,10 @@ async function loadSpreadsheet(db2, onSheetChange2) {
|
|
|
15401
15406
|
logger.log("Loading fresh spreadsheet");
|
|
15402
15407
|
await loadUserBudgets(db2);
|
|
15403
15408
|
}
|
|
15409
|
+
captureBreadcrumb({
|
|
15410
|
+
message: "loaded spreadsheet",
|
|
15411
|
+
category: "server"
|
|
15412
|
+
});
|
|
15404
15413
|
return sheet;
|
|
15405
15414
|
}
|
|
15406
15415
|
function unloadSpreadsheet() {
|
|
@@ -55963,6 +55972,7 @@ function withMutatorContext(context, func) {
|
|
|
55963
55972
|
function getMutatorContext() {
|
|
55964
55973
|
if (currentContext == null) {
|
|
55965
55974
|
captureBreadcrumb({
|
|
55975
|
+
category: "server",
|
|
55966
55976
|
message: "Recent methods: " + _latestHandlerNames.join(", ")
|
|
55967
55977
|
});
|
|
55968
55978
|
return {};
|
|
@@ -60536,7 +60546,7 @@ async function getCategoriesGrouped(ids) {
|
|
|
60536
60546
|
async function insertCategoryGroup(group) {
|
|
60537
60547
|
const existingGroup = await first$2(`SELECT id, name, hidden FROM category_groups WHERE UPPER(name) = ? and tombstone = 0 LIMIT 1`, [group.name.toUpperCase()]);
|
|
60538
60548
|
if (existingGroup) {
|
|
60539
|
-
throw new Error(`A ${existingGroup.hidden ? "hidden " : ""}
|
|
60549
|
+
throw new Error(`A ${existingGroup.hidden ? "hidden " : ""}'${existingGroup.name}' category group already exists.`);
|
|
60540
60550
|
}
|
|
60541
60551
|
const lastGroup = await first$2(`
|
|
60542
60552
|
SELECT sort_order FROM category_groups WHERE tombstone = 0 ORDER BY sort_order DESC, id DESC LIMIT 1
|
|
@@ -60572,7 +60582,7 @@ async function insertCategory(category, { atEnd } = { atEnd: void 0 }) {
|
|
|
60572
60582
|
await batchMessages(async () => {
|
|
60573
60583
|
const existingCatInGroup = await first$2(`SELECT id FROM categories WHERE cat_group = ? and UPPER(name) = ? and tombstone = 0 LIMIT 1`, [category.cat_group, category.name.toUpperCase()]);
|
|
60574
60584
|
if (existingCatInGroup) {
|
|
60575
|
-
throw new Error(`Category
|
|
60585
|
+
throw new Error(`Category '${category.name}' already exists in group '${category.cat_group}'`);
|
|
60576
60586
|
}
|
|
60577
60587
|
if (atEnd) {
|
|
60578
60588
|
const lastCat = await first$2(`
|
|
@@ -105994,7 +106004,7 @@ class Action {
|
|
|
105994
106004
|
case "number": {
|
|
105995
106005
|
const numValue = typeof result === "number" ? result : parseFloat(String(result));
|
|
105996
106006
|
if (isNaN(numValue)) {
|
|
105997
|
-
const error = `Formula for
|
|
106007
|
+
const error = `Formula for "${this.field}" must produce a numeric value. Got: ${JSON.stringify(result)}`;
|
|
105998
106008
|
object._ruleErrors.push(error);
|
|
105999
106009
|
}
|
|
106000
106010
|
else {
|
|
@@ -106008,7 +106018,7 @@ class Action {
|
|
|
106008
106018
|
object[this.field] = format$1(parsed, "yyyy-MM-dd");
|
|
106009
106019
|
}
|
|
106010
106020
|
else {
|
|
106011
|
-
const error = `Formula for
|
|
106021
|
+
const error = `Formula for "${this.field}" must produce a valid date. Got: ${JSON.stringify(result)}`;
|
|
106012
106022
|
object._ruleErrors.push(error);
|
|
106013
106023
|
}
|
|
106014
106024
|
break;
|
|
@@ -106021,10 +106031,13 @@ class Action {
|
|
|
106021
106031
|
object[this.field] = String(result);
|
|
106022
106032
|
break;
|
|
106023
106033
|
}
|
|
106034
|
+
default: {
|
|
106035
|
+
break;
|
|
106036
|
+
}
|
|
106024
106037
|
}
|
|
106025
106038
|
}
|
|
106026
106039
|
catch (err) {
|
|
106027
|
-
const error = `Error executing formula for
|
|
106040
|
+
const error = `Error executing formula for "${this.field}": ${err instanceof Error ? err.message : String(err)}`;
|
|
106028
106041
|
object._ruleErrors.push(error);
|
|
106029
106042
|
break;
|
|
106030
106043
|
}
|
|
@@ -106046,7 +106059,7 @@ class Action {
|
|
|
106046
106059
|
object[this.field] = format$1(parsed, "yyyy-MM-dd");
|
|
106047
106060
|
}
|
|
106048
106061
|
else {
|
|
106049
|
-
logger.error(`rules: invalid date produced by template for field
|
|
106062
|
+
logger.error(`rules: invalid date produced by template for field "${this.field}":`, object[this.field]);
|
|
106050
106063
|
object[this.field] = "9999-12-31";
|
|
106051
106064
|
}
|
|
106052
106065
|
break;
|
|
@@ -106183,10 +106196,10 @@ const CONDITION_TYPES = {
|
|
|
106183
106196
|
const parsed = typeof value === "string" ? parseDateString(value) : value.frequency != null ? parseRecurDate(value) : null;
|
|
106184
106197
|
assert(parsed, "date-format", `Invalid date format (field: ${fieldName})`);
|
|
106185
106198
|
if (op === "isapprox") {
|
|
106186
|
-
assert(parsed.type === "date" || parsed.type === "recur", "date-format", `Invalid date value for
|
|
106199
|
+
assert(parsed.type === "date" || parsed.type === "recur", "date-format", `Invalid date value for "isapprox" (field: ${fieldName})`);
|
|
106187
106200
|
}
|
|
106188
106201
|
else if (op === "gt" || op === "gte" || op === "lt" || op === "lte") {
|
|
106189
|
-
assert(parsed.type === "date", "date-format", `Invalid date value for
|
|
106202
|
+
assert(parsed.type === "date", "date-format", `Invalid date value for "${op}" (field: ${fieldName})`);
|
|
106190
106203
|
}
|
|
106191
106204
|
return parsed;
|
|
106192
106205
|
}
|
|
@@ -106247,10 +106260,10 @@ const CONDITION_TYPES = {
|
|
|
106247
106260
|
const parsed = typeof value === "number" ? { type: "literal", value } : parseBetweenAmount(value);
|
|
106248
106261
|
assert(parsed != null, "not-number", `Value must be a number or between amount: ${JSON.stringify(value)} (field: ${fieldName})`);
|
|
106249
106262
|
if (op === "isbetween") {
|
|
106250
|
-
assert(parsed.type === "between", "number-format", `Invalid between value for
|
|
106263
|
+
assert(parsed.type === "between", "number-format", `Invalid between value for "${op}" (field: ${fieldName})`);
|
|
106251
106264
|
}
|
|
106252
106265
|
else {
|
|
106253
|
-
assert(parsed.type === "literal", "number-format", `Invalid number value for
|
|
106266
|
+
assert(parsed.type === "literal", "number-format", `Invalid number value for "${op}" (field: ${fieldName})`);
|
|
106254
106267
|
}
|
|
106255
106268
|
return parsed;
|
|
106256
106269
|
}
|
|
@@ -107565,9 +107578,7 @@ async function idsWithChildren(ids) {
|
|
|
107565
107578
|
return [...set];
|
|
107566
107579
|
}
|
|
107567
107580
|
async function getTransactionsByIds(ids) {
|
|
107568
|
-
return incrFetch((query, params) => selectWithSchema("transactions", query, params), ids,
|
|
107569
|
-
// eslint-disable-next-line actual/typography
|
|
107570
|
-
(id2) => `id = '${id2}'`, (where) => `SELECT * FROM v_transactions_internal WHERE ${where}`);
|
|
107581
|
+
return incrFetch((query, params) => selectWithSchema("transactions", query, params), ids, (id2) => `id = '${id2}'`, (where) => `SELECT * FROM v_transactions_internal WHERE ${where}`);
|
|
107571
107582
|
}
|
|
107572
107583
|
async function batchUpdateTransactions({ added, deleted, updated, learnCategories = false, detectOrphanPayees = true, runTransfers = true }) {
|
|
107573
107584
|
let addedIds = [];
|
|
@@ -108873,7 +108884,7 @@ function handleSyncError(err, acct) {
|
|
|
108873
108884
|
const syncError = {
|
|
108874
108885
|
type: "SyncError",
|
|
108875
108886
|
accountId: acct.id,
|
|
108876
|
-
message:
|
|
108887
|
+
message: 'Failed syncing account "' + acct.name + '."',
|
|
108877
108888
|
category: error.category,
|
|
108878
108889
|
code: error.code
|
|
108879
108890
|
};
|
|
@@ -108888,7 +108899,7 @@ function handleSyncError(err, acct) {
|
|
|
108888
108899
|
if (err instanceof PostError && err.reason !== "internal") {
|
|
108889
108900
|
return {
|
|
108890
108901
|
accountId: acct.id,
|
|
108891
|
-
message: err.reason ? err.reason : `Account
|
|
108902
|
+
message: err.reason ? err.reason : `Account "${acct.name}" is not linked properly. Please link it again.`
|
|
108892
108903
|
};
|
|
108893
108904
|
}
|
|
108894
108905
|
return {
|
|
@@ -108926,7 +108937,7 @@ async function accountsBankSync({ ids = [] }) {
|
|
|
108926
108937
|
errors2.push(handleSyncError(error, acct));
|
|
108927
108938
|
captureException({
|
|
108928
108939
|
...error,
|
|
108929
|
-
message:
|
|
108940
|
+
message: 'Failed syncing account "' + acct.name + '."'
|
|
108930
108941
|
});
|
|
108931
108942
|
}
|
|
108932
108943
|
finally {
|
|
@@ -108967,7 +108978,7 @@ async function simpleFinBatchSync({ ids = [] }) {
|
|
|
108967
108978
|
if (syncResponse.res.error_code) {
|
|
108968
108979
|
errors2.push(handleSyncError({
|
|
108969
108980
|
type: "BankSyncError",
|
|
108970
|
-
reason:
|
|
108981
|
+
reason: 'Failed syncing account "' + account.name + '."',
|
|
108971
108982
|
category: syncResponse.res.error_type,
|
|
108972
108983
|
code: syncResponse.res.error_code
|
|
108973
108984
|
}, account));
|
|
@@ -109294,10 +109305,10 @@ function getSyncError(error, id2) {
|
|
|
109294
109305
|
return t("This budget cannot be loaded with this version of the app.");
|
|
109295
109306
|
}
|
|
109296
109307
|
else if (error === "budget-not-found") {
|
|
109297
|
-
return t(
|
|
109308
|
+
return t('Budget "{{id}}" not found. Check the ID of your budget in the Advanced section of the settings page.', { id: id2 });
|
|
109298
109309
|
}
|
|
109299
109310
|
else {
|
|
109300
|
-
return t(
|
|
109311
|
+
return t('We had an unknown problem opening "{{id}}".', { id: id2 });
|
|
109301
109312
|
}
|
|
109302
109313
|
}
|
|
109303
109314
|
function getBankSyncError(error) {
|
|
@@ -109480,10 +109491,10 @@ async function validateExpenseCategory(debug, id2) {
|
|
|
109480
109491
|
}
|
|
109481
109492
|
const row = await first$2("SELECT is_income FROM categories WHERE id = ?", [id2]);
|
|
109482
109493
|
if (!row) {
|
|
109483
|
-
throw APIError(`${debug}: category
|
|
109494
|
+
throw APIError(`${debug}: category "${id2}" does not exist`);
|
|
109484
109495
|
}
|
|
109485
109496
|
if (row.is_income !== 0) {
|
|
109486
|
-
throw APIError(`${debug}: category
|
|
109497
|
+
throw APIError(`${debug}: category "${id2}" is not an expense category`);
|
|
109487
109498
|
}
|
|
109488
109499
|
}
|
|
109489
109500
|
function checkFileOpen() {
|
|
@@ -109544,7 +109555,7 @@ handlers["api/download-budget"] = async function ({ syncId, password }) {
|
|
|
109544
109555
|
}
|
|
109545
109556
|
const file = files.find((f2) => f2.groupId === syncId);
|
|
109546
109557
|
if (!file) {
|
|
109547
|
-
throw new Error(`Budget
|
|
109558
|
+
throw new Error(`Budget "${syncId}" not found. Check the sync id of your budget in the Advanced section of the settings page.`);
|
|
109548
109559
|
}
|
|
109549
109560
|
remoteBudget = file;
|
|
109550
109561
|
}
|
|
@@ -115660,6 +115671,7 @@ async function getUpcomingDates({ config: config2, count }) {
|
|
|
115660
115671
|
return schedule.occurrences({ start: startOfDay(/* @__PURE__ */ new Date()), take: count }).toArray().map((date) => config2.skipWeekend ? getDateWithSkippedWeekend(date.date, config2.weekendSolveMode) : date.date).map((date) => dayFromDate(date));
|
|
115661
115672
|
}
|
|
115662
115673
|
catch (err) {
|
|
115674
|
+
captureBreadcrumb(config2);
|
|
115663
115675
|
throw err;
|
|
115664
115676
|
}
|
|
115665
115677
|
}
|
|
@@ -118616,7 +118628,7 @@ Error: ${template.error}`);
|
|
|
118616
118628
|
}
|
|
118617
118629
|
}
|
|
118618
118630
|
else if (template.type === "schedule" && !scheduleNames.includes(template.name)) {
|
|
118619
|
-
errors2.push(`${name}: Schedule
|
|
118631
|
+
errors2.push(`${name}: Schedule "${template.name}" does not exist`);
|
|
118620
118632
|
}
|
|
118621
118633
|
});
|
|
118622
118634
|
});
|
|
@@ -119206,6 +119218,8 @@ class CategoryTemplateContext {
|
|
|
119206
119218
|
case "year":
|
|
119207
119219
|
dateShiftFunction = (date2, numPeriods2) => addMonths(date2, numPeriods2 * 12);
|
|
119208
119220
|
break;
|
|
119221
|
+
default:
|
|
119222
|
+
throw new Error(`Unrecognized periodic period: ${period}`);
|
|
119209
119223
|
}
|
|
119210
119224
|
while (templateContext.month > date) {
|
|
119211
119225
|
date = dateShiftFunction(date, numPeriods);
|
|
@@ -121001,6 +121015,8 @@ async function importTransactions(data, entityIdMap) {
|
|
|
121001
121015
|
case 1:
|
|
121002
121016
|
subtransactionIdx++;
|
|
121003
121017
|
break;
|
|
121018
|
+
default:
|
|
121019
|
+
throw new Error(`Unrecognized orphan transfer comparator result`);
|
|
121004
121020
|
}
|
|
121005
121021
|
} while (transactionIdx < transactions.length && subtransactionIdx < subtransactions.length);
|
|
121006
121022
|
}
|
|
@@ -121841,7 +121857,7 @@ const DEFAULT_DASHBOARD_STATE = [
|
|
|
121841
121857
|
x: 8,
|
|
121842
121858
|
y: 10,
|
|
121843
121859
|
meta: {
|
|
121844
|
-
content:
|
|
121860
|
+
content: '## Dashboard Tips\n\nYou can add new widgets or edit existing widgets by using the buttons at the top of the page. Choose a widget type and customize it to fit your needs.\n\n**Moving cards:** Drag any card by its header to reposition it.\n\n**Deleting cards:** Click the three-dot menu on any card and select "Remove".'
|
|
121845
121861
|
}
|
|
121846
121862
|
}
|
|
121847
121863
|
];
|
|
@@ -122079,7 +122095,7 @@ async function validateBudgetName(name) {
|
|
|
122079
122095
|
message2 = "Budget name is too long (max length 100)";
|
|
122080
122096
|
}
|
|
122081
122097
|
if (uniqueName !== trimmedName) {
|
|
122082
|
-
message2 =
|
|
122098
|
+
message2 = `"${name}" already exists, try "${uniqueName}" instead`;
|
|
122083
122099
|
}
|
|
122084
122100
|
return message2 ? { valid: false, message: message2 } : { valid: true };
|
|
122085
122101
|
}
|
|
@@ -122388,6 +122404,7 @@ async function createDemoBudget() {
|
|
|
122388
122404
|
});
|
|
122389
122405
|
}
|
|
122390
122406
|
async function closeBudget() {
|
|
122407
|
+
captureBreadcrumb({ message: "Closing budget" });
|
|
122391
122408
|
await waitOnSpreadsheet();
|
|
122392
122409
|
unloadSpreadsheet();
|
|
122393
122410
|
clearFullSyncTimeout();
|
|
@@ -122555,6 +122572,7 @@ async function _loadBudget(id2) {
|
|
|
122555
122572
|
captureException(new Error("`getBudgetDir` failed in `loadBudget`: " + e.message));
|
|
122556
122573
|
return { error: "budget-not-found" };
|
|
122557
122574
|
}
|
|
122575
|
+
captureBreadcrumb({ message: "Loading budget " + dir });
|
|
122558
122576
|
if (!await exists(dir)) {
|
|
122559
122577
|
captureException(new Error("budget directory does not exist"));
|
|
122560
122578
|
return { error: "budget-not-found" };
|
|
@@ -122564,6 +122582,7 @@ async function _loadBudget(id2) {
|
|
|
122564
122582
|
await openDatabase(id2);
|
|
122565
122583
|
}
|
|
122566
122584
|
catch (e) {
|
|
122585
|
+
captureBreadcrumb({ message: "Error loading budget " + id2 });
|
|
122567
122586
|
captureException(e);
|
|
122568
122587
|
await closeBudget();
|
|
122569
122588
|
return { error: "opening-budget" };
|
|
@@ -133823,7 +133842,6 @@ async function parseCSV(filepath, options) {
|
|
|
133823
133842
|
columns: options?.hasHeaderRow,
|
|
133824
133843
|
bom: true,
|
|
133825
133844
|
delimiter: options?.delimiter || ",",
|
|
133826
|
-
// eslint-disable-next-line actual/typography
|
|
133827
133845
|
quote: '"',
|
|
133828
133846
|
trim: true,
|
|
133829
133847
|
relax_column_count: true,
|
|
@@ -133848,7 +133866,7 @@ async function parseQIF(filepath, options = {}) {
|
|
|
133848
133866
|
}
|
|
133849
133867
|
catch (err) {
|
|
133850
133868
|
errors2.push({
|
|
133851
|
-
message: "Failed parsing: doesn
|
|
133869
|
+
message: "Failed parsing: doesn't look like a valid QIF file.",
|
|
133852
133870
|
internal: err.stack
|
|
133853
133871
|
});
|
|
133854
133872
|
return { errors: errors2, transactions: [] };
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.default = runMigration;
|
|
4
4
|
const uuid_1 = require("uuid");
|
|
5
5
|
const dashboard_1 = require("../src/shared/dashboard");
|
|
6
|
-
/* eslint-disable actual/typography */
|
|
7
6
|
async function runMigration(db) {
|
|
8
7
|
db.transaction(() => {
|
|
9
8
|
const reports = db.runQuery('SELECT id FROM custom_reports WHERE tombstone = 0 ORDER BY name COLLATE NOCASE ASC', [], true);
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@actual-app/api",
|
|
3
|
-
"version": "26.1.0-nightly.
|
|
3
|
+
"version": "26.1.0-nightly.20251221",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "An API for Actual",
|
|
6
6
|
"engines": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"build:migrations": "cp migrations/*.sql dist/migrations",
|
|
20
20
|
"build:default-db": "cp default-db.sqlite dist/",
|
|
21
21
|
"build": "yarn run clean && yarn run build:app && yarn run build:node && yarn run build:migrations && yarn run build:default-db",
|
|
22
|
-
"test": "yarn run build:app && yarn run build:crdt && vitest --run",
|
|
22
|
+
"test": "yarn run clean && yarn run build:app && yarn run build:crdt && vitest --run",
|
|
23
23
|
"clean": "rm -rf dist @types"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@actual-app/api",
|
|
3
|
-
"version": "26.1.0-nightly.
|
|
3
|
+
"version": "26.1.0-nightly.20251221",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "An API for Actual",
|
|
6
6
|
"engines": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"build:migrations": "cp migrations/*.sql dist/migrations",
|
|
20
20
|
"build:default-db": "cp default-db.sqlite dist/",
|
|
21
21
|
"build": "yarn run clean && yarn run build:app && yarn run build:node && yarn run build:migrations && yarn run build:default-db",
|
|
22
|
-
"test": "yarn run build:app && yarn run build:crdt && vitest --run",
|
|
22
|
+
"test": "yarn run clean && yarn run build:app && yarn run build:crdt && vitest --run",
|
|
23
23
|
"clean": "rm -rf dist @types"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|