diff --git a/packages/server/src/common/pipes/ClassValidation.pipe.ts b/packages/server/src/common/pipes/ClassValidation.pipe.ts index ec6efd9c8..c7cc134be 100644 --- a/packages/server/src/common/pipes/ClassValidation.pipe.ts +++ b/packages/server/src/common/pipes/ClassValidation.pipe.ts @@ -14,7 +14,10 @@ export class ValidationPipe implements PipeTransform { return value; } const object = plainToInstance(metatype, value); - const errors = await validate(object); + const errors = await validate(object, { + // Strip validated object of any properties that do not have any decorators. + whitelist: true, + }); if (errors.length > 0) { throw new BadRequestException(errors); diff --git a/packages/server/src/modules/BankRules/dtos/BankRule.dto.ts b/packages/server/src/modules/BankRules/dtos/BankRule.dto.ts index 00c92d1f2..455caa36d 100644 --- a/packages/server/src/modules/BankRules/dtos/BankRule.dto.ts +++ b/packages/server/src/modules/BankRules/dtos/BankRule.dto.ts @@ -12,6 +12,7 @@ import { } from 'class-validator'; import { BankRuleComparator } from '../types'; import { ApiProperty } from '@nestjs/swagger'; +import { ToNumber } from '@/common/decorators/Validators'; class BankRuleConditionDto { @IsNotEmpty() @@ -44,6 +45,8 @@ export class CommandBankRuleDto { }) name: string; + @IsNotEmpty() + @ToNumber() @IsInt() @Min(0) @ApiProperty({ @@ -53,6 +56,7 @@ export class CommandBankRuleDto { order: number; @IsOptional() + @ToNumber() @IsInt() @Min(0) @ApiProperty({ @@ -61,6 +65,7 @@ export class CommandBankRuleDto { }) applyIfAccountId?: number; + @IsNotEmpty() @IsIn(['deposit', 'withdrawal']) @ApiProperty({ description: 'The transaction type to apply the rule if', @@ -82,11 +87,14 @@ export class CommandBankRuleDto { @Type(() => BankRuleConditionDto) @ApiProperty({ description: 'The conditions to apply the rule if', - example: [{ field: 'description', comparator: 'contains', value: 'Salary' }], + example: [ + { field: 'description', comparator: 'contains', value: 'Salary' }, + ], }) conditions: BankRuleConditionDto[]; @IsString() + @IsNotEmpty() @ApiProperty({ description: 'The category to assign the rule if', example: 'Income:Salary', @@ -95,6 +103,8 @@ export class CommandBankRuleDto { @IsInt() @Min(0) + @ToNumber() + @IsNotEmpty() @ApiProperty({ description: 'The account ID to assign the rule if', example: 1, diff --git a/packages/server/src/modules/BankingTranasctionsRegonize/events/TriggerRecognizedTransactions.ts b/packages/server/src/modules/BankingTranasctionsRegonize/events/TriggerRecognizedTransactions.ts index 4322e8fda..c6d932648 100644 --- a/packages/server/src/modules/BankingTranasctionsRegonize/events/TriggerRecognizedTransactions.ts +++ b/packages/server/src/modules/BankingTranasctionsRegonize/events/TriggerRecognizedTransactions.ts @@ -24,7 +24,7 @@ export class TriggerRecognizedTransactionsSubscriber { * @param {IBankRuleEventEditedPayload} payload - */ @OnEvent(events.bankRules.onEdited) - private async recognizedTransactionsOnRuleEdited({ + async recognizedTransactionsOnRuleEdited({ editRuleDTO, oldBankRule, bankRule, diff --git a/packages/server/src/modules/CreditNotes/CreditNoteApplication.service.ts b/packages/server/src/modules/CreditNotes/CreditNoteApplication.service.ts index 74f298a29..a024cde88 100644 --- a/packages/server/src/modules/CreditNotes/CreditNoteApplication.service.ts +++ b/packages/server/src/modules/CreditNotes/CreditNoteApplication.service.ts @@ -7,6 +7,8 @@ import { GetCreditNotePdf } from './queries/GetCreditNotePdf.serivce'; import { ICreditNotesQueryDTO } from './types/CreditNotes.types'; import { GetCreditNotesService } from './queries/GetCreditNotes.service'; import { CreateCreditNoteDto, EditCreditNoteDto } from './dtos/CreditNote.dto'; +import { GetCreditNoteState } from './queries/GetCreditNoteState.service'; +import { GetCreditNoteService } from './queries/GetCreditNote.service'; @Injectable() export class CreditNoteApplication { @@ -17,6 +19,8 @@ export class CreditNoteApplication { private readonly deleteCreditNoteService: DeleteCreditNoteService, private readonly getCreditNotePdfService: GetCreditNotePdf, private readonly getCreditNotesService: GetCreditNotesService, + private readonly getCreditNoteStateService: GetCreditNoteState, + private readonly getCreditNoteService: GetCreditNoteService ) {} /** @@ -76,4 +80,21 @@ export class CreditNoteApplication { getCreditNotes(creditNotesQuery: ICreditNotesQueryDTO) { return this.getCreditNotesService.getCreditNotesList(creditNotesQuery); } + + /** + * Retrieves the create/edit initial state of the credit note. + * @returns {Promise} + */ + getCreditNoteState() { + return this.getCreditNoteStateService.getCreditNoteState(); + } + + /** + * Retrieves the credit note. + * @param {number} creditNoteId + * @returns {Promise} + */ + getCreditNote(creditNoteId: number) { + return this.getCreditNoteService.getCreditNote(creditNoteId); + } } diff --git a/packages/server/src/modules/CreditNotes/CreditNotes.controller.ts b/packages/server/src/modules/CreditNotes/CreditNotes.controller.ts index 98a9f3012..c759414c7 100644 --- a/packages/server/src/modules/CreditNotes/CreditNotes.controller.ts +++ b/packages/server/src/modules/CreditNotes/CreditNotes.controller.ts @@ -1,4 +1,4 @@ -import { ApiTags } from '@nestjs/swagger'; +import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger'; import { Body, Controller, @@ -22,16 +22,42 @@ export class CreditNotesController { constructor(private creditNoteApplication: CreditNoteApplication) {} @Post() + @ApiOperation({ summary: 'Create a new credit note' }) + @ApiResponse({ status: 201, description: 'Credit note successfully created' }) + @ApiResponse({ status: 400, description: 'Invalid input data' }) createCreditNote(@Body() creditNoteDTO: CreateCreditNoteDto) { return this.creditNoteApplication.createCreditNote(creditNoteDTO); } + @Get('state') + @ApiOperation({ summary: 'Get credit note state' }) + @ApiResponse({ status: 200, description: 'Returns the credit note state' }) + getCreditNoteState() { + return this.creditNoteApplication.getCreditNoteState(); + } + + @Get(':id') + @ApiOperation({ summary: 'Get a specific credit note by ID' }) + @ApiParam({ name: 'id', description: 'Credit note ID', type: 'number' }) + @ApiResponse({ status: 200, description: 'Returns the credit note' }) + @ApiResponse({ status: 404, description: 'Credit note not found' }) + getCreditNote(@Param('id') creditNoteId: number) { + return this.creditNoteApplication.getCreditNote(creditNoteId); + } + @Get() + @ApiOperation({ summary: 'Get all credit notes' }) + @ApiResponse({ status: 200, description: 'Returns a list of credit notes' }) getCreditNotes(@Query() creditNotesQuery: ICreditNotesQueryDTO) { return this.creditNoteApplication.getCreditNotes(creditNotesQuery); } @Put(':id') + @ApiOperation({ summary: 'Update a credit note' }) + @ApiParam({ name: 'id', description: 'Credit note ID', type: 'number' }) + @ApiResponse({ status: 200, description: 'Credit note successfully updated' }) + @ApiResponse({ status: 404, description: 'Credit note not found' }) + @ApiResponse({ status: 400, description: 'Invalid input data' }) editCreditNote( @Param('id') creditNoteId: number, @Body() creditNoteDTO: EditCreditNoteDto, @@ -43,11 +69,19 @@ export class CreditNotesController { } @Put(':id/open') + @ApiOperation({ summary: 'Open a credit note' }) + @ApiParam({ name: 'id', description: 'Credit note ID', type: 'number' }) + @ApiResponse({ status: 200, description: 'Credit note successfully opened' }) + @ApiResponse({ status: 404, description: 'Credit note not found' }) openCreditNote(@Param('id') creditNoteId: number) { return this.creditNoteApplication.openCreditNote(creditNoteId); } @Delete(':id') + @ApiOperation({ summary: 'Delete a credit note' }) + @ApiParam({ name: 'id', description: 'Credit note ID', type: 'number' }) + @ApiResponse({ status: 200, description: 'Credit note successfully deleted' }) + @ApiResponse({ status: 404, description: 'Credit note not found' }) deleteCreditNote(@Param('id') creditNoteId: number) { return this.creditNoteApplication.deleteCreditNote(creditNoteId); } diff --git a/packages/server/src/modules/CreditNotes/CreditNotes.module.ts b/packages/server/src/modules/CreditNotes/CreditNotes.module.ts index d5dfabaa9..63801e042 100644 --- a/packages/server/src/modules/CreditNotes/CreditNotes.module.ts +++ b/packages/server/src/modules/CreditNotes/CreditNotes.module.ts @@ -15,7 +15,7 @@ import { WarehousesModule } from '../Warehouses/Warehouses.module'; import { PdfTemplatesModule } from '../PdfTemplate/PdfTemplates.module'; import { ChromiumlyTenancyModule } from '../ChromiumlyTenancy/ChromiumlyTenancy.module'; import { TemplateInjectableModule } from '../TemplateInjectable/TemplateInjectable.module'; -import { GetCreditNote } from './queries/GetCreditNote.service'; +import { GetCreditNoteService } from './queries/GetCreditNote.service'; import { CreditNoteBrandingTemplate } from './queries/CreditNoteBrandingTemplate.service'; import { AutoIncrementOrdersModule } from '../AutoIncrementOrders/AutoIncrementOrders.module'; import { CreditNoteGLEntries } from './commands/CreditNoteGLEntries'; @@ -52,7 +52,7 @@ import { CreditNotesApplyInvoiceModule } from '../CreditNotesApplyInvoice/Credit ], providers: [ CreateCreditNoteService, - GetCreditNote, + GetCreditNoteService, CommandCreditNoteDTOTransform, EditCreditNoteService, OpenCreditNoteService, @@ -74,7 +74,7 @@ import { CreditNotesApplyInvoiceModule } from '../CreditNotesApplyInvoice/Credit ], exports: [ CreateCreditNoteService, - GetCreditNote, + GetCreditNoteService, CommandCreditNoteDTOTransform, EditCreditNoteService, OpenCreditNoteService, diff --git a/packages/server/src/modules/CreditNotes/dtos/CreditNote.dto.ts b/packages/server/src/modules/CreditNotes/dtos/CreditNote.dto.ts index f2051c059..56ef9027b 100644 --- a/packages/server/src/modules/CreditNotes/dtos/CreditNote.dto.ts +++ b/packages/server/src/modules/CreditNotes/dtos/CreditNote.dto.ts @@ -1,3 +1,4 @@ +import { ToNumber } from '@/common/decorators/Validators'; import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto'; import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; @@ -5,9 +6,10 @@ import { ArrayMinSize, IsArray, IsBoolean, - IsDate, + IsDateString, IsEnum, IsInt, + IsNotEmpty, IsNumber, IsOptional, IsPositive, @@ -25,21 +27,25 @@ export class CreditNoteEntryDto extends ItemEntryDto {} class AttachmentDto { @IsString() + @IsNotEmpty() key: string; } export class CommandCreditNoteDto { + @ToNumber() @IsInt() + @IsNotEmpty() @ApiProperty({ example: 1, description: 'The customer ID' }) customerId: number; @IsOptional() + @ToNumber() @IsPositive() @ApiProperty({ example: 3.43, description: 'The exchange rate' }) exchangeRate?: number; - @IsDate() - @Type(() => Date) + @IsNotEmpty() + @IsDateString() @ApiProperty({ example: '2021-09-01', description: 'The credit note date' }) creditNoteDate: Date; @@ -64,26 +70,19 @@ export class CommandCreditNoteDto { termsConditions?: string; @IsBoolean() - @ApiProperty({ - example: false, - description: 'The credit note is open', - }) + @ApiProperty({ example: false, description: 'The credit note is open' }) open: boolean = false; @IsOptional() + @ToNumber() @IsInt() - @ApiProperty({ - example: 1, - description: 'The warehouse ID', - }) + @ApiProperty({ example: 1, description: 'The warehouse ID' }) warehouseId?: number; @IsOptional() + @ToNumber() @IsInt() - @ApiProperty({ - example: 1, - description: 'The branch ID', - }) + @ApiProperty({ example: 1, description: 'The branch ID' }) branchId?: number; @IsArray() @@ -91,14 +90,7 @@ export class CommandCreditNoteDto { @Type(() => CreditNoteEntryDto) @ArrayMinSize(1) @ApiProperty({ - example: [ - { - itemId: 1, - quantity: 1, - rate: 10, - taxRateId: 1, - }, - ], + example: [{ itemId: 1, quantity: 1, rate: 10, taxRateId: 1 }], description: 'The credit note entries', }) entries: CreditNoteEntryDto[]; @@ -110,19 +102,15 @@ export class CommandCreditNoteDto { attachments?: AttachmentDto[]; @IsOptional() + @ToNumber() @IsInt() - @ApiProperty({ - example: 1, - description: 'The pdf template ID', - }) + @ApiProperty({ example: 1, description: 'The pdf template ID' }) pdfTemplateId?: number; @IsOptional() + @ToNumber() @IsNumber() - @ApiProperty({ - example: 10, - description: 'The discount amount', - }) + @ApiProperty({ example: 10, description: 'The discount amount' }) discount?: number; @IsOptional() @@ -135,6 +123,7 @@ export class CommandCreditNoteDto { discountType?: DiscountType; @IsOptional() + @ToNumber() @IsNumber() adjustment?: number; } diff --git a/packages/server/src/modules/CreditNotes/queries/GetCreditNote.service.ts b/packages/server/src/modules/CreditNotes/queries/GetCreditNote.service.ts index 498e7e187..4c0ad5b65 100644 --- a/packages/server/src/modules/CreditNotes/queries/GetCreditNote.service.ts +++ b/packages/server/src/modules/CreditNotes/queries/GetCreditNote.service.ts @@ -7,7 +7,7 @@ import { ServiceError } from '@/modules/Items/ServiceError'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; @Injectable() -export class GetCreditNote { +export class GetCreditNoteService { constructor( private readonly transformer: TransformerInjectable, diff --git a/packages/server/src/modules/CreditNotes/queries/GetCreditNotePdf.serivce.ts b/packages/server/src/modules/CreditNotes/queries/GetCreditNotePdf.serivce.ts index 80cc91477..f71ba89a0 100644 --- a/packages/server/src/modules/CreditNotes/queries/GetCreditNotePdf.serivce.ts +++ b/packages/server/src/modules/CreditNotes/queries/GetCreditNotePdf.serivce.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { GetCreditNote } from './GetCreditNote.service'; +import { GetCreditNoteService } from './GetCreditNote.service'; import { CreditNoteBrandingTemplate } from './CreditNoteBrandingTemplate.service'; import { transformCreditNoteToPdfTemplate } from '../utils'; import { CreditNote } from '../models/CreditNote'; @@ -25,7 +25,7 @@ export class GetCreditNotePdf { constructor( private readonly chromiumlyTenancy: ChromiumlyTenancy, private readonly templateInjectable: TemplateInjectable, - private readonly getCreditNoteService: GetCreditNote, + private readonly getCreditNoteService: GetCreditNoteService, private readonly creditNoteBrandingTemplate: CreditNoteBrandingTemplate, private readonly eventPublisher: EventEmitter2, diff --git a/packages/server/src/modules/CreditNotesApplyInvoice/queries/GetCreditNoteAssociatedInvoicesToApply.service.ts b/packages/server/src/modules/CreditNotesApplyInvoice/queries/GetCreditNoteAssociatedInvoicesToApply.service.ts index 83a975071..1eb03a838 100644 --- a/packages/server/src/modules/CreditNotesApplyInvoice/queries/GetCreditNoteAssociatedInvoicesToApply.service.ts +++ b/packages/server/src/modules/CreditNotesApplyInvoice/queries/GetCreditNoteAssociatedInvoicesToApply.service.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service'; import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice'; -import { GetCreditNote } from '../../CreditNotes/queries/GetCreditNote.service'; +import { GetCreditNoteService } from '../../CreditNotes/queries/GetCreditNote.service'; import { CreditNoteWithInvoicesToApplyTransformer } from './CreditNoteWithInvoicesToApplyTransformer'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; @@ -14,7 +14,7 @@ export class GetCreditNoteAssociatedInvoicesToApply { */ constructor( private transformer: TransformerInjectable, - private getCreditNote: GetCreditNote, + private getCreditNote: GetCreditNoteService, @Inject(SaleInvoice.name) private saleInvoiceModel: TenantModelProxy, diff --git a/packages/server/src/modules/Currencies/models/Currency.model.ts b/packages/server/src/modules/Currencies/models/Currency.model.ts index 29abf94f2..8f40d440c 100644 --- a/packages/server/src/modules/Currencies/models/Currency.model.ts +++ b/packages/server/src/modules/Currencies/models/Currency.model.ts @@ -1,6 +1,6 @@ -import { TenantModel } from "@/modules/System/models/TenantModel"; +import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel'; -export class Currency extends TenantModel { +export class Currency extends TenantBaseModel { public readonly currencySign: string; public readonly currencyName: string; public readonly currencyCode: string; @@ -22,4 +22,4 @@ export class Currency extends TenantModel { static get resourceable() { return true; } -} \ No newline at end of file +} diff --git a/packages/server/src/modules/DynamicListing/DynamicFilter/DynamicFilterRoleAbstractor.ts b/packages/server/src/modules/DynamicListing/DynamicFilter/DynamicFilterRoleAbstractor.ts index 28e939e46..ef27449e7 100644 --- a/packages/server/src/modules/DynamicListing/DynamicFilter/DynamicFilterRoleAbstractor.ts +++ b/packages/server/src/modules/DynamicListing/DynamicFilter/DynamicFilterRoleAbstractor.ts @@ -394,7 +394,5 @@ export abstract class DynamicFilterRoleAbstractor implements IDynamicFilter { /** * Retrieves the response meta. */ - getResponseMeta() { - throw new Error('Method not implemented.'); - } + getResponseMeta() {} } diff --git a/packages/server/src/modules/Expenses/dtos/Expense.dto.ts b/packages/server/src/modules/Expenses/dtos/Expense.dto.ts index 3535d8247..6bdd9949d 100644 --- a/packages/server/src/modules/Expenses/dtos/Expense.dto.ts +++ b/packages/server/src/modules/Expenses/dtos/Expense.dto.ts @@ -1,10 +1,12 @@ +import { ToNumber } from '@/common/decorators/Validators'; import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsArray, IsBoolean, - IsDate, + IsDateString, IsInt, + IsISO4217CurrencyCode, IsNotEmpty, IsNumber, IsOptional, @@ -23,10 +25,12 @@ export class ExpenseCategoryDto { @IsNotEmpty() index: number; - @IsInt() @IsNotEmpty() + @ToNumber() + @IsInt() expenseAccountId: number; + @ToNumber() @IsNumber() @IsOptional() amount?: number; @@ -40,6 +44,7 @@ export class ExpenseCategoryDto { @IsOptional() landedCost?: boolean; + @ToNumber() @IsInt() @IsOptional() projectId?: number; @@ -55,7 +60,7 @@ export class CommandExpenseDto { }) referenceNo?: string; - @IsDate() + @IsDateString() @IsNotEmpty() @ApiProperty({ description: 'The payment date of the expense', @@ -63,8 +68,9 @@ export class CommandExpenseDto { }) paymentDate: Date; - @IsInt() @IsNotEmpty() + @ToNumber() + @IsInt() @ApiProperty({ description: 'The payment account id of the expense', example: 1, @@ -80,31 +86,22 @@ export class CommandExpenseDto { }) description?: string; + @ToNumber() @IsNumber() @IsOptional() - @ApiProperty({ - description: 'The exchange rate of the expense', - example: 1, - }) + @ApiProperty({ description: 'The exchange rate of the expense', example: 1 }) exchangeRate?: number; @IsString() @MaxLength(3) @IsOptional() + @IsISO4217CurrencyCode() @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({ @@ -113,14 +110,16 @@ export class CommandExpenseDto { }) publish?: boolean; - @IsInt() @IsOptional() + @ToNumber() + @IsInt() @ApiProperty({ description: 'The payee id of the expense', example: 1, }) payeeId?: number; + @ToNumber() @IsInt() @IsOptional() @ApiProperty({ diff --git a/packages/server/src/modules/ManualJournals/ManualJournals.controller.ts b/packages/server/src/modules/ManualJournals/ManualJournals.controller.ts index 69c979caa..a898ca6ce 100644 --- a/packages/server/src/modules/ManualJournals/ManualJournals.controller.ts +++ b/packages/server/src/modules/ManualJournals/ManualJournals.controller.ts @@ -108,11 +108,7 @@ export class ManualJournalsController { description: 'The manual journal details have been successfully retrieved.', }) @ApiResponse({ status: 404, description: 'The manual journal not found.' }) - public getManualJournals( - @Query() filterDto: Partial - ) { + public getManualJournals(@Query() filterDto: Partial) { return this.manualJournalsApplication.getManualJournals(filterDto); } - - } diff --git a/packages/server/src/modules/ManualJournals/dtos/ManualJournal.dto.ts b/packages/server/src/modules/ManualJournals/dtos/ManualJournal.dto.ts index d8b584da1..957ad6510 100644 --- a/packages/server/src/modules/ManualJournals/dtos/ManualJournal.dto.ts +++ b/packages/server/src/modules/ManualJournals/dtos/ManualJournal.dto.ts @@ -1,10 +1,13 @@ +import { ToNumber } from '@/common/decorators/Validators'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsArray, IsBoolean, IsDate, + IsDateString, IsInt, + IsNotEmpty, IsNumber, IsOptional, IsPositive, @@ -20,18 +23,21 @@ export class ManualJournalEntryDto { index: number; @ApiPropertyOptional({ description: 'Credit amount' }) + @ToNumber() @IsOptional() @IsNumber() @Min(0) credit?: number; @ApiPropertyOptional({ description: 'Debit amount' }) + @ToNumber() @IsOptional() @IsNumber() @Min(0) debit?: number; @ApiProperty({ description: 'Account ID' }) + @IsNotEmpty() @IsInt() accountId: number; @@ -41,16 +47,19 @@ export class ManualJournalEntryDto { note?: string; @ApiPropertyOptional({ description: 'Contact ID' }) - @IsOptional() + @ToNumber() @IsInt() + @IsOptional() contactId?: number; @ApiPropertyOptional({ description: 'Branch ID' }) + @ToNumber() @IsOptional() @IsInt() branchId?: number; @ApiPropertyOptional({ description: 'Project ID' }) + @ToNumber() @IsOptional() @IsInt() projectId?: number; @@ -64,8 +73,7 @@ class AttachmentDto { export class CommandManualJournalDto { @ApiProperty({ description: 'Journal date' }) - @IsDate() - @Type(() => Date) + @IsDateString() date: Date; @ApiPropertyOptional({ description: 'Currency code' }) @@ -74,6 +82,7 @@ export class CommandManualJournalDto { currencyCode?: string; @ApiPropertyOptional({ description: 'Exchange rate' }) + @ToNumber() @IsOptional() @IsNumber() @IsPositive() @@ -103,6 +112,7 @@ export class CommandManualJournalDto { description?: string; @ApiPropertyOptional({ description: 'Branch ID' }) + @ToNumber() @IsOptional() @IsInt() branchId?: number; diff --git a/packages/server/src/modules/PaymentReceived/PaymentsReceived.controller.ts b/packages/server/src/modules/PaymentReceived/PaymentsReceived.controller.ts index f72b83514..8ade4b9f3 100644 --- a/packages/server/src/modules/PaymentReceived/PaymentsReceived.controller.ts +++ b/packages/server/src/modules/PaymentReceived/PaymentsReceived.controller.ts @@ -18,6 +18,7 @@ import { IPaymentsReceivedFilter, PaymentReceiveMailOptsDTO, } from './types/PaymentReceived.types'; +import { CreatePaymentReceivedDto, EditPaymentReceivedDto } from './dtos/PaymentReceived.dto'; @Controller('payments-received') @ApiTags('payments-received') @@ -57,7 +58,7 @@ export class PaymentReceivesController { @Post() @ApiOperation({ summary: 'Create a new payment received.' }) public createPaymentReceived( - @Body() paymentReceiveDTO: IPaymentReceivedCreateDTO, + @Body() paymentReceiveDTO: CreatePaymentReceivedDto, ) { return this.paymentReceivesApplication.createPaymentReceived( paymentReceiveDTO, @@ -68,7 +69,7 @@ export class PaymentReceivesController { @ApiOperation({ summary: 'Edit the given payment received.' }) public editPaymentReceive( @Param('id', ParseIntPipe) paymentReceiveId: number, - @Body() paymentReceiveDTO: IPaymentReceivedEditDTO, + @Body() paymentReceiveDTO: EditPaymentReceivedDto, ) { return this.paymentReceivesApplication.editPaymentReceive( paymentReceiveId, diff --git a/packages/server/src/modules/PaymentReceived/dtos/PaymentReceived.dto.ts b/packages/server/src/modules/PaymentReceived/dtos/PaymentReceived.dto.ts index dea3e29c0..0c25fa108 100644 --- a/packages/server/src/modules/PaymentReceived/dtos/PaymentReceived.dto.ts +++ b/packages/server/src/modules/PaymentReceived/dtos/PaymentReceived.dto.ts @@ -1,16 +1,25 @@ -import { AttachmentLinkDto } from '@/modules/Attachments/dtos/Attachment.dto'; import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsArray, IsNotEmpty, ValidateNested } from 'class-validator'; -import { IsString } from 'class-validator'; -import { IsDateString, IsNumber, IsOptional } from 'class-validator'; -import { IsInt } from 'class-validator'; +import { + IsString, + IsDateString, + IsNumber, + IsOptional, + IsArray, + IsNotEmpty, + IsInt, + ValidateNested, +} from 'class-validator'; +import { ToNumber } from '@/common/decorators/Validators'; +import { AttachmentLinkDto } from '@/modules/Attachments/dtos/Attachment.dto'; export class PaymentReceivedEntryDto { + @ToNumber() @IsOptional() @IsInt() id?: number; + @ToNumber() @IsOptional() @IsInt() index?: number; @@ -20,13 +29,16 @@ export class PaymentReceivedEntryDto { paymentReceiveId?: number; @IsInt() + @IsNotEmpty() invoiceId: number; @IsNumber() + @IsNotEmpty() paymentAmount: number; } export class CommandPaymentReceivedDto { + @ToNumber() @IsInt() @IsNotEmpty() @ApiProperty({ description: 'The id of the customer', example: 1 }) @@ -40,6 +52,7 @@ export class CommandPaymentReceivedDto { paymentDate: Date | string; @IsOptional() + @ToNumber() @IsNumber() @ApiProperty({ description: 'The amount of the payment received', @@ -48,6 +61,7 @@ export class CommandPaymentReceivedDto { amount?: number; @IsOptional() + @ToNumber() @IsNumber() @ApiProperty({ description: 'The exchange rate of the payment received', @@ -63,6 +77,7 @@ export class CommandPaymentReceivedDto { }) referenceNo?: string; + @ToNumber() @IsInt() @IsNotEmpty() @ApiProperty({ @@ -72,6 +87,7 @@ export class CommandPaymentReceivedDto { depositAccountId: number; @IsOptional() + @ToNumber() @IsString() @ApiProperty({ description: 'The payment receive number of the payment received', @@ -97,6 +113,7 @@ export class CommandPaymentReceivedDto { entries: PaymentReceivedEntryDto[]; @IsOptional() + @ToNumber() @IsInt() @ApiProperty({ description: 'The id of the branch', diff --git a/packages/server/src/modules/SaleEstimates/dtos/SaleEstimate.dto.ts b/packages/server/src/modules/SaleEstimates/dtos/SaleEstimate.dto.ts index 4c6180ed3..e05f29d9b 100644 --- a/packages/server/src/modules/SaleEstimates/dtos/SaleEstimate.dto.ts +++ b/packages/server/src/modules/SaleEstimates/dtos/SaleEstimate.dto.ts @@ -5,16 +5,16 @@ import { ArrayMinSize, IsArray, IsBoolean, - IsDate, + IsDateString, + IsEmail, IsEnum, IsNotEmpty, IsNumber, - IsOptional, IsString, Min, - MinLength, ValidateNested, } from 'class-validator'; +import { IsOptional, ToNumber } from '@/common/decorators/Validators'; enum DiscountType { Percentage = 'percentage', @@ -28,24 +28,22 @@ class AttachmentDto { key: string; } export class CommandSaleEstimateDto { - @IsNumber() @IsNotEmpty() - @ApiProperty({ - description: 'The id of the customer', - example: 1, - }) + @ToNumber() + @IsNumber() + @ApiProperty({ description: 'The id of the customer', example: 1 }) customerId: number; - @IsDate() - @Type(() => Date) + @IsNotEmpty() + @IsDateString() @ApiProperty({ description: 'The date of the estimate', example: '2021-01-01', }) estimateDate: Date; - @IsDate() - @Type(() => Date) + @IsNotEmpty() + @IsDateString() @ApiProperty({ description: 'The expiration date of the estimate', example: '2021-01-01', @@ -65,31 +63,26 @@ export class CommandSaleEstimateDto { estimateNumber?: string; @IsBoolean() - delivered: boolean = false; + @IsOptional() + delivered?: boolean = false; + @IsOptional() + @ToNumber() @IsNumber() @Min(0.01) - @IsOptional() - @ApiProperty({ - description: 'The exchange rate of the estimate', - example: 1, - }) + @ApiProperty({ description: 'The exchange rate of the estimate', example: 1 }) exchangeRate?: number; - @IsNumber() @IsOptional() - @ApiProperty({ - description: 'The id of the warehouse', - example: 1, - }) + @ToNumber() + @IsNumber() + @ApiProperty({ description: 'The id of the warehouse', example: 1 }) warehouseId?: number; - @IsNumber() @IsOptional() - @ApiProperty({ - description: 'The id of the branch', - example: 1, - }) + @ToNumber() + @IsNumber() + @ApiProperty({ description: 'The id of the branch', example: 1 }) branchId?: number; @IsArray() @@ -110,32 +103,33 @@ export class CommandSaleEstimateDto { }) entries: SaleEstimateEntryDto[]; - @IsString() @IsOptional() + @IsString() @ApiProperty({ description: 'The note of the estimate', example: 'This is a note', }) note?: string; - @IsString() @IsOptional() + @IsString() @ApiProperty({ description: 'The terms and conditions of the estimate', example: 'This is a terms and conditions', }) termsConditions?: string; - @IsString() @IsOptional() + @IsString() + @IsEmail() @ApiProperty({ description: 'The email to send the estimate to', example: 'test@test.com', }) sendToEmail?: string; - @IsArray() @IsOptional() + @IsArray() @ValidateNested({ each: true }) @Type(() => AttachmentDto) @ApiProperty({ @@ -146,22 +140,26 @@ export class CommandSaleEstimateDto { }, ], }) - @IsNumber() + @IsOptional() + @ToNumber() + @IsNumber() @ApiProperty({ description: 'The id of the pdf template', example: 1, }) pdfTemplateId?: number; - @IsNumber() @IsOptional() + @ToNumber() + @IsNumber() @ApiProperty({ description: 'The discount of the estimate', example: 1, }) discount?: number; + @IsOptional() @IsEnum(DiscountType) @ApiProperty({ description: 'The type of the discount', @@ -169,8 +167,9 @@ export class CommandSaleEstimateDto { }) discountType: DiscountType = DiscountType.Amount; - @IsNumber() + @ToNumber() @IsOptional() + @IsNumber() @ApiProperty({ description: 'The adjustment of the estimate', example: 1, diff --git a/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts b/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts index a1d24c118..b0a3eb0c7 100644 --- a/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts +++ b/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts @@ -21,6 +21,7 @@ import { ApiHeader, ApiOperation, ApiParam, + ApiQuery, ApiResponse, ApiTags, } from '@nestjs/swagger'; @@ -111,7 +112,7 @@ export class SaleInvoicesController { return this.saleInvoiceApplication.deleteSaleInvoice(id); } - @Get('receivable/:customerId?') + @Get('receivable') @ApiOperation({ summary: 'Retrieves the receivable sale invoices.' }) @ApiResponse({ status: 200, @@ -119,13 +120,13 @@ export class SaleInvoicesController { 'The receivable sale invoices have been successfully retrieved.', }) @ApiResponse({ status: 404, description: 'The customer not found.' }) - @ApiParam({ + @ApiQuery({ name: 'customerId', required: false, type: Number, description: 'The customer id', }) - getReceivableSaleInvoices(@Param('customerId') customerId?: number) { + getReceivableSaleInvoices(@Query('customerId') customerId?: number) { return this.saleInvoiceApplication.getReceivableSaleInvoices(customerId); } diff --git a/packages/server/src/modules/SaleInvoices/dtos/SaleInvoice.dto.ts b/packages/server/src/modules/SaleInvoices/dtos/SaleInvoice.dto.ts index 1793f2c26..078031f9c 100644 --- a/packages/server/src/modules/SaleInvoices/dtos/SaleInvoice.dto.ts +++ b/packages/server/src/modules/SaleInvoices/dtos/SaleInvoice.dto.ts @@ -1,3 +1,4 @@ +import { IsOptional, ToNumber } from '@/common/decorators/Validators'; import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto'; import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; @@ -5,12 +6,11 @@ import { ArrayMinSize, IsArray, IsBoolean, - IsDate, + IsDateString, IsEnum, IsInt, IsNotEmpty, IsNumber, - IsOptional, IsString, Min, ValidateNested, @@ -29,26 +29,24 @@ class PaymentMethodDto { enable: boolean; } - class AttachmentDto { @IsString() key: string; } class CommandSaleInvoiceDto { + @ToNumber() @IsInt() @IsNotEmpty() @ApiProperty({ description: 'Customer ID', example: 1 }) customerId: number; - @IsDate() - @Type(() => Date) + @IsDateString() @IsNotEmpty() @ApiProperty({ description: 'Invoice date', example: '2023-01-01T00:00:00Z' }) invoiceDate: Date; - @IsDate() - @Type(() => Date) + @IsDateString() @IsNotEmpty() @ApiProperty({ description: 'Due date', example: '2023-01-15T00:00:00Z' }) dueDate: Date; @@ -99,6 +97,7 @@ class CommandSaleInvoiceDto { termsConditions?: string; @IsOptional() + @ToNumber() @IsNumber() @Min(0) @ApiProperty({ @@ -110,16 +109,19 @@ class CommandSaleInvoiceDto { exchangeRate?: number; @IsOptional() + @ToNumber() @IsInt() @ApiProperty({ description: 'Warehouse ID', required: false, example: 1 }) warehouseId?: number; @IsOptional() + @ToNumber() @IsInt() @ApiProperty({ description: 'Branch ID', required: false, example: 1 }) branchId?: number; @IsOptional() + @ToNumber() @IsInt() @ApiProperty({ description: 'Project ID', required: false, example: 1 }) projectId?: number; @@ -145,6 +147,7 @@ class CommandSaleInvoiceDto { entries: ItemEntryDto[]; @IsOptional() + @ToNumber() @IsInt() @ApiProperty({ description: 'PDF template ID', required: false, example: 1 }) pdfTemplateId?: number; @@ -161,6 +164,7 @@ class CommandSaleInvoiceDto { paymentMethods?: PaymentMethodDto[]; @IsOptional() + @ToNumber() @IsNumber() @ApiProperty({ description: 'Discount value', required: false, example: 10 }) discount?: number; @@ -176,6 +180,7 @@ class CommandSaleInvoiceDto { discountType?: DiscountType; @IsOptional() + @ToNumber() @IsNumber() @ApiProperty({ description: 'Adjustment amount', @@ -185,6 +190,7 @@ class CommandSaleInvoiceDto { adjustment?: number; @IsOptional() + @ToNumber() @IsInt() @ApiProperty({ description: 'ID of the estimate this invoice is created from', diff --git a/packages/server/src/modules/SaleReceipts/dtos/SaleReceipt.dto.ts b/packages/server/src/modules/SaleReceipts/dtos/SaleReceipt.dto.ts index 943704087..123999c48 100644 --- a/packages/server/src/modules/SaleReceipts/dtos/SaleReceipt.dto.ts +++ b/packages/server/src/modules/SaleReceipts/dtos/SaleReceipt.dto.ts @@ -1,10 +1,13 @@ +import { ToNumber } from '@/common/decorators/Validators'; import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto'; import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { + ArrayMinSize, IsArray, IsBoolean, IsDate, + IsDateString, IsEnum, IsNotEmpty, IsNumber, @@ -28,8 +31,9 @@ class AttachmentDto { } export class CommandSaleReceiptDto { - @IsNumber() @IsNotEmpty() + @ToNumber() + @IsNumber() @ApiProperty({ description: 'The id of the customer', example: 1, @@ -37,6 +41,7 @@ export class CommandSaleReceiptDto { customerId: number; @IsOptional() + @ToNumber() @IsNumber() @IsPositive() @ApiProperty({ @@ -46,11 +51,12 @@ export class CommandSaleReceiptDto { exchangeRate?: number; @IsNumber() + @ToNumber() @IsNotEmpty() @ApiProperty({ description: 'The id of the deposit account', example: 1 }) depositAccountId: number; - @IsDate() + @IsDateString() @IsNotEmpty() @ApiProperty({ description: 'The date of the sale receipt', @@ -83,6 +89,7 @@ export class CommandSaleReceiptDto { @IsOptional() @IsNumber() + @ToNumber() @ApiProperty({ description: 'The id of the warehouse', example: 1, @@ -90,6 +97,7 @@ export class CommandSaleReceiptDto { warehouseId?: number; @IsOptional() + @ToNumber() @IsNumber() @ApiProperty({ description: 'The id of the branch', @@ -100,7 +108,7 @@ export class CommandSaleReceiptDto { @IsArray() @ValidateNested({ each: true }) @Type(() => SaleReceiptEntryDto) - @Min(1) + @ArrayMinSize(1) @ApiProperty({ description: 'The entries of the sale receipt', example: [{ key: '123456' }], @@ -134,6 +142,7 @@ export class CommandSaleReceiptDto { attachments?: AttachmentDto[]; @IsOptional() + @ToNumber() @IsNumber() @ApiProperty({ description: 'The id of the pdf template', @@ -142,6 +151,7 @@ export class CommandSaleReceiptDto { pdfTemplateId?: number; @IsOptional() + @ToNumber() @IsNumber() @ApiProperty({ description: 'The discount of the sale receipt', @@ -158,6 +168,7 @@ export class CommandSaleReceiptDto { discountType?: DiscountType; @IsOptional() + @ToNumber() @IsNumber() @ApiProperty({ description: 'The adjustment of the sale receipt', diff --git a/packages/server/src/modules/TransactionItemEntry/dto/ItemEntry.dto.ts b/packages/server/src/modules/TransactionItemEntry/dto/ItemEntry.dto.ts index 20a815ead..17ab6a252 100644 --- a/packages/server/src/modules/TransactionItemEntry/dto/ItemEntry.dto.ts +++ b/packages/server/src/modules/TransactionItemEntry/dto/ItemEntry.dto.ts @@ -1,3 +1,4 @@ +import { ToNumber } from '@/common/decorators/Validators'; import { DiscountType } from '@/common/types/Discount'; import { ApiProperty } from '@nestjs/swagger'; import { @@ -12,30 +13,35 @@ import { export class ItemEntryDto { @IsInt() + @IsOptional() @ApiProperty({ description: 'The index of the item entry', example: 1, }) index: number; - @IsInt() @IsNotEmpty() + @IsInt() @ApiProperty({ description: 'The id of the item', example: 1, }) itemId: number; - @IsNumber() + @IsOptional() @IsNotEmpty() + @ToNumber() + @IsNumber() @ApiProperty({ description: 'The rate of the item entry', example: 1, }) rate: number; - @IsNumber() + @IsOptional() @IsNotEmpty() + @ToNumber() + @IsNumber() @ApiProperty({ description: 'The quantity of the item entry', example: 1, @@ -43,7 +49,9 @@ export class ItemEntryDto { quantity: number; @IsOptional() + @IsNotEmpty() @IsNumber() + @ToNumber() @ApiProperty({ description: 'The discount of the item entry', example: 1, @@ -67,6 +75,7 @@ export class ItemEntryDto { description?: string; @IsOptional() + @IsNotEmpty() @IsString() @ApiProperty({ description: 'The tax code of the item entry', @@ -75,6 +84,7 @@ export class ItemEntryDto { taxCode?: string; @IsOptional() + @IsNotEmpty() @IsInt() @ApiProperty({ description: 'The tax rate id of the item entry', @@ -83,6 +93,7 @@ export class ItemEntryDto { taxRateId?: number; @IsOptional() + @IsNotEmpty() @IsInt() @ApiProperty({ description: 'The warehouse id of the item entry', @@ -91,6 +102,7 @@ export class ItemEntryDto { warehouseId?: number; @IsOptional() + @IsNotEmpty() @IsInt() @ApiProperty({ description: 'The project id of the item entry', @@ -99,6 +111,7 @@ export class ItemEntryDto { projectId?: number; @IsOptional() + @IsNotEmpty() @IsInt() @ApiProperty({ description: 'The project ref id of the item entry', @@ -107,6 +120,7 @@ export class ItemEntryDto { projectRefId?: number; @IsOptional() + @IsNotEmpty() @IsString() @IsIn(['TASK', 'BILL', 'EXPENSE']) @ApiProperty({ @@ -116,6 +130,7 @@ export class ItemEntryDto { projectRefType?: string; @IsOptional() + @IsNotEmpty() @IsNumber() @ApiProperty({ description: 'The project ref invoiced amount of the item entry', @@ -124,6 +139,7 @@ export class ItemEntryDto { projectRefInvoicedAmount?: number; @IsOptional() + @IsNotEmpty() @IsInt() @ApiProperty({ description: 'The sell account id of the item entry', @@ -132,6 +148,7 @@ export class ItemEntryDto { sellAccountId?: number; @IsOptional() + @IsNotEmpty() @IsInt() @ApiProperty({ description: 'The cost account id of the item entry', diff --git a/packages/webapp/src/hooks/query/bank-rules.ts b/packages/webapp/src/hooks/query/bank-rules.ts index 501bfbc75..eaf1e9b5f 100644 --- a/packages/webapp/src/hooks/query/bank-rules.ts +++ b/packages/webapp/src/hooks/query/bank-rules.ts @@ -225,7 +225,7 @@ export function useBankRule( () => apiRequest .get(`/banking/rules/${bankRuleId}`) - .then((res) => res.data.bank_rule), + .then((res) => res.data), { ...options }, ); } diff --git a/packages/webapp/src/hooks/query/invoices.tsx b/packages/webapp/src/hooks/query/invoices.tsx index 483b45d7b..80a7bcb91 100644 --- a/packages/webapp/src/hooks/query/invoices.tsx +++ b/packages/webapp/src/hooks/query/invoices.tsx @@ -238,11 +238,11 @@ export function useDueInvoices(customerId, props) { [t.SALE_INVOICES, t.SALE_INVOICES_DUE, customerId], { method: 'get', - url: `sale-invoices/payable`, + url: `sale-invoices/receivable`, params: { customer_id: customerId }, }, { - select: (res) => res.data.sales_invoices, + select: (res) => res.data, defaultData: [], ...props, },