mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 21:00:31 +00:00
feat(server): wip activate/inactivate tax rate
This commit is contained in:
@@ -33,6 +33,20 @@ export class TaxRatesController extends BaseController {
|
||||
asyncMiddleware(this.editTaxRate.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id/active',
|
||||
[param('id').exists().toInt()],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.activateTaxRate.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id/inactive',
|
||||
[param('id').exists().toInt()],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.inactivateTaxRate.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
router.delete(
|
||||
'/:id',
|
||||
[param('id').exists().toInt()],
|
||||
@@ -64,7 +78,9 @@ export class TaxRatesController extends BaseController {
|
||||
body('name').exists(),
|
||||
body('code').exists().isString(),
|
||||
body('rate').exists().isNumeric().toFloat(),
|
||||
body('description').optional().trim().isString(),
|
||||
body('is_non_recoverable').optional().isBoolean().default(false),
|
||||
body('is_compound').optional().isBoolean().default(false),
|
||||
body('status').optional().toUpperCase().isIn(['ARCHIVED', 'ACTIVE']),
|
||||
];
|
||||
}
|
||||
@@ -173,6 +189,52 @@ export class TaxRatesController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inactivates the given tax rate.
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
* @returns
|
||||
*/
|
||||
public async inactivateTaxRate(req: Request, res: Response, next) {
|
||||
const { tenantId } = req;
|
||||
const { id: taxRateId } = req.params;
|
||||
|
||||
try {
|
||||
await this.taxRatesApplication.inactivateTaxRate(tenantId, taxRateId);
|
||||
|
||||
return res.status(200).send({
|
||||
id: taxRateId,
|
||||
message: 'The given tax rate has been inactivated successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inactivates the given tax rate.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @returns
|
||||
*/
|
||||
public async activateTaxRate(req: Request, res: Response, next) {
|
||||
const { tenantId } = req;
|
||||
const { id: taxRateId } = req.params;
|
||||
|
||||
try {
|
||||
await this.taxRatesApplication.activateTaxRate(tenantId, taxRateId);
|
||||
|
||||
return res.status(200).send({
|
||||
id: taxRateId,
|
||||
message: 'The given tax rate has been activated successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles service errors.
|
||||
* @param {Error} error
|
||||
@@ -197,6 +259,16 @@ export class TaxRatesController extends BaseController {
|
||||
errors: [{ type: ERRORS.TAX_RATE_NOT_FOUND, code: 200 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === ERRORS.TAX_RATE_ALREADY_INACTIVE) {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: ERRORS.TAX_RATE_ALREADY_INACTIVE, code: 300 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === ERRORS.TAX_RATE_ALREADY_ACTIVE) {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: ERRORS.TAX_RATE_ALREADY_ACTIVE, code: 400 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@ exports.up = (knex) => {
|
||||
table.string('name');
|
||||
table.string('code');
|
||||
table.decimal('rate');
|
||||
table.string('description');
|
||||
table.boolean('is_non_recoverable');
|
||||
table.boolean('is_compound');
|
||||
table.integer('status');
|
||||
table.boolean('active').defaultTo(false);
|
||||
table.date('deleted_at');
|
||||
table.timestamps();
|
||||
})
|
||||
.table('items_entries', (table) => {
|
||||
@@ -43,9 +45,6 @@ exports.up = (knex) => {
|
||||
.references('id')
|
||||
.inTable('tax_rates');
|
||||
table.decimal('tax_rate').unsigned();
|
||||
})
|
||||
.table('sales_invoices', (table) => {
|
||||
table.rename('balance', 'amount');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
exports.up = function (knex) {
|
||||
return knex.table('sales_invoices', (table) => {
|
||||
table.renameColumn('balance', 'amount');
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function (knex) {};
|
||||
@@ -5,14 +5,20 @@ export interface ITaxRate {
|
||||
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 {}
|
||||
@@ -47,6 +53,18 @@ export interface ITaxRateDeletingPayload {
|
||||
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: ITaxRate;
|
||||
tenantId: number;
|
||||
|
||||
64
packages/server/src/services/TaxRates/ActivateTaxRate.ts
Normal file
64
packages/server/src/services/TaxRates/ActivateTaxRate.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
ITaxRateActivatedPayload,
|
||||
ITaxRateActivatingPayload,
|
||||
} from '@/interfaces';
|
||||
import { Inject, Service } 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';
|
||||
|
||||
@Service()
|
||||
export class ActivateTaxRateService {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private validators: CommandTaxRatesValidators;
|
||||
|
||||
/**
|
||||
* Edits the given tax rate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} taxRateId
|
||||
* @param {IEditTaxRateDTO} taxRateEditDTO
|
||||
* @returns {Promise<ITaxRate>}
|
||||
*/
|
||||
public activateTaxRate(tenantId: number, taxRateId: number) {
|
||||
const { TaxRate } = this.tenancy.models(tenantId);
|
||||
|
||||
const oldTaxRate = TaxRate.query().findById(taxRateId);
|
||||
|
||||
// Validates the tax rate existance.
|
||||
this.validators.validateTaxRateExistance(oldTaxRate);
|
||||
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onTaxRateActivating` event.
|
||||
await this.eventPublisher.emitAsync(events.taxRates.onActivating, {
|
||||
taxRateId,
|
||||
tenantId,
|
||||
trx,
|
||||
} as ITaxRateActivatingPayload);
|
||||
|
||||
const taxRate = await TaxRate.query(trx)
|
||||
.findById(taxRateId)
|
||||
.patch({ active: 1 });
|
||||
|
||||
// Triggers `onTaxRateCreated` event.
|
||||
await this.eventPublisher.emitAsync(events.taxRates.onActivated, {
|
||||
taxRateId,
|
||||
tenantId,
|
||||
trx,
|
||||
} as ITaxRateActivatedPayload);
|
||||
|
||||
return taxRate;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,26 @@ export class CommandTaxRatesValidators {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given tax rate active.
|
||||
* @param {ITaxRate} taxRate
|
||||
*/
|
||||
public validateTaxRateNotActive(taxRate: ITaxRate) {
|
||||
if (taxRate.active) {
|
||||
throw new ServiceError(ERRORS.TAX_RATE_ALREADY_ACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given tax rate inactive.
|
||||
* @param {ITaxRate} taxRate
|
||||
*/
|
||||
public validateTaxRateNotInactive(taxRate: ITaxRate) {
|
||||
if (!taxRate.active) {
|
||||
throw new ServiceError(ERRORS.TAX_RATE_ALREADY_INACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the tax code uniquiness.
|
||||
* @param {number} tenantId
|
||||
|
||||
67
packages/server/src/services/TaxRates/InactivateTaxRate.ts
Normal file
67
packages/server/src/services/TaxRates/InactivateTaxRate.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import {
|
||||
ITaxRateActivatedPayload,
|
||||
ITaxRateActivatingPayload,
|
||||
} from '@/interfaces';
|
||||
import { Inject, Service } 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';
|
||||
|
||||
@Service()
|
||||
export class InactivateTaxRateService {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private validators: CommandTaxRatesValidators;
|
||||
|
||||
/**
|
||||
* Edits the given tax rate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} taxRateId
|
||||
* @param {IEditTaxRateDTO} taxRateEditDTO
|
||||
* @returns {Promise<ITaxRate>}
|
||||
*/
|
||||
public inactivateTaxRate(tenantId: number, taxRateId: number) {
|
||||
const { TaxRate } = this.tenancy.models(tenantId);
|
||||
|
||||
const oldTaxRate = TaxRate.query().findById(taxRateId);
|
||||
|
||||
// Validates the tax rate existance.
|
||||
this.validators.validateTaxRateExistance(oldTaxRate);
|
||||
|
||||
//
|
||||
this.validators.validateTaxRateNotInactive(oldTaxRate);
|
||||
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onTaxRateActivating` event.
|
||||
await this.eventPublisher.emitAsync(events.taxRates.onInactivating, {
|
||||
taxRateId,
|
||||
tenantId,
|
||||
trx,
|
||||
} as ITaxRateActivatingPayload);
|
||||
|
||||
const taxRate = await TaxRate.query(trx)
|
||||
.findById(taxRateId)
|
||||
.patch({ active: 0 });
|
||||
|
||||
// Triggers `onTaxRateCreated` event.
|
||||
await this.eventPublisher.emitAsync(events.taxRates.onInactivated, {
|
||||
taxRateId,
|
||||
tenantId,
|
||||
trx,
|
||||
} as ITaxRateActivatedPayload);
|
||||
|
||||
return taxRate;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import { DeleteTaxRateService } from './DeleteTaxRate';
|
||||
import { EditTaxRateService } from './EditTaxRate';
|
||||
import { GetTaxRateService } from './GetTaxRate';
|
||||
import { GetTaxRatesService } from './GetTaxRates';
|
||||
import { ActivateTaxRateService } from './ActivateTaxRate';
|
||||
import { InactivateTaxRateService } from './InactivateTaxRate';
|
||||
|
||||
@Service()
|
||||
export class TaxRatesApplication {
|
||||
@@ -23,6 +25,12 @@ export class TaxRatesApplication {
|
||||
@Inject()
|
||||
private getTaxRatesService: GetTaxRatesService;
|
||||
|
||||
@Inject()
|
||||
private activateTaxRateService: ActivateTaxRateService;
|
||||
|
||||
@Inject()
|
||||
private inactivateTaxRateService: InactivateTaxRateService;
|
||||
|
||||
/**
|
||||
* Creates a new tax rate.
|
||||
* @param {number} tenantId
|
||||
@@ -80,4 +88,22 @@ export class TaxRatesApplication {
|
||||
public getTaxRates(tenantId: number) {
|
||||
return this.getTaxRatesService.getTaxRates(tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the given tax rate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} taxRateId
|
||||
*/
|
||||
public activateTaxRate(tenantId: number, taxRateId: number) {
|
||||
return this.activateTaxRateService.activateTaxRate(tenantId, taxRateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inactivates the given tax rate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} taxRateId
|
||||
*/
|
||||
public inactivateTaxRate(tenantId: number, taxRateId: number) {
|
||||
return this.inactivateTaxRateService.inactivateTaxRate(tenantId, taxRateId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,6 @@ export const ERRORS = {
|
||||
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'
|
||||
};
|
||||
|
||||
@@ -570,5 +570,11 @@ export default {
|
||||
|
||||
onDeleting: 'onTaxRateDeleting',
|
||||
onDeleted: 'onTaxRateDeleted',
|
||||
|
||||
onActivating: 'onTaxRateActivating',
|
||||
onActivated: 'onTaxRateActivated',
|
||||
|
||||
onInactivating: 'onTaxRateInactivating',
|
||||
onInactivated: 'onTaxRateInactivated'
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user