taxtank-core 0.28.110 → 0.28.112

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