mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
feat(nestjs): migrate to NestJS
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { keyBy, sumBy } from 'lodash';
|
||||
import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry';
|
||||
import { TaxRateModel } from './models/TaxRate.model';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class ItemEntriesTaxTransactions {
|
||||
constructor(
|
||||
@Inject(ItemEntry.name)
|
||||
private itemEntryModel: TenantModelProxy<typeof ItemEntry>,
|
||||
|
||||
@Inject(TaxRateModel.name)
|
||||
private taxRateModel: TenantModelProxy<typeof TaxRateModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Associates tax amount withheld to the model.
|
||||
* @param model
|
||||
* @returns
|
||||
*/
|
||||
public assocTaxAmountWithheldFromEntries = (model: any) => {
|
||||
const entries = model.entries.map((entry) =>
|
||||
this.itemEntryModel().fromJson(entry),
|
||||
);
|
||||
const taxAmountWithheld = sumBy(entries, 'taxAmount');
|
||||
|
||||
if (taxAmountWithheld) {
|
||||
model.taxAmountWithheld = taxAmountWithheld;
|
||||
}
|
||||
return model;
|
||||
};
|
||||
|
||||
/**
|
||||
* Associates tax rate id from tax code to entries.
|
||||
* @param {any} entries
|
||||
*/
|
||||
public assocTaxRateIdFromCodeToEntries = async (entries: any) => {
|
||||
const entriesWithCode = entries.filter((entry) => entry.taxCode);
|
||||
const taxCodes = entriesWithCode.map((entry) => entry.taxCode);
|
||||
const foundTaxCodes = await this.taxRateModel()
|
||||
.query()
|
||||
.whereIn('code', taxCodes);
|
||||
|
||||
const taxCodesMap = keyBy(foundTaxCodes, 'code');
|
||||
|
||||
return entries.map((entry) => {
|
||||
if (entry.taxCode) {
|
||||
entry.taxRateId = taxCodesMap[entry.taxCode]?.id;
|
||||
}
|
||||
return entry;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Associates tax rate from tax id to entries.
|
||||
* @returns {Promise<ItemEntry[]>}
|
||||
*/
|
||||
public assocTaxRateFromTaxIdToEntries = async (entries: ItemEntry[]) => {
|
||||
const entriesWithId = entries.filter((e) => e.taxRateId);
|
||||
const taxRateIds = entriesWithId.map((e) => e.taxRateId);
|
||||
const foundTaxes = await this.taxRateModel()
|
||||
.query()
|
||||
.whereIn('id', taxRateIds);
|
||||
|
||||
const taxRatesMap = keyBy(foundTaxes, 'id');
|
||||
|
||||
return entries.map((entry) => {
|
||||
if (entry.taxRateId) {
|
||||
entry.taxRate = taxRatesMap[entry.taxRateId]?.rate;
|
||||
}
|
||||
return entry;
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Item } from '../Items/models/Item';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class SyncItemTaxRateOnEditTaxRate {
|
||||
constructor(
|
||||
@Inject(Item.name)
|
||||
private readonly itemModel: TenantModelProxy<typeof Item>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Syncs the new tax rate created to item default sell tax rate.
|
||||
* @param {number} itemId - Item id.
|
||||
* @param {number} sellTaxRateId - Sell tax rate id.
|
||||
*/
|
||||
public updateItemSellTaxRate = async (
|
||||
oldSellTaxRateId: number,
|
||||
sellTaxRateId: number,
|
||||
trx?: Knex.Transaction,
|
||||
) => {
|
||||
// Can't continue if the old and new sell tax rate id are equal.
|
||||
if (oldSellTaxRateId === sellTaxRateId) return;
|
||||
|
||||
await this.itemModel()
|
||||
.query()
|
||||
.where('sellTaxRateId', oldSellTaxRateId)
|
||||
.update({
|
||||
sellTaxRateId,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Syncs the new tax rate created to item default purchase tax rate.
|
||||
* @param {number} itemId
|
||||
* @param {number} purchaseTaxRateId
|
||||
*/
|
||||
public updateItemPurchaseTaxRate = async (
|
||||
oldPurchaseTaxRateId: number,
|
||||
purchaseTaxRateId: number,
|
||||
trx?: Knex.Transaction,
|
||||
) => {
|
||||
// Can't continue if the old and new sell tax rate id are equal.
|
||||
if (oldPurchaseTaxRateId === purchaseTaxRateId) return;
|
||||
|
||||
await this.itemModel()
|
||||
.query(trx)
|
||||
.where('purchaseTaxRateId', oldPurchaseTaxRateId)
|
||||
.update({
|
||||
purchaseTaxRateId,
|
||||
});
|
||||
};
|
||||
}
|
||||
88
packages/server/src/modules/TaxRates/TaxRate.application.ts
Normal file
88
packages/server/src/modules/TaxRates/TaxRate.application.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { CreateTaxRate } from './commands/CreateTaxRate.service';
|
||||
import { DeleteTaxRateService } from './commands/DeleteTaxRate.service';
|
||||
import { EditTaxRateService } from './commands/EditTaxRate.service';
|
||||
import { GetTaxRateService } from './queries/GetTaxRate.service';
|
||||
import { ActivateTaxRateService } from './commands/ActivateTaxRate.service';
|
||||
import { InactivateTaxRateService } from './commands/InactivateTaxRate';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { GetTaxRatesService } from './queries/GetTaxRates.service';
|
||||
import { CreateTaxRateDto, EditTaxRateDto } from './dtos/TaxRate.dto';
|
||||
|
||||
@Injectable()
|
||||
export class TaxRatesApplication {
|
||||
constructor(
|
||||
private readonly createTaxRateService: CreateTaxRate,
|
||||
private readonly editTaxRateService: EditTaxRateService,
|
||||
private readonly deleteTaxRateService: DeleteTaxRateService,
|
||||
private readonly getTaxRateService: GetTaxRateService,
|
||||
private readonly activateTaxRateService: ActivateTaxRateService,
|
||||
private readonly inactivateTaxRateService: InactivateTaxRateService,
|
||||
private readonly getTaxRatesService: GetTaxRatesService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Creates a new tax rate.
|
||||
* @param {ICreateTaxRateDTO} createTaxRateDTO
|
||||
* @returns {Promise<ITaxRate>}
|
||||
*/
|
||||
public createTaxRate(createTaxRateDTO: CreateTaxRateDto) {
|
||||
return this.createTaxRateService.createTaxRate(createTaxRateDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the given tax rate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} taxRateId
|
||||
* @param {IEditTaxRateDTO} taxRateEditDTO
|
||||
* @returns {Promise<ITaxRate>}
|
||||
*/
|
||||
public editTaxRate(taxRateId: number, editTaxRateDTO: EditTaxRateDto) {
|
||||
return this.editTaxRateService.editTaxRate(taxRateId, editTaxRateDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given tax rate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} taxRateId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public deleteTaxRate(taxRateId: number) {
|
||||
return this.deleteTaxRateService.deleteTaxRate(taxRateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the given tax rate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} taxRateId
|
||||
* @returns {Promise<ITaxRate>}
|
||||
*/
|
||||
public getTaxRate(taxRateId: number) {
|
||||
return this.getTaxRateService.getTaxRate(taxRateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the tax rates list.
|
||||
* @returns {Promise<ITaxRate[]>}
|
||||
*/
|
||||
public getTaxRates() {
|
||||
return this.getTaxRatesService.getTaxRates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the given tax rate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} taxRateId
|
||||
*/
|
||||
public activateTaxRate(taxRateId: number) {
|
||||
return this.activateTaxRateService.activateTaxRate(taxRateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inactivates the given tax rate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} taxRateId
|
||||
*/
|
||||
public inactivateTaxRate(taxRateId: number) {
|
||||
return this.inactivateTaxRateService.inactivateTaxRate(taxRateId);
|
||||
}
|
||||
}
|
||||
63
packages/server/src/modules/TaxRates/TaxRate.controller.ts
Normal file
63
packages/server/src/modules/TaxRates/TaxRate.controller.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
} from '@nestjs/common';
|
||||
import { TaxRatesApplication } from './TaxRate.application';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { CreateTaxRateDto, EditTaxRateDto } from './dtos/TaxRate.dto';
|
||||
|
||||
@Controller('tax-rates')
|
||||
@ApiTags('tax-rates')
|
||||
export class TaxRatesController {
|
||||
constructor(private readonly taxRatesApplication: TaxRatesApplication) {}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: 'Create a new tax rate.' })
|
||||
public createTaxRate(@Body() createTaxRateDTO: CreateTaxRateDto) {
|
||||
return this.taxRatesApplication.createTaxRate(createTaxRateDTO);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
@ApiOperation({ summary: 'Edit the given tax rate.' })
|
||||
public editTaxRate(
|
||||
@Param('id') taxRateId: number,
|
||||
@Body() editTaxRateDTO: EditTaxRateDto,
|
||||
) {
|
||||
return this.taxRatesApplication.editTaxRate(taxRateId, editTaxRateDTO);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: 'Delete the given tax rate.' })
|
||||
public deleteTaxRate(@Param('id') taxRateId: number) {
|
||||
return this.taxRatesApplication.deleteTaxRate(taxRateId);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Retrieves the tax rate details.' })
|
||||
public getTaxRate(@Param('id') taxRateId: number) {
|
||||
return this.taxRatesApplication.getTaxRate(taxRateId);
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Retrieves the tax rates.' })
|
||||
public getTaxRates() {
|
||||
return this.taxRatesApplication.getTaxRates();
|
||||
}
|
||||
|
||||
@Put(':id/activate')
|
||||
@ApiOperation({ summary: 'Activate the given tax rate.' })
|
||||
public activateTaxRate(@Param('id') taxRateId: number) {
|
||||
return this.taxRatesApplication.activateTaxRate(taxRateId);
|
||||
}
|
||||
|
||||
@Put(':id/inactivate')
|
||||
@ApiOperation({ summary: 'Inactivate the given tax rate.' })
|
||||
public inactivateTaxRate(@Param('id') taxRateId: number) {
|
||||
return this.taxRatesApplication.inactivateTaxRate(taxRateId);
|
||||
}
|
||||
}
|
||||
53
packages/server/src/modules/TaxRates/TaxRate.module.ts
Normal file
53
packages/server/src/modules/TaxRates/TaxRate.module.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TransformerInjectable } from '../Transformer/TransformerInjectable.service';
|
||||
import { TaxRatesController } from './TaxRate.controller';
|
||||
import { CreateTaxRate } from './commands/CreateTaxRate.service';
|
||||
import { InactivateTaxRateService } from './commands/InactivateTaxRate';
|
||||
import { ActivateTaxRateService } from './commands/ActivateTaxRate.service';
|
||||
import { GetTaxRateService } from './queries/GetTaxRate.service';
|
||||
import { DeleteTaxRateService } from './commands/DeleteTaxRate.service';
|
||||
import { EditTaxRateService } from './commands/EditTaxRate.service';
|
||||
import { CommandTaxRatesValidators } from './commands/CommandTaxRatesValidator.service';
|
||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||
import { TaxRatesApplication } from './TaxRate.application';
|
||||
import { ItemEntriesTaxTransactions } from './ItemEntriesTaxTransactions.service';
|
||||
import { GetTaxRatesService } from './queries/GetTaxRates.service';
|
||||
import { WriteBillTaxTransactionsSubscriber } from './subscribers/WriteBillTaxTransactionsSubscriber';
|
||||
import { WriteInvoiceTaxTransactionsSubscriber } from './subscribers/WriteInvoiceTaxTransactionsSubscriber';
|
||||
import { BillTaxRateValidateSubscriber } from './subscribers/BillTaxRateValidateSubscriber';
|
||||
import { SaleInvoiceTaxRateValidateSubscriber } from './subscribers/SaleInvoiceTaxRateValidateSubscriber';
|
||||
import { SyncItemTaxRateOnEditTaxSubscriber } from './subscribers/SyncItemTaxRateOnEditTaxSubscriber';
|
||||
import { WriteTaxTransactionsItemEntries } from './WriteTaxTransactionsItemEntries';
|
||||
import { SyncItemTaxRateOnEditTaxRate } from './SyncItemTaxRateOnEditTaxRate';
|
||||
import { RegisterTenancyModel } from '../Tenancy/TenancyModels/Tenancy.module';
|
||||
import { TaxRateTransaction } from './models/TaxRateTransaction.model';
|
||||
|
||||
const models = [RegisterTenancyModel(TaxRateTransaction)];
|
||||
|
||||
@Module({
|
||||
imports: [...models],
|
||||
controllers: [TaxRatesController],
|
||||
providers: [
|
||||
CreateTaxRate,
|
||||
EditTaxRateService,
|
||||
DeleteTaxRateService,
|
||||
GetTaxRateService,
|
||||
GetTaxRatesService,
|
||||
ActivateTaxRateService,
|
||||
InactivateTaxRateService,
|
||||
CommandTaxRatesValidators,
|
||||
TransformerInjectable,
|
||||
TenancyContext,
|
||||
TaxRatesApplication,
|
||||
ItemEntriesTaxTransactions,
|
||||
WriteBillTaxTransactionsSubscriber,
|
||||
WriteInvoiceTaxTransactionsSubscriber,
|
||||
BillTaxRateValidateSubscriber,
|
||||
SaleInvoiceTaxRateValidateSubscriber,
|
||||
SyncItemTaxRateOnEditTaxSubscriber,
|
||||
WriteTaxTransactionsItemEntries,
|
||||
SyncItemTaxRateOnEditTaxRate,
|
||||
],
|
||||
exports: [ItemEntriesTaxTransactions, ...models],
|
||||
})
|
||||
export class TaxRatesModule {}
|
||||
90
packages/server/src/modules/TaxRates/TaxRates.types.ts
Normal file
90
packages/server/src/modules/TaxRates/TaxRates.types.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { Knex } from 'knex';
|
||||
import { TaxRateModel } from './models/TaxRate.model';
|
||||
|
||||
export interface ITaxRate {
|
||||
id?: number;
|
||||
name: string;
|
||||
code: string;
|
||||
rate: number;
|
||||
description: string;
|
||||
IsNonRecoverable: boolean;
|
||||
IsCompound: boolean;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export interface ICommonTaxRateDTO {
|
||||
name: string;
|
||||
code: string;
|
||||
rate: number;
|
||||
description: string;
|
||||
IsNonRecoverable: boolean;
|
||||
IsCompound: boolean;
|
||||
active: boolean;
|
||||
}
|
||||
export interface ICreateTaxRateDTO extends ICommonTaxRateDTO {}
|
||||
export interface IEditTaxRateDTO extends ICommonTaxRateDTO {}
|
||||
|
||||
export interface ITaxRateCreatingPayload {
|
||||
createTaxRateDTO: ICreateTaxRateDTO;
|
||||
// tenantId: number;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
export interface ITaxRateCreatedPayload {
|
||||
createTaxRateDTO: ICreateTaxRateDTO;
|
||||
taxRate: TaxRateModel;
|
||||
// tenantId: number;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface ITaxRateEditingPayload {
|
||||
oldTaxRate: TaxRateModel;
|
||||
editTaxRateDTO: IEditTaxRateDTO;
|
||||
// tenantId: number;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
export interface ITaxRateEditedPayload {
|
||||
editTaxRateDTO: IEditTaxRateDTO;
|
||||
oldTaxRate: TaxRateModel;
|
||||
taxRate: TaxRateModel;
|
||||
// tenantId: number;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface ITaxRateDeletingPayload {
|
||||
oldTaxRate: TaxRateModel;
|
||||
// tenantId: number;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface ITaxRateActivatingPayload {
|
||||
taxRateId: number;
|
||||
// tenantId: number;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
export interface ITaxRateActivatedPayload {
|
||||
taxRateId: number;
|
||||
// tenantId: number;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface ITaxRateDeletedPayload {
|
||||
oldTaxRate: TaxRateModel;
|
||||
// tenantId: number;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface ITaxTransaction {
|
||||
id?: number;
|
||||
taxRateId: number;
|
||||
referenceType: string;
|
||||
referenceId: number;
|
||||
rate: number;
|
||||
taxAccountId: number;
|
||||
}
|
||||
|
||||
export enum TaxRateAction {
|
||||
CREATE = 'Create',
|
||||
EDIT = 'Edit',
|
||||
DELETE = 'Delete',
|
||||
VIEW = 'View',
|
||||
}
|
||||
18
packages/server/src/modules/TaxRates/TaxRatesExportable.ts
Normal file
18
packages/server/src/modules/TaxRates/TaxRatesExportable.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// import { Inject, Service } from 'typedi';
|
||||
// import { Exportable } from '../Export/Exportable';
|
||||
// import { TaxRatesApplication } from './TaxRate.application';
|
||||
|
||||
// @Service()
|
||||
// export class TaxRatesExportable extends Exportable {
|
||||
// @Inject()
|
||||
// private taxRatesApplication: TaxRatesApplication;
|
||||
|
||||
// /**
|
||||
// * Retrieves the accounts data to exportable sheet.
|
||||
// * @param {number} tenantId
|
||||
// * @returns
|
||||
// */
|
||||
// public exportable(tenantId: number) {
|
||||
// return this.taxRatesApplication.getTaxRates(tenantId);
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,18 @@
|
||||
export const TaxRatesSampleData = [
|
||||
{
|
||||
'Tax Name': 'Value Added Tax',
|
||||
Code: 'VAT-STD',
|
||||
Rate: '20',
|
||||
Description: 'Standard VAT rate applied to most goods and services.',
|
||||
'Is Non Recoverable': 'F',
|
||||
Active: 'T',
|
||||
},
|
||||
{
|
||||
'Tax Name': 'Luxury Goods Tax',
|
||||
Code: 'TAX-LUXURY',
|
||||
Rate: '25',
|
||||
Description: 'Tax imposed on the sale of luxury items.',
|
||||
'Is Non Recoverable': 'T',
|
||||
Active: 'T',
|
||||
},
|
||||
];
|
||||
46
packages/server/src/modules/TaxRates/TaxRatesImportable.ts
Normal file
46
packages/server/src/modules/TaxRates/TaxRatesImportable.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
// import { Inject, Service } from 'typedi';
|
||||
// import { Knex } from 'knex';
|
||||
// import { ICreateTaxRateDTO } from '@/interfaces';
|
||||
// import { CreateTaxRate } from './commands/CreateTaxRate.service';
|
||||
// import { Importable } from '../Import/Importable';
|
||||
// import { TaxRatesSampleData } from './TaxRatesImportable.SampleData';
|
||||
|
||||
// @Service()
|
||||
// export class TaxRatesImportable extends Importable {
|
||||
// @Inject()
|
||||
// private createTaxRateService: CreateTaxRate;
|
||||
|
||||
// /**
|
||||
// * Importing to tax rate creating service.
|
||||
// * @param {number} tenantId -
|
||||
// * @param {ICreateTaxRateDTO} ICreateTaxRateDTO -
|
||||
// * @param {Knex.Transaction} trx -
|
||||
// * @returns
|
||||
// */
|
||||
// public importable(
|
||||
// tenantId: number,
|
||||
// createAccountDTO: ICreateTaxRateDTO,
|
||||
// trx?: Knex.Transaction
|
||||
// ) {
|
||||
// return this.createTaxRateService.createTaxRate(
|
||||
// tenantId,
|
||||
// createAccountDTO,
|
||||
// trx
|
||||
// );
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Concurrrency controlling of the importing process.
|
||||
// * @returns {number}
|
||||
// */
|
||||
// public get concurrency() {
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Retrieves the sample data that used to download accounts sample sheet.
|
||||
// */
|
||||
// public sampleData(): any[] {
|
||||
// return TaxRatesSampleData;
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,105 @@
|
||||
import { sumBy, chain, keyBy } from 'lodash';
|
||||
import { Knex } from 'knex';
|
||||
import { ModelObject } from 'objection';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
import { TaxRateModel } from './models/TaxRate.model';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { ItemEntry } from '../TransactionItemEntry/models/ItemEntry';
|
||||
import { TaxRateTransaction } from './models/TaxRateTransaction.model';
|
||||
|
||||
@Injectable()
|
||||
export class WriteTaxTransactionsItemEntries {
|
||||
constructor(
|
||||
@Inject(TaxRateTransaction.name)
|
||||
private readonly taxRateTransactionModel: TenantModelProxy<
|
||||
typeof TaxRateTransaction
|
||||
>,
|
||||
|
||||
@Inject(TaxRateModel.name)
|
||||
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Writes the tax transactions from the given item entries.
|
||||
* @param {number} tenantId
|
||||
* @param {IItemEntry[]} itemEntries
|
||||
*/
|
||||
public async writeTaxTransactionsFromItemEntries(
|
||||
itemEntries: ModelObject<ItemEntry>[],
|
||||
trx?: Knex.Transaction,
|
||||
) {
|
||||
const aggregatedEntries = this.aggregateItemEntriesByTaxCode(itemEntries);
|
||||
const entriesTaxRateIds = aggregatedEntries.map((entry) => entry.taxRateId);
|
||||
|
||||
const taxRates = await this.taxRateModel()
|
||||
.query(trx)
|
||||
.whereIn('id', entriesTaxRateIds);
|
||||
const taxRatesById = keyBy(taxRates, 'id');
|
||||
|
||||
const taxTransactions = aggregatedEntries.map((entry) => ({
|
||||
taxRateId: entry.taxRateId,
|
||||
referenceType: entry.referenceType,
|
||||
referenceId: entry.referenceId,
|
||||
rate: entry.taxRate || (taxRatesById[entry.taxRateId]?.rate as number),
|
||||
}));
|
||||
await this.taxRateTransactionModel()
|
||||
.query(trx)
|
||||
.upsertGraph(taxTransactions as ModelObject<TaxRateTransaction>[]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites the tax rate transactions from the given item entries.
|
||||
* @param {number} tenantId
|
||||
* @param {IItemEntry[]} itemEntries
|
||||
* @param {string} referenceType
|
||||
* @param {number} referenceId
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public async rewriteTaxRateTransactionsFromItemEntries(
|
||||
itemEntries: ModelObject<ItemEntry>[],
|
||||
referenceType: string,
|
||||
referenceId: number,
|
||||
trx?: Knex.Transaction,
|
||||
) {
|
||||
await Promise.all([
|
||||
this.removeTaxTransactionsFromItemEntries(
|
||||
referenceId,
|
||||
referenceType,
|
||||
trx,
|
||||
),
|
||||
this.writeTaxTransactionsFromItemEntries(itemEntries, trx),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregates by tax code id and sums the amount.
|
||||
* @param {IItemEntry[]} itemEntries
|
||||
* @returns {IItemEntry[]}
|
||||
*/
|
||||
private aggregateItemEntriesByTaxCode = (
|
||||
itemEntries: ModelObject<ItemEntry>[],
|
||||
): ModelObject<ItemEntry>[] => {
|
||||
return chain(itemEntries.filter((item) => item.taxRateId))
|
||||
.groupBy((item) => item.taxRateId)
|
||||
.values()
|
||||
.map((group) => ({ ...group[0], amount: sumBy(group, 'amount') }))
|
||||
.value();
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the tax transactions from the given item entries.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {string} referenceType - Reference type.
|
||||
* @param {number} referenceId - Reference id.
|
||||
*/
|
||||
public async removeTaxTransactionsFromItemEntries(
|
||||
referenceId: number,
|
||||
referenceType: string,
|
||||
trx?: Knex.Transaction,
|
||||
) {
|
||||
await this.taxRateTransactionModel()
|
||||
.query(trx)
|
||||
.where({ referenceType, referenceId })
|
||||
.delete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
ITaxRateActivatedPayload,
|
||||
ITaxRateActivatingPayload,
|
||||
} from '../TaxRates.types';
|
||||
import { CommandTaxRatesValidators } from './CommandTaxRatesValidator.service';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { TaxRateModel } from '../models/TaxRate.model';
|
||||
import { events } from '@/common/events/events';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class ActivateTaxRateService {
|
||||
/**
|
||||
* @param {EventEmitter2} eventEmitter - The event emitter.
|
||||
* @param {UnitOfWork} uow - The unit of work.
|
||||
* @param {CommandTaxRatesValidators} validators - The tax rates validators.
|
||||
* @param {typeof TaxRateModel} taxRateModel - The tax rate model.
|
||||
*/
|
||||
constructor(
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly uow: UnitOfWork,
|
||||
private readonly validators: CommandTaxRatesValidators,
|
||||
|
||||
@Inject(TaxRateModel.name)
|
||||
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Activates the given tax rate.
|
||||
* @param {number} taxRateId
|
||||
* @returns {Promise<ITaxRate>}
|
||||
*/
|
||||
public async activateTaxRate(taxRateId: number) {
|
||||
const oldTaxRate = await this.taxRateModel().query().findById(taxRateId);
|
||||
|
||||
// Validates the tax rate existance.
|
||||
this.validators.validateTaxRateExistance(oldTaxRate);
|
||||
|
||||
// Validates the tax rate inactive.
|
||||
this.validators.validateTaxRateNotActive(oldTaxRate);
|
||||
|
||||
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||
// Triggers `onTaxRateActivating` event.
|
||||
await this.eventEmitter.emitAsync(events.taxRates.onActivating, {
|
||||
taxRateId,
|
||||
trx,
|
||||
} as ITaxRateActivatingPayload);
|
||||
|
||||
const taxRate = await this.taxRateModel()
|
||||
.query(trx)
|
||||
.findById(taxRateId)
|
||||
.patch({ active: true });
|
||||
|
||||
// Triggers `onTaxRateCreated` event.
|
||||
await this.eventEmitter.emitAsync(events.taxRates.onActivated, {
|
||||
taxRateId,
|
||||
trx,
|
||||
} as ITaxRateActivatedPayload);
|
||||
|
||||
return taxRate;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import { Knex } from 'knex';
|
||||
import { difference } from 'lodash';
|
||||
// import { IItemEntryDTO } from '@/modules/Items/';
|
||||
import { ERRORS } from '../constants';
|
||||
import { TaxRateModel } from '../models/TaxRate.model';
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { IItemEntryDTO } from '@/modules/TransactionItemEntry/ItemEntry.types';
|
||||
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
|
||||
|
||||
@Injectable()
|
||||
export class CommandTaxRatesValidators {
|
||||
/**
|
||||
* @param {TenantModelProxy<typeof TaxRateModel>} taxRateModel - The tax rate model.
|
||||
*/
|
||||
constructor(
|
||||
@Inject(TaxRateModel.name)
|
||||
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validates the tax rate existance.
|
||||
* @param {TaxRate | undefined | null} taxRate
|
||||
*/
|
||||
public validateTaxRateExistance(taxRate: TaxRateModel | undefined | null) {
|
||||
if (!taxRate) {
|
||||
throw new ServiceError(ERRORS.TAX_RATE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given tax rate active.
|
||||
* @param {TaxRateModel} taxRate
|
||||
*/
|
||||
public validateTaxRateNotActive(taxRate: TaxRateModel) {
|
||||
if (taxRate.active) {
|
||||
throw new ServiceError(ERRORS.TAX_RATE_ALREADY_ACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given tax rate inactive.
|
||||
* @param {TaxRateModel} taxRate
|
||||
*/
|
||||
public validateTaxRateNotInactive(taxRate: TaxRateModel) {
|
||||
if (!taxRate.active) {
|
||||
throw new ServiceError(ERRORS.TAX_RATE_ALREADY_INACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the tax code uniquiness.
|
||||
* @param {number} tenantId
|
||||
* @param {string} taxCode
|
||||
* @param {Knex.Transaction} trx -
|
||||
*/
|
||||
public async validateTaxCodeUnique(taxCode: string, trx?: Knex.Transaction) {
|
||||
const foundTaxCode = await this.taxRateModel()
|
||||
.query(trx)
|
||||
.findOne({ code: taxCode });
|
||||
|
||||
if (foundTaxCode) {
|
||||
throw new ServiceError(ERRORS.TAX_CODE_NOT_UNIQUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the tax codes of the given item entries DTO.
|
||||
* @param {IItemEntryDTO[]} itemEntriesDTO
|
||||
* @throws {ServiceError}
|
||||
*/
|
||||
public async validateItemEntriesTaxCode(itemEntriesDTO: ItemEntryDto[]) {
|
||||
const filteredTaxEntries = itemEntriesDTO.filter((e) => e.taxCode);
|
||||
const taxCodes = filteredTaxEntries.map((e) => e.taxCode);
|
||||
|
||||
// Can't validate if there is no tax codes.
|
||||
if (taxCodes.length === 0) return;
|
||||
|
||||
const foundTaxCodes = await this.taxRateModel()
|
||||
.query()
|
||||
.whereIn('code', taxCodes);
|
||||
const foundCodes = foundTaxCodes.map((tax) => tax.code);
|
||||
|
||||
const notFoundTaxCodes = difference(taxCodes, foundCodes);
|
||||
|
||||
if (notFoundTaxCodes.length > 0) {
|
||||
throw new ServiceError(ERRORS.ITEM_ENTRY_TAX_RATE_CODE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the tax rate id of the given item entries DTO.
|
||||
* @param {ItemEntryDto[]} itemEntriesDTO
|
||||
* @throws {ServiceError}
|
||||
*/
|
||||
public async validateItemEntriesTaxCodeId(itemEntriesDTO: ItemEntryDto[]) {
|
||||
const filteredTaxEntries = itemEntriesDTO.filter((e) => e.taxRateId);
|
||||
const taxRatesIds = filteredTaxEntries.map((e) => e.taxRateId);
|
||||
|
||||
// Can't validate if there is no tax codes.
|
||||
if (taxRatesIds.length === 0) return;
|
||||
|
||||
const foundTaxCodes = await this.taxRateModel()
|
||||
.query()
|
||||
.whereIn('id', taxRatesIds);
|
||||
const foundTaxRatesIds = foundTaxCodes.map((tax) => tax.id);
|
||||
|
||||
const notFoundTaxCodes = difference(taxRatesIds, foundTaxRatesIds);
|
||||
|
||||
if (notFoundTaxCodes.length > 0) {
|
||||
throw new ServiceError(ERRORS.ITEM_ENTRY_TAX_RATE_ID_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
ICreateTaxRateDTO,
|
||||
ITaxRateCreatedPayload,
|
||||
ITaxRateCreatingPayload,
|
||||
} from '../TaxRates.types';
|
||||
import { CommandTaxRatesValidators } from './CommandTaxRatesValidator.service';
|
||||
import { TaxRateModel } from '../models/TaxRate.model';
|
||||
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { CreateTaxRateDto } from '../dtos/TaxRate.dto';
|
||||
|
||||
@Injectable()
|
||||
export class CreateTaxRate {
|
||||
/**
|
||||
* @param {EventEmitter2} eventEmitter - The event emitter.
|
||||
* @param {UnitOfWork} uow - The unit of work.
|
||||
* @param {CommandTaxRatesValidators} validators - The tax rates validators.
|
||||
* @param {typeof TaxRateModel} taxRateModel - The tax rate model.
|
||||
*/
|
||||
constructor(
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly uow: UnitOfWork,
|
||||
private readonly validators: CommandTaxRatesValidators,
|
||||
|
||||
@Inject(TaxRateModel.name)
|
||||
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Creates a new tax rate.
|
||||
* @param {ICreateTaxRateDTO} createTaxRateDTO
|
||||
*/
|
||||
public async createTaxRate(
|
||||
createTaxRateDTO: CreateTaxRateDto,
|
||||
trx?: Knex.Transaction,
|
||||
) {
|
||||
// Validates the tax code uniquiness.
|
||||
await this.validators.validateTaxCodeUnique(createTaxRateDTO.code, trx);
|
||||
|
||||
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||
// Triggers `onTaxRateCreating` event.
|
||||
await this.eventEmitter.emitAsync(events.taxRates.onCreating, {
|
||||
createTaxRateDTO,
|
||||
trx,
|
||||
} as ITaxRateCreatingPayload);
|
||||
|
||||
const taxRate = await this.taxRateModel()
|
||||
.query(trx)
|
||||
.insertAndFetch({
|
||||
...createTaxRateDTO,
|
||||
});
|
||||
|
||||
// Triggers `onTaxRateCreated` event.
|
||||
await this.eventEmitter.emitAsync(events.taxRates.onCreated, {
|
||||
createTaxRateDTO,
|
||||
taxRate,
|
||||
trx,
|
||||
} as ITaxRateCreatedPayload);
|
||||
|
||||
return taxRate;
|
||||
}, trx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
ITaxRateDeletedPayload,
|
||||
ITaxRateDeletingPayload,
|
||||
} from '../TaxRates.types';
|
||||
import { CommandTaxRatesValidators } from './CommandTaxRatesValidator.service';
|
||||
import { TaxRateModel } from '../models/TaxRate.model';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { events } from '@/common/events/events';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteTaxRateService {
|
||||
/**
|
||||
* @param {EventEmitter2} eventEmitter - The event emitter.
|
||||
* @param {UnitOfWork} uow - The unit of work.
|
||||
* @param {CommandTaxRatesValidators} validators - The tax rates validators.
|
||||
* @param {typeof TaxRateModel} taxRateModel - The tax rate model.
|
||||
*/
|
||||
constructor(
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly uow: UnitOfWork,
|
||||
private readonly validators: CommandTaxRatesValidators,
|
||||
|
||||
@Inject(TaxRateModel.name)
|
||||
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Deletes the given tax rate.
|
||||
* @param {number} taxRateId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async deleteTaxRate(taxRateId: number): Promise<void> {
|
||||
const oldTaxRate = await this.taxRateModel().query().findById(taxRateId);
|
||||
|
||||
// Validates the tax rate existance.
|
||||
this.validators.validateTaxRateExistance(oldTaxRate);
|
||||
|
||||
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||
// Triggers `onTaxRateDeleting` event.
|
||||
await this.eventEmitter.emitAsync(events.taxRates.onDeleting, {
|
||||
oldTaxRate,
|
||||
trx,
|
||||
} as ITaxRateDeletingPayload);
|
||||
|
||||
await this.taxRateModel().query(trx).findById(taxRateId).delete();
|
||||
|
||||
// Triggers `onTaxRateDeleted` event.
|
||||
await this.eventEmitter.emitAsync(events.taxRates.onDeleted, {
|
||||
oldTaxRate,
|
||||
trx,
|
||||
} as ITaxRateDeletedPayload);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Knex } from 'knex';
|
||||
import { omit } from 'lodash';
|
||||
import {
|
||||
IEditTaxRateDTO,
|
||||
ITaxRateEditedPayload,
|
||||
ITaxRateEditingPayload,
|
||||
} from '../TaxRates.types';
|
||||
import { CommandTaxRatesValidators } from './CommandTaxRatesValidator.service';
|
||||
import { TaxRateModel } from '../models/TaxRate.model';
|
||||
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { events } from '@/common/events/events';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { EditTaxRateDto } from '../dtos/TaxRate.dto';
|
||||
|
||||
@Injectable()
|
||||
export class EditTaxRateService {
|
||||
/**
|
||||
* @param {EventEmitter2} eventEmitter - The event emitter.
|
||||
* @param {UnitOfWork} uow - The unit of work.
|
||||
* @param {CommandTaxRatesValidators} validators - The tax rates validators.
|
||||
* @param {TenantModelProxy<typeof TaxRateModel>} taxRateModel - The tax rate model.
|
||||
*/
|
||||
constructor(
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly uow: UnitOfWork,
|
||||
private readonly validators: CommandTaxRatesValidators,
|
||||
|
||||
@Inject(TaxRateModel.name)
|
||||
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Determines whether the tax rate, name or code have been changed.
|
||||
* @param {ITaxRate} taxRate
|
||||
* @param {IEditTaxRateDTO} editTaxRateDTO
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private isTaxRateDTOChanged = (
|
||||
taxRate: TaxRateModel,
|
||||
editTaxRateDTO: EditTaxRateDto,
|
||||
) => {
|
||||
return (
|
||||
taxRate.rate !== editTaxRateDTO.rate ||
|
||||
taxRate.name !== editTaxRateDTO.name ||
|
||||
taxRate.code !== editTaxRateDTO.code
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits the given tax rate or creates a new if the rate or name have been changed.
|
||||
* @param {number} tenantId
|
||||
* @param {ITaxRate} oldTaxRate
|
||||
* @param {IEditTaxRateDTO} editTaxRateDTO
|
||||
* @param {Knex.Transaction} trx
|
||||
* @returns {Promise<ITaxRate>}
|
||||
*/
|
||||
private async editTaxRateOrCreate(
|
||||
oldTaxRate: TaxRateModel,
|
||||
editTaxRateDTO: EditTaxRateDto,
|
||||
trx?: Knex.Transaction,
|
||||
) {
|
||||
const isTaxDTOChanged = this.isTaxRateDTOChanged(
|
||||
oldTaxRate,
|
||||
editTaxRateDTO,
|
||||
);
|
||||
if (isTaxDTOChanged) {
|
||||
// Soft deleting the old tax rate.
|
||||
await this.taxRateModel().query(trx).findById(oldTaxRate.id).delete();
|
||||
|
||||
// Create a new tax rate with new edited data.
|
||||
return this.taxRateModel()
|
||||
.query(trx)
|
||||
.insertAndFetch({
|
||||
...omit(oldTaxRate, ['id']),
|
||||
...editTaxRateDTO,
|
||||
});
|
||||
} else {
|
||||
return this.taxRateModel()
|
||||
.query(trx)
|
||||
.patchAndFetchById(oldTaxRate.id, {
|
||||
...editTaxRateDTO,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the given tax rate.
|
||||
* @param {number} taxRateId - The tax rate id.
|
||||
* @param {IEditTaxRateDTO} editTaxRateDTO - The tax rate data to edit.
|
||||
* @returns {Promise<ITaxRate>}
|
||||
*/
|
||||
public async editTaxRate(taxRateId: number, editTaxRateDTO: EditTaxRateDto) {
|
||||
const oldTaxRate = await this.taxRateModel().query().findById(taxRateId);
|
||||
|
||||
// Validates the tax rate existance.
|
||||
this.validators.validateTaxRateExistance(oldTaxRate);
|
||||
|
||||
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||
// Triggers `onTaxRateEditing` event.
|
||||
await this.eventEmitter.emitAsync(events.taxRates.onEditing, {
|
||||
editTaxRateDTO,
|
||||
trx,
|
||||
} as ITaxRateEditingPayload);
|
||||
|
||||
const taxRate = await this.editTaxRateOrCreate(
|
||||
oldTaxRate,
|
||||
editTaxRateDTO,
|
||||
trx,
|
||||
);
|
||||
// Triggers `onTaxRateEdited` event.
|
||||
await this.eventEmitter.emitAsync(events.taxRates.onEdited, {
|
||||
editTaxRateDTO,
|
||||
oldTaxRate,
|
||||
taxRate,
|
||||
trx,
|
||||
} as ITaxRateEditedPayload);
|
||||
|
||||
return taxRate;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import {
|
||||
ITaxRateActivatedPayload,
|
||||
ITaxRateActivatingPayload,
|
||||
} from '../TaxRates.types';
|
||||
import { Knex } from 'knex';
|
||||
import { CommandTaxRatesValidators } from './CommandTaxRatesValidator.service';
|
||||
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { TaxRateModel } from '../models/TaxRate.model';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class InactivateTaxRateService {
|
||||
/**
|
||||
* @param {EventEmitter2} eventEmitter - The event emitter.
|
||||
* @param {UnitOfWork} uow - The unit of work.
|
||||
* @param {CommandTaxRatesValidators} validators - The tax rates validators.
|
||||
* @param {typeof TaxRateModel} taxRateModel - The tax rate model.
|
||||
*/
|
||||
constructor(
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly uow: UnitOfWork,
|
||||
private readonly validators: CommandTaxRatesValidators,
|
||||
|
||||
@Inject(TaxRateModel.name)
|
||||
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Edits the given tax rate.
|
||||
* @param {number} taxRateId
|
||||
* @returns {Promise<ITaxRate>}
|
||||
*/
|
||||
public async inactivateTaxRate(taxRateId: number) {
|
||||
const oldTaxRate = await this.taxRateModel().query().findById(taxRateId);
|
||||
|
||||
// Validates the tax rate existance.
|
||||
this.validators.validateTaxRateExistance(oldTaxRate);
|
||||
|
||||
// Validates the tax rate active.
|
||||
this.validators.validateTaxRateNotInactive(oldTaxRate);
|
||||
|
||||
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||
// Triggers `onTaxRateActivating` event.
|
||||
await this.eventEmitter.emitAsync(events.taxRates.onInactivating, {
|
||||
taxRateId,
|
||||
trx,
|
||||
} as ITaxRateActivatingPayload);
|
||||
|
||||
const taxRate = await this.taxRateModel()
|
||||
.query(trx)
|
||||
.findById(taxRateId)
|
||||
.patch({ active: false });
|
||||
|
||||
// Triggers `onTaxRateCreated` event.
|
||||
await this.eventEmitter.emitAsync(events.taxRates.onInactivated, {
|
||||
taxRateId,
|
||||
trx,
|
||||
} as ITaxRateActivatedPayload);
|
||||
|
||||
return taxRate;
|
||||
});
|
||||
}
|
||||
}
|
||||
8
packages/server/src/modules/TaxRates/constants.ts
Normal file
8
packages/server/src/modules/TaxRates/constants.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const ERRORS = {
|
||||
TAX_RATE_NOT_FOUND: 'TAX_RATE_NOT_FOUND',
|
||||
TAX_CODE_NOT_UNIQUE: 'TAX_CODE_NOT_UNIQUE',
|
||||
ITEM_ENTRY_TAX_RATE_CODE_NOT_FOUND: 'ITEM_ENTRY_TAX_RATE_CODE_NOT_FOUND',
|
||||
ITEM_ENTRY_TAX_RATE_ID_NOT_FOUND: 'ITEM_ENTRY_TAX_RATE_ID_NOT_FOUND',
|
||||
TAX_RATE_ALREADY_ACTIVE: 'TAX_RATE_ALREADY_ACTIVE',
|
||||
TAX_RATE_ALREADY_INACTIVE: 'TAX_RATE_ALREADY_INACTIVE'
|
||||
};
|
||||
94
packages/server/src/modules/TaxRates/dtos/TaxRate.dto.ts
Normal file
94
packages/server/src/modules/TaxRates/dtos/TaxRate.dto.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Transform } from 'class-transformer';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsNumber,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
} from 'class-validator';
|
||||
|
||||
export class CommandTaxRateDto {
|
||||
/**
|
||||
* Tax rate name.
|
||||
*/
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({
|
||||
description: 'The name of the tax rate.',
|
||||
example: 'VAT',
|
||||
})
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Tax rate code.
|
||||
*/
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({
|
||||
description: 'The code of the tax rate.',
|
||||
example: 'VAT',
|
||||
})
|
||||
code: string;
|
||||
|
||||
/**
|
||||
* Tax rate percentage.
|
||||
*/
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({
|
||||
description: 'The rate of the tax rate.',
|
||||
example: 10,
|
||||
})
|
||||
rate: number;
|
||||
|
||||
/**
|
||||
* Tax rate description.
|
||||
*/
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@ApiProperty({
|
||||
description: 'The description of the tax rate.',
|
||||
example: 'VAT',
|
||||
})
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* Whether the tax is non-recoverable.
|
||||
*/
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => value ?? false)
|
||||
@ApiProperty({
|
||||
description: 'Whether the tax is non-recoverable.',
|
||||
example: false,
|
||||
})
|
||||
isNonRecoverable?: boolean;
|
||||
|
||||
/**
|
||||
* Whether the tax is compound.
|
||||
*/
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => value ?? false)
|
||||
@ApiProperty({
|
||||
description: 'Whether the tax is compound.',
|
||||
example: false,
|
||||
})
|
||||
isCompound?: boolean;
|
||||
|
||||
/**
|
||||
* Whether the tax rate is active.
|
||||
*/
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => value ?? false)
|
||||
@ApiProperty({
|
||||
description: 'Whether the tax rate is active.',
|
||||
example: false,
|
||||
})
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
export class CreateTaxRateDto extends CommandTaxRateDto {}
|
||||
export class EditTaxRateDto extends CommandTaxRateDto {}
|
||||
64
packages/server/src/modules/TaxRates/models/TaxRate.model.ts
Normal file
64
packages/server/src/modules/TaxRates/models/TaxRate.model.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { mixin, Model, raw } from 'objection';
|
||||
// import TenantModel from 'models/TenantModel';
|
||||
// import ModelSearchable from './ModelSearchable';
|
||||
// import SoftDeleteQueryBuilder from '@/collection/SoftDeleteQueryBuilder';
|
||||
// import TaxRateMeta from './TaxRate.settings';
|
||||
// import ModelSetting from './ModelSetting';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
|
||||
export class TaxRateModel extends BaseModel {
|
||||
active!: boolean;
|
||||
code!: string;
|
||||
name!: string;
|
||||
rate!: number;
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
static get tableName() {
|
||||
return 'tax_rates';
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft delete query builder.
|
||||
*/
|
||||
// static get QueryBuilder() {
|
||||
// return SoftDeleteQueryBuilder;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Timestamps columns.
|
||||
*/
|
||||
get timestamps() {
|
||||
return ['createdAt', 'updatedAt'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the tax rate meta.
|
||||
*/
|
||||
// static get meta() {
|
||||
// return TaxRateMeta;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Virtual attributes.
|
||||
*/
|
||||
static get virtualAttributes() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Model modifiers.
|
||||
*/
|
||||
static get modifiers() {
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Relationship mapping.
|
||||
*/
|
||||
static get relationMappings() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { mixin, Model, raw } from 'objection';
|
||||
// import TenantModel from 'models/TenantModel';
|
||||
// import ModelSearchable from './ModelSearchable';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
|
||||
export class TaxRateTransaction extends BaseModel {
|
||||
public id: number;
|
||||
public taxRateId: number;
|
||||
public referenceType: string;
|
||||
public referenceId: string;
|
||||
public rate: number;
|
||||
public taxAccountId?: number;
|
||||
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
static get tableName() {
|
||||
return 'tax_rate_transactions';
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamps columns.
|
||||
*/
|
||||
get timestamps() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Virtual attributes.
|
||||
*/
|
||||
static get virtualAttributes() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Model modifiers.
|
||||
*/
|
||||
static get modifiers() {
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Relationship mapping.
|
||||
*/
|
||||
static get relationMappings() {
|
||||
const { TaxRateModel } = require('./TaxRate.model');
|
||||
|
||||
return {
|
||||
/**
|
||||
* Belongs to the tax rate.
|
||||
*/
|
||||
taxRate: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: TaxRateModel,
|
||||
join: {
|
||||
from: 'tax_rate_transactions.taxRateId',
|
||||
to: 'tax_rates.id',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { TaxRateTransformer } from './TaxRate.transformer';
|
||||
import { TaxRateModel } from '../models/TaxRate.model';
|
||||
import { CommandTaxRatesValidators } from '../commands/CommandTaxRatesValidator.service';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class GetTaxRateService {
|
||||
/**
|
||||
* @param {typeof TaxRateModel} taxRateModel - The tax rate model.
|
||||
* @param {CommandTaxRatesValidators} validators - The tax rates validators.
|
||||
* @param {TransformerInjectable} transformer - The transformer.
|
||||
*/
|
||||
constructor(
|
||||
@Inject(TaxRateModel.name)
|
||||
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
|
||||
private readonly validators: CommandTaxRatesValidators,
|
||||
private readonly transformer: TransformerInjectable,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the given tax rate.
|
||||
* @param {number} taxRateId
|
||||
* @returns {Promise<ITaxRate>}
|
||||
*/
|
||||
public async getTaxRate(taxRateId: number) {
|
||||
const taxRate = await this.taxRateModel().query().findById(taxRateId);
|
||||
|
||||
// Validates the tax rate existance.
|
||||
this.validators.validateTaxRateExistance(taxRate);
|
||||
|
||||
// Transforms the tax rate.
|
||||
return this.transformer.transform(taxRate, new TaxRateTransformer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { TaxRateTransformer } from './TaxRate.transformer';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { TaxRateModel } from '../models/TaxRate.model';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class GetTaxRatesService {
|
||||
constructor(
|
||||
private transformer: TransformerInjectable,
|
||||
@Inject(TaxRateModel.name)
|
||||
private taxRateModel: TenantModelProxy<typeof TaxRateModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the tax rates list.
|
||||
* @returns {Promise<ITaxRate[]>}
|
||||
*/
|
||||
public async getTaxRates() {
|
||||
// Retrieves the tax rates.
|
||||
const taxRates = await this.taxRateModel().query().orderBy('name', 'ASC');
|
||||
|
||||
// Transforms the tax rates.
|
||||
return this.transformer.transform(taxRates, new TaxRateTransformer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||
import { TaxRateModel } from '../models/TaxRate.model';
|
||||
|
||||
export class TaxRateTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to tax rate object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return ['nameFormatted', 'rateFormatted'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the formatted rate.
|
||||
* @param taxRate
|
||||
* @returns {string}
|
||||
*/
|
||||
public rateFormatted = (taxRate: TaxRateModel): string => {
|
||||
return `${taxRate.rate}%`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats the tax rate name.
|
||||
* @param taxRate
|
||||
* @returns {string}
|
||||
*/
|
||||
protected nameFormatted = (taxRate: TaxRateModel): string => {
|
||||
return `${taxRate.name} [${taxRate.rate}%]`;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
import { CommandTaxRatesValidators } from '../commands/CommandTaxRatesValidator.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { IBillCreatingPayload } from '@/modules/Bills/Bills.types';
|
||||
import { IBillEditingPayload } from '@/modules/Bills/Bills.types';
|
||||
|
||||
@Injectable()
|
||||
export class BillTaxRateValidateSubscriber {
|
||||
constructor(
|
||||
private readonly taxRateDTOValidator: CommandTaxRatesValidators,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validate bill entries tax rate code existance when creating.
|
||||
* @param {IBillCreatingPayload}
|
||||
*/
|
||||
@OnEvent(events.bill.onCreating)
|
||||
async validateBillEntriesTaxCodeExistanceOnCreating({
|
||||
billDTO,
|
||||
}: IBillCreatingPayload) {
|
||||
await this.taxRateDTOValidator.validateItemEntriesTaxCode(billDTO.entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the tax rate id existance when creating.
|
||||
* @param {IBillCreatingPayload}
|
||||
*/
|
||||
@OnEvent(events.bill.onCreating)
|
||||
async validateBillEntriesTaxIdExistanceOnCreating({
|
||||
billDTO,
|
||||
}: IBillCreatingPayload) {
|
||||
await this.taxRateDTOValidator.validateItemEntriesTaxCodeId(
|
||||
billDTO.entries,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate bill entries tax rate code existance when editing.
|
||||
* @param {IBillEditingPayload}
|
||||
*/
|
||||
@OnEvent(events.bill.onEditing)
|
||||
async validateBillEntriesTaxCodeExistanceOnEditing({
|
||||
billDTO,
|
||||
}: IBillEditingPayload) {
|
||||
await this.taxRateDTOValidator.validateItemEntriesTaxCode(billDTO.entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the bill entries tax rate id existance when editing.
|
||||
* @param {ISaleInvoiceEditingPayload} payload -
|
||||
*/
|
||||
@OnEvent(events.bill.onEditing)
|
||||
async validateBillEntriesTaxIdExistanceOnEditing({
|
||||
billDTO,
|
||||
}: IBillEditingPayload) {
|
||||
await this.taxRateDTOValidator.validateItemEntriesTaxCodeId(
|
||||
billDTO.entries,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { CommandTaxRatesValidators } from '../commands/CommandTaxRatesValidator.service';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
ISaleInvoiceCreatingPaylaod,
|
||||
ISaleInvoiceEditingPayload,
|
||||
} from '@/modules/SaleInvoices/SaleInvoice.types';
|
||||
import { events } from '@/common/events/events';
|
||||
|
||||
@Injectable()
|
||||
export class SaleInvoiceTaxRateValidateSubscriber {
|
||||
constructor(
|
||||
private readonly taxRateDTOValidator: CommandTaxRatesValidators,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validate invoice entries tax rate code existance when creating.
|
||||
* @param {ISaleInvoiceCreatingPaylaod}
|
||||
*/
|
||||
@OnEvent(events.saleInvoice.onCreating)
|
||||
async validateSaleInvoiceEntriesTaxCodeExistanceOnCreating({
|
||||
saleInvoiceDTO,
|
||||
}: ISaleInvoiceCreatingPaylaod) {
|
||||
await this.taxRateDTOValidator.validateItemEntriesTaxCode(
|
||||
saleInvoiceDTO.entries,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the tax rate id existance when creating.
|
||||
* @param {ISaleInvoiceCreatingPaylaod}
|
||||
*/
|
||||
@OnEvent(events.saleInvoice.onCreating)
|
||||
async validateSaleInvoiceEntriesTaxIdExistanceOnCreating({
|
||||
saleInvoiceDTO,
|
||||
}: ISaleInvoiceCreatingPaylaod) {
|
||||
await this.taxRateDTOValidator.validateItemEntriesTaxCodeId(
|
||||
saleInvoiceDTO.entries,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate invoice entries tax rate code existance when editing.
|
||||
* @param {ISaleInvoiceEditingPayload}
|
||||
*/
|
||||
@OnEvent(events.saleInvoice.onEditing)
|
||||
async validateSaleInvoiceEntriesTaxCodeExistanceOnEditing({
|
||||
saleInvoiceDTO,
|
||||
}: ISaleInvoiceEditingPayload) {
|
||||
await this.taxRateDTOValidator.validateItemEntriesTaxCode(
|
||||
saleInvoiceDTO.entries,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the invoice entries tax rate id existance when editing.
|
||||
* @param {ISaleInvoiceEditingPayload} payload -
|
||||
*/
|
||||
@OnEvent(events.saleInvoice.onEditing)
|
||||
async validateSaleInvoiceEntriesTaxIdExistanceOnEditing({
|
||||
saleInvoiceDTO,
|
||||
}: ISaleInvoiceEditingPayload) {
|
||||
await this.taxRateDTOValidator.validateItemEntriesTaxCodeId(
|
||||
saleInvoiceDTO.entries,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ITaxRateEditedPayload } from '../TaxRates.types';
|
||||
import { runAfterTransaction } from '@/modules/Tenancy/TenancyDB/TransactionsHooks';
|
||||
import { events } from '@/common/events/events';
|
||||
import { SyncItemTaxRateOnEditTaxRate } from '../SyncItemTaxRateOnEditTaxRate';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
|
||||
@Injectable()
|
||||
export class SyncItemTaxRateOnEditTaxSubscriber {
|
||||
constructor(
|
||||
private readonly syncItemRateOnEdit: SyncItemTaxRateOnEditTaxRate,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Syncs the new tax rate created to default item tax rates.
|
||||
* @param {ITaxRateEditedPayload} payload -
|
||||
*/
|
||||
@OnEvent(events.taxRates.onEdited)
|
||||
async handleSyncNewTaxRateToItemTaxRate({
|
||||
taxRate,
|
||||
oldTaxRate,
|
||||
trx,
|
||||
}: ITaxRateEditedPayload) {
|
||||
runAfterTransaction(trx, async () => {
|
||||
await this.syncItemRateOnEdit.updateItemPurchaseTaxRate(
|
||||
oldTaxRate.id,
|
||||
taxRate.id,
|
||||
);
|
||||
await this.syncItemRateOnEdit.updateItemSellTaxRate(
|
||||
oldTaxRate.id,
|
||||
taxRate.id,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import {
|
||||
IBIllEventDeletedPayload,
|
||||
IBillCreatedPayload,
|
||||
IBillEditedPayload,
|
||||
} from '@/modules/Bills/Bills.types';
|
||||
import { WriteTaxTransactionsItemEntries } from '../WriteTaxTransactionsItemEntries';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class WriteBillTaxTransactionsSubscriber {
|
||||
constructor(
|
||||
@Inject()
|
||||
private readonly writeTaxTransactions: WriteTaxTransactionsItemEntries,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Writes the bill tax transactions on invoice created.
|
||||
* @param {ISaleInvoiceCreatingPaylaod}
|
||||
*/
|
||||
@OnEvent(events.bill.onCreated)
|
||||
async writeInvoiceTaxTransactionsOnCreated({
|
||||
bill,
|
||||
trx,
|
||||
}: IBillCreatedPayload) {
|
||||
await this.writeTaxTransactions.writeTaxTransactionsFromItemEntries(
|
||||
bill.entries,
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites the bill tax transactions on invoice edited.
|
||||
* @param {IBillEditedPayload} payload -
|
||||
*/
|
||||
@OnEvent(events.bill.onEdited)
|
||||
async rewriteInvoiceTaxTransactionsOnEdited({
|
||||
bill,
|
||||
trx,
|
||||
}: IBillEditedPayload) {
|
||||
await this.writeTaxTransactions.rewriteTaxRateTransactionsFromItemEntries(
|
||||
bill.entries,
|
||||
'Bill',
|
||||
bill.id,
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the invoice tax transactions on invoice deleted.
|
||||
* @param {IBIllEventDeletedPayload}
|
||||
*/
|
||||
@OnEvent(events.bill.onDeleted)
|
||||
async removeInvoiceTaxTransactionsOnDeleted({
|
||||
oldBill,
|
||||
trx,
|
||||
}: IBIllEventDeletedPayload) {
|
||||
await this.writeTaxTransactions.removeTaxTransactionsFromItemEntries(
|
||||
oldBill.id,
|
||||
'Bill',
|
||||
trx,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
ISaleInvoiceDeletedPayload,
|
||||
ISaleInvoiceEditedPayload,
|
||||
} from '@/modules/SaleInvoices/SaleInvoice.types';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { WriteTaxTransactionsItemEntries } from '../WriteTaxTransactionsItemEntries';
|
||||
import { events } from '@/common/events/events';
|
||||
import { ISaleInvoiceCreatedPayload } from '@/modules/SaleInvoices/SaleInvoice.types';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class WriteInvoiceTaxTransactionsSubscriber {
|
||||
constructor(
|
||||
private readonly writeTaxTransactions: WriteTaxTransactionsItemEntries,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Writes the invoice tax transactions on invoice created.
|
||||
* @param {ISaleInvoiceCreatingPaylaod}
|
||||
*/
|
||||
@OnEvent(events.saleInvoice.onCreated)
|
||||
async writeInvoiceTaxTransactionsOnCreated({
|
||||
saleInvoice,
|
||||
trx,
|
||||
}: ISaleInvoiceCreatedPayload) {
|
||||
await this.writeTaxTransactions.writeTaxTransactionsFromItemEntries(
|
||||
saleInvoice.entries,
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites the invoice tax transactions on invoice edited.
|
||||
* @param {ISaleInvoiceEditedPayload} payload -
|
||||
*/
|
||||
@OnEvent(events.saleInvoice.onEdited)
|
||||
async rewriteInvoiceTaxTransactionsOnEdited({
|
||||
saleInvoice,
|
||||
trx,
|
||||
}: ISaleInvoiceEditedPayload) {
|
||||
await this.writeTaxTransactions.rewriteTaxRateTransactionsFromItemEntries(
|
||||
saleInvoice.entries,
|
||||
'SaleInvoice',
|
||||
saleInvoice.id,
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the invoice tax transactions on invoice deleted.
|
||||
* @param {ISaleInvoiceEditingPayload}
|
||||
*/
|
||||
@OnEvent(events.saleInvoice.onDelete)
|
||||
async removeInvoiceTaxTransactionsOnDeleted({
|
||||
oldSaleInvoice,
|
||||
trx,
|
||||
}: ISaleInvoiceDeletedPayload) {
|
||||
await this.writeTaxTransactions.removeTaxTransactionsFromItemEntries(
|
||||
oldSaleInvoice.id,
|
||||
'SaleInvoice',
|
||||
trx,
|
||||
);
|
||||
}
|
||||
}
|
||||
19
packages/server/src/modules/TaxRates/utils.ts
Normal file
19
packages/server/src/modules/TaxRates/utils.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Get inclusive tax amount.
|
||||
* @param {number} amount
|
||||
* @param {number} taxRate
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getInclusiveTaxAmount = (amount: number, taxRate: number) => {
|
||||
return (amount * taxRate) / (100 + taxRate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get exclusive tax amount.
|
||||
* @param {number} amount
|
||||
* @param {number} taxRate
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getExlusiveTaxAmount = (amount: number, taxRate: number) => {
|
||||
return (amount * taxRate) / 100;
|
||||
};
|
||||
Reference in New Issue
Block a user