mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 12:20:31 +00:00
refactor: api validation schema
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
import {
|
||||
PipeTransform,
|
||||
Injectable,
|
||||
ArgumentMetadata,
|
||||
BadRequestException,
|
||||
} from '@nestjs/common';
|
||||
import { validate } from 'class-validator';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
|
||||
@Injectable()
|
||||
export class ValidationPipe implements PipeTransform<any> {
|
||||
async transform(value: any, { metatype }: ArgumentMetadata) {
|
||||
if (!metatype || !this.toValidate(metatype)) {
|
||||
return value;
|
||||
}
|
||||
const object = plainToInstance(metatype, value);
|
||||
const errors = await validate(object);
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new BadRequestException(errors);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private toValidate(metatype: Function): boolean {
|
||||
const types: Function[] = [String, Boolean, Number, Array, Object];
|
||||
return !types.includes(metatype);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { ClsMiddleware } from 'nestjs-cls';
|
||||
import './utils/moment-mysql';
|
||||
import { AppModule } from './modules/App/App.module';
|
||||
import { ServiceErrorFilter } from './common/filters/service-error.filter';
|
||||
import { ValidationPipe } from './common/pipes/ClassValidation.pipe';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
@@ -12,6 +13,9 @@ async function bootstrap() {
|
||||
// create and mount the middleware manually here
|
||||
app.use(new ClsMiddleware({}).use);
|
||||
|
||||
// use the validation pipe globally
|
||||
app.useGlobalPipes(new ValidationPipe());
|
||||
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Bigcapital')
|
||||
.setDescription('Financial accounting software')
|
||||
|
||||
@@ -14,8 +14,6 @@ import { EditAccountDTO } from './EditAccount.dto';
|
||||
import { PublicRoute } from '../Auth/Jwt.guard';
|
||||
import { IAccountsFilter, IAccountsTransactionsFilter } from './Accounts.types';
|
||||
import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
// import { IAccountsFilter, IAccountsTransactionsFilter } from './Accounts.types';
|
||||
// import { ZodValidationPipe } from '@/common/pipes/ZodValidation.pipe';
|
||||
|
||||
@Controller('accounts')
|
||||
@ApiTags('accounts')
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Param,
|
||||
} from '@nestjs/common';
|
||||
import { BranchesApplication } from './BranchesApplication.service';
|
||||
import { ICreateBranchDTO, IEditBranchDTO } from './Branches.types';
|
||||
import { CreateBranchDto, EditBranchDto } from './dtos/Branch.dto';
|
||||
import { PublicRoute } from '../Auth/Jwt.guard';
|
||||
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
|
||||
@@ -46,7 +46,7 @@ export class BranchesController {
|
||||
description: 'The branch has been successfully created.',
|
||||
})
|
||||
@ApiResponse({ status: 404, description: 'The branch not found.' })
|
||||
createBranch(@Body() createBranchDTO: ICreateBranchDTO) {
|
||||
createBranch(@Body() createBranchDTO: CreateBranchDto) {
|
||||
return this.branchesApplication.createBranch(createBranchDTO);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export class BranchesController {
|
||||
description: 'The branch has been successfully edited.',
|
||||
})
|
||||
@ApiResponse({ status: 404, description: 'The branch not found.' })
|
||||
editBranch(@Param('id') id: string, @Body() editBranchDTO: IEditBranchDTO) {
|
||||
editBranch(@Param('id') id: string, @Body() editBranchDTO: EditBranchDto) {
|
||||
return this.branchesApplication.editBranch(Number(id), editBranchDTO);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import { GetBranchesService } from './queries/GetBranches.service';
|
||||
import { MarkBranchAsPrimaryService } from './commands/MarkBranchAsPrimary.service';
|
||||
import { Branch } from './models/Branch.model';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateBranchDto, EditBranchDto } from './dtos/Branch.dto';
|
||||
|
||||
@Injectable()
|
||||
export class BranchesApplication {
|
||||
@@ -27,8 +28,7 @@ export class BranchesApplication {
|
||||
|
||||
/**
|
||||
* Retrieves branches list.
|
||||
* @param {number} tenantId
|
||||
* @returns {IBranch}
|
||||
* @returns {Branch[]}
|
||||
*/
|
||||
public getBranches = (): Promise<Branch[]> => {
|
||||
return this.getBranchesService.getBranches();
|
||||
@@ -50,7 +50,7 @@ export class BranchesApplication {
|
||||
* @returns {Promise<IBranch>}
|
||||
*/
|
||||
public createBranch = (
|
||||
createBranchDTO: ICreateBranchDTO,
|
||||
createBranchDTO: CreateBranchDto,
|
||||
): Promise<Branch> => {
|
||||
return this.createBranchService.createBranch(createBranchDTO);
|
||||
};
|
||||
@@ -63,7 +63,7 @@ export class BranchesApplication {
|
||||
*/
|
||||
public editBranch = (
|
||||
branchId: number,
|
||||
editBranchDTO: IEditBranchDTO,
|
||||
editBranchDTO: EditBranchDto,
|
||||
): Promise<Branch> => {
|
||||
return this.editBranchService.editBranch(branchId, editBranchDTO);
|
||||
};
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
IBranchCreatedPayload,
|
||||
IBranchCreatePayload,
|
||||
ICreateBranchDTO,
|
||||
} from '../Branches.types';
|
||||
import { IBranchCreatedPayload, IBranchCreatePayload } from '../Branches.types';
|
||||
import { UnitOfWork } from '../../Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { Branch } from '../models/Branch.model';
|
||||
import { events } from '@/common/events/events';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { CreateBranchDto } from '../dtos/Branch.dto';
|
||||
|
||||
@Injectable()
|
||||
export class CreateBranchService {
|
||||
@@ -28,11 +25,11 @@ export class CreateBranchService {
|
||||
|
||||
/**
|
||||
* Creates a new branch.
|
||||
* @param {ICreateBranchDTO} createBranchDTO
|
||||
* @returns {Promise<IBranch>}
|
||||
* @param {CreateBranchDto} createBranchDTO
|
||||
* @returns {Promise<Branch>}
|
||||
*/
|
||||
public createBranch = async (
|
||||
createBranchDTO: ICreateBranchDTO,
|
||||
createBranchDTO: CreateBranchDto,
|
||||
): Promise<Branch> => {
|
||||
// Creates a new branch under unit-of-work.
|
||||
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
IBranchEditedPayload,
|
||||
IBranchEditPayload,
|
||||
IEditBranchDTO,
|
||||
} from '../Branches.types';
|
||||
import { IBranchEditedPayload, IBranchEditPayload } from '../Branches.types';
|
||||
import { Branch } from '../models/Branch.model';
|
||||
import { UnitOfWork } from '../../Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { EditBranchDto } from '../dtos/Branch.dto';
|
||||
|
||||
@Injectable()
|
||||
export class EditBranchService {
|
||||
@@ -27,7 +24,7 @@ export class EditBranchService {
|
||||
*/
|
||||
public editBranch = async (
|
||||
branchId: number,
|
||||
editBranchDTO: IEditBranchDTO,
|
||||
editBranchDTO: EditBranchDto,
|
||||
) => {
|
||||
// Retrieves the old branch or throw not found service error.
|
||||
const oldBranch = await this.branchModel()
|
||||
|
||||
55
packages/server-nest/src/modules/Branches/dtos/Branch.dto.ts
Normal file
55
packages/server-nest/src/modules/Branches/dtos/Branch.dto.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import {
|
||||
IsEmail,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUrl,
|
||||
} from 'class-validator';
|
||||
|
||||
class CommandBranchDto {
|
||||
@ApiProperty({ description: 'Branch name' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Branch code' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
code?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Branch address' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
address?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Branch city' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
city?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Branch country' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
country?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Branch phone number' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone_number?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Branch email' })
|
||||
@IsOptional()
|
||||
@IsEmail()
|
||||
@IsString()
|
||||
email?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Branch website' })
|
||||
@IsOptional()
|
||||
@IsUrl()
|
||||
@IsString()
|
||||
website?: string;
|
||||
}
|
||||
|
||||
export class CreateBranchDto extends CommandBranchDto {}
|
||||
export class EditBranchDto extends CommandBranchDto {}
|
||||
@@ -0,0 +1,3 @@
|
||||
export class CreateExpenseDto {}
|
||||
|
||||
export class EditExpenseDto {}
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
|
||||
export class CreateItemCategoryDto {}
|
||||
|
||||
export class EditItemCategoryDto {}
|
||||
@@ -8,6 +8,7 @@ import { ItemsValidators } from './ItemValidator.service';
|
||||
import { Item } from './models/Item';
|
||||
import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
import { CreateItemDto } from './dtos/Item.dto';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class CreateItemService {
|
||||
@@ -32,7 +33,7 @@ export class CreateItemService {
|
||||
* @param {number} tenantId
|
||||
* @param {IItemDTO} itemDTO
|
||||
*/
|
||||
async authorize(itemDTO: IItemDTO) {
|
||||
async authorize(itemDTO: CreateItemDto) {
|
||||
// Validate whether the given item name already exists on the storage.
|
||||
await this.validators.validateItemNameUniquiness(itemDTO.name);
|
||||
|
||||
@@ -76,10 +77,10 @@ export class CreateItemService {
|
||||
|
||||
/**
|
||||
* Transforms the item DTO to model.
|
||||
* @param {IItemDTO} itemDTO - Item DTO.
|
||||
* @param {CreateItemDto} itemDTO - Item DTO.
|
||||
* @return {IItem}
|
||||
*/
|
||||
private transformNewItemDTOToModel(itemDTO: IItemDTO) {
|
||||
private transformNewItemDTOToModel(itemDTO: CreateItemDto) {
|
||||
return {
|
||||
...itemDTO,
|
||||
active: defaultTo(itemDTO.active, 1),
|
||||
@@ -93,7 +94,7 @@ export class CreateItemService {
|
||||
* @return {Promise<number>} - The created item id.
|
||||
*/
|
||||
public async createItem(
|
||||
itemDTO: IItemDTO,
|
||||
itemDTO: CreateItemDto,
|
||||
trx?: Knex.Transaction,
|
||||
): Promise<number> {
|
||||
// Authorize the item before creating.
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ItemsValidators } from './ItemValidator.service';
|
||||
import { Item } from './models/Item';
|
||||
import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
import { EditItemDto } from './dtos/Item.dto';
|
||||
|
||||
@Injectable()
|
||||
export class EditItemService {
|
||||
@@ -31,7 +32,7 @@ export class EditItemService {
|
||||
* @param {IItemDTO} itemDTO
|
||||
* @param {Item} oldItem
|
||||
*/
|
||||
async authorize(itemDTO: IItemDTO, oldItem: Item) {
|
||||
async authorize(itemDTO: EditItemDto, oldItem: Item) {
|
||||
// Validate edit item type from inventory type.
|
||||
this.validators.validateEditItemFromInventory(itemDTO, oldItem);
|
||||
|
||||
@@ -86,7 +87,7 @@ export class EditItemService {
|
||||
* @return {Partial<Item>}
|
||||
*/
|
||||
private transformEditItemDTOToModel(
|
||||
itemDTO: IItemDTO,
|
||||
itemDTO: EditItemDto,
|
||||
oldItem: Item,
|
||||
): Partial<Item> {
|
||||
return {
|
||||
@@ -107,7 +108,7 @@ export class EditItemService {
|
||||
*/
|
||||
public async editItem(
|
||||
itemId: number,
|
||||
itemDTO: IItemDTO,
|
||||
itemDTO: EditItemDto,
|
||||
trx?: Knex.Transaction,
|
||||
): Promise<number> {
|
||||
// Validates the given item existance on the storage.
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
} from '@nestjs/swagger';
|
||||
import { IItemsFilter } from './types/Items.types';
|
||||
import { IItemDTO } from '@/interfaces/Item';
|
||||
import { CreateItemDto, EditItemDto } from './dtos/Item.dto';
|
||||
|
||||
@Controller('/items')
|
||||
@UseGuards(SubscriptionGuard)
|
||||
@@ -120,7 +121,7 @@ export class ItemsController extends TenantController {
|
||||
// @UsePipes(new ZodValidationPipe(createItemSchema))
|
||||
async editItem(
|
||||
@Param('id') id: string,
|
||||
@Body() editItemDto: IItemDTO,
|
||||
@Body() editItemDto: EditItemDto,
|
||||
): Promise<number> {
|
||||
const itemId = parseInt(id, 10);
|
||||
return this.itemsApplication.editItem(itemId, editItemDto);
|
||||
@@ -138,7 +139,7 @@ export class ItemsController extends TenantController {
|
||||
description: 'The item has been successfully created.',
|
||||
})
|
||||
// @UsePipes(new ZodValidationPipe(createItemSchema))
|
||||
async createItem(@Body() createItemDto: IItemDTO): Promise<number> {
|
||||
async createItem(@Body() createItemDto: CreateItemDto): Promise<number> {
|
||||
return this.itemsApplication.createItem(createItemDto);
|
||||
}
|
||||
|
||||
@@ -182,8 +183,6 @@ export class ItemsController extends TenantController {
|
||||
description: 'The item id',
|
||||
})
|
||||
async inactivateItem(@Param('id') id: string): Promise<void> {
|
||||
console.log(id, 'XXXXXX');
|
||||
|
||||
const itemId = parseInt(id, 10);
|
||||
return this.itemsApplication.inactivateItem(itemId);
|
||||
}
|
||||
@@ -243,7 +242,8 @@ export class ItemsController extends TenantController {
|
||||
})
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'The item associated invoices transactions have been successfully retrieved.',
|
||||
description:
|
||||
'The item associated invoices transactions have been successfully retrieved.',
|
||||
})
|
||||
@ApiResponse({ status: 404, description: 'The item not found.' })
|
||||
@ApiParam({
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
ACCOUNT_TYPE,
|
||||
} from '@/constants/accounts';
|
||||
import { ServiceError } from './ServiceError';
|
||||
import { IItemDTO } from '@/interfaces/Item';
|
||||
import { ERRORS } from './Items.constants';
|
||||
import { Item } from './models/Item';
|
||||
import { Account } from '../Accounts/models/Account.model';
|
||||
@@ -15,6 +14,7 @@ import { ItemCategory } from '../ItemCategories/models/ItemCategory.model';
|
||||
import { AccountTransaction } from '../Accounts/models/AccountTransaction.model';
|
||||
import { InventoryAdjustment } from '../InventoryAdjutments/models/InventoryAdjustment';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
import { CreateItemDto, EditItemDto } from './dtos/Item.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ItemsValidators {
|
||||
@@ -229,7 +229,7 @@ export class ItemsValidators {
|
||||
*/
|
||||
public async validateEditItemTypeToInventory(
|
||||
oldItem: Item,
|
||||
newItemDTO: IItemDTO,
|
||||
newItemDTO: CreateItemDto | EditItemDto,
|
||||
) {
|
||||
// We have no problem in case the item type not modified.
|
||||
if (newItemDTO.type === oldItem.type || oldItem.type === 'inventory') {
|
||||
@@ -254,12 +254,12 @@ export class ItemsValidators {
|
||||
* Validate the item inventory account whether modified and item
|
||||
* has associated inventory transactions.
|
||||
* @param {Item} oldItem - Old item.
|
||||
* @param {IItemDTO} newItemDTO - New item DTO.
|
||||
* @param {CreateItemDto | EditItemDto} newItemDTO - New item DTO.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async validateItemInvnetoryAccountModified(
|
||||
oldItem: Item,
|
||||
newItemDTO: IItemDTO,
|
||||
newItemDTO: CreateItemDto | EditItemDto,
|
||||
) {
|
||||
if (
|
||||
newItemDTO.type !== 'inventory' ||
|
||||
@@ -279,10 +279,13 @@ export class ItemsValidators {
|
||||
|
||||
/**
|
||||
* Validate edit item type from inventory to another type that not allowed.
|
||||
* @param {IItemDTO} itemDTO - Item DTO.
|
||||
* @param {CreateItemDto | EditItemDto} itemDTO - Item DTO.
|
||||
* @param {IItem} oldItem - Old item.
|
||||
*/
|
||||
public validateEditItemFromInventory(itemDTO: IItemDTO, oldItem: Item) {
|
||||
public validateEditItemFromInventory(
|
||||
itemDTO: CreateItemDto | EditItemDto,
|
||||
oldItem: Item,
|
||||
) {
|
||||
if (
|
||||
itemDTO.type &&
|
||||
oldItem.type === 'inventory' &&
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ItemTransactionsService } from './ItemTransactions.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { GetItemsService } from './GetItems.service';
|
||||
import { IItemsFilter } from './types/Items.types';
|
||||
import { EditItemDto, CreateItemDto } from './dtos/Item.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ItemsApplicationService {
|
||||
@@ -32,7 +33,7 @@ export class ItemsApplicationService {
|
||||
* @return {Promise<number>} - The created item id.
|
||||
*/
|
||||
async createItem(
|
||||
createItemDto: IItemDTO,
|
||||
createItemDto: CreateItemDto,
|
||||
trx?: Knex.Transaction,
|
||||
): Promise<number> {
|
||||
return this.createItemService.createItem(createItemDto);
|
||||
@@ -47,7 +48,7 @@ export class ItemsApplicationService {
|
||||
*/
|
||||
async editItem(
|
||||
itemId: number,
|
||||
editItemDto: IItemDTO,
|
||||
editItemDto: EditItemDto,
|
||||
trx?: Knex.Transaction,
|
||||
): Promise<number> {
|
||||
return this.editItemService.editItem(itemId, editItemDto, trx);
|
||||
|
||||
109
packages/server-nest/src/modules/Items/dtos/Item.dto.ts
Normal file
109
packages/server-nest/src/modules/Items/dtos/Item.dto.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import {
|
||||
IsString,
|
||||
IsIn,
|
||||
IsOptional,
|
||||
IsBoolean,
|
||||
IsNumber,
|
||||
IsInt,
|
||||
IsArray,
|
||||
ValidateIf,
|
||||
MaxLength,
|
||||
Min,
|
||||
Max,
|
||||
IsNotEmpty,
|
||||
} from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class CommandItemDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(255)
|
||||
name: string;
|
||||
|
||||
@IsString()
|
||||
@IsIn(['service', 'non-inventory', 'inventory'])
|
||||
type: 'service' | 'non-inventory' | 'inventory';
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(255)
|
||||
code?: string;
|
||||
|
||||
// Purchase attributes
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
purchasable?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber({ maxDecimalPlaces: 3 })
|
||||
@Min(0)
|
||||
@ValidateIf((o) => o.purchasable === true)
|
||||
costPrice?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@ValidateIf((o) => o.purchasable === true)
|
||||
costAccountId?: number;
|
||||
|
||||
// Sell attributes
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
sellable?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber({ maxDecimalPlaces: 3 })
|
||||
@Min(0)
|
||||
@ValidateIf((o) => o.sellable === true)
|
||||
sellPrice?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@ValidateIf((o) => o.sellable === true)
|
||||
sellAccountId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@ValidateIf((o) => o.type === 'inventory')
|
||||
inventoryAccountId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
sellDescription?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
purchaseDescription?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
sellTaxRateId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
purchaseTaxRateId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
categoryId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
note?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
active?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@Type(() => Number)
|
||||
@IsInt({ each: true })
|
||||
mediaIds?: number[];
|
||||
}
|
||||
|
||||
export class CreateItemDto extends CommandItemDto {}
|
||||
export class EditItemDto extends CommandItemDto {}
|
||||
@@ -11,6 +11,7 @@ import { ManualJournalsApplication } from './ManualJournalsApplication.service';
|
||||
import { IManualJournalDTO } from './types/ManualJournals.types';
|
||||
import { PublicRoute } from '../Auth/Jwt.guard';
|
||||
import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { CreateManualJournalDto, EditManualJournalDto } from './dtos/ManualJournal.dto';
|
||||
|
||||
@Controller('manual-journals')
|
||||
@ApiTags('manual-journals')
|
||||
@@ -20,7 +21,7 @@ export class ManualJournalsController {
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: 'Create a new manual journal.' })
|
||||
public createManualJournal(@Body() manualJournalDTO: IManualJournalDTO) {
|
||||
public createManualJournal(@Body() manualJournalDTO: CreateManualJournalDto) {
|
||||
return this.manualJournalsApplication.createManualJournal(manualJournalDTO);
|
||||
}
|
||||
|
||||
@@ -39,7 +40,7 @@ export class ManualJournalsController {
|
||||
})
|
||||
public editManualJournal(
|
||||
@Param('id') manualJournalId: number,
|
||||
@Body() manualJournalDTO: IManualJournalDTO,
|
||||
@Body() manualJournalDTO: EditManualJournalDto
|
||||
) {
|
||||
return this.manualJournalsApplication.editManualJournal(
|
||||
manualJournalId,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { PublishManualJournal } from './commands/PublishManualJournal.service';
|
||||
import { GetManualJournal } from './queries/GetManualJournal.service';
|
||||
import { DeleteManualJournalService } from './commands/DeleteManualJournal.service';
|
||||
import { IManualJournalDTO, } from './types/ManualJournals.types';
|
||||
import { CreateManualJournalDto, EditManualJournalDto } from './dtos/ManualJournal.dto';
|
||||
// import { GetManualJournals } from './queries/GetManualJournals';
|
||||
|
||||
@Injectable()
|
||||
@@ -24,7 +25,7 @@ export class ManualJournalsApplication {
|
||||
* @param {IManualJournalDTO} manualJournalDTO
|
||||
* @returns {Promise<IManualJournal>}
|
||||
*/
|
||||
public createManualJournal = (manualJournalDTO: IManualJournalDTO) => {
|
||||
public createManualJournal = (manualJournalDTO: CreateManualJournalDto) => {
|
||||
return this.createManualJournalService.makeJournalEntries(manualJournalDTO);
|
||||
};
|
||||
|
||||
@@ -35,7 +36,7 @@ export class ManualJournalsApplication {
|
||||
*/
|
||||
public editManualJournal = (
|
||||
manualJournalId: number,
|
||||
manualJournalDTO: IManualJournalDTO,
|
||||
manualJournalDTO: EditManualJournalDto,
|
||||
) => {
|
||||
return this.editManualJournalService.editJournalEntries(
|
||||
manualJournalId,
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { difference, isEmpty, round, sumBy } from 'lodash';
|
||||
import {
|
||||
IManualJournalDTO,
|
||||
IManualJournalEntryDTO,
|
||||
} from '../types/ManualJournals.types';
|
||||
import { ERRORS } from '../constants';
|
||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||
import { ManualJournal } from '../models/ManualJournal';
|
||||
import { Contact } from '@/modules/Contacts/models/Contact';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import {
|
||||
CreateManualJournalDto,
|
||||
EditManualJournalDto,
|
||||
ManualJournalEntryDto,
|
||||
} from '../dtos/ManualJournal.dto';
|
||||
|
||||
@Injectable()
|
||||
export class CommandManualJournalValidators {
|
||||
@@ -26,9 +27,11 @@ export class CommandManualJournalValidators {
|
||||
|
||||
/**
|
||||
* Validate manual journal credit and debit should be equal.
|
||||
* @param {IManualJournalDTO} manualJournalDTO
|
||||
* @param {CreateManualJournalDto | EditManualJournalDto} manualJournalDTO
|
||||
*/
|
||||
public valdiateCreditDebitTotalEquals(manualJournalDTO: IManualJournalDTO) {
|
||||
public valdiateCreditDebitTotalEquals(
|
||||
manualJournalDTO: CreateManualJournalDto | EditManualJournalDto,
|
||||
) {
|
||||
const totalCredit = round(
|
||||
sumBy(manualJournalDTO.entries, (entry) => entry.credit || 0),
|
||||
2,
|
||||
@@ -48,9 +51,11 @@ export class CommandManualJournalValidators {
|
||||
/**
|
||||
* Validate manual entries accounts existance on the storage.
|
||||
* @param {number} tenantId -
|
||||
* @param {IManualJournalDTO} manualJournalDTO -
|
||||
* @param {CreateManualJournalDto | EditManualJournalDto} manualJournalDTO -
|
||||
*/
|
||||
public async validateAccountsExistance(manualJournalDTO: IManualJournalDTO) {
|
||||
public async validateAccountsExistance(
|
||||
manualJournalDTO: CreateManualJournalDto | EditManualJournalDto,
|
||||
) {
|
||||
const manualAccountsIds = manualJournalDTO.entries.map((e) => e.accountId);
|
||||
const accounts = await this.accountModel()
|
||||
.query()
|
||||
@@ -66,7 +71,7 @@ export class CommandManualJournalValidators {
|
||||
/**
|
||||
* Validate manual journal number unique.
|
||||
* @param {number} tenantId
|
||||
* @param {IManualJournalDTO} manualJournalDTO
|
||||
* @param {CreateManualJournalDto | EditManualJournalDto} manualJournalDTO
|
||||
*/
|
||||
public async validateManualJournalNoUnique(
|
||||
journalNumber: string,
|
||||
@@ -91,12 +96,12 @@ export class CommandManualJournalValidators {
|
||||
/**
|
||||
* Validate accounts with contact type.
|
||||
* @param {number} tenantId
|
||||
* @param {IManualJournalDTO} manualJournalDTO
|
||||
* @param {CreateManualJournalDto | EditManualJournalDto} manualJournalDTO
|
||||
* @param {string} accountBySlug
|
||||
* @param {string} contactType
|
||||
*/
|
||||
public async validateAccountWithContactType(
|
||||
entriesDTO: IManualJournalEntryDTO[],
|
||||
entriesDTO: ManualJournalEntryDto[],
|
||||
accountBySlug: string,
|
||||
contactType: string,
|
||||
): Promise<void | ServiceError> {
|
||||
@@ -147,10 +152,10 @@ export class CommandManualJournalValidators {
|
||||
/**
|
||||
* Dynamic validates accounts with contacts.
|
||||
* @param {number} tenantId
|
||||
* @param {IManualJournalDTO} manualJournalDTO
|
||||
* @param {CreateManualJournalDto | EditManualJournalDto} manualJournalDTO
|
||||
*/
|
||||
public async dynamicValidateAccountsWithContactType(
|
||||
entriesDTO: IManualJournalEntryDTO[],
|
||||
entriesDTO: ManualJournalEntryDto[],
|
||||
): Promise<any> {
|
||||
return Promise.all([
|
||||
this.validateAccountWithContactType(
|
||||
@@ -182,9 +187,11 @@ export class CommandManualJournalValidators {
|
||||
|
||||
/**
|
||||
* Validate entries contacts existance.
|
||||
* @param {IManualJournalDTO} manualJournalDTO
|
||||
* @param {CreateManualJournalDto | EditManualJournalDto} manualJournalDTO
|
||||
*/
|
||||
public async validateContactsExistance(manualJournalDTO: IManualJournalDTO) {
|
||||
public async validateContactsExistance(
|
||||
manualJournalDTO: CreateManualJournalDto | EditManualJournalDto,
|
||||
) {
|
||||
// Filters the entries that have contact only.
|
||||
const entriesContactPairs = manualJournalDTO.entries.filter(
|
||||
(entry) => entry.contactId,
|
||||
@@ -268,10 +275,10 @@ export class CommandManualJournalValidators {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IManualJournalDTO} manualJournalDTO
|
||||
* @param {CreateManualJournalDto | EditManualJournalDto} manualJournalDTO
|
||||
*/
|
||||
public validateJournalCurrencyWithAccountsCurrency = async (
|
||||
manualJournalDTO: IManualJournalDTO,
|
||||
manualJournalDTO: CreateManualJournalDto | EditManualJournalDto,
|
||||
baseCurrency: string,
|
||||
) => {
|
||||
const accountsIds = manualJournalDTO.entries.map((e) => e.accountId);
|
||||
|
||||
@@ -18,6 +18,7 @@ import { assocItemEntriesDefaultIndex } from '@/utils/associate-item-entries-ind
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
import { ManualJournalBranchesDTOTransformer } from '@/modules/Branches/integrations/ManualJournals/ManualJournalDTOTransformer.service';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { CreateManualJournalDto } from '../dtos/ManualJournal.dto';
|
||||
|
||||
@Injectable()
|
||||
export class CreateManualJournalService {
|
||||
@@ -39,7 +40,7 @@ export class CreateManualJournalService {
|
||||
* @returns {Promise<ManualJournal>}
|
||||
*/
|
||||
private async transformNewDTOToModel(
|
||||
manualJournalDTO: IManualJournalDTO,
|
||||
manualJournalDTO: CreateManualJournalDto,
|
||||
): Promise<ManualJournal> {
|
||||
const amount = sumBy(manualJournalDTO.entries, 'credit') || 0;
|
||||
const date = moment(manualJournalDTO.date).format('YYYY-MM-DD');
|
||||
@@ -84,7 +85,7 @@ export class CreateManualJournalService {
|
||||
* @param {IManualJournalDTO} manualJournalDTO
|
||||
* @param {ISystemUser} authorizedUser
|
||||
*/
|
||||
private authorize = async (manualJournalDTO: IManualJournalDTO) => {
|
||||
private authorize = async (manualJournalDTO: CreateManualJournalDto) => {
|
||||
const tenant = await this.tenancyContext.getTenant(true);
|
||||
|
||||
// Validate the total credit should equals debit.
|
||||
@@ -124,7 +125,7 @@ export class CreateManualJournalService {
|
||||
* @param {ISystemUser} authorizedUser
|
||||
*/
|
||||
public makeJournalEntries = async (
|
||||
manualJournalDTO: IManualJournalDTO,
|
||||
manualJournalDTO: CreateManualJournalDto,
|
||||
trx?: Knex.Transaction,
|
||||
): Promise<ManualJournal> => {
|
||||
// Authorize manual journal creating.
|
||||
|
||||
@@ -13,6 +13,7 @@ import { CommandManualJournalValidators } from './CommandManualJournalValidators
|
||||
import { events } from '@/common/events/events';
|
||||
import { ManualJournal } from '../models/ManualJournal';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { EditManualJournalDto } from '../dtos/ManualJournal.dto';
|
||||
|
||||
@Injectable()
|
||||
export class EditManualJournal {
|
||||
@@ -32,7 +33,7 @@ export class EditManualJournal {
|
||||
*/
|
||||
private authorize = async (
|
||||
manualJournalId: number,
|
||||
manualJournalDTO: IManualJournalDTO,
|
||||
manualJournalDTO: EditManualJournalDto,
|
||||
) => {
|
||||
// Validates the total credit and debit to be equals.
|
||||
this.validator.valdiateCreditDebitTotalEquals(manualJournalDTO);
|
||||
@@ -62,7 +63,7 @@ export class EditManualJournal {
|
||||
* @param {IManualJournal} oldManualJournal
|
||||
*/
|
||||
private transformEditDTOToModel = (
|
||||
manualJournalDTO: IManualJournalDTO,
|
||||
manualJournalDTO: EditManualJournalDto,
|
||||
oldManualJournal: ManualJournal,
|
||||
) => {
|
||||
const amount = sumBy(manualJournalDTO.entries, 'credit') || 0;
|
||||
@@ -86,7 +87,7 @@ export class EditManualJournal {
|
||||
*/
|
||||
public async editJournalEntries(
|
||||
manualJournalId: number,
|
||||
manualJournalDTO: IManualJournalDTO,
|
||||
manualJournalDTO: EditManualJournalDto,
|
||||
): Promise<{
|
||||
manualJournal: ManualJournal;
|
||||
oldManualJournal: ManualJournal;
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsDate,
|
||||
IsInt,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsPositive,
|
||||
IsString,
|
||||
MaxLength,
|
||||
Min,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
|
||||
export class ManualJournalEntryDto {
|
||||
@ApiProperty({ description: 'Entry index' })
|
||||
@IsInt()
|
||||
index: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Credit amount' })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
credit?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Debit amount' })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
debit?: number;
|
||||
|
||||
@ApiProperty({ description: 'Account ID' })
|
||||
@IsInt()
|
||||
accountId: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Entry note' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
note?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Contact ID' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
contactId?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Branch ID' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
branchId?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Project ID' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
projectId?: number;
|
||||
}
|
||||
|
||||
class AttachmentDto {
|
||||
@ApiProperty({ description: 'Attachment key' })
|
||||
@IsString()
|
||||
key: string;
|
||||
}
|
||||
|
||||
export class CommandManualJournalDto {
|
||||
@ApiProperty({ description: 'Journal date' })
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
date: Date;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Currency code' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
currencyCode?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Exchange rate' })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@IsPositive()
|
||||
exchangeRate?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Journal number' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(255)
|
||||
journalNumber?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Journal type' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(255)
|
||||
journalType?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Reference' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(255)
|
||||
reference?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Description' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Branch ID' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
branchId?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Publish status' })
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
publish?: boolean;
|
||||
|
||||
@ApiProperty({ description: 'Journal entries', type: [ManualJournalEntryDto] })
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => ManualJournalEntryDto)
|
||||
entries: ManualJournalEntryDto[];
|
||||
|
||||
@ApiPropertyOptional({ description: 'Attachments', type: [AttachmentDto] })
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => AttachmentDto)
|
||||
attachments?: AttachmentDto[];
|
||||
}
|
||||
|
||||
export class CreateManualJournalDto extends CommandManualJournalDto {}
|
||||
export class EditManualJournalDto extends CommandManualJournalDto {}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { IsObject, IsString } from 'class-validator';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
export class CreatePdfTemplateDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
templateName: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
resource: string;
|
||||
|
||||
@IsObject()
|
||||
@IsNotEmpty()
|
||||
attributes: Record<string, any>;
|
||||
}
|
||||
|
||||
export class EditPdfTemplateDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
templateName: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
resource: string;
|
||||
|
||||
@IsObject()
|
||||
@IsNotEmpty()
|
||||
attributes: Record<string, any>;
|
||||
}
|
||||
@@ -2,12 +2,11 @@ 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 { GetTaxRatesService } from './queries/GetTaxRates';
|
||||
import { ActivateTaxRateService } from './commands/ActivateTaxRate.service';
|
||||
import { InactivateTaxRateService } from './commands/InactivateTaxRate';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ICreateTaxRateDTO, IEditTaxRateDTO } from './TaxRates.types';
|
||||
import { GetTaxRatesService } from './queries/GetTaxRates.service';
|
||||
import { CreateTaxRateDto, EditTaxRateDto } from './dtos/TaxRate.dto';
|
||||
|
||||
@Injectable()
|
||||
export class TaxRatesApplication {
|
||||
@@ -26,7 +25,7 @@ export class TaxRatesApplication {
|
||||
* @param {ICreateTaxRateDTO} createTaxRateDTO
|
||||
* @returns {Promise<ITaxRate>}
|
||||
*/
|
||||
public createTaxRate(createTaxRateDTO: ICreateTaxRateDTO) {
|
||||
public createTaxRate(createTaxRateDTO: CreateTaxRateDto) {
|
||||
return this.createTaxRateService.createTaxRate(createTaxRateDTO);
|
||||
}
|
||||
|
||||
@@ -37,7 +36,7 @@ export class TaxRatesApplication {
|
||||
* @param {IEditTaxRateDTO} taxRateEditDTO
|
||||
* @returns {Promise<ITaxRate>}
|
||||
*/
|
||||
public editTaxRate(taxRateId: number, editTaxRateDTO: IEditTaxRateDTO) {
|
||||
public editTaxRate(taxRateId: number, editTaxRateDTO: EditTaxRateDto) {
|
||||
return this.editTaxRateService.editTaxRate(taxRateId, editTaxRateDTO);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
Put,
|
||||
} from '@nestjs/common';
|
||||
import { TaxRatesApplication } from './TaxRate.application';
|
||||
import { ICreateTaxRateDTO, IEditTaxRateDTO } from './TaxRates.types';
|
||||
import { PublicRoute } from '../Auth/Jwt.guard';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { CreateTaxRateDto, EditTaxRateDto } from './dtos/TaxRate.dto';
|
||||
|
||||
@Controller('tax-rates')
|
||||
@ApiTags('tax-rates')
|
||||
@@ -20,7 +20,7 @@ export class TaxRatesController {
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: 'Create a new tax rate.' })
|
||||
public createTaxRate(@Body() createTaxRateDTO: ICreateTaxRateDTO) {
|
||||
public createTaxRate(@Body() createTaxRateDTO: CreateTaxRateDto) {
|
||||
return this.taxRatesApplication.createTaxRate(createTaxRateDTO);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export class TaxRatesController {
|
||||
@ApiOperation({ summary: 'Edit the given tax rate.' })
|
||||
public editTaxRate(
|
||||
@Param('id') taxRateId: number,
|
||||
@Body() editTaxRateDTO: IEditTaxRateDTO,
|
||||
@Body() editTaxRateDTO: EditTaxRateDto,
|
||||
) {
|
||||
return this.taxRatesApplication.editTaxRate(taxRateId, editTaxRateDTO);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ 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 {
|
||||
@@ -34,7 +35,7 @@ export class CreateTaxRate {
|
||||
* @param {ICreateTaxRateDTO} createTaxRateDTO
|
||||
*/
|
||||
public async createTaxRate(
|
||||
createTaxRateDTO: ICreateTaxRateDTO,
|
||||
createTaxRateDTO: CreateTaxRateDto,
|
||||
trx?: Knex.Transaction,
|
||||
) {
|
||||
// Validates the tax code uniquiness.
|
||||
|
||||
@@ -12,6 +12,7 @@ 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 {
|
||||
@@ -38,7 +39,7 @@ export class EditTaxRateService {
|
||||
*/
|
||||
private isTaxRateDTOChanged = (
|
||||
taxRate: TaxRateModel,
|
||||
editTaxRateDTO: IEditTaxRateDTO,
|
||||
editTaxRateDTO: EditTaxRateDto,
|
||||
) => {
|
||||
return (
|
||||
taxRate.rate !== editTaxRateDTO.rate ||
|
||||
@@ -57,7 +58,7 @@ export class EditTaxRateService {
|
||||
*/
|
||||
private async editTaxRateOrCreate(
|
||||
oldTaxRate: TaxRateModel,
|
||||
editTaxRateDTO: IEditTaxRateDTO,
|
||||
editTaxRateDTO: EditTaxRateDto,
|
||||
trx?: Knex.Transaction,
|
||||
) {
|
||||
const isTaxDTOChanged = this.isTaxRateDTOChanged(
|
||||
@@ -90,7 +91,7 @@ export class EditTaxRateService {
|
||||
* @param {IEditTaxRateDTO} editTaxRateDTO - The tax rate data to edit.
|
||||
* @returns {Promise<ITaxRate>}
|
||||
*/
|
||||
public async editTaxRate(taxRateId: number, editTaxRateDTO: IEditTaxRateDTO) {
|
||||
public async editTaxRate(taxRateId: number, editTaxRateDTO: EditTaxRateDto) {
|
||||
const oldTaxRate = await this.taxRateModel().query().findById(taxRateId);
|
||||
|
||||
// Validates the tax rate existance.
|
||||
|
||||
@@ -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 {}
|
||||
@@ -7,16 +7,18 @@ import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
export class InventoryTransactionsWarehouses {
|
||||
constructor(
|
||||
@Inject(AccountTransaction.name)
|
||||
private readonly accountTransactionModel: TenantModelProxy<typeof AccountTransaction>,
|
||||
private readonly accountTransactionModel: TenantModelProxy<
|
||||
typeof AccountTransaction
|
||||
>,
|
||||
) {}
|
||||
|
||||
|
||||
/**
|
||||
* Updates all accounts transctions with the priamry branch.
|
||||
* @param {number} primaryBranchId - The primary branch id.
|
||||
*/
|
||||
public updateTransactionsWithWarehouse = async (
|
||||
primaryBranchId: number,
|
||||
trx?: Knex.Transaction
|
||||
trx?: Knex.Transaction,
|
||||
) => {
|
||||
await this.accountTransactionModel().query(trx).update({
|
||||
branchId: primaryBranchId,
|
||||
|
||||
@@ -11,6 +11,7 @@ import { WarehousesApplication } from './WarehousesApplication.service';
|
||||
import { ICreateWarehouseDTO, IEditWarehouseDTO } from './Warehouse.types';
|
||||
import { PublicRoute } from '../Auth/Jwt.guard';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { CreateWarehouseDto, EditWarehouseDto } from './dtos/Warehouse.dto';
|
||||
|
||||
@Controller('warehouses')
|
||||
@ApiTags('warehouses')
|
||||
@@ -20,14 +21,14 @@ export class WarehousesController {
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: 'Create a warehouse' })
|
||||
createWarehouse(@Body() createWarehouseDTO: ICreateWarehouseDTO) {
|
||||
createWarehouse(@Body() createWarehouseDTO: CreateWarehouseDto) {
|
||||
return this.warehousesApplication.createWarehouse(createWarehouseDTO);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
editWarehouse(
|
||||
@Param('id') warehouseId: string,
|
||||
@Body() editWarehouseDTO: IEditWarehouseDTO,
|
||||
@Body() editWarehouseDTO: EditWarehouseDto,
|
||||
) {
|
||||
return this.warehousesApplication.editWarehouse(
|
||||
Number(warehouseId),
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import {
|
||||
ICreateWarehouseDTO,
|
||||
IEditWarehouseDTO,
|
||||
IWarehouse,
|
||||
} from './Warehouse.types';
|
||||
import { ActivateWarehousesService } from './commands/ActivateWarehouses.service';
|
||||
@@ -12,6 +10,7 @@ import { GetWarehouses } from './queries/GetWarehouses';
|
||||
import { GetItemWarehouses } from './Items/GetItemWarehouses';
|
||||
import { WarehouseMarkPrimary } from './commands/WarehouseMarkPrimary.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateWarehouseDto, EditWarehouseDto } from './dtos/Warehouse.dto';
|
||||
|
||||
@Injectable()
|
||||
export class WarehousesApplication {
|
||||
@@ -31,20 +30,19 @@ export class WarehousesApplication {
|
||||
* @param {ICreateWarehouseDTO} createWarehouseDTO
|
||||
* @returns {Promise<IWarehouse>}
|
||||
*/
|
||||
public createWarehouse = (createWarehouseDTO: ICreateWarehouseDTO) => {
|
||||
public createWarehouse = (createWarehouseDTO: CreateWarehouseDto) => {
|
||||
return this.createWarehouseService.createWarehouse(createWarehouseDTO);
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits the given warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseId
|
||||
* @param {IEditWarehouseDTO} editWarehouseDTO
|
||||
* @param {number} warehouseId
|
||||
* @param {EditWarehouseDto} editWarehouseDTO
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public editWarehouse = (
|
||||
warehouseId: number,
|
||||
editWarehouseDTO: IEditWarehouseDTO,
|
||||
editWarehouseDTO: EditWarehouseDto,
|
||||
) => {
|
||||
return this.editWarehouseService.editWarehouse(
|
||||
warehouseId,
|
||||
@@ -54,7 +52,6 @@ export class WarehousesApplication {
|
||||
|
||||
/**
|
||||
* Deletes the given warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseId
|
||||
*/
|
||||
public deleteWarehouse = (warehouseId: number) => {
|
||||
@@ -63,7 +60,7 @@ export class WarehousesApplication {
|
||||
|
||||
/**
|
||||
* Retrieves the specific warehouse.
|
||||
* @param {number} warehouseId
|
||||
* @param {number} warehouseId
|
||||
* @returns
|
||||
*/
|
||||
public getWarehouse = (warehouseId: number) => {
|
||||
@@ -71,9 +68,8 @@ export class WarehousesApplication {
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @returns
|
||||
* Retrieves the warehouses list.
|
||||
* @returns {Promise<IWarehouse[]>}
|
||||
*/
|
||||
public getWarehouses = () => {
|
||||
return this.getWarehousesService.getWarehouses();
|
||||
|
||||
@@ -11,6 +11,7 @@ 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 { CreateWarehouseDto } from '../dtos/Warehouse.dto';
|
||||
|
||||
@Injectable()
|
||||
export class CreateWarehouse {
|
||||
@@ -33,7 +34,7 @@ export class CreateWarehouse {
|
||||
* Authorize the warehouse before creating.
|
||||
* @param {ICreateWarehouseDTO} warehouseDTO -
|
||||
*/
|
||||
public authorize = async (warehouseDTO: ICreateWarehouseDTO) => {
|
||||
public authorize = async (warehouseDTO: CreateWarehouseDto) => {
|
||||
if (warehouseDTO.code) {
|
||||
await this.validator.validateWarehouseCodeUnique(warehouseDTO.code);
|
||||
}
|
||||
@@ -44,7 +45,7 @@ export class CreateWarehouse {
|
||||
* @param {ICreateWarehouseDTO} warehouseDTO
|
||||
*/
|
||||
public createWarehouse = async (
|
||||
warehouseDTO: ICreateWarehouseDTO,
|
||||
warehouseDTO: CreateWarehouseDto,
|
||||
): Promise<Warehouse> => {
|
||||
// Authorize warehouse before creating.
|
||||
await this.authorize(warehouseDTO);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { Warehouse } from '../models/Warehouse.model';
|
||||
import { events } from '@/common/events/events';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { EditWarehouseDto } from '../dtos/Warehouse.dto';
|
||||
|
||||
@Injectable()
|
||||
export class EditWarehouse {
|
||||
@@ -29,7 +30,7 @@ export class EditWarehouse {
|
||||
* Authorize the warehouse before deleting.
|
||||
*/
|
||||
public authorize = async (
|
||||
warehouseDTO: IEditWarehouseDTO,
|
||||
warehouseDTO: EditWarehouseDto,
|
||||
warehouseId: number,
|
||||
) => {
|
||||
if (warehouseDTO.code) {
|
||||
@@ -47,7 +48,7 @@ export class EditWarehouse {
|
||||
*/
|
||||
public editWarehouse = async (
|
||||
warehouseId: number,
|
||||
warehouseDTO: IEditWarehouseDTO,
|
||||
warehouseDTO: EditWarehouseDto,
|
||||
): Promise<Warehouse> => {
|
||||
// Authorize the warehouse DTO before editing.
|
||||
await this.authorize(warehouseDTO, warehouseId);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsBoolean, IsEmail, IsOptional, IsUrl } from "class-validator";
|
||||
import { IsNotEmpty } from "class-validator";
|
||||
import { IsString } from "class-validator";
|
||||
|
||||
|
||||
export class CommandWarehouseDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({ description: 'The name of the warehouse' })
|
||||
name: string;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@ApiProperty({ description: 'Whether the warehouse is primary' })
|
||||
primary?: boolean;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@ApiProperty({ description: 'The code of the warehouse' })
|
||||
code?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@ApiProperty({ description: 'The address of the warehouse' })
|
||||
address?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@ApiProperty({ description: 'The city of the warehouse' })
|
||||
city?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@ApiProperty({ description: 'The country of the warehouse' })
|
||||
country?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@ApiProperty({ description: 'The phone number of the warehouse' })
|
||||
phoneNumber?: string;
|
||||
|
||||
@IsEmail()
|
||||
@IsOptional()
|
||||
@ApiProperty({ description: 'The email of the warehouse' })
|
||||
email?: string;
|
||||
|
||||
@IsUrl()
|
||||
@IsOptional()
|
||||
@ApiProperty({ description: 'The website of the warehouse' })
|
||||
website?: string;
|
||||
}
|
||||
|
||||
export class CreateWarehouseDto extends CommandWarehouseDto {}
|
||||
export class EditWarehouseDto extends CommandWarehouseDto {}
|
||||
Reference in New Issue
Block a user