@actual-app/api 25.12.0-nightly.20251107 → 25.12.0-nightly.20251109
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/@types/loot-core/src/server/budget/category-template-context.d.ts +3 -2
- package/@types/loot-core/src/shared/currencies.d.ts +11 -0
- package/@types/loot-core/src/types/models/dashboard.d.ts +11 -1
- package/@types/loot-core/src/types/prefs.d.ts +1 -1
- package/dist/app/bundle.api.js +65 -19
- package/dist/package.json +1 -1
- package/package.json +1 -1
|
@@ -34,7 +34,8 @@ export declare class CategoryTemplateContext {
|
|
|
34
34
|
private limitCheck;
|
|
35
35
|
private limitHold;
|
|
36
36
|
readonly previouslyBudgeted: number;
|
|
37
|
-
|
|
37
|
+
private currency;
|
|
38
|
+
protected constructor(templates: Template[], category: CategoryEntity, month: string, fromLastMonth: number, budgeted: number, currencyCode: string, hideDecimal?: boolean);
|
|
38
39
|
private runGoal;
|
|
39
40
|
static checkByAndScheduleAndSpend(templates: Template[], month: string): Promise<void>;
|
|
40
41
|
static checkPercentage(templates: Template[]): Promise<void>;
|
|
@@ -42,7 +43,7 @@ export declare class CategoryTemplateContext {
|
|
|
42
43
|
private checkSpend;
|
|
43
44
|
private checkGoal;
|
|
44
45
|
private removeFraction;
|
|
45
|
-
static runSimple(template: SimpleTemplate,
|
|
46
|
+
static runSimple(template: SimpleTemplate, templateContext: CategoryTemplateContext): number;
|
|
46
47
|
static runCopy(template: CopyTemplate, templateContext: CategoryTemplateContext): Promise<number>;
|
|
47
48
|
static runPeriodic(template: PeriodicTemplate, templateContext: CategoryTemplateContext): number;
|
|
48
49
|
static runSpend(template: SpendTemplate, templateContext: CategoryTemplateContext): Promise<number>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { NumberFormats } from './util';
|
|
2
|
+
export type Currency = {
|
|
3
|
+
code: string;
|
|
4
|
+
symbol: string;
|
|
5
|
+
name: string;
|
|
6
|
+
decimalPlaces: number;
|
|
7
|
+
numberFormat: NumberFormats;
|
|
8
|
+
symbolFirst: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare const currencies: Currency[];
|
|
11
|
+
export declare function getCurrency(code: string): Currency;
|
|
@@ -41,11 +41,21 @@ export type SpendingWidget = AbstractWidget<'spending-card', {
|
|
|
41
41
|
export type CustomReportWidget = AbstractWidget<'custom-report', {
|
|
42
42
|
id: string;
|
|
43
43
|
}>;
|
|
44
|
+
export type CrossoverWidget = AbstractWidget<'crossover-card', {
|
|
45
|
+
name?: string;
|
|
46
|
+
expenseCategoryIds?: string[];
|
|
47
|
+
incomeAccountIds?: string[];
|
|
48
|
+
timeFrame?: TimeFrame;
|
|
49
|
+
safeWithdrawalRate?: number;
|
|
50
|
+
estimatedReturn?: number | null;
|
|
51
|
+
projectionType?: 'trend' | 'hampel';
|
|
52
|
+
showHiddenCategories?: boolean;
|
|
53
|
+
} | null>;
|
|
44
54
|
export type MarkdownWidget = AbstractWidget<'markdown-card', {
|
|
45
55
|
content: string;
|
|
46
56
|
text_align?: 'left' | 'right' | 'center';
|
|
47
57
|
}>;
|
|
48
|
-
type SpecializedWidget = NetWorthWidget | CashFlowWidget | SpendingWidget | MarkdownWidget | SummaryWidget | CalendarWidget | FormulaWidget;
|
|
58
|
+
type SpecializedWidget = NetWorthWidget | CashFlowWidget | SpendingWidget | CrossoverWidget | MarkdownWidget | SummaryWidget | CalendarWidget | FormulaWidget;
|
|
49
59
|
export type Widget = SpecializedWidget | CustomReportWidget;
|
|
50
60
|
export type NewWidget = Omit<Widget, 'id' | 'tombstone'>;
|
|
51
61
|
export type ExportImportCustomReportWidget = Omit<CustomReportWidget, 'id' | 'meta' | 'tombstone'> & {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type FeatureFlag = 'goalTemplatesEnabled' | 'goalTemplatesUIEnabled' | 'actionTemplating' | 'formulaMode' | 'currency' | 'plugins';
|
|
1
|
+
export type FeatureFlag = 'goalTemplatesEnabled' | 'goalTemplatesUIEnabled' | 'actionTemplating' | 'formulaMode' | 'currency' | 'crossoverReport' | 'plugins';
|
|
2
2
|
/**
|
|
3
3
|
* Cross-device preferences. These sync across devices when they are changed.
|
|
4
4
|
*/
|
package/dist/app/bundle.api.js
CHANGED
|
@@ -108908,7 +108908,7 @@ async function reconcileTransactions(acctId, transactions, isBankSyncAccount = f
|
|
|
108908
108908
|
category: existing.category || trans.category || null,
|
|
108909
108909
|
imported_payee: trans.imported_payee || null,
|
|
108910
108910
|
notes: existing.notes || trans.notes || null,
|
|
108911
|
-
cleared:
|
|
108911
|
+
cleared: existing.cleared || trans.cleared || false,
|
|
108912
108912
|
raw_synced_data: existing.raw_synced_data ?? trans.raw_synced_data ?? null
|
|
108913
108913
|
};
|
|
108914
108914
|
const fieldsToMarkUpdated = Object.keys(updates).filter((k) => {
|
|
@@ -112125,6 +112125,45 @@ async function getCategoryTemplates() {
|
|
|
112125
112125
|
}
|
|
112126
112126
|
return templates;
|
|
112127
112127
|
}
|
|
112128
|
+
const currencies = [
|
|
112129
|
+
{ code: "", name: "None", symbol: "", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: true },
|
|
112130
|
+
{ code: "AED", name: "UAE Dirham", symbol: "د.إ", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: false },
|
|
112131
|
+
{ code: "ARS", name: "Argentinian Peso", symbol: "Arg$", decimalPlaces: 2, numberFormat: "dot-comma", symbolFirst: true },
|
|
112132
|
+
{ code: "AUD", name: "Australian Dollar", symbol: "A$", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: true },
|
|
112133
|
+
{ code: "BRL", name: "Brazilian Real", symbol: "R$", decimalPlaces: 2, numberFormat: "dot-comma", symbolFirst: true },
|
|
112134
|
+
{ code: "CAD", name: "Canadian Dollar", symbol: "CA$", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: true },
|
|
112135
|
+
{ code: "CHF", name: "Swiss Franc", symbol: "Fr.", decimalPlaces: 2, numberFormat: "apostrophe-dot", symbolFirst: true },
|
|
112136
|
+
{ code: "CNY", name: "Yuan Renminbi", symbol: "¥", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: true },
|
|
112137
|
+
{ code: "CRC", name: "Costa Rican Colón", symbol: "₡", decimalPlaces: 2, numberFormat: "space-comma", symbolFirst: true },
|
|
112138
|
+
{ code: "DKK", name: "Danish Krone", symbol: "kr", decimalPlaces: 2, numberFormat: "dot-comma", symbolFirst: false },
|
|
112139
|
+
{ code: "EGP", name: "Egyptian Pound", symbol: "ج.م", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: false },
|
|
112140
|
+
{ code: "EUR", name: "Euro", symbol: "€", decimalPlaces: 2, numberFormat: "dot-comma", symbolFirst: false },
|
|
112141
|
+
{ code: "GBP", name: "Pound Sterling", symbol: "£", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: true },
|
|
112142
|
+
{ code: "GTQ", name: "Guatemalan Quetzal", symbol: "Q", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: true },
|
|
112143
|
+
{ code: "HKD", name: "Hong Kong Dollar", symbol: "HK$", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: true },
|
|
112144
|
+
{ code: "INR", name: "Indian Rupee", symbol: "₹", decimalPlaces: 2, numberFormat: "comma-dot-in", symbolFirst: true },
|
|
112145
|
+
{ code: "JMD", name: "Jamaican Dollar", symbol: "J$", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: true },
|
|
112146
|
+
{ code: "JPY", name: "Japanese Yen", symbol: "¥", decimalPlaces: 0, numberFormat: "comma-dot", symbolFirst: true },
|
|
112147
|
+
{ code: "LKR", name: "Sri Lankan Rupee", symbol: "Rs.", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: true },
|
|
112148
|
+
{ code: "MDL", name: "Moldovan Leu", symbol: "L", decimalPlaces: 2, numberFormat: "dot-comma", symbolFirst: false },
|
|
112149
|
+
{ code: "PHP", name: "Philippine Peso", symbol: "₱", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: true },
|
|
112150
|
+
{ code: "PLN", name: "Polish Złoty", symbol: "zł", decimalPlaces: 2, numberFormat: "space-comma", symbolFirst: false },
|
|
112151
|
+
{ code: "QAR", name: "Qatari Riyal", symbol: "ر.ق", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: false },
|
|
112152
|
+
{ code: "RON", name: "Romanian Leu", symbol: "lei", decimalPlaces: 2, numberFormat: "dot-comma", symbolFirst: false },
|
|
112153
|
+
{ code: "RSD", name: "Serbian Dinar", symbol: "дин", decimalPlaces: 2, numberFormat: "dot-comma", symbolFirst: false },
|
|
112154
|
+
{ code: "RUB", name: "Russian Ruble", symbol: "₽", decimalPlaces: 2, numberFormat: "space-comma", symbolFirst: false },
|
|
112155
|
+
{ code: "SAR", name: "Saudi Riyal", symbol: "ر.س", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: false },
|
|
112156
|
+
{ code: "SEK", name: "Swedish Krona", symbol: "kr", decimalPlaces: 2, numberFormat: "space-comma", symbolFirst: false },
|
|
112157
|
+
{ code: "SGD", name: "Singapore Dollar", symbol: "S$", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: true },
|
|
112158
|
+
{ code: "THB", name: "Thai Baht", symbol: "฿", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: true },
|
|
112159
|
+
{ code: "TRY", name: "Turkish Lira", symbol: "₺", decimalPlaces: 2, numberFormat: "dot-comma", symbolFirst: true },
|
|
112160
|
+
{ code: "UAH", name: "Ukrainian Hryvnia", symbol: "₴", decimalPlaces: 2, numberFormat: "space-comma", symbolFirst: false },
|
|
112161
|
+
{ code: "USD", name: "US Dollar", symbol: "$", decimalPlaces: 2, numberFormat: "comma-dot", symbolFirst: true },
|
|
112162
|
+
{ code: "UZS", name: "Uzbek Soum", symbol: "UZS", decimalPlaces: 2, numberFormat: "space-comma", symbolFirst: false }
|
|
112163
|
+
];
|
|
112164
|
+
function getCurrency(code) {
|
|
112165
|
+
return currencies.find((c) => c.code === code) || currencies[0];
|
|
112166
|
+
}
|
|
112128
112167
|
var isArguments$1;
|
|
112129
112168
|
var hasRequiredIsArguments$2;
|
|
112130
112169
|
function requireIsArguments$2() {
|
|
@@ -119644,7 +119683,9 @@ class CategoryTemplateContext {
|
|
|
119644
119683
|
await CategoryTemplateContext.checkByAndScheduleAndSpend(templates, month);
|
|
119645
119684
|
await CategoryTemplateContext.checkPercentage(templates);
|
|
119646
119685
|
const hideDecimal = await aqlQuery(q("preferences").filter({ id: "hideFraction" }).select("*"));
|
|
119647
|
-
|
|
119686
|
+
const currencyPref = await aqlQuery(q("preferences").filter({ id: "defaultCurrencyCode" }).select("*"));
|
|
119687
|
+
const currencyCode = currencyPref.data.length > 0 ? currencyPref.data[0].value : "";
|
|
119688
|
+
return new CategoryTemplateContext(templates, category, month, fromLastMonth, budgeted, currencyCode, hideDecimal.data.length > 0 ? hideDecimal.data[0].value === "true" : false);
|
|
119648
119689
|
}
|
|
119649
119690
|
isGoalOnly() {
|
|
119650
119691
|
return this.templates.length === 0 && this.remainder.length === 0 && this.goals.length > 0;
|
|
@@ -119688,7 +119729,7 @@ class CategoryTemplateContext {
|
|
|
119688
119729
|
let newBudget = 0;
|
|
119689
119730
|
switch (template.type) {
|
|
119690
119731
|
case "simple": {
|
|
119691
|
-
newBudget = CategoryTemplateContext.runSimple(template, this
|
|
119732
|
+
newBudget = CategoryTemplateContext.runSimple(template, this);
|
|
119692
119733
|
break;
|
|
119693
119734
|
}
|
|
119694
119735
|
case "copy": {
|
|
@@ -119785,7 +119826,7 @@ class CategoryTemplateContext {
|
|
|
119785
119826
|
longGoal: this.isLongGoal
|
|
119786
119827
|
};
|
|
119787
119828
|
}
|
|
119788
|
-
constructor(templates, category, month, fromLastMonth, budgeted, hideDecimal = false) {
|
|
119829
|
+
constructor(templates, category, month, fromLastMonth, budgeted, currencyCode, hideDecimal = false) {
|
|
119789
119830
|
this.templates = [];
|
|
119790
119831
|
this.remainder = [];
|
|
119791
119832
|
this.goals = [];
|
|
@@ -119811,6 +119852,7 @@ class CategoryTemplateContext {
|
|
|
119811
119852
|
this.month = month;
|
|
119812
119853
|
this.fromLastMonth = fromLastMonth;
|
|
119813
119854
|
this.previouslyBudgeted = budgeted;
|
|
119855
|
+
this.currency = getCurrency(currencyCode);
|
|
119814
119856
|
this.hideDecimal = hideDecimal;
|
|
119815
119857
|
if (templates) {
|
|
119816
119858
|
templates.forEach((t2) => {
|
|
@@ -119837,7 +119879,7 @@ class CategoryTemplateContext {
|
|
|
119837
119879
|
if (this.isGoalOnly())
|
|
119838
119880
|
this.toBudgetAmount = this.previouslyBudgeted;
|
|
119839
119881
|
this.isLongGoal = true;
|
|
119840
|
-
this.goalAmount = amountToInteger(this.goals[0].amount);
|
|
119882
|
+
this.goalAmount = amountToInteger(this.goals[0].amount, this.currency.decimalPlaces);
|
|
119841
119883
|
return;
|
|
119842
119884
|
}
|
|
119843
119885
|
this.goalAmount = this.fullAmount;
|
|
@@ -119901,12 +119943,15 @@ class CategoryTemplateContext {
|
|
|
119901
119943
|
}
|
|
119902
119944
|
if (limitDef.period === "daily") {
|
|
119903
119945
|
const numDays2 = differenceInCalendarDays(addMonths(this.month, 1), this.month);
|
|
119904
|
-
this.limitAmount += amountToInteger(limitDef.amount) * numDays2;
|
|
119946
|
+
this.limitAmount += amountToInteger(limitDef.amount, this.currency.decimalPlaces) * numDays2;
|
|
119905
119947
|
}
|
|
119906
119948
|
else if (limitDef.period === "weekly") {
|
|
119949
|
+
if (!limitDef.start) {
|
|
119950
|
+
throw new Error("Weekly limit requires a start date (YYYY-MM-DD)");
|
|
119951
|
+
}
|
|
119907
119952
|
const nextMonth$1 = nextMonth(this.month);
|
|
119908
119953
|
let week2 = limitDef.start;
|
|
119909
|
-
const baseLimit = amountToInteger(limitDef.amount);
|
|
119954
|
+
const baseLimit = amountToInteger(limitDef.amount, this.currency.decimalPlaces);
|
|
119910
119955
|
while (week2 < nextMonth$1) {
|
|
119911
119956
|
if (week2 >= this.month) {
|
|
119912
119957
|
this.limitAmount += baseLimit;
|
|
@@ -119915,7 +119960,7 @@ class CategoryTemplateContext {
|
|
|
119915
119960
|
}
|
|
119916
119961
|
}
|
|
119917
119962
|
else if (limitDef.period === "monthly") {
|
|
119918
|
-
this.limitAmount = amountToInteger(limitDef.amount);
|
|
119963
|
+
this.limitAmount = amountToInteger(limitDef.amount, this.currency.decimalPlaces);
|
|
119919
119964
|
}
|
|
119920
119965
|
else {
|
|
119921
119966
|
throw new Error("Invalid limit period. Check template syntax");
|
|
@@ -119949,16 +119994,16 @@ class CategoryTemplateContext {
|
|
|
119949
119994
|
}
|
|
119950
119995
|
}
|
|
119951
119996
|
removeFraction(amount) {
|
|
119952
|
-
return amountToInteger(Math.round(integerToAmount(amount)));
|
|
119997
|
+
return amountToInteger(Math.round(integerToAmount(amount, this.currency.decimalPlaces)), this.currency.decimalPlaces);
|
|
119953
119998
|
}
|
|
119954
119999
|
//-----------------------------------------------------------------------------
|
|
119955
120000
|
// Processor Functions
|
|
119956
|
-
static runSimple(template,
|
|
120001
|
+
static runSimple(template, templateContext) {
|
|
119957
120002
|
if (template.monthly != null) {
|
|
119958
|
-
return amountToInteger(template.monthly);
|
|
120003
|
+
return amountToInteger(template.monthly, templateContext.currency.decimalPlaces);
|
|
119959
120004
|
}
|
|
119960
120005
|
else {
|
|
119961
|
-
return
|
|
120006
|
+
return templateContext.limitAmount;
|
|
119962
120007
|
}
|
|
119963
120008
|
}
|
|
119964
120009
|
static async runCopy(template, templateContext) {
|
|
@@ -119967,7 +120012,7 @@ class CategoryTemplateContext {
|
|
|
119967
120012
|
}
|
|
119968
120013
|
static runPeriodic(template, templateContext) {
|
|
119969
120014
|
let toBudget = 0;
|
|
119970
|
-
const amount = amountToInteger(template.amount);
|
|
120015
|
+
const amount = amountToInteger(template.amount, templateContext.currency.decimalPlaces);
|
|
119971
120016
|
const period = template.period.period;
|
|
119972
120017
|
const numPeriods = template.period.amount;
|
|
119973
120018
|
let date = template.starting;
|
|
@@ -120026,7 +120071,7 @@ class CategoryTemplateContext {
|
|
|
120026
120071
|
}
|
|
120027
120072
|
}
|
|
120028
120073
|
const numMonths = differenceInCalendarMonths(toMonth, templateContext.month);
|
|
120029
|
-
const target = amountToInteger(template.amount);
|
|
120074
|
+
const target = amountToInteger(template.amount, templateContext.currency.decimalPlaces);
|
|
120030
120075
|
if (numMonths < 0) {
|
|
120031
120076
|
return 0;
|
|
120032
120077
|
}
|
|
@@ -120091,13 +120136,13 @@ class CategoryTemplateContext {
|
|
|
120091
120136
|
const period = savedInfo[i].period;
|
|
120092
120137
|
let amount;
|
|
120093
120138
|
if (numMonths > shortNumMonths && period) {
|
|
120094
|
-
amount = Math.round(amountToInteger(template.amount) / period * (period - numMonths + shortNumMonths));
|
|
120139
|
+
amount = Math.round(amountToInteger(template.amount, templateContext.currency.decimalPlaces) / period * (period - numMonths + shortNumMonths));
|
|
120095
120140
|
}
|
|
120096
120141
|
else if (numMonths > shortNumMonths) {
|
|
120097
|
-
amount = Math.round(amountToInteger(template.amount) / (numMonths + 1) * (shortNumMonths + 1));
|
|
120142
|
+
amount = Math.round(amountToInteger(template.amount, templateContext.currency.decimalPlaces) / (numMonths + 1) * (shortNumMonths + 1));
|
|
120098
120143
|
}
|
|
120099
120144
|
else {
|
|
120100
|
-
amount = amountToInteger(template.amount);
|
|
120145
|
+
amount = amountToInteger(template.amount, templateContext.currency.decimalPlaces);
|
|
120101
120146
|
}
|
|
120102
120147
|
totalNeeded += amount;
|
|
120103
120148
|
}
|
|
@@ -120119,13 +120164,13 @@ async function storeTemplates({ categoriesWithTemplates, source }) {
|
|
|
120119
120164
|
async function applyTemplate({ month }) {
|
|
120120
120165
|
await storeNoteTemplates();
|
|
120121
120166
|
const categoryTemplates = await getTemplates();
|
|
120122
|
-
const ret = await processTemplate(month, false, categoryTemplates);
|
|
120167
|
+
const ret = await processTemplate(month, false, categoryTemplates, []);
|
|
120123
120168
|
return ret;
|
|
120124
120169
|
}
|
|
120125
120170
|
async function overwriteTemplate({ month }) {
|
|
120126
120171
|
await storeNoteTemplates();
|
|
120127
120172
|
const categoryTemplates = await getTemplates();
|
|
120128
|
-
const ret = await processTemplate(month, true, categoryTemplates);
|
|
120173
|
+
const ret = await processTemplate(month, true, categoryTemplates, []);
|
|
120129
120174
|
return ret;
|
|
120130
120175
|
}
|
|
120131
120176
|
async function applyMultipleCategoryTemplates({ month, categoryIds }) {
|
|
@@ -125214,6 +125259,7 @@ const exportModel = {
|
|
|
125214
125259
|
"net-worth-card",
|
|
125215
125260
|
"cash-flow-card",
|
|
125216
125261
|
"spending-card",
|
|
125262
|
+
"crossover-card",
|
|
125217
125263
|
"custom-report",
|
|
125218
125264
|
"markdown-card",
|
|
125219
125265
|
"summary-card",
|
package/dist/package.json
CHANGED