feat(server): wip tax rates service

This commit is contained in:
Ahmed Bouhuolia
2023-08-11 01:31:52 +02:00
parent 26c6ca9e36
commit 04d134806b
16 changed files with 550 additions and 1 deletions

View File

@@ -0,0 +1,108 @@
import { Inject, Service } from 'typedi';
import { Router, Request, Response } from 'express';
import { body, query } from 'express-validator';
import { pick } from 'lodash';
import { IOptionDTO, IOptionsDTO } from '@/interfaces';
import BaseController from '@/api/controllers/BaseController';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import { AbilitySubject, PreferencesAction } from '@/interfaces';
import SettingsService from '@/services/Settings/SettingsService';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import { TaxRatesApplication } from '@/services/TaxRates/TaxRatesApplication';
@Service()
export class TaxRatesController extends BaseController {
@Inject()
private taxRatesApplication: TaxRatesApplication;
/**
* Router constructor.
*/
public router() {
const router = Router();
router.post(
'/',
this.taxRateValidationSchema,
this.validationResult,
asyncMiddleware(this.createTaxRate.bind(this))
);
router.post(
'/:tax_rate_id',
this.taxRateValidationSchema,
this.validationResult,
asyncMiddleware(this.editTaxRate.bind(this))
);
router.delete(
'/:tax_rate_id',
this.taxRateValidationSchema,
this.validationResult,
asyncMiddleware(this.deleteTaxRate.bind(this))
);
router.get(
'/:tax_rate_id',
this.taxRateValidationSchema,
this.validationResult,
asyncMiddleware(this.getTaxRate.bind(this))
);
router.get(
'/',
this.taxRateValidationSchema,
this.validationResult,
asyncMiddleware(this.getTaxRates.bind(this))
);
return router;
}
/**
* Save settings validation schema.
*/
private get taxRateValidationSchema() {
return [
body('rate').exists().isNumeric().toFloat(),
body('is_non_recoverable').exists().isBoolean().default(false),
];
}
/**
*
* @param {Request} req -
* @param {Response} res -
*/
public async createTaxRate(req: Request, res: Response, next) {
const taxRate = await this.taxRatesApplication.createTaxRate()
}
/**
*
* @param {Request} req -
* @param {Response} res -
*/
public async editTaxRate(req: Request, res: Response, next) {
const taxRate = await this.taxRatesApplication.editTaxRate();
}
/**
*
* @param {Request} req -
* @param {Response} res -
*/
public async deleteTaxRate(req: Request, res: Response, next) {
await this.taxRatesApplication.deleteTaxRate();
}
/**
*
* @param {Request} req -
* @param {Response} res -
*/
public async getTaxRate(req: Request, res: Response, next) {}
/**
*
* @param {Request} req -
* @param {Response} res -
*/
public async getTaxRates(req: Request, res: Response, next) {}
}

View File

@@ -0,0 +1,14 @@
exports.up = (knex) => {
return knex.schema.createTable('tax_rates', (table) => {
table.increments();
table.string('name');
table.decimal('rate');
table.boolean('is_non_recoverable');
table.boolean('is_compound');
table.timestamps();
});
};
exports.down = (knex) => {
return knex.schema.dropTableIfExists('tax_rates');
};

View File

@@ -0,0 +1,48 @@
import { Knex } from 'knex';
export interface ITaxRate {}
export interface ICommonTaxRateDTO {
name: string;
rate: number;
IsNonRecoverable: boolean;
IsCompound: 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: ITaxRate;
tenantId: number;
trx: Knex.Transaction;
}
export interface ITaxRateEditingPayload {
editTaxRateDTO: IEditTaxRateDTO;
tenantId: number;
trx: Knex.Transaction;
}
export interface ITaxRateEditedPayload {
editTaxRateDTO: IEditTaxRateDTO;
oldTaxRate: ITaxRate;
taxRate: ITaxRate;
tenantId: number;
trx: Knex.Transaction;
}
export interface ITaxRateDeletingPayload {
oldTaxRate: ITaxRate;
tenantId: number;
trx: Knex.Transaction;
}
export interface ITaxRateDeletedPayload {
oldTaxRate: ITaxRate;
tenantId: number;
trx: Knex.Transaction;
}

View File

