From 2eb56e58500f6fb9b7a76d7385f481eb94893298 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sat, 22 Mar 2025 20:36:48 +0200 Subject: [PATCH] refactor: nestjs --- .../Accounts/AccountsApplication.service.ts | 10 ++ .../Accounts/ActivateAccount.service.ts | 10 +- .../src/modules/Accounts/CreateAccount.dto.ts | 52 ++++++ .../src/modules/Accounts/EditAccount.dto.ts | 26 ++- .../modules/BankRules/dtos/BankRule.dto.ts | 41 +++++ .../BankRules/queries/GetBankRule.service.ts | 2 +- .../dtos/MatchBankTransaction.dto.ts | 18 +- .../Branches/BranchesApplication.service.ts | 9 + .../src/modules/Branches/dtos/Branch.dto.ts | 46 ++++- .../CommandCreditNoteDTOTransform.service.ts | 4 +- .../CreditNotes/dtos/CreditNote.dto.ts | 2 +- .../modules/Customers/Customers.controller.ts | 12 +- .../Customers/CustomersApplication.service.ts | 13 +- .../commands/CreateCustomer.service.ts | 4 +- .../commands/EditCustomer.service.ts | 3 +- .../Customers/dtos/ContactAddress.dto.ts | 84 +++++++++ .../Customers/dtos/CreateCustomer.dto.ts | 100 +++++++++++ .../Customers/dtos/EditCustomer.dto.ts | 65 +++++++ .../src/modules/Customers/models/Customer.ts | 18 -- .../Customers/types/Customers.types.ts | 18 +- .../modules/Expenses/Expenses.controller.ts | 9 +- .../Expenses/ExpensesApplication.service.ts | 37 ++-- .../commands/CommandExpenseDTO.transformer.ts | 19 +- .../CommandExpenseValidator.service.ts | 10 +- .../commands/CreateExpense.service.ts | 6 +- .../Expenses/commands/EditExpense.service.ts | 10 +- .../src/modules/Expenses/dtos/Expense.dto.ts | 167 +++++++++++++++++- .../Expenses/interfaces/Expenses.interface.ts | 40 +---- .../src/modules/Items/dtos/Item.dto.ts | 95 ++++++++++ .../SaleEstimates/dtos/SaleEstimate.dto.ts | 81 ++++++++- .../SaleInvoices/dtos/SaleInvoice.dto.ts | 71 ++++++++ .../TransactionItemEntry/dto/ItemEntry.dto.ts | 65 +++++++ .../TransactionsLocking.controller.ts | 2 +- .../commands/EditVendorCredit.service.ts | 5 +- .../VendorCreditDTOTransform.service.ts | 4 +- .../VendorCredit/dtos/VendorCredit.dto.ts | 2 +- .../src/modules/Vendors/Vendors.controller.ts | 8 +- .../Vendors/VendorsApplication.service.ts | 10 +- .../Vendors/commands/CreateEditVendorDTO.ts | 3 +- .../Vendors/commands/CreateVendor.service.ts | 3 +- .../Vendors/commands/EditVendor.service.ts | 4 +- .../modules/Vendors/dtos/CreateVendor.dto.ts | 103 +++++++++++ .../modules/Vendors/dtos/EditVendor.dto.ts | 60 +++++++ .../modules/Vendors/types/Vendors.types.ts | 20 +-- .../dtos/WarehouseTransfer.dto.ts | 37 ++++ 45 files changed, 1210 insertions(+), 198 deletions(-) create mode 100644 packages/server-nest/src/modules/Customers/dtos/ContactAddress.dto.ts create mode 100644 packages/server-nest/src/modules/Customers/dtos/CreateCustomer.dto.ts create mode 100644 packages/server-nest/src/modules/Customers/dtos/EditCustomer.dto.ts create mode 100644 packages/server-nest/src/modules/Vendors/dtos/CreateVendor.dto.ts create mode 100644 packages/server-nest/src/modules/Vendors/dtos/EditVendor.dto.ts diff --git a/packages/server-nest/src/modules/Accounts/AccountsApplication.service.ts b/packages/server-nest/src/modules/Accounts/AccountsApplication.service.ts index 003b4d3cf..3a0858f3b 100644 --- a/packages/server-nest/src/modules/Accounts/AccountsApplication.service.ts +++ b/packages/server-nest/src/modules/Accounts/AccountsApplication.service.ts @@ -20,6 +20,16 @@ import { IFilterMeta } from '@/interfaces/Model'; @Injectable() 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( private readonly createAccountService: CreateAccountService, private readonly editAccountService: EditAccount, diff --git a/packages/server-nest/src/modules/Accounts/ActivateAccount.service.ts b/packages/server-nest/src/modules/Accounts/ActivateAccount.service.ts index d08602938..4b8ed6217 100644 --- a/packages/server-nest/src/modules/Accounts/ActivateAccount.service.ts +++ b/packages/server-nest/src/modules/Accounts/ActivateAccount.service.ts @@ -10,6 +10,12 @@ import { TenantModelProxy } from '../System/models/TenantBaseModel'; @Injectable() export class ActivateAccount { + /** + * @param {EventEmitter2} eventEmitter - The event emitter. + * @param {UnitOfWork} uow - The unit of work. + * @param {AccountRepository} accountRepository - The account repository. + * @param {TenantModelProxy} accountModel - The account model. + */ constructor( private readonly eventEmitter: EventEmitter2, private readonly uow: UnitOfWork, @@ -21,8 +27,8 @@ export class ActivateAccount { /** * Activates/Inactivates the given account. - * @param {number} accountId - * @param {boolean} activate + * @param {number} accountId - The account id. + * @param {boolean} activate - Activate or inactivate the account. */ public activateAccount = async (accountId: number, activate?: boolean) => { // Retrieve the given account or throw not found error. diff --git a/packages/server-nest/src/modules/Accounts/CreateAccount.dto.ts b/packages/server-nest/src/modules/Accounts/CreateAccount.dto.ts index ffb80d2fd..22392ff76 100644 --- a/packages/server-nest/src/modules/Accounts/CreateAccount.dto.ts +++ b/packages/server-nest/src/modules/Accounts/CreateAccount.dto.ts @@ -1,3 +1,4 @@ +import { ApiProperty } from '@nestjs/swagger'; import { IsString, IsOptional, @@ -11,41 +12,92 @@ export class CreateAccountDTO { @IsString() @MinLength(3) @MaxLength(255) // Assuming DATATYPES_LENGTH.STRING is 255 + @ApiProperty({ + description: 'Account name', + example: 'Cash Account', + minLength: 3, + maxLength: 255, + }) name: string; @IsOptional() @IsString() @MinLength(3) @MaxLength(6) + @ApiProperty({ + description: 'Account code', + example: 'CA001', + required: false, + minLength: 3, + maxLength: 6, + }) code?: string; @IsOptional() @IsString() + @ApiProperty({ + description: 'Currency code for the account', + example: 'USD', + required: false, + }) currencyCode?: string; @IsString() @MinLength(3) @MaxLength(255) + @ApiProperty({ + description: 'Type of account', + example: 'asset', + minLength: 3, + maxLength: 255, + }) accountType: string; @IsOptional() @IsString() @MaxLength(65535) + @ApiProperty({ + description: 'Account description', + example: 'Main cash account for daily operations', + required: false, + maxLength: 65535, + }) description?: string; @IsOptional() @IsInt() + @ApiProperty({ + description: 'ID of the parent account', + example: 1, + required: false, + }) parentAccountId?: number; @IsOptional() @IsBoolean() + @ApiProperty({ + description: 'Whether the account is active', + example: true, + required: false, + default: true, + }) active?: boolean; @IsOptional() @IsString() + @ApiProperty({ + description: 'Plaid account ID for syncing', + example: 'plaid_account_123456', + required: false, + }) plaidAccountId?: string; @IsOptional() @IsString() + @ApiProperty({ + description: 'Plaid item ID for syncing', + example: 'plaid_item_123456', + required: false, + }) plaidItemId?: string; } diff --git a/packages/server-nest/src/modules/Accounts/EditAccount.dto.ts b/packages/server-nest/src/modules/Accounts/EditAccount.dto.ts index 8503e6951..2def3a3b3 100644 --- a/packages/server-nest/src/modules/Accounts/EditAccount.dto.ts +++ b/packages/server-nest/src/modules/Accounts/EditAccount.dto.ts @@ -1,3 +1,4 @@ +import { ApiProperty } from '@nestjs/swagger'; import { IsString, IsOptional, @@ -9,26 +10,45 @@ import { export class EditAccountDTO { @IsString() @MinLength(3) - @MaxLength(255) // Assuming DATATYPES_LENGTH.STRING is 255 + @MaxLength(255) + @ApiProperty({ + description: 'The name of the account', + example: 'Bank Account', + }) name: string; @IsOptional() @IsString() @MinLength(3) @MaxLength(6) + @ApiProperty({ + description: 'The code of the account', + example: '123456', + }) code?: string; @IsString() @MinLength(3) - @MaxLength(255) // Assuming DATATYPES_LENGTH.STRING is 255 + @MaxLength(255) + @ApiProperty({ + description: 'The type of the account', + example: 'Bank Account', + }) accountType: string; @IsOptional() @IsString() - @MaxLength(65535) // Assuming DATATYPES_LENGTH.TEXT is 65535 + @ApiProperty({ + description: 'The description of the account', + example: 'This is a description', + }) description?: string; @IsOptional() @IsInt() + @ApiProperty({ + description: 'The parent account ID of the account', + example: 1, + }) parentAccountId?: number; } diff --git a/packages/server-nest/src/modules/BankRules/dtos/BankRule.dto.ts b/packages/server-nest/src/modules/BankRules/dtos/BankRule.dto.ts index 90da54015..00c92d1f2 100644 --- a/packages/server-nest/src/modules/BankRules/dtos/BankRule.dto.ts +++ b/packages/server-nest/src/modules/BankRules/dtos/BankRule.dto.ts @@ -11,6 +11,7 @@ import { IsNotEmpty, } from 'class-validator'; import { BankRuleComparator } from '../types'; +import { ApiProperty } from '@nestjs/swagger'; class BankRuleConditionDto { @IsNotEmpty() @@ -37,43 +38,83 @@ class BankRuleConditionDto { export class CommandBankRuleDto { @IsString() @IsNotEmpty() + @ApiProperty({ + description: 'The name of the bank rule', + example: 'Monthly Salary', + }) name: string; @IsInt() @Min(0) + @ApiProperty({ + description: 'The order of the bank rule', + example: 1, + }) order: number; @IsOptional() @IsInt() @Min(0) + @ApiProperty({ + description: 'The account ID to apply the rule if', + example: 1, + }) applyIfAccountId?: number; @IsIn(['deposit', 'withdrawal']) + @ApiProperty({ + description: 'The transaction type to apply the rule if', + example: 'deposit', + }) applyIfTransactionType: 'deposit' | 'withdrawal'; @IsString() @IsIn(['and', 'or']) + @ApiProperty({ + description: 'The conditions type to apply the rule if', + example: 'and', + }) conditionsType: 'and' | 'or' = 'and'; @IsArray() @ArrayMinSize(1) @ValidateNested({ each: true }) @Type(() => BankRuleConditionDto) + @ApiProperty({ + description: 'The conditions to apply the rule if', + example: [{ field: 'description', comparator: 'contains', value: 'Salary' }], + }) conditions: BankRuleConditionDto[]; @IsString() + @ApiProperty({ + description: 'The category to assign the rule if', + example: 'Income:Salary', + }) assignCategory: string; @IsInt() @Min(0) + @ApiProperty({ + description: 'The account ID to assign the rule if', + example: 1, + }) assignAccountId: number; @IsOptional() @IsString() + @ApiProperty({ + description: 'The payee to assign the rule if', + example: 'Employer Inc.', + }) assignPayee?: string; @IsOptional() @IsString() + @ApiProperty({ + description: 'The memo to assign the rule if', + example: 'Monthly Salary', + }) assignMemo?: string; } diff --git a/packages/server-nest/src/modules/BankRules/queries/GetBankRule.service.ts b/packages/server-nest/src/modules/BankRules/queries/GetBankRule.service.ts index 4fc2cf514..3e8609521 100644 --- a/packages/server-nest/src/modules/BankRules/queries/GetBankRule.service.ts +++ b/packages/server-nest/src/modules/BankRules/queries/GetBankRule.service.ts @@ -15,7 +15,7 @@ export class GetBankRuleService { /** * Retrieves the bank rule. - * @param {number} ruleId + * @param {number} ruleId - Rule id. * @returns {Promise} */ async getBankRule(ruleId: number): Promise { diff --git a/packages/server-nest/src/modules/BankingMatching/dtos/MatchBankTransaction.dto.ts b/packages/server-nest/src/modules/BankingMatching/dtos/MatchBankTransaction.dto.ts index f512128d6..40d2ea975 100644 --- a/packages/server-nest/src/modules/BankingMatching/dtos/MatchBankTransaction.dto.ts +++ b/packages/server-nest/src/modules/BankingMatching/dtos/MatchBankTransaction.dto.ts @@ -6,14 +6,23 @@ import { ValidateNested, } from 'class-validator'; import { Type } from 'class-transformer'; +import { ApiProperty } from '@nestjs/swagger'; export class MatchTransactionEntryDto { @IsString() @IsNotEmpty() + @ApiProperty({ + description: 'The type of the reference', + example: 'SaleInvoice', + }) referenceType: string; @IsNumber() @IsNotEmpty() + @ApiProperty({ + description: 'The ID of the reference', + example: 1, + }) referenceId: number; } @@ -21,5 +30,12 @@ export class MatchBankTransactionDto { @IsArray() @ValidateNested({ each: true }) @Type(() => MatchTransactionEntryDto) + @ApiProperty({ + description: 'The entries to match', + example: [ + { referenceType: 'SaleInvoice', referenceId: 1 }, + { referenceType: 'SaleInvoice', referenceId: 2 }, + ], + }) entries: MatchTransactionEntryDto[]; -} +} \ No newline at end of file diff --git a/packages/server-nest/src/modules/Branches/BranchesApplication.service.ts b/packages/server-nest/src/modules/Branches/BranchesApplication.service.ts index 0ca78cbcc..f151686b6 100644 --- a/packages/server-nest/src/modules/Branches/BranchesApplication.service.ts +++ b/packages/server-nest/src/modules/Branches/BranchesApplication.service.ts @@ -16,6 +16,15 @@ import { CreateBranchDto, EditBranchDto } from './dtos/Branch.dto'; @Injectable() 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( private readonly createBranchService: CreateBranchService, private readonly editBranchService: EditBranchService, diff --git a/packages/server-nest/src/modules/Branches/dtos/Branch.dto.ts b/packages/server-nest/src/modules/Branches/dtos/Branch.dto.ts index 648248fdc..732745b66 100644 --- a/packages/server-nest/src/modules/Branches/dtos/Branch.dto.ts +++ b/packages/server-nest/src/modules/Branches/dtos/Branch.dto.ts @@ -9,48 +9,76 @@ import { } from 'class-validator'; class CommandBranchDto { - @ApiProperty({ description: 'Branch name' }) + @ApiProperty({ + description: 'Branch name', + example: 'Main Branch', + }) @IsNotEmpty() @IsString() name: string; - @ApiPropertyOptional({ description: 'Branch code' }) + @ApiPropertyOptional({ + description: 'Whether this is the primary branch', + example: true, + default: false, + }) @IsOptional() @IsBoolean() primary?: boolean; - @ApiPropertyOptional({ description: 'Branch code' }) + @ApiPropertyOptional({ + description: 'Branch code', + example: 'BR001', + }) @IsOptional() @IsString() code?: string; - @ApiPropertyOptional({ description: 'Branch address' }) + @ApiPropertyOptional({ + description: 'Branch address', + example: '123 Main Street', + }) @IsOptional() @IsString() address?: string; - @ApiPropertyOptional({ description: 'Branch city' }) + @ApiPropertyOptional({ + description: 'Branch city', + example: 'New York', + }) @IsOptional() @IsString() city?: string; - @ApiPropertyOptional({ description: 'Branch country' }) + @ApiPropertyOptional({ + description: 'Branch country', + example: 'USA', + }) @IsOptional() @IsString() country?: string; - @ApiPropertyOptional({ description: 'Branch phone number' }) + @ApiPropertyOptional({ + description: 'Branch phone number', + example: '+1-555-123-4567', + }) @IsOptional() @IsString() phone_number?: string; - @ApiPropertyOptional({ description: 'Branch email' }) + @ApiPropertyOptional({ + description: 'Branch email', + example: 'branch@example.com', + }) @IsOptional() @IsEmail() @IsString() email?: string; - @ApiPropertyOptional({ description: 'Branch website' }) + @ApiPropertyOptional({ + description: 'Branch website', + example: 'https://www.example.com/branch', + }) @IsOptional() @IsUrl() @IsString() diff --git a/packages/server-nest/src/modules/CreditNotes/commands/CommandCreditNoteDTOTransform.service.ts b/packages/server-nest/src/modules/CreditNotes/commands/CommandCreditNoteDTOTransform.service.ts index 0f66e0990..5990b95af 100644 --- a/packages/server-nest/src/modules/CreditNotes/commands/CommandCreditNoteDTOTransform.service.ts +++ b/packages/server-nest/src/modules/CreditNotes/commands/CommandCreditNoteDTOTransform.service.ts @@ -17,7 +17,7 @@ import { BrandingTemplateDTOTransformer } from '../../PdfTemplate/BrandingTempla import { assocItemEntriesDefaultIndex } from '@/utils/associate-item-entries-index'; import { CreditNoteAutoIncrementService } from './CreditNoteAutoIncrement.service'; import { CreditNote } from '../models/CreditNote'; -import { CreateCreditNoteDto, EditCreditNoteDto } from '../dtos/CreditNote.dto'; +import { CreateCreditNoteDto, CreditNoteEntryDto, EditCreditNoteDto } from '../dtos/CreditNote.dto'; @Injectable() export class CommandCreditNoteDTOTransform { @@ -55,7 +55,7 @@ export class CommandCreditNoteDTOTransform { assocItemEntriesDefaultIndex, // Associate the reference type to credit note entries. - R.map((entry: ICreditNoteEntryNewDTO) => ({ + R.map((entry: CreditNoteEntryDto) => ({ ...entry, referenceType: 'CreditNote', })), diff --git a/packages/server-nest/src/modules/CreditNotes/dtos/CreditNote.dto.ts b/packages/server-nest/src/modules/CreditNotes/dtos/CreditNote.dto.ts index 251f6bc5a..ac92d49b1 100644 --- a/packages/server-nest/src/modules/CreditNotes/dtos/CreditNote.dto.ts +++ b/packages/server-nest/src/modules/CreditNotes/dtos/CreditNote.dto.ts @@ -19,7 +19,7 @@ enum DiscountType { Amount = 'amount', } -class CreditNoteEntryDto extends ItemEntryDto {} +export class CreditNoteEntryDto extends ItemEntryDto {} class AttachmentDto { @IsString() diff --git a/packages/server-nest/src/modules/Customers/Customers.controller.ts b/packages/server-nest/src/modules/Customers/Customers.controller.ts index 740c97d3c..af9c0b6e5 100644 --- a/packages/server-nest/src/modules/Customers/Customers.controller.ts +++ b/packages/server-nest/src/modules/Customers/Customers.controller.ts @@ -8,13 +8,11 @@ import { Put, } from '@nestjs/common'; import { CustomersApplication } from './CustomersApplication.service'; -import { - ICustomerEditDTO, - ICustomerNewDTO, - ICustomerOpeningBalanceEditDTO, -} from './types/Customers.types'; +import { ICustomerOpeningBalanceEditDTO } from './types/Customers.types'; import { PublicRoute } from '../Auth/Jwt.guard'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { CreateCustomerDto } from './dtos/CreateCustomer.dto'; +import { EditCustomerDto } from './dtos/EditCustomer.dto'; @Controller('customers') @ApiTags('customers') @@ -30,7 +28,7 @@ export class CustomersController { @Post() @ApiOperation({ summary: 'Create a new customer.' }) - createCustomer(@Body() customerDTO: ICustomerNewDTO) { + createCustomer(@Body() customerDTO: CreateCustomerDto) { return this.customersApplication.createCustomer(customerDTO); } @@ -38,7 +36,7 @@ export class CustomersController { @ApiOperation({ summary: 'Edit the given customer.' }) editCustomer( @Param('id') customerId: number, - @Body() customerDTO: ICustomerEditDTO, + @Body() customerDTO: EditCustomerDto, ) { return this.customersApplication.editCustomer(customerId, customerDTO); } diff --git a/packages/server-nest/src/modules/Customers/CustomersApplication.service.ts b/packages/server-nest/src/modules/Customers/CustomersApplication.service.ts index 332e6e686..4d1da82c7 100644 --- a/packages/server-nest/src/modules/Customers/CustomersApplication.service.ts +++ b/packages/server-nest/src/modules/Customers/CustomersApplication.service.ts @@ -4,12 +4,9 @@ import { CreateCustomer } from './commands/CreateCustomer.service'; import { EditCustomer } from './commands/EditCustomer.service'; import { DeleteCustomer } from './commands/DeleteCustomer.service'; import { EditOpeningBalanceCustomer } from './commands/EditOpeningBalanceCustomer.service'; -import { - ICustomerEditDTO, - ICustomerNewDTO, - ICustomerOpeningBalanceEditDTO, - // ICustomersFilter, -} from './types/Customers.types'; +import { ICustomerOpeningBalanceEditDTO } from './types/Customers.types'; +import { CreateCustomerDto } from './dtos/CreateCustomer.dto'; +import { EditCustomerDto } from './dtos/EditCustomer.dto'; @Injectable() export class CustomersApplication { @@ -36,7 +33,7 @@ export class CustomersApplication { * @param {ICustomerNewDTO} customerDTO * @returns {Promise} */ - public createCustomer = (customerDTO: ICustomerNewDTO) => { + public createCustomer = (customerDTO: CreateCustomerDto) => { return this.createCustomerService.createCustomer(customerDTO); }; @@ -46,7 +43,7 @@ export class CustomersApplication { * @param {ICustomerEditDTO} customerDTO - Customer edit DTO. * @return {Promise} */ - public editCustomer = (customerId: number, customerDTO: ICustomerEditDTO) => { + public editCustomer = (customerId: number, customerDTO: EditCustomerDto) => { return this.editCustomerService.editCustomer(customerId, customerDTO); }; diff --git a/packages/server-nest/src/modules/Customers/commands/CreateCustomer.service.ts b/packages/server-nest/src/modules/Customers/commands/CreateCustomer.service.ts index 4f40dd0c9..145656519 100644 --- a/packages/server-nest/src/modules/Customers/commands/CreateCustomer.service.ts +++ b/packages/server-nest/src/modules/Customers/commands/CreateCustomer.service.ts @@ -8,9 +8,9 @@ import { events } from '@/common/events/events'; import { ICustomerEventCreatedPayload, ICustomerEventCreatingPayload, - ICustomerNewDTO, } from '../types/Customers.types'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; +import { CreateCustomerDto } from '../dtos/CreateCustomer.dto'; @Injectable() export class CreateCustomer { @@ -35,7 +35,7 @@ export class CreateCustomer { * @return {Promise} */ public async createCustomer( - customerDTO: ICustomerNewDTO, + customerDTO: CreateCustomerDto, trx?: Knex.Transaction, ): Promise { // Transformes the customer DTO to customer object. diff --git a/packages/server-nest/src/modules/Customers/commands/EditCustomer.service.ts b/packages/server-nest/src/modules/Customers/commands/EditCustomer.service.ts index 3fa40273d..8e6b83800 100644 --- a/packages/server-nest/src/modules/Customers/commands/EditCustomer.service.ts +++ b/packages/server-nest/src/modules/Customers/commands/EditCustomer.service.ts @@ -11,6 +11,7 @@ import { events } from '@/common/events/events'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; +import { EditCustomerDto } from '../dtos/EditCustomer.dto'; @Injectable() export class EditCustomer { @@ -37,7 +38,7 @@ export class EditCustomer { */ public async editCustomer( customerId: number, - customerDTO: ICustomerEditDTO, + customerDTO: EditCustomerDto, ): Promise { // Retrieve the customer or throw not found error. const oldCustomer = await this.customerModel() diff --git a/packages/server-nest/src/modules/Customers/dtos/ContactAddress.dto.ts b/packages/server-nest/src/modules/Customers/dtos/ContactAddress.dto.ts new file mode 100644 index 000000000..8e4224eb6 --- /dev/null +++ b/packages/server-nest/src/modules/Customers/dtos/ContactAddress.dto.ts @@ -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; +} diff --git a/packages/server-nest/src/modules/Customers/dtos/CreateCustomer.dto.ts b/packages/server-nest/src/modules/Customers/dtos/CreateCustomer.dto.ts new file mode 100644 index 000000000..a3d5ff254 --- /dev/null +++ b/packages/server-nest/src/modules/Customers/dtos/CreateCustomer.dto.ts @@ -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; +} diff --git a/packages/server-nest/src/modules/Customers/dtos/EditCustomer.dto.ts b/packages/server-nest/src/modules/Customers/dtos/EditCustomer.dto.ts new file mode 100644 index 000000000..4e633bed4 --- /dev/null +++ b/packages/server-nest/src/modules/Customers/dtos/EditCustomer.dto.ts @@ -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; +} diff --git a/packages/server-nest/src/modules/Customers/models/Customer.ts b/packages/server-nest/src/modules/Customers/models/Customer.ts index f047b0382..1e720625a 100644 --- a/packages/server-nest/src/modules/Customers/models/Customer.ts +++ b/packages/server-nest/src/modules/Customers/models/Customer.ts @@ -1,23 +1,5 @@ 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{ contactService: string; diff --git a/packages/server-nest/src/modules/Customers/types/Customers.types.ts b/packages/server-nest/src/modules/Customers/types/Customers.types.ts index c3eedcb5d..0ff4bd00f 100644 --- a/packages/server-nest/src/modules/Customers/types/Customers.types.ts +++ b/packages/server-nest/src/modules/Customers/types/Customers.types.ts @@ -3,6 +3,8 @@ import { Customer } from '../models/Customer'; import { IContactAddressDTO } from '@/modules/Contacts/types/Contacts.types'; import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types'; import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model'; +import { CreateCustomerDto } from '../dtos/CreateCustomer.dto'; +import { EditCustomerDto } from '../dtos/EditCustomer.dto'; // Customer Interfaces. // ---------------------------------- @@ -63,46 +65,38 @@ export interface GetCustomersResponse { // Customer Events. // ---------------------------------- export interface ICustomerEventCreatedPayload { - // tenantId: number; customerId: number; - // authorizedUser: ISystemUser; customer: Customer; trx: Knex.Transaction; } export interface ICustomerEventCreatingPayload { - // tenantId: number; - customerDTO: ICustomerNewDTO; + customerDTO: CreateCustomerDto; trx: Knex.Transaction; } export interface ICustomerEventEditedPayload { - // tenantId: number customerId: number; customer: Customer; trx: Knex.Transaction; } export interface ICustomerEventEditingPayload { - // tenantId: number; - customerDTO: ICustomerEditDTO; + customerDTO: EditCustomerDto; customerId: number; trx: Knex.Transaction; } export interface ICustomerDeletingPayload { - // tenantId: number; customerId: number; oldCustomer: Customer; } export interface ICustomerEventDeletedPayload { - // tenantId: number; customerId: number; oldCustomer: Customer; trx: Knex.Transaction; } export interface ICustomerEventCreatingPayload { - // tenantId: number; - customerDTO: ICustomerNewDTO; + customerDTO: CreateCustomerDto; trx: Knex.Transaction; } export enum CustomerAction { @@ -141,13 +135,11 @@ export interface ICustomerOpeningBalanceEditedPayload { export interface ICustomerActivatingPayload { - // tenantId: number; trx: Knex.Transaction, oldCustomer: Customer; } export interface ICustomerActivatedPayload { - // tenantId: number; trx?: Knex.Transaction; oldCustomer: Customer; customer: Customer; diff --git a/packages/server-nest/src/modules/Expenses/Expenses.controller.ts b/packages/server-nest/src/modules/Expenses/Expenses.controller.ts index 46c646ba4..49d7e3521 100644 --- a/packages/server-nest/src/modules/Expenses/Expenses.controller.ts +++ b/packages/server-nest/src/modules/Expenses/Expenses.controller.ts @@ -9,13 +9,10 @@ import { Query, } from '@nestjs/common'; import { ExpensesApplication } from './ExpensesApplication.service'; -import { - IExpenseCreateDTO, - IExpenseEditDTO, -} from './interfaces/Expenses.interface'; import { PublicRoute } from '../Auth/Jwt.guard'; import { IExpensesFilter } from './Expenses.types'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { CreateExpenseDto, EditExpenseDto } from './dtos/Expense.dto'; @Controller('expenses') @ApiTags('expenses') @@ -29,7 +26,7 @@ export class ExpensesController { */ @Post() @ApiOperation({ summary: 'Create a new expense transaction.' }) - public createExpense(@Body() expenseDTO: IExpenseCreateDTO) { + public createExpense(@Body() expenseDTO: CreateExpenseDto) { return this.expensesApplication.createExpense(expenseDTO); } @@ -42,7 +39,7 @@ export class ExpensesController { @ApiOperation({ summary: 'Edit the given expense transaction.' }) public editExpense( @Param('id') expenseId: number, - @Body() expenseDTO: IExpenseEditDTO, + @Body() expenseDTO: EditExpenseDto, ) { return this.expensesApplication.editExpense(expenseId, expenseDTO); } diff --git a/packages/server-nest/src/modules/Expenses/ExpensesApplication.service.ts b/packages/server-nest/src/modules/Expenses/ExpensesApplication.service.ts index 9d8b21487..57b920d9d 100644 --- a/packages/server-nest/src/modules/Expenses/ExpensesApplication.service.ts +++ b/packages/server-nest/src/modules/Expenses/ExpensesApplication.service.ts @@ -4,12 +4,9 @@ import { EditExpense } from './commands/EditExpense.service'; import { DeleteExpense } from './commands/DeleteExpense.service'; import { PublishExpense } from './commands/PublishExpense.service'; import { GetExpenseService } from './queries/GetExpense.service'; -import { - IExpenseCreateDTO, - IExpenseEditDTO, - IExpensesFilter, -} from './interfaces/Expenses.interface'; +import { IExpensesFilter } from './interfaces/Expenses.interface'; import { GetExpensesService } from './queries/GetExpenses.service'; +import { CreateExpenseDto, EditExpenseDto } from './dtos/Expense.dto'; @Injectable() export class ExpensesApplication { @@ -24,55 +21,55 @@ export class ExpensesApplication { /** * Create a new expense transaction. - * @param {IExpenseDTO} expenseDTO + * @param {CreateExpenseDto} expenseDTO * @returns {Promise} */ - public createExpense = (expenseDTO: IExpenseCreateDTO) => { + public createExpense(expenseDTO: CreateExpenseDto) { return this.createExpenseService.newExpense(expenseDTO); - }; + } /** * Edits the given expense transaction. * @param {number} expenseId - Expense id. - * @param {IExpenseEditDTO} expenseDTO + * @param {EditExpenseDto} expenseDTO * @returns {Promise} */ - public editExpense = (expenseId: number, expenseDTO: IExpenseEditDTO) => { + public editExpense(expenseId: number, expenseDTO: EditExpenseDto) { return this.editExpenseService.editExpense(expenseId, expenseDTO); - }; + } /** * Deletes the given expense. * @param {number} expenseId - Expense id. * @returns {Promise} */ - public deleteExpense = (expenseId: number) => { + public deleteExpense(expenseId: number) { return this.deleteExpenseService.deleteExpense(expenseId); - }; + } /** * Publishes the given expense. * @param {number} expenseId - Expense id. * @returns {Promise} */ - public publishExpense = (expenseId: number) => { + public publishExpense(expenseId: number) { return this.publishExpenseService.publishExpense(expenseId); - }; + } /** * Retrieve the given expense details. - * @param {number} expenseId -Expense id. + * @param {number} expenseId -Expense id. * @return {Promise} */ - public getExpense = (expenseId: number) => { + public getExpense(expenseId: number) { return this.getExpenseService.getExpense(expenseId); - }; + } /** * Retrieve expenses paginated list. * @param {IExpensesFilter} expensesFilter */ - public getExpenses = (filterDTO: IExpensesFilter) => { + public getExpenses(filterDTO: IExpensesFilter) { return this.getExpensesService.getExpensesList(filterDTO); - }; + } } diff --git a/packages/server-nest/src/modules/Expenses/commands/CommandExpenseDTO.transformer.ts b/packages/server-nest/src/modules/Expenses/commands/CommandExpenseDTO.transformer.ts index 5532178f1..81f8a26cf 100644 --- a/packages/server-nest/src/modules/Expenses/commands/CommandExpenseDTO.transformer.ts +++ b/packages/server-nest/src/modules/Expenses/commands/CommandExpenseDTO.transformer.ts @@ -2,14 +2,11 @@ import { Injectable } from '@nestjs/common'; import { omit, sumBy } from 'lodash'; import * as moment from 'moment'; import * as R from 'ramda'; -import { - IExpenseCreateDTO, - IExpenseEditDTO, -} from '../interfaces/Expenses.interface'; import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform'; import { Expense } from '../models/Expense.model'; import { assocItemEntriesDefaultIndex } from '@/utils/associate-item-entries-index'; import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service'; +import { CreateExpenseDto, EditExpenseDto } from '../dtos/Expense.dto'; @Injectable() export class ExpenseDTOTransformer { @@ -28,7 +25,7 @@ export class ExpenseDTOTransformer { * @return {number} */ private getExpenseLandedCostAmount = ( - expenseDTO: IExpenseCreateDTO | IExpenseEditDTO, + expenseDTO: CreateExpenseDto | EditExpenseDto, ): number => { const landedCostEntries = expenseDTO.categories.filter((entry) => { return entry.landedCost === true; @@ -52,7 +49,7 @@ export class ExpenseDTOTransformer { * @return {IExpense} */ private expenseDTOToModel( - expenseDTO: IExpenseCreateDTO | IExpenseEditDTO, + expenseDTO: CreateExpenseDto | EditExpenseDto, ): Expense { const landedCostAmount = this.getExpenseLandedCostAmount(expenseDTO); const totalAmount = this.getExpenseCategoriesTotal(expenseDTO.categories); @@ -85,7 +82,7 @@ export class ExpenseDTOTransformer { * @returns {Promise} */ public expenseCreateDTO = async ( - expenseDTO: IExpenseCreateDTO, + expenseDTO: CreateExpenseDto | EditExpenseDto, ): Promise> => { const initialDTO = this.expenseDTOToModel(expenseDTO); const tenant = await this.tenancyContext.getTenant(true); @@ -104,13 +101,11 @@ export class ExpenseDTOTransformer { /** * Transformes the expense edit DTO. - * @param {number} tenantId - * @param {IExpenseEditDTO} expenseDTO - * @param {ISystemUser} user - * @returns {IExpense} + * @param {EditExpenseDto} expenseDTO + * @returns {Promise} */ public expenseEditDTO = async ( - expenseDTO: IExpenseEditDTO, + expenseDTO: EditExpenseDto, ): Promise => { return this.expenseDTOToModel(expenseDTO); }; diff --git a/packages/server-nest/src/modules/Expenses/commands/CommandExpenseValidator.service.ts b/packages/server-nest/src/modules/Expenses/commands/CommandExpenseValidator.service.ts index 8710f7ab0..544037044 100644 --- a/packages/server-nest/src/modules/Expenses/commands/CommandExpenseValidator.service.ts +++ b/packages/server-nest/src/modules/Expenses/commands/CommandExpenseValidator.service.ts @@ -1,14 +1,11 @@ import { sumBy, difference } from 'lodash'; 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 } from '@/modules/Accounts/models/Account.model'; import { Injectable } from '@nestjs/common'; import { Expense } from '../models/Expense.model'; import { ServiceError } from '@/modules/Items/ServiceError'; +import { CreateExpenseDto, EditExpenseDto } from '../dtos/Expense.dto'; @Injectable() export class CommandExpenseValidator { @@ -18,7 +15,7 @@ export class CommandExpenseValidator { * @throws {ServiceError} */ public validateCategoriesNotEqualZero = ( - expenseDTO: IExpenseCreateDTO | IExpenseEditDTO, + expenseDTO: CreateExpenseDto | EditExpenseDto, ) => { 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 * not found not the storage. - * @param {Array} tenantId * @param {number} expenseAccountsIds * @throws {ServiceError} * @returns {Promise} @@ -40,7 +36,6 @@ export class CommandExpenseValidator { DTOAccountsIds: number[], ) { const storedExpenseAccountsIds = expenseAccounts.map((a: Account) => a.id); - const notStoredAccountsIds = difference( DTOAccountsIds, storedExpenseAccountsIds, @@ -84,7 +79,6 @@ export class CommandExpenseValidator { /** * Validates the expense has not associated landed cost * references to the given expense. - * @param {number} tenantId * @param {number} expenseId */ public async validateNoAssociatedLandedCost(expenseId: number) { diff --git a/packages/server-nest/src/modules/Expenses/commands/CreateExpense.service.ts b/packages/server-nest/src/modules/Expenses/commands/CreateExpense.service.ts index 82144a71a..a85f82c3b 100644 --- a/packages/server-nest/src/modules/Expenses/commands/CreateExpense.service.ts +++ b/packages/server-nest/src/modules/Expenses/commands/CreateExpense.service.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Knex } from 'knex'; import { - IExpenseCreateDTO, IExpenseCreatedPayload, IExpenseCreatingPayload, } from '../interfaces/Expenses.interface'; @@ -13,6 +12,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 { CreateExpenseDto } from '../dtos/Expense.dto'; @Injectable() export class CreateExpense { @@ -41,7 +41,7 @@ export class CreateExpense { * Authorize before create a new expense transaction. * @param {IExpenseDTO} expenseDTO */ - private authorize = async (expenseDTO: IExpenseCreateDTO) => { + private authorize = async (expenseDTO: CreateExpenseDto) => { // Validate payment account existance on the storage. const paymentAccount = await this.accountModel() .query() @@ -86,7 +86,7 @@ export class CreateExpense { * @param {IExpenseDTO} expenseDTO */ public newExpense = async ( - expenseDTO: IExpenseCreateDTO, + expenseDTO: CreateExpenseDto, trx?: Knex.Transaction, ): Promise => { // Authorize before create a new expense. diff --git a/packages/server-nest/src/modules/Expenses/commands/EditExpense.service.ts b/packages/server-nest/src/modules/Expenses/commands/EditExpense.service.ts index 5b74228b4..327213b19 100644 --- a/packages/server-nest/src/modules/Expenses/commands/EditExpense.service.ts +++ b/packages/server-nest/src/modules/Expenses/commands/EditExpense.service.ts @@ -3,17 +3,16 @@ import { Knex } from 'knex'; import { IExpenseEventEditPayload, IExpenseEventEditingPayload, - IExpenseEditDTO, } from '../interfaces/Expenses.interface'; import { CommandExpenseValidator } from './CommandExpenseValidator.service'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { ExpenseDTOTransformer } from './CommandExpenseDTO.transformer'; -// import { EntriesService } from '@/services/Entries'; import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service'; import { Account } from '@/modules/Accounts/models/Account.model'; import { Expense } from '../models/Expense.model'; import { events } from '@/common/events/events'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; +import { EditExpenseDto } from '../dtos/Expense.dto'; @Injectable() export class EditExpense { @@ -30,7 +29,6 @@ export class EditExpense { private uow: UnitOfWork, private validator: CommandExpenseValidator, private transformDTO: ExpenseDTOTransformer, - // private entriesService: EntriesService, @Inject(Expense.name) private expenseModel: TenantModelProxy, @@ -40,11 +38,11 @@ export class EditExpense { /** * Authorize the DTO before editing expense transaction. - * @param {IExpenseEditDTO} expenseDTO + * @param {EditExpenseDto} expenseDTO */ public authorize = async ( oldExpense: Expense, - expenseDTO: IExpenseEditDTO, + expenseDTO: EditExpenseDto, ) => { // Validate payment account existance on the storage. const paymentAccount = await this.accountModel() @@ -102,7 +100,7 @@ export class EditExpense { */ public async editExpense( expenseId: number, - expenseDTO: IExpenseEditDTO, + expenseDTO: EditExpenseDto, ): Promise { // Retrieves the expense model or throw not found error. const oldExpense = await this.expenseModel() diff --git a/packages/server-nest/src/modules/Expenses/dtos/Expense.dto.ts b/packages/server-nest/src/modules/Expenses/dtos/Expense.dto.ts index d2427e95a..3535d8247 100644 --- a/packages/server-nest/src/modules/Expenses/dtos/Expense.dto.ts +++ b/packages/server-nest/src/modules/Expenses/dtos/Expense.dto.ts @@ -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 {} diff --git a/packages/server-nest/src/modules/Expenses/interfaces/Expenses.interface.ts b/packages/server-nest/src/modules/Expenses/interfaces/Expenses.interface.ts index 55c138867..2337d94ab 100644 --- a/packages/server-nest/src/modules/Expenses/interfaces/Expenses.interface.ts +++ b/packages/server-nest/src/modules/Expenses/interfaces/Expenses.interface.ts @@ -1,6 +1,7 @@ import { IFilterRole } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types'; import { Knex } from 'knex'; import { Expense } from '../models/Expense.model'; +import { CreateExpenseDto, EditExpenseDto } from '../dtos/Expense.dto'; // import { AttachmentLinkDTO } from '../Attachments/Attachments'; @@ -20,26 +21,6 @@ export interface IExpensesFilter { 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 { id?: number; expenseAccountId: number; @@ -52,55 +33,44 @@ export interface IExpenseCategoryDTO { } export interface IExpenseCreatingPayload { + expenseDTO: CreateExpenseDto; trx: Knex.Transaction; - tenantId: number; - expenseDTO: IExpenseCreateDTO; } export interface IExpenseEventEditingPayload { - tenantId: number; oldExpense: Expense; - expenseDTO: IExpenseEditDTO; + expenseDTO: EditExpenseDto; trx: Knex.Transaction; } export interface IExpenseCreatedPayload { - tenantId: number; expenseId: number; - // authorizedUser: ISystemUser; expense: Expense; - expenseDTO: IExpenseCreateDTO; + expenseDTO: CreateExpenseDto; trx: Knex.Transaction; } export interface IExpenseEventEditPayload { - tenantId: number; expenseId: number; expense: Expense; - expenseDTO: IExpenseEditDTO; - // authorizedUser: ISystemUser; + expenseDTO: EditExpenseDto; oldExpense: Expense; trx: Knex.Transaction; } export interface IExpenseEventDeletePayload { - tenantId: number; expenseId: number; - // authorizedUser: ISystemUser; oldExpense: Expense; trx: Knex.Transaction; } export interface IExpenseDeletingPayload { trx: Knex.Transaction; - tenantId: number; oldExpense: Expense; } export interface IExpenseEventPublishedPayload { - tenantId: number; expenseId: number; oldExpense: Expense; expense: Expense; - // authorizedUser: ISystemUser; trx: Knex.Transaction; } diff --git a/packages/server-nest/src/modules/Items/dtos/Item.dto.ts b/packages/server-nest/src/modules/Items/dtos/Item.dto.ts index 3d95fadae..9df366807 100644 --- a/packages/server-nest/src/modules/Items/dtos/Item.dto.ts +++ b/packages/server-nest/src/modules/Items/dtos/Item.dto.ts @@ -13,95 +13,190 @@ import { IsNotEmpty, } from 'class-validator'; import { Type } from 'class-transformer'; +import { ApiProperty } from '@nestjs/swagger'; export class CommandItemDto { @IsString() @IsNotEmpty() @MaxLength(255) + @ApiProperty({ description: 'Item name', example: 'Office Chair' }) name: string; @IsString() @IsIn(['service', 'non-inventory', 'inventory']) + @ApiProperty({ + description: 'Item type', + enum: ['service', 'non-inventory', 'inventory'], + example: 'inventory', + }) type: 'service' | 'non-inventory' | 'inventory'; @IsOptional() @IsString() @MaxLength(255) + @ApiProperty({ + description: 'Item code/SKU', + required: false, + example: 'ITEM-001', + }) code?: string; // Purchase attributes @IsOptional() @IsBoolean() + @ApiProperty({ + description: 'Whether the item can be purchased', + required: false, + example: true, + }) purchasable?: boolean; @IsOptional() @IsNumber({ maxDecimalPlaces: 3 }) @Min(0) @ValidateIf((o) => o.purchasable === true) + @ApiProperty({ + description: 'Cost price of the item', + required: false, + minimum: 0, + example: 100.5, + }) costPrice?: number; @IsOptional() @IsInt() @Min(0) @ValidateIf((o) => o.purchasable === true) + @ApiProperty({ + description: 'ID of the cost account', + required: false, + minimum: 0, + example: 1, + }) costAccountId?: number; // Sell attributes @IsOptional() @IsBoolean() + @ApiProperty({ + description: 'Whether the item can be sold', + required: false, + example: true, + }) sellable?: boolean; @IsOptional() @IsNumber({ maxDecimalPlaces: 3 }) @Min(0) @ValidateIf((o) => o.sellable === true) + @ApiProperty({ + description: 'Selling price of the item', + required: false, + minimum: 0, + example: 150.75, + }) sellPrice?: number; @IsOptional() @IsInt() @Min(0) @ValidateIf((o) => o.sellable === true) + @ApiProperty({ + description: 'ID of the sell account', + required: false, + minimum: 0, + example: 2, + }) sellAccountId?: number; @IsOptional() @IsInt() @Min(0) @ValidateIf((o) => o.type === 'inventory') + @ApiProperty({ + description: 'ID of the inventory account (required for inventory items)', + required: false, + minimum: 0, + example: 3, + }) inventoryAccountId?: number; @IsOptional() @IsString() + @ApiProperty({ + description: 'Description shown on sales documents', + required: false, + example: 'High-quality ergonomic office chair', + }) sellDescription?: string; @IsOptional() @IsString() + @ApiProperty({ + description: 'Description shown on purchase documents', + required: false, + example: 'Ergonomic office chair with adjustable height', + }) purchaseDescription?: string; @IsOptional() @IsInt() + @ApiProperty({ + description: 'ID of the tax rate applied to sales', + required: false, + example: 1, + }) sellTaxRateId?: number; @IsOptional() @IsInt() + @ApiProperty({ + description: 'ID of the tax rate applied to purchases', + required: false, + example: 1, + }) purchaseTaxRateId?: number; @IsOptional() @IsInt() @Min(0) + @ApiProperty({ + description: 'ID of the item category', + required: false, + minimum: 0, + example: 5, + }) categoryId?: number; @IsOptional() @IsString() + @ApiProperty({ + description: 'Additional notes about the item', + required: false, + example: 'Available in multiple colors', + }) note?: string; @IsOptional() @IsBoolean() + @ApiProperty({ + description: 'Whether the item is active', + required: false, + default: true, + example: true, + }) active?: boolean; @IsOptional() @IsArray() @Type(() => Number) @IsInt({ each: true }) + @ApiProperty({ + description: 'IDs of media files associated with the item', + required: false, + type: [Number], + example: [1, 2, 3], + }) mediaIds?: number[]; } diff --git a/packages/server-nest/src/modules/SaleEstimates/dtos/SaleEstimate.dto.ts b/packages/server-nest/src/modules/SaleEstimates/dtos/SaleEstimate.dto.ts index 4a374b770..bed501d40 100644 --- a/packages/server-nest/src/modules/SaleEstimates/dtos/SaleEstimate.dto.ts +++ b/packages/server-nest/src/modules/SaleEstimates/dtos/SaleEstimate.dto.ts @@ -1,10 +1,12 @@ import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto'; +import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsArray, IsBoolean, IsDate, IsEnum, + IsNotEmpty, IsNumber, IsOptional, IsString, @@ -26,18 +28,35 @@ class AttachmentDto { } export class CommandSaleEstimateDto { @IsNumber() + @IsNotEmpty() + @ApiProperty({ + description: 'The id of the customer', + example: 1, + }) customerId: number; @IsDate() @Type(() => Date) + @ApiProperty({ + description: 'The date of the estimate', + example: '2021-01-01', + }) estimateDate: Date; @IsDate() @Type(() => Date) + @ApiProperty({ + description: 'The expiration date of the estimate', + example: '2021-01-01', + }) expirationDate: Date; @IsString() @IsOptional() + @ApiProperty({ + description: 'The reference of the estimate', + example: '123456', + }) reference?: string; @IsString() @@ -50,53 +69,111 @@ export class CommandSaleEstimateDto { @IsNumber() @Min(0.01) @IsOptional() + @ApiProperty({ + description: 'The exchange rate of the estimate', + example: 1, + }) exchangeRate?: number; @IsNumber() @IsOptional() + @ApiProperty({ + description: 'The id of the warehouse', + example: 1, + }) warehouseId?: number; @IsNumber() @IsOptional() + @ApiProperty({ + description: 'The id of the branch', + example: 1, + }) branchId?: number; @IsArray() @MinLength(1) @ValidateNested({ each: true }) @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[]; @IsString() @IsOptional() + @ApiProperty({ + description: 'The note of the estimate', + example: 'This is a note', + }) note?: string; @IsString() @IsOptional() + @ApiProperty({ + description: 'The terms and conditions of the estimate', + example: 'This is a terms and conditions', + }) termsConditions?: string; @IsString() @IsOptional() + @ApiProperty({ + description: 'The email to send the estimate to', + example: 'test@test.com', + }) sendToEmail?: string; @IsArray() @IsOptional() @ValidateNested({ each: true }) @Type(() => AttachmentDto) - attachments?: AttachmentDto[]; - + @ApiProperty({ + description: 'The attachments of the estimate', + example: [ + { + key: '123456', + }, + ], + }) @IsNumber() @IsOptional() + @ApiProperty({ + description: 'The id of the pdf template', + example: 1, + }) pdfTemplateId?: number; @IsNumber() @IsOptional() + @ApiProperty({ + description: 'The discount of the estimate', + example: 1, + }) discount?: number; @IsEnum(DiscountType) + @ApiProperty({ + description: 'The type of the discount', + example: DiscountType.Amount, + }) discountType: DiscountType = DiscountType.Amount; @IsNumber() @IsOptional() + @ApiProperty({ + description: 'The adjustment of the estimate', + example: 1, + }) adjustment?: number; } diff --git a/packages/server-nest/src/modules/SaleInvoices/dtos/SaleInvoice.dto.ts b/packages/server-nest/src/modules/SaleInvoices/dtos/SaleInvoice.dto.ts index 2713d4b21..6b1a052a6 100644 --- a/packages/server-nest/src/modules/SaleInvoices/dtos/SaleInvoice.dto.ts +++ b/packages/server-nest/src/modules/SaleInvoices/dtos/SaleInvoice.dto.ts @@ -1,4 +1,5 @@ import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto'; +import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsArray, @@ -31,89 +32,159 @@ class PaymentMethodDto { class CommandSaleInvoiceDto { @IsInt() @IsNotEmpty() + @ApiProperty({ description: 'Customer ID', example: 1 }) customerId: number; @IsDate() @Type(() => Date) @IsNotEmpty() + @ApiProperty({ description: 'Invoice date', example: '2023-01-01T00:00:00Z' }) invoiceDate: Date; @IsDate() @Type(() => Date) @IsNotEmpty() + @ApiProperty({ description: 'Due date', example: '2023-01-15T00:00:00Z' }) dueDate: Date; @IsOptional() @IsString() + @ApiProperty({ + description: 'Invoice number', + required: false, + example: 'INV-001', + }) invoiceNo?: string; @IsOptional() @IsString() + @ApiProperty({ + description: 'Reference number', + required: false, + example: 'REF-001', + }) referenceNo?: string; @IsOptional() @IsBoolean() + @ApiProperty({ + description: 'Whether the invoice is delivered', + default: false, + required: false, + }) delivered: boolean = false; @IsOptional() @IsString() + @ApiProperty({ + description: 'Invoice message', + required: false, + example: 'Thank you for your business', + }) invoiceMessage?: string; @IsOptional() @IsString() + @ApiProperty({ + description: 'Terms and conditions', + required: false, + example: 'Payment due within 14 days', + }) termsConditions?: string; @IsOptional() @IsNumber() @Min(0) + @ApiProperty({ + description: 'Exchange rate', + required: false, + minimum: 0, + example: 1.0, + }) exchangeRate?: number; @IsOptional() @IsInt() + @ApiProperty({ description: 'Warehouse ID', required: false, example: 1 }) warehouseId?: number; @IsOptional() @IsInt() + @ApiProperty({ description: 'Branch ID', required: false, example: 1 }) branchId?: number; @IsOptional() @IsInt() + @ApiProperty({ description: 'Project ID', required: false, example: 1 }) projectId?: number; @IsOptional() @IsBoolean() + @ApiProperty({ + description: 'Whether tax is inclusive', + required: false, + example: false, + }) isInclusiveTax?: boolean; @IsArray() @ValidateNested({ each: true }) @Type(() => ItemEntryDto) @MinLength(1) + @ApiProperty({ + description: 'Invoice line items', + type: [ItemEntryDto], + minItems: 1, + }) entries: ItemEntryDto[]; @IsOptional() @IsInt() + @ApiProperty({ description: 'PDF template ID', required: false, example: 1 }) pdfTemplateId?: number; @IsOptional() @IsArray() @ValidateNested({ each: true }) @Type(() => PaymentMethodDto) + @ApiProperty({ + description: 'Payment methods', + type: [PaymentMethodDto], + required: false, + }) paymentMethods?: PaymentMethodDto[]; @IsOptional() @IsNumber() + @ApiProperty({ description: 'Discount value', required: false, example: 10 }) discount?: number; @IsOptional() @IsEnum(DiscountType) + @ApiProperty({ + description: 'Discount type', + enum: DiscountType, + required: false, + example: DiscountType.Percentage, + }) discountType?: DiscountType; @IsOptional() @IsNumber() + @ApiProperty({ + description: 'Adjustment amount', + required: false, + example: 5, + }) adjustment?: number; @IsOptional() @IsInt() + @ApiProperty({ + description: 'ID of the estimate this invoice is created from', + required: false, + example: 1, + }) fromEstimateId?: number; } diff --git a/packages/server-nest/src/modules/TransactionItemEntry/dto/ItemEntry.dto.ts b/packages/server-nest/src/modules/TransactionItemEntry/dto/ItemEntry.dto.ts index 3b22aa955..20a815ead 100644 --- a/packages/server-nest/src/modules/TransactionItemEntry/dto/ItemEntry.dto.ts +++ b/packages/server-nest/src/modules/TransactionItemEntry/dto/ItemEntry.dto.ts @@ -1,4 +1,5 @@ import { DiscountType } from '@/common/types/Discount'; +import { ApiProperty } from '@nestjs/swagger'; import { IsEnum, IsIn, @@ -11,66 +12,130 @@ import { export class ItemEntryDto { @IsInt() + @ApiProperty({ + description: 'The index of the item entry', + example: 1, + }) index: number; @IsInt() @IsNotEmpty() + @ApiProperty({ + description: 'The id of the item', + example: 1, + }) itemId: number; @IsNumber() @IsNotEmpty() + @ApiProperty({ + description: 'The rate of the item entry', + example: 1, + }) rate: number; @IsNumber() @IsNotEmpty() + @ApiProperty({ + description: 'The quantity of the item entry', + example: 1, + }) quantity: number; @IsOptional() @IsNumber() + @ApiProperty({ + description: 'The discount of the item entry', + example: 1, + }) discount?: number; @IsOptional() @IsEnum(DiscountType) + @ApiProperty({ + description: 'The type of the discount', + example: DiscountType.Percentage, + }) discountType?: DiscountType = DiscountType.Percentage; @IsOptional() @IsString() + @ApiProperty({ + description: 'The description of the item entry', + example: 'This is a description', + }) description?: string; @IsOptional() @IsString() + @ApiProperty({ + description: 'The tax code of the item entry', + example: '123456', + }) taxCode?: string; @IsOptional() @IsInt() + @ApiProperty({ + description: 'The tax rate id of the item entry', + example: 1, + }) taxRateId?: number; @IsOptional() @IsInt() + @ApiProperty({ + description: 'The warehouse id of the item entry', + example: 1, + }) warehouseId?: number; @IsOptional() @IsInt() + @ApiProperty({ + description: 'The project id of the item entry', + example: 1, + }) projectId?: number; @IsOptional() @IsInt() + @ApiProperty({ + description: 'The project ref id of the item entry', + example: 1, + }) projectRefId?: number; @IsOptional() @IsString() @IsIn(['TASK', 'BILL', 'EXPENSE']) + @ApiProperty({ + description: 'The project ref type of the item entry', + example: 'TASK', + }) projectRefType?: string; @IsOptional() @IsNumber() + @ApiProperty({ + description: 'The project ref invoiced amount of the item entry', + example: 100, + }) projectRefInvoicedAmount?: number; @IsOptional() @IsInt() + @ApiProperty({ + description: 'The sell account id of the item entry', + example: 1020, + }) sellAccountId?: number; @IsOptional() @IsInt() + @ApiProperty({ + description: 'The cost account id of the item entry', + example: 1021, + }) costAccountId?: number; } diff --git a/packages/server-nest/src/modules/TransactionsLocking/TransactionsLocking.controller.ts b/packages/server-nest/src/modules/TransactionsLocking/TransactionsLocking.controller.ts index 1390378c7..9e69e11e2 100644 --- a/packages/server-nest/src/modules/TransactionsLocking/TransactionsLocking.controller.ts +++ b/packages/server-nest/src/modules/TransactionsLocking/TransactionsLocking.controller.ts @@ -56,7 +56,7 @@ export class TransactionsLockingController { @ApiOperation({ summary: 'Partial unlock all transactions locking for a module or all modules' }) async unlockTransactionsLockingBetweenPeriod( @Body('module') module: TransactionsLockingGroup, - @Body() unlockDTO: UnlockTransactionsLockingDto, + @Body() unlockDTO: ITransactionLockingPartiallyDTO, ) { const transactionMeta = await this.transactionsLockingService.unlockTransactionsLockingPartially( diff --git a/packages/server-nest/src/modules/VendorCredit/commands/EditVendorCredit.service.ts b/packages/server-nest/src/modules/VendorCredit/commands/EditVendorCredit.service.ts index 3aab9502c..89236b249 100644 --- a/packages/server-nest/src/modules/VendorCredit/commands/EditVendorCredit.service.ts +++ b/packages/server-nest/src/modules/VendorCredit/commands/EditVendorCredit.service.ts @@ -1,6 +1,6 @@ +import { Knex } from 'knex'; import { Inject, Injectable } from '@nestjs/common'; import { - IVendorCreditEditDTO, IVendorCreditEditedPayload, IVendorCreditEditingPayload, } from '../types/VendorCredit.types'; @@ -10,7 +10,6 @@ import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service'; import { VendorCredit } from '../models/VendorCredit'; import { Contact } from '@/modules/Contacts/models/Contact'; import { events } from '@/common/events/events'; -import { Knex } from 'knex'; import { VendorCreditDTOTransformService } from './VendorCreditDTOTransform.service'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; import { EditVendorCreditDto } from '../dtos/VendorCredit.dto'; @@ -40,7 +39,7 @@ export class EditVendorCreditService { /** * Deletes the given vendor credit. * @param {number} vendorCreditId - Vendor credit id. - * @param {EditVendorCreditDto} vendorCreditDto - + * @param {EditVendorCreditDto} vendorCreditDto - */ public editVendorCredit = async ( vendorCreditId: number, diff --git a/packages/server-nest/src/modules/VendorCredit/commands/VendorCreditDTOTransform.service.ts b/packages/server-nest/src/modules/VendorCredit/commands/VendorCreditDTOTransform.service.ts index 104b232ae..17b377a98 100644 --- a/packages/server-nest/src/modules/VendorCredit/commands/VendorCreditDTOTransform.service.ts +++ b/packages/server-nest/src/modules/VendorCredit/commands/VendorCreditDTOTransform.service.ts @@ -2,7 +2,6 @@ import * as moment from 'moment'; import { omit } from 'lodash'; import * as R from 'ramda'; import { ERRORS } from '../constants'; -import { IVendorCreditEntryDTO } from '../types/VendorCredit.types'; import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service'; import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform'; import { WarehouseTransactionDTOTransform } from '@/modules/Warehouses/Integrations/WarehouseTransactionDTOTransform'; @@ -14,6 +13,7 @@ import { Injectable } from '@nestjs/common'; import { CreateVendorCreditDto, EditVendorCreditDto, + VendorCreditEntryDto, } from '../dtos/VendorCredit.dto'; @Injectable() @@ -52,7 +52,7 @@ export class VendorCreditDTOTransformService { assocItemEntriesDefaultIndex, // Associate the reference type to item entries. - R.map((entry: IVendorCreditEntryDTO) => ({ + R.map((entry: VendorCreditEntryDto) => ({ referenceType: 'VendorCredit', ...entry, })), diff --git a/packages/server-nest/src/modules/VendorCredit/dtos/VendorCredit.dto.ts b/packages/server-nest/src/modules/VendorCredit/dtos/VendorCredit.dto.ts index f9965c8d4..f1cb21eb8 100644 --- a/packages/server-nest/src/modules/VendorCredit/dtos/VendorCredit.dto.ts +++ b/packages/server-nest/src/modules/VendorCredit/dtos/VendorCredit.dto.ts @@ -14,7 +14,7 @@ enum DiscountType { Amount = 'amount', } -class VendorCreditEntryDto extends ItemEntryDto {} +export class VendorCreditEntryDto extends ItemEntryDto {} class AttachmentDto { @IsString() diff --git a/packages/server-nest/src/modules/Vendors/Vendors.controller.ts b/packages/server-nest/src/modules/Vendors/Vendors.controller.ts index ccb7bddb3..db968072e 100644 --- a/packages/server-nest/src/modules/Vendors/Vendors.controller.ts +++ b/packages/server-nest/src/modules/Vendors/Vendors.controller.ts @@ -10,13 +10,13 @@ import { } from '@nestjs/common'; import { VendorsApplication } from './VendorsApplication.service'; import { - IVendorEditDTO, - IVendorNewDTO, IVendorOpeningBalanceEditDTO, IVendorsFilter, } from './types/Vendors.types'; import { PublicRoute } from '../Auth/Jwt.guard'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { CreateVendorDto } from './dtos/CreateVendor.dto'; +import { EditVendorDto } from './dtos/EditVendor.dto'; @Controller('vendors') @ApiTags('vendors') @@ -38,13 +38,13 @@ export class VendorsController { @Post() @ApiOperation({ summary: 'Create a new vendor.' }) - createVendor(@Body() vendorDTO: IVendorNewDTO) { + createVendor(@Body() vendorDTO: CreateVendorDto) { return this.vendorsApplication.createVendor(vendorDTO); } @Put(':id') @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); } diff --git a/packages/server-nest/src/modules/Vendors/VendorsApplication.service.ts b/packages/server-nest/src/modules/Vendors/VendorsApplication.service.ts index 4bd1c1448..1508c1352 100644 --- a/packages/server-nest/src/modules/Vendors/VendorsApplication.service.ts +++ b/packages/server-nest/src/modules/Vendors/VendorsApplication.service.ts @@ -6,12 +6,12 @@ import { DeleteVendorService } from './commands/DeleteVendor.service'; import { EditOpeningBalanceVendorService } from './commands/EditOpeningBalanceVendor.service'; import { GetVendorService } from './queries/GetVendor'; import { - IVendorEditDTO, - IVendorNewDTO, IVendorOpeningBalanceEditDTO, IVendorsFilter, } from './types/Vendors.types'; import { GetVendorsService } from './queries/GetVendors.service'; +import { CreateVendorDto } from './dtos/CreateVendor.dto'; +import { EditVendorDto } from './dtos/EditVendor.dto'; @Injectable() export class VendorsApplication { @@ -29,7 +29,7 @@ export class VendorsApplication { * @param {IVendorNewDTO} vendorDTO * @return {Promise} */ - public createVendor(vendorDTO: IVendorNewDTO, trx?: Knex.Transaction) { + public createVendor(vendorDTO: CreateVendorDto, trx?: Knex.Transaction) { return this.createVendorService.createVendor(vendorDTO, trx); } @@ -39,7 +39,7 @@ export class VendorsApplication { * @param {IVendorEditDTO} vendorDTO - * @returns {Promise} */ - public editVendor(vendorId: number, vendorDTO: IVendorEditDTO) { + public editVendor(vendorId: number, vendorDTO: EditVendorDto) { return this.editVendorService.editVendor(vendorId, vendorDTO); } @@ -84,5 +84,5 @@ export class VendorsApplication { */ public getVendors(filterDTO: IVendorsFilter) { return this.getVendorsService.getVendorsList(filterDTO); - }; + } } diff --git a/packages/server-nest/src/modules/Vendors/commands/CreateEditVendorDTO.ts b/packages/server-nest/src/modules/Vendors/commands/CreateEditVendorDTO.ts index 000643683..ce8e0f8a6 100644 --- a/packages/server-nest/src/modules/Vendors/commands/CreateEditVendorDTO.ts +++ b/packages/server-nest/src/modules/Vendors/commands/CreateEditVendorDTO.ts @@ -5,6 +5,7 @@ import { IVendorEditDTO, IVendorNewDTO } from '../types/Vendors.types'; import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service'; import { ContactService } from '@/modules/Contacts/types/Contacts.types'; import { Vendor } from '../models/Vendor'; +import { CreateVendorDto } from '../dtos/CreateVendor.dto'; @Injectable() export class CreateEditVendorDTOService { @@ -30,7 +31,7 @@ export class CreateEditVendorDTOService { * @returns {IVendorNewDTO} */ public transformCreateDTO = async ( - vendorDTO: IVendorNewDTO, + vendorDTO: CreateVendorDto, ): Promise> => { const commonDTO = this.transformCommonDTO(vendorDTO); diff --git a/packages/server-nest/src/modules/Vendors/commands/CreateVendor.service.ts b/packages/server-nest/src/modules/Vendors/commands/CreateVendor.service.ts index 88b76e152..fcc7f69b8 100644 --- a/packages/server-nest/src/modules/Vendors/commands/CreateVendor.service.ts +++ b/packages/server-nest/src/modules/Vendors/commands/CreateVendor.service.ts @@ -11,6 +11,7 @@ import { } from '../types/Vendors.types'; import { CreateEditVendorDTOService } from './CreateEditVendorDTO'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; +import { CreateVendorDto } from '../dtos/CreateVendor.dto'; @Injectable() export class CreateVendorService { @@ -34,7 +35,7 @@ export class CreateVendorService { * @param {IVendorNewDTO} vendorDTO * @return {Promise} */ - public async createVendor(vendorDTO: IVendorNewDTO, trx?: Knex.Transaction) { + public async createVendor(vendorDTO: CreateVendorDto, trx?: Knex.Transaction) { // Transforms create DTO to customer object. const vendorObject = await this.transformDTO.transformCreateDTO(vendorDTO); diff --git a/packages/server-nest/src/modules/Vendors/commands/EditVendor.service.ts b/packages/server-nest/src/modules/Vendors/commands/EditVendor.service.ts index 25312f193..90bfc2f9a 100644 --- a/packages/server-nest/src/modules/Vendors/commands/EditVendor.service.ts +++ b/packages/server-nest/src/modules/Vendors/commands/EditVendor.service.ts @@ -1,5 +1,4 @@ import { - IVendorEditDTO, IVendorEventEditedPayload, IVendorEventEditingPayload, } from '../types/Vendors.types'; @@ -11,6 +10,7 @@ import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service'; import { Vendor } from '../models/Vendor'; import { events } from '@/common/events/events'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; +import { EditVendorDto } from '../dtos/EditVendor.dto'; @Injectable() export class EditVendorService { @@ -29,7 +29,7 @@ export class EditVendorService { * @param {IVendorEditDTO} vendorDTO - * @returns {Promise} */ - public async editVendor(vendorId: number, vendorDTO: IVendorEditDTO) { + public async editVendor(vendorId: number, vendorDTO: EditVendorDto) { // Retrieve the vendor or throw not found error. const oldVendor = await this.vendorModel() .query() diff --git a/packages/server-nest/src/modules/Vendors/dtos/CreateVendor.dto.ts b/packages/server-nest/src/modules/Vendors/dtos/CreateVendor.dto.ts new file mode 100644 index 000000000..a83e0bbc7 --- /dev/null +++ b/packages/server-nest/src/modules/Vendors/dtos/CreateVendor.dto.ts @@ -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; +} diff --git a/packages/server-nest/src/modules/Vendors/dtos/EditVendor.dto.ts b/packages/server-nest/src/modules/Vendors/dtos/EditVendor.dto.ts new file mode 100644 index 000000000..1396b948a --- /dev/null +++ b/packages/server-nest/src/modules/Vendors/dtos/EditVendor.dto.ts @@ -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; +} diff --git a/packages/server-nest/src/modules/Vendors/types/Vendors.types.ts b/packages/server-nest/src/modules/Vendors/types/Vendors.types.ts index 9b8aadb9d..4357bb7b4 100644 --- a/packages/server-nest/src/modules/Vendors/types/Vendors.types.ts +++ b/packages/server-nest/src/modules/Vendors/types/Vendors.types.ts @@ -5,6 +5,8 @@ import { Vendor } from '../models/Vendor'; import { IContactAddressDTO } from '@/modules/Contacts/types/Contacts.types'; import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types'; import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model'; +import { CreateVendorDto } from '../dtos/CreateVendor.dto'; +import { EditVendorDto } from '../dtos/EditVendor.dto'; // ---------------------------------- export interface IVendorNewDTO extends IContactAddressDTO { @@ -60,43 +62,33 @@ export interface GetVendorsResponse { // Vendor Events. // ---------------------------------- export interface IVendorEventCreatingPayload { - // tenantId: number; - vendorDTO: IVendorNewDTO; - // authorizedUser: ISystemUser; + vendorDTO: CreateVendorDto; trx: Knex.Transaction; } export interface IVendorEventCreatedPayload { - // tenantId: number; vendorId: number; vendor: Vendor; - // authorizedUser: ISystemUser; trx: Knex.Transaction; } export interface IVendorEventDeletingPayload { - // tenantId: number; vendorId: number; oldVendor: Vendor; } export interface IVendorEventDeletedPayload { - // tenantId: number; vendorId: number; - // authorizedUser: ISystemUser; oldVendor: Vendor; trx?: Knex.Transaction; } export interface IVendorEventEditingPayload { - // tenantId: number; - vendorDTO: IVendorEditDTO; + vendorDTO: EditVendorDto; trx?: Knex.Transaction; } export interface IVendorEventEditedPayload { - // tenantId: number; vendorId: number; vendor: Vendor; - // authorizedUser: ISystemUser; trx?: Knex.Transaction; } @@ -108,14 +100,12 @@ export interface IVendorOpeningBalanceEditDTO { } export interface IVendorOpeningBalanceEditingPayload { - // tenantId: number; oldVendor: Vendor; openingBalanceEditDTO: IVendorOpeningBalanceEditDTO; trx?: Knex.Transaction; } export interface IVendorOpeningBalanceEditedPayload { - // tenantId: number; vendor: Vendor; oldVendor: Vendor; openingBalanceEditDTO: IVendorOpeningBalanceEditDTO; @@ -123,13 +113,11 @@ export interface IVendorOpeningBalanceEditedPayload { } export interface IVendorActivatingPayload { - // tenantId: number; oldVendor: Vendor; trx: Knex.Transaction; } export interface IVendorActivatedPayload { - // tenantId: number; vendor: Vendor; oldVendor: Vendor; trx?: Knex.Transaction; diff --git a/packages/server-nest/src/modules/WarehousesTransfers/dtos/WarehouseTransfer.dto.ts b/packages/server-nest/src/modules/WarehousesTransfers/dtos/WarehouseTransfer.dto.ts index a4f34bcbd..03374944f 100644 --- a/packages/server-nest/src/modules/WarehousesTransfers/dtos/WarehouseTransfer.dto.ts +++ b/packages/server-nest/src/modules/WarehousesTransfers/dtos/WarehouseTransfer.dto.ts @@ -1,3 +1,4 @@ +import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsArray, @@ -39,32 +40,68 @@ export class WarehouseTransferEntryDto { export class CommandWarehouseTransferDto { @IsNotEmpty() @IsInt() + @ApiProperty({ + description: 'The id of the warehouse to transfer from', + example: 1, + }) fromWarehouseId: number; @IsNotEmpty() @IsInt() + @ApiProperty({ + description: 'The id of the warehouse to transfer to', + example: 2, + }) toWarehouseId: number; @IsNotEmpty() @IsDate() + @ApiProperty({ + description: 'The date of the warehouse transfer', + example: '2021-01-01', + }) date: Date; @IsOptional() @IsString() + @ApiProperty({ + description: 'The transaction number of the warehouse transfer', + example: '123456', + }) transactionNumber?: string; @IsBoolean() @IsOptional() + @ApiProperty({ + description: 'Whether the warehouse transfer has been initiated', + example: false, + }) transferInitiated: boolean = false; @IsBoolean() @IsOptional() + @ApiProperty({ + description: 'Whether the warehouse transfer has been delivered', + example: false, + }) transferDelivered: boolean = false; @IsArray() @ArrayMinSize(1) @ValidateNested({ each: true }) @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[]; }