taxtank-core 0.28.70 → 0.28.73

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.
@@ -2409,6 +2409,10 @@ __decorate([
2409
2409
  Type(() => SoleBusinessLossOffsetRule)
2410
2410
  ], SoleBusinessLossOffsetRule.prototype, "parent", void 0);
2411
2411
 
2412
+ /**
2413
+ * If a sole trader business makes a tax loss in a current year, you can generally carry forward that loss and offset profit in future years.
2414
+ * https://taxtank.atlassian.net/wiki/spaces/TAXTANK/pages/4641357930/Rules+when+a+business+makes+a+loss+Tax+Summary#Offsetting-current-year-business-losses
2415
+ */
2412
2416
  class SoleBusinessLoss extends SoleBusinessLoss$1 {
2413
2417
  constructor() {
2414
2418
  super(...arguments);
@@ -4198,6 +4202,24 @@ class BasReport extends BasReport$1 {
4198
4202
  get taxWithheldTotal() {
4199
4203
  return this.taxWithheldSalary + this.taxWithheldNoABN;
4200
4204
  }
4205
+ get paygTaxInstalmentOwedToATO() {
4206
+ return this.paygTaxInstalment > 0 ? this.paygTaxInstalment : 0;
4207
+ }
4208
+ get paygTaxInstalmentOwedByATO() {
4209
+ return this.paygTaxInstalment < 0 ? Math.abs(this.paygTaxInstalment) : 0;
4210
+ }
4211
+ get fuelTaxCreditOwedToATO() {
4212
+ return this.fuelTaxCredit < 0 ? this.fuelTaxCredit : 0;
4213
+ }
4214
+ get fuelTaxCreditOwedByATO() {
4215
+ return this.fuelTaxCredit > 0 ? Math.abs(this.fuelTaxCredit) : 0;
4216
+ }
4217
+ get owesToATO() {
4218
+ return this.incomeGST + this.taxWithheldTotal + this.paygTaxInstalmentOwedToATO + this.fuelTaxCreditOwedToATO;
4219
+ }
4220
+ get owedByATO() {
4221
+ return this.expenseGST + this.paygTaxInstalmentOwedByATO + this.fuelTaxCreditOwedByATO;
4222
+ }
4201
4223
  /**
4202
4224
  * GST payable to the ATO, or refundable from the ATO in case it's negative
4203
4225
  */
@@ -5991,26 +6013,40 @@ class Dictionary {
5991
6013
 
5992
6014
  class SoleBusinessLossesCollection extends Collection {
5993
6015
  /**
5994
- * Calculate business losses applied to the current year
6016
+ * Business loss applied in current year, includes previous year losses and current year losses for businesses matching offset rule
5995
6017
  */
5996
6018
  calculateBusinessLossApplied(transactions) {
5997
- const transactionsByBusinessIds = new CollectionDictionary(transactions, 'business.id');
5998
- // Dictionary with pairs key = business id, value = business claim amount
6019
+ // claim amounts for businesses that can be applied to be reduced by prior year business losses
6020
+ const claimAmountsByBusinessId = this.getClaimAmountsByBusinessId(transactions);
6021
+ return Object.keys(claimAmountsByBusinessId.items).reduce((sum, businessId) => {
6022
+ const loss = this.findBy('business.id', +businessId);
6023
+ const lossOpenBalance = (loss === null || loss === void 0 ? void 0 : loss.openBalance) || 0;
6024
+ // business loss can be applied to business profit or other income types profit in case in offset rule met
6025
+ return sum + (loss.offsetRule ? lossOpenBalance : Math.min(lossOpenBalance, claimAmountsByBusinessId.get(businessId)));
6026
+ }, 0);
6027
+ }
6028
+ /**
6029
+ * Get business claim amounts that can be applied to be reduced by prior year business losses:
6030
+ * businesses with income or businesses with a loss, but which met the non-commercial loss rules
6031
+ * https://www.ato.gov.au/Business/Non-commercial-losses/
6032
+ */
6033
+ getClaimAmountsByBusinessId(transactions) {
5999
6034
  const claimAmountsByBusinessId = new Dictionary([]);
6000
- transactionsByBusinessIds.keys.forEach((businessId) => {
6001
- const businessClaimAmount = transactionsByBusinessIds
6035
+ const transactionsByBusinessId = new CollectionDictionary(transactions, 'business.id');
6036
+ transactionsByBusinessId.keys.forEach((businessId) => {
6037
+ var _a;
6038
+ // business loss may not exist if, when creating a business, user didn't add losses for previous years
6039
+ const lossOffsetRule = (_a = this.findBy('business.id', +businessId)) === null || _a === void 0 ? void 0 : _a.offsetRule;
6040
+ const businessClaimAmount = transactionsByBusinessId
6002
6041
  .get(businessId)
6003
6042
  .getClaimAmountByBusinessId(+businessId);
6004
- // only businesses with positive income are included in the calculation
6005
- if (businessClaimAmount > 0) {
6006
- claimAmountsByBusinessId.add(businessId, businessClaimAmount);
6043
+ // no way to apply loss for business without profit if offset rules not met
6044
+ if (businessClaimAmount < 0 && !lossOffsetRule) {
6045
+ return;
6007
6046
  }
6047
+ claimAmountsByBusinessId.add(businessId, businessClaimAmount);
6008
6048
  });
6009
- return Object.keys(claimAmountsByBusinessId.items).reduce((sum, businessId) => {
6010
- var _a;
6011
- const lossOpenBalance = ((_a = this.findBy('business.id', +businessId)) === null || _a === void 0 ? void 0 : _a.openBalance) || 0;
6012
- return sum + Math.min(lossOpenBalance, claimAmountsByBusinessId.get(businessId));
6013
- }, 0);
6049
+ return claimAmountsByBusinessId;
6014
6050
  }
6015
6051
  }
6016
6052
 
@@ -10466,6 +10502,16 @@ class SoleInvoiceService extends RestService {
10466
10502
  sendEmail(invoice) {
10467
10503
  return this.http.post(`${this.environment.apiV2}/${this.url}/${invoice.id}/send`, {});
10468
10504
  }
10505
+ /**
10506
+ * Return the next number from the last invoice.
10507
+ * Basically needed to create a new invoice to show its number when the invoice has not yet been sent to the backend
10508
+ */
10509
+ getNewInvoiceNumber() {
10510
+ if (!this.cache) {
10511
+ return 0;
10512
+ }
10513
+ return last(this.cache).number + 1;
10514
+ }
10469
10515
  }
10470
10516
  SoleInvoiceService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.15", ngImport: i0, type: SoleInvoiceService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
10471
10517
  SoleInvoiceService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.15", ngImport: i0, type: SoleInvoiceService, providedIn: 'root' });
@@ -16044,31 +16090,37 @@ class SoleInvoiceItemForm extends AbstractForm {
16044
16090
  }
16045
16091
  }
16046
16092
 
16093
+ /**
16094
+ * Form is divided into two groups, since the creation of Sole invoice takes place in two steps:
16095
+ * a common invoice data and sole invoice items.
16096
+ */
16047
16097
  class SoleInvoiceForm extends AbstractForm {
16048
16098
  constructor(invoice, soleDetailsGST,
16049
16099
  // default template to be preselected
16050
16100
  defaultTemplate) {
16051
16101
  super({
16052
- dateFrom: new FormControl(invoice.dateFrom || new Date(), Validators.required),
16053
- dateTo: new FormControl(invoice.dateTo, Validators.required),
16102
+ commonData: new FormGroup({
16103
+ dateFrom: new FormControl(invoice.dateFrom || new Date(), Validators.required),
16104
+ dateTo: new FormControl(invoice.dateTo, Validators.required),
16105
+ payer: new FormControl(invoice.payer, Validators.required),
16106
+ taxType: new FormControl(invoice.taxType, Validators.required),
16107
+ bankAccount: new FormControl(invoice.bankAccount, Validators.required),
16108
+ reference: new FormControl(invoice.reference)
16109
+ }),
16054
16110
  items: new FormArray((invoice.items || [plainToClass(SoleInvoiceItem, {})]).map((item) => new SoleInvoiceItemForm(item))),
16055
- payer: new FormControl(invoice.payer, Validators.required),
16056
- taxType: new FormControl(invoice.taxType, Validators.required),
16057
- bankAccount: new FormControl(invoice.bankAccount, Validators.required),
16058
- reference: new FormControl(invoice.reference)
16059
16111
  }, invoice);
16060
16112
  this.invoice = invoice;
16061
16113
  this.soleDetailsGST = soleDetailsGST;
16062
16114
  this.defaultTemplate = defaultTemplate;
16063
16115
  // we need invoice template only for new invoices
16064
16116
  if (!invoice.id) {
16065
- this.addControl('template', new FormControl(this.defaultTemplate));
16117
+ (this.commonData).addControl('template', new FormControl(this.defaultTemplate));
16066
16118
  this.updateTemplateRelatedFields(this.defaultTemplate);
16067
16119
  }
16068
16120
  // invoice.taxType is always NONE ('No Tax') when soleDetails.isGST === false
16069
16121
  if (!this.soleDetailsGST) {
16070
- this.get('taxType').setValue(SoleInvoiceTaxTypeEnum.NO_TAX);
16071
- this.get('taxType').disable();
16122
+ this.commonData.get('taxType').setValue(SoleInvoiceTaxTypeEnum.NO_TAX);
16123
+ this.commonData.get('taxType').disable();
16072
16124
  // Items isGST is not available when invoice.taxType === NONE ('No Tax')
16073
16125
  this.items.controls.forEach((itemForm) => {
16074
16126
  this.disableItemGST(itemForm);
@@ -16088,8 +16140,8 @@ class SoleInvoiceForm extends AbstractForm {
16088
16140
  this.listenItemChartAccountsChanges(itemForm);
16089
16141
  });
16090
16142
  }
16091
- // nothing to listen if template field is not учшые (edit invoice case)
16092
- if (this.contains('template')) {
16143
+ // nothing to listen if template field is not exist (edit invoice case)
16144
+ if ((this.commonData).contains('template')) {
16093
16145
  this.listenTemplateChanges();
16094
16146
  }
16095
16147
  }
@@ -16106,15 +16158,20 @@ class SoleInvoiceForm extends AbstractForm {
16106
16158
  this.items.removeAt(index);
16107
16159
  }
16108
16160
  submit(data = {}) {
16109
- // @TODO Alex TT-2190: move child custom forms submit to Abstract Form
16110
- const items = this.items.controls.map((control) => control.submit());
16111
- return super.submit(merge(data, { items }));
16161
+ this.submitted = true;
16162
+ this.markAllAsTouched();
16163
+ if (!this.disabled && !this.valid) {
16164
+ return null;
16165
+ }
16166
+ const invoiceInstance = this.currentValue;
16167
+ this.onSubmit.emit(invoiceInstance);
16168
+ return invoiceInstance;
16112
16169
  }
16113
16170
  /**
16114
16171
  * Update default values from selected invoice template
16115
16172
  */
16116
16173
  listenTemplateChanges() {
16117
- this.get('template').valueChanges.subscribe((template) => {
16174
+ this.commonData.get('template').valueChanges.subscribe((template) => {
16118
16175
  this.updateTemplateRelatedFields(template);
16119
16176
  });
16120
16177
  }
@@ -16122,7 +16179,7 @@ class SoleInvoiceForm extends AbstractForm {
16122
16179
  * GST is not available for items when invoice.taxType === NONE
16123
16180
  */
16124
16181
  listenTaxTypeChanges() {
16125
- this.get('taxType').valueChanges.subscribe((type) => {
16182
+ this.commonData.get('taxType').valueChanges.subscribe((type) => {
16126
16183
  this.items.controls.forEach((itemForm) => {
16127
16184
  const chartAccounts = itemForm.get('chartAccounts').value;
16128
16185
  // Item GST is available when invoice.taxType !== NONE ('No Tax')
@@ -16135,18 +16192,18 @@ class SoleInvoiceForm extends AbstractForm {
16135
16192
  });
16136
16193
  }
16137
16194
  updateTemplateRelatedFields(template) {
16138
- this.get('bankAccount').setValue(template.bankAccount);
16139
- const dateFrom = this.get('dateFrom').value;
16195
+ this.commonData.get('bankAccount').setValue(template.bankAccount);
16196
+ const dateFrom = this.commonData.get('dateFrom').value;
16140
16197
  if (dateFrom) {
16141
- this.get('dateTo').setValue(new Date(dateFrom.getTime() + template.termTime));
16198
+ this.commonData.get('dateTo').setValue(new Date(dateFrom.getTime() + template.termTime));
16142
16199
  }
16143
16200
  // invoice.taxType is always 'No Tax' when soleDetails.isGST = false and not available for changing
16144
16201
  if (this.soleDetailsGST) {
16145
- this.get('taxType').setValue(template.taxType);
16202
+ this.commonData.get('taxType').setValue(template.taxType);
16146
16203
  }
16147
16204
  }
16148
16205
  /**
16149
- * GST availability depends of chart accounts isGST flag
16206
+ * GST availability depends on chart accounts isGST flag
16150
16207
  */
16151
16208
  listenItemChartAccountsChanges(itemForm) {
16152
16209
  itemForm.get('chartAccounts').valueChanges.subscribe((chartAccounts) => {
@@ -16170,6 +16227,16 @@ class SoleInvoiceForm extends AbstractForm {
16170
16227
  itemForm.get('isGST').setValue(true);
16171
16228
  itemForm.get('isGST').enable();
16172
16229
  }
16230
+ get commonData() {
16231
+ return this.get('commonData');
16232
+ }
16233
+ /**
16234
+ * Rewrite abstract form getter to flat "commonData" field, which is not in the class
16235
+ */
16236
+ get currentValue() {
16237
+ const formRawValue = this.getRawValue();
16238
+ return plainToClass(SoleInvoice, Object.assign({}, this.model, Object.assign(Object.assign({}, formRawValue.commonData), { items: formRawValue.items })));
16239
+ }
16173
16240
  }
16174
16241
 
16175
16242
  class SoleInvoiceTemplateForm extends AbstractForm {