taxtank-core 2.0.38 → 2.0.40
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/fesm2022/taxtank-core.mjs +154 -160
- package/fesm2022/taxtank-core.mjs.map +1 -1
- package/index.d.ts +13 -15
- package/package.json +1 -1
- package/index.d.ts.map +0 -1
|
@@ -511,6 +511,10 @@ __decorate([
|
|
|
511
511
|
Type(() => Number)
|
|
512
512
|
], Loan$1.prototype, "repaymentAmount", void 0);
|
|
513
513
|
|
|
514
|
+
// @todo: taxtankit TT-5413 for now it's for stubs only. Need to make real solve for Goals and Expenses/Incomes
|
|
515
|
+
class MoneyScheduleItem extends AbstractModel {
|
|
516
|
+
}
|
|
517
|
+
|
|
514
518
|
let PropertySaleTaxExemptionMetaField$1 = class PropertySaleTaxExemptionMetaField extends AbstractModel {
|
|
515
519
|
};
|
|
516
520
|
|
|
@@ -9291,27 +9295,15 @@ class DocumentFolderCollection extends Collection {
|
|
|
9291
9295
|
}
|
|
9292
9296
|
}
|
|
9293
9297
|
|
|
9298
|
+
class BudgetCollection extends Collection {
|
|
9299
|
+
}
|
|
9300
|
+
|
|
9294
9301
|
class BudgetRuleCollection extends Collection {
|
|
9295
9302
|
get expenses() {
|
|
9296
9303
|
return this.filter(rule => rule.chartAccounts.isExpense());
|
|
9297
9304
|
}
|
|
9298
9305
|
get incomes() {
|
|
9299
|
-
return this.filter(rule => rule.chartAccounts.
|
|
9300
|
-
}
|
|
9301
|
-
}
|
|
9302
|
-
|
|
9303
|
-
class BudgetCollection extends Collection {
|
|
9304
|
-
get chartAccountsIds() {
|
|
9305
|
-
return this.mapBy('chartAccountsIds').flat();
|
|
9306
|
-
}
|
|
9307
|
-
get rules() {
|
|
9308
|
-
return new BudgetRuleCollection(this.mapBy('rules').flat());
|
|
9309
|
-
}
|
|
9310
|
-
get expense() {
|
|
9311
|
-
return this.findBy('type', BudgetTypeEnum.EXPENSE);
|
|
9312
|
-
}
|
|
9313
|
-
get income() {
|
|
9314
|
-
return this.findBy('type', BudgetTypeEnum.INCOME);
|
|
9306
|
+
return this.filter(rule => rule.chartAccounts.isIncome());
|
|
9315
9307
|
}
|
|
9316
9308
|
}
|
|
9317
9309
|
|
|
@@ -11361,8 +11353,8 @@ var SharesightPortfolioMessages;
|
|
|
11361
11353
|
class BudgetRule extends BudgetRule$1 {
|
|
11362
11354
|
constructor() {
|
|
11363
11355
|
super(...arguments);
|
|
11356
|
+
this.startDate = new Date();
|
|
11364
11357
|
this.inCalendar = false;
|
|
11365
|
-
this.frequency = DailyFrequencyEnum.MONTHLY;
|
|
11366
11358
|
}
|
|
11367
11359
|
}
|
|
11368
11360
|
__decorate([
|
|
@@ -11415,17 +11407,12 @@ var YoutubeVideosEnum;
|
|
|
11415
11407
|
})(YoutubeVideosEnum || (YoutubeVideosEnum = {}));
|
|
11416
11408
|
|
|
11417
11409
|
class Budget extends Budget$1 {
|
|
11418
|
-
|
|
11419
|
-
|
|
11420
|
-
|
|
11421
|
-
get amount() {
|
|
11422
|
-
return this.rulesCollection.sumBy('amount');
|
|
11423
|
-
}
|
|
11424
|
-
get chartAccountsIds() {
|
|
11425
|
-
return this.rulesCollection.mapBy('chartAccounts.id');
|
|
11410
|
+
constructor() {
|
|
11411
|
+
super(...arguments);
|
|
11412
|
+
this.financialYear = new FinancialYear().year;
|
|
11426
11413
|
}
|
|
11427
|
-
getMetadata() {
|
|
11428
|
-
return
|
|
11414
|
+
getMetadata(isIncome) {
|
|
11415
|
+
return isIncome ? {
|
|
11429
11416
|
name: 'Income',
|
|
11430
11417
|
thumbnail: 'income-budget.png',
|
|
11431
11418
|
videoId: YoutubeVideosEnum.BUDGET_INCOME,
|
|
@@ -11439,11 +11426,14 @@ class Budget extends Budget$1 {
|
|
|
11439
11426
|
categories: EXPENSE_CATEGORY_BY_TYPE,
|
|
11440
11427
|
};
|
|
11441
11428
|
}
|
|
11442
|
-
|
|
11443
|
-
|
|
11429
|
+
/**
|
|
11430
|
+
* @TODO TT-4522 remove after release
|
|
11431
|
+
*/
|
|
11432
|
+
get rulesCollection() {
|
|
11433
|
+
return new BudgetRuleCollection(this.rules);
|
|
11444
11434
|
}
|
|
11445
|
-
|
|
11446
|
-
return this.
|
|
11435
|
+
get amount() {
|
|
11436
|
+
return this.rulesCollection.sumBy('amount');
|
|
11447
11437
|
}
|
|
11448
11438
|
}
|
|
11449
11439
|
__decorate([
|
|
@@ -22883,6 +22873,9 @@ class AddressForm extends AbstractForm {
|
|
|
22883
22873
|
}
|
|
22884
22874
|
}
|
|
22885
22875
|
|
|
22876
|
+
/**
|
|
22877
|
+
* @TODO TT-4522 remove after release
|
|
22878
|
+
*/
|
|
22886
22879
|
class BudgetForm extends AbstractForm {
|
|
22887
22880
|
constructor(budget) {
|
|
22888
22881
|
super({
|
|
@@ -22944,135 +22937,6 @@ class BudgetForm extends AbstractForm {
|
|
|
22944
22937
|
}
|
|
22945
22938
|
}
|
|
22946
22939
|
|
|
22947
|
-
class BudgetRuleForm extends AbstractForm {
|
|
22948
|
-
constructor(rule) {
|
|
22949
|
-
super({
|
|
22950
|
-
tankType: new FormControl(rule.chartAccounts?.tankType ?? TankTypeEnum.PERSONAL, [Validators.required]),
|
|
22951
|
-
chartAccounts: new FormControl({ value: rule.chartAccounts, disabled: !!rule.chartAccounts?.category }, [Validators.required, RxwebValidators.unique()]),
|
|
22952
|
-
amount: new FormControl(rule.amount, Validators.required),
|
|
22953
|
-
startDate: new FormControl({ value: rule.startDate, disabled: true }),
|
|
22954
|
-
endDate: new FormControl(rule.endDate),
|
|
22955
|
-
inCalendar: new FormControl(rule.inCalendar),
|
|
22956
|
-
frequency: new FormControl(rule.frequency),
|
|
22957
|
-
bankAccount: new FormControl(rule.endDate),
|
|
22958
|
-
property: new FormControl(rule.property),
|
|
22959
|
-
business: new FormControl(rule.business),
|
|
22960
|
-
incomeSource: new FormControl(rule.incomeSource),
|
|
22961
|
-
}, rule);
|
|
22962
|
-
}
|
|
22963
|
-
}
|
|
22964
|
-
|
|
22965
|
-
/**
|
|
22966
|
-
* general validation rules
|
|
22967
|
-
*/
|
|
22968
|
-
var FormValidationsEnum;
|
|
22969
|
-
(function (FormValidationsEnum) {
|
|
22970
|
-
FormValidationsEnum[FormValidationsEnum["INPUT_MAX_LENGTH"] = 60] = "INPUT_MAX_LENGTH";
|
|
22971
|
-
FormValidationsEnum[FormValidationsEnum["TEXT_MAX_LENGTH"] = 300] = "TEXT_MAX_LENGTH";
|
|
22972
|
-
FormValidationsEnum[FormValidationsEnum["FILE_MAX_SIZE"] = 4194304] = "FILE_MAX_SIZE";
|
|
22973
|
-
// @TODO move to file uploader component
|
|
22974
|
-
FormValidationsEnum["FILE_ACCEPTED_TYPES"] = "Accepted file type: .doc .jpg .png .pdf .tiff .bmp .excel .csv";
|
|
22975
|
-
})(FormValidationsEnum || (FormValidationsEnum = {}));
|
|
22976
|
-
|
|
22977
|
-
class DocumentFolderForm extends AbstractForm {
|
|
22978
|
-
constructor(folder) {
|
|
22979
|
-
super({
|
|
22980
|
-
name: new FormControl(folder.name, [Validators.required, Validators.maxLength(FormValidationsEnum.INPUT_MAX_LENGTH)]),
|
|
22981
|
-
parent: new FormControl(folder.parent),
|
|
22982
|
-
}, folder);
|
|
22983
|
-
}
|
|
22984
|
-
}
|
|
22985
|
-
|
|
22986
|
-
class DocumentForm extends AbstractForm {
|
|
22987
|
-
constructor(document) {
|
|
22988
|
-
super({
|
|
22989
|
-
folder: new FormControl(document.folder, Validators.required),
|
|
22990
|
-
file: new FormControl(document.file, Validators.required),
|
|
22991
|
-
}, document);
|
|
22992
|
-
}
|
|
22993
|
-
}
|
|
22994
|
-
|
|
22995
|
-
/**
|
|
22996
|
-
* Form with loan details.
|
|
22997
|
-
* Loan could be created from bank account (Bank Loan) or directly from loan page (Vehicle Loan)
|
|
22998
|
-
*/
|
|
22999
|
-
class LoanForm extends AbstractForm {
|
|
23000
|
-
static { this.mortgageLoanTypes = [LoanTypeEnum.MORTGAGE, LoanTypeEnum.HOME_EQUITY_LINE_OF_CREDIT, LoanTypeEnum.HOME_LOAN]; }
|
|
23001
|
-
constructor(loan = plainToClass(Loan, {})) {
|
|
23002
|
-
super({
|
|
23003
|
-
type: new UntypedFormControl(loan.type, Validators.required),
|
|
23004
|
-
amount: new UntypedFormControl(loan.amount, Validators.required),
|
|
23005
|
-
interestRate: new UntypedFormControl(loan.interestRate, [Validators.required, Validators.min(0), Validators.max(100)]),
|
|
23006
|
-
commencementDate: new UntypedFormControl(loan.commencementDate, Validators.required),
|
|
23007
|
-
repaymentAmount: new UntypedFormControl(loan.repaymentAmount),
|
|
23008
|
-
repaymentFrequency: new UntypedFormControl(loan.repaymentFrequency, Validators.required),
|
|
23009
|
-
term: new UntypedFormControl(loan.term, Validators.required),
|
|
23010
|
-
// interestType is predefined for vehicle loans
|
|
23011
|
-
interestType: new UntypedFormControl({ value: loan.interestType, disabled: !loan.bankAccount }, Validators.required),
|
|
23012
|
-
// availableRedraw is predefined for vehicle loans
|
|
23013
|
-
availableRedraw: new UntypedFormControl({ value: loan.availableRedraw, disabled: !loan.bankAccount }, Validators.required),
|
|
23014
|
-
// repaymentType is predefined for vehicle loans
|
|
23015
|
-
repaymentType: new UntypedFormControl({ value: loan.repaymentType, disabled: !loan.bankAccount }, Validators.required),
|
|
23016
|
-
}, loan);
|
|
23017
|
-
this.loan = loan;
|
|
23018
|
-
// Set data which always the same for vehicle loans
|
|
23019
|
-
if (!loan.bankAccount) {
|
|
23020
|
-
Object.assign(this.model, {
|
|
23021
|
-
repaymentType: LoanRepaymentTypeEnum.PRINCIPAL_AND_INTEREST,
|
|
23022
|
-
availableRedraw: 0,
|
|
23023
|
-
interestType: LoanInterestTypeEnum.FIXED_RATE
|
|
23024
|
-
});
|
|
23025
|
-
}
|
|
23026
|
-
this.updateTermValidation();
|
|
23027
|
-
this.listenEvents();
|
|
23028
|
-
}
|
|
23029
|
-
listenEvents() {
|
|
23030
|
-
// We need to set term automatically only for bank loans.
|
|
23031
|
-
// For vehicle loans user should fill it manually with validation depended of frequency
|
|
23032
|
-
if (this.loan.bankAccount) {
|
|
23033
|
-
this.listenTypeChanges();
|
|
23034
|
-
}
|
|
23035
|
-
else {
|
|
23036
|
-
this.listenRepaymentFrequencyChanges();
|
|
23037
|
-
}
|
|
23038
|
-
}
|
|
23039
|
-
/**
|
|
23040
|
-
* Set term automatically by loan type changes
|
|
23041
|
-
*/
|
|
23042
|
-
listenTypeChanges() {
|
|
23043
|
-
this.get('type').valueChanges.subscribe((type) => {
|
|
23044
|
-
this.get('term').setValue(LoanForm.mortgageLoanTypes.includes(type) ? Loan.mortgageDefaultTerm : Loan.loanDefaultTerm);
|
|
23045
|
-
});
|
|
23046
|
-
}
|
|
23047
|
-
/**
|
|
23048
|
-
* term validation depends on selected repaymentFrequency
|
|
23049
|
-
*/
|
|
23050
|
-
listenRepaymentFrequencyChanges() {
|
|
23051
|
-
this.get('repaymentFrequency').valueChanges.subscribe(() => {
|
|
23052
|
-
this.updateTermValidation();
|
|
23053
|
-
});
|
|
23054
|
-
}
|
|
23055
|
-
/**
|
|
23056
|
-
* For vehicle loans term has a maximum value depended of repayment frequency
|
|
23057
|
-
*/
|
|
23058
|
-
updateTermValidation() {
|
|
23059
|
-
// no need terms for bank loans
|
|
23060
|
-
if (this.loan.bankAccount) {
|
|
23061
|
-
return;
|
|
23062
|
-
}
|
|
23063
|
-
const currentRepaymentFrequency = this.get('repaymentFrequency').value;
|
|
23064
|
-
// term validation depends on selected repayment frequency, so can not validate when frequency is empty
|
|
23065
|
-
// repaymentType is required field, so we don't need to clear validation
|
|
23066
|
-
if (!currentRepaymentFrequency) {
|
|
23067
|
-
return;
|
|
23068
|
-
}
|
|
23069
|
-
const termControl = this.get('term');
|
|
23070
|
-
const maxTermValue = LoanMaxNumberOfPaymentsEnum[AnnualFrequencyEnum[currentRepaymentFrequency]];
|
|
23071
|
-
termControl.setValidators([Validators.max(maxTermValue)]);
|
|
23072
|
-
termControl.updateValueAndValidity();
|
|
23073
|
-
}
|
|
23074
|
-
}
|
|
23075
|
-
|
|
23076
22940
|
/**
|
|
23077
22941
|
* Check if at least one form field is true, otherwise form is invalid.
|
|
23078
22942
|
* Use with groups of boolean form controls (checkbox, toggle, etc.)
|
|
@@ -23306,6 +23170,136 @@ function matchSumValidator(controlsFn, error) {
|
|
|
23306
23170
|
};
|
|
23307
23171
|
}
|
|
23308
23172
|
|
|
23173
|
+
class BudgetRuleForm extends AbstractForm {
|
|
23174
|
+
constructor(rule) {
|
|
23175
|
+
super({
|
|
23176
|
+
tankType: new FormControl(rule.chartAccounts?.tankType ?? TankTypeEnum.PERSONAL, [Validators.required]),
|
|
23177
|
+
chartAccounts: new FormControl({ value: rule.chartAccounts, disabled: !!rule.chartAccounts?.category }, [Validators.required, RxwebValidators.unique()]),
|
|
23178
|
+
amount: new FormControl(rule.amount, Validators.required),
|
|
23179
|
+
startDate: new FormControl(rule.startDate, Validators.required),
|
|
23180
|
+
endDate: new FormControl(rule.endDate),
|
|
23181
|
+
inCalendar: new FormControl(rule.inCalendar),
|
|
23182
|
+
frequency: new FormControl(rule.frequency ?? false),
|
|
23183
|
+
description: new FormControl(rule.description),
|
|
23184
|
+
bankAccount: new FormControl(rule.endDate, conditionalValidator(() => !!this.value.inCalendar, Validators.required)),
|
|
23185
|
+
property: new FormControl(rule.property, conditionalValidator(() => this.value.tankType === TankTypeEnum.PROPERTY, Validators.required)),
|
|
23186
|
+
business: new FormControl(rule.business, conditionalValidator(() => this.value.tankType === TankTypeEnum.SOLE, Validators.required)),
|
|
23187
|
+
incomeSource: new FormControl(rule.incomeSource, conditionalValidator(() => this.value.chartAccounts?.isWorkIncome(), Validators.required)),
|
|
23188
|
+
}, rule);
|
|
23189
|
+
}
|
|
23190
|
+
}
|
|
23191
|
+
|
|
23192
|
+
/**
|
|
23193
|
+
* general validation rules
|
|
23194
|
+
*/
|
|
23195
|
+
var FormValidationsEnum;
|
|
23196
|
+
(function (FormValidationsEnum) {
|
|
23197
|
+
FormValidationsEnum[FormValidationsEnum["INPUT_MAX_LENGTH"] = 60] = "INPUT_MAX_LENGTH";
|
|
23198
|
+
FormValidationsEnum[FormValidationsEnum["TEXT_MAX_LENGTH"] = 300] = "TEXT_MAX_LENGTH";
|
|
23199
|
+
FormValidationsEnum[FormValidationsEnum["FILE_MAX_SIZE"] = 4194304] = "FILE_MAX_SIZE";
|
|
23200
|
+
// @TODO move to file uploader component
|
|
23201
|
+
FormValidationsEnum["FILE_ACCEPTED_TYPES"] = "Accepted file type: .doc .jpg .png .pdf .tiff .bmp .excel .csv";
|
|
23202
|
+
})(FormValidationsEnum || (FormValidationsEnum = {}));
|
|
23203
|
+
|
|
23204
|
+
class DocumentFolderForm extends AbstractForm {
|
|
23205
|
+
constructor(folder) {
|
|
23206
|
+
super({
|
|
23207
|
+
name: new FormControl(folder.name, [Validators.required, Validators.maxLength(FormValidationsEnum.INPUT_MAX_LENGTH)]),
|
|
23208
|
+
parent: new FormControl(folder.parent),
|
|
23209
|
+
}, folder);
|
|
23210
|
+
}
|
|
23211
|
+
}
|
|
23212
|
+
|
|
23213
|
+
class DocumentForm extends AbstractForm {
|
|
23214
|
+
constructor(document) {
|
|
23215
|
+
super({
|
|
23216
|
+
folder: new FormControl(document.folder, Validators.required),
|
|
23217
|
+
file: new FormControl(document.file, Validators.required),
|
|
23218
|
+
}, document);
|
|
23219
|
+
}
|
|
23220
|
+
}
|
|
23221
|
+
|
|
23222
|
+
/**
|
|
23223
|
+
* Form with loan details.
|
|
23224
|
+
* Loan could be created from bank account (Bank Loan) or directly from loan page (Vehicle Loan)
|
|
23225
|
+
*/
|
|
23226
|
+
class LoanForm extends AbstractForm {
|
|
23227
|
+
static { this.mortgageLoanTypes = [LoanTypeEnum.MORTGAGE, LoanTypeEnum.HOME_EQUITY_LINE_OF_CREDIT, LoanTypeEnum.HOME_LOAN]; }
|
|
23228
|
+
constructor(loan = plainToClass(Loan, {})) {
|
|
23229
|
+
super({
|
|
23230
|
+
type: new UntypedFormControl(loan.type, Validators.required),
|
|
23231
|
+
amount: new UntypedFormControl(loan.amount, Validators.required),
|
|
23232
|
+
interestRate: new UntypedFormControl(loan.interestRate, [Validators.required, Validators.min(0), Validators.max(100)]),
|
|
23233
|
+
commencementDate: new UntypedFormControl(loan.commencementDate, Validators.required),
|
|
23234
|
+
repaymentAmount: new UntypedFormControl(loan.repaymentAmount),
|
|
23235
|
+
repaymentFrequency: new UntypedFormControl(loan.repaymentFrequency, Validators.required),
|
|
23236
|
+
term: new UntypedFormControl(loan.term, Validators.required),
|
|
23237
|
+
// interestType is predefined for vehicle loans
|
|
23238
|
+
interestType: new UntypedFormControl({ value: loan.interestType, disabled: !loan.bankAccount }, Validators.required),
|
|
23239
|
+
// availableRedraw is predefined for vehicle loans
|
|
23240
|
+
availableRedraw: new UntypedFormControl({ value: loan.availableRedraw, disabled: !loan.bankAccount }, Validators.required),
|
|
23241
|
+
// repaymentType is predefined for vehicle loans
|
|
23242
|
+
repaymentType: new UntypedFormControl({ value: loan.repaymentType, disabled: !loan.bankAccount }, Validators.required),
|
|
23243
|
+
}, loan);
|
|
23244
|
+
this.loan = loan;
|
|
23245
|
+
// Set data which always the same for vehicle loans
|
|
23246
|
+
if (!loan.bankAccount) {
|
|
23247
|
+
Object.assign(this.model, {
|
|
23248
|
+
repaymentType: LoanRepaymentTypeEnum.PRINCIPAL_AND_INTEREST,
|
|
23249
|
+
availableRedraw: 0,
|
|
23250
|
+
interestType: LoanInterestTypeEnum.FIXED_RATE
|
|
23251
|
+
});
|
|
23252
|
+
}
|
|
23253
|
+
this.updateTermValidation();
|
|
23254
|
+
this.listenEvents();
|
|
23255
|
+
}
|
|
23256
|
+
listenEvents() {
|
|
23257
|
+
// We need to set term automatically only for bank loans.
|
|
23258
|
+
// For vehicle loans user should fill it manually with validation depended of frequency
|
|
23259
|
+
if (this.loan.bankAccount) {
|
|
23260
|
+
this.listenTypeChanges();
|
|
23261
|
+
}
|
|
23262
|
+
else {
|
|
23263
|
+
this.listenRepaymentFrequencyChanges();
|
|
23264
|
+
}
|
|
23265
|
+
}
|
|
23266
|
+
/**
|
|
23267
|
+
* Set term automatically by loan type changes
|
|
23268
|
+
*/
|
|
23269
|
+
listenTypeChanges() {
|
|
23270
|
+
this.get('type').valueChanges.subscribe((type) => {
|
|
23271
|
+
this.get('term').setValue(LoanForm.mortgageLoanTypes.includes(type) ? Loan.mortgageDefaultTerm : Loan.loanDefaultTerm);
|
|
23272
|
+
});
|
|
23273
|
+
}
|
|
23274
|
+
/**
|
|
23275
|
+
* term validation depends on selected repaymentFrequency
|
|
23276
|
+
*/
|
|
23277
|
+
listenRepaymentFrequencyChanges() {
|
|
23278
|
+
this.get('repaymentFrequency').valueChanges.subscribe(() => {
|
|
23279
|
+
this.updateTermValidation();
|
|
23280
|
+
});
|
|
23281
|
+
}
|
|
23282
|
+
/**
|
|
23283
|
+
* For vehicle loans term has a maximum value depended of repayment frequency
|
|
23284
|
+
*/
|
|
23285
|
+
updateTermValidation() {
|
|
23286
|
+
// no need terms for bank loans
|
|
23287
|
+
if (this.loan.bankAccount) {
|
|
23288
|
+
return;
|
|
23289
|
+
}
|
|
23290
|
+
const currentRepaymentFrequency = this.get('repaymentFrequency').value;
|
|
23291
|
+
// term validation depends on selected repayment frequency, so can not validate when frequency is empty
|
|
23292
|
+
// repaymentType is required field, so we don't need to clear validation
|
|
23293
|
+
if (!currentRepaymentFrequency) {
|
|
23294
|
+
return;
|
|
23295
|
+
}
|
|
23296
|
+
const termControl = this.get('term');
|
|
23297
|
+
const maxTermValue = LoanMaxNumberOfPaymentsEnum[AnnualFrequencyEnum[currentRepaymentFrequency]];
|
|
23298
|
+
termControl.setValidators([Validators.max(maxTermValue)]);
|
|
23299
|
+
termControl.updateValueAndValidity();
|
|
23300
|
+
}
|
|
23301
|
+
}
|
|
23302
|
+
|
|
23309
23303
|
const END_DATE_VALIDATION_ERROR = 'Target date must be more than start date';
|
|
23310
23304
|
class FinancialGoalForm extends AbstractForm {
|
|
23311
23305
|
constructor(goal = plainToClass(FinancialGoal, {})) {
|