From c4668d7d2203a4a2801e4bd96e50724d9df1625b Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Mon, 16 Jun 2025 13:50:30 +0200 Subject: [PATCH] feat: add swagger docs for responses --- .../modules/Accounts/Accounts.controller.ts | 19 +- .../modules/Accounts/GetAccount.service.ts | 8 +- .../Accounts/dtos/AccountResponse.dto.ts | 171 +++++++++++++ .../modules/BankRules/BankRules.controller.ts | 41 ++- .../BankRules/dtos/BankRuleResponse.dto.ts | 140 +++++++++++ .../src/modules/BankRules/models/BankRule.ts | 8 +- .../server/src/modules/Bills/dtos/Bill.dto.ts | 20 +- .../modules/Branches/Branches.controller.ts | 28 ++- .../Branches/dtos/BranchResponse.dto.ts | 75 ++++++ .../Currencies/Currencies.controller.ts | 31 ++- .../Currencies/dtos/CurrencyResponse.dto.ts | 45 ++++ .../modules/Customers/Customers.controller.ts | 42 +++- .../src/modules/Customers/Customers.module.ts | 2 +- .../Customers/dtos/CreateCustomer.dto.ts | 79 +++++- .../Customers/dtos/CustomerResponse.dto.ts | 118 +++++++++ .../ItemCategories/ItemCategory.controller.ts | 23 +- .../dtos/ItemCategoryResponse.dto.ts | 74 ++++++ .../src/modules/Items/Item.controller.ts | 2 +- .../server/src/modules/Items/dtos/Item.dto.ts | 25 +- .../modules/SaleInvoices/SaleInvoice.types.ts | 9 +- .../SaleInvoices/SaleInvoices.controller.ts | 7 + .../SaleInvoices/dtos/SaleInvoice.dto.ts | 10 +- .../dtos/SaleInvoiceResponse.dto.ts | 237 ++++++++++++++++++ .../SaleInvoices/dtos/SaleInvoiceState.dto.ts | 11 + .../queries/GetSaleInvoice.service.ts | 5 +- .../queries/GetSaleInvoiceState.service.ts | 6 +- .../modules/TaxRates/TaxRate.controller.ts | 60 ++++- .../TaxRates/dtos/TaxRateResponse.dto.ts | 77 ++++++ .../Warehouses/Warehouses.controller.ts | 15 +- .../Warehouses/dtos/WarehouseResponse.dto.ts | 39 +++ 30 files changed, 1363 insertions(+), 64 deletions(-) create mode 100644 packages/server/src/modules/Accounts/dtos/AccountResponse.dto.ts create mode 100644 packages/server/src/modules/BankRules/dtos/BankRuleResponse.dto.ts create mode 100644 packages/server/src/modules/Branches/dtos/BranchResponse.dto.ts create mode 100644 packages/server/src/modules/Currencies/dtos/CurrencyResponse.dto.ts create mode 100644 packages/server/src/modules/Customers/dtos/CustomerResponse.dto.ts create mode 100644 packages/server/src/modules/ItemCategories/dtos/ItemCategoryResponse.dto.ts create mode 100644 packages/server/src/modules/SaleInvoices/dtos/SaleInvoiceResponse.dto.ts create mode 100644 packages/server/src/modules/SaleInvoices/dtos/SaleInvoiceState.dto.ts create mode 100644 packages/server/src/modules/TaxRates/dtos/TaxRateResponse.dto.ts create mode 100644 packages/server/src/modules/Warehouses/dtos/WarehouseResponse.dto.ts diff --git a/packages/server/src/modules/Accounts/Accounts.controller.ts b/packages/server/src/modules/Accounts/Accounts.controller.ts index a44011303..6546ead11 100644 --- a/packages/server/src/modules/Accounts/Accounts.controller.ts +++ b/packages/server/src/modules/Accounts/Accounts.controller.ts @@ -12,7 +12,14 @@ import { AccountsApplication } from './AccountsApplication.service'; import { CreateAccountDTO } from './CreateAccount.dto'; import { EditAccountDTO } from './EditAccount.dto'; import { IAccountsFilter, IAccountsTransactionsFilter } from './Accounts.types'; -import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { + ApiOperation, + ApiParam, + ApiResponse, + ApiTags, + getSchemaPath, +} from '@nestjs/swagger'; +import { AccountResponseDto } from './dtos/AccountResponse.dto'; @Controller('accounts') @ApiTags('Accounts') @@ -133,6 +140,11 @@ export class AccountsController { type: Number, description: 'The account id', }) + @ApiResponse({ + status: 200, + description: 'The account details have been successfully retrieved.', + schema: { $ref: getSchemaPath(AccountResponseDto) }, + }) async getAccount(@Param('id', ParseIntPipe) id: number) { return this.accountsApplication.getAccount(id); } @@ -142,7 +154,12 @@ export class AccountsController { @ApiResponse({ status: 200, description: 'The accounts have been successfully retrieved.', + schema: { + type: 'array', + items: { $ref: getSchemaPath(AccountResponseDto) }, + }, }) + @ApiResponse({}) async getAccounts(@Query() filter: Partial) { return this.accountsApplication.getAccounts(filter); } diff --git a/packages/server/src/modules/Accounts/GetAccount.service.ts b/packages/server/src/modules/Accounts/GetAccount.service.ts index 3f2c3b56b..7058c87d5 100644 --- a/packages/server/src/modules/Accounts/GetAccount.service.ts +++ b/packages/server/src/modules/Accounts/GetAccount.service.ts @@ -6,6 +6,7 @@ import { TransformerInjectable } from '../Transformer/TransformerInjectable.serv import { EventEmitter2 } from '@nestjs/event-emitter'; import { events } from '@/common/events/events'; import { TenantModelProxy } from '../System/models/TenantBaseModel'; +import { AccountResponseDto } from './dtos/AccountResponse.dto'; @Injectable() export class GetAccount { @@ -19,9 +20,10 @@ export class GetAccount { /** * Retrieve the given account details. - * @param {number} accountId + * @param {number} accountId - The account id. + * @returns {Promise} - The account details. */ - public getAccount = async (accountId: number) => { + public async getAccount(accountId: number): Promise { // Find the given account or throw not found error. const account = await this.accountModel() .query() @@ -43,5 +45,5 @@ export class GetAccount { await this.eventEmitter.emitAsync(events.accounts.onViewed, eventPayload); return transformed; - }; + } } diff --git a/packages/server/src/modules/Accounts/dtos/AccountResponse.dto.ts b/packages/server/src/modules/Accounts/dtos/AccountResponse.dto.ts new file mode 100644 index 000000000..f98d3112c --- /dev/null +++ b/packages/server/src/modules/Accounts/dtos/AccountResponse.dto.ts @@ -0,0 +1,171 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class AccountResponseDto { + @ApiProperty({ + description: 'The unique identifier of the account', + example: 1, + }) + id: number; + + @ApiProperty({ + description: 'The name of the account', + example: 'Cash Account', + }) + name: string; + + @ApiProperty({ + description: 'The slug of the account', + example: 'cash-account', + }) + slug: string; + + @ApiProperty({ + description: 'The code of the account', + example: '1001', + }) + code: string; + + @ApiProperty({ + description: 'The index of the account', + example: 1, + }) + index: number; + + @ApiProperty({ + description: 'The type of the account', + example: 'bank', + }) + accountType: string; + + @ApiProperty({ + description: 'The formatted account type label', + example: 'Bank Account', + }) + accountTypeLabel: string; + + @ApiProperty({ + description: 'The parent account ID', + example: null, + }) + parentAccountId: number | null; + + @ApiProperty({ + description: 'Whether the account is predefined', + example: false, + }) + predefined: boolean; + + @ApiProperty({ + description: 'The currency code of the account', + example: 'USD', + }) + currencyCode: string; + + @ApiProperty({ + description: 'Whether the account is active', + example: true, + }) + active: boolean; + + @ApiProperty({ + description: 'The bank balance of the account', + example: 5000.0, + }) + bankBalance: number; + + @ApiProperty({ + description: 'The formatted bank balance', + example: '$5,000.00', + }) + bankBalanceFormatted: string; + + @ApiProperty({ + description: 'The last feeds update timestamp', + example: '2024-03-20T10:30:00Z', + }) + lastFeedsUpdatedAt: string | Date | null; + + @ApiProperty({ + description: 'The formatted last feeds update timestamp', + example: 'Mar 20, 2024 10:30 AM', + }) + lastFeedsUpdatedAtFormatted: string; + + @ApiProperty({ + description: 'The amount of the account', + example: 5000.0, + }) + amount: number; + + @ApiProperty({ + description: 'The formatted amount', + example: '$5,000.00', + }) + formattedAmount: string; + + @ApiProperty({ + description: 'The Plaid item ID', + example: 'plaid-item-123', + }) + plaidItemId: string; + + @ApiProperty({ + description: 'The Plaid account ID', + example: 'plaid-account-456', + }) + plaidAccountId: string | null; + + @ApiProperty({ + description: 'Whether the feeds are active', + example: true, + }) + isFeedsActive: boolean; + + @ApiProperty({ + description: 'Whether the account is syncing owner', + example: true, + }) + isSyncingOwner: boolean; + + @ApiProperty({ + description: 'Whether the feeds are paused', + example: false, + }) + isFeedsPaused: boolean; + + @ApiProperty({ + description: 'The account normal', + example: 'debit', + }) + accountNormal: string; + + @ApiProperty({ + description: 'The formatted account normal', + example: 'Debit', + }) + accountNormalFormatted: string; + + @ApiProperty({ + description: 'The flatten name with all dependant accounts names', + example: 'Assets: Cash Account', + }) + flattenName: string; + + @ApiProperty({ + description: 'The account level in the hierarchy', + example: 2, + }) + accountLevel?: number; + + @ApiProperty({ + description: 'The creation timestamp', + example: '2024-03-20T10:00:00Z', + }) + createdAt: Date; + + @ApiProperty({ + description: 'The update timestamp', + example: '2024-03-20T10:30:00Z', + }) + updatedAt: Date; +} diff --git a/packages/server/src/modules/BankRules/BankRules.controller.ts b/packages/server/src/modules/BankRules/BankRules.controller.ts index 3e4325216..d66c5cd01 100644 --- a/packages/server/src/modules/BankRules/BankRules.controller.ts +++ b/packages/server/src/modules/BankRules/BankRules.controller.ts @@ -1,4 +1,10 @@ -import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { + ApiOperation, + ApiTags, + ApiResponse, + getSchemaPath, + ApiExtraModels, +} from '@nestjs/swagger'; import { Body, Controller, @@ -9,20 +15,28 @@ import { Put, } from '@nestjs/common'; import { BankRulesApplication } from './BankRulesApplication'; -import { BankRule } from './models/BankRule'; import { CreateBankRuleDto } from './dtos/BankRule.dto'; import { EditBankRuleDto } from './dtos/BankRule.dto'; +import { BankRuleResponseDto } from './dtos/BankRuleResponse.dto'; @Controller('banking/rules') @ApiTags('Bank Rules') +@ApiExtraModels(BankRuleResponseDto) export class BankRulesController { constructor(private readonly bankRulesApplication: BankRulesApplication) {} @Post() @ApiOperation({ summary: 'Create a new bank rule.' }) + @ApiResponse({ + status: 201, + description: 'The bank rule has been successfully created.', + schema: { + $ref: getSchemaPath(BankRuleResponseDto), + }, + }) async createBankRule( @Body() createRuleDTO: CreateBankRuleDto, - ): Promise { + ): Promise { return this.bankRulesApplication.createBankRule(createRuleDTO); } @@ -37,19 +51,36 @@ export class BankRulesController { @Delete(':id') @ApiOperation({ summary: 'Delete the given bank rule.' }) + @ApiResponse({ + status: 200, + description: 'The bank rule has been successfully deleted.', + }) async deleteBankRule(@Param('id') ruleId: number): Promise { return this.bankRulesApplication.deleteBankRule(ruleId); } @Get(':id') @ApiOperation({ summary: 'Retrieves the bank rule details.' }) - async getBankRule(@Param('id') ruleId: number): Promise { + @ApiResponse({ + status: 200, + description: 'The bank rule details have been successfully retrieved.', + schema: { $ref: getSchemaPath(BankRuleResponseDto) }, + }) + async getBankRule(@Param('id') ruleId: number): Promise { return this.bankRulesApplication.getBankRule(ruleId); } @Get() @ApiOperation({ summary: 'Retrieves the bank rules.' }) - async getBankRules(): Promise { + @ApiResponse({ + status: 200, + description: 'The bank rules have been successfully retrieved.', + schema: { + type: 'array', + items: { $ref: getSchemaPath(BankRuleResponseDto) }, + }, + }) + async getBankRules(): Promise { return this.bankRulesApplication.getBankRules(); } } diff --git a/packages/server/src/modules/BankRules/dtos/BankRuleResponse.dto.ts b/packages/server/src/modules/BankRules/dtos/BankRuleResponse.dto.ts new file mode 100644 index 000000000..fcc648007 --- /dev/null +++ b/packages/server/src/modules/BankRules/dtos/BankRuleResponse.dto.ts @@ -0,0 +1,140 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { BankRuleAssignCategory, BankRuleConditionType } from '../types'; + +class BankRuleConditionResponseDto { + @ApiProperty({ + description: 'The unique identifier of the bank rule condition', + example: 1, + }) + id: number; + + @ApiProperty({ + description: 'The field to check in the condition', + example: 'description', + enum: ['description', 'amount'], + }) + field: string; + + @ApiProperty({ + description: 'The comparison operator to use', + example: 'contains', + enum: [ + 'equals', + 'equal', + 'contains', + 'not_contain', + 'bigger', + 'bigger_or_equal', + 'smaller', + 'smaller_or_equal', + ], + }) + comparator: string; + + @ApiProperty({ + description: 'The value to compare against', + example: 'Salary', + }) + value: string; +} + +export class BankRuleResponseDto { + @ApiProperty({ + description: 'The unique identifier of the bank rule', + example: 1, + }) + id: number; + + @ApiProperty({ + description: 'The name of the bank rule', + example: 'Monthly Salary', + }) + name: string; + + @ApiProperty({ + description: 'The order in which the rule should be applied', + example: 1, + }) + order: number; + + @ApiProperty({ + description: 'The account ID to apply the rule if', + example: 1, + }) + applyIfAccountId: number; + + @ApiProperty({ + description: 'The transaction type to apply the rule if', + example: 'deposit', + enum: ['deposit', 'withdrawal'], + }) + applyIfTransactionType: string; + + @ApiProperty({ + description: 'The conditions type to apply the rule if', + example: 'and', + enum: ['and', 'or'], + }) + conditionsType: BankRuleConditionType; + + @ApiProperty({ + description: 'The conditions to apply the rule if', + type: [BankRuleConditionResponseDto], + example: [ + { + id: 1, + field: 'description', + comparator: 'contains', + value: 'Salary', + }, + ], + }) + @Type(() => BankRuleConditionResponseDto) + conditions: BankRuleConditionResponseDto[]; + + @ApiProperty({ + description: 'The category to assign the rule if', + example: 'InterestIncome', + enum: [ + 'InterestIncome', + 'OtherIncome', + 'Deposit', + 'Expense', + 'OwnerDrawings', + ], + }) + assignCategory: BankRuleAssignCategory; + + @ApiProperty({ + description: 'The account ID to assign the rule if', + example: 1, + }) + assignAccountId: number; + + @ApiProperty({ + description: 'The payee to assign the rule if', + example: 'Employer Inc.', + required: false, + }) + assignPayee?: string; + + @ApiProperty({ + description: 'The memo to assign the rule if', + example: 'Monthly Salary', + required: false, + }) + assignMemo?: string; + + @ApiProperty({ + description: 'The creation timestamp of the bank rule', + example: '2024-03-20T10:00:00Z', + }) + createdAt: string; + + @ApiProperty({ + description: 'The last update timestamp of the bank rule', + example: '2024-03-20T10:00:00Z', + }) + updatedAt: string; +} diff --git a/packages/server/src/modules/BankRules/models/BankRule.ts b/packages/server/src/modules/BankRules/models/BankRule.ts index 948dbeaac..f833d80aa 100644 --- a/packages/server/src/modules/BankRules/models/BankRule.ts +++ b/packages/server/src/modules/BankRules/models/BankRule.ts @@ -2,8 +2,9 @@ import { BaseModel } from '@/models/Model'; import { Model } from 'objection'; import { BankRuleCondition } from './BankRuleCondition'; import { BankRuleAssignCategory, BankRuleConditionType } from '../types'; +import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel'; -export class BankRule extends BaseModel { +export class BankRule extends TenantBaseModel { public readonly id!: number; public readonly name!: string; public readonly order!: number; @@ -17,6 +18,9 @@ export class BankRule extends BaseModel { public readonly conditions!: BankRuleCondition[]; + public readonly createdAt: string; + public readonly updatedAt: string; + /** * Table name */ @@ -28,7 +32,7 @@ export class BankRule extends BaseModel { * Timestamps columns. */ static get timestamps() { - return ['created_at', 'updated_at']; + return ['createdAt', 'updatedAt']; } /** diff --git a/packages/server/src/modules/Bills/dtos/Bill.dto.ts b/packages/server/src/modules/Bills/dtos/Bill.dto.ts index b62d7b2f3..f0dc2bc1e 100644 --- a/packages/server/src/modules/Bills/dtos/Bill.dto.ts +++ b/packages/server/src/modules/Bills/dtos/Bill.dto.ts @@ -47,7 +47,7 @@ class AttachmentDto { export class CommandBillDto { @ApiProperty({ description: 'Unique bill number', - example: 'BILL-0001', + example: 'BILL-2024-001', required: false, }) @IsOptional() @@ -56,7 +56,7 @@ export class CommandBillDto { @ApiProperty({ description: 'Reference number', - example: 'REF-12345', + example: 'PO-2024-001', required: false, }) @IsOptional() @@ -65,7 +65,7 @@ export class CommandBillDto { @ApiProperty({ description: 'Date the bill was issued', - example: '2025-06-01', + example: '2024-03-15', }) @IsNotEmpty() @IsDateString() @@ -73,7 +73,7 @@ export class CommandBillDto { @ApiProperty({ description: 'Date the bill is due', - example: '2025-07-01', + example: '2024-04-15', required: false, }) @IsOptional() @@ -82,7 +82,7 @@ export class CommandBillDto { @ApiProperty({ description: 'Vendor identifier', - example: 10, + example: 1001, }) @IsInt() @IsNotEmpty() @@ -90,7 +90,7 @@ export class CommandBillDto { @ApiProperty({ description: 'Exchange rate applied to bill amounts', - example: 3.67, + example: 1.25, required: false, }) @IsOptional() @@ -101,7 +101,7 @@ export class CommandBillDto { @ApiProperty({ description: 'Warehouse identifier', - example: 4, + example: 101, required: false, }) @IsOptional() @@ -111,7 +111,7 @@ export class CommandBillDto { @ApiProperty({ description: 'Branch identifier', - example: 2, + example: 201, required: false, }) @IsOptional() @@ -121,7 +121,7 @@ export class CommandBillDto { @ApiProperty({ description: 'Project identifier', - example: 5, + example: 301, required: false, }) @IsOptional() @@ -131,7 +131,7 @@ export class CommandBillDto { @ApiProperty({ description: 'Additional notes about the bill', - example: 'Payment due next month', + example: 'Office supplies and equipment for Q2 2024', required: false, }) @IsOptional() diff --git a/packages/server/src/modules/Branches/Branches.controller.ts b/packages/server/src/modules/Branches/Branches.controller.ts index 8213906a5..7221a020a 100644 --- a/packages/server/src/modules/Branches/Branches.controller.ts +++ b/packages/server/src/modules/Branches/Branches.controller.ts @@ -9,10 +9,18 @@ import { } from '@nestjs/common'; import { BranchesApplication } from './BranchesApplication.service'; import { CreateBranchDto, EditBranchDto } from './dtos/Branch.dto'; -import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { + ApiExtraModels, + ApiOperation, + ApiResponse, + ApiTags, + getSchemaPath, +} from '@nestjs/swagger'; +import { BranchResponseDto } from './dtos/BranchResponse.dto'; @Controller('branches') @ApiTags('Branches') +@ApiExtraModels(BranchResponseDto) export class BranchesController { constructor(private readonly branchesApplication: BranchesApplication) {} @@ -21,6 +29,12 @@ export class BranchesController { @ApiResponse({ status: 200, description: 'The branches have been successfully retrieved.', + schema: { + type: 'array', + items: { + $ref: getSchemaPath(BranchResponseDto), + }, + }, }) getBranches() { return this.branchesApplication.getBranches(); @@ -31,6 +45,9 @@ export class BranchesController { @ApiResponse({ status: 200, description: 'The branch details have been successfully retrieved.', + schema: { + $ref: getSchemaPath(BranchResponseDto), + }, }) @ApiResponse({ status: 404, description: 'The branch not found.' }) getBranch(@Param('id') id: string) { @@ -42,6 +59,9 @@ export class BranchesController { @ApiResponse({ status: 200, description: 'The branch has been successfully created.', + schema: { + $ref: getSchemaPath(BranchResponseDto), + }, }) @ApiResponse({ status: 404, description: 'The branch not found.' }) createBranch(@Body() createBranchDTO: CreateBranchDto) { @@ -53,6 +73,9 @@ export class BranchesController { @ApiResponse({ status: 200, description: 'The branch has been successfully edited.', + schema: { + $ref: getSchemaPath(BranchResponseDto), + }, }) @ApiResponse({ status: 404, description: 'The branch not found.' }) editBranch(@Param('id') id: string, @Body() editBranchDTO: EditBranchDto) { @@ -90,6 +113,9 @@ export class BranchesController { @ApiResponse({ status: 200, description: 'The branch has been successfully marked as primary.', + schema: { + $ref: getSchemaPath(BranchResponseDto), + }, }) @ApiResponse({ status: 404, description: 'The branch not found.' }) markBranchAsPrimary(@Param('id') id: string) { diff --git a/packages/server/src/modules/Branches/dtos/BranchResponse.dto.ts b/packages/server/src/modules/Branches/dtos/BranchResponse.dto.ts new file mode 100644 index 000000000..547859e80 --- /dev/null +++ b/packages/server/src/modules/Branches/dtos/BranchResponse.dto.ts @@ -0,0 +1,75 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class BranchResponseDto { + @ApiProperty({ + description: 'Branch ID', + example: 1, + }) + id: number; + + @ApiProperty({ + description: 'Branch name', + example: 'Main Branch', + }) + name: string; + + @ApiProperty({ + description: 'Branch code', + example: 'BR001', + }) + code: string; + + @ApiProperty({ + description: 'Branch address', + example: '123 Main Street', + }) + address: string; + + @ApiProperty({ + description: 'Branch city', + example: 'New York', + }) + city: string; + + @ApiProperty({ + description: 'Branch country', + example: 'USA', + }) + country: string; + + @ApiProperty({ + description: 'Branch phone number', + example: '+1-555-123-4567', + }) + phoneNumber: string; + + @ApiProperty({ + description: 'Branch email', + example: 'branch@example.com', + }) + email: string; + + @ApiProperty({ + description: 'Branch website', + example: 'https://www.example.com/branch', + }) + website: string; + + @ApiProperty({ + description: 'Whether this is the primary branch', + example: true, + }) + primary: boolean; + + @ApiProperty({ + description: 'Creation timestamp', + example: '2024-03-20T10:00:00Z', + }) + createdAt: Date; + + @ApiProperty({ + description: 'Last update timestamp', + example: '2024-03-20T10:00:00Z', + }) + updatedAt: Date; +} diff --git a/packages/server/src/modules/Currencies/Currencies.controller.ts b/packages/server/src/modules/Currencies/Currencies.controller.ts index 901db4436..9d5885695 100644 --- a/packages/server/src/modules/Currencies/Currencies.controller.ts +++ b/packages/server/src/modules/Currencies/Currencies.controller.ts @@ -16,13 +16,17 @@ import { ApiParam, ApiOkResponse, ApiNotFoundResponse, + ApiExtraModels, + getSchemaPath, } from '@nestjs/swagger'; import { CurrenciesApplication } from './CurrenciesApplication.service'; import { CreateCurrencyDto } from './dtos/CreateCurrency.dto'; import { EditCurrencyDto } from './dtos/EditCurrency.dto'; +import { CurrencyResponseDto } from './dtos/CurrencyResponse.dto'; @ApiTags('Currencies') @Controller('/currencies') +@ApiExtraModels(CurrencyResponseDto) export class CurrenciesController { constructor(private readonly currenciesApp: CurrenciesApplication) {} @@ -31,6 +35,9 @@ export class CurrenciesController { @ApiBody({ type: CreateCurrencyDto }) @ApiCreatedResponse({ description: 'The currency has been successfully created.', + schema: { + $ref: getSchemaPath(CurrencyResponseDto), + }, }) @ApiBadRequestResponse({ description: 'Invalid input data.' }) create(@Body() dto: CreateCurrencyDto) { @@ -41,7 +48,12 @@ export class CurrenciesController { @ApiOperation({ summary: 'Edit an existing currency' }) @ApiParam({ name: 'id', type: Number, description: 'Currency ID' }) @ApiBody({ type: EditCurrencyDto }) - @ApiOkResponse({ description: 'The currency has been successfully updated.' }) + @ApiOkResponse({ + description: 'The currency has been successfully updated.', + schema: { + $ref: getSchemaPath(CurrencyResponseDto), + }, + }) @ApiNotFoundResponse({ description: 'Currency not found.' }) @ApiBadRequestResponse({ description: 'Invalid input data.' }) edit(@Param('id') id: number, @Body() dto: EditCurrencyDto) { @@ -59,7 +71,15 @@ export class CurrenciesController { @Get() @ApiOperation({ summary: 'Get all currencies' }) - @ApiOkResponse({ description: 'List of all currencies.' }) + @ApiOkResponse({ + description: 'List of all currencies.', + schema: { + type: 'array', + items: { + $ref: getSchemaPath(CurrencyResponseDto), + }, + }, + }) findAll() { return this.currenciesApp.getCurrencies(); } @@ -71,7 +91,12 @@ export class CurrenciesController { type: String, description: 'Currency code', }) - @ApiOkResponse({ description: 'The currency details.' }) + @ApiOkResponse({ + description: 'The currency details.', + schema: { + $ref: getSchemaPath(CurrencyResponseDto), + }, + }) @ApiNotFoundResponse({ description: 'Currency not found.' }) findOne(@Param('currencyCode') currencyCode: string) { return this.currenciesApp.getCurrency(currencyCode); diff --git a/packages/server/src/modules/Currencies/dtos/CurrencyResponse.dto.ts b/packages/server/src/modules/Currencies/dtos/CurrencyResponse.dto.ts new file mode 100644 index 000000000..ab159f2cb --- /dev/null +++ b/packages/server/src/modules/Currencies/dtos/CurrencyResponse.dto.ts @@ -0,0 +1,45 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class CurrencyResponseDto { + @ApiProperty({ + description: 'The unique identifier of the currency', + example: 1, + }) + id: number; + + @ApiProperty({ + description: 'The name of the currency', + example: 'US Dollar', + }) + currencyName: string; + + @ApiProperty({ + description: 'The code of the currency', + example: 'USD', + }) + currencyCode: string; + + @ApiProperty({ + description: 'The sign/symbol of the currency', + example: '$', + }) + currencySign: string; + + @ApiProperty({ + description: 'Whether this is the base currency for the organization', + example: true, + }) + isBaseCurrency: boolean; + + @ApiProperty({ + description: 'The creation timestamp', + example: '2024-03-20T10:00:00Z', + }) + createdAt: Date; + + @ApiProperty({ + description: 'The last update timestamp', + example: '2024-03-20T10:00:00Z', + }) + updatedAt: Date; +} diff --git a/packages/server/src/modules/Customers/Customers.controller.ts b/packages/server/src/modules/Customers/Customers.controller.ts index 7b70ec38a..ac589f587 100644 --- a/packages/server/src/modules/Customers/Customers.controller.ts +++ b/packages/server/src/modules/Customers/Customers.controller.ts @@ -13,35 +13,66 @@ import { ICustomerOpeningBalanceEditDTO, ICustomersFilter, } from './types/Customers.types'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { + ApiOperation, + ApiResponse, + ApiTags, + ApiExtraModels, + getSchemaPath, +} from '@nestjs/swagger'; import { CreateCustomerDto } from './dtos/CreateCustomer.dto'; import { EditCustomerDto } from './dtos/EditCustomer.dto'; +import { CustomerResponseDto } from './dtos/CustomerResponse.dto'; @Controller('customers') @ApiTags('Customers') +@ApiExtraModels(CustomerResponseDto) export class CustomersController { constructor(private customersApplication: CustomersApplication) {} @Get(':id') @ApiOperation({ summary: 'Retrieves the customer details.' }) + @ApiResponse({ + status: 200, + description: 'The customer details have been successfully retrieved.', + schema: { $ref: getSchemaPath(CustomerResponseDto) }, + }) getCustomer(@Param('id') customerId: number) { return this.customersApplication.getCustomer(customerId); } @Get() @ApiOperation({ summary: 'Retrieves the customers paginated list.' }) + @ApiResponse({ + status: 200, + description: 'The customers have been successfully retrieved.', + schema: { + type: 'array', + items: { $ref: getSchemaPath(CustomerResponseDto) }, + }, + }) getCustomers(@Query() filterDTO: Partial) { return this.customersApplication.getCustomers(filterDTO); } @Post() @ApiOperation({ summary: 'Create a new customer.' }) + @ApiResponse({ + status: 201, + description: 'The customer has been successfully created.', + schema: { $ref: getSchemaPath(CustomerResponseDto) }, + }) createCustomer(@Body() customerDTO: CreateCustomerDto) { return this.customersApplication.createCustomer(customerDTO); } @Put(':id') @ApiOperation({ summary: 'Edit the given customer.' }) + @ApiResponse({ + status: 200, + description: 'The customer has been successfully updated.', + schema: { $ref: getSchemaPath(CustomerResponseDto) }, + }) editCustomer( @Param('id') customerId: number, @Body() customerDTO: EditCustomerDto, @@ -51,12 +82,21 @@ export class CustomersController { @Delete(':id') @ApiOperation({ summary: 'Delete the given customer.' }) + @ApiResponse({ + status: 200, + description: 'The customer has been successfully deleted.', + }) deleteCustomer(@Param('id') customerId: number) { return this.customersApplication.deleteCustomer(customerId); } @Put(':id/opening-balance') @ApiOperation({ summary: 'Edit the opening balance of the given customer.' }) + @ApiResponse({ + status: 200, + description: 'The customer opening balance has been successfully updated.', + schema: { $ref: getSchemaPath(CustomerResponseDto) }, + }) editOpeningBalance( @Param('id') customerId: number, @Body() openingBalanceDTO: ICustomerOpeningBalanceEditDTO, diff --git a/packages/server/src/modules/Customers/Customers.module.ts b/packages/server/src/modules/Customers/Customers.module.ts index 434b7893a..5396a0424 100644 --- a/packages/server/src/modules/Customers/Customers.module.ts +++ b/packages/server/src/modules/Customers/Customers.module.ts @@ -36,7 +36,7 @@ import { DynamicListModule } from '../DynamicListing/DynamicList.module'; GetCustomerService, CustomersExportable, CustomersImportable, - GetCustomers + GetCustomers, ], }) export class CustomersModule {} diff --git a/packages/server/src/modules/Customers/dtos/CreateCustomer.dto.ts b/packages/server/src/modules/Customers/dtos/CreateCustomer.dto.ts index a3d5ff254..cbdbf379a 100644 --- a/packages/server/src/modules/Customers/dtos/CreateCustomer.dto.ts +++ b/packages/server/src/modules/Customers/dtos/CreateCustomer.dto.ts @@ -10,22 +10,38 @@ import { ApiProperty } from '@nestjs/swagger'; import { ContactAddressDto } from './ContactAddress.dto'; export class CreateCustomerDto extends ContactAddressDto { - @ApiProperty({ required: true, description: 'Customer type' }) + @ApiProperty({ + required: true, + description: 'Customer type', + example: 'business', + }) @IsString() @IsNotEmpty() customerType: string; - @ApiProperty({ required: true, description: 'Currency code' }) + @ApiProperty({ + required: true, + description: 'Currency code', + example: 'USD', + }) @IsString() @IsNotEmpty() currencyCode: string; - @ApiProperty({ required: false, description: 'Opening balance' }) + @ApiProperty({ + required: false, + description: 'Opening balance', + example: 5000.0, + }) @IsOptional() @IsNumber() openingBalance?: number; - @ApiProperty({ required: false, description: 'Opening balance date' }) + @ApiProperty({ + required: false, + description: 'Opening balance date', + example: '2024-01-01', + }) @IsOptional() @IsString() openingBalanceAt?: string; @@ -33,52 +49,89 @@ export class CreateCustomerDto extends ContactAddressDto { @ApiProperty({ required: false, description: 'Opening balance exchange rate', + example: 1.0, }) @IsOptional() @IsNumber() openingBalanceExchangeRate?: number; - @ApiProperty({ required: false, description: 'Opening balance branch ID' }) + @ApiProperty({ + required: false, + description: 'Opening balance branch ID', + example: 101, + }) @IsOptional() @IsNumber() openingBalanceBranchId?: number; - @ApiProperty({ required: false, description: 'Salutation' }) + @ApiProperty({ + required: false, + description: 'Salutation', + example: 'Mr.', + }) @IsOptional() @IsString() salutation?: string; - @ApiProperty({ required: false, description: 'First name' }) + @ApiProperty({ + required: false, + description: 'First name', + example: 'John', + }) @IsOptional() @IsString() firstName?: string; - @ApiProperty({ required: false, description: 'Last name' }) + @ApiProperty({ + required: false, + description: 'Last name', + example: 'Smith', + }) @IsOptional() @IsString() lastName?: string; - @ApiProperty({ required: false, description: 'Company name' }) + @ApiProperty({ + required: false, + description: 'Company name', + example: 'Acme Corporation', + }) @IsOptional() @IsString() companyName?: string; - @ApiProperty({ required: true, description: 'Display name' }) + @ApiProperty({ + required: true, + description: 'Display name', + example: 'Acme Corporation', + }) @IsString() @IsNotEmpty() displayName: string; - @ApiProperty({ required: false, description: 'Website' }) + @ApiProperty({ + required: false, + description: 'Website', + example: 'https://www.acmecorp.com', + }) @IsOptional() @IsString() website?: string; - @ApiProperty({ required: false, description: 'Email' }) + @ApiProperty({ + required: false, + description: 'Email', + example: 'contact@acmecorp.com', + }) @IsOptional() @IsEmail() email?: string; - @ApiProperty({ required: false, description: 'Work phone' }) + @ApiProperty({ + required: false, + description: 'Work phone', + example: '+1 (555) 123-4567', + }) @IsOptional() @IsString() workPhone?: string; diff --git a/packages/server/src/modules/Customers/dtos/CustomerResponse.dto.ts b/packages/server/src/modules/Customers/dtos/CustomerResponse.dto.ts new file mode 100644 index 000000000..556b6cafb --- /dev/null +++ b/packages/server/src/modules/Customers/dtos/CustomerResponse.dto.ts @@ -0,0 +1,118 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; + +export class CustomerResponseDto { + @ApiProperty({ example: 1500.0 }) + balance: number; + + @ApiProperty({ example: 'USD' }) + currencyCode: string; + + @ApiProperty({ example: 1000.0 }) + openingBalance: number; + + @ApiProperty({ example: '2024-01-01T00:00:00Z' }) + @Type(() => Date) + openingBalanceAt: Date; + + @ApiProperty({ example: 1.0 }) + openingBalanceExchangeRate: number; + + @ApiProperty({ required: false, example: 1 }) + openingBalanceBranchId?: number; + + @ApiProperty({ required: false, example: 'Mr.' }) + salutation?: string; + + @ApiProperty({ required: false, example: 'John' }) + firstName?: string; + + @ApiProperty({ required: false, example: 'Doe' }) + lastName?: string; + + @ApiProperty({ required: false, example: 'Acme Corporation' }) + companyName?: string; + + @ApiProperty({ example: 'John Doe - Acme Corporation' }) + displayName: string; + + @ApiProperty({ required: false, example: 'john.doe@acme.com' }) + email?: string; + + @ApiProperty({ required: false, example: '+1 (555) 123-4567' }) + workPhone?: string; + + @ApiProperty({ required: false, example: '+1 (555) 987-6543' }) + personalPhone?: string; + + @ApiProperty({ required: false, example: 'https://www.acme.com' }) + website?: string; + + @ApiProperty({ required: false, example: '123 Business Ave' }) + billingAddress1?: string; + + @ApiProperty({ required: false, example: 'Suite 100' }) + billingAddress2?: string; + + @ApiProperty({ required: false, example: 'New York' }) + billingAddressCity?: string; + + @ApiProperty({ required: false, example: 'United States' }) + billingAddressCountry?: string; + + @ApiProperty({ required: false, example: 'billing@acme.com' }) + billingAddressEmail?: string; + + @ApiProperty({ required: false, example: '10001' }) + billingAddressPostcode?: string; + + @ApiProperty({ required: false, example: '+1 (555) 111-2222' }) + billingAddressPhone?: string; + + @ApiProperty({ required: false, example: 'NY' }) + billingAddressState?: string; + + @ApiProperty({ required: false, example: '456 Shipping St' }) + shippingAddress1?: string; + + @ApiProperty({ required: false, example: 'Unit 200' }) + shippingAddress2?: string; + + @ApiProperty({ required: false, example: 'Los Angeles' }) + shippingAddressCity?: string; + + @ApiProperty({ required: false, example: 'United States' }) + shippingAddressCountry?: string; + + @ApiProperty({ required: false, example: 'shipping@acme.com' }) + shippingAddressEmail?: string; + + @ApiProperty({ required: false, example: '90001' }) + shippingAddressPostcode?: string; + + @ApiProperty({ required: false, example: '+1 (555) 333-4444' }) + shippingAddressPhone?: string; + + @ApiProperty({ required: false, example: 'CA' }) + shippingAddressState?: string; + + @ApiProperty({ example: 'Important client with regular monthly orders' }) + note: string; + + @ApiProperty({ example: true }) + active: boolean; + + @ApiProperty({ example: '2024-01-01T00:00:00Z' }) + @Type(() => Date) + createdAt: Date; + + @ApiProperty({ example: '2024-01-01T00:00:00Z' }) + @Type(() => Date) + updatedAt: Date; + + @ApiProperty({ example: 1000.0 }) + localOpeningBalance: number; + + @ApiProperty({ example: 1500.0 }) + closingBalance: number; +} diff --git a/packages/server/src/modules/ItemCategories/ItemCategory.controller.ts b/packages/server/src/modules/ItemCategories/ItemCategory.controller.ts index cf52c7bed..a95d34661 100644 --- a/packages/server/src/modules/ItemCategories/ItemCategory.controller.ts +++ b/packages/server/src/modules/ItemCategories/ItemCategory.controller.ts @@ -13,14 +13,22 @@ import { GetItemCategoriesResponse, IItemCategoriesFilter, } from './ItemCategory.interfaces'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { + ApiExtraModels, + ApiOperation, + ApiResponse, + ApiTags, + getSchemaPath, +} from '@nestjs/swagger'; import { CreateItemCategoryDto, EditItemCategoryDto, } from './dtos/ItemCategory.dto'; +import { ItemCategoryResponseDto } from './dtos/ItemCategoryResponse.dto'; @Controller('item-categories') @ApiTags('Item Categories') +@ApiExtraModels(ItemCategoryResponseDto) export class ItemCategoryController { constructor( private readonly itemCategoryApplication: ItemCategoryApplication, @@ -34,6 +42,14 @@ export class ItemCategoryController { @Get() @ApiOperation({ summary: 'Retrieves the item categories.' }) + @ApiResponse({ + status: 200, + description: 'The item categories have been successfully retrieved.', + schema: { + type: 'array', + items: { $ref: getSchemaPath(ItemCategoryResponseDto) }, + }, + }) async getItemCategories( @Query() filterDTO: Partial, ): Promise { @@ -51,6 +67,11 @@ export class ItemCategoryController { @Get(':id') @ApiOperation({ summary: 'Retrieves the item category details.' }) + @ApiResponse({ + status: 200, + description: 'The item category details have been successfully retrieved.', + schema: { $ref: getSchemaPath(ItemCategoryResponseDto) }, + }) async getItemCategory(@Param('id') id: number) { return this.itemCategoryApplication.getItemCategory(id); } diff --git a/packages/server/src/modules/ItemCategories/dtos/ItemCategoryResponse.dto.ts b/packages/server/src/modules/ItemCategories/dtos/ItemCategoryResponse.dto.ts new file mode 100644 index 000000000..f9d8856ec --- /dev/null +++ b/packages/server/src/modules/ItemCategories/dtos/ItemCategoryResponse.dto.ts @@ -0,0 +1,74 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class ItemCategoryResponseDto { + @ApiProperty({ + example: 1, + description: 'The unique identifier of the item category', + }) + id: number; + + @ApiProperty({ + example: 'Electronics', + description: 'The name of the item category', + }) + name: string; + + @ApiProperty({ + example: 'Electronic devices and accessories', + description: 'The description of the item category', + required: false, + }) + description?: string; + + @ApiProperty({ + example: 1, + description: 'The cost account ID', + required: false, + }) + costAccountId?: number; + + @ApiProperty({ + example: 1, + description: 'The sell account ID', + required: false, + }) + sellAccountId?: number; + + @ApiProperty({ + example: 1, + description: 'The inventory account ID', + required: false, + }) + inventoryAccountId?: number; + + @ApiProperty({ + example: 'FIFO', + description: 'The cost method', + required: false, + }) + costMethod?: string; + + @ApiProperty({ + example: 1, + description: 'The user ID who created the category', + }) + userId: number; + + @ApiProperty({ + example: '2024-03-20T10:00:00Z', + description: 'The creation date of the category', + }) + createdAt: Date; + + @ApiProperty({ + example: '2024-03-20T10:00:00Z', + description: 'The last update date of the category', + }) + updatedAt: Date; + + @ApiProperty({ + example: 5, + description: 'The number of items in this category', + }) + count?: number; +} diff --git a/packages/server/src/modules/Items/Item.controller.ts b/packages/server/src/modules/Items/Item.controller.ts index 02783391a..c1a2acf19 100644 --- a/packages/server/src/modules/Items/Item.controller.ts +++ b/packages/server/src/modules/Items/Item.controller.ts @@ -25,8 +25,8 @@ import { CreateItemDto, EditItemDto } from './dtos/Item.dto'; import { GetItemsQueryDto } from './dtos/GetItemsQuery.dto'; @Controller('/items') -@UseGuards(SubscriptionGuard) @ApiTags('Items') +@UseGuards(SubscriptionGuard) export class ItemsController extends TenantController { constructor(private readonly itemsApplication: ItemsApplicationService) { super(); diff --git a/packages/server/src/modules/Items/dtos/Item.dto.ts b/packages/server/src/modules/Items/dtos/Item.dto.ts index 332daabfd..d85f49c7b 100644 --- a/packages/server/src/modules/Items/dtos/Item.dto.ts +++ b/packages/server/src/modules/Items/dtos/Item.dto.ts @@ -17,7 +17,10 @@ export class CommandItemDto { @IsString() @IsNotEmpty() @MaxLength(255) - @ApiProperty({ description: 'Item name', example: 'Office Chair' }) + @ApiProperty({ + description: 'Item name', + example: 'Ergonomic Office Chair Model X-2000', + }) name: string; @IsString() @@ -36,7 +39,7 @@ export class CommandItemDto { @ApiProperty({ description: 'Item code/SKU', required: false, - example: 'ITEM-001', + example: 'CHAIR-X2000', }) code?: string; @@ -59,7 +62,7 @@ export class CommandItemDto { description: 'Cost price of the item', required: false, minimum: 0, - example: 100.5, + example: 299.99, }) costPrice?: number; @@ -72,7 +75,7 @@ export class CommandItemDto { description: 'ID of the cost account', required: false, minimum: 0, - example: 1, + example: 1001, }) costAccountId?: number; @@ -95,7 +98,7 @@ export class CommandItemDto { description: 'Selling price of the item', required: false, minimum: 0, - example: 150.75, + example: 399.99, }) sellPrice?: number; @@ -108,7 +111,7 @@ export class CommandItemDto { description: 'ID of the sell account', required: false, minimum: 0, - example: 2, + example: 2001, }) sellAccountId?: number; @@ -121,7 +124,7 @@ export class CommandItemDto { description: 'ID of the inventory account (required for inventory items)', required: false, minimum: 0, - example: 3, + example: 3001, }) inventoryAccountId?: number; @@ -130,7 +133,8 @@ export class CommandItemDto { @ApiProperty({ description: 'Description shown on sales documents', required: false, - example: 'High-quality ergonomic office chair', + example: + 'Premium ergonomic office chair with adjustable height, lumbar support, and breathable mesh back', }) sellDescription?: string; @@ -139,7 +143,7 @@ export class CommandItemDto { @ApiProperty({ description: 'Description shown on purchase documents', required: false, - example: 'Ergonomic office chair with adjustable height', + example: 'Ergonomic office chair - Model X-2000 with standard features', }) purchaseDescription?: string; @@ -180,7 +184,8 @@ export class CommandItemDto { @ApiProperty({ description: 'Additional notes about the item', required: false, - example: 'Available in multiple colors', + example: + 'Available in black, gray, and navy colors. 5-year warranty included.', }) note?: string; diff --git a/packages/server/src/modules/SaleInvoices/SaleInvoice.types.ts b/packages/server/src/modules/SaleInvoices/SaleInvoice.types.ts index 8f19536be..13f09e236 100644 --- a/packages/server/src/modules/SaleInvoices/SaleInvoice.types.ts +++ b/packages/server/src/modules/SaleInvoices/SaleInvoice.types.ts @@ -8,7 +8,10 @@ import { CommonMailOptionsDTO, } from '../MailNotification/MailNotification.types'; import { TenantJobPayload } from '@/interfaces/Tenant'; -import { CreateSaleInvoiceDto, EditSaleInvoiceDto } from './dtos/SaleInvoice.dto'; +import { + CreateSaleInvoiceDto, + EditSaleInvoiceDto, +} from './dtos/SaleInvoice.dto'; export interface PaymentIntegrationTransactionLink { id: number; @@ -301,10 +304,6 @@ export interface InvoicePdfTemplateAttributes { statement: string; } -export interface ISaleInvocieState { - defaultTemplateId: number; -} - export interface SaleInvoiceSendMailData { saleInvoiceId: number; messageOptions: SendInvoiceMailDTO; diff --git a/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts b/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts index 04cc77465..04514ec47 100644 --- a/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts +++ b/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts @@ -27,15 +27,19 @@ import { ApiQuery, ApiResponse, ApiTags, + ApiExtraModels, + getSchemaPath, } from '@nestjs/swagger'; import { CreateSaleInvoiceDto, EditSaleInvoiceDto, } from './dtos/SaleInvoice.dto'; import { AcceptType } from '@/constants/accept-type'; +import { SaleInvoiceResponseDto } from './dtos/SaleInvoiceResponse.dto'; @Controller('sale-invoices') @ApiTags('Sale Invoices') +@ApiExtraModels(SaleInvoiceResponseDto) @ApiHeader({ name: 'organization-id', description: 'The organization id', @@ -150,6 +154,9 @@ export class SaleInvoicesController { @ApiResponse({ status: 200, description: 'The sale invoice details have been successfully retrieved.', + schema: { + $ref: getSchemaPath(SaleInvoiceResponseDto), + }, }) @ApiResponse({ status: 404, description: 'The sale invoice not found.' }) @ApiParam({ diff --git a/packages/server/src/modules/SaleInvoices/dtos/SaleInvoice.dto.ts b/packages/server/src/modules/SaleInvoices/dtos/SaleInvoice.dto.ts index 078031f9c..4627475a6 100644 --- a/packages/server/src/modules/SaleInvoices/dtos/SaleInvoice.dto.ts +++ b/packages/server/src/modules/SaleInvoices/dtos/SaleInvoice.dto.ts @@ -21,10 +21,18 @@ enum DiscountType { Amount = 'amount', } -class PaymentMethodDto { +export class PaymentMethodDto { + @ApiProperty({ + description: 'The ID of the payment integration', + example: 1, + }) @IsInt() paymentIntegrationId: number; + @ApiProperty({ + description: 'Whether the payment method is enabled', + example: true, + }) @IsBoolean() enable: boolean; } diff --git a/packages/server/src/modules/SaleInvoices/dtos/SaleInvoiceResponse.dto.ts b/packages/server/src/modules/SaleInvoices/dtos/SaleInvoiceResponse.dto.ts new file mode 100644 index 000000000..29ec568c6 --- /dev/null +++ b/packages/server/src/modules/SaleInvoices/dtos/SaleInvoiceResponse.dto.ts @@ -0,0 +1,237 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto'; +import { AttachmentLinkDto } from '@/modules/Attachments/dtos/Attachment.dto'; +import { PaymentMethodDto } from '../dtos/SaleInvoice.dto'; +import { DiscountType } from '@/common/types/Discount'; + +export class SaleInvoiceResponseDto { + @ApiProperty({ + description: 'The unique identifier of the sale invoice', + example: 1, + }) + id: number; + + @ApiProperty({ + description: 'The date of the invoice', + example: '2023-01-01T00:00:00Z', + }) + invoiceDate: Date; + + @ApiProperty({ + description: 'The due date of the invoice', + example: '2023-01-15T00:00:00Z', + }) + dueDate: Date; + + @ApiProperty({ + description: 'The invoice number', + example: 'INV-001', + }) + invoiceNo: string; + + @ApiProperty({ + description: 'The reference number', + example: 'REF-001', + required: false, + }) + referenceNo?: string; + + @ApiProperty({ + description: 'The ID of the customer', + example: 1, + }) + customerId: number; + + @ApiProperty({ + description: 'The exchange rate for currency conversion', + example: 1.0, + required: false, + }) + exchangeRate?: number; + + @ApiProperty({ + description: 'The currency code', + example: 'USD', + required: false, + }) + currencyCode?: string; + + @ApiProperty({ + description: 'Custom message on the invoice', + example: 'Thank you for your business', + required: false, + }) + invoiceMessage?: string; + + @ApiProperty({ + description: 'Terms and conditions of the invoice', + example: 'Payment due within 14 days', + required: false, + }) + termsConditions?: string; + + @ApiProperty({ + description: 'Whether tax is inclusive in the item rates', + example: false, + required: false, + }) + isInclusiveTax?: boolean; + + @ApiProperty({ + description: 'The line items of the invoice', + type: [ItemEntryDto], + }) + entries: ItemEntryDto[]; + + @ApiProperty({ + description: 'Whether the invoice has been delivered', + example: false, + }) + delivered: boolean; + + @ApiProperty({ + description: 'The date when the invoice was delivered', + example: '2023-01-02T00:00:00Z', + required: false, + }) + deliveredAt?: Date; + + @ApiProperty({ + description: 'The ID of the warehouse', + example: 1, + required: false, + }) + warehouseId?: number; + + @ApiProperty({ + description: 'The ID of the branch', + example: 1, + required: false, + }) + branchId?: number; + + @ApiProperty({ + description: 'The ID of the project', + example: 1, + required: false, + }) + projectId?: number; + + @ApiProperty({ + description: 'The attachments of the invoice', + type: [AttachmentLinkDto], + required: false, + }) + attachments?: AttachmentLinkDto[]; + + @ApiProperty({ + description: 'The payment methods associated with the invoice', + type: [PaymentMethodDto], + required: false, + }) + paymentMethods?: PaymentMethodDto[]; + + @ApiProperty({ + description: 'The discount value', + example: 10, + required: false, + }) + discount?: number; + + @ApiProperty({ + description: 'The type of discount (percentage or fixed)', + enum: DiscountType, + example: DiscountType.Percentage, + required: false, + }) + discountType?: DiscountType; + + @ApiProperty({ + description: 'The adjustment amount', + example: 5, + required: false, + }) + adjustment?: number; + + @ApiProperty({ + description: 'The ID of the PDF template', + example: 1, + required: false, + }) + pdfTemplateId?: number; + + @ApiProperty({ + description: 'The total amount of tax withheld', + example: 50, + required: false, + }) + taxAmountWithheld?: number; + + @ApiProperty({ + description: 'The balance of the invoice', + example: 1000, + }) + balance: number; + + @ApiProperty({ + description: 'The amount paid', + example: 500, + }) + paymentAmount: number; + + @ApiProperty({ + description: 'The amount credited', + example: 0, + required: false, + }) + creditedAmount?: number; + + @ApiProperty({ + description: 'The subtotal amount before tax and adjustments', + example: 900, + }) + subtotal: number; + + @ApiProperty({ + description: 'The total amount including tax and adjustments', + example: 1000, + }) + total: number; + + @ApiProperty({ + description: 'The due amount remaining to be paid', + example: 500, + }) + dueAmount: number; + + @ApiProperty({ + description: 'Whether the invoice is overdue', + example: false, + }) + isOverdue: boolean; + + @ApiProperty({ + description: 'Whether the invoice is partially paid', + example: true, + }) + isPartiallyPaid: boolean; + + @ApiProperty({ + description: 'Whether the invoice is fully paid', + example: false, + }) + isFullyPaid: boolean; + + @ApiProperty({ + description: 'The date when the invoice was created', + example: '2023-01-01T00:00:00Z', + }) + createdAt: Date; + + @ApiProperty({ + description: 'The date when the invoice was last updated', + example: '2023-01-02T00:00:00Z', + required: false, + }) + updatedAt?: Date; +} diff --git a/packages/server/src/modules/SaleInvoices/dtos/SaleInvoiceState.dto.ts b/packages/server/src/modules/SaleInvoices/dtos/SaleInvoiceState.dto.ts new file mode 100644 index 000000000..f2aa9675c --- /dev/null +++ b/packages/server/src/modules/SaleInvoices/dtos/SaleInvoiceState.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class SaleInvoiceStateResponseDto { + @ApiProperty({ + description: 'The ID of the default PDF template for sale invoices', + example: 1, + type: Number, + nullable: true, + }) + defaultTemplateId: number; +} diff --git a/packages/server/src/modules/SaleInvoices/queries/GetSaleInvoice.service.ts b/packages/server/src/modules/SaleInvoices/queries/GetSaleInvoice.service.ts index 31a71d073..d621f2191 100644 --- a/packages/server/src/modules/SaleInvoices/queries/GetSaleInvoice.service.ts +++ b/packages/server/src/modules/SaleInvoices/queries/GetSaleInvoice.service.ts @@ -6,6 +6,7 @@ import { SaleInvoiceTransformer } from './SaleInvoice.transformer'; import { CommandSaleInvoiceValidators } from '../commands/CommandSaleInvoiceValidators.service'; import { events } from '@/common/events/events'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; +import { SaleInvoiceResponseDto } from '../dtos/SaleInvoiceResponse.dto'; @Injectable() export class GetSaleInvoice { @@ -24,7 +25,9 @@ export class GetSaleInvoice { * @param {ISystemUser} authorizedUser - * @return {Promise} */ - public async getSaleInvoice(saleInvoiceId: number): Promise { + public async getSaleInvoice( + saleInvoiceId: number, + ): Promise { const saleInvoice = await this.saleInvoiceModel() .query() .findById(saleInvoiceId) diff --git a/packages/server/src/modules/SaleInvoices/queries/GetSaleInvoiceState.service.ts b/packages/server/src/modules/SaleInvoices/queries/GetSaleInvoiceState.service.ts index fe9b1734c..202fd254b 100644 --- a/packages/server/src/modules/SaleInvoices/queries/GetSaleInvoiceState.service.ts +++ b/packages/server/src/modules/SaleInvoices/queries/GetSaleInvoiceState.service.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { PdfTemplateModel } from '@/modules/PdfTemplate/models/PdfTemplate'; -import { ISaleInvocieState } from '../SaleInvoice.types'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; +import { SaleInvoiceStateResponseDto } from '../dtos/SaleInvoiceState.dto'; @Injectable() export class GetSaleInvoiceState { @@ -12,9 +12,9 @@ export class GetSaleInvoiceState { /** * Retrieves the create/edit invoice state. - * @return {Promise} + * @returns {Promise} */ - public async getSaleInvoiceState(): Promise { + public async getSaleInvoiceState(): Promise { const defaultPdfTemplate = await this.pdfTemplateModel() .query() .findOne({ resource: 'SaleInvoice' }) diff --git a/packages/server/src/modules/TaxRates/TaxRate.controller.ts b/packages/server/src/modules/TaxRates/TaxRate.controller.ts index c2259c94f..24c75b22a 100644 --- a/packages/server/src/modules/TaxRates/TaxRate.controller.ts +++ b/packages/server/src/modules/TaxRates/TaxRate.controller.ts @@ -8,22 +8,42 @@ import { Put, } from '@nestjs/common'; import { TaxRatesApplication } from './TaxRate.application'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { + ApiExtraModels, + ApiOperation, + ApiResponse, + ApiTags, + getSchemaPath, +} from '@nestjs/swagger'; import { CreateTaxRateDto, EditTaxRateDto } from './dtos/TaxRate.dto'; +import { TaxRateResponseDto } from './dtos/TaxRateResponse.dto'; @Controller('tax-rates') @ApiTags('Tax Rates') +@ApiExtraModels(TaxRateResponseDto) export class TaxRatesController { constructor(private readonly taxRatesApplication: TaxRatesApplication) {} @Post() @ApiOperation({ summary: 'Create a new tax rate.' }) + @ApiResponse({ + status: 201, + description: 'The tax rate has been successfully created.', + schema: { $ref: getSchemaPath(TaxRateResponseDto) }, + }) public createTaxRate(@Body() createTaxRateDTO: CreateTaxRateDto) { return this.taxRatesApplication.createTaxRate(createTaxRateDTO); } @Put(':id') @ApiOperation({ summary: 'Edit the given tax rate.' }) + @ApiResponse({ + status: 200, + description: 'The tax rate has been successfully updated.', + schema: { + $ref: getSchemaPath(TaxRateResponseDto), + }, + }) public editTaxRate( @Param('id') taxRateId: number, @Body() editTaxRateDTO: EditTaxRateDto, @@ -33,30 +53,68 @@ export class TaxRatesController { @Delete(':id') @ApiOperation({ summary: 'Delete the given tax rate.' }) + @ApiResponse({ + status: 200, + description: 'The tax rate has been successfully deleted.', + schema: { + $ref: getSchemaPath(TaxRateResponseDto), + }, + }) public deleteTaxRate(@Param('id') taxRateId: number) { return this.taxRatesApplication.deleteTaxRate(taxRateId); } @Get(':id') @ApiOperation({ summary: 'Retrieves the tax rate details.' }) + @ApiResponse({ + status: 200, + description: 'The tax rate details have been successfully retrieved.', + schema: { + $ref: getSchemaPath(TaxRateResponseDto), + }, + }) public getTaxRate(@Param('id') taxRateId: number) { return this.taxRatesApplication.getTaxRate(taxRateId); } @Get() @ApiOperation({ summary: 'Retrieves the tax rates.' }) + @ApiResponse({ + status: 200, + description: 'The tax rates have been successfully retrieved.', + schema: { + type: 'array', + items: { + $ref: getSchemaPath(TaxRateResponseDto), + }, + }, + }) public getTaxRates() { return this.taxRatesApplication.getTaxRates(); } @Put(':id/activate') @ApiOperation({ summary: 'Activate the given tax rate.' }) + @ApiResponse({ + status: 200, + description: 'The tax rate has been successfully activated.', + schema: { + $ref: getSchemaPath(TaxRateResponseDto), + }, + }) public activateTaxRate(@Param('id') taxRateId: number) { return this.taxRatesApplication.activateTaxRate(taxRateId); } @Put(':id/inactivate') @ApiOperation({ summary: 'Inactivate the given tax rate.' }) + @ApiResponse({ + status: 200, + description: 'The tax rate has been successfully inactivated.', + schema: { + $ref: getSchemaPath(TaxRateResponseDto), + }, + }) public inactivateTaxRate(@Param('id') taxRateId: number) { return this.taxRatesApplication.inactivateTaxRate(taxRateId); } diff --git a/packages/server/src/modules/TaxRates/dtos/TaxRateResponse.dto.ts b/packages/server/src/modules/TaxRates/dtos/TaxRateResponse.dto.ts new file mode 100644 index 000000000..738ce481d --- /dev/null +++ b/packages/server/src/modules/TaxRates/dtos/TaxRateResponse.dto.ts @@ -0,0 +1,77 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class TaxRateResponseDto { + @ApiProperty({ + description: 'The unique identifier of the tax rate', + example: 1, + }) + id: number; + + @ApiProperty({ + description: 'The name of the tax rate', + example: 'VAT', + }) + name: string; + + @ApiProperty({ + description: + 'The formatted name of the tax rate including the rate percentage', + example: 'VAT [10%]', + }) + nameFormatted: string; + + @ApiProperty({ + description: 'The code of the tax rate', + example: 'VAT', + }) + code: string; + + @ApiProperty({ + description: 'The rate of the tax rate as a decimal number', + example: 10, + }) + rate: number; + + @ApiProperty({ + description: 'The formatted rate of the tax rate with percentage symbol', + example: '10%', + }) + rateFormatted: string; + + @ApiProperty({ + description: 'The description of the tax rate', + example: 'Value Added Tax', + required: false, + }) + description?: string; + + @ApiProperty({ + description: 'Whether the tax is non-recoverable', + example: false, + }) + isNonRecoverable: boolean; + + @ApiProperty({ + description: 'Whether the tax is compound', + example: false, + }) + isCompound: boolean; + + @ApiProperty({ + description: 'Whether the tax rate is active', + example: true, + }) + active: boolean; + + @ApiProperty({ + description: 'The date when the tax rate was created', + example: '2024-03-20T10:00:00Z', + }) + createdAt: Date; + + @ApiProperty({ + description: 'The date when the tax rate was last updated', + example: '2024-03-20T10:00:00Z', + }) + updatedAt: Date; +} diff --git a/packages/server/src/modules/Warehouses/Warehouses.controller.ts b/packages/server/src/modules/Warehouses/Warehouses.controller.ts index 393ebb47d..dc529b38d 100644 --- a/packages/server/src/modules/Warehouses/Warehouses.controller.ts +++ b/packages/server/src/modules/Warehouses/Warehouses.controller.ts @@ -8,11 +8,19 @@ import { Put, } from '@nestjs/common'; import { WarehousesApplication } from './WarehousesApplication.service'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { + ApiExtraModels, + ApiOperation, + ApiResponse, + ApiTags, + getSchemaPath, +} from '@nestjs/swagger'; import { CreateWarehouseDto, EditWarehouseDto } from './dtos/Warehouse.dto'; +import { WarehouseResponseDto } from './dtos/WarehouseResponse.dto'; @Controller('warehouses') @ApiTags('Warehouses') +@ApiExtraModels(WarehouseResponseDto) export class WarehousesController { constructor(private warehousesApplication: WarehousesApplication) {} @@ -41,6 +49,11 @@ export class WarehousesController { @Get(':id') @ApiOperation({ summary: 'Get a warehouse' }) + @ApiResponse({ + status: 200, + description: 'The warehouse details have been successfully retrieved.', + schema: { $ref: getSchemaPath(WarehouseResponseDto) }, + }) getWarehouse(@Param('id') warehouseId: string) { return this.warehousesApplication.getWarehouse(Number(warehouseId)); } diff --git a/packages/server/src/modules/Warehouses/dtos/WarehouseResponse.dto.ts b/packages/server/src/modules/Warehouses/dtos/WarehouseResponse.dto.ts new file mode 100644 index 000000000..f6a92637c --- /dev/null +++ b/packages/server/src/modules/Warehouses/dtos/WarehouseResponse.dto.ts @@ -0,0 +1,39 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class WarehouseResponseDto { + @ApiProperty({ + description: 'The name of the warehouse', + example: 'Main Warehouse', + }) + name!: string; + + @ApiProperty({ + description: 'The unique code identifier for the warehouse', + example: 'WH-001', + }) + code!: string; + + @ApiProperty({ + description: 'The city where the warehouse is located', + example: 'New York', + }) + city!: string; + + @ApiProperty({ + description: 'The country where the warehouse is located', + example: 'United States', + }) + country!: string; + + @ApiProperty({ + description: 'The full address of the warehouse', + example: '123 Warehouse Street, New York, NY 10001', + }) + address!: string; + + @ApiProperty({ + description: 'Indicates if this is the primary warehouse', + example: true, + }) + primary!: boolean; +}