mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-22 07:40:32 +00:00
wip
This commit is contained in:
@@ -35,10 +35,11 @@ import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis'
|
|||||||
const password = configService.get<string>('redis.password');
|
const password = configService.get<string>('redis.password');
|
||||||
const db = configService.get<number>('redis.db');
|
const db = configService.get<number>('redis.db');
|
||||||
|
|
||||||
const globalTtl = configService.get<number>('throttle.global.ttl');
|
// Ensure we always have valid numbers with fallback defaults
|
||||||
const globalLimit = configService.get<number>('throttle.global.limit');
|
const globalTtl = configService.get<number>('throttle.global.ttl') ?? 60000;
|
||||||
const authTtl = configService.get<number>('throttle.auth.ttl');
|
const globalLimit = configService.get<number>('throttle.global.limit') ?? 100;
|
||||||
const authLimit = configService.get<number>('throttle.auth.limit');
|
const authTtl = configService.get<number>('throttle.auth.ttl') ?? 60000;
|
||||||
|
const authLimit = configService.get<number>('throttle.auth.limit') ?? 10;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
throttlers: [
|
throttlers: [
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ import { TransactionsByReferenceRepository } from './TransactionsByReferenceRepo
|
|||||||
import { TransactionsByReferenceService } from './TransactionsByReference.service';
|
import { TransactionsByReferenceService } from './TransactionsByReference.service';
|
||||||
import { TransactionsByReferenceController } from './TransactionsByReference.controller';
|
import { TransactionsByReferenceController } from './TransactionsByReference.controller';
|
||||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||||
|
import { FinancialSheetCommonModule } from '../../common/FinancialSheetCommon.module';
|
||||||
|
import { AccountsModule } from '@/modules/Accounts/Accounts.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
imports: [FinancialSheetCommonModule, AccountsModule],
|
||||||
providers: [
|
providers: [
|
||||||
TransactionsByReferenceRepository,
|
TransactionsByReferenceRepository,
|
||||||
TransactionsByReferenceApplication,
|
TransactionsByReferenceApplication,
|
||||||
@@ -14,4 +17,4 @@ import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
|||||||
],
|
],
|
||||||
controllers: [TransactionsByReferenceController],
|
controllers: [TransactionsByReferenceController],
|
||||||
})
|
})
|
||||||
export class TransactionsByReferenceModule {}
|
export class TransactionsByReferenceModule { }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Controller, Get, Query } from '@nestjs/common';
|
import { Controller, Get, Query } from '@nestjs/common';
|
||||||
import { TransactionsByReferenceApplication } from './TransactionsByReferenceApplication';
|
import { TransactionsByReferenceApplication } from './TransactionsByReferenceApplication';
|
||||||
import { ITransactionsByReferenceQuery } from './TransactionsByReference.types';
|
import { TransactionsByReferenceQueryDto } from './TransactionsByReferenceQuery.dto';
|
||||||
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
|
|
||||||
@Controller('reports/transactions-by-reference')
|
@Controller('reports/transactions-by-reference')
|
||||||
@@ -8,13 +8,13 @@ import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
|||||||
export class TransactionsByReferenceController {
|
export class TransactionsByReferenceController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly transactionsByReferenceApp: TransactionsByReferenceApplication,
|
private readonly transactionsByReferenceApp: TransactionsByReferenceApplication,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiResponse({ status: 200, description: 'Transactions by reference' })
|
@ApiResponse({ status: 200, description: 'Transactions by reference' })
|
||||||
@ApiOperation({ summary: 'Get transactions by reference' })
|
@ApiOperation({ summary: 'Get transactions by reference' })
|
||||||
async getTransactionsByReference(
|
async getTransactionsByReference(
|
||||||
@Query() query: ITransactionsByReferenceQuery,
|
@Query() query: TransactionsByReferenceQueryDto,
|
||||||
) {
|
) {
|
||||||
const data = await this.transactionsByReferenceApp.getTransactions(query);
|
const data = await this.transactionsByReferenceApp.getTransactions(query);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
ITransactionsByReferencePojo,
|
ITransactionsByReferencePojo,
|
||||||
ITransactionsByReferenceQuery,
|
ITransactionsByReferenceQuery,
|
||||||
@@ -13,7 +13,7 @@ export class TransactionsByReferenceService {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly repository: TransactionsByReferenceRepository,
|
private readonly repository: TransactionsByReferenceRepository,
|
||||||
private readonly tenancyContext: TenancyContext
|
private readonly tenancyContext: TenancyContext
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve accounts transactions by given reference id and type.
|
* Retrieve accounts transactions by given reference id and type.
|
||||||
@@ -23,6 +23,12 @@ export class TransactionsByReferenceService {
|
|||||||
public async getTransactionsByReference(
|
public async getTransactionsByReference(
|
||||||
query: ITransactionsByReferenceQuery
|
query: ITransactionsByReferenceQuery
|
||||||
): Promise<ITransactionsByReferencePojo> {
|
): Promise<ITransactionsByReferencePojo> {
|
||||||
|
// Validate referenceId is a valid positive number
|
||||||
|
const referenceId = Number(query.referenceId);
|
||||||
|
if (isNaN(referenceId) || referenceId <= 0) {
|
||||||
|
throw new BadRequestException('referenceId must be a valid positive number');
|
||||||
|
}
|
||||||
|
|
||||||
const filter = {
|
const filter = {
|
||||||
...getTransactionsByReferenceQuery(),
|
...getTransactionsByReferenceQuery(),
|
||||||
...query,
|
...query,
|
||||||
@@ -31,7 +37,7 @@ export class TransactionsByReferenceService {
|
|||||||
|
|
||||||
// Retrieve the accounts transactions of the given reference.
|
// Retrieve the accounts transactions of the given reference.
|
||||||
const transactions = await this.repository.getTransactions(
|
const transactions = await this.repository.getTransactions(
|
||||||
Number(filter.referenceId),
|
referenceId,
|
||||||
filter.referenceType
|
filter.referenceType
|
||||||
);
|
);
|
||||||
// Transactions by reference report.
|
// Transactions by reference report.
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { IsNotEmpty, IsString } from 'class-validator';
|
||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class TransactionsByReferenceQueryDto {
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'The type of the reference (e.g., SaleInvoice, Bill, etc.)',
|
||||||
|
example: 'SaleInvoice',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
referenceType: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'The ID of the reference',
|
||||||
|
example: '1',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
referenceId: string;
|
||||||
|
}
|
||||||
@@ -47,18 +47,18 @@ export class TransactionsByReference extends FinancialSheet {
|
|||||||
debit: this.getAmountMeta(transaction.debit, { money: false }),
|
debit: this.getAmountMeta(transaction.debit, { money: false }),
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// referenceTypeFormatted: transaction.referenceTypeFormatted,
|
// formattedReferenceType: transaction.referenceTypeFormatted,
|
||||||
referenceTypeFormatted: '',
|
formattedReferenceType: '',
|
||||||
|
|
||||||
referenceType: transaction.referenceType,
|
referenceType: transaction.referenceType,
|
||||||
referenceId: transaction.referenceId,
|
referenceId: transaction.referenceId,
|
||||||
|
|
||||||
contactId: transaction.contactId,
|
contactId: transaction.contactId,
|
||||||
contactType: transaction.contactType,
|
contactType: transaction.contactType,
|
||||||
contactTypeFormatted: transaction.contactType,
|
formattedContactType: transaction.contactType || '',
|
||||||
|
|
||||||
accountName: transaction.account.name,
|
accountName: transaction.account?.name || '',
|
||||||
accountCode: transaction.account.code,
|
accountCode: transaction.account?.code || '',
|
||||||
accountId: transaction.accountId,
|
accountId: transaction.accountId,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export class PaymentReceivedValidators {
|
|||||||
|
|
||||||
@Inject(Account.name)
|
@Inject(Account.name)
|
||||||
private readonly accountModel: TenantModelProxy<typeof Account>,
|
private readonly accountModel: TenantModelProxy<typeof Account>,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the payment existance.
|
* Validates the payment existance.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Controller,
|
Controller,
|
||||||
Post,
|
Post,
|
||||||
|
Put,
|
||||||
Get,
|
Get,
|
||||||
Delete,
|
Delete,
|
||||||
Param,
|
Param,
|
||||||
@@ -45,7 +46,7 @@ export class RolesController {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post(':id')
|
@Put(':id')
|
||||||
@ApiOperation({ summary: 'Edit an existing role' })
|
@ApiOperation({ summary: 'Edit an existing role' })
|
||||||
@ApiParam({ name: 'id', description: 'Role ID' })
|
@ApiParam({ name: 'id', description: 'Role ID' })
|
||||||
@ApiBody({ type: EditRoleDto })
|
@ApiBody({ type: EditRoleDto })
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export class EditRoleService {
|
|||||||
|
|
||||||
@Inject(Role.name)
|
@Inject(Role.name)
|
||||||
private readonly roleModel: TenantModelProxy<typeof Role>,
|
private readonly roleModel: TenantModelProxy<typeof Role>,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edits details of the given role on the storage.
|
* Edits details of the given role on the storage.
|
||||||
@@ -30,7 +30,13 @@ export class EditRoleService {
|
|||||||
|
|
||||||
// Retrieve the given role or throw not found serice error.
|
// Retrieve the given role or throw not found serice error.
|
||||||
const oldRole = await this.roleModel().query().findById(roleId);
|
const oldRole = await this.roleModel().query().findById(roleId);
|
||||||
const permissions = editRoleDTO.permissions;
|
// Transform permissions: map permissionId to id for Objection.js upsertGraph
|
||||||
|
const permissions = editRoleDTO.permissions.map((perm) => ({
|
||||||
|
id: perm.permissionId,
|
||||||
|
subject: perm.subject,
|
||||||
|
ability: perm.ability,
|
||||||
|
value: perm.value,
|
||||||
|
}));
|
||||||
|
|
||||||
// Updates the role on the storage.
|
// Updates the role on the storage.
|
||||||
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export class CommandRolePermissionDto {
|
|||||||
value: boolean;
|
value: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CreateRolePermissionDto extends CommandRolePermissionDto {}
|
export class CreateRolePermissionDto extends CommandRolePermissionDto { }
|
||||||
export class EditRolePermissionDto extends CommandRolePermissionDto {
|
export class EditRolePermissionDto extends CommandRolePermissionDto {
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@@ -83,9 +83,9 @@ export class EditRoleDto extends CommandRoleDto {
|
|||||||
@IsArray()
|
@IsArray()
|
||||||
@ArrayMinSize(1)
|
@ArrayMinSize(1)
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => CommandRolePermissionDto)
|
@Type(() => EditRolePermissionDto)
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
type: [CommandRolePermissionDto],
|
type: [EditRolePermissionDto],
|
||||||
description: 'The permissions of the role',
|
description: 'The permissions of the role',
|
||||||
})
|
})
|
||||||
permissions: Array<EditRolePermissionDto>;
|
permissions: Array<EditRolePermissionDto>;
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ export class SaleReceiptsController {
|
|||||||
@Headers('accept') acceptHeader: string,
|
@Headers('accept') acceptHeader: string,
|
||||||
@Res({ passthrough: true }) res: Response,
|
@Res({ passthrough: true }) res: Response,
|
||||||
) {
|
) {
|
||||||
if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
|
if (acceptHeader?.includes(AcceptType.ApplicationPdf)) {
|
||||||
const [pdfContent] =
|
const [pdfContent] =
|
||||||
await this.saleReceiptApplication.getSaleReceiptPdf(id);
|
await this.saleReceiptApplication.getSaleReceiptPdf(id);
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ export class SaleReceiptsController {
|
|||||||
'Content-Length': pdfContent.length,
|
'Content-Length': pdfContent.length,
|
||||||
});
|
});
|
||||||
res.send(pdfContent);
|
res.send(pdfContent);
|
||||||
} else if (acceptHeader.includes(AcceptType.ApplicationTextHtml)) {
|
} else if (acceptHeader?.includes(AcceptType.ApplicationTextHtml)) {
|
||||||
const htmlContent =
|
const htmlContent =
|
||||||
await this.saleReceiptApplication.getSaleReceiptHtml(id);
|
await this.saleReceiptApplication.getSaleReceiptHtml(id);
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export class CreateSaleReceipt {
|
|||||||
|
|
||||||
@Inject(Customer.name)
|
@Inject(Customer.name)
|
||||||
private readonly customerModel: TenantModelProxy<typeof Customer>,
|
private readonly customerModel: TenantModelProxy<typeof Customer>,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new sale receipt with associated entries.
|
* Creates a new sale receipt with associated entries.
|
||||||
@@ -89,7 +89,7 @@ export class CreateSaleReceipt {
|
|||||||
|
|
||||||
// Inserts the sale receipt graph to the storage.
|
// Inserts the sale receipt graph to the storage.
|
||||||
const saleReceipt = await this.saleReceiptModel()
|
const saleReceipt = await this.saleReceiptModel()
|
||||||
.query()
|
.query(trx)
|
||||||
.upsertGraph({
|
.upsertGraph({
|
||||||
...saleReceiptObj,
|
...saleReceiptObj,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -163,7 +163,11 @@ describe('Financial Statements (e2e)', () => {
|
|||||||
it('/reports/transactions-by-reference (GET)', () => {
|
it('/reports/transactions-by-reference (GET)', () => {
|
||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.get('/reports/transactions-by-reference')
|
.get('/reports/transactions-by-reference')
|
||||||
.query(baseQuery)
|
.query({
|
||||||
|
...baseQuery,
|
||||||
|
referenceId: '1',
|
||||||
|
referenceType: 'SaleInvoice',
|
||||||
|
})
|
||||||
.set('organization-id', orgainzationId)
|
.set('organization-id', orgainzationId)
|
||||||
.set('Authorization', AuthorizationHeader)
|
.set('Authorization', AuthorizationHeader)
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ const createRoleRequest = () => ({
|
|||||||
roleDescription: faker.lorem.sentence(),
|
roleDescription: faker.lorem.sentence(),
|
||||||
permissions: [
|
permissions: [
|
||||||
{
|
{
|
||||||
subject: 'items',
|
subject: 'Item',
|
||||||
ability: 'read',
|
ability: 'View',
|
||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
subject: 'items',
|
subject: 'Item',
|
||||||
ability: 'create',
|
ability: 'Create',
|
||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -27,7 +27,7 @@ describe('Roles (e2e)', () => {
|
|||||||
.set('organization-id', orgainzationId)
|
.set('organization-id', orgainzationId)
|
||||||
.set('Authorization', AuthorizationHeader)
|
.set('Authorization', AuthorizationHeader)
|
||||||
.send(createRoleRequest())
|
.send(createRoleRequest())
|
||||||
.expect(200);
|
.expect(201);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('/roles (GET)', () => {
|
it('/roles (GET)', () => {
|
||||||
@@ -61,16 +61,16 @@ describe('Roles (e2e)', () => {
|
|||||||
.expect(200);
|
.expect(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('/roles/:id (POST)', async () => {
|
it('/roles/:id (PUT)', async () => {
|
||||||
const response = await request(app.getHttpServer())
|
const createResponse = await request(app.getHttpServer())
|
||||||
.post('/roles')
|
.post('/roles')
|
||||||
.set('organization-id', orgainzationId)
|
.set('organization-id', orgainzationId)
|
||||||
.set('Authorization', AuthorizationHeader)
|
.set('Authorization', AuthorizationHeader)
|
||||||
.send(createRoleRequest());
|
.send(createRoleRequest());
|
||||||
const roleId = response.body.data.id;
|
const roleId = createResponse.body.data.id;
|
||||||
|
|
||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.post(`/roles/${roleId}`)
|
.put(`/roles/${roleId}`)
|
||||||
.set('organization-id', orgainzationId)
|
.set('organization-id', orgainzationId)
|
||||||
.set('Authorization', AuthorizationHeader)
|
.set('Authorization', AuthorizationHeader)
|
||||||
.send({
|
.send({
|
||||||
@@ -78,9 +78,8 @@ describe('Roles (e2e)', () => {
|
|||||||
roleDescription: faker.lorem.sentence(),
|
roleDescription: faker.lorem.sentence(),
|
||||||
permissions: [
|
permissions: [
|
||||||
{
|
{
|
||||||
permissionId: 1,
|
subject: 'Item',
|
||||||
subject: 'items',
|
ability: 'View',
|
||||||
ability: 'read',
|
|
||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -94,7 +93,7 @@ describe('Roles (e2e)', () => {
|
|||||||
.set('organization-id', orgainzationId)
|
.set('organization-id', orgainzationId)
|
||||||
.set('Authorization', AuthorizationHeader)
|
.set('Authorization', AuthorizationHeader)
|
||||||
.send(createRoleRequest());
|
.send(createRoleRequest());
|
||||||
const roleId = response.body.data.roleId;
|
const roleId = response.body.data.id;
|
||||||
|
|
||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.delete(`/roles/${roleId}`)
|
.delete(`/roles/${roleId}`)
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ describe('Sale Receipts (e2e)', () => {
|
|||||||
costPrice: 100,
|
costPrice: 100,
|
||||||
sellPrice: 100,
|
sellPrice: 100,
|
||||||
});
|
});
|
||||||
itemId = parseInt(item.text, 10);
|
itemId = parseInt(item.body.id, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('/sale-reeipts (POST)', () => {
|
it('/sale-reeipts (POST)', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user