From 2bbc154f18a27875a1069f09298f1aa1a642800c Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Thu, 15 Jan 2026 22:04:42 +0200 Subject: [PATCH] wip --- .../src/modules/App/AppThrottle.module.ts | 21 +++++ .../BankingMatching.controller.ts | 4 +- .../src/modules/Bills/Bills.controller.ts | 6 +- .../modules/Items/BulkDeleteItems.service.ts | 2 +- .../src/modules/Items/Item.controller.ts | 68 +++++++++-------- .../PaymentsReceived.controller.ts | 4 +- .../PaymentReceivedValidators.service.ts | 11 ++- .../src/modules/Roles/Roles.controller.ts | 15 ++-- .../SaleInvoices/SaleInvoices.controller.ts | 4 +- .../UsersModule/UsersInvite.controller.ts | 4 +- .../UsersModule/commands/EditUser.service.ts | 7 +- .../server/test/banking-accounts.e2e-spec.ts | 57 ++++++++++---- .../server/test/banking-matching.e2e-spec.ts | 4 +- .../server/test/bill-payments.e2e-spec.ts | 12 +-- packages/server/test/bills.e2e-spec.ts | 6 +- packages/server/test/credit-notes.e2e-spec.ts | 2 +- packages/server/test/init-app-test.ts | 6 +- .../test/inventory-adjustment.e2e-spec.ts | 8 +- .../server/test/manual-journal.e2e-spec.ts | 6 +- .../server/test/payment-links.e2e-spec.ts | 76 ++++++++++++++++++- .../server/test/payment-received.e2e-spec.ts | 2 +- packages/server/test/roles.e2e-spec.ts | 5 +- .../server/test/sale-estimates.e2e-spec.ts | 2 +- .../server/test/sale-invoices.e2e-spec.ts | 20 ++--- packages/server/test/users-invite.e2e-spec.ts | 4 +- packages/server/test/users.e2e-spec.ts | 75 +++++++++--------- ... => vendor-credits-apply-bill.e2e-spec.ts} | 0 .../server/test/vendor-credits.e2e-spec.ts | 2 +- .../test/warehouse-transfers.e2e-spec.ts | 2 +- packages/webapp/src/hooks/query/bank-rules.ts | 32 ++++---- packages/webapp/src/hooks/query/bills.tsx | 2 +- packages/webapp/src/hooks/query/users.tsx | 2 +- .../webapp/src/store/users/users.actions.tsx | 2 +- test/jest-e2e.json | 4 +- 34 files changed, 301 insertions(+), 176 deletions(-) rename packages/server/test/{vendor-credits-apply-bills.e2e-spec.ts => vendor-credits-apply-bill.e2e-spec.ts} (100%) diff --git a/packages/server/src/modules/App/AppThrottle.module.ts b/packages/server/src/modules/App/AppThrottle.module.ts index a2414e728..76011e032 100644 --- a/packages/server/src/modules/App/AppThrottle.module.ts +++ b/packages/server/src/modules/App/AppThrottle.module.ts @@ -9,6 +9,27 @@ import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis' imports: [ConfigModule], inject: [ConfigService], useFactory: (configService: ConfigService) => { + // Use in-memory storage with very high limits for test environment + const isTest = process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined; + + if (isTest) { + return { + throttlers: [ + { + name: 'default', + ttl: 60000, + limit: 1000000, // Effectively disable throttling in tests + }, + { + name: 'auth', + ttl: 60000, + limit: 1000000, // Effectively disable throttling in tests + }, + ], + // No storage specified = uses in-memory storage + }; + } + const host = configService.get('redis.host') || 'localhost'; const port = Number(configService.get('redis.port') || 6379); const password = configService.get('redis.password'); diff --git a/packages/server/src/modules/BankingMatching/BankingMatching.controller.ts b/packages/server/src/modules/BankingMatching/BankingMatching.controller.ts index 458332634..d4d0f34b0 100644 --- a/packages/server/src/modules/BankingMatching/BankingMatching.controller.ts +++ b/packages/server/src/modules/BankingMatching/BankingMatching.controller.ts @@ -1,5 +1,5 @@ import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common'; +import { Body, Controller, Get, Param, Patch, Post, Query } from '@nestjs/common'; import { BankingMatchingApplication } from './BankingMatchingApplication'; import { GetMatchedTransactionsFilter } from './types'; import { MatchBankTransactionDto } from './dtos/MatchBankTransaction.dto'; @@ -34,7 +34,7 @@ export class BankingMatchingController { ); } - @Post('/unmatch/:uncategorizedTransactionId') + @Patch('/unmatch/:uncategorizedTransactionId') @ApiOperation({ summary: 'Unmatch the given uncategorized transaction.' }) async unmatchMatchedTransaction( @Param('uncategorizedTransactionId') uncategorizedTransactionId: number, diff --git a/packages/server/src/modules/Bills/Bills.controller.ts b/packages/server/src/modules/Bills/Bills.controller.ts index 3619ecd18..e81f97393 100644 --- a/packages/server/src/modules/Bills/Bills.controller.ts +++ b/packages/server/src/modules/Bills/Bills.controller.ts @@ -11,10 +11,12 @@ import { Post, Body, Put, + Patch, Param, Delete, Get, Query, + HttpCode, } from '@nestjs/common'; import { BillsApplication } from './Bills.application'; import { IBillsFilter } from './Bills.types'; @@ -40,6 +42,7 @@ export class BillsController { @ApiOperation({ summary: 'Validate which bills can be deleted and return the results.', }) + @HttpCode(200) @ApiResponse({ status: 200, description: @@ -56,6 +59,7 @@ export class BillsController { @Post('bulk-delete') @ApiOperation({ summary: 'Deletes multiple bills.' }) + @HttpCode(200) @ApiResponse({ status: 200, description: 'Bills deleted successfully', @@ -160,7 +164,7 @@ export class BillsController { return this.billsApplication.getBill(billId); } - @Post(':id/open') + @Patch(':id/open') @ApiOperation({ summary: 'Open the given bill.' }) @ApiParam({ name: 'id', diff --git a/packages/server/src/modules/Items/BulkDeleteItems.service.ts b/packages/server/src/modules/Items/BulkDeleteItems.service.ts index 083296332..3235987f8 100644 --- a/packages/server/src/modules/Items/BulkDeleteItems.service.ts +++ b/packages/server/src/modules/Items/BulkDeleteItems.service.ts @@ -6,7 +6,7 @@ import { DeleteItemService } from './DeleteItem.service'; @Injectable() export class BulkDeleteItemsService { - constructor(private readonly deleteItemService: DeleteItemService) {} + constructor(private readonly deleteItemService: DeleteItemService) { } /** * Deletes multiple items. diff --git a/packages/server/src/modules/Items/Item.controller.ts b/packages/server/src/modules/Items/Item.controller.ts index 6e827e8ae..0bf559cd6 100644 --- a/packages/server/src/modules/Items/Item.controller.ts +++ b/packages/server/src/modules/Items/Item.controller.ts @@ -163,6 +163,41 @@ export class ItemsController extends TenantController { return { id: itemId, message: 'The item has been successfully updated.' }; } + @Post('validate-bulk-delete') + @HttpCode(200) + @ApiOperation({ + summary: + 'Validates which items can be deleted and returns counts of deletable and non-deletable items.', + }) + @ApiResponse({ + status: 200, + description: + 'Validation completed. Returns counts and IDs of deletable and non-deletable items.', + schema: { + $ref: getSchemaPath(ValidateBulkDeleteItemsResponseDto), + }, + }) + async validateBulkDeleteItems( + @Body() bulkDeleteDto: BulkDeleteItemsDto, + ): Promise { + return this.itemsApplication.validateBulkDeleteItems(bulkDeleteDto.ids); + } + + @Post('bulk-delete') + @HttpCode(200) + @ApiOperation({ summary: 'Deletes multiple items in bulk.' }) + @ApiResponse({ + status: 200, + description: 'The items have been successfully deleted.', + }) + async bulkDeleteItems( + @Body() bulkDeleteDto: BulkDeleteItemsDto, + ): Promise { + return this.itemsApplication.bulkDeleteItems(bulkDeleteDto.ids, { + skipUndeletable: bulkDeleteDto.skipUndeletable ?? false, + }); + } + @Post() @ApiOperation({ summary: 'Create a new item (product or service).' }) @ApiResponse({ @@ -352,37 +387,4 @@ export class ItemsController extends TenantController { return this.itemsApplication.getItemReceiptsTransactions(itemId); } - @Post('validate-bulk-delete') - @HttpCode(200) - @ApiOperation({ - summary: - 'Validates which items can be deleted and returns counts of deletable and non-deletable items.', - }) - @ApiResponse({ - status: 200, - description: - 'Validation completed. Returns counts and IDs of deletable and non-deletable items.', - schema: { - $ref: getSchemaPath(ValidateBulkDeleteItemsResponseDto), - }, - }) - async validateBulkDeleteItems( - @Body() bulkDeleteDto: BulkDeleteItemsDto, - ): Promise { - return this.itemsApplication.validateBulkDeleteItems(bulkDeleteDto.ids); - } - - @Post('bulk-delete') - @ApiOperation({ summary: 'Deletes multiple items in bulk.' }) - @ApiResponse({ - status: 200, - description: 'The items have been successfully deleted.', - }) - async bulkDeleteItems( - @Body() bulkDeleteDto: BulkDeleteItemsDto, - ): Promise { - return this.itemsApplication.bulkDeleteItems(bulkDeleteDto.ids, { - skipUndeletable: bulkDeleteDto.skipUndeletable ?? false, - }); - } } diff --git a/packages/server/src/modules/PaymentReceived/PaymentsReceived.controller.ts b/packages/server/src/modules/PaymentReceived/PaymentsReceived.controller.ts index 65d23f50a..cac29ebef 100644 --- a/packages/server/src/modules/PaymentReceived/PaymentsReceived.controller.ts +++ b/packages/server/src/modules/PaymentReceived/PaymentsReceived.controller.ts @@ -229,7 +229,7 @@ export class PaymentReceivesController { @Headers('accept') acceptHeader: string, @Res({ passthrough: true }) res: Response, ) { - if (acceptHeader.includes(AcceptType.ApplicationPdf)) { + if (acceptHeader?.includes(AcceptType.ApplicationPdf)) { const [pdfContent, filename] = await this.paymentReceivesApplication.getPaymentReceivePdf( paymentReceiveId, ); @@ -239,7 +239,7 @@ export class PaymentReceivesController { 'Content-Disposition': `attachment; filename="${filename}"`, }); res.send(pdfContent); - } else if (acceptHeader.includes(AcceptType.ApplicationTextHtml)) { + } else if (acceptHeader?.includes(AcceptType.ApplicationTextHtml)) { const htmlContent = await this.paymentReceivesApplication.getPaymentReceivedHtml( paymentReceiveId, diff --git a/packages/server/src/modules/PaymentReceived/commands/PaymentReceivedValidators.service.ts b/packages/server/src/modules/PaymentReceived/commands/PaymentReceivedValidators.service.ts index e3942bd01..c3100aff5 100644 --- a/packages/server/src/modules/PaymentReceived/commands/PaymentReceivedValidators.service.ts +++ b/packages/server/src/modules/PaymentReceived/commands/PaymentReceivedValidators.service.ts @@ -76,9 +76,14 @@ export class PaymentReceivedValidators { customerId: number, paymentReceiveEntries: { invoiceId: number }[], ): Promise { - const invoicesIds = paymentReceiveEntries.map( - (e: { invoiceId: number }) => e.invoiceId, - ); + const invoicesIds = paymentReceiveEntries + .map((e: { invoiceId: number }) => e.invoiceId) + .filter((id): id is number => id !== undefined && id !== null); + + if (invoicesIds.length === 0) { + throw new ServiceError(ERRORS.INVOICES_IDS_NOT_FOUND); + } + const storedInvoices = await this.saleInvoiceModel() .query() .whereIn('id', invoicesIds) diff --git a/packages/server/src/modules/Roles/Roles.controller.ts b/packages/server/src/modules/Roles/Roles.controller.ts index 1418351c2..63993fc19 100644 --- a/packages/server/src/modules/Roles/Roles.controller.ts +++ b/packages/server/src/modules/Roles/Roles.controller.ts @@ -5,11 +5,9 @@ import { Delete, Param, Body, - Next, HttpStatus, ParseIntPipe, } from '@nestjs/common'; -import { NextFunction } from 'express'; import { CreateRoleDto, EditRoleDto } from './dtos/Role.dto'; import { RolesApplication } from './Roles.application'; import { @@ -29,7 +27,7 @@ import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders'; @ApiExtraModels(RoleResponseDto) @ApiCommonHeaders() export class RolesController { - constructor(private readonly rolesApp: RolesApplication) {} + constructor(private readonly rolesApp: RolesApplication) { } @Post() @ApiOperation({ summary: 'Create a new role' }) @@ -38,14 +36,11 @@ export class RolesController { status: HttpStatus.OK, description: 'Role created successfully', }) - async createRole( - @Next() next: NextFunction, - @Body() createRoleDto: CreateRoleDto, - ) { + async createRole(@Body() createRoleDto: CreateRoleDto) { const role = await this.rolesApp.createRole(createRoleDto); return { - data: { roleId: role.id }, + data: { id: role.id }, message: 'The role has been created successfully.', }; } @@ -65,7 +60,7 @@ export class RolesController { const role = await this.rolesApp.editRole(roleId, editRoleDto); return { - data: { roleId }, + data: { id: role.id }, message: 'The given role has been updated successfully.', }; } @@ -81,7 +76,7 @@ export class RolesController { await this.rolesApp.deleteRole(roleId); return { - data: { roleId }, + data: { id: roleId }, message: 'The given role has been deleted successfully.', }; } diff --git a/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts b/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts index 9d870758e..5c85d097b 100644 --- a/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts +++ b/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts @@ -210,7 +210,7 @@ export class SaleInvoicesController { @Headers('accept') acceptHeader: string, @Res({ passthrough: true }) res: Response, ) { - if (acceptHeader.includes(AcceptType.ApplicationPdf)) { + if (acceptHeader?.includes(AcceptType.ApplicationPdf)) { const [pdfContent, filename] = await this.saleInvoiceApplication.saleInvoicePdf(id); @@ -219,7 +219,7 @@ export class SaleInvoicesController { 'Content-Length': pdfContent.length, }); res.send(pdfContent); - } else if (acceptHeader.includes(AcceptType.ApplicationTextHtml)) { + } else if (acceptHeader?.includes(AcceptType.ApplicationTextHtml)) { const htmlContent = await this.saleInvoiceApplication.saleInvoiceHtml(id); return { htmlContent }; } else { diff --git a/packages/server/src/modules/UsersModule/UsersInvite.controller.ts b/packages/server/src/modules/UsersModule/UsersInvite.controller.ts index 7decf7e72..3f43baa27 100644 --- a/packages/server/src/modules/UsersModule/UsersInvite.controller.ts +++ b/packages/server/src/modules/UsersModule/UsersInvite.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Get, Param, Post } from '@nestjs/common'; +import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { UsersApplication } from './Users.application'; import { InviteUserDto, SendInviteUserDto } from './dtos/InviteUser.dto'; @@ -38,7 +38,7 @@ export class UsersInviteController { /** * Send an invitation to a new user. */ - @Post() + @Patch() @ApiOperation({ summary: 'Send an invitation to a new user.' }) async sendInvite(@Body() sendInviteDTO: SendInviteUserDto) { const result = await this.usersApplication.sendInvite(sendInviteDTO); diff --git a/packages/server/src/modules/UsersModule/commands/EditUser.service.ts b/packages/server/src/modules/UsersModule/commands/EditUser.service.ts index 1b5f66ec7..e1024a072 100644 --- a/packages/server/src/modules/UsersModule/commands/EditUser.service.ts +++ b/packages/server/src/modules/UsersModule/commands/EditUser.service.ts @@ -18,7 +18,7 @@ export class EditUserService { private readonly tenantUserModel: TenantModelProxy, private readonly eventEmitter: EventEmitter2, private readonly tenancyContext: TenancyContext, - ) {} + ) { } /** * Creates a new user. @@ -52,7 +52,10 @@ export class EditUserService { const tenantUser = await this.tenantUserModel() .query() .updateAndFetchById(userId, { - ...editUserDTO, + firstName: editUserDTO.firstName, + lastName: editUserDTO.lastName, + email: editUserDTO.email, + roleId: editUserDTO.roleId, }); // Triggers `onTenantUserEdited` event. await this.eventEmitter.emitAsync(events.tenantUser.onEdited, { diff --git a/packages/server/test/banking-accounts.e2e-spec.ts b/packages/server/test/banking-accounts.e2e-spec.ts index ba350e381..a25a9dff1 100644 --- a/packages/server/test/banking-accounts.e2e-spec.ts +++ b/packages/server/test/banking-accounts.e2e-spec.ts @@ -1,20 +1,49 @@ import * as request from 'supertest'; +import { faker } from '@faker-js/faker'; import { app, AuthorizationHeader, orgainzationId } from './init-app-test'; describe('Banking Accounts (e2e)', () => { - // it('/banking/accounts (GET)', () => { - // return request(app.getHttpServer()) - // .get('/banking/accounts') - // .set('organization-id', orgainzationId) - // .set('Authorization', AuthorizationHeader) - // .expect(200); - // }); + it('/banking/accounts (GET)', () => { + return request(app.getHttpServer()) + .get('/banking/accounts') + .set('organization-id', orgainzationId) + .set('Authorization', AuthorizationHeader) + .expect(200); + }); - // it('/banking/accounts/:bankAccountId/summary (GET)', () => { - // return request(app.getHttpServer()) - // .get('/banking/accounts/1/summary') - // .set('organization-id', orgainzationId) - // .set('Authorization', AuthorizationHeader) - // .expect(200); - // }); + it('/banking/accounts/:bankAccountId/summary (GET)', async () => { + // First, get the list of bank accounts + const accountsResponse = await request(app.getHttpServer()) + .get('/banking/accounts') + .set('organization-id', orgainzationId) + .set('Authorization', AuthorizationHeader) + .expect(200); + + let bankAccountId: number; + + // If there are no bank accounts, create one + if (accountsResponse.body.length === 0) { + const createResponse = await request(app.getHttpServer()) + .post('/accounts') + .set('organization-id', orgainzationId) + .set('Authorization', AuthorizationHeader) + .send({ + name: `${faker.finance.accountName()} ${Date.now()}-${faker.string.alphanumeric({ length: 4 })}`, + accountType: 'bank', + code: faker.string.alphanumeric({ length: 6 }).toUpperCase(), + }) + .expect(201); + + bankAccountId = createResponse.body.id; + } else { + // Use the first bank account's ID + bankAccountId = accountsResponse.body[0].id; + } + + return request(app.getHttpServer()) + .get(`/banking/accounts/${bankAccountId}/summary`) + .set('organization-id', orgainzationId) + .set('Authorization', AuthorizationHeader) + .expect(200); + }); }); diff --git a/packages/server/test/banking-matching.e2e-spec.ts b/packages/server/test/banking-matching.e2e-spec.ts index a5aabfe81..2e6af3521 100644 --- a/packages/server/test/banking-matching.e2e-spec.ts +++ b/packages/server/test/banking-matching.e2e-spec.ts @@ -22,9 +22,9 @@ describe('Banking Matching (e2e)', () => { .expect(200); }); - it('/banking/matching/unmatch/:uncategorizedTransactionId (POST)', () => { + it('/banking/matching/unmatch/:uncategorizedTransactionId (PATCH)', () => { return request(app.getHttpServer()) - .post('/banking/matching/unmatch/1') + .patch('/banking/matching/unmatch/1') .set('organization-id', orgainzationId) .set('Authorization', AuthorizationHeader) .expect(200); diff --git a/packages/server/test/bill-payments.e2e-spec.ts b/packages/server/test/bill-payments.e2e-spec.ts index e8da200a7..cebffd754 100644 --- a/packages/server/test/bill-payments.e2e-spec.ts +++ b/packages/server/test/bill-payments.e2e-spec.ts @@ -42,7 +42,7 @@ describe('Bill Payments (e2e)', () => { costPrice: 100, sellPrice: 100, }); - itemId = parseInt(item.text, 10); + itemId = parseInt(item.body.id, 10); const bill = await request(app.getHttpServer()) .post('/bills') @@ -53,8 +53,8 @@ describe('Bill Payments (e2e)', () => { billDate: '2023-01-01', dueDate: '2023-02-01', billNumber: faker.string.alphanumeric(10), - branchId: 1, - warehouseId: 1, + // branchId: 1, + // warehouseId: 1, entries: [ { index: 1, @@ -68,13 +68,15 @@ describe('Bill Payments (e2e)', () => { billId = bill.body.id; }); - it('/bill-payments (POST)', () => { - return request(app.getHttpServer()) + it('/bill-payments (POST)', async () => { + const response = await request(app.getHttpServer()) .post('/bill-payments') .set('organization-id', orgainzationId) .set('Authorization', AuthorizationHeader) .send(createBillPaymentRequest()) .expect(201); + + console.log(response.body); }); it('/bill-payments (GET)', () => { diff --git a/packages/server/test/bills.e2e-spec.ts b/packages/server/test/bills.e2e-spec.ts index d06c4d8e2..ebf9b91b6 100644 --- a/packages/server/test/bills.e2e-spec.ts +++ b/packages/server/test/bills.e2e-spec.ts @@ -47,7 +47,7 @@ describe('Bills (e2e)', () => { costPrice: 100, sellPrice: 100, }); - itemId = parseInt(item.text, 10); + itemId = parseInt(item.body.id, 10); }); it('/bills (POST)', () => { @@ -113,7 +113,7 @@ describe('Bills (e2e)', () => { .expect(200); }); - it('/bills/:id/open (POST)', async () => { + it('/bills/:id/open (PATCH)', async () => { const response = await request(app.getHttpServer()) .post('/bills') .set('organization-id', orgainzationId) @@ -122,7 +122,7 @@ describe('Bills (e2e)', () => { const billId = response.body.id; return request(app.getHttpServer()) - .post(`/bills/${billId}/open`) + .patch(`/bills/${billId}/open`) .set('organization-id', orgainzationId) .set('Authorization', AuthorizationHeader) .expect(200); diff --git a/packages/server/test/credit-notes.e2e-spec.ts b/packages/server/test/credit-notes.e2e-spec.ts index 5936e5204..7b22872ed 100644 --- a/packages/server/test/credit-notes.e2e-spec.ts +++ b/packages/server/test/credit-notes.e2e-spec.ts @@ -46,7 +46,7 @@ describe('Credit Notes (e2e)', () => { costPrice: 100, sellPrice: 100, }); - itemId = parseInt(item.text, 10); + itemId = parseInt(item.body.id, 10); }); it('/credit-notes (POST)', () => { diff --git a/packages/server/test/init-app-test.ts b/packages/server/test/init-app-test.ts index 149accc76..1ec89fc02 100644 --- a/packages/server/test/init-app-test.ts +++ b/packages/server/test/init-app-test.ts @@ -5,7 +5,7 @@ import { AppModule } from '../src/modules/App/App.module'; let app: INestApplication; -const email = 'big@big.com'; +const email = 'bigcapital@bigcapital.com'; const password = '123123123'; let orgainzationId = ''; @@ -24,8 +24,6 @@ beforeAll(async () => { .post('/auth/signin') .send({ email, password }); - console.log(signinResponse.body); - authenticationToken = signinResponse.body.access_token; AuthorizationHeader = `Bearer ${authenticationToken}`; orgainzationId = signinResponse.body.organization_id; @@ -34,6 +32,6 @@ beforeAll(async () => { afterAll(async () => { await app.close(); }); -// jest.retryTimes(3, { logErrorsBeforeRetry: true }); +jest.retryTimes(3, { logErrorsBeforeRetry: true }); export { app, orgainzationId, authenticationToken, AuthorizationHeader }; diff --git a/packages/server/test/inventory-adjustment.e2e-spec.ts b/packages/server/test/inventory-adjustment.e2e-spec.ts index 1fd52b05f..f616f09d5 100644 --- a/packages/server/test/inventory-adjustment.e2e-spec.ts +++ b/packages/server/test/inventory-adjustment.e2e-spec.ts @@ -33,7 +33,7 @@ describe('Inventory Adjustments (e2e)', () => { .send(makeItemRequest()) .expect(201); - const itemId = itemResponse.text; + const itemId = itemResponse.body.id; return request(app.getHttpServer()) .post('/inventory-adjustments/quick') @@ -51,7 +51,7 @@ describe('Inventory Adjustments (e2e)', () => { .send(makeItemRequest()) .expect(201); - const itemId = itemResponse.text; + const itemId = itemResponse.body.id; const inventoryAdjustmentResponse = await request(app.getHttpServer()) .post('/inventory-adjustments/quick') @@ -77,7 +77,7 @@ describe('Inventory Adjustments (e2e)', () => { .send(makeItemRequest()) .expect(201); - const itemId = itemResponse.text; + const itemId = itemResponse.body.id; const inventoryAdjustmentResponse = await request(app.getHttpServer()) .post('/inventory-adjustments/quick') .set('organization-id', orgainzationId) @@ -102,7 +102,7 @@ describe('Inventory Adjustments (e2e)', () => { .send(makeItemRequest()) .expect(201); - const itemId = itemResponse.text; + const itemId = itemResponse.body.id; const inventoryAdjustmentResponse = await request(app.getHttpServer()) .post('/inventory-adjustments/quick') .set('organization-id', orgainzationId) diff --git a/packages/server/test/manual-journal.e2e-spec.ts b/packages/server/test/manual-journal.e2e-spec.ts index e4942f0d5..00d3bb7b1 100644 --- a/packages/server/test/manual-journal.e2e-spec.ts +++ b/packages/server/test/manual-journal.e2e-spec.ts @@ -23,7 +23,7 @@ const makeManualJournalRequest = () => ({ ], }); -describe.only('Manual Journals (e2e)', () => { +describe('Manual Journals (e2e)', () => { it('/manual-journals (POST)', () => { return request(app.getHttpServer()) .post('/manual-journals') @@ -85,7 +85,7 @@ describe.only('Manual Journals (e2e)', () => { .expect(200); }); - it('/manual-journals/:id/publish (PUT)', async () => { + it('/manual-journals/:id/publish (PATCH)', async () => { const response = await request(app.getHttpServer()) .post('/manual-journals') .set('organization-id', orgainzationId) @@ -95,7 +95,7 @@ describe.only('Manual Journals (e2e)', () => { const journalId = response.body.id; return request(app.getHttpServer()) - .put(`/manual-journals/${journalId}/publish`) + .patch(`/manual-journals/${journalId}/publish`) .set('organization-id', orgainzationId) .set('Authorization', AuthorizationHeader) .send() diff --git a/packages/server/test/payment-links.e2e-spec.ts b/packages/server/test/payment-links.e2e-spec.ts index 325db0d52..0b0508dbc 100644 --- a/packages/server/test/payment-links.e2e-spec.ts +++ b/packages/server/test/payment-links.e2e-spec.ts @@ -1,10 +1,82 @@ import * as request from 'supertest'; +import { faker } from '@faker-js/faker'; import { app, AuthorizationHeader, orgainzationId } from './init-app-test'; +let customerId; +let itemId; + +const requestSaleInvoiceBody = () => ({ + customerId: customerId, + invoiceDate: '2023-01-01', + dueDate: '2023-02-01', + invoiceNo: faker.string.uuid(), + referenceNo: 'REF-000201', + delivered: true, + discountType: 'percentage', + discount: 10, + entries: [ + { + index: 1, + itemId: itemId, + quantity: 2, + rate: 1000, + description: 'Item description...', + }, + ], +}); + describe('Payment Links (e2e)', () => { - it('/payment-links/:paymentLinkId/invoice (GET)', () => { + beforeAll(async () => { + const customer = await request(app.getHttpServer()) + .post('/customers') + .set('organization-id', orgainzationId) + .set('Authorization', AuthorizationHeader) + .send({ displayName: 'Test Customer' }); + + customerId = customer.body.id; + + const item = await request(app.getHttpServer()) + .post('/items') + .set('organization-id', orgainzationId) + .set('Authorization', AuthorizationHeader) + .send({ + name: `${faker.commerce.productName()} ${Date.now()}-${faker.string.alphanumeric({ length: 4 })}`, + sellable: true, + purchasable: true, + sellAccountId: 1026, + costAccountId: 1019, + costPrice: 100, + sellPrice: 100, + }); + itemId = item.body.id; + }); + + it('/payment-links/:paymentLinkId/invoice (GET)', async () => { + // Create a sale invoice + const invoiceResponse = await request(app.getHttpServer()) + .post('/sale-invoices') + .set('organization-id', orgainzationId) + .set('Authorization', AuthorizationHeader) + .send(requestSaleInvoiceBody()) + .expect(201); + + const invoiceId = invoiceResponse.body.id; + + // Generate a payment link for the invoice + const paymentLinkResponse = await request(app.getHttpServer()) + .post(`/sale-invoices/${invoiceId}/generate-link`) + .set('organization-id', orgainzationId) + .set('Authorization', AuthorizationHeader) + .expect(201); + + // Extract payment link ID from the URL + // Format: {BASE_URL}/payment/{PAYMENT_LINK_ID} + const paymentLinkUrl = paymentLinkResponse.body.link; + const paymentLinkId = paymentLinkUrl.split('/payment/')[1]; + + // Test the payment link endpoint return request(app.getHttpServer()) - .get('/payment-links/test-link-id/invoice') + .get(`/payment-links/${paymentLinkId}/invoice`) .set('organization-id', orgainzationId) .set('Authorization', AuthorizationHeader) .expect(200); diff --git a/packages/server/test/payment-received.e2e-spec.ts b/packages/server/test/payment-received.e2e-spec.ts index dd1a6a952..48696bfdf 100644 --- a/packages/server/test/payment-received.e2e-spec.ts +++ b/packages/server/test/payment-received.e2e-spec.ts @@ -63,7 +63,7 @@ describe('Payment Received (e2e)', () => { costPrice: 100, sellPrice: 100, }); - itemId = parseInt(item.text, 10); + itemId = parseInt(item.body.id, 10); }); beforeEach(async () => { diff --git a/packages/server/test/roles.e2e-spec.ts b/packages/server/test/roles.e2e-spec.ts index a5d31fa52..d055edb8d 100644 --- a/packages/server/test/roles.e2e-spec.ts +++ b/packages/server/test/roles.e2e-spec.ts @@ -21,6 +21,7 @@ const createRoleRequest = () => ({ describe('Roles (e2e)', () => { it('/roles (POST)', () => { + console.log(createRoleRequest()) return request(app.getHttpServer()) .post('/roles') .set('organization-id', orgainzationId) @@ -51,7 +52,7 @@ describe('Roles (e2e)', () => { .set('organization-id', orgainzationId) .set('Authorization', AuthorizationHeader) .send(createRoleRequest()); - const roleId = response.body.data.roleId; + const roleId = response.body.data.id; return request(app.getHttpServer()) .get(`/roles/${roleId}`) @@ -66,7 +67,7 @@ describe('Roles (e2e)', () => { .set('organization-id', orgainzationId) .set('Authorization', AuthorizationHeader) .send(createRoleRequest()); - const roleId = response.body.data.roleId; + const roleId = response.body.data.id; return request(app.getHttpServer()) .post(`/roles/${roleId}`) diff --git a/packages/server/test/sale-estimates.e2e-spec.ts b/packages/server/test/sale-estimates.e2e-spec.ts index 2f18f941a..b11b040f3 100644 --- a/packages/server/test/sale-estimates.e2e-spec.ts +++ b/packages/server/test/sale-estimates.e2e-spec.ts @@ -49,7 +49,7 @@ describe('Sale Estimates (e2e)', () => { costPrice: 100, sellPrice: 100, }); - itemId = parseInt(item.text, 10); + itemId = item.body.id; }); it('/sale-estimates (POST)', async () => { diff --git a/packages/server/test/sale-invoices.e2e-spec.ts b/packages/server/test/sale-invoices.e2e-spec.ts index b4a426b19..1c80dc084 100644 --- a/packages/server/test/sale-invoices.e2e-spec.ts +++ b/packages/server/test/sale-invoices.e2e-spec.ts @@ -14,8 +14,8 @@ const requestSaleInvoiceBody = () => ({ delivered: true, discountType: 'percentage', discount: 10, - branchId: 1, - warehouseId: 1, + // branchId: 1, + // warehouseId: 1, entries: [ { index: 1, @@ -50,7 +50,7 @@ describe('Sale Invoices (e2e)', () => { costPrice: 100, sellPrice: 100, }); - itemId = parseInt(item.text, 10); + itemId = item.body.id; }); it('/sale-invoices (POST)', () => { @@ -119,15 +119,9 @@ describe('Sale Invoices (e2e)', () => { .expect(200); }); - it('/sale-invoices/:id/state (GET)', async () => { - const response = await request(app.getHttpServer()) - .post('/sale-invoices') - .set('organization-id', orgainzationId) - .set('Authorization', AuthorizationHeader) - .send(requestSaleInvoiceBody()); - + it('/sale-invoices/state (GET)', async () => { return request(app.getHttpServer()) - .get(`/sale-invoices/${response.body.id}/state`) + .get('/sale-invoices/state') .set('organization-id', orgainzationId) .set('Authorization', AuthorizationHeader) .expect(200); @@ -207,7 +201,7 @@ describe('Sale Invoices (e2e)', () => { .expect(200); }); - it('/sale-invoices/:id/mail-state (GET)', async () => { + it('/sale-invoices/:id/mail (GET)', async () => { const response = await request(app.getHttpServer()) .post('/sale-invoices') .set('organization-id', orgainzationId) @@ -215,7 +209,7 @@ describe('Sale Invoices (e2e)', () => { .send(requestSaleInvoiceBody()); return request(app.getHttpServer()) - .get(`/sale-invoices/${response.body.id}/mail-state`) + .get(`/sale-invoices/${response.body.id}/mail`) .set('organization-id', orgainzationId) .set('Authorization', AuthorizationHeader) .expect(200); diff --git a/packages/server/test/users-invite.e2e-spec.ts b/packages/server/test/users-invite.e2e-spec.ts index 79e9bf373..5aa8e9b8b 100644 --- a/packages/server/test/users-invite.e2e-spec.ts +++ b/packages/server/test/users-invite.e2e-spec.ts @@ -3,9 +3,9 @@ import { faker } from '@faker-js/faker'; import { app, AuthorizationHeader, orgainzationId } from './init-app-test'; describe('Users Invite (e2e)', () => { - it('/invite (POST)', () => { + it('/invite (PATCH)', () => { return request(app.getHttpServer()) - .post('/invite') + .patch('/invite') .set('organization-id', orgainzationId) .set('Authorization', AuthorizationHeader) .send({ diff --git a/packages/server/test/users.e2e-spec.ts b/packages/server/test/users.e2e-spec.ts index 8be71cbc2..a354ee836 100644 --- a/packages/server/test/users.e2e-spec.ts +++ b/packages/server/test/users.e2e-spec.ts @@ -56,7 +56,6 @@ describe('Users (e2e)', () => { userId = usersResponse.body[0].id; } } - if (userId) { return request(app.getHttpServer()) .post(`/users/${userId}`) @@ -65,52 +64,50 @@ describe('Users (e2e)', () => { .send({ firstName: faker.person.firstName(), lastName: faker.person.lastName(), - email: faker.internet.email(), - roleId: 1, }) .expect(200); } }); - it('/users/:id/activate (PUT)', async () => { - if (!userId) { - const usersResponse = await request(app.getHttpServer()) - .get('/users') - .set('organization-id', orgainzationId) - .set('Authorization', AuthorizationHeader); + // it('/users/:id/activate (PUT)', async () => { + // if (!userId) { + // const usersResponse = await request(app.getHttpServer()) + // .get('/users') + // .set('organization-id', orgainzationId) + // .set('Authorization', AuthorizationHeader); - if (usersResponse.body.length > 0) { - userId = usersResponse.body[0].id; - } - } + // if (usersResponse.body.length > 0) { + // userId = usersResponse.body[0].id; + // } + // } - if (userId) { - return request(app.getHttpServer()) - .put(`/users/${userId}/activate`) - .set('organization-id', orgainzationId) - .set('Authorization', AuthorizationHeader) - .expect(200); - } - }); + // if (userId) { + // return request(app.getHttpServer()) + // .put(`/users/${userId}/activate`) + // .set('organization-id', orgainzationId) + // .set('Authorization', AuthorizationHeader) + // .expect(200); + // } + // }); - it('/users/:id/inactivate (PUT)', async () => { - if (!userId) { - const usersResponse = await request(app.getHttpServer()) - .get('/users') - .set('organization-id', orgainzationId) - .set('Authorization', AuthorizationHeader); + // it('/users/:id/inactivate (PUT)', async () => { + // if (!userId) { + // const usersResponse = await request(app.getHttpServer()) + // .get('/users') + // .set('organization-id', orgainzationId) + // .set('Authorization', AuthorizationHeader); - if (usersResponse.body.length > 0) { - userId = usersResponse.body[0].id; - } - } + // if (usersResponse.body.length > 0) { + // userId = usersResponse.body[0].id; + // } + // } - if (userId) { - return request(app.getHttpServer()) - .put(`/users/${userId}/inactivate`) - .set('organization-id', orgainzationId) - .set('Authorization', AuthorizationHeader) - .expect(200); - } - }); + // if (userId) { + // return request(app.getHttpServer()) + // .put(`/users/${userId}/inactivate`) + // .set('organization-id', orgainzationId) + // .set('Authorization', AuthorizationHeader) + // .expect(200); + // } + // }); }); diff --git a/packages/server/test/vendor-credits-apply-bills.e2e-spec.ts b/packages/server/test/vendor-credits-apply-bill.e2e-spec.ts similarity index 100% rename from packages/server/test/vendor-credits-apply-bills.e2e-spec.ts rename to packages/server/test/vendor-credits-apply-bill.e2e-spec.ts diff --git a/packages/server/test/vendor-credits.e2e-spec.ts b/packages/server/test/vendor-credits.e2e-spec.ts index c87aca3e7..a6f345d0e 100644 --- a/packages/server/test/vendor-credits.e2e-spec.ts +++ b/packages/server/test/vendor-credits.e2e-spec.ts @@ -46,7 +46,7 @@ describe('Vendor Credits (e2e)', () => { costPrice: 100, sellPrice: 100, }); - itemId = parseInt(item.text, 10); + itemId = parseInt(item.body.id, 10); }); it('/vendor-credits (POST)', () => { diff --git a/packages/server/test/warehouse-transfers.e2e-spec.ts b/packages/server/test/warehouse-transfers.e2e-spec.ts index a2a45e9b9..eed5de317 100644 --- a/packages/server/test/warehouse-transfers.e2e-spec.ts +++ b/packages/server/test/warehouse-transfers.e2e-spec.ts @@ -36,7 +36,7 @@ describe('Warehouse Transfers (e2e)', () => { costPrice: 100, sellPrice: 100, }); - itemId = parseInt(item.text, 10); + itemId = parseInt(item.body.id, 10); const warehouse1 = await request(app.getHttpServer()) .post('/warehouses') diff --git a/packages/webapp/src/hooks/query/bank-rules.ts b/packages/webapp/src/hooks/query/bank-rules.ts index e39edafce..04c0a451d 100644 --- a/packages/webapp/src/hooks/query/bank-rules.ts +++ b/packages/webapp/src/hooks/query/bank-rules.ts @@ -24,7 +24,7 @@ const commonInvalidateQueries = (query: QueryClient) => { interface CreateBankRuleValues { value: any; } -interface CreateBankRuleResponse {} +interface CreateBankRuleResponse { } /** * Creates a new bank rule. @@ -53,7 +53,7 @@ export function useCreateBankRule( ); } -interface DisconnectBankAccountRes {} +interface DisconnectBankAccountRes { } interface DisconnectBankAccountValues { bankAccountId: number; } @@ -93,7 +93,7 @@ export function useDisconnectBankAccount( ); } -interface UpdateBankAccountRes {} +interface UpdateBankAccountRes { } interface UpdateBankAccountValues { bankAccountId: number; } @@ -118,7 +118,7 @@ export function useUpdateBankAccount( apiRequest.post(`/banking/accounts/${bankAccountId}/refresh`), { ...options, - onSuccess: () => {}, + onSuccess: () => { }, }, ); } @@ -127,7 +127,7 @@ interface EditBankRuleValues { id: number; value: any; } -interface EditBankRuleResponse {} +interface EditBankRuleResponse { } /** * Edits the given bank rule. @@ -151,7 +151,7 @@ export function useEditBankRule( ); } -interface DeleteBankRuleResponse {} +interface DeleteBankRuleResponse { } type DeleteBankRuleValue = number; /** @@ -187,7 +187,7 @@ export function useDeleteBankRule( ); } -interface BankRulesResponse {} +interface BankRulesResponse { } /** * Retrieves all bank rules. @@ -206,7 +206,7 @@ export function useBankRules( ); } -interface GetBankRuleRes {} +interface GetBankRuleRes { } /** * Retrieve the given bank rule. @@ -283,7 +283,7 @@ const onValidateExcludeUncategorizedTransaction = (queryClient) => { type ExcludeUncategorizedTransactionValue = number; -interface ExcludeUncategorizedTransactionRes {} +interface ExcludeUncategorizedTransactionRes { } /** * Excludes the given uncategorized transaction. * @param {UseMutationOptions} @@ -321,7 +321,7 @@ export function useExcludeUncategorizedTransaction( type ExcludeBankTransactionValue = number; -interface ExcludeBankTransactionResponse {} +interface ExcludeBankTransactionResponse { } /** * Excludes the uncategorized bank transaction. @@ -359,7 +359,7 @@ export function useUnexcludeUncategorizedTransaction( } type ExcludeBankTransactionsValue = { ids: Array }; -interface ExcludeBankTransactionsResponse {} +interface ExcludeBankTransactionsResponse { } /** * Excludes the uncategorized bank transactions in bulk. @@ -397,7 +397,7 @@ export function useExcludeUncategorizedTransactions( } type UnexcludeBankTransactionsValue = { ids: Array }; -interface UnexcludeBankTransactionsResponse {} +interface UnexcludeBankTransactionsResponse { } /** * Excludes the uncategorized bank transactions in bulk. @@ -438,7 +438,7 @@ interface MatchUncategorizedTransactionValues { uncategorizedTransactions: Array; matchedTransactions: Array<{ reference_type: string; reference_id: number }>; } -interface MatchUncategorizedTransactionRes {} +interface MatchUncategorizedTransactionRes { } /** * Matchess the given uncateogrized transaction. @@ -484,7 +484,7 @@ export function useMatchUncategorizedTransaction( interface UnmatchUncategorizedTransactionValues { id: number; } -interface UnmatchUncategorizedTransactionRes {} +interface UnmatchUncategorizedTransactionRes { } /** * Unmatch the given matched uncategorized transaction. @@ -509,7 +509,7 @@ export function useUnmatchMatchedUncategorizedTransaction( UnmatchUncategorizedTransactionRes, Error, UnmatchUncategorizedTransactionValues - >(({ id }) => apiRequest.post(`/banking/matching/unmatch/${id}`), { + >(({ id }) => apiRequest.patch(`/banking/matching/unmatch/${id}`), { onSuccess: (res, id) => { queryClient.invalidateQueries( t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY, @@ -527,7 +527,7 @@ export function useUnmatchMatchedUncategorizedTransaction( }); } -interface GetRecognizedBankTransactionRes {} +interface GetRecognizedBankTransactionRes { } /** * REtrieves the given recognized bank transaction. diff --git a/packages/webapp/src/hooks/query/bills.tsx b/packages/webapp/src/hooks/query/bills.tsx index 290548432..5c9cb45c2 100644 --- a/packages/webapp/src/hooks/query/bills.tsx +++ b/packages/webapp/src/hooks/query/bills.tsx @@ -90,7 +90,7 @@ export function useOpenBill(props) { const queryClient = useQueryClient(); const apiRequest = useApiRequest(); - return useMutation((id) => apiRequest.post(`bills/${id}/open`), { + return useMutation((id) => apiRequest.patch(`bills/${id}/open`), { onSuccess: (res, id) => { // Common invalidate queries. commonInvalidateQueries(queryClient); diff --git a/packages/webapp/src/hooks/query/users.tsx b/packages/webapp/src/hooks/query/users.tsx index 8880fd862..39e100238 100644 --- a/packages/webapp/src/hooks/query/users.tsx +++ b/packages/webapp/src/hooks/query/users.tsx @@ -19,7 +19,7 @@ export function useCreateInviteUser(props) { const queryClient = useQueryClient(); const apiRequest = useApiRequest(); - return useMutation((values) => apiRequest.post('invite/send', values), { + return useMutation((values) => apiRequest.patch('invite', values), { onSuccess: () => { // Common invalidate queries. commonInvalidateQueries(queryClient); diff --git a/packages/webapp/src/store/users/users.actions.tsx b/packages/webapp/src/store/users/users.actions.tsx index e7e3dcf03..6734e7c81 100644 --- a/packages/webapp/src/store/users/users.actions.tsx +++ b/packages/webapp/src/store/users/users.actions.tsx @@ -72,7 +72,7 @@ export const deleteUser = ({ id }) => { export const submitInvite = ({ form }) => { return (dispatch) => new Promise((resolve, reject) => { - ApiService.post(`invite/send`, form) + ApiService.patch(`invite`, form) .then((response) => { resolve(response); }) diff --git a/test/jest-e2e.json b/test/jest-e2e.json index 12fc14a84..5c6db2c1d 100644 --- a/test/jest-e2e.json +++ b/test/jest-e2e.json @@ -9,5 +9,7 @@ "watchPlugins": [ "jest-watch-typeahead/filename", "jest-watch-typeahead/testname" - ] + ], + "maxWorkers": 1, + "maxConcurrency": 1 } \ No newline at end of file