refactor: nestjs

This commit is contained in:
Ahmed Bouhuolia
2025-03-22 20:36:48 +02:00
parent 136cc907bb
commit 2eb56e5850
45 changed files with 1210 additions and 198 deletions

View File

@@ -20,6 +20,16 @@ import { IFilterMeta } from '@/interfaces/Model';
@Injectable() @Injectable()
export class AccountsApplication { export class AccountsApplication {
/**
* @param {CreateAccountService} createAccountService - The create account service.
* @param {EditAccount} editAccountService - The edit account service.
* @param {DeleteAccount} deleteAccountService - The delete account service.
* @param {ActivateAccount} activateAccountService - The activate account service.
* @param {GetAccountTypesService} getAccountTypesService - The get account types service.
* @param {GetAccount} getAccountService - The get account service.
* @param {GetAccountTransactionsService} getAccountTransactionsService - The get account transactions service.
* @param {GetAccountsService} getAccountsService - The get accounts service.
*/
constructor( constructor(
private readonly createAccountService: CreateAccountService, private readonly createAccountService: CreateAccountService,
private readonly editAccountService: EditAccount, private readonly editAccountService: EditAccount,

View File

@@ -10,6 +10,12 @@ import { TenantModelProxy } from '../System/models/TenantBaseModel';
@Injectable() @Injectable()
export class ActivateAccount { export class ActivateAccount {
/**
* @param {EventEmitter2} eventEmitter - The event emitter.
* @param {UnitOfWork} uow - The unit of work.
* @param {AccountRepository} accountRepository - The account repository.
* @param {TenantModelProxy<typeof Account>} accountModel - The account model.
*/
constructor( constructor(
private readonly eventEmitter: EventEmitter2, private readonly eventEmitter: EventEmitter2,
private readonly uow: UnitOfWork, private readonly uow: UnitOfWork,
@@ -21,8 +27,8 @@ export class ActivateAccount {
/** /**
* Activates/Inactivates the given account. * Activates/Inactivates the given account.
* @param {number} accountId * @param {number} accountId - The account id.
* @param {boolean} activate * @param {boolean} activate - Activate or inactivate the account.
*/ */
public activateAccount = async (accountId: number, activate?: boolean) => { public activateAccount = async (accountId: number, activate?: boolean) => {
// Retrieve the given account or throw not found error. // Retrieve the given account or throw not found error.

View File

@@ -1,3 +1,4 @@
import { ApiProperty } from '@nestjs/swagger';
import { import {
IsString, IsString,
IsOptional, IsOptional,
@@ -11,41 +12,92 @@ export class CreateAccountDTO {
@IsString() @IsString()
@MinLength(3) @MinLength(3)
@MaxLength(255) // Assuming DATATYPES_LENGTH.STRING is 255 @MaxLength(255) // Assuming DATATYPES_LENGTH.STRING is 255
@ApiProperty({
description: 'Account name',
example: 'Cash Account',
minLength: 3,
maxLength: 255,
})
name: string; name: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@MinLength(3) @MinLength(3)
@MaxLength(6) @MaxLength(6)
@ApiProperty({
description: 'Account code',
example: 'CA001',
required: false,
minLength: 3,
maxLength: 6,
})
code?: string; code?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'Currency code for the account',
example: 'USD',
required: false,
})
currencyCode?: string; currencyCode?: string;
@IsString() @IsString()
@MinLength(3) @MinLength(3)
@MaxLength(255) @MaxLength(255)
@ApiProperty({
description: 'Type of account',
example: 'asset',
minLength: 3,
maxLength: 255,
})
accountType: string; accountType: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@MaxLength(65535) @MaxLength(65535)
@ApiProperty({
description: 'Account description',
example: 'Main cash account for daily operations',
required: false,
maxLength: 65535,
})
description?: string; description?: string;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({
description: 'ID of the parent account',
example: 1,
required: false,
})
parentAccountId?: number; parentAccountId?: number;
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiProperty({
description: 'Whether the account is active',
example: true,
required: false,
default: true,
})
active?: boolean; active?: boolean;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'Plaid account ID for syncing',
example: 'plaid_account_123456',
required: false,
})
plaidAccountId?: string; plaidAccountId?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'Plaid item ID for syncing',
example: 'plaid_item_123456',
required: false,
})
plaidItemId?: string; plaidItemId?: string;
} }

View File

@@ -1,3 +1,4 @@
import { ApiProperty } from '@nestjs/swagger';
import { import {
IsString, IsString,
IsOptional, IsOptional,
@@ -9,26 +10,45 @@ import {
export class EditAccountDTO { export class EditAccountDTO {
@IsString() @IsString()
@MinLength(3) @MinLength(3)
@MaxLength(255) // Assuming DATATYPES_LENGTH.STRING is 255 @MaxLength(255)
@ApiProperty({
description: 'The name of the account',
example: 'Bank Account',
})
name: string; name: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@MinLength(3) @MinLength(3)
@MaxLength(6) @MaxLength(6)
@ApiProperty({
description: 'The code of the account',
example: '123456',
})
code?: string; code?: string;
@IsString() @IsString()
@MinLength(3) @MinLength(3)
@MaxLength(255) // Assuming DATATYPES_LENGTH.STRING is 255 @MaxLength(255)
@ApiProperty({
description: 'The type of the account',
example: 'Bank Account',
})
accountType: string; accountType: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@MaxLength(65535) // Assuming DATATYPES_LENGTH.TEXT is 65535 @ApiProperty({
description: 'The description of the account',
example: 'This is a description',
})
description?: string; description?: string;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({
description: 'The parent account ID of the account',
example: 1,
})
parentAccountId?: number; parentAccountId?: number;
} }

View File

@@ -11,6 +11,7 @@ import {
IsNotEmpty, IsNotEmpty,
} from 'class-validator'; } from 'class-validator';
import { BankRuleComparator } from '../types'; import { BankRuleComparator } from '../types';
import { ApiProperty } from '@nestjs/swagger';
class BankRuleConditionDto { class BankRuleConditionDto {
@IsNotEmpty() @IsNotEmpty()
@@ -37,43 +38,83 @@ class BankRuleConditionDto {
export class CommandBankRuleDto { export class CommandBankRuleDto {
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@ApiProperty({
description: 'The name of the bank rule',
example: 'Monthly Salary',
})
name: string; name: string;
@IsInt() @IsInt()
@Min(0) @Min(0)
@ApiProperty({
description: 'The order of the bank rule',
example: 1,
})
order: number; order: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@Min(0) @Min(0)
@ApiProperty({
description: 'The account ID to apply the rule if',
example: 1,
})
applyIfAccountId?: number; applyIfAccountId?: number;
@IsIn(['deposit', 'withdrawal']) @IsIn(['deposit', 'withdrawal'])
@ApiProperty({
description: 'The transaction type to apply the rule if',
example: 'deposit',
})
applyIfTransactionType: 'deposit' | 'withdrawal'; applyIfTransactionType: 'deposit' | 'withdrawal';
@IsString() @IsString()
@IsIn(['and', 'or']) @IsIn(['and', 'or'])
@ApiProperty({
description: 'The conditions type to apply the rule if',
example: 'and',
})
conditionsType: 'and' | 'or' = 'and'; conditionsType: 'and' | 'or' = 'and';
@IsArray() @IsArray()
@ArrayMinSize(1) @ArrayMinSize(1)
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => BankRuleConditionDto) @Type(() => BankRuleConditionDto)
@ApiProperty({
description: 'The conditions to apply the rule if',
example: [{ field: 'description', comparator: 'contains', value: 'Salary' }],
})
conditions: BankRuleConditionDto[]; conditions: BankRuleConditionDto[];
@IsString() @IsString()
@ApiProperty({
description: 'The category to assign the rule if',
example: 'Income:Salary',
})
assignCategory: string; assignCategory: string;
@IsInt() @IsInt()
@Min(0) @Min(0)
@ApiProperty({
description: 'The account ID to assign the rule if',
example: 1,
})
assignAccountId: number; assignAccountId: number;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'The payee to assign the rule if',
example: 'Employer Inc.',
})
assignPayee?: string; assignPayee?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'The memo to assign the rule if',
example: 'Monthly Salary',
})
assignMemo?: string; assignMemo?: string;
} }

View File

@@ -15,7 +15,7 @@ export class GetBankRuleService {
/** /**
* Retrieves the bank rule. * Retrieves the bank rule.
* @param {number} ruleId * @param {number} ruleId - Rule id.
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
async getBankRule(ruleId: number): Promise<any> { async getBankRule(ruleId: number): Promise<any> {

View File

@@ -6,14 +6,23 @@ import {
ValidateNested, ValidateNested,
} from 'class-validator'; } from 'class-validator';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { ApiProperty } from '@nestjs/swagger';
export class MatchTransactionEntryDto { export class MatchTransactionEntryDto {
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@ApiProperty({
description: 'The type of the reference',
example: 'SaleInvoice',
})
referenceType: string; referenceType: string;
@IsNumber() @IsNumber()
@IsNotEmpty() @IsNotEmpty()
@ApiProperty({
description: 'The ID of the reference',
example: 1,
})
referenceId: number; referenceId: number;
} }
@@ -21,5 +30,12 @@ export class MatchBankTransactionDto {
@IsArray() @IsArray()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => MatchTransactionEntryDto) @Type(() => MatchTransactionEntryDto)
@ApiProperty({
description: 'The entries to match',
example: [
{ referenceType: 'SaleInvoice', referenceId: 1 },
{ referenceType: 'SaleInvoice', referenceId: 2 },
],
})
entries: MatchTransactionEntryDto[]; entries: MatchTransactionEntryDto[];
} }

View File

@@ -16,6 +16,15 @@ import { CreateBranchDto, EditBranchDto } from './dtos/Branch.dto';
@Injectable() @Injectable()
export class BranchesApplication { export class BranchesApplication {
/**
* @param {CreateBranchService} createBranchService - Create branch service.
* @param {EditBranchService} editBranchService - Edit branch service.
* @param {DeleteBranchService} deleteBranchService - Delete branch service.
* @param {GetBranchService} getBranchService - Get branch service.
* @param {GetBranchesService} getBranchesService - Get branches service.
* @param {ActivateBranches} activateBranchesService - Activate branches service.
* @param {MarkBranchAsPrimaryService} markBranchAsPrimaryService - Mark branch as primary service.
*/
constructor( constructor(
private readonly createBranchService: CreateBranchService, private readonly createBranchService: CreateBranchService,
private readonly editBranchService: EditBranchService, private readonly editBranchService: EditBranchService,

View File

@@ -9,48 +9,76 @@ import {
} from 'class-validator'; } from 'class-validator';
class CommandBranchDto { class CommandBranchDto {
@ApiProperty({ description: 'Branch name' }) @ApiProperty({
description: 'Branch name',
example: 'Main Branch',
})
@IsNotEmpty() @IsNotEmpty()
@IsString() @IsString()
name: string; name: string;
@ApiPropertyOptional({ description: 'Branch code' }) @ApiPropertyOptional({
description: 'Whether this is the primary branch',
example: true,
default: false,
})
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
primary?: boolean; primary?: boolean;
@ApiPropertyOptional({ description: 'Branch code' }) @ApiPropertyOptional({
description: 'Branch code',
example: 'BR001',
})
@IsOptional() @IsOptional()
@IsString() @IsString()
code?: string; code?: string;
@ApiPropertyOptional({ description: 'Branch address' }) @ApiPropertyOptional({
description: 'Branch address',
example: '123 Main Street',
})
@IsOptional() @IsOptional()
@IsString() @IsString()
address?: string; address?: string;
@ApiPropertyOptional({ description: 'Branch city' }) @ApiPropertyOptional({
description: 'Branch city',
example: 'New York',
})
@IsOptional() @IsOptional()
@IsString() @IsString()
city?: string; city?: string;
@ApiPropertyOptional({ description: 'Branch country' }) @ApiPropertyOptional({
description: 'Branch country',
example: 'USA',
})
@IsOptional() @IsOptional()
@IsString() @IsString()
country?: string; country?: string;
@ApiPropertyOptional({ description: 'Branch phone number' }) @ApiPropertyOptional({
description: 'Branch phone number',
example: '+1-555-123-4567',
})
@IsOptional() @IsOptional()
@IsString() @IsString()
phone_number?: string; phone_number?: string;
@ApiPropertyOptional({ description: 'Branch email' }) @ApiPropertyOptional({
description: 'Branch email',
example: 'branch@example.com',
})
@IsOptional() @IsOptional()
@IsEmail() @IsEmail()
@IsString() @IsString()
email?: string; email?: string;
@ApiPropertyOptional({ description: 'Branch website' }) @ApiPropertyOptional({
description: 'Branch website',
example: 'https://www.example.com/branch',
})
@IsOptional() @IsOptional()
@IsUrl() @IsUrl()
@IsString() @IsString()

View File

@@ -17,7 +17,7 @@ import { BrandingTemplateDTOTransformer } from '../../PdfTemplate/BrandingTempla
import { assocItemEntriesDefaultIndex } from '@/utils/associate-item-entries-index'; import { assocItemEntriesDefaultIndex } from '@/utils/associate-item-entries-index';
import { CreditNoteAutoIncrementService } from './CreditNoteAutoIncrement.service'; import { CreditNoteAutoIncrementService } from './CreditNoteAutoIncrement.service';
import { CreditNote } from '../models/CreditNote'; import { CreditNote } from '../models/CreditNote';
import { CreateCreditNoteDto, EditCreditNoteDto } from '../dtos/CreditNote.dto'; import { CreateCreditNoteDto, CreditNoteEntryDto, EditCreditNoteDto } from '../dtos/CreditNote.dto';
@Injectable() @Injectable()
export class CommandCreditNoteDTOTransform { export class CommandCreditNoteDTOTransform {
@@ -55,7 +55,7 @@ export class CommandCreditNoteDTOTransform {
assocItemEntriesDefaultIndex, assocItemEntriesDefaultIndex,
// Associate the reference type to credit note entries. // Associate the reference type to credit note entries.
R.map((entry: ICreditNoteEntryNewDTO) => ({ R.map((entry: CreditNoteEntryDto) => ({
...entry, ...entry,
referenceType: 'CreditNote', referenceType: 'CreditNote',
})), })),

View File

@@ -19,7 +19,7 @@ enum DiscountType {
Amount = 'amount', Amount = 'amount',
} }
class CreditNoteEntryDto extends ItemEntryDto {} export class CreditNoteEntryDto extends ItemEntryDto {}
class AttachmentDto { class AttachmentDto {
@IsString() @IsString()

View File

@@ -8,13 +8,11 @@ import {
Put, Put,
} from '@nestjs/common'; } from '@nestjs/common';
import { CustomersApplication } from './CustomersApplication.service'; import { CustomersApplication } from './CustomersApplication.service';
import { import { ICustomerOpeningBalanceEditDTO } from './types/Customers.types';
ICustomerEditDTO,
ICustomerNewDTO,
ICustomerOpeningBalanceEditDTO,
} from './types/Customers.types';
import { PublicRoute } from '../Auth/Jwt.guard'; import { PublicRoute } from '../Auth/Jwt.guard';
import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { CreateCustomerDto } from './dtos/CreateCustomer.dto';
import { EditCustomerDto } from './dtos/EditCustomer.dto';
@Controller('customers') @Controller('customers')
@ApiTags('customers') @ApiTags('customers')
@@ -30,7 +28,7 @@ export class CustomersController {
@Post() @Post()
@ApiOperation({ summary: 'Create a new customer.' }) @ApiOperation({ summary: 'Create a new customer.' })
createCustomer(@Body() customerDTO: ICustomerNewDTO) { createCustomer(@Body() customerDTO: CreateCustomerDto) {
return this.customersApplication.createCustomer(customerDTO); return this.customersApplication.createCustomer(customerDTO);
} }
@@ -38,7 +36,7 @@ export class CustomersController {
@ApiOperation({ summary: 'Edit the given customer.' }) @ApiOperation({ summary: 'Edit the given customer.' })
editCustomer( editCustomer(
@Param('id') customerId: number, @Param('id') customerId: number,
@Body() customerDTO: ICustomerEditDTO, @Body() customerDTO: EditCustomerDto,
) { ) {
return this.customersApplication.editCustomer(customerId, customerDTO); return this.customersApplication.editCustomer(customerId, customerDTO);
} }

View File

@@ -4,12 +4,9 @@ import { CreateCustomer } from './commands/CreateCustomer.service';
import { EditCustomer } from './commands/EditCustomer.service'; import { EditCustomer } from './commands/EditCustomer.service';
import { DeleteCustomer } from './commands/DeleteCustomer.service'; import { DeleteCustomer } from './commands/DeleteCustomer.service';
import { EditOpeningBalanceCustomer } from './commands/EditOpeningBalanceCustomer.service'; import { EditOpeningBalanceCustomer } from './commands/EditOpeningBalanceCustomer.service';
import { import { ICustomerOpeningBalanceEditDTO } from './types/Customers.types';
ICustomerEditDTO, import { CreateCustomerDto } from './dtos/CreateCustomer.dto';
ICustomerNewDTO, import { EditCustomerDto } from './dtos/EditCustomer.dto';
ICustomerOpeningBalanceEditDTO,
// ICustomersFilter,
} from './types/Customers.types';
@Injectable() @Injectable()
export class CustomersApplication { export class CustomersApplication {
@@ -36,7 +33,7 @@ export class CustomersApplication {
* @param {ICustomerNewDTO} customerDTO * @param {ICustomerNewDTO} customerDTO
* @returns {Promise<ICustomer>} * @returns {Promise<ICustomer>}
*/ */
public createCustomer = (customerDTO: ICustomerNewDTO) => { public createCustomer = (customerDTO: CreateCustomerDto) => {
return this.createCustomerService.createCustomer(customerDTO); return this.createCustomerService.createCustomer(customerDTO);
}; };
@@ -46,7 +43,7 @@ export class CustomersApplication {
* @param {ICustomerEditDTO} customerDTO - Customer edit DTO. * @param {ICustomerEditDTO} customerDTO - Customer edit DTO.
* @return {Promise<ICustomer>} * @return {Promise<ICustomer>}
*/ */
public editCustomer = (customerId: number, customerDTO: ICustomerEditDTO) => { public editCustomer = (customerId: number, customerDTO: EditCustomerDto) => {
return this.editCustomerService.editCustomer(customerId, customerDTO); return this.editCustomerService.editCustomer(customerId, customerDTO);
}; };

View File

@@ -8,9 +8,9 @@ import { events } from '@/common/events/events';
import { import {
ICustomerEventCreatedPayload, ICustomerEventCreatedPayload,
ICustomerEventCreatingPayload, ICustomerEventCreatingPayload,
ICustomerNewDTO,
} from '../types/Customers.types'; } from '../types/Customers.types';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { CreateCustomerDto } from '../dtos/CreateCustomer.dto';
@Injectable() @Injectable()
export class CreateCustomer { export class CreateCustomer {
@@ -35,7 +35,7 @@ export class CreateCustomer {
* @return {Promise<ICustomer>} * @return {Promise<ICustomer>}
*/ */
public async createCustomer( public async createCustomer(
customerDTO: ICustomerNewDTO, customerDTO: CreateCustomerDto,
trx?: Knex.Transaction, trx?: Knex.Transaction,
): Promise<Customer> { ): Promise<Customer> {
// Transformes the customer DTO to customer object. // Transformes the customer DTO to customer object.

View File

@@ -11,6 +11,7 @@ import { events } from '@/common/events/events';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service'; import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { EditCustomerDto } from '../dtos/EditCustomer.dto';
@Injectable() @Injectable()
export class EditCustomer { export class EditCustomer {
@@ -37,7 +38,7 @@ export class EditCustomer {
*/ */
public async editCustomer( public async editCustomer(
customerId: number, customerId: number,
customerDTO: ICustomerEditDTO, customerDTO: EditCustomerDto,
): Promise<Customer> { ): Promise<Customer> {
// Retrieve the customer or throw not found error. // Retrieve the customer or throw not found error.
const oldCustomer = await this.customerModel() const oldCustomer = await this.customerModel()

View File

@@ -0,0 +1,84 @@
import { IsEmail, IsOptional, IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class ContactAddressDto {
@ApiProperty({ required: false, description: 'Billing address line 1' })
@IsOptional()
@IsString()
billingAddress1?: string;
@ApiProperty({ required: false, description: 'Billing address line 2' })
@IsOptional()
@IsString()
billingAddress2?: string;
@ApiProperty({ required: false, description: 'Billing address city' })
@IsOptional()
@IsString()
billingAddressCity?: string;
@ApiProperty({ required: false, description: 'Billing address country' })
@IsOptional()
@IsString()
billingAddressCountry?: string;
@ApiProperty({ required: false, description: 'Billing address email' })
@IsOptional()
@IsEmail()
billingAddressEmail?: string;
@ApiProperty({ required: false, description: 'Billing address zipcode' })
@IsOptional()
@IsString()
billingAddressZipcode?: string;
@ApiProperty({ required: false, description: 'Billing address phone' })
@IsOptional()
@IsString()
billingAddressPhone?: string;
@ApiProperty({ required: false, description: 'Billing address state' })
@IsOptional()
@IsString()
billingAddressState?: string;
@ApiProperty({ required: false, description: 'Shipping address line 1' })
@IsOptional()
@IsString()
shippingAddress1?: string;
@ApiProperty({ required: false, description: 'Shipping address line 2' })
@IsOptional()
@IsString()
shippingAddress2?: string;
@ApiProperty({ required: false, description: 'Shipping address city' })
@IsOptional()
@IsString()
shippingAddressCity?: string;
@ApiProperty({ required: false, description: 'Shipping address country' })
@IsOptional()
@IsString()
shippingAddressCountry?: string;
@ApiProperty({ required: false, description: 'Shipping address email' })
@IsOptional()
@IsEmail()
shippingAddressEmail?: string;
@ApiProperty({ required: false, description: 'Shipping address zipcode' })
@IsOptional()
@IsString()
shippingAddressZipcode?: string;
@ApiProperty({ required: false, description: 'Shipping address phone' })
@IsOptional()
@IsString()
shippingAddressPhone?: string;
@ApiProperty({ required: false, description: 'Shipping address state' })
@IsOptional()
@IsString()
shippingAddressState?: string;
}

View File

@@ -0,0 +1,100 @@
import {
IsBoolean,
IsEmail,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { ContactAddressDto } from './ContactAddress.dto';
export class CreateCustomerDto extends ContactAddressDto {
@ApiProperty({ required: true, description: 'Customer type' })
@IsString()
@IsNotEmpty()
customerType: string;
@ApiProperty({ required: true, description: 'Currency code' })
@IsString()
@IsNotEmpty()
currencyCode: string;
@ApiProperty({ required: false, description: 'Opening balance' })
@IsOptional()
@IsNumber()
openingBalance?: number;
@ApiProperty({ required: false, description: 'Opening balance date' })
@IsOptional()
@IsString()
openingBalanceAt?: string;
@ApiProperty({
required: false,
description: 'Opening balance exchange rate',
})
@IsOptional()
@IsNumber()
openingBalanceExchangeRate?: number;
@ApiProperty({ required: false, description: 'Opening balance branch ID' })
@IsOptional()
@IsNumber()
openingBalanceBranchId?: number;
@ApiProperty({ required: false, description: 'Salutation' })
@IsOptional()
@IsString()
salutation?: string;
@ApiProperty({ required: false, description: 'First name' })
@IsOptional()
@IsString()
firstName?: string;
@ApiProperty({ required: false, description: 'Last name' })
@IsOptional()
@IsString()
lastName?: string;
@ApiProperty({ required: false, description: 'Company name' })
@IsOptional()
@IsString()
companyName?: string;
@ApiProperty({ required: true, description: 'Display name' })
@IsString()
@IsNotEmpty()
displayName: string;
@ApiProperty({ required: false, description: 'Website' })
@IsOptional()
@IsString()
website?: string;
@ApiProperty({ required: false, description: 'Email' })
@IsOptional()
@IsEmail()
email?: string;
@ApiProperty({ required: false, description: 'Work phone' })
@IsOptional()
@IsString()
workPhone?: string;
@ApiProperty({ required: false, description: 'Personal phone' })
@IsOptional()
@IsString()
personalPhone?: string;
@ApiProperty({ required: false, description: 'Note' })
@IsOptional()
@IsString()
note?: string;
@ApiProperty({ required: false, description: 'Active status', default: true })
@IsOptional()
@IsBoolean()
active?: boolean;
}

View File

@@ -0,0 +1,65 @@
import { IsBoolean, IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { ContactAddressDto } from './ContactAddress.dto';
export class EditCustomerDto extends ContactAddressDto {
@ApiProperty({ required: true, description: 'Customer type' })
@IsString()
@IsNotEmpty()
customerType: string;
@ApiProperty({ required: false, description: 'Salutation' })
@IsOptional()
@IsString()
salutation?: string;
@ApiProperty({ required: false, description: 'First name' })
@IsOptional()
@IsString()
firstName?: string;
@ApiProperty({ required: false, description: 'Last name' })
@IsOptional()
@IsString()
lastName?: string;
@ApiProperty({ required: false, description: 'Company name' })
@IsOptional()
@IsString()
companyName?: string;
@ApiProperty({ required: true, description: 'Display name' })
@IsString()
@IsNotEmpty()
displayName: string;
@ApiProperty({ required: false, description: 'Website' })
@IsOptional()
@IsString()
website?: string;
@ApiProperty({ required: false, description: 'Email' })
@IsOptional()
@IsEmail()
email?: string;
@ApiProperty({ required: false, description: 'Work phone' })
@IsOptional()
@IsString()
workPhone?: string;
@ApiProperty({ required: false, description: 'Personal phone' })
@IsOptional()
@IsString()
personalPhone?: string;
@ApiProperty({ required: false, description: 'Note' })
@IsOptional()
@IsString()
note?: string;
@ApiProperty({ required: false, description: 'Active status' })
@IsOptional()
@IsBoolean()
active?: boolean;
}

View File

@@ -1,23 +1,5 @@
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel'; import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
// import TenantModel from 'models/TenantModel';
// import PaginationQueryBuilder from './Pagination';
// import ModelSetting from './ModelSetting';
// import CustomerSettings from './Customer.Settings';
// import CustomViewBaseModel from './CustomViewBaseModel';
// import { DEFAULT_VIEWS } from '@/services/Contacts/Customers/constants';
// import ModelSearchable from './ModelSearchable';
// class CustomerQueryBuilder extends PaginationQueryBuilder {
// constructor(...args) {
// super(...args);
// this.onBuild((builder) => {
// if (builder.isFind() || builder.isDelete() || builder.isUpdate()) {
// builder.where('contact_service', 'customer');
// }
// });
// }
// }
export class Customer extends TenantBaseModel{ export class Customer extends TenantBaseModel{
contactService: string; contactService: string;

View File

@@ -3,6 +3,8 @@ import { Customer } from '../models/Customer';
import { IContactAddressDTO } from '@/modules/Contacts/types/Contacts.types'; import { IContactAddressDTO } from '@/modules/Contacts/types/Contacts.types';
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types'; import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model'; import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { CreateCustomerDto } from '../dtos/CreateCustomer.dto';
import { EditCustomerDto } from '../dtos/EditCustomer.dto';
// Customer Interfaces. // Customer Interfaces.
// ---------------------------------- // ----------------------------------
@@ -63,46 +65,38 @@ export interface GetCustomersResponse {
// Customer Events. // Customer Events.
// ---------------------------------- // ----------------------------------
export interface ICustomerEventCreatedPayload { export interface ICustomerEventCreatedPayload {
// tenantId: number;
customerId: number; customerId: number;
// authorizedUser: ISystemUser;
customer: Customer; customer: Customer;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export interface ICustomerEventCreatingPayload { export interface ICustomerEventCreatingPayload {
// tenantId: number; customerDTO: CreateCustomerDto;
customerDTO: ICustomerNewDTO;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export interface ICustomerEventEditedPayload { export interface ICustomerEventEditedPayload {
// tenantId: number
customerId: number; customerId: number;
customer: Customer; customer: Customer;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export interface ICustomerEventEditingPayload { export interface ICustomerEventEditingPayload {
// tenantId: number; customerDTO: EditCustomerDto;
customerDTO: ICustomerEditDTO;
customerId: number; customerId: number;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export interface ICustomerDeletingPayload { export interface ICustomerDeletingPayload {
// tenantId: number;
customerId: number; customerId: number;
oldCustomer: Customer; oldCustomer: Customer;
} }
export interface ICustomerEventDeletedPayload { export interface ICustomerEventDeletedPayload {
// tenantId: number;
customerId: number; customerId: number;
oldCustomer: Customer; oldCustomer: Customer;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export interface ICustomerEventCreatingPayload { export interface ICustomerEventCreatingPayload {
// tenantId: number; customerDTO: CreateCustomerDto;
customerDTO: ICustomerNewDTO;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export enum CustomerAction { export enum CustomerAction {
@@ -141,13 +135,11 @@ export interface ICustomerOpeningBalanceEditedPayload {
export interface ICustomerActivatingPayload { export interface ICustomerActivatingPayload {
// tenantId: number;
trx: Knex.Transaction, trx: Knex.Transaction,
oldCustomer: Customer; oldCustomer: Customer;
} }
export interface ICustomerActivatedPayload { export interface ICustomerActivatedPayload {
// tenantId: number;
trx?: Knex.Transaction; trx?: Knex.Transaction;
oldCustomer: Customer; oldCustomer: Customer;
customer: Customer; customer: Customer;

View File

@@ -9,13 +9,10 @@ import {
Query, Query,
} from '@nestjs/common'; } from '@nestjs/common';
import { ExpensesApplication } from './ExpensesApplication.service'; import { ExpensesApplication } from './ExpensesApplication.service';
import {
IExpenseCreateDTO,
IExpenseEditDTO,
} from './interfaces/Expenses.interface';
import { PublicRoute } from '../Auth/Jwt.guard'; import { PublicRoute } from '../Auth/Jwt.guard';
import { IExpensesFilter } from './Expenses.types'; import { IExpensesFilter } from './Expenses.types';
import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { CreateExpenseDto, EditExpenseDto } from './dtos/Expense.dto';
@Controller('expenses') @Controller('expenses')
@ApiTags('expenses') @ApiTags('expenses')
@@ -29,7 +26,7 @@ export class ExpensesController {
*/ */
@Post() @Post()
@ApiOperation({ summary: 'Create a new expense transaction.' }) @ApiOperation({ summary: 'Create a new expense transaction.' })
public createExpense(@Body() expenseDTO: IExpenseCreateDTO) { public createExpense(@Body() expenseDTO: CreateExpenseDto) {
return this.expensesApplication.createExpense(expenseDTO); return this.expensesApplication.createExpense(expenseDTO);
} }
@@ -42,7 +39,7 @@ export class ExpensesController {
@ApiOperation({ summary: 'Edit the given expense transaction.' }) @ApiOperation({ summary: 'Edit the given expense transaction.' })
public editExpense( public editExpense(
@Param('id') expenseId: number, @Param('id') expenseId: number,
@Body() expenseDTO: IExpenseEditDTO, @Body() expenseDTO: EditExpenseDto,
) { ) {
return this.expensesApplication.editExpense(expenseId, expenseDTO); return this.expensesApplication.editExpense(expenseId, expenseDTO);
} }

View File

@@ -4,12 +4,9 @@ import { EditExpense } from './commands/EditExpense.service';
import { DeleteExpense } from './commands/DeleteExpense.service'; import { DeleteExpense } from './commands/DeleteExpense.service';
import { PublishExpense } from './commands/PublishExpense.service'; import { PublishExpense } from './commands/PublishExpense.service';
import { GetExpenseService } from './queries/GetExpense.service'; import { GetExpenseService } from './queries/GetExpense.service';
import { import { IExpensesFilter } from './interfaces/Expenses.interface';
IExpenseCreateDTO,
IExpenseEditDTO,
IExpensesFilter,
} from './interfaces/Expenses.interface';
import { GetExpensesService } from './queries/GetExpenses.service'; import { GetExpensesService } from './queries/GetExpenses.service';
import { CreateExpenseDto, EditExpenseDto } from './dtos/Expense.dto';
@Injectable() @Injectable()
export class ExpensesApplication { export class ExpensesApplication {
@@ -24,55 +21,55 @@ export class ExpensesApplication {
/** /**
* Create a new expense transaction. * Create a new expense transaction.
* @param {IExpenseDTO} expenseDTO * @param {CreateExpenseDto} expenseDTO
* @returns {Promise<Expense>} * @returns {Promise<Expense>}
*/ */
public createExpense = (expenseDTO: IExpenseCreateDTO) => { public createExpense(expenseDTO: CreateExpenseDto) {
return this.createExpenseService.newExpense(expenseDTO); return this.createExpenseService.newExpense(expenseDTO);
}; }
/** /**
* Edits the given expense transaction. * Edits the given expense transaction.
* @param {number} expenseId - Expense id. * @param {number} expenseId - Expense id.
* @param {IExpenseEditDTO} expenseDTO * @param {EditExpenseDto} expenseDTO
* @returns {Promise<Expense>} * @returns {Promise<Expense>}
*/ */
public editExpense = (expenseId: number, expenseDTO: IExpenseEditDTO) => { public editExpense(expenseId: number, expenseDTO: EditExpenseDto) {
return this.editExpenseService.editExpense(expenseId, expenseDTO); return this.editExpenseService.editExpense(expenseId, expenseDTO);
}; }
/** /**
* Deletes the given expense. * Deletes the given expense.
* @param {number} expenseId - Expense id. * @param {number} expenseId - Expense id.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public deleteExpense = (expenseId: number) => { public deleteExpense(expenseId: number) {
return this.deleteExpenseService.deleteExpense(expenseId); return this.deleteExpenseService.deleteExpense(expenseId);
}; }
/** /**
* Publishes the given expense. * Publishes the given expense.
* @param {number} expenseId - Expense id. * @param {number} expenseId - Expense id.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public publishExpense = (expenseId: number) => { public publishExpense(expenseId: number) {
return this.publishExpenseService.publishExpense(expenseId); return this.publishExpenseService.publishExpense(expenseId);
}; }
/** /**
* Retrieve the given expense details. * Retrieve the given expense details.
* @param {number} expenseId -Expense id. * @param {number} expenseId -Expense id.
* @return {Promise<Expense>} * @return {Promise<Expense>}
*/ */
public getExpense = (expenseId: number) => { public getExpense(expenseId: number) {
return this.getExpenseService.getExpense(expenseId); return this.getExpenseService.getExpense(expenseId);
}; }
/** /**
* Retrieve expenses paginated list. * Retrieve expenses paginated list.
* @param {IExpensesFilter} expensesFilter * @param {IExpensesFilter} expensesFilter
*/ */
public getExpenses = (filterDTO: IExpensesFilter) => { public getExpenses(filterDTO: IExpensesFilter) {
return this.getExpensesService.getExpensesList(filterDTO); return this.getExpensesService.getExpensesList(filterDTO);
}; }
} }

View File

@@ -2,14 +2,11 @@ import { Injectable } from '@nestjs/common';
import { omit, sumBy } from 'lodash'; import { omit, sumBy } from 'lodash';
import * as moment from 'moment'; import * as moment from 'moment';
import * as R from 'ramda'; import * as R from 'ramda';
import {
IExpenseCreateDTO,
IExpenseEditDTO,
} from '../interfaces/Expenses.interface';
import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform'; import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform';
import { Expense } from '../models/Expense.model'; import { Expense } from '../models/Expense.model';
import { assocItemEntriesDefaultIndex } from '@/utils/associate-item-entries-index'; import { assocItemEntriesDefaultIndex } from '@/utils/associate-item-entries-index';
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service'; import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
import { CreateExpenseDto, EditExpenseDto } from '../dtos/Expense.dto';
@Injectable() @Injectable()
export class ExpenseDTOTransformer { export class ExpenseDTOTransformer {
@@ -28,7 +25,7 @@ export class ExpenseDTOTransformer {
* @return {number} * @return {number}
*/ */
private getExpenseLandedCostAmount = ( private getExpenseLandedCostAmount = (
expenseDTO: IExpenseCreateDTO | IExpenseEditDTO, expenseDTO: CreateExpenseDto | EditExpenseDto,
): number => { ): number => {
const landedCostEntries = expenseDTO.categories.filter((entry) => { const landedCostEntries = expenseDTO.categories.filter((entry) => {
return entry.landedCost === true; return entry.landedCost === true;
@@ -52,7 +49,7 @@ export class ExpenseDTOTransformer {
* @return {IExpense} * @return {IExpense}
*/ */
private expenseDTOToModel( private expenseDTOToModel(
expenseDTO: IExpenseCreateDTO | IExpenseEditDTO, expenseDTO: CreateExpenseDto | EditExpenseDto,
): Expense { ): Expense {
const landedCostAmount = this.getExpenseLandedCostAmount(expenseDTO); const landedCostAmount = this.getExpenseLandedCostAmount(expenseDTO);
const totalAmount = this.getExpenseCategoriesTotal(expenseDTO.categories); const totalAmount = this.getExpenseCategoriesTotal(expenseDTO.categories);
@@ -85,7 +82,7 @@ export class ExpenseDTOTransformer {
* @returns {Promise<Expense>} * @returns {Promise<Expense>}
*/ */
public expenseCreateDTO = async ( public expenseCreateDTO = async (
expenseDTO: IExpenseCreateDTO, expenseDTO: CreateExpenseDto | EditExpenseDto,
): Promise<Partial<Expense>> => { ): Promise<Partial<Expense>> => {
const initialDTO = this.expenseDTOToModel(expenseDTO); const initialDTO = this.expenseDTOToModel(expenseDTO);
const tenant = await this.tenancyContext.getTenant(true); const tenant = await this.tenancyContext.getTenant(true);
@@ -104,13 +101,11 @@ export class ExpenseDTOTransformer {
/** /**
* Transformes the expense edit DTO. * Transformes the expense edit DTO.
* @param {number} tenantId * @param {EditExpenseDto} expenseDTO
* @param {IExpenseEditDTO} expenseDTO * @returns {Promise<Expense>}
* @param {ISystemUser} user
* @returns {IExpense}
*/ */
public expenseEditDTO = async ( public expenseEditDTO = async (
expenseDTO: IExpenseEditDTO, expenseDTO: EditExpenseDto,
): Promise<Expense> => { ): Promise<Expense> => {
return this.expenseDTOToModel(expenseDTO); return this.expenseDTOToModel(expenseDTO);
}; };

View File

@@ -1,14 +1,11 @@
import { sumBy, difference } from 'lodash'; import { sumBy, difference } from 'lodash';
import { ERRORS, SUPPORTED_EXPENSE_PAYMENT_ACCOUNT_TYPES } from '../constants'; import { ERRORS, SUPPORTED_EXPENSE_PAYMENT_ACCOUNT_TYPES } from '../constants';
import {
IExpenseCreateDTO,
IExpenseEditDTO,
} from '../interfaces/Expenses.interface';
import { ACCOUNT_ROOT_TYPE } from '@/constants/accounts'; import { ACCOUNT_ROOT_TYPE } from '@/constants/accounts';
import { Account } from '@/modules/Accounts/models/Account.model'; import { Account } from '@/modules/Accounts/models/Account.model';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Expense } from '../models/Expense.model'; import { Expense } from '../models/Expense.model';
import { ServiceError } from '@/modules/Items/ServiceError'; import { ServiceError } from '@/modules/Items/ServiceError';
import { CreateExpenseDto, EditExpenseDto } from '../dtos/Expense.dto';
@Injectable() @Injectable()
export class CommandExpenseValidator { export class CommandExpenseValidator {
@@ -18,7 +15,7 @@ export class CommandExpenseValidator {
* @throws {ServiceError} * @throws {ServiceError}
*/ */
public validateCategoriesNotEqualZero = ( public validateCategoriesNotEqualZero = (
expenseDTO: IExpenseCreateDTO | IExpenseEditDTO, expenseDTO: CreateExpenseDto | EditExpenseDto,
) => { ) => {
const totalAmount = sumBy(expenseDTO.categories, 'amount') || 0; const totalAmount = sumBy(expenseDTO.categories, 'amount') || 0;
@@ -30,7 +27,6 @@ export class CommandExpenseValidator {
/** /**
* Retrieve expense accounts or throw error in case one of the given accounts * Retrieve expense accounts or throw error in case one of the given accounts
* not found not the storage. * not found not the storage.
* @param {Array<Account>} tenantId
* @param {number} expenseAccountsIds * @param {number} expenseAccountsIds
* @throws {ServiceError} * @throws {ServiceError}
* @returns {Promise<IAccount[]>} * @returns {Promise<IAccount[]>}
@@ -40,7 +36,6 @@ export class CommandExpenseValidator {
DTOAccountsIds: number[], DTOAccountsIds: number[],
) { ) {
const storedExpenseAccountsIds = expenseAccounts.map((a: Account) => a.id); const storedExpenseAccountsIds = expenseAccounts.map((a: Account) => a.id);
const notStoredAccountsIds = difference( const notStoredAccountsIds = difference(
DTOAccountsIds, DTOAccountsIds,
storedExpenseAccountsIds, storedExpenseAccountsIds,
@@ -84,7 +79,6 @@ export class CommandExpenseValidator {
/** /**
* Validates the expense has not associated landed cost * Validates the expense has not associated landed cost
* references to the given expense. * references to the given expense.
* @param {number} tenantId
* @param {number} expenseId * @param {number} expenseId
*/ */
public async validateNoAssociatedLandedCost(expenseId: number) { public async validateNoAssociatedLandedCost(expenseId: number) {

View File

@@ -1,7 +1,6 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { import {
IExpenseCreateDTO,
IExpenseCreatedPayload, IExpenseCreatedPayload,
IExpenseCreatingPayload, IExpenseCreatingPayload,
} from '../interfaces/Expenses.interface'; } from '../interfaces/Expenses.interface';
@@ -13,6 +12,7 @@ import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { events } from '@/common/events/events'; import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { CreateExpenseDto } from '../dtos/Expense.dto';
@Injectable() @Injectable()
export class CreateExpense { export class CreateExpense {
@@ -41,7 +41,7 @@ export class CreateExpense {
* Authorize before create a new expense transaction. * Authorize before create a new expense transaction.
* @param {IExpenseDTO} expenseDTO * @param {IExpenseDTO} expenseDTO
*/ */
private authorize = async (expenseDTO: IExpenseCreateDTO) => { private authorize = async (expenseDTO: CreateExpenseDto) => {
// Validate payment account existance on the storage. // Validate payment account existance on the storage.
const paymentAccount = await this.accountModel() const paymentAccount = await this.accountModel()
.query() .query()
@@ -86,7 +86,7 @@ export class CreateExpense {
* @param {IExpenseDTO} expenseDTO * @param {IExpenseDTO} expenseDTO
*/ */
public newExpense = async ( public newExpense = async (
expenseDTO: IExpenseCreateDTO, expenseDTO: CreateExpenseDto,
trx?: Knex.Transaction, trx?: Knex.Transaction,
): Promise<Expense> => { ): Promise<Expense> => {
// Authorize before create a new expense. // Authorize before create a new expense.

View File

@@ -3,17 +3,16 @@ import { Knex } from 'knex';
import { import {
IExpenseEventEditPayload, IExpenseEventEditPayload,
IExpenseEventEditingPayload, IExpenseEventEditingPayload,
IExpenseEditDTO,
} from '../interfaces/Expenses.interface'; } from '../interfaces/Expenses.interface';
import { CommandExpenseValidator } from './CommandExpenseValidator.service'; import { CommandExpenseValidator } from './CommandExpenseValidator.service';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { ExpenseDTOTransformer } from './CommandExpenseDTO.transformer'; import { ExpenseDTOTransformer } from './CommandExpenseDTO.transformer';
// import { EntriesService } from '@/services/Entries';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service'; import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { Account } from '@/modules/Accounts/models/Account.model'; import { Account } from '@/modules/Accounts/models/Account.model';
import { Expense } from '../models/Expense.model'; import { Expense } from '../models/Expense.model';
import { events } from '@/common/events/events'; import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { EditExpenseDto } from '../dtos/Expense.dto';
@Injectable() @Injectable()
export class EditExpense { export class EditExpense {
@@ -30,7 +29,6 @@ export class EditExpense {
private uow: UnitOfWork, private uow: UnitOfWork,
private validator: CommandExpenseValidator, private validator: CommandExpenseValidator,
private transformDTO: ExpenseDTOTransformer, private transformDTO: ExpenseDTOTransformer,
// private entriesService: EntriesService,
@Inject(Expense.name) @Inject(Expense.name)
private expenseModel: TenantModelProxy<typeof Expense>, private expenseModel: TenantModelProxy<typeof Expense>,
@@ -40,11 +38,11 @@ export class EditExpense {
/** /**
* Authorize the DTO before editing expense transaction. * Authorize the DTO before editing expense transaction.
* @param {IExpenseEditDTO} expenseDTO * @param {EditExpenseDto} expenseDTO
*/ */
public authorize = async ( public authorize = async (
oldExpense: Expense, oldExpense: Expense,
expenseDTO: IExpenseEditDTO, expenseDTO: EditExpenseDto,
) => { ) => {
// Validate payment account existance on the storage. // Validate payment account existance on the storage.
const paymentAccount = await this.accountModel() const paymentAccount = await this.accountModel()
@@ -102,7 +100,7 @@ export class EditExpense {
*/ */
public async editExpense( public async editExpense(
expenseId: number, expenseId: number,
expenseDTO: IExpenseEditDTO, expenseDTO: EditExpenseDto,
): Promise<Expense> { ): Promise<Expense> {
// Retrieves the expense model or throw not found error. // Retrieves the expense model or throw not found error.
const oldExpense = await this.expenseModel() const oldExpense = await this.expenseModel()

View File

@@ -1,3 +1,166 @@
export class CreateExpenseDto {} import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import {
IsArray,
IsBoolean,
IsDate,
IsInt,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
MaxLength,
ValidateNested,
} from 'class-validator';
export class EditExpenseDto {} class AttachmentDto {
@IsString()
key: string;
}
export class ExpenseCategoryDto {
@IsInt()
@IsNotEmpty()
index: number;
@IsInt()
@IsNotEmpty()
expenseAccountId: number;
@IsNumber()
@IsOptional()
amount?: number;
@IsString()
@MaxLength(255)
@IsOptional()
description?: string;
@IsBoolean()
@IsOptional()
landedCost?: boolean;
@IsInt()
@IsOptional()
projectId?: number;
}
export class CommandExpenseDto {
@IsString()
@MaxLength(255)
@IsOptional()
@ApiProperty({
description: 'The reference number of the expense',
example: 'INV-123456',
})
referenceNo?: string;
@IsDate()
@IsNotEmpty()
@ApiProperty({
description: 'The payment date of the expense',
example: '2021-01-01',
})
paymentDate: Date;
@IsInt()
@IsNotEmpty()
@ApiProperty({
description: 'The payment account id of the expense',
example: 1,
})
paymentAccountId: number;
@IsString()
@MaxLength(1000)
@IsOptional()
@ApiProperty({
description: 'The description of the expense',
example: 'This is a description',
})
description?: string;
@IsNumber()
@IsOptional()
@ApiProperty({
description: 'The exchange rate of the expense',
example: 1,
})
exchangeRate?: number;
@IsString()
@MaxLength(3)
@IsOptional()
@ApiProperty({
description: 'The currency code of the expense',
example: 'USD',
})
currencyCode?: string;
@IsNumber()
@IsOptional()
@ApiProperty({
description: 'The exchange rate of the expense',
example: 1,
})
exchange_rate?: number;
@IsBoolean()
@IsOptional()
@ApiProperty({
description: 'The publish status of the expense',
example: true,
})
publish?: boolean;
@IsInt()
@IsOptional()
@ApiProperty({
description: 'The payee id of the expense',
example: 1,
})
payeeId?: number;
@IsInt()
@IsOptional()
@ApiProperty({
description: 'The branch id of the expense',
example: 1,
})
branchId?: number;
@IsArray()
@ValidateNested({ each: true })
@Type(() => ExpenseCategoryDto)
@ApiProperty({
description: 'The categories of the expense',
example: [
{
index: 1,
expenseAccountId: 1,
amount: 100,
description: 'This is a description',
landedCost: true,
projectId: 1,
},
],
})
categories: ExpenseCategoryDto[];
@IsArray()
@ValidateNested({ each: true })
@Type(() => AttachmentDto)
@IsOptional()
@ApiProperty({
description: 'The attachments of the expense',
example: [
{
key: '123456',
},
],
})
attachments?: AttachmentDto[];
}
export class CreateExpenseDto extends CommandExpenseDto {}
export class EditExpenseDto extends CommandExpenseDto {}

View File

@@ -1,6 +1,7 @@
import { IFilterRole } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types'; import { IFilterRole } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { Expense } from '../models/Expense.model'; import { Expense } from '../models/Expense.model';
import { CreateExpenseDto, EditExpenseDto } from '../dtos/Expense.dto';
// import { AttachmentLinkDTO } from '../Attachments/Attachments'; // import { AttachmentLinkDTO } from '../Attachments/Attachments';
@@ -20,26 +21,6 @@ export interface IExpensesFilter {
filterQuery?: (query: any) => void; filterQuery?: (query: any) => void;
} }
export interface IExpenseCommonDTO {
currencyCode: string;
exchangeRate?: number;
description?: string;
paymentAccountId: number;
peyeeId?: number;
referenceNo?: string;
publish: boolean;
userId: number;
paymentDate: Date;
payeeId: number;
categories: IExpenseCategoryDTO[];
branchId?: number;
// attachments?: AttachmentLinkDTO[];
}
export interface IExpenseCreateDTO extends IExpenseCommonDTO {}
export interface IExpenseEditDTO extends IExpenseCommonDTO {}
export interface IExpenseCategoryDTO { export interface IExpenseCategoryDTO {
id?: number; id?: number;
expenseAccountId: number; expenseAccountId: number;
@@ -52,55 +33,44 @@ export interface IExpenseCategoryDTO {
} }
export interface IExpenseCreatingPayload { export interface IExpenseCreatingPayload {
expenseDTO: CreateExpenseDto;
trx: Knex.Transaction; trx: Knex.Transaction;
tenantId: number;
expenseDTO: IExpenseCreateDTO;
} }
export interface IExpenseEventEditingPayload { export interface IExpenseEventEditingPayload {
tenantId: number;
oldExpense: Expense; oldExpense: Expense;
expenseDTO: IExpenseEditDTO; expenseDTO: EditExpenseDto;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export interface IExpenseCreatedPayload { export interface IExpenseCreatedPayload {
tenantId: number;
expenseId: number; expenseId: number;
// authorizedUser: ISystemUser;
expense: Expense; expense: Expense;
expenseDTO: IExpenseCreateDTO; expenseDTO: CreateExpenseDto;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export interface IExpenseEventEditPayload { export interface IExpenseEventEditPayload {
tenantId: number;
expenseId: number; expenseId: number;
expense: Expense; expense: Expense;
expenseDTO: IExpenseEditDTO; expenseDTO: EditExpenseDto;
// authorizedUser: ISystemUser;
oldExpense: Expense; oldExpense: Expense;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export interface IExpenseEventDeletePayload { export interface IExpenseEventDeletePayload {
tenantId: number;
expenseId: number; expenseId: number;
// authorizedUser: ISystemUser;
oldExpense: Expense; oldExpense: Expense;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export interface IExpenseDeletingPayload { export interface IExpenseDeletingPayload {
trx: Knex.Transaction; trx: Knex.Transaction;
tenantId: number;
oldExpense: Expense; oldExpense: Expense;
} }
export interface IExpenseEventPublishedPayload { export interface IExpenseEventPublishedPayload {
tenantId: number;
expenseId: number; expenseId: number;
oldExpense: Expense; oldExpense: Expense;
expense: Expense; expense: Expense;
// authorizedUser: ISystemUser;
trx: Knex.Transaction; trx: Knex.Transaction;
} }

View File

@@ -13,95 +13,190 @@ import {
IsNotEmpty, IsNotEmpty,
} from 'class-validator'; } from 'class-validator';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { ApiProperty } from '@nestjs/swagger';
export class CommandItemDto { export class CommandItemDto {
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@MaxLength(255) @MaxLength(255)
@ApiProperty({ description: 'Item name', example: 'Office Chair' })
name: string; name: string;
@IsString() @IsString()
@IsIn(['service', 'non-inventory', 'inventory']) @IsIn(['service', 'non-inventory', 'inventory'])
@ApiProperty({
description: 'Item type',
enum: ['service', 'non-inventory', 'inventory'],
example: 'inventory',
})
type: 'service' | 'non-inventory' | 'inventory'; type: 'service' | 'non-inventory' | 'inventory';
@IsOptional() @IsOptional()
@IsString() @IsString()
@MaxLength(255) @MaxLength(255)
@ApiProperty({
description: 'Item code/SKU',
required: false,
example: 'ITEM-001',
})
code?: string; code?: string;
// Purchase attributes // Purchase attributes
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiProperty({
description: 'Whether the item can be purchased',
required: false,
example: true,
})
purchasable?: boolean; purchasable?: boolean;
@IsOptional() @IsOptional()
@IsNumber({ maxDecimalPlaces: 3 }) @IsNumber({ maxDecimalPlaces: 3 })
@Min(0) @Min(0)
@ValidateIf((o) => o.purchasable === true) @ValidateIf((o) => o.purchasable === true)
@ApiProperty({
description: 'Cost price of the item',
required: false,
minimum: 0,
example: 100.5,
})
costPrice?: number; costPrice?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@Min(0) @Min(0)
@ValidateIf((o) => o.purchasable === true) @ValidateIf((o) => o.purchasable === true)
@ApiProperty({
description: 'ID of the cost account',
required: false,
minimum: 0,
example: 1,
})
costAccountId?: number; costAccountId?: number;
// Sell attributes // Sell attributes
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiProperty({
description: 'Whether the item can be sold',
required: false,
example: true,
})
sellable?: boolean; sellable?: boolean;
@IsOptional() @IsOptional()
@IsNumber({ maxDecimalPlaces: 3 }) @IsNumber({ maxDecimalPlaces: 3 })
@Min(0) @Min(0)
@ValidateIf((o) => o.sellable === true) @ValidateIf((o) => o.sellable === true)
@ApiProperty({
description: 'Selling price of the item',
required: false,
minimum: 0,
example: 150.75,
})
sellPrice?: number; sellPrice?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@Min(0) @Min(0)
@ValidateIf((o) => o.sellable === true) @ValidateIf((o) => o.sellable === true)
@ApiProperty({
description: 'ID of the sell account',
required: false,
minimum: 0,
example: 2,
})
sellAccountId?: number; sellAccountId?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@Min(0) @Min(0)
@ValidateIf((o) => o.type === 'inventory') @ValidateIf((o) => o.type === 'inventory')
@ApiProperty({
description: 'ID of the inventory account (required for inventory items)',
required: false,
minimum: 0,
example: 3,
})
inventoryAccountId?: number; inventoryAccountId?: number;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'Description shown on sales documents',
required: false,
example: 'High-quality ergonomic office chair',
})
sellDescription?: string; sellDescription?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'Description shown on purchase documents',
required: false,
example: 'Ergonomic office chair with adjustable height',
})
purchaseDescription?: string; purchaseDescription?: string;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({
description: 'ID of the tax rate applied to sales',
required: false,
example: 1,
})
sellTaxRateId?: number; sellTaxRateId?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({
description: 'ID of the tax rate applied to purchases',
required: false,
example: 1,
})
purchaseTaxRateId?: number; purchaseTaxRateId?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@Min(0) @Min(0)
@ApiProperty({
description: 'ID of the item category',
required: false,
minimum: 0,
example: 5,
})
categoryId?: number; categoryId?: number;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'Additional notes about the item',
required: false,
example: 'Available in multiple colors',
})
note?: string; note?: string;
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiProperty({
description: 'Whether the item is active',
required: false,
default: true,
example: true,
})
active?: boolean; active?: boolean;
@IsOptional() @IsOptional()
@IsArray() @IsArray()
@Type(() => Number) @Type(() => Number)
@IsInt({ each: true }) @IsInt({ each: true })
@ApiProperty({
description: 'IDs of media files associated with the item',
required: false,
type: [Number],
example: [1, 2, 3],
})
mediaIds?: number[]; mediaIds?: number[];
} }

View File

@@ -1,10 +1,12 @@
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto'; import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { import {
IsArray, IsArray,
IsBoolean, IsBoolean,
IsDate, IsDate,
IsEnum, IsEnum,
IsNotEmpty,
IsNumber, IsNumber,
IsOptional, IsOptional,
IsString, IsString,
@@ -26,18 +28,35 @@ class AttachmentDto {
} }
export class CommandSaleEstimateDto { export class CommandSaleEstimateDto {
@IsNumber() @IsNumber()
@IsNotEmpty()
@ApiProperty({
description: 'The id of the customer',
example: 1,
})
customerId: number; customerId: number;
@IsDate() @IsDate()
@Type(() => Date) @Type(() => Date)
@ApiProperty({
description: 'The date of the estimate',
example: '2021-01-01',
})
estimateDate: Date; estimateDate: Date;
@IsDate() @IsDate()
@Type(() => Date) @Type(() => Date)
@ApiProperty({
description: 'The expiration date of the estimate',
example: '2021-01-01',
})
expirationDate: Date; expirationDate: Date;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiProperty({
description: 'The reference of the estimate',
example: '123456',
})
reference?: string; reference?: string;
@IsString() @IsString()
@@ -50,53 +69,111 @@ export class CommandSaleEstimateDto {
@IsNumber() @IsNumber()
@Min(0.01) @Min(0.01)
@IsOptional() @IsOptional()
@ApiProperty({
description: 'The exchange rate of the estimate',
example: 1,
})
exchangeRate?: number; exchangeRate?: number;
@IsNumber() @IsNumber()
@IsOptional() @IsOptional()
@ApiProperty({
description: 'The id of the warehouse',
example: 1,
})
warehouseId?: number; warehouseId?: number;
@IsNumber() @IsNumber()
@IsOptional() @IsOptional()
@ApiProperty({
description: 'The id of the branch',
example: 1,
})
branchId?: number; branchId?: number;
@IsArray() @IsArray()
@MinLength(1) @MinLength(1)
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => SaleEstimateEntryDto) @Type(() => SaleEstimateEntryDto)
@ApiProperty({
description: 'The entries of the estimate',
example: [
{
index: 1,
itemId: 1,
description: 'This is a description',
quantity: 100,
cost: 100,
},
],
})
entries: SaleEstimateEntryDto[]; entries: SaleEstimateEntryDto[];
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiProperty({
description: 'The note of the estimate',
example: 'This is a note',
})
note?: string; note?: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiProperty({
description: 'The terms and conditions of the estimate',
example: 'This is a terms and conditions',
})
termsConditions?: string; termsConditions?: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiProperty({
description: 'The email to send the estimate to',
example: 'test@test.com',
})
sendToEmail?: string; sendToEmail?: string;
@IsArray() @IsArray()
@IsOptional() @IsOptional()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => AttachmentDto) @Type(() => AttachmentDto)
attachments?: AttachmentDto[]; @ApiProperty({
description: 'The attachments of the estimate',
example: [
{
key: '123456',
},
],
})
@IsNumber() @IsNumber()
@IsOptional() @IsOptional()
@ApiProperty({
description: 'The id of the pdf template',
example: 1,
})
pdfTemplateId?: number; pdfTemplateId?: number;
@IsNumber() @IsNumber()
@IsOptional() @IsOptional()
@ApiProperty({
description: 'The discount of the estimate',
example: 1,
})
discount?: number; discount?: number;
@IsEnum(DiscountType) @IsEnum(DiscountType)
@ApiProperty({
description: 'The type of the discount',
example: DiscountType.Amount,
})
discountType: DiscountType = DiscountType.Amount; discountType: DiscountType = DiscountType.Amount;
@IsNumber() @IsNumber()
@IsOptional() @IsOptional()
@ApiProperty({
description: 'The adjustment of the estimate',
example: 1,
})
adjustment?: number; adjustment?: number;
} }

View File

@@ -1,4 +1,5 @@
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto'; import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { import {
IsArray, IsArray,
@@ -31,89 +32,159 @@ class PaymentMethodDto {
class CommandSaleInvoiceDto { class CommandSaleInvoiceDto {
@IsInt() @IsInt()
@IsNotEmpty() @IsNotEmpty()
@ApiProperty({ description: 'Customer ID', example: 1 })
customerId: number; customerId: number;
@IsDate() @IsDate()
@Type(() => Date) @Type(() => Date)
@IsNotEmpty() @IsNotEmpty()
@ApiProperty({ description: 'Invoice date', example: '2023-01-01T00:00:00Z' })
invoiceDate: Date; invoiceDate: Date;
@IsDate() @IsDate()
@Type(() => Date) @Type(() => Date)
@IsNotEmpty() @IsNotEmpty()
@ApiProperty({ description: 'Due date', example: '2023-01-15T00:00:00Z' })
dueDate: Date; dueDate: Date;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'Invoice number',
required: false,
example: 'INV-001',
})
invoiceNo?: string; invoiceNo?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'Reference number',
required: false,
example: 'REF-001',
})
referenceNo?: string; referenceNo?: string;
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiProperty({
description: 'Whether the invoice is delivered',
default: false,
required: false,
})
delivered: boolean = false; delivered: boolean = false;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'Invoice message',
required: false,
example: 'Thank you for your business',
})
invoiceMessage?: string; invoiceMessage?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'Terms and conditions',
required: false,
example: 'Payment due within 14 days',
})
termsConditions?: string; termsConditions?: string;
@IsOptional() @IsOptional()
@IsNumber() @IsNumber()
@Min(0) @Min(0)
@ApiProperty({
description: 'Exchange rate',
required: false,
minimum: 0,
example: 1.0,
})
exchangeRate?: number; exchangeRate?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({ description: 'Warehouse ID', required: false, example: 1 })
warehouseId?: number; warehouseId?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({ description: 'Branch ID', required: false, example: 1 })
branchId?: number; branchId?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({ description: 'Project ID', required: false, example: 1 })
projectId?: number; projectId?: number;
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiProperty({
description: 'Whether tax is inclusive',
required: false,
example: false,
})
isInclusiveTax?: boolean; isInclusiveTax?: boolean;
@IsArray() @IsArray()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => ItemEntryDto) @Type(() => ItemEntryDto)
@MinLength(1) @MinLength(1)
@ApiProperty({
description: 'Invoice line items',
type: [ItemEntryDto],
minItems: 1,
})
entries: ItemEntryDto[]; entries: ItemEntryDto[];
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({ description: 'PDF template ID', required: false, example: 1 })
pdfTemplateId?: number; pdfTemplateId?: number;
@IsOptional() @IsOptional()
@IsArray() @IsArray()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => PaymentMethodDto) @Type(() => PaymentMethodDto)
@ApiProperty({
description: 'Payment methods',
type: [PaymentMethodDto],
required: false,
})
paymentMethods?: PaymentMethodDto[]; paymentMethods?: PaymentMethodDto[];
@IsOptional() @IsOptional()
@IsNumber() @IsNumber()
@ApiProperty({ description: 'Discount value', required: false, example: 10 })
discount?: number; discount?: number;
@IsOptional() @IsOptional()
@IsEnum(DiscountType) @IsEnum(DiscountType)
@ApiProperty({
description: 'Discount type',
enum: DiscountType,
required: false,
example: DiscountType.Percentage,
})
discountType?: DiscountType; discountType?: DiscountType;
@IsOptional() @IsOptional()
@IsNumber() @IsNumber()
@ApiProperty({
description: 'Adjustment amount',
required: false,
example: 5,
})
adjustment?: number; adjustment?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({
description: 'ID of the estimate this invoice is created from',
required: false,
example: 1,
})
fromEstimateId?: number; fromEstimateId?: number;
} }

View File

@@ -1,4 +1,5 @@
import { DiscountType } from '@/common/types/Discount'; import { DiscountType } from '@/common/types/Discount';
import { ApiProperty } from '@nestjs/swagger';
import { import {
IsEnum, IsEnum,
IsIn, IsIn,
@@ -11,66 +12,130 @@ import {
export class ItemEntryDto { export class ItemEntryDto {
@IsInt() @IsInt()
@ApiProperty({
description: 'The index of the item entry',
example: 1,
})
index: number; index: number;
@IsInt() @IsInt()
@IsNotEmpty() @IsNotEmpty()
@ApiProperty({
description: 'The id of the item',
example: 1,
})
itemId: number; itemId: number;
@IsNumber() @IsNumber()
@IsNotEmpty() @IsNotEmpty()
@ApiProperty({
description: 'The rate of the item entry',
example: 1,
})
rate: number; rate: number;
@IsNumber() @IsNumber()
@IsNotEmpty() @IsNotEmpty()
@ApiProperty({
description: 'The quantity of the item entry',
example: 1,
})
quantity: number; quantity: number;
@IsOptional() @IsOptional()
@IsNumber() @IsNumber()
@ApiProperty({
description: 'The discount of the item entry',
example: 1,
})
discount?: number; discount?: number;
@IsOptional() @IsOptional()
@IsEnum(DiscountType) @IsEnum(DiscountType)
@ApiProperty({
description: 'The type of the discount',
example: DiscountType.Percentage,
})
discountType?: DiscountType = DiscountType.Percentage; discountType?: DiscountType = DiscountType.Percentage;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'The description of the item entry',
example: 'This is a description',
})
description?: string; description?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'The tax code of the item entry',
example: '123456',
})
taxCode?: string; taxCode?: string;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({
description: 'The tax rate id of the item entry',
example: 1,
})
taxRateId?: number; taxRateId?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({
description: 'The warehouse id of the item entry',
example: 1,
})
warehouseId?: number; warehouseId?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({
description: 'The project id of the item entry',
example: 1,
})
projectId?: number; projectId?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({
description: 'The project ref id of the item entry',
example: 1,
})
projectRefId?: number; projectRefId?: number;
@IsOptional() @IsOptional()
@IsString() @IsString()
@IsIn(['TASK', 'BILL', 'EXPENSE']) @IsIn(['TASK', 'BILL', 'EXPENSE'])
@ApiProperty({
description: 'The project ref type of the item entry',
example: 'TASK',
})
projectRefType?: string; projectRefType?: string;
@IsOptional() @IsOptional()
@IsNumber() @IsNumber()
@ApiProperty({
description: 'The project ref invoiced amount of the item entry',
example: 100,
})
projectRefInvoicedAmount?: number; projectRefInvoicedAmount?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({
description: 'The sell account id of the item entry',
example: 1020,
})
sellAccountId?: number; sellAccountId?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({
description: 'The cost account id of the item entry',
example: 1021,
})
costAccountId?: number; costAccountId?: number;
} }

View File

@@ -56,7 +56,7 @@ export class TransactionsLockingController {
@ApiOperation({ summary: 'Partial unlock all transactions locking for a module or all modules' }) @ApiOperation({ summary: 'Partial unlock all transactions locking for a module or all modules' })
async unlockTransactionsLockingBetweenPeriod( async unlockTransactionsLockingBetweenPeriod(
@Body('module') module: TransactionsLockingGroup, @Body('module') module: TransactionsLockingGroup,
@Body() unlockDTO: UnlockTransactionsLockingDto, @Body() unlockDTO: ITransactionLockingPartiallyDTO,
) { ) {
const transactionMeta = const transactionMeta =
await this.transactionsLockingService.unlockTransactionsLockingPartially( await this.transactionsLockingService.unlockTransactionsLockingPartially(

View File

@@ -1,6 +1,6 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { import {
IVendorCreditEditDTO,
IVendorCreditEditedPayload, IVendorCreditEditedPayload,
IVendorCreditEditingPayload, IVendorCreditEditingPayload,
} from '../types/VendorCredit.types'; } from '../types/VendorCredit.types';
@@ -10,7 +10,6 @@ import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
import { VendorCredit } from '../models/VendorCredit'; import { VendorCredit } from '../models/VendorCredit';
import { Contact } from '@/modules/Contacts/models/Contact'; import { Contact } from '@/modules/Contacts/models/Contact';
import { events } from '@/common/events/events'; import { events } from '@/common/events/events';
import { Knex } from 'knex';
import { VendorCreditDTOTransformService } from './VendorCreditDTOTransform.service'; import { VendorCreditDTOTransformService } from './VendorCreditDTOTransform.service';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { EditVendorCreditDto } from '../dtos/VendorCredit.dto'; import { EditVendorCreditDto } from '../dtos/VendorCredit.dto';
@@ -40,7 +39,7 @@ export class EditVendorCreditService {
/** /**
* Deletes the given vendor credit. * Deletes the given vendor credit.
* @param {number} vendorCreditId - Vendor credit id. * @param {number} vendorCreditId - Vendor credit id.
* @param {EditVendorCreditDto} vendorCreditDto - * @param {EditVendorCreditDto} vendorCreditDto -
*/ */
public editVendorCredit = async ( public editVendorCredit = async (
vendorCreditId: number, vendorCreditId: number,

View File

@@ -2,7 +2,6 @@ import * as moment from 'moment';
import { omit } from 'lodash'; import { omit } from 'lodash';
import * as R from 'ramda'; import * as R from 'ramda';
import { ERRORS } from '../constants'; import { ERRORS } from '../constants';
import { IVendorCreditEntryDTO } from '../types/VendorCredit.types';
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service'; import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform'; import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform';
import { WarehouseTransactionDTOTransform } from '@/modules/Warehouses/Integrations/WarehouseTransactionDTOTransform'; import { WarehouseTransactionDTOTransform } from '@/modules/Warehouses/Integrations/WarehouseTransactionDTOTransform';
@@ -14,6 +13,7 @@ import { Injectable } from '@nestjs/common';
import { import {
CreateVendorCreditDto, CreateVendorCreditDto,
EditVendorCreditDto, EditVendorCreditDto,
VendorCreditEntryDto,
} from '../dtos/VendorCredit.dto'; } from '../dtos/VendorCredit.dto';
@Injectable() @Injectable()
@@ -52,7 +52,7 @@ export class VendorCreditDTOTransformService {
assocItemEntriesDefaultIndex, assocItemEntriesDefaultIndex,
// Associate the reference type to item entries. // Associate the reference type to item entries.
R.map((entry: IVendorCreditEntryDTO) => ({ R.map((entry: VendorCreditEntryDto) => ({
referenceType: 'VendorCredit', referenceType: 'VendorCredit',
...entry, ...entry,
})), })),

View File

@@ -14,7 +14,7 @@ enum DiscountType {
Amount = 'amount', Amount = 'amount',
} }
class VendorCreditEntryDto extends ItemEntryDto {} export class VendorCreditEntryDto extends ItemEntryDto {}
class AttachmentDto { class AttachmentDto {
@IsString() @IsString()

View File

@@ -10,13 +10,13 @@ import {
} from '@nestjs/common'; } from '@nestjs/common';
import { VendorsApplication } from './VendorsApplication.service'; import { VendorsApplication } from './VendorsApplication.service';
import { import {
IVendorEditDTO,
IVendorNewDTO,
IVendorOpeningBalanceEditDTO, IVendorOpeningBalanceEditDTO,
IVendorsFilter, IVendorsFilter,
} from './types/Vendors.types'; } from './types/Vendors.types';
import { PublicRoute } from '../Auth/Jwt.guard'; import { PublicRoute } from '../Auth/Jwt.guard';
import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { CreateVendorDto } from './dtos/CreateVendor.dto';
import { EditVendorDto } from './dtos/EditVendor.dto';
@Controller('vendors') @Controller('vendors')
@ApiTags('vendors') @ApiTags('vendors')
@@ -38,13 +38,13 @@ export class VendorsController {
@Post() @Post()
@ApiOperation({ summary: 'Create a new vendor.' }) @ApiOperation({ summary: 'Create a new vendor.' })
createVendor(@Body() vendorDTO: IVendorNewDTO) { createVendor(@Body() vendorDTO: CreateVendorDto) {
return this.vendorsApplication.createVendor(vendorDTO); return this.vendorsApplication.createVendor(vendorDTO);
} }
@Put(':id') @Put(':id')
@ApiOperation({ summary: 'Edit the given vendor.' }) @ApiOperation({ summary: 'Edit the given vendor.' })
editVendor(@Param('id') vendorId: number, @Body() vendorDTO: IVendorEditDTO) { editVendor(@Param('id') vendorId: number, @Body() vendorDTO: EditVendorDto) {
return this.vendorsApplication.editVendor(vendorId, vendorDTO); return this.vendorsApplication.editVendor(vendorId, vendorDTO);
} }

View File

@@ -6,12 +6,12 @@ import { DeleteVendorService } from './commands/DeleteVendor.service';
import { EditOpeningBalanceVendorService } from './commands/EditOpeningBalanceVendor.service'; import { EditOpeningBalanceVendorService } from './commands/EditOpeningBalanceVendor.service';
import { GetVendorService } from './queries/GetVendor'; import { GetVendorService } from './queries/GetVendor';
import { import {
IVendorEditDTO,
IVendorNewDTO,
IVendorOpeningBalanceEditDTO, IVendorOpeningBalanceEditDTO,
IVendorsFilter, IVendorsFilter,
} from './types/Vendors.types'; } from './types/Vendors.types';
import { GetVendorsService } from './queries/GetVendors.service'; import { GetVendorsService } from './queries/GetVendors.service';
import { CreateVendorDto } from './dtos/CreateVendor.dto';
import { EditVendorDto } from './dtos/EditVendor.dto';
@Injectable() @Injectable()
export class VendorsApplication { export class VendorsApplication {
@@ -29,7 +29,7 @@ export class VendorsApplication {
* @param {IVendorNewDTO} vendorDTO * @param {IVendorNewDTO} vendorDTO
* @return {Promise<void>} * @return {Promise<void>}
*/ */
public createVendor(vendorDTO: IVendorNewDTO, trx?: Knex.Transaction) { public createVendor(vendorDTO: CreateVendorDto, trx?: Knex.Transaction) {
return this.createVendorService.createVendor(vendorDTO, trx); return this.createVendorService.createVendor(vendorDTO, trx);
} }
@@ -39,7 +39,7 @@ export class VendorsApplication {
* @param {IVendorEditDTO} vendorDTO - * @param {IVendorEditDTO} vendorDTO -
* @returns {Promise<IVendor>} * @returns {Promise<IVendor>}
*/ */
public editVendor(vendorId: number, vendorDTO: IVendorEditDTO) { public editVendor(vendorId: number, vendorDTO: EditVendorDto) {
return this.editVendorService.editVendor(vendorId, vendorDTO); return this.editVendorService.editVendor(vendorId, vendorDTO);
} }
@@ -84,5 +84,5 @@ export class VendorsApplication {
*/ */
public getVendors(filterDTO: IVendorsFilter) { public getVendors(filterDTO: IVendorsFilter) {
return this.getVendorsService.getVendorsList(filterDTO); return this.getVendorsService.getVendorsList(filterDTO);
}; }
} }

View File

@@ -5,6 +5,7 @@ import { IVendorEditDTO, IVendorNewDTO } from '../types/Vendors.types';
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service'; import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
import { ContactService } from '@/modules/Contacts/types/Contacts.types'; import { ContactService } from '@/modules/Contacts/types/Contacts.types';
import { Vendor } from '../models/Vendor'; import { Vendor } from '../models/Vendor';
import { CreateVendorDto } from '../dtos/CreateVendor.dto';
@Injectable() @Injectable()
export class CreateEditVendorDTOService { export class CreateEditVendorDTOService {
@@ -30,7 +31,7 @@ export class CreateEditVendorDTOService {
* @returns {IVendorNewDTO} * @returns {IVendorNewDTO}
*/ */
public transformCreateDTO = async ( public transformCreateDTO = async (
vendorDTO: IVendorNewDTO, vendorDTO: CreateVendorDto,
): Promise<Partial<Vendor>> => { ): Promise<Partial<Vendor>> => {
const commonDTO = this.transformCommonDTO(vendorDTO); const commonDTO = this.transformCommonDTO(vendorDTO);

View File

@@ -11,6 +11,7 @@ import {
} from '../types/Vendors.types'; } from '../types/Vendors.types';
import { CreateEditVendorDTOService } from './CreateEditVendorDTO'; import { CreateEditVendorDTOService } from './CreateEditVendorDTO';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { CreateVendorDto } from '../dtos/CreateVendor.dto';
@Injectable() @Injectable()
export class CreateVendorService { export class CreateVendorService {
@@ -34,7 +35,7 @@ export class CreateVendorService {
* @param {IVendorNewDTO} vendorDTO * @param {IVendorNewDTO} vendorDTO
* @return {Promise<void>} * @return {Promise<void>}
*/ */
public async createVendor(vendorDTO: IVendorNewDTO, trx?: Knex.Transaction) { public async createVendor(vendorDTO: CreateVendorDto, trx?: Knex.Transaction) {
// Transforms create DTO to customer object. // Transforms create DTO to customer object.
const vendorObject = await this.transformDTO.transformCreateDTO(vendorDTO); const vendorObject = await this.transformDTO.transformCreateDTO(vendorDTO);

View File

@@ -1,5 +1,4 @@
import { import {
IVendorEditDTO,
IVendorEventEditedPayload, IVendorEventEditedPayload,
IVendorEventEditingPayload, IVendorEventEditingPayload,
} from '../types/Vendors.types'; } from '../types/Vendors.types';
@@ -11,6 +10,7 @@ import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { Vendor } from '../models/Vendor'; import { Vendor } from '../models/Vendor';
import { events } from '@/common/events/events'; import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { EditVendorDto } from '../dtos/EditVendor.dto';
@Injectable() @Injectable()
export class EditVendorService { export class EditVendorService {
@@ -29,7 +29,7 @@ export class EditVendorService {
* @param {IVendorEditDTO} vendorDTO - * @param {IVendorEditDTO} vendorDTO -
* @returns {Promise<IVendor>} * @returns {Promise<IVendor>}
*/ */
public async editVendor(vendorId: number, vendorDTO: IVendorEditDTO) { public async editVendor(vendorId: number, vendorDTO: EditVendorDto) {
// Retrieve the vendor or throw not found error. // Retrieve the vendor or throw not found error.
const oldVendor = await this.vendorModel() const oldVendor = await this.vendorModel()
.query() .query()

View File

@@ -0,0 +1,103 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsBoolean, IsEmail, IsString } from 'class-validator';
import { ContactAddressDto } from '@/modules/Customers/dtos/ContactAddress.dto';
import { IsInt, IsNumber } from 'class-validator';
import { IsOptional, Min } from 'class-validator';
import { IsISO8601 } from 'class-validator';
export class CreateVendorDto extends ContactAddressDto {
@ApiProperty({ required: false, description: 'Vendor opening balance' })
@IsOptional()
@IsInt()
@Min(0)
openingBalance?: number;
@ApiProperty({
required: false,
description: 'Vendor opening balance exchange rate',
default: 1,
})
@IsOptional()
@IsNumber()
@Min(0.01)
openingBalanceExchangeRate?: number;
@ApiProperty({ required: false, description: 'Date of the opening balance' })
@IsOptional()
@IsISO8601()
openingBalanceAt?: Date;
@ApiProperty({
required: false,
description: 'Branch ID for the opening balance',
})
@IsOptional()
@IsInt()
openingBalanceBranchId?: number;
@ApiProperty({ description: 'Currency code for the vendor' })
@IsOptional()
@IsString()
currencyCode: string;
@ApiProperty({ required: false, description: 'Vendor salutation' })
@IsOptional()
@IsString()
salutation?: string;
@ApiProperty({ required: false, description: 'Vendor first name' })
@IsOptional()
@IsString()
firstName?: string;
@ApiProperty({ required: false, description: 'Vendor last name' })
@IsOptional()
@IsString()
lastName?: string;
@ApiProperty({ required: false, description: 'Vendor company name' })
@IsOptional()
@IsString()
companyName?: string;
@ApiProperty({ required: false, description: 'Vendor display name' })
@IsString()
displayName: string;
@ApiProperty({ required: false, description: 'Vendor website' })
@IsOptional()
@IsString()
website?: string;
@ApiProperty({ required: false, description: 'Vendor email address' })
@IsOptional()
@IsEmail()
email?: string;
@ApiProperty({ required: false, description: 'Vendor work phone number' })
@IsOptional()
@IsString()
workPhone?: string;
@ApiProperty({ required: false, description: 'Vendor personal phone number' })
@IsOptional()
@IsString()
personalPhone?: string;
@ApiProperty({
required: false,
description: 'Additional notes about the vendor',
})
@IsOptional()
@IsString()
note?: string;
@ApiProperty({
required: false,
description: 'Whether the vendor is active',
default: true,
})
@IsOptional()
@IsBoolean()
active?: boolean;
}

View File

@@ -0,0 +1,60 @@
import { ContactAddressDto } from '@/modules/Customers/dtos/ContactAddress.dto';
import { IsEmail, IsString, IsBoolean, IsOptional } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class EditVendorDto extends ContactAddressDto {
@ApiProperty({ required: false, description: 'Vendor salutation' })
@IsOptional()
@IsString()
salutation?: string;
@ApiProperty({ required: false, description: 'Vendor first name' })
@IsOptional()
@IsString()
firstName?: string;
@ApiProperty({ required: false, description: 'Vendor last name' })
@IsOptional()
@IsString()
lastName?: string;
@ApiProperty({ required: false, description: 'Vendor company name' })
@IsOptional()
@IsString()
companyName?: string;
@ApiProperty({ required: false, description: 'Vendor display name' })
@IsOptional()
@IsString()
displayName?: string;
@ApiProperty({ required: false, description: 'Vendor website' })
@IsOptional()
@IsString()
website?: string;
@ApiProperty({ required: false, description: 'Vendor email address' })
@IsOptional()
@IsEmail()
email?: string;
@ApiProperty({ required: false, description: 'Vendor work phone number' })
@IsOptional()
@IsString()
workPhone?: string;
@ApiProperty({ required: false, description: 'Vendor personal phone number' })
@IsOptional()
@IsString()
personalPhone?: string;
@ApiProperty({ required: false, description: 'Additional notes about the vendor' })
@IsOptional()
@IsString()
note?: string;
@ApiProperty({ required: false, description: 'Whether the vendor is active' })
@IsOptional()
@IsBoolean()
active?: boolean;
}

View File

@@ -5,6 +5,8 @@ import { Vendor } from '../models/Vendor';
import { IContactAddressDTO } from '@/modules/Contacts/types/Contacts.types'; import { IContactAddressDTO } from '@/modules/Contacts/types/Contacts.types';
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types'; import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model'; import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { CreateVendorDto } from '../dtos/CreateVendor.dto';
import { EditVendorDto } from '../dtos/EditVendor.dto';
// ---------------------------------- // ----------------------------------
export interface IVendorNewDTO extends IContactAddressDTO { export interface IVendorNewDTO extends IContactAddressDTO {
@@ -60,43 +62,33 @@ export interface GetVendorsResponse {
// Vendor Events. // Vendor Events.
// ---------------------------------- // ----------------------------------
export interface IVendorEventCreatingPayload { export interface IVendorEventCreatingPayload {
// tenantId: number; vendorDTO: CreateVendorDto;
vendorDTO: IVendorNewDTO;
// authorizedUser: ISystemUser;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export interface IVendorEventCreatedPayload { export interface IVendorEventCreatedPayload {
// tenantId: number;
vendorId: number; vendorId: number;
vendor: Vendor; vendor: Vendor;
// authorizedUser: ISystemUser;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export interface IVendorEventDeletingPayload { export interface IVendorEventDeletingPayload {
// tenantId: number;
vendorId: number; vendorId: number;
oldVendor: Vendor; oldVendor: Vendor;
} }
export interface IVendorEventDeletedPayload { export interface IVendorEventDeletedPayload {
// tenantId: number;
vendorId: number; vendorId: number;
// authorizedUser: ISystemUser;
oldVendor: Vendor; oldVendor: Vendor;
trx?: Knex.Transaction; trx?: Knex.Transaction;
} }
export interface IVendorEventEditingPayload { export interface IVendorEventEditingPayload {
// tenantId: number; vendorDTO: EditVendorDto;
vendorDTO: IVendorEditDTO;
trx?: Knex.Transaction; trx?: Knex.Transaction;
} }
export interface IVendorEventEditedPayload { export interface IVendorEventEditedPayload {
// tenantId: number;
vendorId: number; vendorId: number;
vendor: Vendor; vendor: Vendor;
// authorizedUser: ISystemUser;
trx?: Knex.Transaction; trx?: Knex.Transaction;
} }
@@ -108,14 +100,12 @@ export interface IVendorOpeningBalanceEditDTO {
} }
export interface IVendorOpeningBalanceEditingPayload { export interface IVendorOpeningBalanceEditingPayload {
// tenantId: number;
oldVendor: Vendor; oldVendor: Vendor;
openingBalanceEditDTO: IVendorOpeningBalanceEditDTO; openingBalanceEditDTO: IVendorOpeningBalanceEditDTO;
trx?: Knex.Transaction; trx?: Knex.Transaction;
} }
export interface IVendorOpeningBalanceEditedPayload { export interface IVendorOpeningBalanceEditedPayload {
// tenantId: number;
vendor: Vendor; vendor: Vendor;
oldVendor: Vendor; oldVendor: Vendor;
openingBalanceEditDTO: IVendorOpeningBalanceEditDTO; openingBalanceEditDTO: IVendorOpeningBalanceEditDTO;
@@ -123,13 +113,11 @@ export interface IVendorOpeningBalanceEditedPayload {
} }
export interface IVendorActivatingPayload { export interface IVendorActivatingPayload {
// tenantId: number;
oldVendor: Vendor; oldVendor: Vendor;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export interface IVendorActivatedPayload { export interface IVendorActivatedPayload {
// tenantId: number;
vendor: Vendor; vendor: Vendor;
oldVendor: Vendor; oldVendor: Vendor;
trx?: Knex.Transaction; trx?: Knex.Transaction;

View File

@@ -1,3 +1,4 @@
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { import {
IsArray, IsArray,
@@ -39,32 +40,68 @@ export class WarehouseTransferEntryDto {
export class CommandWarehouseTransferDto { export class CommandWarehouseTransferDto {
@IsNotEmpty() @IsNotEmpty()
@IsInt() @IsInt()
@ApiProperty({
description: 'The id of the warehouse to transfer from',
example: 1,
})
fromWarehouseId: number; fromWarehouseId: number;
@IsNotEmpty() @IsNotEmpty()
@IsInt() @IsInt()
@ApiProperty({
description: 'The id of the warehouse to transfer to',
example: 2,
})
toWarehouseId: number; toWarehouseId: number;
@IsNotEmpty() @IsNotEmpty()
@IsDate() @IsDate()
@ApiProperty({
description: 'The date of the warehouse transfer',
example: '2021-01-01',
})
date: Date; date: Date;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({
description: 'The transaction number of the warehouse transfer',
example: '123456',
})
transactionNumber?: string; transactionNumber?: string;
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiProperty({
description: 'Whether the warehouse transfer has been initiated',
example: false,
})
transferInitiated: boolean = false; transferInitiated: boolean = false;
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiProperty({
description: 'Whether the warehouse transfer has been delivered',
example: false,
})
transferDelivered: boolean = false; transferDelivered: boolean = false;
@IsArray() @IsArray()
@ArrayMinSize(1) @ArrayMinSize(1)
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => WarehouseTransferEntryDto) @Type(() => WarehouseTransferEntryDto)
@ApiProperty({
description: 'The entries of the warehouse transfer',
example: [
{
index: 1,
itemId: 1,
description: 'This is a description',
quantity: 100,
cost: 100,
},
],
})
entries: WarehouseTransferEntryDto[]; entries: WarehouseTransferEntryDto[];
} }