taxtank-core 2.0.66 → 2.0.68

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.
@@ -17,12 +17,12 @@ import uniqBy from 'lodash/uniqBy';
17
17
  import first from 'lodash/first';
18
18
  import last from 'lodash/last';
19
19
  import orderBy from 'lodash/orderBy';
20
+ import round from 'lodash/round';
20
21
  import uniq from 'lodash/uniq';
21
22
  import moment from 'moment';
22
23
  import { DateRange as DateRange$1 } from 'moment-range';
23
24
  import * as i3 from 'taxtank-core/common';
24
25
  import { UserRolesEnum as UserRolesEnum$1, MixpanelService as MixpanelService$1 } from 'taxtank-core/common';
25
- import round from 'lodash/round';
26
26
  import range from 'lodash/range';
27
27
  import { Validators, FormGroup, FormArray, UntypedFormControl, UntypedFormArray, UntypedFormGroup, FormControl } from '@angular/forms';
28
28
  import compact from 'lodash/compact';
@@ -2469,6 +2469,22 @@ class PropertyCollection extends Collection {
2469
2469
  }
2470
2470
  return propertiesByCategory;
2471
2471
  }
2472
+ getEquityPositionByProperty(bankAccounts) {
2473
+ const loanAccounts = bankAccounts.getOwn().getLoanAndOffsetAccounts();
2474
+ const equityPositionByProperty = new Dictionary([]);
2475
+ this.items.forEach(property => {
2476
+ equityPositionByProperty.add(property.id, property.getEquityPosition(loanAccounts.getByPropertyId(property.id)));
2477
+ });
2478
+ return equityPositionByProperty;
2479
+ }
2480
+ getLvrByProperty(bankAccounts) {
2481
+ const loanAccounts = bankAccounts.getOwn().getLoanAndOffsetAccounts();
2482
+ const lvrByProperty = new Dictionary([]);
2483
+ this.items.forEach(property => {
2484
+ lvrByProperty.add(property.id, property.getLvr(loanAccounts.getByPropertyId(property.id)));
2485
+ });
2486
+ return lvrByProperty;
2487
+ }
2472
2488
  }
2473
2489
 