@@ -73,6 +73,7 @@ export * from './Project';
export * from './Tasks';
export * from './Times';
export * from './ProjectProfitabilitySummary';
export * from './TaxRate';
export interface I18nService {
__: (input: string) => string;

View File

@@ -58,6 +58,7 @@ import ItemWarehouseQuantity from 'models/ItemWarehouseQuantity';
import Project from 'models/Project';
import Time from 'models/Time';
import Task from 'models/Task';
import TaxRate from 'models/TaxRate';
export default (knex) => {
const models = {
@@ -119,6 +120,7 @@ export default (knex) => {
Project,
Time,
Task,
TaxRate,
};
return mapValues(models, (model) => model.bindKnex(knex));
};

View File

@@ -0,0 +1,40 @@
import { mixin, Model, raw } from 'objection';
import TenantModel from 'models/TenantModel';
import ModelSearchable from './ModelSearchable';
export default class TaxRate extends mixin(TenantModel, [ModelSearchable]) {
/**
* Table name
*/
static get tableName() {
return 'tax_rates';
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['created_at', 'updated_at'];
}
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return [];
}
/**
* Model modifiers.
*/
static get modifiers() {
return {};
}
/**
* Relationship mapping.
*/
static get relationMappings() {
return {};
}
}

View File

@@ -0,0 +1,16 @@
import { ServiceError } from '@/exceptions';
import TaxRate from '@/models/TaxRate';
import { Service } from 'typedi';
@Service()
export class CommandTaxRatesValidators {
/**
*
* @param {} taxRate
*/
public validateTaxRateExistance(taxRate: TaxRate | undefined | null) {
if (!taxRate) {
throw new ServiceError(ERRORS.TAX_RATE_NOT_FOUND);
}
}
}

View File

@@ -0,0 +1,53 @@
import { Knex } from 'knex';
import {
ICreateTaxRateDTO,
ITaxRateCreatedPayload,
ITaxRateCreatingPayload,
} from '@/interfaces';
import UnitOfWork from '../UnitOfWork';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import HasTenancyService from '../Tenancy/TenancyService';
import { Inject, Service } from 'typedi';
import events from '@/subscribers/events';
@Service()
export class CreateTaxRate {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private eventPublisher: EventPublisher;
@Inject()
private uow: UnitOfWork;
/**
* Creates a new tax rate.
* @param {number} tenantId
* @param {ICreateTaxRateDTO} createTaxRateDTO
*/
public createTaxRate(tenantId: number, createTaxRateDTO: ICreateTaxRateDTO) {
const { TaxRate } = this.tenancy.models(tenantId);
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Triggers `onTaxRateCreating` event.
await this.eventPublisher.emitAsync(events.taxRates.onCreating, {
createTaxRateDTO,
tenantId,
trx,
} as ITaxRateCreatingPayload);
const taxRate = await TaxRate.query(trx).insert({ ...createTaxRateDTO });
// Triggers `onTaxRateCreated` event.
await this.eventPublisher.emitAsync(events.taxRates.onCreated, {
createTaxRateDTO,
taxRate,
tenantId,
trx,
} as ITaxRateCreatedPayload);
return taxRate;
});
}
}

View File

@@ -0,0 +1,54 @@
import { Inject, Service } from 'typedi';
import { Knex } from 'knex';
import { ITaxRateDeletedPayload, ITaxRateDeletingPayload } from '@/interfaces';
import UnitOfWork from '../UnitOfWork';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import HasTenancyService from '../Tenancy/TenancyService';
import { CommandTaxRatesValidators } from './CommandTaxRatesValidators';
import events from '@/subscribers/events';
@Service()
export class DeleteTaxRateService {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private eventPublisher: EventPublisher;
@Inject()
private uow: UnitOfWork;
@Inject()
private validators: CommandTaxRatesValidators;
/**
*
* @param tenantId
* @param taxRateId
*/
public deleteTaxRate(tenantId: number, taxRateId: number) {
const { TaxRate } = this.tenancy.models(tenantId);
const oldTaxRate = TaxRate.query().findById(taxRateId);
this.validators.validateTaxRateExistance(oldTaxRate);
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Triggers `onSaleInvoiceCreating` event.
await this.eventPublisher.emitAsync(events.taxRates.onDeleting, {
oldTaxRate,
tenantId,
trx,
} as ITaxRateDeletingPayload);
await TaxRate.query(trx).findById(taxRateId).delete();
//
await this.eventPublisher.emitAsync(events.taxRates.onDeleted, {
oldTaxRate,
tenantId,
trx,
} as ITaxRateDeletedPayload);
});
}
}

View File

@@ -0,0 +1,68 @@
import {
IEditTaxRateDTO,
ITaxRateCreatingPayload,
ITaxRateEditedPayload,
ITaxRateEditingPayload,
} from '@/interfaces';
import { Inject } from 'typedi';
import UnitOfWork from '../UnitOfWork';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import HasTenancyService from '../Tenancy/TenancyService';
import { Knex } from 'knex';
import { CommandTaxRatesValidators } from './CommandTaxRatesValidators';
import events from '@/subscribers/events';
export class EditTaxRateService {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private eventPublisher: EventPublisher;
@Inject()
private uow: UnitOfWork;
@Inject()
private validators: CommandTaxRatesValidators;
/**
*
* @param {number} tenantId
* @param {number} taxRateId
* @param {IEditTaxRateDTO} taxRateEditDTO
*/
public editTaxRate(
tenantId: number,
taxRateId: number,
editTaxRateDTO: IEditTaxRateDTO
) {
const { TaxRate } = this.tenancy.models(tenantId);
const oldTaxRate = TaxRate.query().findById(taxRateId);
this.validators.validateTaxRateExistance(oldTaxRate);
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Triggers `onTaxRateCreating` event.
await this.eventPublisher.emitAsync(events.taxRates.onCreating, {
editTaxRateDTO,
tenantId,
trx,
} as ITaxRateEditingPayload);
const taxRate = await TaxRate.query(trx)
.findById(taxRateId)
.patch({ ...editTaxRateDTO });
// Triggers `onTaxRateCreated` event.
await this.eventPublisher.emitAsync(events.taxRates.onCreated, {
editTaxRateDTO,
taxRate,
tenantId,
trx,
} as ITaxRateEditedPayload);
return taxRate;
});
}
}

