mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
feat(server): wip tax rates service
This commit is contained in:
108
packages/server/src/api/controllers/TaxRates/TaxRates.ts
Normal file
108
packages/server/src/api/controllers/TaxRates/TaxRates.ts
Normal 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) {}
|
||||
}
|
||||
@@ -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');
|
||||
};
|
||||
48
packages/server/src/interfaces/TaxRate.ts
Normal file
48
packages/server/src/interfaces/TaxRate.ts
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
};
|
||||
|
||||
40
packages/server/src/models/TaxRate.ts
Normal file
40
packages/server/src/models/TaxRate.ts
Normal 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 {};
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
packages/server/src/services/TaxRates/CreateTaxRate.ts
Normal file
53
packages/server/src/services/TaxRates/CreateTaxRate.ts
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
54
packages/server/src/services/TaxRates/DeleteTaxRate.ts
Normal file
54
packages/server/src/services/TaxRates/DeleteTaxRate.ts
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
68
packages/server/src/services/TaxRates/EditTaxRate.ts
Normal file
68
packages/server/src/services/TaxRates/EditTaxRate.ts
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
28
packages/server/src/services/TaxRates/GetTaxRate.ts
Normal file
28
packages/server/src/services/TaxRates/GetTaxRate.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
21
packages/server/src/services/TaxRates/GetTaxRates.ts
Normal file
21
packages/server/src/services/TaxRates/GetTaxRates.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
82
packages/server/src/services/TaxRates/TaxRatesApplication.ts
Normal file
82
packages/server/src/services/TaxRates/TaxRatesApplication.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
3
packages/server/src/services/TaxRates/constants.ts
Normal file
3
packages/server/src/services/TaxRates/constants.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
const ERRORS = {
|
||||
TAX_RATE_NOT_FOUND: 'TAX_RATE_NOT_FOUND',
|
||||
};
|
||||
@@ -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',
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user