2474
2490
  class PropertyCategoryMovementCollection extends Collection {
@@ -6171,6 +6187,12 @@ class Property extends Property$1 {
6171
6187
  getGrowth() {
6172
6188
  return this.valuation.marketValue - this.purchasePrice;
6173
6189
  }
6190
+ getEquityPosition(bankAccounts) {
6191
+ return this.marketValue - Math.abs(bankAccounts.getLoanValue(this));
6192
+ }
6193
+ getLvr(bankAccounts) {
6194
+ return bankAccounts.getActiveLoanAccountsByProperties([this.id]).propertiesBalanceAmount / this.marketValue * 100;
6195
+ }
6174
6196
  }
6175
6197
  __decorate([
6176
6198
  Type(() => Number)
@@ -9477,6 +9499,9 @@ class BankAccountCollection extends Collection {
9477
9499
  getActiveLoanAccountsByProperties(ids) {
9478
9500
  return this.getActive().getLoanAccounts().getByPropertiesIds(ids);
9479
9501
  }
9502
+ getLoanValue(property) {
9503
+ return this.items.reduce((propertyAmount, bankAccount) => propertyAmount + bankAccount.getPropertyBalanceAmount(property.id), 0);
9504
+ }
9480
9505
  }
9481
9506
 
9482
9507
  /**
@@ -9754,7 +9779,21 @@ class ClientPortfolioReportCollection extends Collection {
9754
9779
 
9755
9780
  class FinancialGoalCollection extends Collection {
9756
9781
  getActive() {
9757
- return this.filterBy('status', FinancialGoalStatusEnum.ACTIVE);
9782
+ return this.filterBy('status', [FinancialGoalStatusEnum.ACTIVE, FinancialGoalStatusEnum.PAUSE]);
9783
+ }
9784
+ getPropertiesByGoal(properties) {
9785
+ const propertiesByGoal = new CollectionDictionary(new PropertyCollection());
9786
+ this.items.forEach(goal => {
9787
+ propertiesByGoal.add(goal.id, properties.filterBy('id', goal.properties));
9788
+ });
9789
+ return propertiesByGoal;
9790
+ }
9791
+ getBankAccountsByGoal(bankAccounts) {
9792
+ const bankAccountsByGoal = new CollectionDictionary(new BankAccountCollection());
9793
+ this.items.forEach(goal => {
9794
+ bankAccountsByGoal.add(goal.id, bankAccounts.filterBy('id', goal.bankAccount));
9795
+ });
9796
+ return bankAccountsByGoal;
9758
9797
  }
9759
9798
  }
9760
9799
 
@@ -10803,6 +10842,12 @@ class Dictionary {
10803
10842
  get(key) {
10804
10843
  return this.items[key] !== undefined ? this.items[key] : null;
10805
10844
  }
10845
+ sum(keys) {
10846
+ return round(keys.reduce((sum, key) => sum + (+this.get(key)), 0), 2);
10847
+ }
10848
+ avg(keys) {
10849
+ return round(this.sum(keys) / keys.length, 2);
10850
+ }
10806
10851
  merge(dictionary) {
10807
10852
  Object.assign(this.items, dictionary.items);
10808
10853
  return this;
@@ -11504,12 +11549,15 @@ class FinancialGoal extends AbstractModel {
11504
11549
  constructor() {
11505
11550
  super(...arguments);
11506
11551
  this.startDate = moment().startOf('day').toDate();
11507
- this.paymentFrequency = DailyFrequencyEnum.MONTHLY;
11508
- this.inCalendar = true;
11552
+ this.inCalendar = false;
11553
+ this.properties = [];
11509
11554
  }
11510
11555
  isCompleted() {
11511
11556
  return this.status === FinancialGoalStatusEnum.COMPLETE;
11512
11557
  }
11558
+ isPropertyType() {
11559
+ return [FinancialGoalTypeEnum.PROPERTY_EQUITY, FinancialGoalTypeEnum.PROPERTY_LVR].includes(this.type);
11560
+ }
11513
11561
  /**
11514
11562
  * today's forecasted progress
11515
11563
  */
@@ -11523,6 +11571,9 @@ class FinancialGoal extends AbstractModel {
11523
11571
  * How much user saved so far.
11524
11572
  */
11525
11573
  get progress() {
11574
+ if (this.isPropertyType()) {
11575
+ return 0;
11576
+ }
11526
11577
  return this.isCompleted()
11527
11578
  ? Math.abs(this.finalValue - this.initialValue)
11528
11579
  : Math.abs(this.bankAccount.currentBalance - this.initialValue);
@@ -11585,6 +11636,9 @@ class FinancialGoal extends AbstractModel {
11585
11636
  }
11586
11637
  return payments;
11587
11638
  }
11639
+ get propertyIds() {
11640
+ return this.properties.map(property => property.id);
11641
+ }
11588
11642
  }
11589
11643
  __decorate([
11590
11644
  Type(() => Date)
@@ -21043,6 +21097,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.10", ngImpo
21043
21097
  * Logic here works like collections methods but for several entities
21044
21098
  */
21045
21099
  class PropertyCalculationService {
21100
+ constructor() {
21101
+ this.propertyService = inject(PropertyService);
21102
+ this.bankAccountService = inject(BankAccountService);
21103
+ }
21046
21104
  getTaxPosition(transactions, depreciations) {
21047
21105
  // @TODO hack: math abs added because we have mismatching of real values signs
21048
21106
  return transactions.grossClaimAmount - Math.abs(depreciations.claimAmount);
@@ -21079,18 +21137,8 @@ class PropertyCalculationService {
21079
21137
  return properties.items.reduce((totalAmount, property) => totalAmount + bankAccounts.items
21080
21138
  .reduce((propertyAmount, bankAccount) => propertyAmount + bankAccount.getPropertyBalanceAmount(property.id), 0), 0);
21081
21139
  }
21082
- /**
21083
- * LVR
21084
- */
21085
- getLvr(properties, bankAccounts) {
21086
- // Math abs is required for correct percentage calculation
21087
- return bankAccounts.getActiveLoanAccountsByProperties(properties.getIds()).propertiesBalanceAmount / properties.marketValue;
21088
- }
21089
21140
  getLvr$(properties$, bankAccounts$) {
21090
- return combineLatest([
21091
- properties$,
21092
- bankAccounts$
21093
- ]).pipe(map(([properties, bankAccounts]) => this.getLvr(properties, bankAccounts)));
21141
+ return combineLatest([properties$, bankAccounts$]).pipe(map(([properties, bankAccounts]) => this.getLvr(properties, bankAccounts)));
21094
21142
  }
21095
21143
  getLvrCommencement(properties, loans, bankAccounts) {
21096
21144
  // Math abs is required for correct percentage calculation
@@ -21118,6 +21166,14 @@ class PropertyCalculationService {
21118
21166
  loans$
21119
21167
  ]).pipe(map(([properties, bankAccounts, loans]) => this.getLvrGrowth(properties, bankAccounts, loans)));
21120
21168
  }
21169
+ getEquityPosition$(propertyIds) {
21170
+ return combineLatest([this.propertyService.getBy('id', propertyIds), this.bankAccountService.getActive()])
21171
+ .pipe(map(([properties, bankAccounts]) => this.getEquityPosition(properties, bankAccounts.getOwn().getLoanAndOffsetAccounts().getByPropertiesIds(propertyIds))));
21172
+ }
21173
+ getLVR$(propertyIds) {
21174
+ return combineLatest([this.propertyService.getBy('id', propertyIds), this.bankAccountService.getActive()])
21175
+ .pipe(map(([properties, bankAccounts]) => this.getLvr(properties, bankAccounts.getOwn().getLoanAndOffsetAccounts().getByPropertiesIds(propertyIds))));
21176
+ }
21121
21177
  /**
21122
21178
  * Equity position = Market value - current loan value
21123
21179
  */
@@ -21125,6 +21181,13 @@ class PropertyCalculationService {
21125
21181
  // Math abs is required for correct percentage calculation
21126
21182
  return properties.marketValue - Math.abs(this.getLoanValue(properties, bankAccounts));
21127
21183
  }
21184
+ getLvr(properties, bankAccounts) {
21185
+ if (!properties.marketValue) {
21186
+ return 0;
21187
+ }
21188
+ // Math abs is required for correct percentage calculation
21189
+ return bankAccounts.getActiveLoanAccountsByProperties(properties.getIds()).propertiesBalanceAmount / properties.marketValue;
21190
+ }
21128
21191
  /**
21129
21192
  * Purchase Equity = Purchase price - initial loan value
21130
21193
  */
@@ -23256,7 +23319,7 @@ function greaterThanValidator(value) {
23256
23319
  /**
23257
23320
  * Validator that enforces the current control's value to be >= another control's value.
23258
23321
  */
23259
- function greaterThanControlValidator(controlName) {
23322
+ function greaterThanControlValidator(controlName, alias) {
23260
23323
  return (control) => {
23261
23324
  // skip validation until control is attached to a parent FormGroup
23262
23325
  if (!control?.parent) {
@@ -23265,7 +23328,7 @@ function greaterThanControlValidator(controlName) {
23265
23328
  const group = control.parent;
23266
23329
  const fieldToCompare = group.get(controlName);
23267
23330
  const isLessThan = Number(fieldToCompare.value) > Number(control.value);
23268
- return isLessThan ? { min: { min: fieldToCompare.value, actual: control.value } } : null;
23331
+ return isLessThan ? { greaterThanControl: `The value should be greater than ${alias}` } : null;
23269
23332
  };
23270
23333
  }
23271
23334
 
@@ -23451,38 +23514,57 @@ class LoanForm extends AbstractForm {
23451
23514
  }
23452
23515
  }
23453
23516
 
23517
+ /**
23518
+ * Validator that enforces the current control's value to be >= another control's value.
23519
+ */
23520
+ function lessThanControlValidator(controlName, alias) {
23521
+ return (control) => {
23522
+ // skip validation until control is attached to a parent FormGroup
23523
+ if (!control?.parent) {
23524
+ return null;
23525
+ }
23526
+ const group = control.parent;
23527
+ const fieldToCompare = group.get(controlName);
23528
+ const isLessThan = Number(fieldToCompare.value) > Number(control.value);
23529
+ return isLessThan ? null : { lessThanControl: `The value should be less than ${alias}` };
23530
+ };
23531
+ }
23532
+
23454
23533
  const END_DATE_VALIDATION_ERROR = 'Target date must be more than start date';
23455
23534
  class FinancialGoalForm extends AbstractForm {
23456
- constructor(goal = plainToClass(FinancialGoal, {})) {
23535
+ constructor(goal, equityByProperty, lvrByProperty) {
23457
23536
  super({
23458
- type: new FormControl({
23459
- value: goal.type ?? FinancialGoalTypeEnum.DEBIT,
23460
- disabled: !!goal.id
23461
- }, Validators.required),
23537
+ type: new FormControl({ value: goal.type ?? FinancialGoalTypeEnum.DEBIT, disabled: !!goal.id }, Validators.required),
23462
23538
  name: new FormControl(goal.name, Validators.required),
23463
23539
  bankAccount: new FormControl({ value: goal.bankAccount, disabled: !!goal.id }, [
23464
- conditionalValidator(() => [FinancialGoalTypeEnum.CREDIT, FinancialGoalTypeEnum.DEBIT].includes(this.value.type), Validators.required),
23540
+ conditionalValidator(() => !this.isPropertyType(), Validators.required),
23465
23541
  ]),
23466
23542
  targetValue: new FormControl(goal.targetValue, [
23467
- conditionalValidator(() => this.value.type === FinancialGoalTypeEnum.DEBIT, Validators.min(0)),
23468
- conditionalValidator(() => this.value.type === FinancialGoalTypeEnum.CREDIT, Validators.max(0)),
23469
- greaterThanControlValidator('initialValue'),
23470
- Validators.required
23543
+ Validators.required,
23544
+ conditionalValidator(() => this.isLvrType(), lessThanControlValidator('initialValue', 'Start Value')),
23545
+ conditionalValidator(() => !this.isLvrType(), greaterThanControlValidator('initialValue', 'Start Value')),
23471
23546
  ]),
23472
23547
  initialValue: new FormControl({ value: goal.initialValue, disabled: true }, Validators.required),
23473
23548
  startDate: new FormControl({ value: goal.startDate, disabled: true }),
23474
23549
  endDate: new FormControl(goal.endDate, [Validators.required, minDateValidator(goal.startDate, END_DATE_VALIDATION_ERROR)]),
23475
- paymentFrequency: new FormControl(goal.paymentFrequency, Validators.required),
23476
- paymentAmount: new FormControl(goal.paymentAmount, Validators.required),
23550
+ paymentFrequency: new FormControl(goal.paymentFrequency, [
23551
+ conditionalValidator(() => !this.isPropertyType(), Validators.required),
23552
+ ]),
23553
+ paymentAmount: new FormControl(goal.paymentAmount, [
23554
+ conditionalValidator(() => !this.isPropertyType(), Validators.required),
23555
+ ]),
23477
23556
  inCalendar: new FormControl(goal.inCalendar),
23478
23557
  file: new FormControl(goal.file),
23479
23558
  description: new FormControl(goal.description),
23559
+ properties: new FormControl({ value: goal.properties, disabled: !!goal.id }, [
23560
+ conditionalValidator(() => this.isPropertyType(), Validators.required),
23561
+ ]),
23480
23562
  }, goal);
23563
+ this.equityByProperty = equityByProperty;
23564
+ this.lvrByProperty = lvrByProperty;
23481
23565
  this.includeDisabledFields = true;
23482
23566
  this.bankAccountTypes = [];
23483
- this.isDebit = true;
23484
23567
  this.bankAccountTypes = this.getBankAccountTypes(goal.type);
23485
- this.isDebit = this.getIsDebit(goal.type);
23486
23568
  this.listenEvents();
23487
23569
  }
23488
23570
  listenEvents() {
@@ -23493,8 +23575,14 @@ class FinancialGoalForm extends AbstractForm {
23493
23575
  listenTypeChanges() {
23494
23576
  this.get('type').valueChanges.subscribe((type) => {
23495
23577
  this.get('bankAccount').setValue(null, { onlySelf: true });
23578
+ this.get('properties').setValue([], { onlySelf: true, emitEvent: false });
23579
+ this.get('initialValue').setValue(null, { onlySelf: true, emitEvent: false });
23580
+ if (this.isPropertyType()) {
23581
+ this.get('inCalendar').setValue(false, { onlySelf: true, emitEvent: false });
23582
+ this.get('paymentFrequency').setValue(false, { onlySelf: true, emitEvent: false });
23583
+ this.get('paymentAmount').setValue(false, { onlySelf: true, emitEvent: false });
23584
+ }
23496
23585
  this.bankAccountTypes = this.getBankAccountTypes(type);
23497
- this.isDebit = this.getIsDebit(type);
23498
23586
  });
23499
23587
  }
23500
23588
  /**
@@ -23517,11 +23605,9 @@ class FinancialGoalForm extends AbstractForm {
23517
23605
  this.get('endDate').setValue(this.calculateEndDate(), { emitEvent: false });
23518
23606
  });
23519
23607
  }
23520
- /**
23521
- * keeps updated initialValue when bankAccount updated. only for add form
23522
- */
23523
23608
  listenBankAccountChanges() {
23524
- this.get('bankAccount').valueChanges.pipe(filter((bankAccount) => !!bankAccount))
23609
+ this.get('bankAccount').valueChanges
23610
+ .pipe(filter((bankAccount) => !!bankAccount))
23525
23611
  .subscribe(bankAccount => {
23526
23612
  this.get('initialValue').setValue(bankAccount.currentBalance);
23527
23613
  });
@@ -23563,12 +23649,17 @@ class FinancialGoalForm extends AbstractForm {
23563
23649
  switch (type) {
23564
23650
  case FinancialGoalTypeEnum.CREDIT:
23565
23651
  return [BankAccountTypeEnum.MORTGAGE, BankAccountTypeEnum.LOAN, BankAccountTypeEnum.CREDIT_CARD];
23566
- default:
23652
+ case FinancialGoalTypeEnum.DEBIT:
23567
23653
  return [BankAccountTypeEnum.TRANSACTION, BankAccountTypeEnum.SAVINGS, BankAccountTypeEnum.OFFSET];
23654
+ default:
23655
+ return [];
23568
23656
  }
23569
23657
  }
23570
- getIsDebit(type) {
23571
- return type !== FinancialGoalTypeEnum.CREDIT;
23658
+ isPropertyType() {
23659
+ return [FinancialGoalTypeEnum.PROPERTY_EQUITY, FinancialGoalTypeEnum.PROPERTY_LVR].includes(this.value.type);
23660
+ }
23661
+ isLvrType() {
23662
+ return this.value.type === FinancialGoalTypeEnum.PROPERTY_LVR;
23572
23663
  }
23573
23664
  }
23574
23665