View File

@@ -0,0 +1,28 @@
import { Inject, Service } from 'typedi';
import HasTenancyService from '../Tenancy/TenancyService';
import { CommandTaxRatesValidators } from './CommandTaxRatesValidators';
@Service()
export class GetTaxRateService {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private validators: CommandTaxRatesValidators;
/**
*
* @param {number} tenantId
* @param {number} taxRateId
* @returns
*/
public async getTaxRate(tenantId: number, taxRateId: number) {
const { TaxRate } = this.tenancy.models(tenantId);
const taxRate = await TaxRate.query().findById(taxRateId);
this.validators.validateTaxRateExistance(taxRate);
return taxRate;
}
}

View File

@@ -0,0 +1,21 @@
import { Inject, Service } from 'typedi';
import HasTenancyService from '../Tenancy/TenancyService';
@Service()
export class GetTaxRatesService {
@Inject()
private tenancy: HasTenancyService;
/**
*
* @param {number} tenantId
* @returns
*/
public async getTaxRates(tenantId: number) {
const { TaxRate } = this.tenancy.models(tenantId);
const taxRates = await TaxRate.query();
return taxRates;
}
}

View File

@@ -0,0 +1,82 @@
import { Inject, Service } from 'typedi';
import { ICreateTaxRateDTO, IEditTaxRateDTO } from '@/interfaces';
import { CreateTaxRate } from './CreateTaxRate';
import { DeleteTaxRateService } from './DeleteTaxRate';
import { EditTaxRateService } from './EditTaxRate';
import { GetTaxRateService } from './GetTaxRate';
import { GetTaxRatesService } from './GetTaxRates';
@Service()
export class TaxRatesApplication {
@Inject()
private createTaxRateService: CreateTaxRate;
@Inject()
private editTaxRateService: EditTaxRateService;
@Inject()
private deleteTaxRateService: DeleteTaxRateService;
@Inject()
private getTaxRateService: GetTaxRateService;
@Inject()
private getTaxRatesService: GetTaxRatesService;
/**
* Creates a new tax rate.
* @param {number} tenantId
* @param {ICreateTaxRateDTO} createTaxRateDTO
* @returns
*/
public createTaxRate(tenantId: number, createTaxRateDTO: ICreateTaxRateDTO) {
return this.createTaxRateService.createTaxRate(tenantId, createTaxRateDTO);
}
/**
* Edits the given tax rate.
* @param {number} tenantId
* @param {number} taxRateId
* @param {IEditTaxRateDTO} taxRateEditDTO
*/
public editTaxRate(
tenantId: number,
taxRateId: number,
editTaxRateDTO: IEditTaxRateDTO
) {
return this.editTaxRateService.editTaxRate(
tenantId,
taxRateId,
editTaxRateDTO
);
}
/**
* Deletes the given tax rate.
* @param {number} tenantId
* @param {number} taxRateId
* @returns {Promise<void>}
*/
public deleteTaxRate(tenantId: number, taxRateId: number) {
return this.deleteTaxRateService.deleteTaxRate(tenantId, taxRateId);
}
/**
* Retrieves the given tax rate.
* @param {number} tenantId
* @param {number} taxRateId
* @returns
*/
public getTaxRate(tenantId: number, taxRateId: number) {
return this.getTaxRateService.getTaxRate(tenantId, taxRateId);
}
/**
* Retrieves the tax rates list.
* @param {number} tenantId
* @returns
*/
public getTaxRates(tenantId: number) {
return this.getTaxRatesService.getTaxRates(tenantId);
}
}

View File

@@ -0,0 +1,3 @@
const ERRORS = {
TAX_RATE_NOT_FOUND: 'TAX_RATE_NOT_FOUND',
};

View File

@@ -13,7 +13,7 @@ export default {
sendResetPassword: 'onSendResetPassword',
resetPassword: 'onResetPassword',
resetingPassword: 'onResetingPassword'
resetingPassword: 'onResetingPassword',
},
/**
@@ -557,4 +557,15 @@ export default {
onDeleting: 'onProjectTimeDeleting',
onDeleted: 'onProjectTimeDeleted',
},
taxRates: {
onCreating: 'onTaxRateCreating',
onCreated: 'onTaxRateCreated',
onEditing: 'onTaxRateEditing',
onEdited: 'onTaxRateEdited',
onDeleting: 'onTaxRateDeleting',
onDeleted: 'onTaxRateDeleted',
},
};