taxtank-core 0.28.110 → 0.28.111

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.
@@ -3531,1945 +3531,1951 @@ __decorate([
3531
3531
  Type(() => Transaction)
3532
3532
  ], TransactionAllocation.prototype, "transaction", void 0);
3533
3533
 
3534
- class Depreciation$1 extends TransactionBase {
3535
- }
3536
-
3537
- class DepreciationCapitalProject$1 extends AbstractModel {
3538
- }
3539
-
3540
- class DepreciationCapitalProject extends DepreciationCapitalProject$1 {
3541
- }
3542
- __decorate([
3543
- Type(() => Date)
3544
- ], DepreciationCapitalProject.prototype, "effectiveDate", void 0);
3545
-
3546
- class DepreciationForecast$1 extends AbstractModel {
3547
- }
3548
-
3549
- class DepreciationForecast extends DepreciationForecast$1 {
3550
- get dailyClaimAmount() {
3551
- return this.claimAmount / this.daysApplied;
3552
- }
3553
- get daysApplied() {
3554
- return moment$1(this.toDate).diff(moment$1(this.fromDate), 'days');
3555
- }
3556
- getDaysByMonth(month) {
3557
- // @TODO find a better place
3558
- const year = this.financialYear - (month > 5 ? 1 : 0);
3559
- // forecast date intersect by month
3560
- const range = new DateRange(this.fromDate, this.toDate).intersect(new DateRange(new Date(year, month, 1), new Date(year, month + 1, 0)));
3561
- return range ? range.duration('days') + 1 : 0;
3562
- }
3563
- get claimOpenBalance() {
3564
- return this.openBalance * this.claimRate;
3565
- }
3566
- get claimCloseBalance() {
3567
- return this.closeBalance * this.claimRate;
3568
- }
3569
- get claimAmount() {
3570
- return this.amount * this.claimRate;
3534
+ class PropertySaleCollection extends Collection {
3535
+ get grossCGT() {
3536
+ return this.create(this.items.filter((propertySale) => propertySale.grossCGT > 0)).sumBy('grossCGT');
3571
3537
  }
3572
- get claimRate() {
3573
- return this.claimPercent / 100;
3538
+ /**
3539
+ * Property sales are CGT applicable unless it has "Principle place of residence" exemption type
3540
+ */
3541
+ getCGTApplicable() {
3542
+ return this.create(this.items.filter((propertySale) => propertySale.isCGTApplicable()));
3574
3543
  }
3575
- getClaimAmountByMonth(month) {
3576
- return this.getDaysByMonth(month) * this.dailyClaimAmount;
3544
+ getByPropertyShareIds(ids) {
3545
+ return this.filterBy('share.id', ids);
3577
3546
  }
3578
3547
  }
3579
3548
 
3580
- /**
3581
- * Enum with income amount types (Net or Gross)
3582
- */
3583
- var IncomeAmountTypeEnum;
3584
- (function (IncomeAmountTypeEnum) {
3585
- IncomeAmountTypeEnum[IncomeAmountTypeEnum["NET"] = 0] = "NET";
3586
- IncomeAmountTypeEnum[IncomeAmountTypeEnum["GROSS"] = 1] = "GROSS";
3587
- })(IncomeAmountTypeEnum || (IncomeAmountTypeEnum = {}));
3588
-
3589
- var TransactionCategoryEnum;
3590
- (function (TransactionCategoryEnum) {
3591
- TransactionCategoryEnum[TransactionCategoryEnum["PROPERTY"] = 0] = "PROPERTY";
3592
- TransactionCategoryEnum[TransactionCategoryEnum["WORK"] = 1] = "WORK";
3593
- TransactionCategoryEnum[TransactionCategoryEnum["SOLE"] = 2] = "SOLE";
3594
- TransactionCategoryEnum[TransactionCategoryEnum["PERSONAL"] = 3] = "PERSONAL";
3595
- })(TransactionCategoryEnum || (TransactionCategoryEnum = {}));
3549
+ var TaxExemptionMetadataEnum;
3550
+ (function (TaxExemptionMetadataEnum) {
3551
+ // principle place of residence
3552
+ TaxExemptionMetadataEnum[TaxExemptionMetadataEnum["PPR_DAYS"] = 1] = "PPR_DAYS";
3553
+ // market value once it was moved, decimal
3554
+ TaxExemptionMetadataEnum[TaxExemptionMetadataEnum["MARKET_VALUE"] = 2] = "MARKET_VALUE";
3555
+ TaxExemptionMetadataEnum[TaxExemptionMetadataEnum["CLAIM_PERCENT"] = 3] = "CLAIM_PERCENT";
3556
+ })(TaxExemptionMetadataEnum || (TaxExemptionMetadataEnum = {}));
3596
3557
 
3597
- /**
3598
- * @TODO Alex: clarify grouping rules and refactor
3599
- */
3600
- class Depreciation extends Depreciation$1 {
3601
- constructor() {
3602
- super(...arguments);
3603
- this.forecasts = [];
3604
- this.type = DepreciationTypeEnum.PLANT_EQUIPMENT;
3605
- /**
3606
- * @TODO remove after ? signs removed from db models
3607
- */
3608
- this.amount = 0;
3609
- }
3610
- // Type checking
3611
- isCapital() {
3612
- return this.type === DepreciationTypeEnum.CAPITAL_WORKS;
3613
- }
3614
- isBorrowingExpense() {
3615
- return this.type === DepreciationTypeEnum.BORROWING_EXPENSES;
3616
- }
3617
- isAsset() {
3618
- return this.type === DepreciationTypeEnum.PLANT_EQUIPMENT;
3619
- }
3620
- // Calculation checking
3621
- isSBPCalculation() {
3622
- return this.calculation === DepreciationCalculationEnum.SBP;
3623
- }
3624
- isPrimeCost() {
3625
- return this.calculation === DepreciationCalculationEnum.PRIME_COST;
3558
+ class PropertySaleTaxExemptionMetadataCollection extends Collection {
3559
+ getPPRDays() {
3560
+ var _a, _b;
3561
+ return (_b = (_a = this.getByMetadataId(TaxExemptionMetadataEnum.PPR_DAYS)) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 0;
3626
3562
  }
3627
- isDiminishing() {
3628
- return this.calculation === DepreciationCalculationEnum.DIMINISHING;
3563
+ getMarketValue() {
3564
+ var _a, _b;
3565
+ return (_b = (_a = this.getByMetadataId(TaxExemptionMetadataEnum.MARKET_VALUE)) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 0;
3629
3566
  }
3630
- // Pool checking
3631
- isPlantEquipmentPool() {
3632
- return this.isAsset() && !this.isSBPCalculation() && !this.isWrittenOff() && !this.isLVP();
3567
+ getClaimPercent() {
3568
+ var _a, _b;
3569
+ return (_b = (_a = this.getByMetadataId(TaxExemptionMetadataEnum.CLAIM_PERCENT)) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 100;
3633
3570
  }
3634
- isLVP() {
3635
- return this.isAsset()
3636
- && !this.isSBPCalculation()
3637
- && this.isDiminishing()
3638
- && this.currentYearForecast.closeBalance > Depreciation.WRITTEN_OFF_THRESHOLD
3639
- && this.currentYearForecast.closeBalance <= Depreciation.LOW_VALUE_POOL_THRESHOLD;
3571
+ getByMetadataId(id) {
3572
+ return this.items.find((metadata) => metadata.metadata.id === id);
3640
3573
  }
3641
- isSBP() {
3642
- return this.isAsset() && this.isSBPCalculation() && !this.isWrittenOff();
3574
+ }
3575
+
3576
+ class PropertyCollection extends Collection {
3577
+ /**
3578
+ * Get new property collection filtered by category id
3579
+ * @param id id of category for filter
3580
+ */
3581
+ getByCategoryId(id) {
3582
+ return new PropertyCollection(this.items.filter((property) => property.category.id === id));
3643
3583
  }
3644
- isWrittenOff() {
3645
- return this.writeOffYear === new FinancialYear().year;
3584
+ /**
3585
+ * Get new property collection filtered by active status
3586
+ */
3587
+ getActiveProperties() {
3588
+ return new PropertyCollection(this.items.filter((property) => property.isActive));
3646
3589
  }
3647
- get writeOffYear() {
3648
- var _a;
3649
- if (!this.writeOffManualDate && !this.writeOffDate) {
3650
- return null;
3651
- }
3652
- return (_a = new FinancialYear(this.writeOffManualDate || this.writeOffDate)) === null || _a === void 0 ? void 0 : _a.year;
3590
+ getCreatedProperties() {
3591
+ return new PropertyCollection(this.items.filter((property) => property.isOwn()));
3653
3592
  }
3654
3593
  /**
3655
- * Check if depreciation chart accounts heading related to vehicles category
3594
+ * Get new property collection filtered by shared
3656
3595
  */
3657
- isVehicleDepreciation() {
3658
- var _a;
3659
- return ((_a = this.chartAccounts.heading) === null || _a === void 0 ? void 0 : _a.id) === ChartAccountsHeadingVehicleListEnum.DEPRECIATION_VEHICLES;
3596
+ getSharedProperties() {
3597
+ return new PropertyCollection(this.items.filter((property) => !property.isOwn()));
3660
3598
  }
3661
3599
  /**
3662
- * Get depreciation purchase date
3600
+ * Properties that are taxed and will be included in reports (Tax summary, My tax report, e.t.c.)
3663
3601
  */
3664
- getDate() {
3665
- return this.date;
3602
+ getTaxInclusive() {
3603
+ return this.create(this.items.filter((property) => property.category.isTaxInclusive));
3604
+ }
3605
+ getUnsold() {
3606
+ return this.create(this.items.filter((property) => !property.isSold()));
3666
3607
  }
3667
3608
  /**
3668
- * @TODO Vik: Research a problem with depreciations without current year forecast
3609
+ * Get total purchase price for all properties in the collection
3669
3610
  */
3670
- get currentYearForecast() {
3671
- return this.forecasts.find((forecast) => {
3672
- return forecast.financialYear === new FinancialYear().year;
3673
- }) || plainToClass(DepreciationForecast, {
3674
- financialYear: new FinancialYear().year,
3675
- openBalance: 0,
3676
- closeBalance: 0,
3677
- amount: 0,
3678
- claimPercent: 0
3679
- });
3611
+ get purchasePrice() {
3612
+ return this.sumBy('purchasePrice');
3680
3613
  }
3681
- getForecastByYear(year) {
3682
- return this.forecasts.find((forecast) => {
3683
- return forecast.financialYear === year;
3684
- });
3614
+ get growthPercent() {
3615
+ return this.sumBy('growthPercent');
3685
3616
  }
3686
- getClaimAmountByYear(year) {
3687
- var _a;
3688
- return ((_a = this.getForecastByYear(year)) === null || _a === void 0 ? void 0 : _a.claimAmount) || 0;
3617
+ get marketValue() {
3618
+ return this.sumBy('marketValue');
3689
3619
  }
3690
- getCloseBalanceByYear(year) {
3691
- var _a;
3692
- return ((_a = this.getForecastByYear(year)) === null || _a === void 0 ? void 0 : _a.closeBalance) || 0;
3620
+ get firstForecastYear() {
3621
+ return this.items.reduce((min, property) => {
3622
+ const current = property.firstForecastYear;
3623
+ return min > current ? current : min;
3624
+ }, new FinancialYear().year);
3693
3625
  }
3694
- isBuildingAtCost() {
3695
- return this.chartAccounts.id === ChartAccountsListEnum.BUILDING_AT_COST;
3626
+ get marketValueGrowth() {
3627
+ return (this.marketValue - this.purchasePrice) / this.purchasePrice;
3696
3628
  }
3697
3629
  /**
3698
- * Create a new transaction from current depreciation
3630
+ * list of properties
3699
3631
  */
3700
- toTransaction(params = {}) {
3701
- var _a, _b;
3702
- return plainToClass(Transaction, Object.assign(params, this, {
3703
- amount: -((_a = this.currentYearForecast) === null || _a === void 0 ? void 0 : _a.amount) || 0,
3704
- claimAmount: -((_b = this.currentYearForecast) === null || _b === void 0 ? void 0 : _b.claimAmount) || 0,
3705
- }));
3632
+ getCGTApplicable() {
3633
+ return this.create(this.items.filter((property) => property.isCGTApplicable()));
3706
3634
  }
3707
- /**
3708
- * @TODO Michael: remove and check everywhere in reports
3709
- */
3710
- get claimAmount() {
3711
- var _a;
3712
- return ((_a = this.currentYearForecast) === null || _a === void 0 ? void 0 : _a.claimAmount) || 0;
3635
+ getOwnerOccupiedProperties() {
3636
+ return new PropertyCollection(this.items.filter((property) => property.category.isOwnerOccupied()));
3713
3637
  }
3714
- get amountWithGst() {
3715
- // gst applies only to new assets
3716
- if (this.isNew()) {
3717
- return super.amountWithGst;
3718
- }
3719
- return this.amount;
3638
+ get earliestContractDate() {
3639
+ return this.items.reduce((min, property) => {
3640
+ return min < property.contractDate ? min : property.contractDate;
3641
+ }, new FinancialYear(new Date()).startDate);
3720
3642
  }
3721
- get gstClaimAmount() {
3722
- var _a;
3723
- return this.gstAmount * ((_a = this.currentYearForecast) === null || _a === void 0 ? void 0 : _a.claimRate);
3643
+ /**
3644
+ * Get list of unique property categories from collection
3645
+ */
3646
+ getCategories() {
3647
+ return uniqBy(this.items.map((property) => property.category), 'id');
3724
3648
  }
3725
3649
  /**
3726
- * assets purchased in the current financial year
3650
+ * Get property with the highest growth percent
3727
3651
  */
3728
- isNew() {
3729
- return new FinancialYear(this.date).year === new FinancialYear().year;
3730
- }
3731
- }
3732
- Depreciation.WRITTEN_OFF_THRESHOLD = 300;
3733
- Depreciation.LOW_VALUE_POOL_THRESHOLD = 1000;
3734
- __decorate([
3735
- Type(() => Date)
3736
- ], Depreciation.prototype, "purchaseDate", void 0);
3737
- __decorate([
3738
- Type(() => Date)
3739
- ], Depreciation.prototype, "date", void 0);
3740
- __decorate([
3741
- Type(() => Date)
3742
- ], Depreciation.prototype, "lowValuePoolDate", void 0);
3743
- __decorate([
3744
- Type(() => Date)
3745
- ], Depreciation.prototype, "writeOffManualDate", void 0);
3746
- __decorate([
3747
- Type(() => Date)
3748
- ], Depreciation.prototype, "writeOffDate", void 0);
3749
- __decorate([
3750
- Type(() => DepreciationCapitalProject)
3751
- ], Depreciation.prototype, "depreciationCapitalProject", void 0);
3752
- __decorate([
3753
- Type(() => DepreciationForecast),
3754
- Transform(({ value, obj }) => {
3755
- // value = array of DepreciationForecast
3756
- // obj = plain (whole Depreciation object)
3757
- // Set isLVP flag for each DepreciationForecast
3758
- // @TODO refactor
3759
- value.forEach((forecast) => {
3760
- forecast.isLVP =
3761
- forecast.closeBalance < 1000 &&
3762
- obj.calculation === DepreciationCalculationEnum.DIMINISHING &&
3763
- obj.type === DepreciationTypeEnum.PLANT_EQUIPMENT &&
3764
- !(obj.amount > Depreciation.WRITTEN_OFF_THRESHOLD && obj.amount < Depreciation.LOW_VALUE_POOL_THRESHOLD) &&
3765
- !(obj.writeOffDate && new FinancialYear(new Date(obj.writeOffDate)).year === obj.financialYear);
3766
- });
3767
- return value;
3768
- })
3769
- ], Depreciation.prototype, "forecasts", void 0);
3770
- __decorate([
3771
- Type(() => ChartAccounts)
3772
- ], Depreciation.prototype, "chartAccounts", void 0);
3773
- __decorate([
3774
- Type(() => BankTransaction)
3775
- ], Depreciation.prototype, "bankTransaction", void 0);
3776
- __decorate([
3777
- Exclude()
3778
- ], Depreciation.prototype, "amountWithGst", null);
3779
- __decorate([
3780
- Exclude()
3781
- ], Depreciation.prototype, "gstClaimAmount", null);
3782
-
3783
- // @TODO Alex: refactor: move here allocations methods, netAmount = amount, grossAmount calculation, remove unused methods, etc.
3784
- class Transaction extends Transaction$1 {
3785
- constructor() {
3786
- super(...arguments);
3787
- // list of child transactions (fees)
3788
- this.transactions = [];
3789
- this.metadata = [];
3790
- this.allocations = [];
3791
- this.tax = 0;
3792
- this.operation = TransactionOperationEnum.FIND_AND_MATCH;
3793
- this.claimPercent = 100;
3794
- this.amount = 0;
3795
- }
3796
- isDebit() {
3797
- return this.type === TransactionTypeEnum.DEBIT;
3798
- }
3799
- isCredit() {
3800
- return this.type === TransactionTypeEnum.CREDIT;
3652
+ getBestPerformanceGrowthProperty() {
3653
+ return this.items.reduce((max, current) => {
3654
+ return max.growthPercent < current.growthPercent ? current : max;
3655
+ }, this.first);
3801
3656
  }
3802
3657
  /**
3803
- * @TODO move to base collection
3658
+ * Get property with the lowest tax position
3804
3659
  */
3805
- isIncome() {
3806
- // @TODO not used
3807
- if (!this.chartAccounts) {
3808
- return this.isCredit();
3809
- }
3810
- return CHART_ACCOUNTS_CATEGORIES.income.includes(this.chartAccounts.category);
3660
+ getBestPerformanceTaxProperty(transactions, depreciations) {
3661
+ const transactionsByProperty = transactions.groupBy('property.id');
3662
+ const depreciationsByProperty = depreciations.groupBy('property.id');
3663
+ return this.items.reduce((min, current) => {
3664
+ const minTaxPosition = min.getTaxPosition(transactionsByProperty.get(min.id), depreciationsByProperty.get(min.id));
3665
+ const currentTaxPosition = current.getTaxPosition(transactionsByProperty.get(current.id), depreciationsByProperty.get(current.id));
3666
+ return minTaxPosition > currentTaxPosition ? current : min;
3667
+ }, this.first);
3811
3668
  }
3812
3669
  /**
3813
- * @TODO move to base collection
3670
+ * Show best performance properties first
3671
+ * https://taxtank.atlassian.net/wiki/spaces/TAXTANK/pages/217677997/Property+Tank+Dashboard
3814
3672
  */
3815
- isExpense() {
3816
- // @TODO not used
3817
- if (!this.chartAccounts) {
3818
- return this.isDebit();
3673
+ sortByBestPerformance(transactions, depreciations) {
3674
+ const activeProperties = this.getActiveProperties();
3675
+ // nothing to sort when no active properties
3676
+ if (!activeProperties.length) {
3677
+ return this;
3819
3678
  }
3820
- return CHART_ACCOUNTS_CATEGORIES.expense.includes(this.chartAccounts.category);
3679
+ const bestProperties = uniqBy(this.create([
3680
+ activeProperties.getBestPerformanceGrowthProperty(),
3681
+ activeProperties.getBestPerformanceTaxProperty(transactions, depreciations)
3682
+ ]).toArray(), 'id');
3683
+ const newItems = this.remove(bestProperties).toArray();
3684
+ newItems.unshift(...bestProperties);
3685
+ return this.create(newItems);
3821
3686
  }
3822
- isPersonal() {
3823
- return CHART_ACCOUNTS_CATEGORIES.personal.includes(this.chartAccounts.category);
3687
+ }
3688
+
3689
+ class PropertyCategoryMovementCollection extends Collection {
3690
+ /**
3691
+ * @TODO TT-2355 Alex refactor propertyForecast, use separated api (then I can remove property from param)
3692
+ */
3693
+ getByForecast(property, forecast) {
3694
+ const financialYear = new FinancialYear(forecast.financialYear);
3695
+ return this.filterBy('property.id', property.id).filter((movement) => {
3696
+ return movement.fromDate <= financialYear.endDate && !movement.toDate || movement.toDate >= financialYear.startDate;
3697
+ });
3824
3698
  }
3825
- isInterest() {
3826
- return this.chartAccounts.id === ChartAccountsListEnum.INTEREST_ON_LOAN;
3699
+ hasCategory(categoryId) {
3700
+ return !!this.findBy('propertyCategory.id', categoryId);
3827
3701
  }
3828
- get chartAccountsCategories() {
3829
- switch (true) {
3830
- case this.isPersonal():
3831
- return CHART_ACCOUNTS_CATEGORIES.personal;
3832
- case this.isPropertyTank():
3833
- return CHART_ACCOUNTS_CATEGORIES.property;
3834
- case this.isSoleTank():
3835
- return CHART_ACCOUNTS_CATEGORIES.sole;
3836
- default:
3837
- return CHART_ACCOUNTS_CATEGORIES.work;
3702
+ }
3703
+
3704
+ class VehicleClaimCollection extends Collection {
3705
+ /**
3706
+ * Get remaining kilometers limit. Total limit ({@link VehicleClaim.totalKmsLimit}) - claimed kilometers from other vehicle claims
3707
+ */
3708
+ getKmsLimitForClaim(claim) {
3709
+ let collection = this;
3710
+ if (claim) {
3711
+ collection = collection.removeBy('id', claim.id);
3838
3712
  }
3713
+ return VehicleClaim.totalKmsLimit - collection.sumBy('kilometers');
3839
3714
  }
3840
3715
  /**
3841
- * Check if transaction has 'Kms travelled for work' chart accounts category
3716
+ * Get remaining work usage limit. Total limit ({@link VehicleClaim.totalWorkUsagePercent}) - claimed percent from other vehicle claims
3842
3717
  */
3843
- get isKmsChartAccountsCategory() {
3844
- return this.chartAccounts.id === ChartAccountsListEnum.KLMS_TRAVELLED_FOR_WORK;
3718
+ getWorkUsageLimitForClaim(claim) {
3719
+ let collection = this;
3720
+ if (claim) {
3721
+ collection = collection.removeBy('id', claim.id);
3722
+ }
3723
+ return VehicleClaim.totalWorkUsagePercent - collection.sumBy('workUsage');
3845
3724
  }
3725
+ }
3726
+
3727
+ class VehicleLogbookCollection extends Collection {
3846
3728
  /**
3847
- * Get transaction date
3729
+ * Best period may be calculated only when user has logbooks minimum for VehicleLogbook.bestPeriodWeeks
3730
+ * @TODO Vik: Best period: move this and related logic to backend
3848
3731
  */
3849
- getDate() {
3850
- return this.date;
3732
+ isBestPeriodExist() {
3733
+ if (this.items.length < 2) {
3734
+ return false;
3735
+ }
3736
+ return VehicleLogbook.bestPeriodDuration < (this.last.date.getTime() - this.first.date.getTime());
3851
3737
  }
3852
3738
  /**
3853
- * Check if transaction type is vehicle
3739
+ * Get collection of non-personal logbooks (work-related, sole-related).
3740
+ * @TODO Vik: Best period: move this and related logic to backend
3854
3741
  */
3855
- isVehicleTransaction() {
3856
- return this.chartAccounts.isVehicleExpense();
3857
- }
3858
- get taxFreeComponent() {
3859
- return this.getMetadataFieldValue(ChartAccountsMetadataListEnum.TAX_FREE_COMPONENT);
3860
- }
3861
- get frankingCredit() {
3862
- return this.getMetadataFieldValue(ChartAccountsMetadataListEnum.FRANKING_CREDIT);
3863
- }
3864
- get eligibleForReduction() {
3865
- return this.getMetadataFieldValue(ChartAccountsMetadataListEnum.ELIGIBLE_FOR_REDUCTION);
3866
- }
3867
- get untaxedElement() {
3868
- return this.getMetadataFieldValue(ChartAccountsMetadataListEnum.UNTAXED_ELEMENT);
3742
+ getClaimableLogbooks() {
3743
+ return this.filterBy('isPersonal', false);
3869
3744
  }
3870
3745
  /**
3871
- * Check if transaction reconcile operation if TRANSFER
3872
- * @TODO bad usage of get (and all is* methods), getter should sound like a noun
3746
+ * Get Logbook Period with the biggest work usage percent
3747
+ * Best period duration is defined as VehicleLogbook.bestPeriodWeeks by the ATO
3748
+ * @TODO Vik: Best period: move this and related logic to backend
3873
3749
  */
3874
- get isTransfer() {
3875
- return this.operation === TransactionOperationEnum.TRANSFER;
3876
- }
3877
- isFindAndMatch() {
3878
- return this.operation === TransactionOperationEnum.FIND_AND_MATCH;
3879
- }
3880
- isMatchInvoice() {
3881
- return this.operation === TransactionOperationEnum.MATCH_INVOICE;
3882
- }
3883
- get debit() {
3884
- return this.isDebit() ? Math.abs(this.grossAmount) : 0;
3885
- }
3886
- get credit() {
3887
- return this.isCredit() ? Math.abs(this.grossAmount) : 0;
3750
+ getBestPeriod() {
3751
+ if (!this.isBestPeriodExist()) {
3752
+ return null;
3753
+ }
3754
+ let bestPeriod;
3755
+ // get list of all logbooks available for best period calculation
3756
+ const claimableLogbooks = this.getClaimableLogbooks().toArray();
3757
+ for (let i = 0; i < claimableLogbooks.length; i++) {
3758
+ // no sense to check next logbooks because we already get the end of the year
3759
+ if (bestPeriod && bestPeriod.isEndOfYear()) {
3760
+ break;
3761
+ }
3762
+ // get date range started from current handling logbook date
3763
+ const dateRange = claimableLogbooks[i].getPeriod();
3764
+ // get all logbooks included in current logbook period
3765
+ const logbooksInRange = this.filterByRange('date', dateRange.start, dateRange.end);
3766
+ const currentPeriod = plainToClass(LogbookPeriod, {
3767
+ from: dateRange.start,
3768
+ to: dateRange.end,
3769
+ kilometers: logbooksInRange.getClaimableLogbooks().kilometers,
3770
+ workUsage: logbooksInRange.getWorkUsage(),
3771
+ logbooks: logbooksInRange
3772
+ });
3773
+ // compare with previous best period and overwrite if needs
3774
+ if (!bestPeriod || currentPeriod.workUsage > bestPeriod.workUsage) {
3775
+ bestPeriod = currentPeriod;
3776
+ }
3777
+ else if (currentPeriod.workUsage === bestPeriod.workUsage) {
3778
+ // if work usage is the same then get period with the biggest work usage for work tank
3779
+ const oldWorkUsage = bestPeriod.logbooks.filterBy('tankType', TankTypeEnum.WORK).getWorkUsage();
3780
+ const currentWorkUsage = currentPeriod.logbooks.filterBy('tankType', TankTypeEnum.WORK).getWorkUsage();
3781
+ if (oldWorkUsage < currentWorkUsage) {
3782
+ bestPeriod = currentPeriod;
3783
+ }
3784
+ }
3785
+ }
3786
+ return bestPeriod;
3888
3787
  }
3889
3788
  /**
3890
- * Get value of transaction metadata field
3891
- * @param field for which value should be returned
3892
- * @Todo modify 'metadata' property from array to Collection
3789
+ * Calculate total kilometers traveled
3893
3790
  */
3894
- getMetadataFieldValue(field) {
3895
- var _a;
3896
- return +((_a = this.metadata.find((transactionMetadata) => {
3897
- return transactionMetadata.chartAccountsMetadata.id === field;
3898
- })) === null || _a === void 0 ? void 0 : _a.value) || 0;
3899
- }
3900
- isCash() {
3901
- return this.source === TransactionSourceEnum.CASH;
3791
+ get kilometers() {
3792
+ return this.sumBy('kilometers');
3902
3793
  }
3903
3794
  /**
3904
- * Create Depreciation instance based on Transaction
3795
+ * Calculate work usage (percent of business-related kilometers from total kilometers)
3796
+ * @TODO Alex: TT-2089 replace with getter
3905
3797
  */
3906
- toDepreciation() {
3907
- return plainToClass(Depreciation, {
3908
- date: this.date,
3909
- amount: this.amount,
3910
- chartAccounts: this.chartAccounts,
3911
- description: this.description,
3912
- type: DepreciationTypeEnum.PLANT_EQUIPMENT,
3913
- claimPercent: this.claimPercent,
3914
- property: this.property,
3915
- calculation: this.property ? this.property.depreciationCalculation : DepreciationCalculationEnum.PRIME_COST
3916
- });
3798
+ getWorkUsage() {
3799
+ const workKilometers = this.getClaimableLogbooks().kilometers;
3800
+ return Math.round(workKilometers / this.kilometers * 100);
3917
3801
  }
3918
3802
  /**
3919
- * Check if transaction is completely allocated
3803
+ * Get list of logbooks related to passed vehicle claim
3920
3804
  */
3921
- isAllocated(allocations) {
3922
- return this.grossAmount === this.getAllocatedAmount(allocations);
3805
+ getByVehicleClaim(vehicleClaim) {
3806
+ return vehicleClaim.isSoleTank()
3807
+ // sole tank may have multiple vehicle claims, so we need to filter by business.id
3808
+ ? this.filterBy('business.id', vehicleClaim.business.id)
3809
+ // work tank may have only one vehicle claim, so we need to filter by tank type
3810
+ : this.filterBy('tankType', TankTypeEnum.WORK);
3923
3811
  }
3924
- getAllocatedAmount(allocations) {
3925
- return allocations.filterBy('transaction.id', this.id).sumBy('amount');
3812
+ }
3813
+
3814
+ /**
3815
+ * List of objects grouped by passed property
3816
+ */
3817
+ class Dictionary {
3818
+ constructor(items, path = 'id') {
3819
+ this.items = {};
3820
+ if (!items.length) {
3821
+ return;
3822
+ }
3823
+ // Do nothing if provided path was not found in the 1st item
3824
+ if (!hasIn(items[0], path.split('.')[0])) {
3825
+ return;
3826
+ }
3827
+ this.groupItems(items, path);
3926
3828
  }
3927
- getAllocatedClaimAmount(allocations) {
3928
- return this.getAllocatedAmount(allocations) * this.claimRatio;
3829
+ add(key, value) {
3830
+ this.items[key] = value;
3831
+ }
3832
+ get(key) {
3833
+ return this.items[key] !== undefined ? this.items[key] : null;
3834
+ }
3835
+ groupItems(items, path) {
3836
+ items.forEach((item) => {
3837
+ let key = get(item, path);
3838
+ // if object does not have property for grouping it will be grouped as 'other'
3839
+ if (key === undefined) {
3840
+ key = 'other';
3841
+ }
3842
+ this.items[key] = item;
3843
+ });
3929
3844
  }
3845
+ }
3846
+
3847
+ class SoleBusinessLossesCollection extends Collection {
3930
3848
  /**
3931
- * Get transaction unallocated amount
3849
+ * Business loss applied in current year, includes previous year losses and current year losses for businesses matching offset rule
3932
3850
  */
3933
- getUnallocatedAmount(allocations) {
3934
- return this.grossAmount - this.getAllocatedAmount(allocations);
3851
+ calculateBusinessLossApplied(transactions) {
3852
+ // claim amounts for businesses that can be applied to be reduced by prior year business losses
3853
+ const claimAmountsByBusinessId = this.getClaimAmountsByBusinessId(transactions);
3854
+ return Object.keys(claimAmountsByBusinessId.items).reduce((sum, businessId) => {
3855
+ const loss = this.findBy('business.id', +businessId);
3856
+ const lossOpenBalance = (loss === null || loss === void 0 ? void 0 : loss.openBalance) || 0;
3857
+ // business loss can be applied to business profit or other income types profit in case in offset rule met
3858
+ return sum + ((loss === null || loss === void 0 ? void 0 : loss.offsetRule) ? lossOpenBalance : Math.min(lossOpenBalance, claimAmountsByBusinessId.get(businessId)));
3859
+ }, 0);
3935
3860
  }
3936
3861
  /**
3937
- * Total transaction amount including taxes and other additional amounts
3862
+ * Get business claim amounts that can be applied to be reduced by prior year business losses:
3863
+ * businesses with income or businesses with a loss, but which met the non-commercial loss rules
3864
+ * https://www.ato.gov.au/Business/Non-commercial-losses/
3938
3865
  */
3939
- get grossAmount() {
3940
- let grossAmount = super.grossAmount + this.tax;
3941
- if (this.isExpense()) {
3942
- return grossAmount;
3943
- }
3944
- grossAmount += this.transactions.filter((t) => { var _a; return !((_a = t.chartAccounts) === null || _a === void 0 ? void 0 : _a.isSalaryIncluded()); }).reduce((sum, transaction) => sum + Math.abs(transaction.amount), 0);
3945
- if (this.isWorkTank()) {
3946
- grossAmount += this.frankingCredit - this.taxFreeComponent - this.eligibleForReduction;
3947
- }
3948
- return +(Math.round(grossAmount * 100) / 100).toFixed(2);
3866
+ getClaimAmountsByBusinessId(transactions) {
3867
+ const claimAmountsByBusinessId = new Dictionary([]);
3868
+ const transactionsByBusinessId = new CollectionDictionary(transactions, 'business.id');
3869
+ transactionsByBusinessId.keys.forEach((businessId) => {
3870
+ var _a;
3871
+ // business loss may not exist if, when creating a business, user didn't add losses for previous years
3872
+ const lossOffsetRule = (_a = this.findBy('business.id', +businessId)) === null || _a === void 0 ? void 0 : _a.offsetRule;
3873
+ const businessClaimAmount = transactionsByBusinessId
3874
+ .get(businessId)
3875
+ .getClaimAmountByBusinessId(+businessId);
3876
+ // no way to apply loss for business without profit if offset rules not met
3877
+ if (businessClaimAmount < 0 && !lossOffsetRule) {
3878
+ return;
3879
+ }
3880
+ claimAmountsByBusinessId.add(businessId, businessClaimAmount);
3881
+ });
3882
+ return claimAmountsByBusinessId;
3949
3883
  }
3950
3884
  }
3951
- __decorate([
3952
- Type(() => Transaction)
3953
- ], Transaction.prototype, "transactions", void 0);
3954
- __decorate([
3955
- Type(() => Property)
3956
- ], Transaction.prototype, "property", void 0);
3957
- __decorate([
3958
- Type(() => TransactionReceipt)
3959
- ], Transaction.prototype, "receipt", void 0);
3960
- __decorate([
3961
- Type(() => ChartAccounts)
3962
- ], Transaction.prototype, "chartAccounts", void 0);
3963
- __decorate([
3964
- Type(() => IncomeSource)
3965
- ], Transaction.prototype, "incomeSource", void 0);
3966
- __decorate([
3967
- Type(() => TransactionMetadata)
3968
- ], Transaction.prototype, "metadata", void 0);
3969
- __decorate([
3970
- Type(() => Transaction)
3971
- ], Transaction.prototype, "transfer", void 0);
3972
- __decorate([
3973
- Type(() => Loan)
3974
- ], Transaction.prototype, "loan", void 0);
3975
- __decorate([
3976
- Type(() => Date)
3977
- ], Transaction.prototype, "date", void 0);
3978
- __decorate([
3979
- Type(() => TransactionAllocation)
3980
- ], Transaction.prototype, "allocations", void 0);
3981
3885
 
3982
- class SoleInvoiceItem extends SoleInvoiceItem$1 {
3983
- constructor() {
3984
- super(...arguments);
3985
- this.isGST = false;
3886
+ class SoleInvoiceCollection extends Collection {
3887
+ getOverdue() {
3888
+ return this.filter((invoice) => invoice.isOverdue());
3986
3889
  }
3987
- get totalPrice() {
3988
- return this.price * this.quantity;
3890
+ getUnpaid() {
3891
+ return this.filter((invoice) => invoice.isUnpaid());
3989
3892
  }
3990
- }
3991
- __decorate([
3992
- Type(() => SoleInvoice)
3993
- ], SoleInvoiceItem.prototype, "invoice", void 0);
3994
- __decorate([
3995
- Type(() => ChartAccounts)
3996
- ], SoleInvoiceItem.prototype, "chartAccounts", void 0);
3997
- __decorate([
3998
- Type(() => Transaction)
3999
- ], SoleInvoiceItem.prototype, "transaction", void 0);
4000
-
4001
- class SoleContact$1 extends AbstractModel {
4002
- }
4003
-
4004
- // @TODO Alex/Vik: Create some base class for User and SoleContact with common methods and properties
4005
- class SoleContact extends SoleContact$1 {
4006
- get fullName() {
4007
- return `${this.firstName} ${this.lastName}`;
3893
+ getPaid() {
3894
+ return this.filter((invoice) => invoice.isPaid());
4008
3895
  }
4009
- getPhotoPlaceholder() {
4010
- return `${this.firstName[0].toUpperCase()}${this.lastName[0].toUpperCase()}`;
3896
+ getPending() {
3897
+ return this.filter((invoice) => invoice.isPending());
4011
3898
  }
4012
- // @TODO Vik: add photo field to SoleContact
4013
- getPhoto() {
4014
- return '';
3899
+ getTransactionsIds() {
3900
+ return flatten(this.items.map((invoice) => invoice.getTransactionsIds()));
4015
3901
  }
4016
3902
  }
4017
- __decorate([
4018
- Type(() => User)
4019
- ], SoleContact.prototype, "user", void 0);
4020
- __decorate([
4021
- Type(() => Phone)
4022
- ], SoleContact.prototype, "phone", void 0);
4023
- __decorate([
4024
- Type(() => Address)
4025
- ], SoleContact.prototype, "address", void 0);
4026
- __decorate([
4027
- Type(() => SoleInvoice)
4028
- ], SoleContact.prototype, "invoices", void 0);
4029
3903
 
4030
- class SoleInvoiceTemplate$1 extends AbstractModel {
3904
+ /**
3905
+ * Chart serie class: chart data item
3906
+ * @TODO consider rename to ChartSerieData
3907
+ */
3908
+ class ChartSerie {
4031
3909
  }
4032
3910
 
4033
- var SoleInvoiceTemplateTaxTypeEnum;
4034
- (function (SoleInvoiceTemplateTaxTypeEnum) {
4035
- SoleInvoiceTemplateTaxTypeEnum[SoleInvoiceTemplateTaxTypeEnum["TAX_EXCLUSIVE"] = 0] = "TAX_EXCLUSIVE";
4036
- SoleInvoiceTemplateTaxTypeEnum[SoleInvoiceTemplateTaxTypeEnum["TAX_INCLUSIVE"] = 1] = "TAX_INCLUSIVE";
4037
- SoleInvoiceTemplateTaxTypeEnum[SoleInvoiceTemplateTaxTypeEnum["NO_TAX"] = 2] = "NO_TAX";
4038
- })(SoleInvoiceTemplateTaxTypeEnum || (SoleInvoiceTemplateTaxTypeEnum = {}));
4039
-
4040
- class SoleInvoiceTemplate extends SoleInvoiceTemplate$1 {
4041
- constructor() {
4042
- super(...arguments);
4043
- /**
4044
- * Affects to SoleInvoiceItem.isGST flag availability.
4045
- * When NONE: isGST is unavailable
4046
- * When EXCLUSIVE: GST amount added additionaly to invoice total price
4047
- * When INCLUSIVE: GST amount is already included to invoice total price
4048
- */
4049
- this.taxType = SoleInvoiceTemplateTaxTypeEnum.NO_TAX;
4050
- }
4051
- /**
4052
- * Get term duration in milliseconds
4053
- */
4054
- get termTime() {
4055
- return this.term * 24 * 3600 * 1000;
4056
- }
3911
+ /**
3912
+ * Chart data class
3913
+ * @TODO consider rename to ChartSerie
3914
+ */
3915
+ class ChartData {
4057
3916
  }
4058
3917
  __decorate([
4059
- Type(() => SoleBusiness)
4060
- ], SoleInvoiceTemplate.prototype, "business", void 0);
4061
- __decorate([
4062
- Type(() => BankAccount)
4063
- ], SoleInvoiceTemplate.prototype, "bankAccount", void 0);
4064
-
4065
- var SoleInvoiceStatusesEnum;
4066
- (function (SoleInvoiceStatusesEnum) {
4067
- SoleInvoiceStatusesEnum[SoleInvoiceStatusesEnum["CANCELED"] = 0] = "CANCELED";
4068
- SoleInvoiceStatusesEnum[SoleInvoiceStatusesEnum["DRAFT"] = 1] = "DRAFT";
4069
- SoleInvoiceStatusesEnum[SoleInvoiceStatusesEnum["PENDING"] = 2] = "PENDING";
4070
- SoleInvoiceStatusesEnum[SoleInvoiceStatusesEnum["PAID"] = 3] = "PAID";
4071
- })(SoleInvoiceStatusesEnum || (SoleInvoiceStatusesEnum = {}));
3918
+ Type(() => ChartSerie)
3919
+ ], ChartData.prototype, "data", void 0);
4072
3920
 
4073
- var SoleInvoiceTaxTypeEnum;
4074
- (function (SoleInvoiceTaxTypeEnum) {
4075
- SoleInvoiceTaxTypeEnum[SoleInvoiceTaxTypeEnum["TAX_EXCLUSIVE"] = 0] = "TAX_EXCLUSIVE";
4076
- SoleInvoiceTaxTypeEnum[SoleInvoiceTaxTypeEnum["TAX_INCLUSIVE"] = 1] = "TAX_INCLUSIVE";
4077
- SoleInvoiceTaxTypeEnum[SoleInvoiceTaxTypeEnum["NO_TAX"] = 2] = "NO_TAX";
4078
- })(SoleInvoiceTaxTypeEnum || (SoleInvoiceTaxTypeEnum = {}));
3921
+ const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Jan'];
4079
3922
 
4080
- class SoleInvoiceItemCollection extends Collection {
4081
- get gstPrice() {
4082
- return this.filterBy('isGST', true).sumBy('totalPrice');
3923
+ /**
3924
+ * @TODO extend from TransactionBaseCollection
3925
+ * Collection of transactions
3926
+ */
3927
+ class TransactionCollection extends ExportableCollection {
3928
+ /**
3929
+ * @TODO use TransactionBaseCollection instead
3930
+ * we use depreciations as expense transactions a lot
3931
+ */
3932
+ constructor(transactions = [], depreciations = []) {
3933
+ super([...transactions, ...depreciations.map((depreciation) => depreciation.toTransaction())]);
4083
3934
  }
4084
- }
4085
-
4086
- class SoleInvoice extends SoleInvoice$1 {
4087
- constructor() {
4088
- super(...arguments);
4089
- this.status = SoleInvoiceStatusesEnum.DRAFT;
4090
- this.taxType = SoleInvoiceTaxTypeEnum.NO_TAX;
3935
+ getSoleTransactions() {
3936
+ return this.filter((transaction) => transaction.isSoleTank());
3937
+ }
3938
+ get amount() {
3939
+ return this.sumBy('amount');
3940
+ }
3941
+ getUnallocatedAmount(allocations) {
3942
+ return this.items.reduce((sum, transaction) => {
3943
+ return sum + transaction.getUnallocatedAmount(allocations.filterBy('transaction.id', transaction.id));
3944
+ }, 0);
4091
3945
  }
4092
3946
  /**
4093
- * Get items array as collection
3947
+ * get date of the last transaction
4094
3948
  */
4095
- get itemsCollection() {
4096
- return new SoleInvoiceItemCollection(this.items);
3949
+ getLastTransactionDate() {
3950
+ return new Date(Math.max.apply(Math, this.items.map((transaction) => transaction.date)));
3951
+ }
3952
+ get claimAmount() {
3953
+ return this.items.reduce((sum, transaction) => sum + transaction.claimAmount, 0);
3954
+ }
3955
+ get grossClaimAmount() {
3956
+ return this.items.reduce((sum, transaction) => sum + transaction.grossClaimAmount, 0);
3957
+ }
3958
+ get grossAmount() {
3959
+ return this.items.reduce((sum, transaction) => sum + transaction.grossAmount, 0);
3960
+ }
3961
+ getByChartAccountsCategories(categories) {
3962
+ return new TransactionCollection(this.items.filter((transaction) => categories.includes(transaction.chartAccounts.category)));
4097
3963
  }
4098
3964
  /**
4099
- * Total price of all items
3965
+ * Get transactions by month
3966
+ * @param monthIndex by which desired month should be taken
4100
3967
  */
4101
- get price() {
4102
- return this.itemsCollection.sumBy('totalPrice');
3968
+ getByMonth(monthIndex) {
3969
+ return new TransactionCollection(this.items.filter((transaction) => transaction.date.getMonth() === monthIndex));
4103
3970
  }
4104
3971
  /**
4105
- * Total invoice price without GST
3972
+ * Get collection of transactions metadata
4106
3973
  */
4107
- get netPrice() {
4108
- if (this.isTaxInclusive()) {
4109
- return this.price - this.GSTAmount;
4110
- }
4111
- return this.price;
3974
+ getTransactionsMetadata() {
3975
+ const metadataArray = [];
3976
+ this.items.forEach((transaction) => {
3977
+ metadataArray.push(...transaction.metadata);
3978
+ });
3979
+ return new Collection(metadataArray);
3980
+ }
3981
+ getIncomeTransactions() {
3982
+ return new TransactionCollection(this.items.filter((transaction) => transaction.isIncome()));
3983
+ }
3984
+ getExpenseTransactions() {
3985
+ return new TransactionCollection(this.items.filter((transaction) => transaction.isExpense() && !transaction.isInterest()));
3986
+ }
3987
+ get claimIncome() {
3988
+ return this.getIncomeTransactions().claimAmount;
3989
+ }
3990
+ get claimExpense() {
3991
+ return this.getExpenseTransactions().claimAmount;
3992
+ }
3993
+ getInterestTransactions() {
3994
+ return new TransactionCollection(this.items.filter((transaction) => transaction.isInterest()));
3995
+ }
3996
+ get claimInterest() {
3997
+ return this.getInterestTransactions().claimAmount;
4112
3998
  }
4113
3999
  /**
4114
- * Total final price (net + GST)
4000
+ * Get collection of transactions and properties filtered by properties ids
4001
+ * @param ids Ids of properties for filter
4115
4002
  */
4116
- get grossPrice() {
4117
- if (this.isTaxExclusive()) {
4118
- return this.price + this.GSTAmount;
4119
- }
4120
- return this.price;
4003
+ getByPropertiesIds(ids) {
4004
+ return new TransactionCollection(this.items.filter((transaction) => {
4005
+ var _a;
4006
+ return ids.includes((_a = transaction.property) === null || _a === void 0 ? void 0 : _a.id);
4007
+ }));
4121
4008
  }
4122
4009
  /**
4123
- * Total GST amount
4010
+ * Get new collection filtered by income source id
4011
+ * @param id id of income source for filter
4124
4012
  */
4125
- get GSTAmount() {
4126
- switch (this.taxType) {
4127
- case SoleInvoiceTaxTypeEnum.TAX_INCLUSIVE:
4128
- return this.inclusiveGSTAmount;
4129
- case SoleInvoiceTaxTypeEnum.TAX_EXCLUSIVE:
4130
- return this.exclusiveGSTAmount;
4131
- case SoleInvoiceTaxTypeEnum.NO_TAX:
4132
- return 0;
4133
- }
4013
+ getByIncomeSourceId(id) {
4014
+ return new TransactionCollection(this.items.filter((transaction) => { var _a; return ((_a = transaction.incomeSource) === null || _a === void 0 ? void 0 : _a.id) === id; }));
4134
4015
  }
4135
4016
  /**
4136
- * When tax inclusive, GST amount is included to total price
4017
+ * Get new collection filtered by chart accounts category
4018
+ * @param category Chart accounts category value
4137
4019
  */
4138
- get inclusiveGSTAmount() {
4139
- const gstPrice = this.itemsCollection.gstPrice;
4140
- return +(gstPrice - (gstPrice / (1 + ChartAccounts.GSTRatio))).toFixed(2);
4020
+ getByChartAccountsCategory(category) {
4021
+ return new TransactionCollection(this.items.filter((transaction) => transaction.chartAccounts.category === category));
4141
4022
  }
4142
4023
  /**
4143
- * When tax exclusive, GST amount should be added additionally to total price
4024
+ * Get new collection of property transactions
4144
4025
  */
4145
- get exclusiveGSTAmount() {
4146
- return +(this.itemsCollection.gstPrice * ChartAccounts.GSTRatio).toFixed(2);
4026
+ getPropertyTransactions() {
4027
+ return new TransactionCollection(this.items.filter((transaction) => transaction.isPropertyTank()));
4147
4028
  }
4148
- isDraft() {
4149
- return this.status === SoleInvoiceStatusesEnum.DRAFT;
4029
+ getDebitTransactions() {
4030
+ return new TransactionCollection(this.items.filter((transaction) => transaction.isDebit()));
4150
4031
  }
4151
- isCancelled() {
4152
- return this.status === SoleInvoiceStatusesEnum.CANCELED;
4032
+ getCreditTransactions() {
4033
+ return new TransactionCollection(this.items.filter((transaction) => transaction.isCredit()));
4153
4034
  }
4154
- isPending() {
4155
- return this.status === SoleInvoiceStatusesEnum.PENDING;
4035
+ getByAllocations(allocations) {
4036
+ return new TransactionCollection(this.items.filter((transaction) => allocations.hasTransaction(transaction)));
4156
4037
  }
4157
- isPaid() {
4158
- return this.status === SoleInvoiceStatusesEnum.PAID;
4038
+ /**
4039
+ * Get transactions related to Vehicle category
4040
+ */
4041
+ getVehicleTransactions() {
4042
+ return this.create(this.items.filter((transaction) => {
4043
+ return transaction.isVehicleTransaction();
4044
+ }));
4159
4045
  }
4160
- isOverdue() {
4161
- return this.isPending() && this.dateTo < new Date();
4046
+ /**
4047
+ * Get new transaction collection filtered by tank type
4048
+ */
4049
+ getByTankType(tankType) {
4050
+ return this.create(this.items.filter((transaction) => {
4051
+ switch (tankType) {
4052
+ case TankTypeEnum.PROPERTY:
4053
+ return transaction.isPropertyTank();
4054
+ case TankTypeEnum.WORK:
4055
+ return transaction.isWorkTank();
4056
+ case TankTypeEnum.SOLE:
4057
+ return transaction.isSoleTank();
4058
+ // Transaction may be not related to any tank type (personal)
4059
+ default:
4060
+ return false;
4061
+ }
4062
+ }));
4162
4063
  }
4163
- isUnpaid() {
4164
- return this.isPending() && this.dateTo >= new Date();
4064
+ getExportHeader() {
4065
+ return ['Date', 'Description', 'Debit', 'Credit'];
4066
+ }
4067
+ getExportFooter() {
4068
+ return [
4069
+ plainToClass(ExportCell, { value: 'Total', type: ExportCellTypeEnum.STRING }),
4070
+ plainToClass(ExportCell, { value: '', type: ExportCellTypeEnum.STRING }),
4071
+ plainToClass(ExportCell, { value: this.sumBy('debit'), type: ExportCellTypeEnum.CURRENCY }),
4072
+ plainToClass(ExportCell, { value: this.sumBy('credit'), type: ExportCellTypeEnum.CURRENCY })
4073
+ ];
4074
+ }
4075
+ getExportBody() {
4076
+ return this.items.map((transaction) => {
4077
+ return [
4078
+ plainToClass(ExportCell, { value: transaction.date, type: ExportCellTypeEnum.DATE }),
4079
+ plainToClass(ExportCell, { value: transaction.description, type: ExportCellTypeEnum.STRING }),
4080
+ plainToClass(ExportCell, { value: transaction.debit, type: ExportCellTypeEnum.CURRENCY }),
4081
+ plainToClass(ExportCell, { value: transaction.credit, type: ExportCellTypeEnum.CURRENCY })
4082
+ ];
4083
+ });
4165
4084
  }
4166
4085
  /**
4167
- * GST is not available for invoices without taxes
4086
+ * Get list of vehicle transactions filtered by vehicle claim
4168
4087
  */
4169
- isNoTax() {
4170
- return this.taxType === SoleInvoiceTaxTypeEnum.NO_TAX;
4088
+ getByVehicleClaim(vehicleClaim) {
4089
+ if (!vehicleClaim) {
4090
+ return this.create([]);
4091
+ }
4092
+ return vehicleClaim.isSoleTank()
4093
+ // sole tank may have multiple vehicle claims, so we need to filter by business.id
4094
+ ? this.getVehicleTransactions().filterBy('business.id', vehicleClaim.business.id)
4095
+ // work tank may have only one vehicle claim, so we need to filter by tank type
4096
+ : this.getVehicleTransactions().filterBy('tankType', TankTypeEnum.WORK);
4171
4097
  }
4172
4098
  /**
4173
- * GST amount is not included in items prices for invoices with tax exclusive,
4174
- * we should add GST amount additionaly to subtotal price to get total price
4099
+ * Get list of vehicle transactions except KMS transactions
4175
4100
  */
4176
- isTaxExclusive() {
4177
- return this.taxType === SoleInvoiceTaxTypeEnum.TAX_EXCLUSIVE;
4101
+ getLogbookTransactions() {
4102
+ return this
4103
+ .getVehicleTransactions()
4104
+ .removeBy('chartAccounts.id', [ChartAccountsListEnum.KLMS_TRAVELLED_FOR_WORK, ChartAccountsListEnum.KLMS_TRAVELLED]);
4178
4105
  }
4179
4106
  /**
4180
- * GST amount already included in items prices for invoices with tax inclusive,
4181
- * we should subtract GST amount from total price to get subtotal price
4107
+ * Build chart data with transactions cash position.
4108
+ * Cash position = Income - Expenses (include depreciations)
4109
+ * Chart data for each month from fin year start till current month
4182
4110
  */
4183
- isTaxInclusive() {
4184
- return this.taxType === SoleInvoiceTaxTypeEnum.TAX_INCLUSIVE;
4111
+ getCashPositionChartData() {
4112
+ const chartData = [
4113
+ plainToClass(ChartData, { name: 'Income', data: [] }),
4114
+ plainToClass(ChartData, { name: 'Expense', data: [] })
4115
+ ];
4116
+ new FinancialYear().getPastMonths().forEach((month) => {
4117
+ chartData[0].data.push({
4118
+ label: MONTHS[month],
4119
+ value: this.getIncomeTransactions().getByMonth(month).claimAmount
4120
+ });
4121
+ chartData[1].data.push({
4122
+ label: MONTHS[month],
4123
+ value: Math.abs(this.getExpenseTransactions().getByMonth(month).claimAmount)
4124
+ });
4125
+ });
4126
+ return chartData;
4185
4127
  }
4186
- getNumber() {
4187
- return String(this.number).padStart(SoleInvoice.NUMBER_LENGTH, '0');
4128
+ /**
4129
+ * user pays GST only from allocated part (or paid) of income
4130
+ */
4131
+ calculateAllocatedClaimAmount(allocations) {
4132
+ let allocatedClaimAmount = 0;
4133
+ this.filterBy('isGST', true).toArray().forEach((transaction) => {
4134
+ allocatedClaimAmount += transaction.getAllocatedClaimAmount(allocations);
4135
+ });
4136
+ return allocatedClaimAmount;
4188
4137
  }
4189
- get name() {
4190
- return `invoice-${this.getNumber()}.pdf`;
4138
+ calculateAllocatedGST(allocations) {
4139
+ return this.calculateAllocatedClaimAmount(allocations) * ChartAccounts.GSTRatio;
4191
4140
  }
4192
- getTransactionsIds() {
4193
- return new Collection(this.items).mapBy('transaction.id');
4194
- this.items.map((item) => item.transaction.id);
4141
+ getAllocatedAmount(allocations) {
4142
+ return allocations.getByTransactionsIds(this.getIds()).sumBy('amount');
4195
4143
  }
4196
4144
  }
4197
- SoleInvoice.NUMBER_LENGTH = 4;
4198
- __decorate([
4199
- Type(() => Date)
4200
- ], SoleInvoice.prototype, "dateFrom", void 0);
4201
- __decorate([
4202
- Type(() => Date)
4203
- ], SoleInvoice.prototype, "dateTo", void 0);
4204
- __decorate([
4205
- Type(() => User)
4206
- ], SoleInvoice.prototype, "user", void 0);
4207
- __decorate([
4208
- Type(() => SoleBusiness)
4209
- ], SoleInvoice.prototype, "business", void 0);
4210
- __decorate([
4211
- Type(() => SoleInvoiceItem)
4212
- ], SoleInvoice.prototype, "items", void 0);
4213
- __decorate([
4214
- Type(() => SoleContact)
4215
- ], SoleInvoice.prototype, "payer", void 0);
4216
- __decorate([
4217
- Type(() => SoleInvoiceTemplate)
4218
- ], SoleInvoice.prototype, "template", void 0);
4219
- __decorate([
4220
- Type(() => BankAccount)
4221
- ], SoleInvoice.prototype, "bankAccount", void 0);
4222
4145
 
4223
- /**
4224
- * Class contains traveled kilometers and work usage percent in 12 weeks date range
4225
- * @TODO Vik: Best period: move this and related logic to backend
4226
- * @TODO Alex: check if we need this class when calculation refactored with backend
4227
- */
4228
- class LogbookPeriod {
4229
- isEndOfYear() {
4230
- return this.to === new FinancialYear().endDate;
4231
- }
4232
- getWorkUsageByClaim(claim) {
4233
- const claimKilometers = this.logbooks.getByVehicleClaim(claim).getClaimableLogbooks().kilometers;
4234
- return Math.round(this.workUsage * (claimKilometers / this.kilometers));
4146
+ class TransactionAllocationCollection extends Collection {
4147
+ get amount() {
4148
+ return this.sumBy('amount');
4235
4149
  }
4236
- }
4237
- __decorate([
4238
- Type(() => Date)
4239
- ], LogbookPeriod.prototype, "from", void 0);
4240
- __decorate([
4241
- Type(() => Date)
4242
- ], LogbookPeriod.prototype, "to", void 0);
4243
-
4244
- class Vehicle$1 extends AbstractModel {
4245
- }
4246
-
4247
- class VehicleLogbook$1 extends AbstractModel {
4248
- }
4249
-
4250
- // problem with DateRange and typescript. See more https://github.com/rotaready/moment-range/issues/263
4251
- const moment = extendMoment(moment$1);
4252
- class VehicleLogbook extends VehicleLogbook$1 {
4253
- get kilometers() {
4254
- return this.odometerEnd - this.odometerStart;
4150
+ getByTransactionsIds(ids) {
4151
+ return new TransactionAllocationCollection(this.items.filter((allocation) => ids.includes(allocation.transaction.id)));
4255
4152
  }
4256
- get tankType() {
4257
- return this.isSoleTank() ? TankTypeEnum.SOLE : TankTypeEnum.WORK;
4153
+ getByBankTransactionsIds(ids) {
4154
+ return new TransactionAllocationCollection(this.items.filter((allocation) => ids.includes(allocation.bankTransaction.id)));
4258
4155
  }
4259
- isWorkTank() {
4260
- return !this.business;
4156
+ /**
4157
+ * Group allocations by bank account via bank transactions collection
4158
+ */
4159
+ groupByBankAccount(bankTransactions) {
4160
+ // Group bank transactions by bank account id
4161
+ const bankTransactionsByBankAccount = new CollectionDictionary(bankTransactions, 'bankAccount.id');
4162
+ // Create empty dictionary of transaction allocations
4163
+ const allocationsByBankAccount = new CollectionDictionary(new TransactionAllocationCollection([]));
4164
+ // Fill allocations dictionary with bank transactions dictionary keys and allocations related with each bank transaction collection
4165
+ bankTransactionsByBankAccount.keys.forEach((key) => {
4166
+ allocationsByBankAccount.add(key, this.getByBankTransactionsIds(bankTransactionsByBankAccount.get(key).getIds()));
4167
+ });
4168
+ return allocationsByBankAccount;
4261
4169
  }
4262
- isSoleTank() {
4263
- return !!this.business;
4170
+ /**
4171
+ * check if collection includes allocation of passed transaction
4172
+ */
4173
+ hasTransaction(transaction) {
4174
+ return !!this.items.find((allocation) => allocation.transaction.id === transaction.id);
4264
4175
  }
4265
4176
  /**
4266
- * Get logbook period date range from logbook date and weeksInPeriod duration
4177
+ * Check if bank transaction is related with current allocations
4267
4178
  */
4268
- getPeriod() {
4269
- return moment.rangeFromInterval('milliseconds', VehicleLogbook.bestPeriodDuration, this.date);
4179
+ hasBankTransaction(bankTransaction) {
4180
+ return !!this.items.find((allocation) => allocation.bankTransaction.id === bankTransaction.id);
4270
4181
  }
4271
4182
  }
4272
- /**
4273
- * Logbook period duration in milliseconds.
4274
- * https://taxtank.atlassian.net/wiki/spaces/TAXTANK/pages/211517441/Logbook+Vehicle
4275
- */
4276
- VehicleLogbook.bestPeriodDuration = 12 * 7 * 24 * 3600 * 1000;
4277
- __decorate([
4278
- Type(() => Date)
4279
- ], VehicleLogbook.prototype, "date", void 0);
4280
- __decorate([
4281
- Type(() => SoleBusiness)
4282
- ], VehicleLogbook.prototype, "business", void 0);
4283
4183
 
4284
- class Vehicle extends Vehicle$1 {
4184
+ class Depreciation$1 extends TransactionBase {
4185
+ }
4186
+
4187
+ class DepreciationCapitalProject$1 extends AbstractModel {
4188
+ }
4189
+
4190
+ class DepreciationCapitalProject extends DepreciationCapitalProject$1 {
4285
4191
  }
4286
4192
  __decorate([
4287
- Type(() => VehicleLogbook)
4288
- ], Vehicle.prototype, "logbook", void 0);
4193
+ Type(() => Date)
4194
+ ], DepreciationCapitalProject.prototype, "effectiveDate", void 0);
4289
4195
 
4290
- class VehicleClaim$1 extends AbstractModel {
4196
+ class DepreciationForecast$1 extends AbstractModel {
4291
4197
  }
4292
4198
 
4293
- class VehicleClaimDetails$1 extends AbstractModel {
4199
+ class DepreciationForecast extends DepreciationForecast$1 {
4200
+ get dailyClaimAmount() {
4201
+ return this.claimAmount / this.daysApplied;
4202
+ }
4203
+ get daysApplied() {
4204
+ return moment$1(this.toDate).diff(moment$1(this.fromDate), 'days');
4205
+ }
4206
+ getDaysByMonth(month) {
4207
+ // @TODO find a better place
4208
+ const year = this.financialYear - (month > 5 ? 1 : 0);
4209
+ // forecast date intersect by month
4210
+ const range = new DateRange(this.fromDate, this.toDate).intersect(new DateRange(new Date(year, month, 1), new Date(year, month + 1, 0)));
4211
+ return range ? range.duration('days') + 1 : 0;
4212
+ }
4213
+ get claimOpenBalance() {
4214
+ return this.openBalance * this.claimRate;
4215
+ }
4216
+ get claimCloseBalance() {
4217
+ return this.closeBalance * this.claimRate;
4218
+ }
4219
+ get claimAmount() {
4220
+ return this.amount * this.claimRate;
4221
+ }
4222
+ get claimRate() {
4223
+ return this.claimPercent / 100;
4224
+ }
4225
+ getClaimAmountByMonth(month) {
4226
+ return this.getDaysByMonth(month) * this.dailyClaimAmount;
4227
+ }
4294
4228
  }
4295
4229
 
4296
- var VehicleClaimDetailsMethodEnum;
4297
- (function (VehicleClaimDetailsMethodEnum) {
4298
- VehicleClaimDetailsMethodEnum[VehicleClaimDetailsMethodEnum["KMS"] = 1] = "KMS";
4299
- VehicleClaimDetailsMethodEnum[VehicleClaimDetailsMethodEnum["LOGBOOK"] = 2] = "LOGBOOK";
4300
- })(VehicleClaimDetailsMethodEnum || (VehicleClaimDetailsMethodEnum = {}));
4230
+ /**
4231
+ * Enum with income amount types (Net or Gross)
4232
+ */
4233
+ var IncomeAmountTypeEnum;
4234
+ (function (IncomeAmountTypeEnum) {
4235
+ IncomeAmountTypeEnum[IncomeAmountTypeEnum["NET"] = 0] = "NET";
4236
+ IncomeAmountTypeEnum[IncomeAmountTypeEnum["GROSS"] = 1] = "GROSS";
4237
+ })(IncomeAmountTypeEnum || (IncomeAmountTypeEnum = {}));
4301
4238
 
4302
- class VehicleClaimDetails extends VehicleClaimDetails$1 {
4239
+ var TransactionCategoryEnum;
4240
+ (function (TransactionCategoryEnum) {
4241
+ TransactionCategoryEnum[TransactionCategoryEnum["PROPERTY"] = 0] = "PROPERTY";
4242
+ TransactionCategoryEnum[TransactionCategoryEnum["WORK"] = 1] = "WORK";
4243
+ TransactionCategoryEnum[TransactionCategoryEnum["SOLE"] = 2] = "SOLE";
4244
+ TransactionCategoryEnum[TransactionCategoryEnum["PERSONAL"] = 3] = "PERSONAL";
4245
+ })(TransactionCategoryEnum || (TransactionCategoryEnum = {}));
4246
+
4247
+ /**
4248
+ * @TODO Alex: clarify grouping rules and refactor
4249
+ */
4250
+ class Depreciation extends Depreciation$1 {
4303
4251
  constructor() {
4304
4252
  super(...arguments);
4253
+ this.forecasts = [];
4254
+ this.type = DepreciationTypeEnum.PLANT_EQUIPMENT;
4305
4255
  /**
4306
- * Init default values for the new instances
4256
+ * @TODO remove after ? signs removed from db models
4307
4257
  */
4308
- this.isManual = true;
4309
- this.method = VehicleClaimDetailsMethodEnum.KMS;
4310
- this.financialYear = new FinancialYear().year;
4258
+ this.amount = 0;
4311
4259
  }
4312
- isLogbookMethod() {
4313
- return this.method === VehicleClaimDetailsMethodEnum.LOGBOOK;
4260
+ // Type checking
4261
+ isCapital() {
4262
+ return this.type === DepreciationTypeEnum.CAPITAL_WORKS;
4314
4263
  }
4315
- isKmsMethod() {
4316
- return this.method === VehicleClaimDetailsMethodEnum.KMS;
4264
+ isBorrowingExpense() {
4265
+ return this.type === DepreciationTypeEnum.BORROWING_EXPENSES;
4317
4266
  }
4318
- }
4319
- __decorate([
4320
- Type(() => User)
4321
- ], VehicleClaimDetails.prototype, "user", void 0);
4322
-
4323
- class VehicleClaim extends VehicleClaim$1 {
4324
- constructor() {
4325
- super(...arguments);
4326
- this.kilometers = 0;
4327
- this.workUsage = 0;
4267
+ isAsset() {
4268
+ return this.type === DepreciationTypeEnum.PLANT_EQUIPMENT;
4328
4269
  }
4329
- isWorkTank() {
4330
- return !this.business;
4270
+ // Calculation checking
4271
+ isSBPCalculation() {
4272
+ return this.calculation === DepreciationCalculationEnum.SBP;
4331
4273
  }
4332
- isSoleTank() {
4333
- return !!this.business;
4274
+ isPrimeCost() {
4275
+ return this.calculation === DepreciationCalculationEnum.PRIME_COST;
4334
4276
  }
4335
- get tankType() {
4336
- return this.isSoleTank() ? TankTypeEnum.SOLE : TankTypeEnum.WORK;
4277
+ isDiminishing() {
4278
+ return this.calculation === DepreciationCalculationEnum.DIMINISHING;
4279
+ }
4280
+ // Pool checking
4281
+ isPlantEquipmentPool() {
4282
+ return this.isAsset() && !this.isSBPCalculation() && !this.isWrittenOff() && !this.isLVP();
4283
+ }
4284
+ isLVP() {
4285
+ return this.isAsset()
4286
+ && !this.isSBPCalculation()
4287
+ && this.isDiminishing()
4288
+ && this.currentYearForecast.closeBalance > Depreciation.WRITTEN_OFF_THRESHOLD
4289
+ && this.currentYearForecast.closeBalance <= Depreciation.LOW_VALUE_POOL_THRESHOLD;
4290
+ }
4291
+ isSBP() {
4292
+ return this.isAsset() && this.isSBPCalculation() && !this.isWrittenOff();
4293
+ }
4294
+ isWrittenOff() {
4295
+ return this.writeOffYear === new FinancialYear().year;
4296
+ }
4297
+ get writeOffYear() {
4298
+ var _a;
4299
+ if (!this.writeOffManualDate && !this.writeOffDate) {
4300
+ return null;
4301
+ }
4302
+ return (_a = new FinancialYear(this.writeOffManualDate || this.writeOffDate)) === null || _a === void 0 ? void 0 : _a.year;
4337
4303
  }
4338
4304
  /**
4339
- * Claim amount for KMs method. Exists only for KMs method.
4305
+ * Check if depreciation chart accounts heading related to vehicles category
4340
4306
  */
4341
- getKMSClaimAmount(vehicleClaimRate) {
4342
- return +(this.kilometers * vehicleClaimRate).toFixed(2);
4307
+ isVehicleDepreciation() {
4308
+ var _a;
4309
+ return ((_a = this.chartAccounts.heading) === null || _a === void 0 ? void 0 : _a.id) === ChartAccountsHeadingVehicleListEnum.DEPRECIATION_VEHICLES;
4343
4310
  }
4344
4311
  /**
4345
- * Get logbook claim amount. Exists only for logbook method.
4346
- * ClaimAmount = WorkUsage * transaction/depreciation amount
4312
+ * Get depreciation purchase date
4347
4313
  */
4348
- getLogbookClaimAmount(transactions) {
4349
- const transactionsAmount = transactions
4350
- .getByVehicleClaim(this)
4351
- .getLogbookTransactions()
4352
- .sumBy('amount');
4353
- // Math.abs because amount will be negative (because we sum expenses), but we don't want negative percent value
4354
- return Math.abs(transactionsAmount) * this.workUsage / 100;
4355
- }
4356
- getAverageWeeklyKMS() {
4357
- return this.kilometers / FinancialYear.weeksInYear;
4358
- }
4359
- static getKMSChartAccountsIdByTankType(tankType) {
4360
- return tankType === TankTypeEnum.WORK
4361
- ? ChartAccountsListEnum.KLMS_TRAVELLED_FOR_WORK
4362
- : ChartAccountsListEnum.KLMS_TRAVELLED;
4314
+ getDate() {
4315
+ return this.date;
4363
4316
  }
4364
- }
4365
- /**
4366
- * limit for kms claim method
4367
- */
4368
- VehicleClaim.totalKmsLimit = 5000;
4369
- /**
4370
- * limit for work usage claim method
4371
- */
4372
- VehicleClaim.totalWorkUsagePercent = 100;
4373
- __decorate([
4374
- Type(() => VehicleClaimDetails)
4375
- ], VehicleClaim.prototype, "details", void 0);
4376
- __decorate([
4377
- Type(() => SoleBusiness)
4378
- ], VehicleClaim.prototype, "business", void 0);
4379
-
4380
- class SoleBusinessActivity$1 extends AbstractModel {
4381
- }
4382
-
4383
- class SoleBusiness extends SoleBusiness$1 {
4384
- getPhotoPlaceholder() {
4385
- return `${this.name[0]}${this.name[1]}`;
4386
- }
4387
- getPhoto() {
4388
- return this.logo;
4317
+ /**
4318
+ * @TODO Vik: Research a problem with depreciations without current year forecast
4319
+ */
4320
+ get currentYearForecast() {
4321
+ return this.forecasts.find((forecast) => {
4322
+ return forecast.financialYear === new FinancialYear().year;
4323
+ }) || plainToClass(DepreciationForecast, {
4324
+ financialYear: new FinancialYear().year,
4325
+ openBalance: 0,
4326
+ closeBalance: 0,
4327
+ amount: 0,
4328
+ claimPercent: 0
4329
+ });
4389
4330
  }
4390
- }
4391
- /**
4392
- * Maximum number of businesses that a person can have, according to the ATO
4393
- */
4394
- SoleBusiness.businessesLimit = 6;
4395
- __decorate([
4396
- Type(() => User)
4397
- ], SoleBusiness.prototype, "user", void 0);
4398
- __decorate([
4399
- Type(() => SoleBusinessAllocation)
4400
- ], SoleBusiness.prototype, "allocations", void 0);
4401
- __decorate([
4402
- Type(() => SoleBusinessLoss)
4403
- ], SoleBusiness.prototype, "losses", void 0);
4404
- __decorate([
4405
- Type(() => SoleInvoice)
4406
- ], SoleBusiness.prototype, "invoices", void 0);
4407
- __decorate([
4408
- Type(() => SoleInvoiceTemplate)
4409
- ], SoleBusiness.prototype, "invoiceTemplates", void 0);
4410
- __decorate([
4411
- Type(() => VehicleClaim)
4412
- ], SoleBusiness.prototype, "vehicleClaims", void 0);
4413
- __decorate([
4414
- Type(() => Transaction)
4415
- ], SoleBusiness.prototype, "transactions", void 0);
4416
- __decorate([
4417
- Type(() => Depreciation)
4418
- ], SoleBusiness.prototype, "depreciations", void 0);
4419
- __decorate([
4420
- Type(() => SoleBusinessActivity$1)
4421
- ], SoleBusiness.prototype, "activity", void 0);
4422
- __decorate([
4423
- Type(() => IncomeSource)
4424
- ], SoleBusiness.prototype, "incomeSource", void 0);
4425
-
4426
- class SoleBusinessActivity extends SoleBusinessActivity$1 {
4427
- }
4428
-
4429
- class SoleDepreciationMethod$1 extends AbstractModel {
4430
- }
4431
-
4432
- var SoleDepreciationMethodEnum;
4433
- (function (SoleDepreciationMethodEnum) {
4434
- SoleDepreciationMethodEnum[SoleDepreciationMethodEnum["SBP"] = 1] = "SBP";
4435
- SoleDepreciationMethodEnum[SoleDepreciationMethodEnum["DEPRECIATION"] = 2] = "DEPRECIATION";
4436
- })(SoleDepreciationMethodEnum || (SoleDepreciationMethodEnum = {}));
4437
-
4438
- class SoleDepreciationMethod extends SoleDepreciationMethod$1 {
4439
- isSBP() {
4440
- return this.method === SoleDepreciationMethodEnum.SBP;
4331
+ getForecastByYear(year) {
4332
+ return this.forecasts.find((forecast) => {
4333
+ return forecast.financialYear === year;
4334
+ });
4441
4335
  }
4442
- }
4443
-
4444
- class SoleDetails$1 extends AbstractModel {
4445
- }
4446
-
4447
- class SoleDetails extends SoleDetails$1 {
4448
- }
4449
- __decorate([
4450
- Type(() => User)
4451
- ], SoleDetails.prototype, "user", void 0);
4452
-
4453
- class BasReport$1 extends AbstractModel {
4454
- }
4455
-
4456
- class BasReport extends BasReport$1 {
4457
- get taxWithheldTotal() {
4458
- return this.taxWithheldSalary + this.taxWithheldNoABN;
4336
+ getClaimAmountByYear(year) {
4337
+ var _a;
4338
+ return ((_a = this.getForecastByYear(year)) === null || _a === void 0 ? void 0 : _a.claimAmount) || 0;
4459
4339
  }
4460
- get paygTaxInstalmentOwedToATO() {
4461
- return this.paygTaxInstalment > 0 ? this.paygTaxInstalment : 0;
4340
+ getCloseBalanceByYear(year) {
4341
+ var _a;
4342
+ return ((_a = this.getForecastByYear(year)) === null || _a === void 0 ? void 0 : _a.closeBalance) || 0;
4462
4343
  }
4463
- get paygTaxInstalmentOwedByATO() {
4464
- return this.paygTaxInstalment < 0 ? Math.abs(this.paygTaxInstalment) : 0;
4344
+ isBuildingAtCost() {
4345
+ return this.chartAccounts.id === ChartAccountsListEnum.BUILDING_AT_COST;
4465
4346
  }
4466
- get fuelTaxCreditOwedToATO() {
4467
- return this.fuelTaxCredit < 0 ? this.fuelTaxCredit : 0;
4347
+ /**
4348
+ * Create a new transaction from current depreciation
4349
+ */
4350
+ toTransaction(params = {}) {
4351
+ var _a, _b;
4352
+ return plainToClass(Transaction, Object.assign(params, this, {
4353
+ amount: -((_a = this.currentYearForecast) === null || _a === void 0 ? void 0 : _a.amount) || 0,
4354
+ claimAmount: -((_b = this.currentYearForecast) === null || _b === void 0 ? void 0 : _b.claimAmount) || 0,
4355
+ }));
4468
4356
  }
4469
- get fuelTaxCreditOwedByATO() {
4470
- return this.fuelTaxCredit > 0 ? Math.abs(this.fuelTaxCredit) : 0;
4357
+ /**
4358
+ * @TODO Michael: remove and check everywhere in reports
4359
+ */
4360
+ get claimAmount() {
4361
+ var _a;
4362
+ return ((_a = this.currentYearForecast) === null || _a === void 0 ? void 0 : _a.claimAmount) || 0;
4471
4363
  }
4472
- get owesToATO() {
4473
- return this.incomeGST + this.taxWithheldTotal + this.paygTaxInstalmentOwedToATO + this.fuelTaxCreditOwedToATO;
4364
+ get amountWithGst() {
4365
+ // gst applies only to new assets
4366
+ if (this.isNew()) {
4367
+ return super.amountWithGst;
4368
+ }
4369
+ return this.amount;
4474
4370
  }
4475
- get owedByATO() {
4476
- return this.expenseGST + this.paygTaxInstalmentOwedByATO + this.fuelTaxCreditOwedByATO;
4371
+ get gstClaimAmount() {
4372
+ var _a;
4373
+ return this.gstAmount * ((_a = this.currentYearForecast) === null || _a === void 0 ? void 0 : _a.claimRate);
4477
4374
  }
4478
4375
  /**
4479
- * GST payable to the ATO, or refundable from the ATO in case it's negative
4376
+ * assets purchased in the current financial year
4480
4377
  */
4481
- get gst() {
4482
- return this.incomeGST + this.expenseGST + this.taxWithheldTotal + this.paygTaxInstalment - this.fuelTaxCredit;
4378
+ isNew() {
4379
+ return new FinancialYear(this.date).year === new FinancialYear().year;
4483
4380
  }
4484
4381
  }
4382
+ Depreciation.WRITTEN_OFF_THRESHOLD = 300;
4383
+ Depreciation.LOW_VALUE_POOL_THRESHOLD = 1000;
4485
4384
  __decorate([
4486
4385
  Type(() => Date)
4487
- ], BasReport.prototype, "dateFrom", void 0);
4386
+ ], Depreciation.prototype, "purchaseDate", void 0);
4488
4387
  __decorate([
4489
4388
  Type(() => Date)
4490
- ], BasReport.prototype, "dateTo", void 0);
4491
-
4492
- /**
4493
- * role hierarchy
4494
- */
4495
- const USER_ROLES = {
4496
- ROLE_FIRM_OWNER: [UserRolesEnum.FIRM_OWNER, UserRolesEnum.FIRM_MANAGER, UserRolesEnum.ACCOUNTANT, UserRolesEnum.ADVISOR],
4497
- ROLE_FIRM_MANAGER: [UserRolesEnum.FIRM_MANAGER, UserRolesEnum.ACCOUNTANT, UserRolesEnum.ADVISOR],
4498
- ROLE_EMPLOYEE: [UserRolesEnum.ACCOUNTANT, UserRolesEnum.ADVISOR],
4499
- ROLE_ACCOUNTANT: [UserRolesEnum.ACCOUNTANT],
4500
- ROLE_ADVISOR: [UserRolesEnum.ADVISOR],
4501
- ROLE_CLIENT: [UserRolesEnum.CLIENT],
4502
- ROLE_USER_SUBSCRIPTION: [UserRolesEnum.SUBSCRIPTION],
4503
- ROLE_USER_WORK: [UserRolesEnum.WORK_TANK],
4504
- ROLE_USER_PROPERTY: [UserRolesEnum.PROPERTY_TANK],
4505
- ROLE_USER_SOLE: [UserRolesEnum.SOLE_TANK],
4506
- };
4389
+ ], Depreciation.prototype, "date", void 0);
4390
+ __decorate([
4391
+ Type(() => Date)
4392
+ ], Depreciation.prototype, "lowValuePoolDate", void 0);
4393
+ __decorate([
4394
+ Type(() => Date)
4395
+ ], Depreciation.prototype, "writeOffManualDate", void 0);
4396
+ __decorate([
4397
+ Type(() => Date)
4398
+ ], Depreciation.prototype, "writeOffDate", void 0);
4399
+ __decorate([
4400
+ Type(() => DepreciationCapitalProject)
4401
+ ], Depreciation.prototype, "depreciationCapitalProject", void 0);
4402
+ __decorate([
4403
+ Type(() => DepreciationForecast),
4404
+ Transform(({ value, obj }) => {
4405
+ // value = array of DepreciationForecast
4406
+ // obj = plain (whole Depreciation object)
4407
+ // Set isLVP flag for each DepreciationForecast
4408
+ // @TODO refactor
4409
+ value.forEach((forecast) => {
4410
+ forecast.isLVP =
4411
+ forecast.closeBalance < 1000 &&
4412
+ obj.calculation === DepreciationCalculationEnum.DIMINISHING &&
4413
+ obj.type === DepreciationTypeEnum.PLANT_EQUIPMENT &&
4414
+ !(obj.amount > Depreciation.WRITTEN_OFF_THRESHOLD && obj.amount < Depreciation.LOW_VALUE_POOL_THRESHOLD) &&
4415
+ !(obj.writeOffDate && new FinancialYear(new Date(obj.writeOffDate)).year === obj.financialYear);
4416
+ });
4417
+ return value;
4418
+ })
4419
+ ], Depreciation.prototype, "forecasts", void 0);
4420
+ __decorate([
4421
+ Type(() => ChartAccounts)
4422
+ ], Depreciation.prototype, "chartAccounts", void 0);
4423
+ __decorate([
4424
+ Type(() => BankTransaction)
4425
+ ], Depreciation.prototype, "bankTransaction", void 0);
4426
+ __decorate([
4427
+ Exclude()
4428
+ ], Depreciation.prototype, "amountWithGst", null);
4429
+ __decorate([
4430
+ Exclude()
4431
+ ], Depreciation.prototype, "gstClaimAmount", null);
4507
4432
 
4508
- class User extends User$1 {
4509
- get fullName() {
4510
- return `${this.firstName} ${this.lastName}`;
4511
- }
4512
- get activeSubscription() {
4513
- return this.subscriptions.find((ss) => ss.isActive);
4514
- }
4515
- get lastSubscription() {
4516
- return this.subscriptions[this.subscriptions.length - 1];
4517
- }
4518
- /**
4519
- * @TODO refactor
4520
- *
4521
- */
4522
- get position() {
4523
- switch (true) {
4524
- case this.isFirmOwner():
4525
- return USER_WORK_POSITION[UserRolesEnum.FIRM_OWNER];
4526
- case this.isManager():
4527
- return USER_WORK_POSITION[UserRolesEnum.FIRM_MANAGER];
4528
- case this.isEmployee():
4529
- return USER_WORK_POSITION[UserRolesEnum.EMPLOYEE];
4530
- default:
4531
- return '';
4532
- }
4533
- }
4534
- /**
4535
- * search roles including hierarchy
4536
- */
4537
- hasRoles(roles) {
4538
- roles = roles instanceof Array ? roles : [roles];
4539
- if (!roles.length) {
4540
- return true;
4541
- }
4542
- roles = flatten(roles.map((role) => USER_ROLES[role]));
4543
- return !!intersection(this.roles, roles).length;
4544
- }
4545
- isOnboarding() {
4546
- return this.status === UserStatusEnum.ON_BOARDING;
4547
- }
4548
- hasSubscription() {
4549
- return this.roles.includes(UserRolesEnum.SUBSCRIPTION);
4550
- }
4551
- isLoggedIn() {
4552
- return this.id === +localStorage.getItem('userId');
4553
- }
4554
- isClient() {
4555
- return this.roles.includes(UserRolesEnum.CLIENT);
4433
+ /**
4434
+ * used to combine transactions/depreciations
4435
+ */
4436
+ class TransactionBaseCollection extends Collection {
4437
+ getClaimAmountByBusinessId(businessId) {
4438
+ return +this.filterBy('business.id', businessId).items.map((transaction) => transaction instanceof Depreciation ? -transaction.claimAmount : transaction['claimAmount']).reduce((sum, claimAmount) => sum + claimAmount, 0).toFixed(2);
4556
4439
  }
4557
- isEmployee() {
4558
- return this.isAccountant() || this.isAdvisor();
4440
+ getSoleTransactions() {
4441
+ return this.filter((transaction) => transaction.isSoleTank());
4559
4442
  }
4560
- isAccountant() {
4561
- return this.roles.includes(UserRolesEnum.ACCOUNTANT);
4443
+ }
4444
+
4445
+ // @TODO Alex move here all collections
4446
+
4447
+ // @TODO Alex: refactor: move here allocations methods, netAmount = amount, grossAmount calculation, remove unused methods, etc.
4448
+ class Transaction extends Transaction$1 {
4449
+ constructor() {
4450
+ super(...arguments);
4451
+ // list of child transactions (fees)
4452
+ this.transactions = [];
4453
+ this.metadata = [];
4454
+ this.allocations = [];
4455
+ this.tax = 0;
4456
+ this.operation = TransactionOperationEnum.FIND_AND_MATCH;
4457
+ this.claimPercent = 100;
4458
+ this.amount = 0;
4562
4459
  }
4563
- isAdvisor() {
4564
- return this.roles.includes(UserRolesEnum.ADVISOR);
4460
+ isDebit() {
4461
+ return this.type === TransactionTypeEnum.DEBIT;
4565
4462
  }
4566
- isImpersonator() {
4567
- return this.roles.includes(UserRolesEnum.SWITCH_USER);
4463
+ isCredit() {
4464
+ return this.type === TransactionTypeEnum.CREDIT;
4568
4465
  }
4569
4466
  /**
4570
- * Check if current user is firm owner
4467
+ * @TODO move to base collection
4571
4468
  */
4572
- isFirmOwner() {
4573
- return this.roles.includes(UserRolesEnum.FIRM_OWNER);
4469
+ isIncome() {
4470
+ // @TODO not used
4471
+ if (!this.chartAccounts) {
4472
+ return this.isCredit();
4473
+ }
4474
+ return CHART_ACCOUNTS_CATEGORIES.income.includes(this.chartAccounts.category);
4574
4475
  }
4575
4476
  /**
4576
- * Check if current user is firm manager
4477
+ * @TODO move to base collection
4577
4478
  */
4578
- isManager() {
4579
- return this.roles.includes(UserRolesEnum.FIRM_MANAGER) || this.roles.includes(UserRolesEnum.FIRM_OWNER);
4479
+ isExpense() {
4480
+ // @TODO not used
4481
+ if (!this.chartAccounts) {
4482
+ return this.isDebit();
4483
+ }
4484
+ return CHART_ACCOUNTS_CATEGORIES.expense.includes(this.chartAccounts.category);
4580
4485
  }
4581
- /**
4582
- * check if user has property tank access
4583
- */
4584
- hasPropertyTank() {
4585
- return this.roles ?
4586
- this.roles.includes(UserRolesEnum.PROPERTY_TANK) :
4587
- !!this.getSubscriptionRole(UserRolesEnum.PROPERTY_TANK);
4486
+ isPersonal() {
4487
+ return CHART_ACCOUNTS_CATEGORIES.personal.includes(this.chartAccounts.category);
4588
4488
  }
4589
- /**
4590
- * check if user has property tank access
4591
- */
4592
- hasWorkTank() {
4593
- return this.roles ?
4594
- this.roles.includes(UserRolesEnum.WORK_TANK) :
4595
- !!this.getSubscriptionRole(UserRolesEnum.WORK_TANK);
4489
+ isInterest() {
4490
+ return this.chartAccounts.id === ChartAccountsListEnum.INTEREST_ON_LOAN;
4491
+ }
4492
+ get chartAccountsCategories() {
4493
+ switch (true) {
4494
+ case this.isPersonal():
4495
+ return CHART_ACCOUNTS_CATEGORIES.personal;
4496
+ case this.isPropertyTank():
4497
+ return CHART_ACCOUNTS_CATEGORIES.property;
4498
+ case this.isSoleTank():
4499
+ return CHART_ACCOUNTS_CATEGORIES.sole;
4500
+ default:
4501
+ return CHART_ACCOUNTS_CATEGORIES.work;
4502
+ }
4596
4503
  }
4597
4504
  /**
4598
- * Get user subscription role by provided role type
4599
- * @param roleType by which role should be returned
4505
+ * Check if transaction has 'Kms travelled for work' chart accounts category
4600
4506
  */
4601
- getSubscriptionRole(roleType) {
4602
- var _a;
4603
- /**
4604
- * Add @TODO for Alex to check if we should use getLastSubscription here
4605
- */
4606
- return (_a = this.activeSubscription) === null || _a === void 0 ? void 0 : _a.items.find((subscriptionItem) => subscriptionItem.price.product.role.includes(roleType));
4507
+ get isKmsChartAccountsCategory() {
4508
+ return this.chartAccounts.id === ChartAccountsListEnum.KLMS_TRAVELLED_FOR_WORK;
4607
4509
  }
4608
4510
  /**
4609
- * Get user's photo link
4511
+ * Get transaction date
4610
4512
  */
4611
- getPhoto() {
4612
- return this.photo;
4513
+ getDate() {
4514
+ return this.date;
4613
4515
  }
4614
4516
  /**
4615
- * get user's initials
4517
+ * Check if transaction type is vehicle
4616
4518
  */
4617
- getPhotoPlaceholder() {
4618
- return `${this.firstName[0].toUpperCase()}${this.lastName[0].toUpperCase()}`;
4519
+ isVehicleTransaction() {
4520
+ return this.chartAccounts.isVehicleExpense();
4619
4521
  }
4620
- }
4621
- __decorate([
4622
- Type(() => ServiceSubscription)
4623
- ], User.prototype, "subscriptions", void 0);
4624
- __decorate([
4625
- Type(() => ClientDetails)
4626
- ], User.prototype, "clientDetails", void 0);
4627
- __decorate([
4628
- Type(() => EmployeeDetails)
4629
- ], User.prototype, "employeeDetails", void 0);
4630
- __decorate([
4631
- Type(() => SoleDetails)
4632
- ], User.prototype, "soleDetails", void 0);
4633
- __decorate([
4634
- Type(() => Address)
4635
- ], User.prototype, "address", void 0);
4636
- __decorate([
4637
- Type(() => Phone)
4638
- ], User.prototype, "phone", void 0);
4639
- __decorate([
4640
- Type(() => Date)
4641
- ], User.prototype, "createdAt", void 0);
4642
- __decorate([
4643
- Type(() => User)
4644
- ], User.prototype, "clients", void 0);
4645
-
4646
- class PropertySubscription extends PropertySubscription$1 {
4647
- }
4648
- __decorate([
4649
- Type(() => Date)
4650
- ], PropertySubscription.prototype, "createdAt", void 0);
4651
- __decorate([
4652
- Type(() => User)
4653
- ], PropertySubscription.prototype, "user", void 0);
4654
-
4655
- class PropertyCategory$1 extends AbstractModel {
4656
- }
4657
-
4658
- var PropertyCategoryListEnum;
4659
- (function (PropertyCategoryListEnum) {
4660
- PropertyCategoryListEnum[PropertyCategoryListEnum["OWNER_OCCUPIED"] = 3] = "OWNER_OCCUPIED";
4661
- PropertyCategoryListEnum[PropertyCategoryListEnum["SHARED"] = 4] = "SHARED";
4662
- PropertyCategoryListEnum[PropertyCategoryListEnum["VACANT_LAND"] = 5] = "VACANT_LAND";
4663
- })(PropertyCategoryListEnum || (PropertyCategoryListEnum = {}));
4664
-
4665
- class PropertyCategory extends PropertyCategory$1 {
4666
- // @Todo check if category is Owner Occupied. If will be needed to check more categories - move the checking to the backend
4667
- isOwnerOccupied() {
4668
- return this.id === PropertyCategoryListEnum.OWNER_OCCUPIED;
4522
+ get taxFreeComponent() {
4523
+ return this.getMetadataFieldValue(ChartAccountsMetadataListEnum.TAX_FREE_COMPONENT);
4669
4524
  }
4670
- isVacantLand() {
4671
- return this.id === PropertyCategoryListEnum.VACANT_LAND;
4525
+ get frankingCredit() {
4526
+ return this.getMetadataFieldValue(ChartAccountsMetadataListEnum.FRANKING_CREDIT);
4672
4527
  }
4673
- isShared() {
4674
- return this.id === PropertyCategoryListEnum.SHARED;
4528
+ get eligibleForReduction() {
4529
+ return this.getMetadataFieldValue(ChartAccountsMetadataListEnum.ELIGIBLE_FOR_REDUCTION);
4675
4530
  }
4676
- }
4677
-
4678
- class PropertyValuation$1 extends AbstractModel {
4679
- }
4680
-
4681
- class PropertyDocument$1 extends AbstractModel {
4682
- getApiUrlPrefix() {
4683
- return '';
4531
+ get untaxedElement() {
4532
+ return this.getMetadataFieldValue(ChartAccountsMetadataListEnum.UNTAXED_ELEMENT);
4684
4533
  }
4685
- getEntity() {
4686
- return undefined;
4534
+ /**
4535
+ * Check if transaction reconcile operation if TRANSFER
4536
+ * @TODO bad usage of get (and all is* methods), getter should sound like a noun
4537
+ */
4538
+ get isTransfer() {
4539
+ return this.operation === TransactionOperationEnum.TRANSFER;
4687
4540
  }
4688
- }
4689
-
4690
- class PropertyDocument extends PropertyDocument$1 {
4691
- constructor() {
4692
- super(...arguments);
4693
- this.type = AssetTypeEnum.DOCUMENT;
4694
- this.entityType = AssetEntityTypeEnum.PROPERTIES;
4541
+ isFindAndMatch() {
4542
+ return this.operation === TransactionOperationEnum.FIND_AND_MATCH;
4695
4543
  }
4696
- getApiUrlPrefix() {
4697
- return '';
4544
+ isMatchInvoice() {
4545
+ return this.operation === TransactionOperationEnum.MATCH_INVOICE;
4698
4546
  }
4699
- getEntity() {
4700
- return this.property;
4547
+ get debit() {
4548
+ return this.isDebit() ? Math.abs(this.grossAmount) : 0;
4701
4549
  }
4702
- }
4703
-
4704
- class PropertyValuation extends PropertyValuation$1 {
4705
- get financialYear() {
4706
- return new FinancialYear(this.date).year;
4550
+ get credit() {
4551
+ return this.isCredit() ? Math.abs(this.grossAmount) : 0;
4707
4552
  }
4708
- isCurrentYear() {
4709
- return this.financialYear === new FinancialYear(new Date()).year;
4553
+ /**
4554
+ * Get value of transaction metadata field
4555
+ * @param field for which value should be returned
4556
+ * @Todo modify 'metadata' property from array to Collection
4557
+ */
4558
+ getMetadataFieldValue(field) {
4559
+ var _a;
4560
+ return +((_a = this.metadata.find((transactionMetadata) => {
4561
+ return transactionMetadata.chartAccountsMetadata.id === field;
4562
+ })) === null || _a === void 0 ? void 0 : _a.value) || 0;
4710
4563
  }
4711
- }
4712
- __decorate([
4713
- Type(() => Date)
4714
- ], PropertyValuation.prototype, "date", void 0);
4715
- __decorate([
4716
- Type(() => PropertyDocument)
4717
- ], PropertyValuation.prototype, "document", void 0);
4718
- __decorate([
4719
- Exclude()
4720
- ], PropertyValuation.prototype, "documentFile", void 0);
4721
-
4722
- class PropertyForecast$1 extends AbstractModel {
4723
- }
4724
-
4725
- class PropertyForecast extends PropertyForecast$1 {
4726
- constructor() {
4727
- super(...arguments);
4728
- this.financialYear = new FinancialYear(new Date()).year;
4564
+ isCash() {
4565
+ return this.source === TransactionSourceEnum.CASH;
4729
4566
  }
4730
4567
  /**
4731
- * Get cash position
4732
- * Cash Position = Income - Expense - Interest
4568
+ * Create Depreciation instance based on Transaction
4733
4569
  */
4734
- get cashPosition() {
4735
- return this.income + this.expense + this.interest;
4570
+ toDepreciation() {
4571
+ return plainToClass(Depreciation, {
4572
+ date: this.date,
4573
+ amount: this.amount,
4574
+ chartAccounts: this.chartAccounts,
4575
+ description: this.description,
4576
+ type: DepreciationTypeEnum.PLANT_EQUIPMENT,
4577
+ claimPercent: this.claimPercent,
4578
+ property: this.property,
4579
+ calculation: this.property ? this.property.depreciationCalculation : DepreciationCalculationEnum.PRIME_COST
4580
+ });
4736
4581
  }
4737
4582
  /**
4738
- * Get tax position
4739
- * Tax Position = Income - Expense - Interest - Depreciation
4583
+ * Check if transaction is completely allocated
4740
4584
  */
4741
- get taxPosition() {
4742
- return this.cashPosition + this.depreciation;
4585
+ isAllocated(allocations) {
4586
+ return this.grossAmount === this.getAllocatedAmount(allocations);
4587
+ }
4588
+ getAllocatedAmount(allocations) {
4589
+ return allocations.filterBy('transaction.id', this.id).sumBy('amount');
4590
+ }
4591
+ getAllocatedClaimAmount(allocations) {
4592
+ return this.getAllocatedAmount(allocations) * this.claimRatio;
4593
+ }
4594
+ getUnallocatedAmount(allocations) {
4595
+ return this.netAmount - this.getAllocatedAmount(allocations);
4743
4596
  }
4744
4597
  /**
4745
- * Get rental return percent
4746
- * Rental Return = Income / Market Value
4598
+ * Total transaction amount including taxes and other additional amounts
4747
4599
  */
4748
- get rentalReturn() {
4749
- return this.income / this.marketValue;
4600
+ get grossAmount() {
4601
+ let grossAmount = super.grossAmount + this.tax;
4602
+ if (this.isExpense()) {
4603
+ return grossAmount;
4604
+ }
4605
+ // salary included transactions affect parent transaction amount and as a result grossAmount, skip to avoid double sum
4606
+ grossAmount += new Collection(this.transactions)
4607
+ .filter((t) => { var _a; return !((_a = t.chartAccounts) === null || _a === void 0 ? void 0 : _a.isSalaryIncluded()); })
4608
+ .sumBy('amount');
4609
+ if (this.isWorkTank()) {
4610
+ grossAmount += this.frankingCredit - this.taxFreeComponent - this.eligibleForReduction;
4611
+ }
4612
+ return +(Math.round(grossAmount * 100) / 100).toFixed(2);
4750
4613
  }
4751
4614
  /**
4752
- * Check if forecast is for real current fin year (not selected in the sidebar)
4615
+ * netAmount matches received payment (bankTransaction amount), includes gst and salary included adjustments
4616
+ * ie user received 1000$ salary including 100$ tips. NetAmount=1000$, amount=900$, tips=100$
4753
4617
  */
4754
- isCurrentYear() {
4755
- return this.financialYear === new FinancialYear(new Date()).year;
4618
+ get netAmount() {
4619
+ return new Collection(this.transactions)
4620
+ .filter((t) => { var _a; return (_a = t.chartAccounts) === null || _a === void 0 ? void 0 : _a.isSalaryIncluded(); })
4621
+ .sumBy('amount') + this.amountWithGst;
4756
4622
  }
4757
4623
  }
4758
4624
  __decorate([
4759
- Transform(({ value }) => +value)
4760
- ], PropertyForecast.prototype, "income", void 0);
4625
+ Type(() => Transaction)
4626
+ ], Transaction.prototype, "transactions", void 0);
4761
4627
  __decorate([
4762
- Transform(({ value }) => -Math.abs(+value))
4763
- ], PropertyForecast.prototype, "expense", void 0);
4628
+ Type(() => Property)
4629
+ ], Transaction.prototype, "property", void 0);
4764
4630
  __decorate([
4765
- Transform(({ value }) => -Math.abs(+value))
4766
- ], PropertyForecast.prototype, "interest", void 0);
4631
+ Type(() => TransactionReceipt)
4632
+ ], Transaction.prototype, "receipt", void 0);
4767
4633
  __decorate([
4768
- Transform(({ value }) => -Math.abs(+value))
4769
- ], PropertyForecast.prototype, "depreciation", void 0);
4634
+ Type(() => ChartAccounts)
4635
+ ], Transaction.prototype, "chartAccounts", void 0);
4770
4636
  __decorate([
4771
- Transform(({ value }) => +value)
4772
- ], PropertyForecast.prototype, "loanBalance", void 0);
4637
+ Type(() => IncomeSource)
4638
+ ], Transaction.prototype, "incomeSource", void 0);
4773
4639
  __decorate([
4774
- Transform(({ value }) => +value)
4775
- ], PropertyForecast.prototype, "marketValue", void 0);
4640
+ Type(() => TransactionMetadata)
4641
+ ], Transaction.prototype, "metadata", void 0);
4642
+ __decorate([
4643
+ Type(() => Transaction)
4644
+ ], Transaction.prototype, "transfer", void 0);
4645
+ __decorate([
4646
+ Type(() => Loan)
4647
+ ], Transaction.prototype, "loan", void 0);
4648
+ __decorate([
4649
+ Type(() => Date)
4650
+ ], Transaction.prototype, "date", void 0);
4651
+ __decorate([
4652
+ Type(() => TransactionAllocation)
4653
+ ], Transaction.prototype, "allocations", void 0);
4776
4654
 
4777
- class PropertyCategoryMovement$1 extends AbstractModel {
4655
+ class SoleInvoiceItem extends SoleInvoiceItem$1 {
4656
+ constructor() {
4657
+ super(...arguments);
4658
+ this.isGST = false;
4659
+ }
4660
+ get totalPrice() {
4661
+ return this.price * this.quantity;
4662
+ }
4778
4663
  }
4664
+ __decorate([
4665
+ Type(() => SoleInvoice)
4666
+ ], SoleInvoiceItem.prototype, "invoice", void 0);
4667
+ __decorate([
4668
+ Type(() => ChartAccounts)
4669
+ ], SoleInvoiceItem.prototype, "chartAccounts", void 0);
4670
+ __decorate([
4671
+ Type(() => Transaction)
4672
+ ], SoleInvoiceItem.prototype, "transaction", void 0);
4779
4673
 
4780
- class PropertyCategoryMovement extends PropertyCategoryMovement$1 {
4674
+ class SoleContact$1 extends AbstractModel {
4675
+ }
4676
+
4677
+ // @TODO Alex/Vik: Create some base class for User and SoleContact with common methods and properties
4678
+ class SoleContact extends SoleContact$1 {
4679
+ get fullName() {
4680
+ return `${this.firstName} ${this.lastName}`;
4681
+ }
4682
+ getPhotoPlaceholder() {
4683
+ return `${this.firstName[0].toUpperCase()}${this.lastName[0].toUpperCase()}`;
4684
+ }
4685
+ // @TODO Vik: add photo field to SoleContact
4686
+ getPhoto() {
4687
+ return '';
4688
+ }
4781
4689
  }
4782
4690
  __decorate([
4783
- Type(() => PropertyValuation)
4784
- ], PropertyCategoryMovement.prototype, "valuation", void 0);
4691
+ Type(() => User)
4692
+ ], SoleContact.prototype, "user", void 0);
4785
4693
  __decorate([
4786
- Type(() => PropertyCategory)
4787
- ], PropertyCategoryMovement.prototype, "propertyCategory", void 0);
4694
+ Type(() => Phone)
4695
+ ], SoleContact.prototype, "phone", void 0);
4788
4696
  __decorate([
4789
- Type(() => Date)
4790
- ], PropertyCategoryMovement.prototype, "fromDate", void 0);
4697
+ Type(() => Address)
4698
+ ], SoleContact.prototype, "address", void 0);
4791
4699
  __decorate([
4792
- Type(() => Date)
4793
- ], PropertyCategoryMovement.prototype, "toDate", void 0);
4700
+ Type(() => SoleInvoice)
4701
+ ], SoleContact.prototype, "invoices", void 0);
4794
4702
 
4795
- var TaxExemptionEnum;
4796
- (function (TaxExemptionEnum) {
4797
- TaxExemptionEnum[TaxExemptionEnum["ONE_YEAR_RULE"] = 1] = "ONE_YEAR_RULE";
4798
- // principle place of residence
4799
- TaxExemptionEnum[TaxExemptionEnum["PPR"] = 2] = "PPR";
4800
- TaxExemptionEnum[TaxExemptionEnum["SIX_YEARS_RULE"] = 3] = "SIX_YEARS_RULE";
4801
- TaxExemptionEnum[TaxExemptionEnum["INVESTMENT_TO_PPR"] = 4] = "INVESTMENT_TO_PPR";
4802
- TaxExemptionEnum[TaxExemptionEnum["PPR_TO_INVESTMENT"] = 5] = "PPR_TO_INVESTMENT";
4803
- TaxExemptionEnum[TaxExemptionEnum["TRANSFER"] = 6] = "TRANSFER";
4804
- TaxExemptionEnum[TaxExemptionEnum["OTHER"] = 7] = "OTHER";
4805
- })(TaxExemptionEnum || (TaxExemptionEnum = {}));
4703
+ class SoleInvoiceTemplate$1 extends AbstractModel {
4704
+ }
4806
4705
 
4807
- var TaxExemptionMetadataEnum;
4808
- (function (TaxExemptionMetadataEnum) {
4809
- // principle place of residence
4810
- TaxExemptionMetadataEnum[TaxExemptionMetadataEnum["PPR_DAYS"] = 1] = "PPR_DAYS";
4811
- // market value once it was moved, decimal
4812
- TaxExemptionMetadataEnum[TaxExemptionMetadataEnum["MARKET_VALUE"] = 2] = "MARKET_VALUE";
4813
- TaxExemptionMetadataEnum[TaxExemptionMetadataEnum["CLAIM_PERCENT"] = 3] = "CLAIM_PERCENT";
4814
- })(TaxExemptionMetadataEnum || (TaxExemptionMetadataEnum = {}));
4706
+ var SoleInvoiceTemplateTaxTypeEnum;
4707
+ (function (SoleInvoiceTemplateTaxTypeEnum) {
4708
+ SoleInvoiceTemplateTaxTypeEnum[SoleInvoiceTemplateTaxTypeEnum["TAX_EXCLUSIVE"] = 0] = "TAX_EXCLUSIVE";
4709
+ SoleInvoiceTemplateTaxTypeEnum[SoleInvoiceTemplateTaxTypeEnum["TAX_INCLUSIVE"] = 1] = "TAX_INCLUSIVE";
4710
+ SoleInvoiceTemplateTaxTypeEnum[SoleInvoiceTemplateTaxTypeEnum["NO_TAX"] = 2] = "NO_TAX";
4711
+ })(SoleInvoiceTemplateTaxTypeEnum || (SoleInvoiceTemplateTaxTypeEnum = {}));
4815
4712
 
4816
- class PropertySaleTaxExemptionMetadataCollection extends Collection {
4817
- getPPRDays() {
4818
- var _a, _b;
4819
- return (_b = (_a = this.getByMetadataId(TaxExemptionMetadataEnum.PPR_DAYS)) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 0;
4820
- }
4821
- getMarketValue() {
4822
- var _a, _b;
4823
- return (_b = (_a = this.getByMetadataId(TaxExemptionMetadataEnum.MARKET_VALUE)) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 0;
4713
+ class SoleInvoiceTemplate extends SoleInvoiceTemplate$1 {
4714
+ constructor() {
4715
+ super(...arguments);
4716
+ /**
4717
+ * Affects to SoleInvoiceItem.isGST flag availability.
4718
+ * When NONE: isGST is unavailable
4719
+ * When EXCLUSIVE: GST amount added additionaly to invoice total price
4720
+ * When INCLUSIVE: GST amount is already included to invoice total price
4721
+ */
4722
+ this.taxType = SoleInvoiceTemplateTaxTypeEnum.NO_TAX;
4824
4723
  }
4825
- getClaimPercent() {
4826
- var _a, _b;
4827
- return (_b = (_a = this.getByMetadataId(TaxExemptionMetadataEnum.CLAIM_PERCENT)) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 100;
4724
+ /**
4725
+ * Get term duration in milliseconds
4726
+ */
4727
+ get termTime() {
4728
+ return this.term * 24 * 3600 * 1000;
4828
4729
  }
4829
- getByMetadataId(id) {
4830
- return this.items.find((metadata) => metadata.metadata.id === id);
4730
+ }
4731
+ __decorate([
4732
+ Type(() => SoleBusiness)
4733
+ ], SoleInvoiceTemplate.prototype, "business", void 0);
4734
+ __decorate([
4735
+ Type(() => BankAccount)
4736
+ ], SoleInvoiceTemplate.prototype, "bankAccount", void 0);
4737
+
4738
+ var SoleInvoiceStatusesEnum;
4739
+ (function (SoleInvoiceStatusesEnum) {
4740
+ SoleInvoiceStatusesEnum[SoleInvoiceStatusesEnum["CANCELED"] = 0] = "CANCELED";
4741
+ SoleInvoiceStatusesEnum[SoleInvoiceStatusesEnum["DRAFT"] = 1] = "DRAFT";
4742
+ SoleInvoiceStatusesEnum[SoleInvoiceStatusesEnum["PENDING"] = 2] = "PENDING";
4743
+ SoleInvoiceStatusesEnum[SoleInvoiceStatusesEnum["PAID"] = 3] = "PAID";
4744
+ })(SoleInvoiceStatusesEnum || (SoleInvoiceStatusesEnum = {}));
4745
+
4746
+ var SoleInvoiceTaxTypeEnum;
4747
+ (function (SoleInvoiceTaxTypeEnum) {
4748
+ SoleInvoiceTaxTypeEnum[SoleInvoiceTaxTypeEnum["TAX_EXCLUSIVE"] = 0] = "TAX_EXCLUSIVE";
4749
+ SoleInvoiceTaxTypeEnum[SoleInvoiceTaxTypeEnum["TAX_INCLUSIVE"] = 1] = "TAX_INCLUSIVE";
4750
+ SoleInvoiceTaxTypeEnum[SoleInvoiceTaxTypeEnum["NO_TAX"] = 2] = "NO_TAX";
4751
+ })(SoleInvoiceTaxTypeEnum || (SoleInvoiceTaxTypeEnum = {}));
4752
+
4753
+ class SoleInvoiceItemCollection extends Collection {
4754
+ get gstPrice() {
4755
+ return this.filterBy('isGST', true).sumBy('totalPrice');
4831
4756
  }
4832
4757
  }
4833
4758
 
4834
- class PropertySaleCollection extends Collection {
4835
- get grossCGT() {
4836
- return this.create(this.items.filter((propertySale) => propertySale.grossCGT > 0)).sumBy('grossCGT');
4759
+ class SoleInvoice extends SoleInvoice$1 {
4760
+ constructor() {
4761
+ super(...arguments);
4762
+ this.status = SoleInvoiceStatusesEnum.DRAFT;
4763
+ this.taxType = SoleInvoiceTaxTypeEnum.NO_TAX;
4837
4764
  }
4838
4765
  /**
4839
- * Property sales are CGT applicable unless it has "Principle place of residence" exemption type
4766
+ * Get items array as collection
4840
4767
  */
4841
- getCGTApplicable() {
4842
- return this.create(this.items.filter((propertySale) => propertySale.isCGTApplicable()));
4768
+ get itemsCollection() {
4769
+ return new SoleInvoiceItemCollection(this.items);
4843
4770
  }
4844
- getByPropertyShareIds(ids) {
4845
- return this.filterBy('share.id', ids);
4771
+ /**
4772
+ * Total price of all items
4773
+ */
4774
+ get price() {
4775
+ return this.itemsCollection.sumBy('totalPrice');
4846
4776
  }
4847
- }
4848
-
4849
- class PropertyCollection extends Collection {
4850
4777
  /**
4851
- * Get new property collection filtered by category id
4852
- * @param id id of category for filter
4778
+ * Total invoice price without GST
4853
4779
  */
4854
- getByCategoryId(id) {
4855
- return new PropertyCollection(this.items.filter((property) => property.category.id === id));
4780
+ get netPrice() {
4781
+ if (this.isTaxInclusive()) {
4782
+ return this.price - this.GSTAmount;
4783
+ }
4784
+ return this.price;
4856
4785
  }
4857
4786
  /**
4858
- * Get new property collection filtered by active status
4787
+ * Total final price (net + GST)
4859
4788
  */
4860
- getActiveProperties() {
4861
- return new PropertyCollection(this.items.filter((property) => property.isActive));
4789
+ get grossPrice() {
4790
+ if (this.isTaxExclusive()) {
4791
+ return this.price + this.GSTAmount;
4792
+ }
4793
+ return this.price;
4862
4794
  }
4863
- getCreatedProperties() {
4864
- return new PropertyCollection(this.items.filter((property) => property.isOwn()));
4795
+ /**
4796
+ * Total GST amount
4797
+ */
4798
+ get GSTAmount() {
4799
+ switch (this.taxType) {
4800
+ case SoleInvoiceTaxTypeEnum.TAX_INCLUSIVE:
4801
+ return this.inclusiveGSTAmount;
4802
+ case SoleInvoiceTaxTypeEnum.TAX_EXCLUSIVE:
4803
+ return this.exclusiveGSTAmount;
4804
+ case SoleInvoiceTaxTypeEnum.NO_TAX:
4805
+ return 0;
4806
+ }
4865
4807
  }
4866
4808
  /**
4867
- * Get new property collection filtered by shared
4809
+ * When tax inclusive, GST amount is included to total price
4868
4810
  */
4869
- getSharedProperties() {
4870
- return new PropertyCollection(this.items.filter((property) => !property.isOwn()));
4811
+ get inclusiveGSTAmount() {
4812
+ const gstPrice = this.itemsCollection.gstPrice;
4813
+ return +(gstPrice - (gstPrice / (1 + ChartAccounts.GSTRatio))).toFixed(2);
4871
4814
  }
4872
4815
  /**
4873
- * Properties that are taxed and will be included in reports (Tax summary, My tax report, e.t.c.)
4816
+ * When tax exclusive, GST amount should be added additionally to total price
4874
4817
  */
4875
- getTaxInclusive() {
4876
- return this.create(this.items.filter((property) => property.category.isTaxInclusive));
4818
+ get exclusiveGSTAmount() {
4819
+ return +(this.itemsCollection.gstPrice * ChartAccounts.GSTRatio).toFixed(2);
4877
4820
  }
4878
- getUnsold() {
4879
- return this.create(this.items.filter((property) => !property.isSold()));
4821
+ isDraft() {
4822
+ return this.status === SoleInvoiceStatusesEnum.DRAFT;
4880
4823
  }
4881
- /**
4882
- * Get total purchase price for all properties in the collection
4883
- */
4884
- get purchasePrice() {
4885
- return this.sumBy('purchasePrice');
4824
+ isCancelled() {
4825
+ return this.status === SoleInvoiceStatusesEnum.CANCELED;
4886
4826
  }
4887
- get growthPercent() {
4888
- return this.sumBy('growthPercent');
4827
+ isPending() {
4828
+ return this.status === SoleInvoiceStatusesEnum.PENDING;
4889
4829
  }
4890
- get marketValue() {
4891
- return this.sumBy('marketValue');
4830
+ isPaid() {
4831
+ return this.status === SoleInvoiceStatusesEnum.PAID;
4892
4832
  }
4893
- get firstForecastYear() {
4894
- return this.items.reduce((min, property) => {
4895
- const current = property.firstForecastYear;
4896
- return min > current ? current : min;
4897
- }, new FinancialYear().year);
4833
+ isOverdue() {
4834
+ return this.isPending() && this.dateTo < new Date();
4898
4835
  }
4899
- get marketValueGrowth() {
4900
- return (this.marketValue - this.purchasePrice) / this.purchasePrice;
4836
+ isUnpaid() {
4837
+ return this.isPending() && this.dateTo >= new Date();
4901
4838
  }
4902
4839
  /**
4903
- * list of properties
4840
+ * GST is not available for invoices without taxes
4904
4841
  */
4905
- getCGTApplicable() {
4906
- return this.create(this.items.filter((property) => property.isCGTApplicable()));
4907
- }
4908
- getOwnerOccupiedProperties() {
4909
- return new PropertyCollection(this.items.filter((property) => property.category.isOwnerOccupied()));
4910
- }
4911
- get earliestContractDate() {
4912
- return this.items.reduce((min, property) => {
4913
- return min < property.contractDate ? min : property.contractDate;
4914
- }, new FinancialYear(new Date()).startDate);
4842
+ isNoTax() {
4843
+ return this.taxType === SoleInvoiceTaxTypeEnum.NO_TAX;
4915
4844
  }
4916
4845
  /**
4917
- * Get list of unique property categories from collection
4846
+ * GST amount is not included in items prices for invoices with tax exclusive,
4847
+ * we should add GST amount additionaly to subtotal price to get total price
4918
4848
  */
4919
- getCategories() {
4920
- return uniqBy(this.items.map((property) => property.category), 'id');
4849
+ isTaxExclusive() {
4850
+ return this.taxType === SoleInvoiceTaxTypeEnum.TAX_EXCLUSIVE;
4921
4851
  }
4922
4852
  /**
4923
- * Get property with the highest growth percent
4853
+ * GST amount already included in items prices for invoices with tax inclusive,
4854
+ * we should subtract GST amount from total price to get subtotal price
4924
4855
  */
4925
- getBestPerformanceGrowthProperty() {
4926
- return this.items.reduce((max, current) => {
4927
- return max.growthPercent < current.growthPercent ? current : max;
4928
- }, this.first);
4856
+ isTaxInclusive() {
4857
+ return this.taxType === SoleInvoiceTaxTypeEnum.TAX_INCLUSIVE;
4929
4858
  }
4930
- /**
4931
- * Get property with the lowest tax position
4932
- */
4933
- getBestPerformanceTaxProperty(transactions, depreciations) {
4934
- const transactionsByProperty = transactions.groupBy('property.id');
4935
- const depreciationsByProperty = depreciations.groupBy('property.id');
4936
- return this.items.reduce((min, current) => {
4937
- const minTaxPosition = min.getTaxPosition(transactionsByProperty.get(min.id), depreciationsByProperty.get(min.id));
4938
- const currentTaxPosition = current.getTaxPosition(transactionsByProperty.get(current.id), depreciationsByProperty.get(current.id));
4939
- return minTaxPosition > currentTaxPosition ? current : min;
4940
- }, this.first);
4859
+ getNumber() {
4860
+ return String(this.number).padStart(SoleInvoice.NUMBER_LENGTH, '0');
4941
4861
  }
4942
- /**
4943
- * Show best performance properties first
4944
- * https://taxtank.atlassian.net/wiki/spaces/TAXTANK/pages/217677997/Property+Tank+Dashboard
4945
- */
4946
- sortByBestPerformance(transactions, depreciations) {
4947
- const activeProperties = this.getActiveProperties();
4948
- // nothing to sort when no active properties
4949
- if (!activeProperties.length) {
4950
- return this;
4951
- }
4952
- const bestProperties = uniqBy(this.create([
4953
- activeProperties.getBestPerformanceGrowthProperty(),
4954
- activeProperties.getBestPerformanceTaxProperty(transactions, depreciations)
4955
- ]).toArray(), 'id');
4956
- const newItems = this.remove(bestProperties).toArray();
4957
- newItems.unshift(...bestProperties);
4958
- return this.create(newItems);
4862
+ get name() {
4863
+ return `invoice-${this.getNumber()}.pdf`;
4864
+ }
4865
+ getTransactionsIds() {
4866
+ return new Collection(this.items).mapBy('transaction.id');
4867
+ this.items.map((item) => item.transaction.id);
4959
4868
  }
4960
4869
  }
4870
+ SoleInvoice.NUMBER_LENGTH = 4;
4871
+ __decorate([
4872
+ Type(() => Date)
4873
+ ], SoleInvoice.prototype, "dateFrom", void 0);
4874
+ __decorate([
4875
+ Type(() => Date)
4876
+ ], SoleInvoice.prototype, "dateTo", void 0);
4877
+ __decorate([
4878
+ Type(() => User)
4879
+ ], SoleInvoice.prototype, "user", void 0);
4880
+ __decorate([
4881
+ Type(() => SoleBusiness)
4882
+ ], SoleInvoice.prototype, "business", void 0);
4883
+ __decorate([
4884
+ Type(() => SoleInvoiceItem)
4885
+ ], SoleInvoice.prototype, "items", void 0);
4886
+ __decorate([
4887
+ Type(() => SoleContact)
4888
+ ], SoleInvoice.prototype, "payer", void 0);
4889
+ __decorate([
4890
+ Type(() => SoleInvoiceTemplate)
4891
+ ], SoleInvoice.prototype, "template", void 0);
4892
+ __decorate([
4893
+ Type(() => BankAccount)
4894
+ ], SoleInvoice.prototype, "bankAccount", void 0);
4961
4895
 
4962
- class PropertyCategoryMovementCollection extends Collection {
4963
- /**
4964
- * @TODO TT-2355 Alex refactor propertyForecast, use separated api (then I can remove property from param)
4965
- */
4966
- getByForecast(property, forecast) {
4967
- const financialYear = new FinancialYear(forecast.financialYear);
4968
- return this.filterBy('property.id', property.id).filter((movement) => {
4969
- return movement.fromDate <= financialYear.endDate && !movement.toDate || movement.toDate >= financialYear.startDate;
4970
- });
4896
+ /**
4897
+ * Class contains traveled kilometers and work usage percent in 12 weeks date range
4898
+ * @TODO Vik: Best period: move this and related logic to backend
4899
+ * @TODO Alex: check if we need this class when calculation refactored with backend
4900
+ */
4901
+ class LogbookPeriod {
4902
+ isEndOfYear() {
4903
+ return this.to === new FinancialYear().endDate;
4971
4904
  }
4972
- hasCategory(categoryId) {
4973
- return !!this.findBy('propertyCategory.id', categoryId);
4905
+ getWorkUsageByClaim(claim) {
4906
+ const claimKilometers = this.logbooks.getByVehicleClaim(claim).getClaimableLogbooks().kilometers;
4907
+ return Math.round(this.workUsage * (claimKilometers / this.kilometers));
4974
4908
  }
4975
4909
  }
4910
+ __decorate([
4911
+ Type(() => Date)
4912
+ ], LogbookPeriod.prototype, "from", void 0);
4913
+ __decorate([
4914
+ Type(() => Date)
4915
+ ], LogbookPeriod.prototype, "to", void 0);
4976
4916
 
4977
- class VehicleClaimCollection extends Collection {
4978
- /**
4979
- * Get remaining kilometers limit. Total limit ({@link VehicleClaim.totalKmsLimit}) - claimed kilometers from other vehicle claims
4980
- */
4981
- getKmsLimitForClaim(claim) {
4982
- let collection = this;
4983
- if (claim) {
4984
- collection = collection.removeBy('id', claim.id);
4985
- }
4986
- return VehicleClaim.totalKmsLimit - collection.sumBy('kilometers');
4987
- }
4988
- /**
4989
- * Get remaining work usage limit. Total limit ({@link VehicleClaim.totalWorkUsagePercent}) - claimed percent from other vehicle claims
4990
- */
4991
- getWorkUsageLimitForClaim(claim) {
4992
- let collection = this;
4993
- if (claim) {
4994
- collection = collection.removeBy('id', claim.id);
4995
- }
4996
- return VehicleClaim.totalWorkUsagePercent - collection.sumBy('workUsage');
4997
- }
4917
+ class Vehicle$1 extends AbstractModel {
4998
4918
  }
4999
4919
 
5000
- class VehicleLogbookCollection extends Collection {
5001
- /**
5002
- * Best period may be calculated only when user has logbooks minimum for VehicleLogbook.bestPeriodWeeks
5003
- * @TODO Vik: Best period: move this and related logic to backend
5004
- */
5005
- isBestPeriodExist() {
5006
- if (this.items.length < 2) {
5007
- return false;
5008
- }
5009
- return VehicleLogbook.bestPeriodDuration < (this.last.date.getTime() - this.first.date.getTime());
5010
- }
5011
- /**
5012
- * Get collection of non-personal logbooks (work-related, sole-related).
5013
- * @TODO Vik: Best period: move this and related logic to backend
5014
- */
5015
- getClaimableLogbooks() {
5016
- return this.filterBy('isPersonal', false);
4920
+ class VehicleLogbook$1 extends AbstractModel {
4921
+ }
4922
+
4923
+ // problem with DateRange and typescript. See more https://github.com/rotaready/moment-range/issues/263
4924
+ const moment = extendMoment(moment$1);
4925
+ class VehicleLogbook extends VehicleLogbook$1 {
4926
+ get kilometers() {
4927
+ return this.odometerEnd - this.odometerStart;
5017
4928
  }
5018
- /**
5019
- * Get Logbook Period with the biggest work usage percent
5020
- * Best period duration is defined as VehicleLogbook.bestPeriodWeeks by the ATO
5021
- * @TODO Vik: Best period: move this and related logic to backend
5022
- */
5023
- getBestPeriod() {
5024
- if (!this.isBestPeriodExist()) {
5025
- return null;
5026
- }
5027
- let bestPeriod;
5028
- // get list of all logbooks available for best period calculation
5029
- const claimableLogbooks = this.getClaimableLogbooks().toArray();
5030
- for (let i = 0; i < claimableLogbooks.length; i++) {
5031
- // no sense to check next logbooks because we already get the end of the year
5032
- if (bestPeriod && bestPeriod.isEndOfYear()) {
5033
- break;
5034
- }
5035
- // get date range started from current handling logbook date
5036
- const dateRange = claimableLogbooks[i].getPeriod();
5037
- // get all logbooks included in current logbook period
5038
- const logbooksInRange = this.filterByRange('date', dateRange.start, dateRange.end);
5039
- const currentPeriod = plainToClass(LogbookPeriod, {
5040
- from: dateRange.start,
5041
- to: dateRange.end,
5042
- kilometers: logbooksInRange.getClaimableLogbooks().kilometers,
5043
- workUsage: logbooksInRange.getWorkUsage(),
5044
- logbooks: logbooksInRange
5045
- });
5046
- // compare with previous best period and overwrite if needs
5047
- if (!bestPeriod || currentPeriod.workUsage > bestPeriod.workUsage) {
5048
- bestPeriod = currentPeriod;
5049
- }
5050
- else if (currentPeriod.workUsage === bestPeriod.workUsage) {
5051
- // if work usage is the same then get period with the biggest work usage for work tank
5052
- const oldWorkUsage = bestPeriod.logbooks.filterBy('tankType', TankTypeEnum.WORK).getWorkUsage();
5053
- const currentWorkUsage = currentPeriod.logbooks.filterBy('tankType', TankTypeEnum.WORK).getWorkUsage();
5054
- if (oldWorkUsage < currentWorkUsage) {
5055
- bestPeriod = currentPeriod;
5056
- }
5057
- }
5058
- }
5059
- return bestPeriod;
4929
+ get tankType() {
4930
+ return this.isSoleTank() ? TankTypeEnum.SOLE : TankTypeEnum.WORK;
5060
4931
  }
5061
- /**
5062
- * Calculate total kilometers traveled
5063
- */
5064
- get kilometers() {
5065
- return this.sumBy('kilometers');
4932
+ isWorkTank() {
4933
+ return !this.business;
5066
4934
  }
5067
- /**
5068
- * Calculate work usage (percent of business-related kilometers from total kilometers)
5069
- * @TODO Alex: TT-2089 replace with getter
5070
- */
5071
- getWorkUsage() {
5072
- const workKilometers = this.getClaimableLogbooks().kilometers;
5073
- return Math.round(workKilometers / this.kilometers * 100);
4935
+ isSoleTank() {
4936
+ return !!this.business;
5074
4937
  }
5075
4938
  /**
5076
- * Get list of logbooks related to passed vehicle claim
4939
+ * Get logbook period date range from logbook date and weeksInPeriod duration
5077
4940
  */
5078
- getByVehicleClaim(vehicleClaim) {
5079
- return vehicleClaim.isSoleTank()
5080
- // sole tank may have multiple vehicle claims, so we need to filter by business.id
5081
- ? this.filterBy('business.id', vehicleClaim.business.id)
5082
- // work tank may have only one vehicle claim, so we need to filter by tank type
5083
- : this.filterBy('tankType', TankTypeEnum.WORK);
4941
+ getPeriod() {
4942
+ return moment.rangeFromInterval('milliseconds', VehicleLogbook.bestPeriodDuration, this.date);
5084
4943
  }
5085
4944
  }
5086
-
5087
4945
  /**
5088
- * List of objects grouped by passed property
4946
+ * Logbook period duration in milliseconds.
4947
+ * https://taxtank.atlassian.net/wiki/spaces/TAXTANK/pages/211517441/Logbook+Vehicle
5089
4948
  */
5090
- class Dictionary {
5091
- constructor(items, path = 'id') {
5092
- this.items = {};
5093
- if (!items.length) {
5094
- return;
5095
- }
5096
- // Do nothing if provided path was not found in the 1st item
5097
- if (!hasIn(items[0], path.split('.')[0])) {
5098
- return;
5099
- }
5100
- this.groupItems(items, path);
5101
- }
5102
- add(key, value) {
5103
- this.items[key] = value;
4949
+ VehicleLogbook.bestPeriodDuration = 12 * 7 * 24 * 3600 * 1000;
4950
+ __decorate([
4951
+ Type(() => Date)
4952
+ ], VehicleLogbook.prototype, "date", void 0);
4953
+ __decorate([
4954
+ Type(() => SoleBusiness)
4955
+ ], VehicleLogbook.prototype, "business", void 0);
4956
+
4957
+ class Vehicle extends Vehicle$1 {
4958
+ }
4959
+ __decorate([
4960
+ Type(() => VehicleLogbook)
4961
+ ], Vehicle.prototype, "logbook", void 0);
4962
+
4963
+ class VehicleClaim$1 extends AbstractModel {
4964
+ }
4965
+
4966
+ class VehicleClaimDetails$1 extends AbstractModel {
4967
+ }
4968
+
4969
+ var VehicleClaimDetailsMethodEnum;
4970
+ (function (VehicleClaimDetailsMethodEnum) {
4971
+ VehicleClaimDetailsMethodEnum[VehicleClaimDetailsMethodEnum["KMS"] = 1] = "KMS";
4972
+ VehicleClaimDetailsMethodEnum[VehicleClaimDetailsMethodEnum["LOGBOOK"] = 2] = "LOGBOOK";
4973
+ })(VehicleClaimDetailsMethodEnum || (VehicleClaimDetailsMethodEnum = {}));
4974
+
4975
+ class VehicleClaimDetails extends VehicleClaimDetails$1 {
4976
+ constructor() {
4977
+ super(...arguments);
4978
+ /**
4979
+ * Init default values for the new instances
4980
+ */
4981
+ this.isManual = true;
4982
+ this.method = VehicleClaimDetailsMethodEnum.KMS;
4983
+ this.financialYear = new FinancialYear().year;
5104
4984
  }
5105
- get(key) {
5106
- return this.items[key] !== undefined ? this.items[key] : null;
4985
+ isLogbookMethod() {
4986
+ return this.method === VehicleClaimDetailsMethodEnum.LOGBOOK;
5107
4987
  }
5108
- groupItems(items, path) {
5109
- items.forEach((item) => {
5110
- let key = get(item, path);
5111
- // if object does not have property for grouping it will be grouped as 'other'
5112
- if (key === undefined) {
5113
- key = 'other';
5114
- }
5115
- this.items[key] = item;
5116
- });
4988
+ isKmsMethod() {
4989
+ return this.method === VehicleClaimDetailsMethodEnum.KMS;
5117
4990
  }
5118
4991
  }
4992
+ __decorate([
4993
+ Type(() => User)
4994
+ ], VehicleClaimDetails.prototype, "user", void 0);
5119
4995
 
5120
- class SoleBusinessLossesCollection extends Collection {
4996
+ class VehicleClaim extends VehicleClaim$1 {
4997
+ constructor() {
4998
+ super(...arguments);
4999
+ this.kilometers = 0;
5000
+ this.workUsage = 0;
5001
+ }
5002
+ isWorkTank() {
5003
+ return !this.business;
5004
+ }
5005
+ isSoleTank() {
5006
+ return !!this.business;
5007
+ }
5008
+ get tankType() {
5009
+ return this.isSoleTank() ? TankTypeEnum.SOLE : TankTypeEnum.WORK;
5010
+ }
5121
5011
  /**
5122
- * Business loss applied in current year, includes previous year losses and current year losses for businesses matching offset rule
5012
+ * Claim amount for KMs method. Exists only for KMs method.
5123
5013
  */
5124
- calculateBusinessLossApplied(transactions) {
5125
- // claim amounts for businesses that can be applied to be reduced by prior year business losses
5126
- const claimAmountsByBusinessId = this.getClaimAmountsByBusinessId(transactions);
5127
- return Object.keys(claimAmountsByBusinessId.items).reduce((sum, businessId) => {
5128
- const loss = this.findBy('business.id', +businessId);
5129
- const lossOpenBalance = (loss === null || loss === void 0 ? void 0 : loss.openBalance) || 0;
5130
- // business loss can be applied to business profit or other income types profit in case in offset rule met
5131
- return sum + ((loss === null || loss === void 0 ? void 0 : loss.offsetRule) ? lossOpenBalance : Math.min(lossOpenBalance, claimAmountsByBusinessId.get(businessId)));
5132
- }, 0);
5014
+ getKMSClaimAmount(vehicleClaimRate) {
5015
+ return +(this.kilometers * vehicleClaimRate).toFixed(2);
5133
5016
  }
5134
5017
  /**
5135
- * Get business claim amounts that can be applied to be reduced by prior year business losses:
5136
- * businesses with income or businesses with a loss, but which met the non-commercial loss rules
5137
- * https://www.ato.gov.au/Business/Non-commercial-losses/
5018
+ * Get logbook claim amount. Exists only for logbook method.
5019
+ * ClaimAmount = WorkUsage * transaction/depreciation amount
5138
5020
  */
5139
- getClaimAmountsByBusinessId(transactions) {
5140
- const claimAmountsByBusinessId = new Dictionary([]);
5141
- const transactionsByBusinessId = new CollectionDictionary(transactions, 'business.id');
5142
- transactionsByBusinessId.keys.forEach((businessId) => {
5143
- var _a;
5144
- // business loss may not exist if, when creating a business, user didn't add losses for previous years
5145
- const lossOffsetRule = (_a = this.findBy('business.id', +businessId)) === null || _a === void 0 ? void 0 : _a.offsetRule;
5146
- const businessClaimAmount = transactionsByBusinessId
5147
- .get(businessId)
5148
- .getClaimAmountByBusinessId(+businessId);
5149
- // no way to apply loss for business without profit if offset rules not met
5150
- if (businessClaimAmount < 0 && !lossOffsetRule) {
5151
- return;
5152
- }
5153
- claimAmountsByBusinessId.add(businessId, businessClaimAmount);
5154
- });
5155
- return claimAmountsByBusinessId;
5156
- }
5157
- }
5158
-
5159
- class SoleInvoiceCollection extends Collection {
5160
- getOverdue() {
5161
- return this.filter((invoice) => invoice.isOverdue());
5162
- }
5163
- getUnpaid() {
5164
- return this.filter((invoice) => invoice.isUnpaid());
5165
- }
5166
- getPaid() {
5167
- return this.filter((invoice) => invoice.isPaid());
5021
+ getLogbookClaimAmount(transactions) {
5022
+ const transactionsAmount = transactions
5023
+ .getByVehicleClaim(this)
5024
+ .getLogbookTransactions()
5025
+ .sumBy('amount');
5026
+ // Math.abs because amount will be negative (because we sum expenses), but we don't want negative percent value
5027
+ return Math.abs(transactionsAmount) * this.workUsage / 100;
5168
5028
  }
5169
- getPending() {
5170
- return this.filter((invoice) => invoice.isPending());
5029
+ getAverageWeeklyKMS() {
5030
+ return this.kilometers / FinancialYear.weeksInYear;
5171
5031
  }
5172
- getTransactionsIds() {
5173
- return flatten(this.items.map((invoice) => invoice.getTransactionsIds()));
5032
+ static getKMSChartAccountsIdByTankType(tankType) {
5033
+ return tankType === TankTypeEnum.WORK
5034
+ ? ChartAccountsListEnum.KLMS_TRAVELLED_FOR_WORK
5035
+ : ChartAccountsListEnum.KLMS_TRAVELLED;
5174
5036
  }
5175
5037
  }
5176
-
5177
5038
  /**
5178
- * Chart serie class: chart data item
5179
- * @TODO consider rename to ChartSerieData
5039
+ * limit for kms claim method
5180
5040
  */
5181
- class ChartSerie {
5041
+ VehicleClaim.totalKmsLimit = 5000;
5042
+ /**
5043
+ * limit for work usage claim method
5044
+ */
5045
+ VehicleClaim.totalWorkUsagePercent = 100;
5046
+ __decorate([
5047
+ Type(() => VehicleClaimDetails)
5048
+ ], VehicleClaim.prototype, "details", void 0);
5049
+ __decorate([
5050
+ Type(() => SoleBusiness)
5051
+ ], VehicleClaim.prototype, "business", void 0);
5052
+
5053
+ class SoleBusinessActivity$1 extends AbstractModel {
5182
5054
  }
5183
5055
 
5056
+ class SoleBusiness extends SoleBusiness$1 {
5057
+ getPhotoPlaceholder() {
5058
+ return `${this.name[0]}${this.name[1]}`;
5059
+ }
5060
+ getPhoto() {
5061
+ return this.logo;
5062
+ }
5063
+ }
5184
5064
  /**
5185
- * Chart data class
5186
- * @TODO consider rename to ChartSerie
5065
+ * Maximum number of businesses that a person can have, according to the ATO
5187
5066
  */
5188
- class ChartData {
5067
+ SoleBusiness.businessesLimit = 6;
5068
+ __decorate([
5069
+ Type(() => User)
5070
+ ], SoleBusiness.prototype, "user", void 0);
5071
+ __decorate([
5072
+ Type(() => SoleBusinessAllocation)
5073
+ ], SoleBusiness.prototype, "allocations", void 0);
5074
+ __decorate([
5075
+ Type(() => SoleBusinessLoss)
5076
+ ], SoleBusiness.prototype, "losses", void 0);
5077
+ __decorate([
5078
+ Type(() => SoleInvoice)
5079
+ ], SoleBusiness.prototype, "invoices", void 0);
5080
+ __decorate([
5081
+ Type(() => SoleInvoiceTemplate)
5082
+ ], SoleBusiness.prototype, "invoiceTemplates", void 0);
5083
+ __decorate([
5084
+ Type(() => VehicleClaim)
5085
+ ], SoleBusiness.prototype, "vehicleClaims", void 0);
5086
+ __decorate([
5087
+ Type(() => Transaction)
5088
+ ], SoleBusiness.prototype, "transactions", void 0);
5089
+ __decorate([
5090
+ Type(() => Depreciation)
5091
+ ], SoleBusiness.prototype, "depreciations", void 0);
5092
+ __decorate([
5093
+ Type(() => SoleBusinessActivity$1)
5094
+ ], SoleBusiness.prototype, "activity", void 0);
5095
+ __decorate([
5096
+ Type(() => IncomeSource)
5097
+ ], SoleBusiness.prototype, "incomeSource", void 0);
5098
+
5099
+ class SoleBusinessActivity extends SoleBusinessActivity$1 {
5100
+ }
5101
+
5102
+ class SoleDepreciationMethod$1 extends AbstractModel {
5103
+ }
5104
+
5105
+ var SoleDepreciationMethodEnum;
5106
+ (function (SoleDepreciationMethodEnum) {
5107
+ SoleDepreciationMethodEnum[SoleDepreciationMethodEnum["SBP"] = 1] = "SBP";
5108
+ SoleDepreciationMethodEnum[SoleDepreciationMethodEnum["DEPRECIATION"] = 2] = "DEPRECIATION";
5109
+ })(SoleDepreciationMethodEnum || (SoleDepreciationMethodEnum = {}));
5110
+
5111
+ class SoleDepreciationMethod extends SoleDepreciationMethod$1 {
5112
+ isSBP() {
5113
+ return this.method === SoleDepreciationMethodEnum.SBP;
5114
+ }
5115
+ }
5116
+
5117
+ class SoleDetails$1 extends AbstractModel {
5118
+ }
5119
+
5120
+ class SoleDetails extends SoleDetails$1 {
5189
5121
  }
5190
5122
  __decorate([
5191
- Type(() => ChartSerie)
5192
- ], ChartData.prototype, "data", void 0);
5123
+ Type(() => User)
5124
+ ], SoleDetails.prototype, "user", void 0);
5193
5125
 
5194
- const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Jan'];
5126
+ class BasReport$1 extends AbstractModel {
5127
+ }
5195
5128
 
5196
- /**
5197
- * @TODO extend from TransactionBaseCollection
5198
- * Collection of transactions
5199
- */
5200
- class TransactionCollection extends ExportableCollection {
5201
- /**
5202
- * @TODO use TransactionBaseCollection instead
5203
- * we use depreciations as expense transactions a lot
5204
- */
5205
- constructor(transactions = [], depreciations = []) {
5206
- super([...transactions, ...depreciations.map((depreciation) => depreciation.toTransaction())]);
5129
+ class BasReport extends BasReport$1 {
5130
+ get taxWithheldTotal() {
5131
+ return this.taxWithheldSalary + this.taxWithheldNoABN;
5207
5132
  }
5208
- getSoleTransactions() {
5209
- return this.filter((transaction) => transaction.isSoleTank());
5133
+ get paygTaxInstalmentOwedToATO() {
5134
+ return this.paygTaxInstalment > 0 ? this.paygTaxInstalment : 0;
5210
5135
  }
5211
- get amount() {
5212
- return this.sumBy('amount');
5136
+ get paygTaxInstalmentOwedByATO() {
5137
+ return this.paygTaxInstalment < 0 ? Math.abs(this.paygTaxInstalment) : 0;
5213
5138
  }
5214
- /**
5215
- * Difference between allocated amount and total amount
5216
- */
5217
- getUnallocatedAmount(allocations) {
5218
- return this.items.reduce((sum, transaction) => {
5219
- return sum + transaction.getUnallocatedAmount(allocations.filterBy('transaction.id', transaction.id));
5220
- }, 0);
5139
+ get fuelTaxCreditOwedToATO() {
5140
+ return this.fuelTaxCredit < 0 ? this.fuelTaxCredit : 0;
5141
+ }
5142
+ get fuelTaxCreditOwedByATO() {
5143
+ return this.fuelTaxCredit > 0 ? Math.abs(this.fuelTaxCredit) : 0;
5144
+ }
5145
+ get owesToATO() {
5146
+ return this.incomeGST + this.taxWithheldTotal + this.paygTaxInstalmentOwedToATO + this.fuelTaxCreditOwedToATO;
5147
+ }
5148
+ get owedByATO() {
5149
+ return this.expenseGST + this.paygTaxInstalmentOwedByATO + this.fuelTaxCreditOwedByATO;
5221
5150
  }
5222
5151
  /**
5223
- * get date of the last transaction
5152
+ * GST payable to the ATO, or refundable from the ATO in case it's negative
5224
5153
  */
5225
- getLastTransactionDate() {
5226
- return new Date(Math.max.apply(Math, this.items.map((transaction) => transaction.date)));
5227
- }
5228
- get claimAmount() {
5229
- return this.items.reduce((sum, transaction) => sum + transaction.claimAmount, 0);
5154
+ get gst() {
5155
+ return this.incomeGST + this.expenseGST + this.taxWithheldTotal + this.paygTaxInstalment - this.fuelTaxCredit;
5230
5156
  }
5231
- get grossClaimAmount() {
5232
- return this.items.reduce((sum, transaction) => sum + transaction.grossClaimAmount, 0);
5157
+ }
5158
+ __decorate([
5159
+ Type(() => Date)
5160
+ ], BasReport.prototype, "dateFrom", void 0);
5161
+ __decorate([
5162
+ Type(() => Date)
5163
+ ], BasReport.prototype, "dateTo", void 0);
5164
+
5165
+ /**
5166
+ * role hierarchy
5167
+ */
5168
+ const USER_ROLES = {
5169
+ ROLE_FIRM_OWNER: [UserRolesEnum.FIRM_OWNER, UserRolesEnum.FIRM_MANAGER, UserRolesEnum.ACCOUNTANT, UserRolesEnum.ADVISOR],
5170
+ ROLE_FIRM_MANAGER: [UserRolesEnum.FIRM_MANAGER, UserRolesEnum.ACCOUNTANT, UserRolesEnum.ADVISOR],
5171
+ ROLE_EMPLOYEE: [UserRolesEnum.ACCOUNTANT, UserRolesEnum.ADVISOR],
5172
+ ROLE_ACCOUNTANT: [UserRolesEnum.ACCOUNTANT],
5173
+ ROLE_ADVISOR: [UserRolesEnum.ADVISOR],
5174
+ ROLE_CLIENT: [UserRolesEnum.CLIENT],
5175
+ ROLE_USER_SUBSCRIPTION: [UserRolesEnum.SUBSCRIPTION],
5176
+ ROLE_USER_WORK: [UserRolesEnum.WORK_TANK],
5177
+ ROLE_USER_PROPERTY: [UserRolesEnum.PROPERTY_TANK],
5178
+ ROLE_USER_SOLE: [UserRolesEnum.SOLE_TANK],
5179
+ };
5180
+
5181
+ class User extends User$1 {
5182
+ get fullName() {
5183
+ return `${this.firstName} ${this.lastName}`;
5233
5184
  }
5234
- get grossAmount() {
5235
- return this.items.reduce((sum, transaction) => sum + transaction.grossAmount, 0);
5185
+ get activeSubscription() {
5186
+ return this.subscriptions.find((ss) => ss.isActive);
5236
5187
  }
5237
- getByChartAccountsCategories(categories) {
5238
- return new TransactionCollection(this.items.filter((transaction) => categories.includes(transaction.chartAccounts.category)));
5188
+ get lastSubscription() {
5189
+ return this.subscriptions[this.subscriptions.length - 1];
5239
5190
  }
5240
5191
  /**
5241
- * Get transactions by month
5242
- * @param monthIndex by which desired month should be taken
5192
+ * @TODO refactor
5193
+ *
5243
5194
  */
5244
- getByMonth(monthIndex) {
5245
- return new TransactionCollection(this.items.filter((transaction) => transaction.date.getMonth() === monthIndex));
5195
+ get position() {
5196
+ switch (true) {
5197
+ case this.isFirmOwner():
5198
+ return USER_WORK_POSITION[UserRolesEnum.FIRM_OWNER];
5199
+ case this.isManager():
5200
+ return USER_WORK_POSITION[UserRolesEnum.FIRM_MANAGER];
5201
+ case this.isEmployee():
5202
+ return USER_WORK_POSITION[UserRolesEnum.EMPLOYEE];
5203
+ default:
5204
+ return '';
5205
+ }
5246
5206
  }
5247
5207
  /**
5248
- * Get collection of transactions metadata
5208
+ * search roles including hierarchy
5249
5209
  */
5250
- getTransactionsMetadata() {
5251
- const metadataArray = [];
5252
- this.items.forEach((transaction) => {
5253
- metadataArray.push(...transaction.metadata);
5254
- });
5255
- return new Collection(metadataArray);
5256
- }
5257
- getIncomeTransactions() {
5258
- return new TransactionCollection(this.items.filter((transaction) => transaction.isIncome()));
5210
+ hasRoles(roles) {
5211
+ roles = roles instanceof Array ? roles : [roles];
5212
+ if (!roles.length) {
5213
+ return true;
5214
+ }
5215
+ roles = flatten(roles.map((role) => USER_ROLES[role]));
5216
+ return !!intersection(this.roles, roles).length;
5259
5217
  }
5260
- getExpenseTransactions() {
5261
- return new TransactionCollection(this.items.filter((transaction) => transaction.isExpense() && !transaction.isInterest()));
5218
+ isOnboarding() {
5219
+ return this.status === UserStatusEnum.ON_BOARDING;
5262
5220
  }
5263
- get claimIncome() {
5264
- return this.getIncomeTransactions().claimAmount;
5221
+ hasSubscription() {
5222
+ return this.roles.includes(UserRolesEnum.SUBSCRIPTION);
5265
5223
  }
5266
- get claimExpense() {
5267
- return this.getExpenseTransactions().claimAmount;
5224
+ isLoggedIn() {
5225
+ return this.id === +localStorage.getItem('userId');
5268
5226
  }
5269
- getInterestTransactions() {
5270
- return new TransactionCollection(this.items.filter((transaction) => transaction.isInterest()));
5227
+ isClient() {
5228
+ return this.roles.includes(UserRolesEnum.CLIENT);
5271
5229
  }
5272
- get claimInterest() {
5273
- return this.getInterestTransactions().claimAmount;
5230
+ isEmployee() {
5231
+ return this.isAccountant() || this.isAdvisor();
5274
5232
  }
5275
- /**
5276
- * Get collection of transactions and properties filtered by properties ids
5277
- * @param ids Ids of properties for filter
5278
- */
5279
- getByPropertiesIds(ids) {
5280
- return new TransactionCollection(this.items.filter((transaction) => {
5281
- var _a;
5282
- return ids.includes((_a = transaction.property) === null || _a === void 0 ? void 0 : _a.id);
5283
- }));
5233
+ isAccountant() {
5234
+ return this.roles.includes(UserRolesEnum.ACCOUNTANT);
5284
5235
  }
5285
- /**
5286
- * Get new collection filtered by income source id
5287
- * @param id id of income source for filter
5288
- */
5289
- getByIncomeSourceId(id) {
5290
- return new TransactionCollection(this.items.filter((transaction) => { var _a; return ((_a = transaction.incomeSource) === null || _a === void 0 ? void 0 : _a.id) === id; }));
5236
+ isAdvisor() {
5237
+ return this.roles.includes(UserRolesEnum.ADVISOR);
5291
5238
  }
5292
- /**
5293
- * Get new collection filtered by chart accounts category
5294
- * @param category Chart accounts category value
5295
- */
5296
- getByChartAccountsCategory(category) {
5297
- return new TransactionCollection(this.items.filter((transaction) => transaction.chartAccounts.category === category));
5239
+ isImpersonator() {
5240
+ return this.roles.includes(UserRolesEnum.SWITCH_USER);
5298
5241
  }
5299
5242
  /**
5300
- * Get new collection of property transactions
5243
+ * Check if current user is firm owner
5301
5244
  */
5302
- getPropertyTransactions() {
5303
- return new TransactionCollection(this.items.filter((transaction) => transaction.isPropertyTank()));
5304
- }
5305
- getDebitTransactions() {
5306
- return new TransactionCollection(this.items.filter((transaction) => transaction.isDebit()));
5307
- }
5308
- getCreditTransactions() {
5309
- return new TransactionCollection(this.items.filter((transaction) => transaction.isCredit()));
5310
- }
5311
- getByAllocations(allocations) {
5312
- return new TransactionCollection(this.items.filter((transaction) => allocations.hasTransaction(transaction)));
5245
+ isFirmOwner() {
5246
+ return this.roles.includes(UserRolesEnum.FIRM_OWNER);
5313
5247
  }
5314
5248
  /**
5315
- * Get transactions related to Vehicle category
5249
+ * Check if current user is firm manager
5316
5250
  */
5317
- getVehicleTransactions() {
5318
- return this.create(this.items.filter((transaction) => {
5319
- return transaction.isVehicleTransaction();
5320
- }));
5251
+ isManager() {
5252
+ return this.roles.includes(UserRolesEnum.FIRM_MANAGER) || this.roles.includes(UserRolesEnum.FIRM_OWNER);
5321
5253
  }
5322
5254
  /**
5323
- * Get new transaction collection filtered by tank type
5255
+ * check if user has property tank access
5324
5256
  */
5325
- getByTankType(tankType) {
5326
- return this.create(this.items.filter((transaction) => {
5327
- switch (tankType) {
5328
- case TankTypeEnum.PROPERTY:
5329
- return transaction.isPropertyTank();
5330
- case TankTypeEnum.WORK:
5331
- return transaction.isWorkTank();
5332
- case TankTypeEnum.SOLE:
5333
- return transaction.isSoleTank();
5334
- // Transaction may be not related to any tank type (personal)
5335
- default:
5336
- return false;
5337
- }
5338
- }));
5339
- }
5340
- getExportHeader() {
5341
- return ['Date', 'Description', 'Debit', 'Credit'];
5342
- }
5343
- getExportFooter() {
5344
- return [
5345
- plainToClass(ExportCell, { value: 'Total', type: ExportCellTypeEnum.STRING }),
5346
- plainToClass(ExportCell, { value: '', type: ExportCellTypeEnum.STRING }),
5347
- plainToClass(ExportCell, { value: this.sumBy('debit'), type: ExportCellTypeEnum.CURRENCY }),
5348
- plainToClass(ExportCell, { value: this.sumBy('credit'), type: ExportCellTypeEnum.CURRENCY })
5349
- ];
5350
- }
5351
- getExportBody() {
5352
- return this.items.map((transaction) => {
5353
- return [
5354
- plainToClass(ExportCell, { value: transaction.date, type: ExportCellTypeEnum.DATE }),
5355
- plainToClass(ExportCell, { value: transaction.description, type: ExportCellTypeEnum.STRING }),
5356
- plainToClass(ExportCell, { value: transaction.debit, type: ExportCellTypeEnum.CURRENCY }),
5357
- plainToClass(ExportCell, { value: transaction.credit, type: ExportCellTypeEnum.CURRENCY })
5358
- ];
5359
- });
5257
+ hasPropertyTank() {
5258
+ return this.roles ?
5259
+ this.roles.includes(UserRolesEnum.PROPERTY_TANK) :
5260
+ !!this.getSubscriptionRole(UserRolesEnum.PROPERTY_TANK);
5360
5261
  }
5361
5262
  /**
5362
- * Get list of vehicle transactions filtered by vehicle claim
5263
+ * check if user has property tank access
5363
5264
  */
5364
- getByVehicleClaim(vehicleClaim) {
5365
- if (!vehicleClaim) {
5366
- return this.create([]);
5367
- }
5368
- return vehicleClaim.isSoleTank()
5369
- // sole tank may have multiple vehicle claims, so we need to filter by business.id
5370
- ? this.getVehicleTransactions().filterBy('business.id', vehicleClaim.business.id)
5371
- // work tank may have only one vehicle claim, so we need to filter by tank type
5372
- : this.getVehicleTransactions().filterBy('tankType', TankTypeEnum.WORK);
5265
+ hasWorkTank() {
5266
+ return this.roles ?
5267
+ this.roles.includes(UserRolesEnum.WORK_TANK) :
5268
+ !!this.getSubscriptionRole(UserRolesEnum.WORK_TANK);
5373
5269
  }
5374
5270
  /**
5375
- * Get list of vehicle transactions except KMS transactions
5271
+ * Get user subscription role by provided role type
5272
+ * @param roleType by which role should be returned
5376
5273
  */
5377
- getLogbookTransactions() {
5378
- return this
5379
- .getVehicleTransactions()
5380
- .removeBy('chartAccounts.id', [ChartAccountsListEnum.KLMS_TRAVELLED_FOR_WORK, ChartAccountsListEnum.KLMS_TRAVELLED]);
5274
+ getSubscriptionRole(roleType) {
5275
+ var _a;
5276
+ /**
5277
+ * Add @TODO for Alex to check if we should use getLastSubscription here
5278
+ */
5279
+ return (_a = this.activeSubscription) === null || _a === void 0 ? void 0 : _a.items.find((subscriptionItem) => subscriptionItem.price.product.role.includes(roleType));
5381
5280
  }
5382
5281
  /**
5383
- * Build chart data with transactions cash position.
5384
- * Cash position = Income - Expenses (include depreciations)
5385
- * Chart data for each month from fin year start till current month
5282
+ * Get user's photo link
5386
5283
  */
5387
- getCashPositionChartData() {
5388
- const chartData = [
5389
- plainToClass(ChartData, { name: 'Income', data: [] }),
5390
- plainToClass(ChartData, { name: 'Expense', data: [] })
5391
- ];
5392
- new FinancialYear().getPastMonths().forEach((month) => {
5393
- chartData[0].data.push({
5394
- label: MONTHS[month],
5395
- value: this.getIncomeTransactions().getByMonth(month).claimAmount
5396
- });
5397
- chartData[1].data.push({
5398
- label: MONTHS[month],
5399
- value: Math.abs(this.getExpenseTransactions().getByMonth(month).claimAmount)
5400
- });
5401
- });
5402
- return chartData;
5284
+ getPhoto() {
5285
+ return this.photo;
5403
5286
  }
5404
5287
  /**
5405
- * user pays GST only from allocated part (or paid) of income
5288
+ * get user's initials
5406
5289
  */
5407
- calculateAllocatedClaimAmount(allocations) {
5408
- let allocatedClaimAmount = 0;
5409
- this.filterBy('isGST', true).toArray().forEach((transaction) => {
5410
- allocatedClaimAmount += transaction.getAllocatedClaimAmount(allocations);
5411
- });
5412
- return allocatedClaimAmount;
5290
+ getPhotoPlaceholder() {
5291
+ return `${this.firstName[0].toUpperCase()}${this.lastName[0].toUpperCase()}`;
5413
5292
  }
5414
- calculateAllocatedGST(allocations) {
5415
- return this.calculateAllocatedClaimAmount(allocations) * ChartAccounts.GSTRatio;
5293
+ }
5294
+ __decorate([
5295
+ Type(() => ServiceSubscription)
5296
+ ], User.prototype, "subscriptions", void 0);
5297
+ __decorate([
5298
+ Type(() => ClientDetails)
5299
+ ], User.prototype, "clientDetails", void 0);
5300
+ __decorate([
5301
+ Type(() => EmployeeDetails)
5302
+ ], User.prototype, "employeeDetails", void 0);
5303
+ __decorate([
5304
+ Type(() => SoleDetails)
5305
+ ], User.prototype, "soleDetails", void 0);
5306
+ __decorate([
5307
+ Type(() => Address)
5308
+ ], User.prototype, "address", void 0);
5309
+ __decorate([
5310
+ Type(() => Phone)
5311
+ ], User.prototype, "phone", void 0);
5312
+ __decorate([
5313
+ Type(() => Date)
5314
+ ], User.prototype, "createdAt", void 0);
5315
+ __decorate([
5316
+ Type(() => User)
5317
+ ], User.prototype, "clients", void 0);
5318
+
5319
+ class PropertySubscription extends PropertySubscription$1 {
5320
+ }
5321
+ __decorate([
5322
+ Type(() => Date)
5323
+ ], PropertySubscription.prototype, "createdAt", void 0);
5324
+ __decorate([
5325
+ Type(() => User)
5326
+ ], PropertySubscription.prototype, "user", void 0);
5327
+
5328
+ class PropertyCategory$1 extends AbstractModel {
5329
+ }
5330
+
5331
+ var PropertyCategoryListEnum;
5332
+ (function (PropertyCategoryListEnum) {
5333
+ PropertyCategoryListEnum[PropertyCategoryListEnum["OWNER_OCCUPIED"] = 3] = "OWNER_OCCUPIED";
5334
+ PropertyCategoryListEnum[PropertyCategoryListEnum["SHARED"] = 4] = "SHARED";
5335
+ PropertyCategoryListEnum[PropertyCategoryListEnum["VACANT_LAND"] = 5] = "VACANT_LAND";
5336
+ })(PropertyCategoryListEnum || (PropertyCategoryListEnum = {}));
5337
+
5338
+ class PropertyCategory extends PropertyCategory$1 {
5339
+ // @Todo check if category is Owner Occupied. If will be needed to check more categories - move the checking to the backend
5340
+ isOwnerOccupied() {
5341
+ return this.id === PropertyCategoryListEnum.OWNER_OCCUPIED;
5416
5342
  }
5417
- getAllocatedAmount(allocations) {
5418
- return allocations.getByTransactionsIds(this.getIds()).sumBy('amount');
5343
+ isVacantLand() {
5344
+ return this.id === PropertyCategoryListEnum.VACANT_LAND;
5345
+ }
5346
+ isShared() {
5347
+ return this.id === PropertyCategoryListEnum.SHARED;
5419
5348
  }
5420
5349
  }
5421
5350
 
5422
- class TransactionAllocationCollection extends Collection {
5423
- get amount() {
5424
- return this.sumBy('amount');
5351
+ class PropertyValuation$1 extends AbstractModel {
5352
+ }
5353
+
5354
+ class PropertyDocument$1 extends AbstractModel {
5355
+ getApiUrlPrefix() {
5356
+ return '';
5425
5357
  }
5426
- getByTransactionsIds(ids) {
5427
- return new TransactionAllocationCollection(this.items.filter((allocation) => ids.includes(allocation.transaction.id)));
5358
+ getEntity() {
5359
+ return undefined;
5428
5360
  }
5429
- getByBankTransactionsIds(ids) {
5430
- return new TransactionAllocationCollection(this.items.filter((allocation) => ids.includes(allocation.bankTransaction.id)));
5361
+ }
5362
+
5363
+ class PropertyDocument extends PropertyDocument$1 {
5364
+ constructor() {
5365
+ super(...arguments);
5366
+ this.type = AssetTypeEnum.DOCUMENT;
5367
+ this.entityType = AssetEntityTypeEnum.PROPERTIES;
5368
+ }
5369
+ getApiUrlPrefix() {
5370
+ return '';
5371
+ }
5372
+ getEntity() {
5373
+ return this.property;
5374
+ }
5375
+ }
5376
+
5377
+ class PropertyValuation extends PropertyValuation$1 {
5378
+ get financialYear() {
5379
+ return new FinancialYear(this.date).year;
5380
+ }
5381
+ isCurrentYear() {
5382
+ return this.financialYear === new FinancialYear(new Date()).year;
5383
+ }
5384
+ }
5385
+ __decorate([
5386
+ Type(() => Date)
5387
+ ], PropertyValuation.prototype, "date", void 0);
5388
+ __decorate([
5389
+ Type(() => PropertyDocument)
5390
+ ], PropertyValuation.prototype, "document", void 0);
5391
+ __decorate([
5392
+ Exclude()
5393
+ ], PropertyValuation.prototype, "documentFile", void 0);
5394
+
5395
+ class PropertyForecast$1 extends AbstractModel {
5396
+ }
5397
+
5398
+ class PropertyForecast extends PropertyForecast$1 {
5399
+ constructor() {
5400
+ super(...arguments);
5401
+ this.financialYear = new FinancialYear(new Date()).year;
5431
5402
  }
5432
5403
  /**
5433
- * Group allocations by bank account via bank transactions collection
5404
+ * Get cash position
5405
+ * Cash Position = Income - Expense - Interest
5434
5406
  */
5435
- groupByBankAccount(bankTransactions) {
5436
- // Group bank transactions by bank account id
5437
- const bankTransactionsByBankAccount = new CollectionDictionary(bankTransactions, 'bankAccount.id');
5438
- // Create empty dictionary of transaction allocations
5439
- const allocationsByBankAccount = new CollectionDictionary(new TransactionAllocationCollection([]));
5440
- // Fill allocations dictionary with bank transactions dictionary keys and allocations related with each bank transaction collection
5441
- bankTransactionsByBankAccount.keys.forEach((key) => {
5442
- allocationsByBankAccount.add(key, this.getByBankTransactionsIds(bankTransactionsByBankAccount.get(key).getIds()));
5443
- });
5444
- return allocationsByBankAccount;
5407
+ get cashPosition() {
5408
+ return this.income + this.expense + this.interest;
5445
5409
  }
5446
5410
  /**
5447
- * check if collection includes allocation of passed transaction
5411
+ * Get tax position
5412
+ * Tax Position = Income - Expense - Interest - Depreciation
5448
5413
  */
5449
- hasTransaction(transaction) {
5450
- return !!this.items.find((allocation) => allocation.transaction.id === transaction.id);
5414
+ get taxPosition() {
5415
+ return this.cashPosition + this.depreciation;
5451
5416
  }
5452
5417
  /**
5453
- * Check if bank transaction is related with current allocations
5418
+ * Get rental return percent
5419
+ * Rental Return = Income / Market Value
5454
5420
  */
5455
- hasBankTransaction(bankTransaction) {
5456
- return !!this.items.find((allocation) => allocation.bankTransaction.id === bankTransaction.id);
5421
+ get rentalReturn() {
5422
+ return this.income / this.marketValue;
5423
+ }
5424
+ /**
5425
+ * Check if forecast is for real current fin year (not selected in the sidebar)
5426
+ */
5427
+ isCurrentYear() {
5428
+ return this.financialYear === new FinancialYear(new Date()).year;
5457
5429
  }
5458
5430
  }
5431
+ __decorate([
5432
+ Transform(({ value }) => +value)
5433
+ ], PropertyForecast.prototype, "income", void 0);
5434
+ __decorate([
5435
+ Transform(({ value }) => -Math.abs(+value))
5436
+ ], PropertyForecast.prototype, "expense", void 0);
5437
+ __decorate([
5438
+ Transform(({ value }) => -Math.abs(+value))
5439
+ ], PropertyForecast.prototype, "interest", void 0);
5440
+ __decorate([
5441
+ Transform(({ value }) => -Math.abs(+value))
5442
+ ], PropertyForecast.prototype, "depreciation", void 0);
5443
+ __decorate([
5444
+ Transform(({ value }) => +value)
5445
+ ], PropertyForecast.prototype, "loanBalance", void 0);
5446
+ __decorate([
5447
+ Transform(({ value }) => +value)
5448
+ ], PropertyForecast.prototype, "marketValue", void 0);
5459
5449
 
5460
- /**
5461
- * used to combine transactions/depreciations
5462
- */
5463
- class TransactionBaseCollection extends Collection {
5464
- getClaimAmountByBusinessId(businessId) {
5465
- return +this.filterBy('business.id', businessId).items.map((transaction) => transaction instanceof Depreciation ? -transaction.claimAmount : transaction['claimAmount']).reduce((sum, claimAmount) => sum + claimAmount, 0).toFixed(2);
5466
- }
5467
- getSoleTransactions() {
5468
- return this.filter((transaction) => transaction.isSoleTank());
5469
- }
5450
+ class PropertyCategoryMovement$1 extends AbstractModel {
5470
5451
  }
5471
5452
 
5472
- // @TODO Alex move here all collections
5453
+ class PropertyCategoryMovement extends PropertyCategoryMovement$1 {
5454
+ }
5455
+ __decorate([
5456
+ Type(() => PropertyValuation)
5457
+ ], PropertyCategoryMovement.prototype, "valuation", void 0);
5458
+ __decorate([
5459
+ Type(() => PropertyCategory)
5460
+ ], PropertyCategoryMovement.prototype, "propertyCategory", void 0);
5461
+ __decorate([
5462
+ Type(() => Date)
5463
+ ], PropertyCategoryMovement.prototype, "fromDate", void 0);
5464
+ __decorate([
5465
+ Type(() => Date)
5466
+ ], PropertyCategoryMovement.prototype, "toDate", void 0);
5467
+
5468
+ var TaxExemptionEnum;
5469
+ (function (TaxExemptionEnum) {
5470
+ TaxExemptionEnum[TaxExemptionEnum["ONE_YEAR_RULE"] = 1] = "ONE_YEAR_RULE";
5471
+ // principle place of residence
5472
+ TaxExemptionEnum[TaxExemptionEnum["PPR"] = 2] = "PPR";
5473
+ TaxExemptionEnum[TaxExemptionEnum["SIX_YEARS_RULE"] = 3] = "SIX_YEARS_RULE";
5474
+ TaxExemptionEnum[TaxExemptionEnum["INVESTMENT_TO_PPR"] = 4] = "INVESTMENT_TO_PPR";
5475
+ TaxExemptionEnum[TaxExemptionEnum["PPR_TO_INVESTMENT"] = 5] = "PPR_TO_INVESTMENT";
5476
+ TaxExemptionEnum[TaxExemptionEnum["TRANSFER"] = 6] = "TRANSFER";
5477
+ TaxExemptionEnum[TaxExemptionEnum["OTHER"] = 7] = "OTHER";
5478
+ })(TaxExemptionEnum || (TaxExemptionEnum = {}));
5473
5479
 
5474
5480
  /**
5475
5481
  * propertySale docs - https://taxtank.atlassian.net/wiki/spaces/TAXTANK/pages/4209508353/Property+Sold+button
@@ -17948,13 +17954,15 @@ class WorkIncomeForm extends TransactionForm {
17948
17954
  }, Validators.required),
17949
17955
  incomeSource: new FormControl(transaction.incomeSource, [Validators.required, autocompleteValidator()]),
17950
17956
  });
17951
- // salary adjustments (like tips)
17957
+ // @TODO we use amount field as netAmount to avoid extra watcher for amount recalculation
17952
17958
  if (transaction.id) {
17953
- this.get('amount').setValue(this.getAmount(transaction));
17959
+ this.get('amount').setValue(transaction.netAmount);
17954
17960
  }
17961
+ // forbid to edit some fields for allocated transaction
17955
17962
  if (allocations.length) {
17956
17963
  this.get('chartAccounts').disable();
17957
17964
  }
17965
+ // adjustments allowed only for salary
17958
17966
  if (!((_a = transaction.chartAccounts) === null || _a === void 0 ? void 0 : _a.isSalary())) {
17959
17967
  this.get('transactions').disable();
17960
17968
  }
@@ -18013,25 +18021,19 @@ class WorkIncomeForm extends TransactionForm {
18013
18021
  });
18014
18022
  }
18015
18023
  /**
18016
- * @param transaction
18017
- * @param includeAdjustments reduce result by adjustments included in salary if false or increase amount with included salary adjustments to match received payment if true,
18018
- * salary/wage comes to bankAccount together with related chartAccounts like tips (kind of salary, but reported in different tax summary section),
18024
+ * salary comes to bankAccount together with related transactions like tips (kind of salary, but reported in different tax summary section),
18019
18025
  * adjustments used to clarify received payment by separating it into multiple transactions,
18020
- * formAmount always match received payment, it also matches parent transactionAmount when there are no adjustments,
18021
- * otherwise it includes such adjustments, so we should exclude it from formAmount to create a salary transaction (on submit)
18022
- * but include in form->amount to match bank transaction (on edit),
18023
- * ie bankTransaction=1000$, it includes 900$ salary and 100$ tips (should be reported in different tax summary sections),
18024
- * so we should show 1000$ in formAmount, but create 2 transactions with 900$ and 100$
18025
- */
18026
- getAmount(transaction, includeAdjustments = true) {
18027
- if (!transaction.chartAccounts || !transaction.chartAccounts.isSalary()) {
18028
- return transaction.amount;
18029
- }
18030
- const includedTransactions = new Collection(transaction.transactions).filter((t) => t.chartAccounts.isSalaryIncluded());
18031
- return transaction.amount + includedTransactions.sumBy('amount') * (includeAdjustments ? 1 : -1);
18026
+ * netAmount always match received payment and includes related adjustments
18027
+ * amount calculated as netAmount - salary included adjustments
18028
+ * ie bankTransaction=1000$ with 900$ salary and 100$ tips will create 2 transactions with 900$ and 100$
18029
+ */
18030
+ getAmount() {
18031
+ return this.get('amount').value - new Collection(this.currentValue.transactions)
18032
+ .filter((t) => { var _a; return (_a = t.chartAccounts) === null || _a === void 0 ? void 0 : _a.isSalaryIncluded(); })
18033
+ .sumBy('amount');
18032
18034
  }
18033
18035
  submit(data = {}) {
18034
- return super.submit(Object.assign(data, { amount: this.getAmount(this.currentValue, false) }));
18036
+ return super.submit(Object.assign(data, { amount: this.getAmount() }));
18035
18037
  }
18036
18038
  }
18037
18039