Compare commits
1 Commits
nestjs-dto
...
users-modu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
985e1dbc01 |
@@ -1,7 +0,0 @@
|
||||
import { registerAs } from '@nestjs/config';
|
||||
|
||||
export default registerAs('bankfeed', () => ({
|
||||
enabled:
|
||||
process.env.BANK_FEED_ENABLED === 'true' ||
|
||||
process.env.BANK_FEED_ENABLED === 'yes',
|
||||
}));
|
||||
@@ -13,7 +13,6 @@ import signupRestrictions from './signup-restrictions';
|
||||
import jwt from './jwt';
|
||||
import mail from './mail';
|
||||
import loops from './loops';
|
||||
import bankfeed from './bankfeed';
|
||||
|
||||
export const config = [
|
||||
systemDatabase,
|
||||
@@ -30,6 +29,5 @@ export const config = [
|
||||
signupRestrictions,
|
||||
jwt,
|
||||
mail,
|
||||
loops,
|
||||
bankfeed,
|
||||
loops
|
||||
];
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* Map to store all models that have been marked to prevent base currency mutation.
|
||||
* Key is the model name, value is the model class.
|
||||
*/
|
||||
export const preventMutateBaseCurrencyModels = new Map<string, any>();
|
||||
|
||||
/**
|
||||
* Decorator that marks an ORM model to prevent base currency mutation.
|
||||
* When applied to a model class, it adds a static property `preventMutateBaseCurrency` set to true
|
||||
* and registers the model in the preventMutateBaseCurrencyModels map.
|
||||
*
|
||||
* @returns {ClassDecorator} A decorator function that can be applied to a class.
|
||||
*/
|
||||
export function PreventMutateBaseCurrency(): ClassDecorator {
|
||||
return (target: any) => {
|
||||
// Set the static property on the model class
|
||||
target.preventMutateBaseCurrency = true;
|
||||
|
||||
// Register the model in the map
|
||||
const modelName = target.name;
|
||||
preventMutateBaseCurrencyModels.set(modelName, target);
|
||||
|
||||
// Return the modified class
|
||||
return target;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered models that prevent base currency mutation.
|
||||
*
|
||||
* @returns {Map<string, any>} Map of model names to model classes
|
||||
*/
|
||||
export function getPreventMutateBaseCurrencyModels(): Map<string, any> {
|
||||
return preventMutateBaseCurrencyModels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a model is registered to prevent base currency mutation.
|
||||
*
|
||||
* @param {string} modelName - The name of the model to check
|
||||
* @returns {boolean} True if the model is registered, false otherwise
|
||||
*/
|
||||
export function isModelPreventMutateBaseCurrency(modelName: string): boolean {
|
||||
return preventMutateBaseCurrencyModels.has(modelName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific model by name that prevents base currency mutation.
|
||||
*
|
||||
* @param {string} modelName - The name of the model to retrieve
|
||||
* @returns {any | undefined} The model class if found, undefined otherwise
|
||||
*/
|
||||
export function getPreventMutateBaseCurrencyModel(
|
||||
modelName: string,
|
||||
): any | undefined {
|
||||
return preventMutateBaseCurrencyModels.get(modelName);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { Transform } from 'class-transformer';
|
||||
import { ValidateIf, ValidationOptions } from 'class-validator';
|
||||
|
||||
/**
|
||||
* Decorator that converts the property value to a number.
|
||||
* @returns PropertyDecorator
|
||||
*/
|
||||
export function ToNumber() {
|
||||
return Transform(({ value, key }) => {
|
||||
const defaultValue = null;
|
||||
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
// If value is an empty string or undefined/null, return it as-is (won’t pass validation)
|
||||
if (value === '' || value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
const parsed = Number(value);
|
||||
return !isNaN(parsed) ? parsed : value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the property is not empty.
|
||||
* @returns PropertyDecorator
|
||||
*/
|
||||
export function IsOptional(validationOptions?: ValidationOptions) {
|
||||
return ValidateIf((_obj, value) => {
|
||||
return value !== null && value !== undefined && value !== '';
|
||||
}, validationOptions);
|
||||
}
|
||||
@@ -70,10 +70,7 @@ export class SerializeInterceptor implements NestInterceptor<any, any> {
|
||||
next: CallHandler<any>,
|
||||
): Observable<any> {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
|
||||
// Transform both body and query parameters
|
||||
request.body = this.strategy.in(request.body);
|
||||
request.query = this.strategy.in(request.query);
|
||||
|
||||
// handle returns stream..
|
||||
return next.handle().pipe(map(this.strategy.out));
|
||||
|
||||
@@ -14,15 +14,12 @@ export class ValidationPipe implements PipeTransform<any> {
|
||||
return value;
|
||||
}
|
||||
const object = plainToInstance(metatype, value);
|
||||
const errors = await validate(object, {
|
||||
// Strip validated object of any properties that do not have any decorators.
|
||||
whitelist: true,
|
||||
});
|
||||
const errors = await validate(object);
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new BadRequestException(errors);
|
||||
}
|
||||
return object;
|
||||
return value;
|
||||
}
|
||||
|
||||
private toValidate(metatype: Function): boolean {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"primary_warehouse": "Primary Warehouse"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as FormData from 'form-data';
|
||||
import FormData from 'form-data';
|
||||
import { GotenbergUtils } from './GotenbergUtils';
|
||||
import { PageProperties } from './_types';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as FormData from 'form-data';
|
||||
import { Axios } from 'axios';
|
||||
import FormData from 'form-data';
|
||||
import Axios from 'axios';
|
||||
|
||||
export class GotenbergUtils {
|
||||
public static assert(condition: boolean, message: string): asserts condition {
|
||||
@@ -10,12 +10,12 @@ export class GotenbergUtils {
|
||||
|
||||
public static async fetch(endpoint: string, data: FormData): Promise<Buffer> {
|
||||
try {
|
||||
const response = await new Axios({
|
||||
const response = await Axios.post(endpoint, data, {
|
||||
headers: {
|
||||
...data.getHeaders(),
|
||||
},
|
||||
responseType: 'arraybuffer', // This ensures you get a Buffer bac
|
||||
}).post(endpoint, data);
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { constants, createReadStream, PathLike, promises } from 'fs';
|
||||
import * as FormData from 'form-data';
|
||||
import FormData from 'form-data';
|
||||
import { GotenbergUtils } from './GotenbergUtils';
|
||||
import { IConverter, PageProperties } from './_types';
|
||||
import { PdfFormat, ChromiumRoute } from './_types';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as FormData from 'form-data';
|
||||
import FormData from 'form-data';
|
||||
import { IConverter, PageProperties, PdfFormat, ChromiumRoute } from './_types';
|
||||
import { ConverterUtils } from './ConvertUtils';
|
||||
import { Converter } from './Converter';
|
||||
|
||||
@@ -13,7 +13,9 @@ export class MutateBaseCurrencyAccounts {
|
||||
* Mutates the all accounts or the organziation.
|
||||
* @param {string} currencyCode
|
||||
*/
|
||||
async mutateAllAccountsCurrency(currencyCode: string) {
|
||||
await this.accountModel().query().update({ currencyCode });
|
||||
}
|
||||
mutateAllAccountsCurrency = async (
|
||||
currencyCode: string,
|
||||
) => {
|
||||
await Account.query().update({ currencyCode });
|
||||
};
|
||||
}
|
||||
|
||||
@@ -88,8 +88,6 @@ import { ViewsModule } from '../Views/Views.module';
|
||||
import { CurrenciesModule } from '../Currencies/Currencies.module';
|
||||
import { MiscellaneousModule } from '../Miscellaneous/Miscellaneous.module';
|
||||
import { UsersModule } from '../UsersModule/Users.module';
|
||||
import { ContactsModule } from '../Contacts/Contacts.module';
|
||||
import { BankingPlaidModule } from '../BankingPlaid/BankingPlaid.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -187,7 +185,6 @@ import { BankingPlaidModule } from '../BankingPlaid/BankingPlaid.module';
|
||||
BankingTransactionsExcludeModule,
|
||||
BankingTransactionsRegonizeModule,
|
||||
BankingMatchingModule,
|
||||
BankingPlaidModule,
|
||||
TransactionsLockingModule,
|
||||
SettingsModule,
|
||||
FeaturesModule,
|
||||
@@ -213,8 +210,7 @@ import { BankingPlaidModule } from '../BankingPlaid/BankingPlaid.module';
|
||||
ViewsModule,
|
||||
CurrenciesModule,
|
||||
MiscellaneousModule,
|
||||
UsersModule,
|
||||
ContactsModule
|
||||
UsersModule
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [
|
||||
|
||||
@@ -33,7 +33,6 @@ const models = [
|
||||
|
||||
@Module({
|
||||
imports: [S3Module, ...models],
|
||||
exports: [...models],
|
||||
controllers: [AttachmentsController],
|
||||
providers: [
|
||||
DeleteAttachment,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as path from 'path';
|
||||
import path from 'path';
|
||||
// import config from '@/config';
|
||||
|
||||
export const getUploadedObjectUri = (objectKey: string) => {
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
} from 'class-validator';
|
||||
import { BankRuleComparator } from '../types';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { ToNumber } from '@/common/decorators/Validators';
|
||||
|
||||
class BankRuleConditionDto {
|
||||
@IsNotEmpty()
|
||||
@@ -45,8 +44,6 @@ export class CommandBankRuleDto {
|
||||
})
|
||||
name: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@ApiProperty({
|
||||
@@ -56,7 +53,6 @@ export class CommandBankRuleDto {
|
||||
order: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@ApiProperty({
|
||||
@@ -65,7 +61,6 @@ export class CommandBankRuleDto {
|
||||
})
|
||||
applyIfAccountId?: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsIn(['deposit', 'withdrawal'])
|
||||
@ApiProperty({
|
||||
description: 'The transaction type to apply the rule if',
|
||||
@@ -87,14 +82,11 @@ 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',
|
||||
@@ -103,8 +95,6 @@ export class CommandBankRuleDto {
|
||||
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@ToNumber()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({
|
||||
description: 'The account ID to assign the rule if',
|
||||
example: 1,
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||
import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { Knex } from 'knex';
|
||||
import { TENANCY_DB_CONNECTION } from '@/modules/Tenancy/TenancyDB/TenancyDB.constants';
|
||||
import { initialize } from 'objection';
|
||||
import { MatchedBankTransaction } from '@/modules/BankingMatching/models/MatchedBankTransaction';
|
||||
import { TenantModel } from '@/modules/System/models/TenantModel';
|
||||
import { RecognizedBankTransaction } from '@/modules/BankingTranasctionsRegonize/models/RecognizedBankTransaction';
|
||||
|
||||
@Injectable()
|
||||
export class GetBankAccountSummary {
|
||||
@@ -19,19 +14,6 @@ export class GetBankAccountSummary {
|
||||
private readonly uncategorizedBankTransactionModel: TenantModelProxy<
|
||||
typeof UncategorizedBankTransaction
|
||||
>,
|
||||
|
||||
@Inject(MatchedBankTransaction.name)
|
||||
private readonly matchedBankTransactionModel: TenantModelProxy<
|
||||
typeof MatchedBankTransaction
|
||||
>,
|
||||
|
||||
@Inject(RecognizedBankTransaction.name)
|
||||
private readonly recognizedBankTransaction: TenantModelProxy<
|
||||
typeof RecognizedBankTransaction
|
||||
>,
|
||||
|
||||
@Inject(TENANCY_DB_CONNECTION)
|
||||
private readonly tenantDb: () => Knex,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -45,11 +27,6 @@ export class GetBankAccountSummary {
|
||||
.findById(bankAccountId)
|
||||
.throwIfNotFound();
|
||||
|
||||
await initialize(this.tenantDb(), [
|
||||
this.uncategorizedBankTransactionModel(),
|
||||
this.matchedBankTransactionModel(),
|
||||
this.recognizedBankTransaction(),
|
||||
]);
|
||||
const commonQuery = (q) => {
|
||||
// Include just the given account.
|
||||
q.where('accountId', bankAccountId);
|
||||
@@ -60,6 +37,11 @@ export class GetBankAccountSummary {
|
||||
// Only the not categorized.
|
||||
q.modify('notCategorized');
|
||||
};
|
||||
|
||||
interface UncategorizedTransactionsCount {
|
||||
total: number;
|
||||
}
|
||||
|
||||
// Retrieves the uncategorized transactions count of the given bank account.
|
||||
const uncategorizedTranasctionsCount =
|
||||
await this.uncategorizedBankTransactionModel()
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Body, Controller, Post } from '@nestjs/common';
|
||||
import { PlaidApplication } from './PlaidApplication';
|
||||
import { PlaidItemDto } from './dtos/PlaidItem.dto';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
|
||||
@Controller('banking/plaid')
|
||||
@ApiTags('banking-plaid')
|
||||
export class BankingPlaidController {
|
||||
constructor(private readonly plaidApplication: PlaidApplication) {}
|
||||
|
||||
@Post('link-token')
|
||||
@ApiOperation({ summary: 'Get Plaid link token' })
|
||||
getLinkToken() {
|
||||
return this.plaidApplication.getLinkToken();
|
||||
}
|
||||
|
||||
@Post('exchange-token')
|
||||
@ApiOperation({ summary: 'Exchange Plaid access token' })
|
||||
exchangeToken(@Body() itemDTO: PlaidItemDto) {
|
||||
return this.plaidApplication.exchangeToken(itemDTO);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import { PlaidItemService } from './command/PlaidItem';
|
||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||
import { InjectSystemModel } from '../System/SystemModels/SystemModels.module';
|
||||
import { SystemPlaidItem } from './models/SystemPlaidItem';
|
||||
import { BankingPlaidController } from './BankingPlaid.controller';
|
||||
|
||||
const models = [RegisterTenancyModel(PlaidItem)];
|
||||
|
||||
@@ -39,6 +38,5 @@ const models = [RegisterTenancyModel(PlaidItem)];
|
||||
TenancyContext,
|
||||
],
|
||||
exports: [...models],
|
||||
controllers: [BankingPlaidController]
|
||||
})
|
||||
export class BankingPlaidModule {}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { PlaidItemService } from './command/PlaidItem';
|
||||
import { PlaidWebooks } from './command/PlaidWebhooks';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PlaidItemDTO } from './types/BankingPlaid.types';
|
||||
import { PlaidItemDto } from './dtos/PlaidItem.dto';
|
||||
|
||||
@Injectable()
|
||||
export class PlaidApplication {
|
||||
@@ -26,7 +25,7 @@ export class PlaidApplication {
|
||||
* @param {PlaidItemDTO} itemDTO
|
||||
* @returns
|
||||
*/
|
||||
public exchangeToken(itemDTO: PlaidItemDto): Promise<void> {
|
||||
public exchangeToken(itemDTO: PlaidItemDTO): Promise<void> {
|
||||
return this.plaidItemService.item(itemDTO);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,11 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
import { SystemPlaidItem } from '../models/SystemPlaidItem';
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
import { IPlaidItemCreatedEventPayload } from '../types/BankingPlaid.types';
|
||||
import {
|
||||
IPlaidItemCreatedEventPayload,
|
||||
PlaidItemDTO,
|
||||
} from '../types/BankingPlaid.types';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { PlaidItemDto } from '../dtos/PlaidItem.dto';
|
||||
|
||||
@Injectable()
|
||||
export class PlaidItemService {
|
||||
@@ -31,10 +33,10 @@ export class PlaidItemService {
|
||||
/**
|
||||
* Exchanges the public token to get access token and item id and then creates
|
||||
* a new Plaid item.
|
||||
* @param {PlaidItemDto} itemDTO - Plaid item data transfer object.
|
||||
* @param {PlaidItemDTO} itemDTO - Plaid item data transfer object.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async item(itemDTO: PlaidItemDto): Promise<void> {
|
||||
public async item(itemDTO: PlaidItemDTO): Promise<void> {
|
||||
const { publicToken, institutionId } = itemDTO;
|
||||
|
||||
const tenant = await this.tenancyContext.getTenant();
|
||||
|
||||
@@ -15,13 +15,6 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class PlaidUpdateTransactions {
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {PlaidSyncDb} plaidSync - Plaid sync service.
|
||||
* @param {UnitOfWork} uow - Unit of work.
|
||||
* @param {TenantModelProxy<typeof PlaidItem>} plaidItemModel - Plaid item model.
|
||||
* @param {PlaidApi} plaidClient - Plaid client.
|
||||
*/
|
||||
constructor(
|
||||
private readonly plaidSync: PlaidSyncDb,
|
||||
private readonly uow: UnitOfWork,
|
||||
@@ -35,7 +28,8 @@ export class PlaidUpdateTransactions {
|
||||
|
||||
/**
|
||||
* Handles sync the Plaid item to Bigcaptial under UOW.
|
||||
* @param {string} plaidItemId - Plaid item id.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} plaidItemId - Plaid item id.
|
||||
* @returns {Promise<{ addedCount: number; modifiedCount: number; removedCount: number; }>}
|
||||
*/
|
||||
public async updateTransactions(plaidItemId: string) {
|
||||
@@ -50,9 +44,9 @@ export class PlaidUpdateTransactions {
|
||||
* - New bank accounts.
|
||||
* - Last accounts feeds updated at.
|
||||
* - Turn on the accounts feed flag.
|
||||
* @param {number} tenantId - Tenant ID.
|
||||
* @param {string} plaidItemId - The Plaid ID for the item.
|
||||
* @param {Knex.Transaction} trx - Knex transaction.
|
||||
* @returns {Promise<{ addedCount: number; modifiedCount: number; removedCount: number; }>}
|
||||
* @returns {Promise<{ addedCount: number; modifiedCount: number; removedCount: number; }>}
|
||||
*/
|
||||
public async updateTransactionsWork(
|
||||
plaidItemId: string,
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class PlaidItemDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
publicToken: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
institutionId: string;
|
||||
}
|
||||
@@ -24,7 +24,7 @@ export class TriggerRecognizedTransactionsSubscriber {
|
||||
* @param {IBankRuleEventEditedPayload} payload -
|
||||
*/
|
||||
@OnEvent(events.bankRules.onEdited)
|
||||
async recognizedTransactionsOnRuleEdited({
|
||||
private async recognizedTransactionsOnRuleEdited({
|
||||
editRuleDTO,
|
||||
oldBankRule,
|
||||
bankRule,
|
||||
|
||||
@@ -9,8 +9,11 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { BankingTransactionsApplication } from './BankingTransactionsApplication.service';
|
||||
import {
|
||||
IBankAccountsFilter,
|
||||
ICashflowAccountTransactionsQuery,
|
||||
} from './types/BankingTransactions.types';
|
||||
import { CreateBankTransactionDto } from './dtos/CreateBankTransaction.dto';
|
||||
import { GetBankTransactionsQueryDto } from './dtos/GetBankTranasctionsQuery.dto';
|
||||
|
||||
@Controller('banking/transactions')
|
||||
@ApiTags('banking-transactions')
|
||||
@@ -21,7 +24,7 @@ export class BankingTransactionsController {
|
||||
|
||||
@Get()
|
||||
async getBankAccountTransactions(
|
||||
@Query() query: GetBankTransactionsQueryDto,
|
||||
@Query() query: ICashflowAccountTransactionsQuery,
|
||||
) {
|
||||
return this.bankingTransactionsApplication.getBankAccountTransactions(
|
||||
query,
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import {
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsNumberString,
|
||||
IsOptional,
|
||||
} from 'class-validator';
|
||||
import { NumberFormatQueryDto } from './NumberFormatQuery.dto';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class GetBankTransactionsQueryDto {
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
page: number;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
pageSize: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
accountId: number;
|
||||
|
||||
@IsOptional()
|
||||
numberFormat: NumberFormatQueryDto;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Type } from "class-transformer";
|
||||
import { IsBoolean, IsEnum, IsNumber, IsOptional, IsPositive } from "class-validator";
|
||||
|
||||
export class NumberFormatQueryDto {
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@IsPositive()
|
||||
@IsOptional()
|
||||
readonly precision: number;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
readonly divideOn1000: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
readonly showZero: boolean;
|
||||
|
||||
@IsEnum(['total', 'always', 'none'])
|
||||
@IsOptional()
|
||||
readonly formatMoney: 'total' | 'always' | 'none';
|
||||
|
||||
@IsEnum(['parentheses', 'mines'])
|
||||
@IsOptional()
|
||||
readonly negativeFormat: 'parentheses' | 'mines';
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable global-require */
|
||||
import * as moment from 'moment';
|
||||
import { Model } from 'objection';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
|
||||
export class UncategorizedBankTransaction extends TenantBaseModel {
|
||||
export class UncategorizedBankTransaction extends BaseModel {
|
||||
readonly amount!: number;
|
||||
readonly date!: Date | string;
|
||||
readonly categorized!: boolean;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-nocheck
|
||||
import * as R from 'ramda';
|
||||
import * as moment from 'moment';
|
||||
import R from 'ramda';
|
||||
import moment from 'moment';
|
||||
import { first, isEmpty } from 'lodash';
|
||||
import {
|
||||
ICashflowAccountTransaction,
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { ICashflowAccountTransactionsQuery } from '../../types/BankingTransactions.types';
|
||||
import {
|
||||
groupMatchedBankTransactions,
|
||||
groupUncategorizedTransactions,
|
||||
} from './_utils';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { AccountTransaction } from '@/modules/Accounts/models/AccountTransaction.model';
|
||||
import { UncategorizedBankTransaction } from '../../models/UncategorizedBankTransaction';
|
||||
import { MatchedBankTransaction } from '@/modules/BankingMatching/models/MatchedBankTransaction';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class GetBankAccountTransactionsRepository {
|
||||
private models: any;
|
||||
public query: ICashflowAccountTransactionsQuery;
|
||||
public transactions: any;
|
||||
public uncategorizedTransactions: any;
|
||||
@@ -20,28 +17,6 @@ export class GetBankAccountTransactionsRepository {
|
||||
public pagination: any;
|
||||
public openingBalance: any;
|
||||
|
||||
/**
|
||||
* @param {TenantModelProxy<typeof AccountTransaction>} accountTransactionModel - Account transaction model.
|
||||
* @param {TenantModelProxy<typeof UncategorizedBankTransaction>} uncategorizedBankTransactionModel - Uncategorized transaction model
|
||||
* @param {TenantModelProxy<typeof MatchedBankTransaction>} matchedBankTransactionModel - Matched bank transaction model.
|
||||
*/
|
||||
constructor(
|
||||
@Inject(AccountTransaction.name)
|
||||
private readonly accountTransactionModel: TenantModelProxy<
|
||||
typeof AccountTransaction
|
||||
>,
|
||||
|
||||
@Inject(UncategorizedBankTransaction.name)
|
||||
private readonly uncategorizedBankTransactionModel: TenantModelProxy<
|
||||
typeof UncategorizedBankTransaction
|
||||
>,
|
||||
|
||||
@Inject(MatchedBankTransaction.name)
|
||||
private readonly matchedBankTransactionModel: TenantModelProxy<
|
||||
typeof MatchedBankTransaction
|
||||
>,
|
||||
) {}
|
||||
|
||||
setQuery(query: ICashflowAccountTransactionsQuery) {
|
||||
this.query = query;
|
||||
}
|
||||
@@ -62,8 +37,9 @@ export class GetBankAccountTransactionsRepository {
|
||||
* @param {ICashflowAccountTransactionsQuery} query -
|
||||
*/
|
||||
async initCashflowAccountTransactions() {
|
||||
const { results, pagination } = await this.accountTransactionModel()
|
||||
.query()
|
||||
const { AccountTransaction } = this.models;
|
||||
|
||||
const { results, pagination } = await AccountTransaction.query()
|
||||
.where('account_id', this.query.accountId)
|
||||
.orderBy([
|
||||
{ column: 'date', order: 'desc' },
|
||||
@@ -83,9 +59,10 @@ export class GetBankAccountTransactionsRepository {
|
||||
* @return {Promise<number>}
|
||||
*/
|
||||
async initCashflowAccountOpeningBalance(): Promise<void> {
|
||||
const { AccountTransaction } = this.models;
|
||||
|
||||
// Retrieve the opening balance of credit and debit balances.
|
||||
const openingBalancesSubquery = this.accountTransactionModel()
|
||||
.query()
|
||||
const openingBalancesSubquery = AccountTransaction.query()
|
||||
.where('account_id', this.query.accountId)
|
||||
.orderBy([
|
||||
{ column: 'date', order: 'desc' },
|
||||
@@ -95,8 +72,7 @@ export class GetBankAccountTransactionsRepository {
|
||||
.offset(this.pagination.pageSize * (this.pagination.page - 1));
|
||||
|
||||
// Sumation of credit and debit balance.
|
||||
const openingBalances = await this.accountTransactionModel()
|
||||
.query()
|
||||
const openingBalances = await AccountTransaction.query()
|
||||
.sum('credit as credit')
|
||||
.sum('debit as debit')
|
||||
.from(openingBalancesSubquery.as('T'))
|
||||
@@ -111,11 +87,14 @@ export class GetBankAccountTransactionsRepository {
|
||||
* Initialize the uncategorized transactions of the bank account.
|
||||
*/
|
||||
async initCategorizedTransactions() {
|
||||
const { UncategorizedCashflowTransaction } = this.models;
|
||||
const refs = this.transactions.map((t) => [t.referenceType, t.referenceId]);
|
||||
|
||||
const uncategorizedTransactions =
|
||||
await this.uncategorizedBankTransactionModel()
|
||||
.query()
|
||||
.whereIn(['categorizeRefType', 'categorizeRefId'], refs);
|
||||
await UncategorizedCashflowTransaction.query().whereIn(
|
||||
['categorizeRefType', 'categorizeRefId'],
|
||||
refs,
|
||||
);
|
||||
|
||||
this.uncategorizedTransactions = uncategorizedTransactions;
|
||||
this.uncategorizedTransactionsMapByRef = groupUncategorizedTransactions(
|
||||
@@ -127,11 +106,14 @@ export class GetBankAccountTransactionsRepository {
|
||||
* Initialize the matched bank transactions of the bank account.
|
||||
*/
|
||||
async initMatchedTransactions(): Promise<void> {
|
||||
const { MatchedBankTransaction } = this.models;
|
||||
const refs = this.transactions.map((t) => [t.referenceType, t.referenceId]);
|
||||
|
||||
const matchedBankTransactions = await this.matchedBankTransactionModel()
|
||||
.query()
|
||||
.whereIn(['referenceType', 'referenceId'], refs);
|
||||
const matchedBankTransactions =
|
||||
await MatchedBankTransaction.query().whereIn(
|
||||
['referenceType', 'referenceId'],
|
||||
refs,
|
||||
);
|
||||
this.matchedBankTransactions = matchedBankTransactions;
|
||||
this.matchedBankTransactionsMapByRef = groupMatchedBankTransactions(
|
||||
matchedBankTransactions,
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { BillPaymentsApplication } from './BillPaymentsApplication.service';
|
||||
import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
|
||||
@@ -14,16 +13,11 @@ import {
|
||||
CreateBillPaymentDto,
|
||||
EditBillPaymentDto,
|
||||
} from './dtos/BillPayment.dto';
|
||||
import { GetBillPaymentsFilterDto } from './dtos/GetBillPaymentsFilter.dto';
|
||||
import { BillPaymentsPages } from './commands/BillPaymentsPages.service';
|
||||
|
||||
@Controller('bill-payments')
|
||||
@ApiTags('bill-payments')
|
||||
export class BillPaymentsController {
|
||||
constructor(
|
||||
private billPaymentsApplication: BillPaymentsApplication,
|
||||
private billPaymentsPagesService: BillPaymentsPages,
|
||||
) {}
|
||||
constructor(private billPaymentsApplication: BillPaymentsApplication) {}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: 'Create a new bill payment.' })
|
||||
@@ -63,22 +57,16 @@ export class BillPaymentsController {
|
||||
);
|
||||
}
|
||||
|
||||
@Get('/new-page/entries')
|
||||
@ApiOperation({
|
||||
summary:
|
||||
'Retrieves the payable entries of the new page once vendor be selected.',
|
||||
})
|
||||
@Get(':billPaymentId')
|
||||
@ApiOperation({ summary: 'Retrieves the bill payment details.' })
|
||||
@ApiParam({
|
||||
name: 'vendorId',
|
||||
name: 'billPaymentId',
|
||||
required: true,
|
||||
type: Number,
|
||||
description: 'The vendor id',
|
||||
description: 'The bill payment id',
|
||||
})
|
||||
async getBillPaymentNewPageEntries(@Query('vendorId') vendorId: number) {
|
||||
const entries =
|
||||
await this.billPaymentsPagesService.getNewPageEntries(vendorId);
|
||||
|
||||
return entries;
|
||||
public getBillPayment(@Param('billPaymentId') billPaymentId: string) {
|
||||
return this.billPaymentsApplication.getBillPayment(Number(billPaymentId));
|
||||
}
|
||||
|
||||
@Get(':billPaymentId/bills')
|
||||
@@ -92,47 +80,4 @@ export class BillPaymentsController {
|
||||
public getPaymentBills(@Param('billPaymentId') billPaymentId: string) {
|
||||
return this.billPaymentsApplication.getPaymentBills(Number(billPaymentId));
|
||||
}
|
||||
|
||||
@Get('/:billPaymentId/edit-page')
|
||||
@ApiOperation({
|
||||
summary: 'Retrieves the edit page of the given bill payment.',
|
||||
})
|
||||
@ApiParam({
|
||||
name: 'billPaymentId',
|
||||
required: true,
|
||||
type: Number,
|
||||
description: 'The bill payment id',
|
||||
})
|
||||
public async getBillPaymentEditPage(
|
||||
@Param('billPaymentId') billPaymentId: number,
|
||||
) {
|
||||
const billPaymentsWithEditEntries =
|
||||
await this.billPaymentsPagesService.getBillPaymentEditPage(billPaymentId);
|
||||
|
||||
return billPaymentsWithEditEntries;
|
||||
}
|
||||
|
||||
@Get(':billPaymentId')
|
||||
@ApiOperation({ summary: 'Retrieves the bill payment details.' })
|
||||
@ApiParam({
|
||||
name: 'billPaymentId',
|
||||
required: true,
|
||||
type: Number,
|
||||
description: 'The bill payment id',
|
||||
})
|
||||
public getBillPayment(@Param('billPaymentId') billPaymentId: string) {
|
||||
return this.billPaymentsApplication.getBillPayment(Number(billPaymentId));
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Retrieves the bill payments list.' })
|
||||
@ApiParam({
|
||||
name: 'filterDTO',
|
||||
required: true,
|
||||
type: GetBillPaymentsFilterDto,
|
||||
description: 'The bill payments filter dto',
|
||||
})
|
||||
public getBillPayments(@Query() filterDTO: GetBillPaymentsFilterDto) {
|
||||
return this.billPaymentsApplication.getBillPayments(filterDTO);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,11 @@ import { BillPaymentGLEntriesSubscriber } from './subscribers/BillPaymentGLEntri
|
||||
import { LedgerModule } from '../Ledger/Ledger.module';
|
||||
import { AccountsModule } from '../Accounts/Accounts.module';
|
||||
import { BillPaymentsExportable } from './queries/BillPaymentsExportable';
|
||||
import { GetBillPayments } from '../Bills/queries/GetBillPayments';
|
||||
import { BillPaymentsImportable } from './commands/BillPaymentsImportable';
|
||||
import { GetBillPaymentsService } from './queries/GetBillPayments.service';
|
||||
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
|
||||
import { BillPaymentsPages } from './commands/BillPaymentsPages.service';
|
||||
|
||||
@Module({
|
||||
imports: [LedgerModule, AccountsModule, DynamicListModule],
|
||||
imports: [LedgerModule, AccountsModule],
|
||||
providers: [
|
||||
BillPaymentsApplication,
|
||||
CreateBillPaymentService,
|
||||
@@ -39,10 +37,9 @@ import { BillPaymentsPages } from './commands/BillPaymentsPages.service';
|
||||
TenancyContext,
|
||||
BillPaymentGLEntries,
|
||||
BillPaymentGLEntriesSubscriber,
|
||||
GetBillPayments,
|
||||
BillPaymentsExportable,
|
||||
BillPaymentsImportable,
|
||||
GetBillPaymentsService,
|
||||
BillPaymentsPages,
|
||||
],
|
||||
exports: [
|
||||
BillPaymentValidators,
|
||||
|
||||
@@ -2,11 +2,11 @@ import { Injectable } from '@nestjs/common';
|
||||
import { CreateBillPaymentService } from './commands/CreateBillPayment.service';
|
||||
import { DeleteBillPayment } from './commands/DeleteBillPayment.service';
|
||||
import { EditBillPayment } from './commands/EditBillPayment.service';
|
||||
// import { GetBillPayments } from './GetBillPayments';
|
||||
import { GetBillPayment } from './queries/GetBillPayment.service';
|
||||
import { GetPaymentBills } from './queries/GetPaymentBills.service';
|
||||
import { GetBillPayments } from '../Bills/queries/GetBillPayments';
|
||||
import { CreateBillPaymentDto, EditBillPaymentDto } from './dtos/BillPayment.dto';
|
||||
import { GetBillPaymentsService } from './queries/GetBillPayments.service';
|
||||
import { GetBillPaymentsFilterDto } from './dtos/GetBillPaymentsFilter.dto';
|
||||
|
||||
/**
|
||||
* Bill payments application.
|
||||
@@ -20,7 +20,7 @@ export class BillPaymentsApplication {
|
||||
private deleteBillPaymentService: DeleteBillPayment,
|
||||
private getBillPaymentService: GetBillPayment,
|
||||
private getPaymentBillsService: GetPaymentBills,
|
||||
private getBillPaymentsService: GetBillPaymentsService,
|
||||
private getBillPaymentsService: GetBillPayments,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -58,10 +58,9 @@ export class BillPaymentsApplication {
|
||||
|
||||
/**
|
||||
* Retrieves bill payments list.
|
||||
* @param {GetBillPaymentsFilterDto} filterDTO - The given bill payments filter dto.
|
||||
*/
|
||||
public getBillPayments(filterDTO: GetBillPaymentsFilterDto) {
|
||||
return this.getBillPaymentsService.getBillPayments(filterDTO);
|
||||
public getBillPayments() {
|
||||
// return this.getBillPaymentsService.getBillPayments(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
75
packages/server/src/modules/BillPayments/GetBillPayments.ts
Normal file
75
packages/server/src/modules/BillPayments/GetBillPayments.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
// import { Inject, Service } from 'typedi';
|
||||
// import * as R from 'ramda';
|
||||
// import {
|
||||
// IBillPayment,
|
||||
// IBillPaymentsFilter,
|
||||
// IPaginationMeta,
|
||||
// IFilterMeta,
|
||||
// } from '@/interfaces';
|
||||
// import { BillPaymentTransformer } from './queries/BillPaymentTransformer';
|
||||
// import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
// import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
// import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
|
||||
// @Service()
|
||||
// export class GetBillPayments {
|
||||
// @Inject()
|
||||
// private tenancy: HasTenancyService;
|
||||
|
||||
// @Inject()
|
||||
// private dynamicListService: DynamicListingService;
|
||||
|
||||
// @Inject()
|
||||
// private transformer: TransformerInjectable;
|
||||
|
||||
// /**
|
||||
// * Retrieve bill payment paginted and filterable list.
|
||||
// * @param {number} tenantId
|
||||
// * @param {IBillPaymentsFilter} billPaymentsFilter
|
||||
// */
|
||||
// public async getBillPayments(
|
||||
// tenantId: number,
|
||||
// filterDTO: IBillPaymentsFilter
|
||||
// ): Promise<{
|
||||
// billPayments: IBillPayment[];
|
||||
// pagination: IPaginationMeta;
|
||||
// filterMeta: IFilterMeta;
|
||||
// }> {
|
||||
// const { BillPayment } = this.tenancy.models(tenantId);
|
||||
|
||||
// // Parses filter DTO.
|
||||
// const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// // Dynamic list service.
|
||||
// const dynamicList = await this.dynamicListService.dynamicList(
|
||||
// tenantId,
|
||||
// BillPayment,
|
||||
// filter
|
||||
// );
|
||||
// const { results, pagination } = await BillPayment.query()
|
||||
// .onBuild((builder) => {
|
||||
// builder.withGraphFetched('vendor');
|
||||
// builder.withGraphFetched('paymentAccount');
|
||||
|
||||
// dynamicList.buildQuery()(builder);
|
||||
// filter?.filterQuery && filter?.filterQuery(builder);
|
||||
// })
|
||||
// .pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
// // Transformes the bill payments models to POJO.
|
||||
// const billPayments = await this.transformer.transform(
|
||||
// tenantId,
|
||||
// results,
|
||||
// new BillPaymentTransformer()
|
||||
// );
|
||||
// return {
|
||||
// billPayments,
|
||||
// pagination,
|
||||
// filterMeta: dynamicList.getResponseMeta(),
|
||||
// };
|
||||
// }
|
||||
|
||||
// private parseListFilterDTO(filterDTO) {
|
||||
// return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
|
||||
// }
|
||||
// }
|
||||
@@ -8,7 +8,7 @@ import { ServiceError } from '../../Items/ServiceError';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class BillPaymentsPages {
|
||||
export default class BillPaymentsPages {
|
||||
/**
|
||||
* @param {TenantModelProxy<typeof Bill>} billModel - Bill model.
|
||||
* @param {TenantModelProxy<typeof BillPayment>} billPaymentModel - Bill payment model.
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsArray,
|
||||
IsDateString,
|
||||
IsDate,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
@@ -10,33 +9,25 @@ import {
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
import { AttachmentLinkDto } from '@/modules/Attachments/dtos/Attachment.dto';
|
||||
import { ToNumber } from '@/common/decorators/Validators';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class BillPaymentEntryDto {
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({ description: 'The id of the bill', example: 1 })
|
||||
billId: number;
|
||||
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({
|
||||
description: 'The payment amount of the bill payment',
|
||||
example: 100,
|
||||
})
|
||||
paymentAmount: number;
|
||||
}
|
||||
|
||||
export class CommandBillPaymentDTO {
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({ description: 'The id of the vendor', example: 1 })
|
||||
@ApiProperty({
|
||||
description: 'The id of the vendor',
|
||||
example: 1,
|
||||
})
|
||||
vendorId: number;
|
||||
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@ApiProperty({
|
||||
@@ -45,10 +36,12 @@ export class CommandBillPaymentDTO {
|
||||
})
|
||||
amount?: number;
|
||||
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({ description: 'The id of the payment account', example: 1 })
|
||||
@ApiProperty({
|
||||
description: 'The id of the payment account',
|
||||
example: 1,
|
||||
})
|
||||
paymentAccountId: number;
|
||||
|
||||
@IsString()
|
||||
@@ -59,8 +52,8 @@ export class CommandBillPaymentDTO {
|
||||
})
|
||||
paymentNumber?: string;
|
||||
|
||||
@IsDateString()
|
||||
@IsNotEmpty()
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
@ApiProperty({
|
||||
description: 'The payment date of the bill payment',
|
||||
example: '2021-01-01',
|
||||
@@ -83,6 +76,7 @@ export class CommandBillPaymentDTO {
|
||||
})
|
||||
statement?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@@ -98,10 +92,12 @@ export class CommandBillPaymentDTO {
|
||||
})
|
||||
entries: BillPaymentEntryDto[];
|
||||
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@ApiProperty({ description: 'The id of the branch', example: 1 })
|
||||
@ApiProperty({
|
||||
description: 'The id of the branch',
|
||||
example: 1,
|
||||
})
|
||||
branchId?: number;
|
||||
|
||||
@IsArray()
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsIn, IsJSON, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class GetBillPaymentsFilterDto {
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Type(() => Number)
|
||||
readonly customViewId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsJSON()
|
||||
readonly stringifiedFilterRoles?: string;
|
||||
|
||||
@IsOptional()
|
||||
readonly columnSortBy?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(['desc', 'asc'])
|
||||
readonly sortOrder?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Type(() => Number)
|
||||
readonly page?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Type(() => Number)
|
||||
readonly pageSize?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
readonly searchKeyword?: string;
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import { ExportableModel } from '@/modules/Export/decorators/ExportableModel.dec
|
||||
import { InjectModelMeta } from '@/modules/Tenancy/TenancyModels/decorators/InjectModelMeta.decorator';
|
||||
import { BillPaymentMeta } from './BillPayment.meta';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
import { Model } from 'objection';
|
||||
|
||||
@ImportableModel()
|
||||
@ExportableModel()
|
||||
@@ -61,98 +60,103 @@ export class BillPayment extends TenantBaseModel {
|
||||
get localAmount() {
|
||||
return this.amount * this.exchangeRate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Model settings.
|
||||
*/
|
||||
// static get meta() {
|
||||
// return BillPaymentSettings;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Relationship mapping.
|
||||
*/
|
||||
static get relationMappings() {
|
||||
const { BillPaymentEntry } = require('./BillPaymentEntry');
|
||||
const {
|
||||
AccountTransaction,
|
||||
} = require('../../Accounts/models/AccountTransaction.model');
|
||||
const { Vendor } = require('../../Vendors/models/Vendor');
|
||||
const { Account } = require('../../Accounts/models/Account.model');
|
||||
const { Branch } = require('../../Branches/models/Branch.model');
|
||||
const { Document } = require('../../ChromiumlyTenancy/models/Document');
|
||||
// static get relationMappings() {
|
||||
// const BillPaymentEntry = require('models/BillPaymentEntry');
|
||||
// const AccountTransaction = require('models/AccountTransaction');
|
||||
// const Vendor = require('models/Vendor');
|
||||
// const Account = require('models/Account');
|
||||
// const Branch = require('models/Branch');
|
||||
// const Document = require('models/Document');
|
||||
|
||||
return {
|
||||
entries: {
|
||||
relation: Model.HasManyRelation,
|
||||
modelClass: BillPaymentEntry,
|
||||
join: {
|
||||
from: 'bills_payments.id',
|
||||
to: 'bills_payments_entries.billPaymentId',
|
||||
},
|
||||
filter: (query) => {
|
||||
query.orderBy('index', 'ASC');
|
||||
},
|
||||
},
|
||||
// return {
|
||||
// entries: {
|
||||
// relation: Model.HasManyRelation,
|
||||
// modelClass: BillPaymentEntry.default,
|
||||
// join: {
|
||||
// from: 'bills_payments.id',
|
||||
// to: 'bills_payments_entries.billPaymentId',
|
||||
// },
|
||||
// filter: (query) => {
|
||||
// query.orderBy('index', 'ASC');
|
||||
// },
|
||||
// },
|
||||
|
||||
vendor: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: Vendor,
|
||||
join: {
|
||||
from: 'bills_payments.vendorId',
|
||||
to: 'contacts.id',
|
||||
},
|
||||
filter(query) {
|
||||
query.where('contact_service', 'vendor');
|
||||
},
|
||||
},
|
||||
// vendor: {
|
||||
// relation: Model.BelongsToOneRelation,
|
||||
// modelClass: Vendor.default,
|
||||
// join: {
|
||||
// from: 'bills_payments.vendorId',
|
||||
// to: 'contacts.id',
|
||||
// },
|
||||
// filter(query) {
|
||||
// query.where('contact_service', 'vendor');
|
||||
// },
|
||||
// },
|
||||
|
||||
paymentAccount: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: Account,
|
||||
join: {
|
||||
from: 'bills_payments.paymentAccountId',
|
||||
to: 'accounts.id',
|
||||
},
|
||||
},
|
||||
// paymentAccount: {
|
||||
// relation: Model.BelongsToOneRelation,
|
||||
// modelClass: Account.default,
|
||||
// join: {
|
||||
// from: 'bills_payments.paymentAccountId',
|
||||
// to: 'accounts.id',
|
||||
// },
|
||||
// },
|
||||
|
||||
transactions: {
|
||||
relation: Model.HasManyRelation,
|
||||
modelClass: AccountTransaction,
|
||||
join: {
|
||||
from: 'bills_payments.id',
|
||||
to: 'accounts_transactions.referenceId',
|
||||
},
|
||||
filter(builder) {
|
||||
builder.where('reference_type', 'BillPayment');
|
||||
},
|
||||
},
|
||||
// transactions: {
|
||||
// relation: Model.HasManyRelation,
|
||||
// modelClass: AccountTransaction.default,
|
||||
// join: {
|
||||
// from: 'bills_payments.id',
|
||||
// to: 'accounts_transactions.referenceId',
|
||||
// },
|
||||
// filter(builder) {
|
||||
// builder.where('reference_type', 'BillPayment');
|
||||
// },
|
||||
// },
|
||||
|
||||
/**
|
||||
* Bill payment may belongs to branch.
|
||||
*/
|
||||
branch: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: Branch,
|
||||
join: {
|
||||
from: 'bills_payments.branchId',
|
||||
to: 'branches.id',
|
||||
},
|
||||
},
|
||||
// /**
|
||||
// * Bill payment may belongs to branch.
|
||||
// */
|
||||
// branch: {
|
||||
// relation: Model.BelongsToOneRelation,
|
||||
// modelClass: Branch.default,
|
||||
// join: {
|
||||
// from: 'bills_payments.branchId',
|
||||
// to: 'branches.id',
|
||||
// },
|
||||
// },
|
||||
|
||||
/**
|
||||
* Bill payment may has many attached attachments.
|
||||
*/
|
||||
attachments: {
|
||||
relation: Model.ManyToManyRelation,
|
||||
modelClass: Document,
|
||||
join: {
|
||||
from: 'bills_payments.id',
|
||||
through: {
|
||||
from: 'document_links.modelId',
|
||||
to: 'document_links.documentId',
|
||||
},
|
||||
to: 'documents.id',
|
||||
},
|
||||
filter(query) {
|
||||
query.where('model_ref', 'BillPayment');
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
// /**
|
||||
// * Bill payment may has many attached attachments.
|
||||
// */
|
||||
// attachments: {
|
||||
// relation: Model.ManyToManyRelation,
|
||||
// modelClass: Document.default,
|
||||
// join: {
|
||||
// from: 'bills_payments.id',
|
||||
// through: {
|
||||
// from: 'document_links.modelId',
|
||||
// to: 'document_links.documentId',
|
||||
// },
|
||||
// to: 'documents.id',
|
||||
// },
|
||||
// filter(query) {
|
||||
// query.where('model_ref', 'BillPayment');
|
||||
// },
|
||||
// },
|
||||
// };
|
||||
// }
|
||||
|
||||
/**
|
||||
* Retrieve the default custom views, roles and columns.
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { BillPaymentsApplication } from '../BillPaymentsApplication.service';
|
||||
import { Exportable } from '@/modules/Export/Exportable';
|
||||
import { EXPORT_SIZE_LIMIT } from '@/modules/Export/constants';
|
||||
import { ExportableService } from '@/modules/Export/decorators/ExportableModel.decorator';
|
||||
import { BillPayment } from '../models/BillPayment';
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { BillPaymentsApplication } from "../BillPaymentsApplication.service";
|
||||
import { Exportable } from "@/modules/Export/Exportable";
|
||||
import { EXPORT_SIZE_LIMIT } from "@/modules/Export/constants";
|
||||
import { ExportableService } from "@/modules/Export/decorators/ExportableModel.decorator";
|
||||
import { BillPayment } from "../models/BillPayment";
|
||||
|
||||
@Injectable()
|
||||
@ExportableService({ name: BillPayment.name })
|
||||
export class BillPaymentsExportable extends Exportable {
|
||||
constructor(
|
||||
private readonly billPaymentsApplication: BillPaymentsApplication,
|
||||
private readonly billPaymentsApplication: BillPaymentsApplication
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -30,11 +30,13 @@ export class BillPaymentsExportable extends Exportable {
|
||||
...query,
|
||||
page: 1,
|
||||
pageSize: EXPORT_SIZE_LIMIT,
|
||||
filterQuery,
|
||||
filterQuery
|
||||
} as any;
|
||||
|
||||
return this.billPaymentsApplication
|
||||
.getBillPayments(parsedQuery)
|
||||
.then((output) => output.billPayments);
|
||||
return [];
|
||||
|
||||
// return this.billPaymentsApplication
|
||||
// .billPayments(tenantId, parsedQuery)
|
||||
// .then((output) => output.billPayments);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import * as R from 'ramda';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { BillPayment } from '../models/BillPayment';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
|
||||
import { BillPaymentTransformer } from './BillPaymentTransformer';
|
||||
import { GetBillPaymentsFilterDto } from '../dtos/GetBillPaymentsFilter.dto';
|
||||
|
||||
@Injectable()
|
||||
export class GetBillPaymentsService {
|
||||
constructor(
|
||||
private readonly dynamicListService: DynamicListService,
|
||||
private readonly transformer: TransformerInjectable,
|
||||
|
||||
@Inject(BillPayment.name)
|
||||
private readonly billPaymentModel: TenantModelProxy<typeof BillPayment>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieve bill payment paginted and filterable list.
|
||||
* @param {GetBillPaymentsFilterDto} billPaymentsFilter
|
||||
*/
|
||||
public async getBillPayments(filterDTO: GetBillPaymentsFilterDto) {
|
||||
const _filterDto = {
|
||||
page: 1,
|
||||
pageSize: 12,
|
||||
filterRoles: [],
|
||||
sortOrder: 'desc',
|
||||
columnSortBy: 'created_at',
|
||||
...filterDTO,
|
||||
};
|
||||
// Parses filter DTO.
|
||||
const filter = this.parseListFilterDTO(_filterDto);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
BillPayment,
|
||||
filter,
|
||||
);
|
||||
const { results, pagination } = await this.billPaymentModel()
|
||||
.query()
|
||||
.onBuild((builder) => {
|
||||
builder.withGraphFetched('vendor');
|
||||
builder.withGraphFetched('paymentAccount');
|
||||
|
||||
dynamicList.buildQuery()(builder);
|
||||
filter?.filterQuery && filter?.filterQuery(builder);
|
||||
})
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
// Transformes the bill payments models to POJO.
|
||||
const billPayments = await this.transformer.transform(
|
||||
results,
|
||||
new BillPaymentTransformer(),
|
||||
);
|
||||
return {
|
||||
billPayments,
|
||||
pagination,
|
||||
filterMeta: dynamicList.getResponseMeta(),
|
||||
};
|
||||
}
|
||||
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export class BillPaymentGLEntriesSubscriber {
|
||||
* Handle bill payment writing journal entries once created.
|
||||
*/
|
||||
@OnEvent(events.billPayment.onCreated)
|
||||
async handleWriteJournalEntries({
|
||||
private async handleWriteJournalEntries({
|
||||
billPayment,
|
||||
trx,
|
||||
}: IBillPaymentEventCreatedPayload) {
|
||||
@@ -34,7 +34,7 @@ export class BillPaymentGLEntriesSubscriber {
|
||||
* Handle bill payment re-writing journal entries once the payment transaction be edited.
|
||||
*/
|
||||
@OnEvent(events.billPayment.onEdited)
|
||||
async handleRewriteJournalEntriesOncePaymentEdited({
|
||||
private async handleRewriteJournalEntriesOncePaymentEdited({
|
||||
billPayment,
|
||||
trx,
|
||||
}: IBillPaymentEventEditedPayload) {
|
||||
@@ -48,7 +48,7 @@ export class BillPaymentGLEntriesSubscriber {
|
||||
* Reverts journal entries once bill payment deleted.
|
||||
*/
|
||||
@OnEvent(events.billPayment.onDeleted)
|
||||
async handleRevertJournalEntries({
|
||||
private async handleRevertJournalEntries({
|
||||
billPaymentId,
|
||||
trx,
|
||||
}: IBillPaymentEventDeletedPayload) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ToNumber } from '@/common/decorators/Validators';
|
||||
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
@@ -6,14 +5,14 @@ import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsDate,
|
||||
IsDateString,
|
||||
IsEnum,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsPositive,
|
||||
IsString,
|
||||
Min,
|
||||
MinLength,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
|
||||
@@ -30,12 +29,10 @@ export class BillEntryDto extends ItemEntryDto {
|
||||
|
||||
class AttachmentDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
key: string;
|
||||
}
|
||||
|
||||
export class CommandBillDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
billNumber: string;
|
||||
|
||||
@@ -43,36 +40,32 @@ export class CommandBillDto {
|
||||
@IsString()
|
||||
referenceNo?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsDateString()
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
billDate: Date;
|
||||
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
dueDate?: Date;
|
||||
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
vendorId: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsPositive()
|
||||
exchangeRate?: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
warehouseId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
branchId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
projectId?: number;
|
||||
|
||||
@@ -81,11 +74,9 @@ export class CommandBillDto {
|
||||
note?: string;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
open: boolean = false;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
isInclusiveTax: boolean = false;
|
||||
|
||||
@IsArray()
|
||||
@@ -101,17 +92,13 @@ export class CommandBillDto {
|
||||
attachments?: AttachmentDto[];
|
||||
|
||||
@IsEnum(DiscountType)
|
||||
@IsOptional()
|
||||
discountType: DiscountType = DiscountType.Amount;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsPositive()
|
||||
discount?: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
adjustment?: number;
|
||||
}
|
||||
|
||||
@@ -76,13 +76,8 @@ export class BranchesController {
|
||||
status: 200,
|
||||
description: 'The branches feature has been successfully activated.',
|
||||
})
|
||||
async activateBranches() {
|
||||
await this.branchesApplication.activateBranches();
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: 'The branches activated successfully.',
|
||||
};
|
||||
activateBranches() {
|
||||
return this.branchesApplication.activateBranches();
|
||||
}
|
||||
|
||||
@Put(':id/mark-as-primary')
|
||||
|
||||
@@ -17,8 +17,6 @@ export class BranchesSettingsService {
|
||||
const settingsStore = await this.settingsStore();
|
||||
|
||||
settingsStore.set({ group: 'features', key: Features.BRANCHES, value: 1 });
|
||||
|
||||
await settingsStore.save();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { IsOptional } from '@/common/decorators/Validators';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsEmail,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUrl,
|
||||
} from 'class-validator';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import * as path from 'path';
|
||||
import path from 'path';
|
||||
import { promises as fs } from 'fs';
|
||||
import { PageProperties, PdfFormat } from '@/libs/Chromiumly/_types';
|
||||
import { UrlConverter } from '@/libs/Chromiumly/UrlConvert';
|
||||
@@ -40,7 +40,7 @@ export class ChromiumlyHtmlConvert {
|
||||
|
||||
const cleanup = async () => {
|
||||
await fs.unlink(filePath);
|
||||
await this.documentModel().query().where('key', filename).delete();
|
||||
await Document.query().where('key', filename).delete();
|
||||
};
|
||||
return [filename, cleanup];
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as path from 'path';
|
||||
import path from 'path';
|
||||
|
||||
export const PDF_FILE_SUB_DIR = '/pdf';
|
||||
export const PDF_FILE_EXPIRE_IN = 40; // ms
|
||||
@@ -9,5 +9,6 @@ export const getPdfFilesStorageDir = (filename: string) => {
|
||||
|
||||
export const getPdfFilePath = (filename: string) => {
|
||||
const storageDir = getPdfFilesStorageDir(filename);
|
||||
return path.join(global.__static_dirname, storageDir);
|
||||
|
||||
return path.join(global.__storage_dir, storageDir);
|
||||
};
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
|
||||
export const ERRORS = {
|
||||
CONTACT_ALREADY_ACTIVE: 'CONTACT_ALREADY_ACTIVE',
|
||||
CONTACT_ALREADY_INACTIVE: 'CONTACT_ALREADY_INACTIVE'
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Query,
|
||||
Param,
|
||||
Post,
|
||||
ParseIntPipe,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiParam } from '@nestjs/swagger';
|
||||
import { GetContactsAutoCompleteQuery } from './dtos/GetContactsAutoCompleteQuery.dto';
|
||||
import { GetAutoCompleteContactsService } from './queries/GetAutoCompleteContacts.service';
|
||||
import { ActivateContactService } from './commands/ActivateContact.service';
|
||||
import { InactivateContactService } from './commands/InactivateContact.service';
|
||||
|
||||
@Controller('contacts')
|
||||
@ApiTags('contacts')
|
||||
export class ContactsController {
|
||||
constructor(
|
||||
private readonly getAutoCompleteService: GetAutoCompleteContactsService,
|
||||
private readonly activateContactService: ActivateContactService,
|
||||
private readonly inactivateContactService: InactivateContactService,
|
||||
) {}
|
||||
|
||||
@Get('auto-complete')
|
||||
@ApiOperation({ summary: 'Get the auto-complete contacts' })
|
||||
getAutoComplete(@Query() query: GetContactsAutoCompleteQuery) {
|
||||
return this.getAutoCompleteService.autocompleteContacts(query);
|
||||
}
|
||||
|
||||
@Post(':id/activate')
|
||||
@ApiOperation({ summary: 'Activate a contact' })
|
||||
@ApiParam({ name: 'id', type: 'number', description: 'Contact ID' })
|
||||
async activateContact(@Param('id', ParseIntPipe) contactId: number) {
|
||||
await this.activateContactService.activateContact(contactId);
|
||||
return { id: contactId, activated: true };
|
||||
}
|
||||
|
||||
@Post(':id/inactivate')
|
||||
@ApiOperation({ summary: 'Inactivate a contact' })
|
||||
@ApiParam({ name: 'id', type: 'number', description: 'Contact ID' })
|
||||
async inactivateContact(@Param('id', ParseIntPipe) contactId: number) {
|
||||
await this.inactivateContactService.inactivateContact(contactId);
|
||||
return { id: contactId, inactivated: true };
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { GetAutoCompleteContactsService } from './queries/GetAutoCompleteContacts.service';
|
||||
import { ContactsController } from './Contacts.controller';
|
||||
import { ActivateContactService } from './commands/ActivateContact.service';
|
||||
import { InactivateContactService } from './commands/InactivateContact.service';
|
||||
|
||||
@Module({
|
||||
providers: [
|
||||
GetAutoCompleteContactsService,
|
||||
ActivateContactService,
|
||||
InactivateContactService,
|
||||
],
|
||||
controllers: [ContactsController],
|
||||
})
|
||||
export class ContactsModule {}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { IFilterRole } from '../DynamicListing/DynamicFilter/DynamicFilter.types';
|
||||
|
||||
export interface IContactsAutoCompleteFilter {
|
||||
limit: number;
|
||||
keyword: string;
|
||||
filterRoles?: IFilterRole[];
|
||||
columnSortBy: string;
|
||||
sortOrder: string;
|
||||
}
|
||||
|
||||
export interface IContactAutoCompleteItem {
|
||||
displayName: string;
|
||||
contactService: string;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||
import { Contact } from '../models/Contact';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { ERRORS } from '../Contacts.constants';
|
||||
|
||||
@Injectable()
|
||||
export class ActivateContactService {
|
||||
constructor(
|
||||
@Inject(Contact.name)
|
||||
private readonly contactModel: TenantModelProxy<typeof Contact>,
|
||||
) {}
|
||||
|
||||
async activateContact(contactId: number) {
|
||||
const contact = await this.contactModel()
|
||||
.query()
|
||||
.findById(contactId)
|
||||
.throwIfNotFound();
|
||||
|
||||
if (contact.active) {
|
||||
throw new ServiceError(ERRORS.CONTACT_ALREADY_ACTIVE);
|
||||
}
|
||||
await this.contactModel()
|
||||
.query()
|
||||
.findById(contactId)
|
||||
.update({ active: true });
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||
import { Contact } from '../models/Contact';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { ERRORS } from '../Contacts.constants';
|
||||
|
||||
@Injectable()
|
||||
export class InactivateContactService {
|
||||
constructor(
|
||||
@Inject(Contact.name)
|
||||
private readonly contactModel: TenantModelProxy<typeof Contact>,
|
||||
) {}
|
||||
|
||||
async inactivateContact(contactId: number) {
|
||||
const contact = await this.contactModel()
|
||||
.query()
|
||||
.findById(contactId)
|
||||
.throwIfNotFound();
|
||||
|
||||
if (!contact.active) {
|
||||
throw new ServiceError(ERRORS.CONTACT_ALREADY_INACTIVE);
|
||||
}
|
||||
await this.contactModel()
|
||||
.query()
|
||||
.findById(contactId)
|
||||
.update({ active: false });
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class GetContactsAutoCompleteQuery {
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
limit: number;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
keyword: string;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { Contact } from '../models/Contact';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { IContactsAutoCompleteFilter } from '../Contacts.types';
|
||||
import { GetContactsAutoCompleteQuery } from '../dtos/GetContactsAutoCompleteQuery.dto';
|
||||
|
||||
@Injectable()
|
||||
export class GetAutoCompleteContactsService {
|
||||
constructor(
|
||||
@Inject(Contact.name)
|
||||
private readonly contactModel: TenantModelProxy<typeof Contact>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieve auto-complete contacts list.
|
||||
* @param {number} tenantId -
|
||||
* @param {IContactsAutoCompleteFilter} contactsFilter -
|
||||
* @return {IContactAutoCompleteItem}
|
||||
*/
|
||||
async autocompleteContacts(queryDto: GetContactsAutoCompleteQuery) {
|
||||
const _queryDto = {
|
||||
filterRoles: [],
|
||||
sortOrder: 'asc',
|
||||
columnSortBy: 'display_name',
|
||||
limit: 10,
|
||||
...queryDto,
|
||||
};
|
||||
// Retrieve contacts list by the given query.
|
||||
const contacts = await this.contactModel()
|
||||
.query()
|
||||
.onBuild((builder) => {
|
||||
if (_queryDto.keyword) {
|
||||
builder.where('display_name', 'LIKE', `%${_queryDto.keyword}%`);
|
||||
}
|
||||
builder.limit(_queryDto.limit);
|
||||
});
|
||||
return contacts;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Param, Post } from '@nestjs/common';
|
||||
import { ICreditNoteRefundDTO } from '../CreditNotes/types/CreditNotes.types';
|
||||
import { CreditNotesRefundsApplication } from './CreditNotesRefundsApplication.service';
|
||||
import { RefundCreditNote } from './models/RefundCreditNote';
|
||||
@@ -12,14 +12,6 @@ export class CreditNoteRefundsController {
|
||||
private readonly creditNotesRefundsApplication: CreditNotesRefundsApplication,
|
||||
) {}
|
||||
|
||||
@Get(':creditNoteId/refunds')
|
||||
@ApiOperation({ summary: 'Retrieve the credit note graph.' })
|
||||
getCreditNoteRefunds(@Param('creditNoteId') creditNoteId: number) {
|
||||
return this.creditNotesRefundsApplication.getCreditNoteRefunds(
|
||||
creditNoteId,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a refund credit note.
|
||||
* @param {number} creditNoteId - The credit note ID.
|
||||
|
||||
@@ -6,7 +6,6 @@ import { RefundSyncCreditNoteBalanceService } from './commands/RefundSyncCreditN
|
||||
import { CreditNotesRefundsApplication } from './CreditNotesRefundsApplication.service';
|
||||
import { CreditNoteRefundsController } from './CreditNoteRefunds.controller';
|
||||
import { CreditNotesModule } from '../CreditNotes/CreditNotes.module';
|
||||
import { GetCreditNoteRefundsService } from './queries/GetCreditNoteRefunds.service';
|
||||
|
||||
@Module({
|
||||
imports: [forwardRef(() => CreditNotesModule)],
|
||||
@@ -16,9 +15,10 @@ import { GetCreditNoteRefundsService } from './queries/GetCreditNoteRefunds.serv
|
||||
RefundCreditNoteService,
|
||||
RefundSyncCreditNoteBalanceService,
|
||||
CreditNotesRefundsApplication,
|
||||
GetCreditNoteRefundsService,
|
||||
],
|
||||
exports: [RefundSyncCreditNoteBalanceService],
|
||||
exports: [
|
||||
RefundSyncCreditNoteBalanceService
|
||||
],
|
||||
controllers: [CreditNoteRefundsController],
|
||||
})
|
||||
export class CreditNoteRefundsModule {}
|
||||
|
||||
@@ -5,27 +5,16 @@ import { DeleteRefundCreditNoteService } from './commands/DeleteRefundCreditNote
|
||||
import { RefundCreditNoteService } from './commands/RefundCreditNote.service';
|
||||
import { RefundSyncCreditNoteBalanceService } from './commands/RefundSyncCreditNoteBalance';
|
||||
import { CreditNoteRefundDto } from './dto/CreditNoteRefund.dto';
|
||||
import { GetCreditNoteRefundsService } from './queries/GetCreditNoteRefunds.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreditNotesRefundsApplication {
|
||||
constructor(
|
||||
private readonly createRefundCreditNoteService: CreateRefundCreditNoteService,
|
||||
private readonly deleteRefundCreditNoteService: DeleteRefundCreditNoteService,
|
||||
private readonly getCreditNoteRefundsService: GetCreditNoteRefundsService,
|
||||
private readonly refundCreditNoteService: RefundCreditNoteService,
|
||||
private readonly refundSyncCreditNoteBalanceService: RefundSyncCreditNoteBalanceService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieve the credit note graph.
|
||||
* @param {number} creditNoteId - Credit note id.
|
||||
* @returns {Promise<IRefundCreditNotePOJO[]>}
|
||||
*/
|
||||
public getCreditNoteRefunds(creditNoteId: number) {
|
||||
return this.getCreditNoteRefundsService.getCreditNoteRefunds(creditNoteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a refund credit note.
|
||||
* @param {number} creditNoteId - The credit note ID.
|
||||
|
||||
@@ -6,7 +6,7 @@ import { IRefundCreditNotePOJO } from '../types/CreditNoteRefunds.types';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class GetCreditNoteRefundsService {
|
||||
export class ListCreditNoteRefunds {
|
||||
constructor(
|
||||
private readonly transformer: TransformerInjectable,
|
||||
|
||||
@@ -18,7 +18,7 @@ export class GetCreditNoteRefundsService {
|
||||
|
||||
/**
|
||||
* Retrieve the credit note graph.
|
||||
* @param {number} creditNoteId - Credit note id.
|
||||
* @param {number} creditNoteId
|
||||
* @returns {Promise<IRefundCreditNotePOJO[]>}
|
||||
*/
|
||||
public async getCreditNoteRefunds(
|
||||
|
||||
@@ -7,8 +7,6 @@ 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 {
|
||||
@@ -19,8 +17,6 @@ export class CreditNoteApplication {
|
||||
private readonly deleteCreditNoteService: DeleteCreditNoteService,
|
||||
private readonly getCreditNotePdfService: GetCreditNotePdf,
|
||||
private readonly getCreditNotesService: GetCreditNotesService,
|
||||
private readonly getCreditNoteStateService: GetCreditNoteState,
|
||||
private readonly getCreditNoteService: GetCreditNoteService
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -80,21 +76,4 @@ export class CreditNoteApplication {
|
||||
getCreditNotes(creditNotesQuery: ICreditNotesQueryDTO) {
|
||||
return this.getCreditNotesService.getCreditNotesList(creditNotesQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the create/edit initial state of the credit note.
|
||||
* @returns {Promise<ICreditNoteState>}
|
||||
*/
|
||||
getCreditNoteState() {
|
||||
return this.getCreditNoteStateService.getCreditNoteState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the credit note.
|
||||
* @param {number} creditNoteId
|
||||
* @returns {Promise<CreditNote>}
|
||||
*/
|
||||
getCreditNote(creditNoteId: number) {
|
||||
return this.getCreditNoteService.getCreditNote(creditNoteId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { CreditNoteApplication } from './CreditNoteApplication.service';
|
||||
import { ICreditNotesQueryDTO } from './types/CreditNotes.types';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { CreateCreditNoteDto, EditCreditNoteDto } from './dtos/CreditNote.dto';
|
||||
|
||||
@Controller('credit-notes')
|
||||
@@ -22,42 +22,16 @@ 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,
|
||||
@@ -69,19 +43,11 @@ 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);
|
||||
}
|
||||
|
||||
@@ -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 { GetCreditNoteService } from './queries/GetCreditNote.service';
|
||||
import { GetCreditNote } 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,
|
||||
GetCreditNoteService,
|
||||
GetCreditNote,
|
||||
CommandCreditNoteDTOTransform,
|
||||
EditCreditNoteService,
|
||||
OpenCreditNoteService,
|
||||
@@ -74,7 +74,7 @@ import { CreditNotesApplyInvoiceModule } from '../CreditNotesApplyInvoice/Credit
|
||||
],
|
||||
exports: [
|
||||
CreateCreditNoteService,
|
||||
GetCreditNoteService,
|
||||
GetCreditNote,
|
||||
CommandCreditNoteDTOTransform,
|
||||
EditCreditNoteService,
|
||||
OpenCreditNoteService,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ToNumber } from '@/common/decorators/Validators';
|
||||
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
@@ -6,10 +5,9 @@ import {
|
||||
ArrayMinSize,
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsDateString,
|
||||
IsDate,
|
||||
IsEnum,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsPositive,
|
||||
@@ -27,25 +25,21 @@ 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;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsDateString()
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
@ApiProperty({ example: '2021-09-01', description: 'The credit note date' })
|
||||
creditNoteDate: Date;
|
||||
|
||||
@@ -70,19 +64,26 @@ 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()
|
||||
@@ -90,7 +91,14 @@ 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[];
|
||||
@@ -102,15 +110,19 @@ 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()
|
||||
@@ -123,7 +135,6 @@ export class CommandCreditNoteDto {
|
||||
discountType?: DiscountType;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
adjustment?: number;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ServiceError } from '@/modules/Items/ServiceError';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class GetCreditNoteService {
|
||||
export class GetCreditNote {
|
||||
constructor(
|
||||
private readonly transformer: TransformerInjectable,
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { GetCreditNoteService } from './GetCreditNote.service';
|
||||
import { GetCreditNote } 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: GetCreditNoteService,
|
||||
private readonly getCreditNoteService: GetCreditNote,
|
||||
private readonly creditNoteBrandingTemplate: CreditNoteBrandingTemplate,
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
|
||||
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { GetCreditNoteAssociatedAppliedInvoices } from './queries/GetCreditNoteAssociatedAppliedInvoices.service';
|
||||
|
||||
@Controller('credit-notes')
|
||||
@ApiTags('credit-notes-apply-invoice')
|
||||
export class CreditNotesApplyInvoiceController {
|
||||
constructor(
|
||||
private readonly getCreditNoteAssociatedAppliedInvoicesService: GetCreditNoteAssociatedAppliedInvoices,
|
||||
) {}
|
||||
|
||||
@Get(':creditNoteId/applied-invoices')
|
||||
@ApiOperation({ summary: 'Applied credit note to invoices' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Credit note successfully applied to invoices',
|
||||
})
|
||||
@ApiResponse({ status: 404, description: 'Credit note not found' })
|
||||
@ApiResponse({ status: 400, description: 'Invalid input data' })
|
||||
appliedCreditNoteToInvoices(@Param('creditNoteId') creditNoteId: number) {
|
||||
return this.getCreditNoteAssociatedAppliedInvoicesService.getCreditAssociatedAppliedInvoices(
|
||||
creditNoteId,
|
||||
);
|
||||
}
|
||||
|
||||
@Post(':creditNoteId/apply-invoices')
|
||||
@ApiOperation({ summary: 'Apply credit note to invoices' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Credit note successfully applied to invoices',
|
||||
})
|
||||
@ApiResponse({ status: 404, description: 'Credit note not found' })
|
||||
@ApiResponse({ status: 400, description: 'Invalid input data' })
|
||||
applyCreditNoteToInvoices(@Param('creditNoteId') creditNoteId: number) {
|
||||
return this.getCreditNoteAssociatedAppliedInvoicesService.getCreditAssociatedAppliedInvoices(
|
||||
creditNoteId,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import { PaymentsReceivedModule } from '../PaymentReceived/PaymentsReceived.modu
|
||||
import { CreditNotesModule } from '../CreditNotes/CreditNotes.module';
|
||||
import { GetCreditNoteAssociatedAppliedInvoices } from './queries/GetCreditNoteAssociatedAppliedInvoices.service';
|
||||
import { GetCreditNoteAssociatedInvoicesToApply } from './queries/GetCreditNoteAssociatedInvoicesToApply.service';
|
||||
import { CreditNotesApplyInvoiceController } from './CreditNotesApplyInvoice.controller';
|
||||
|
||||
@Module({
|
||||
providers: [
|
||||
@@ -17,11 +16,12 @@ import { CreditNotesApplyInvoiceController } from './CreditNotesApplyInvoice.con
|
||||
CreditNoteApplyToInvoices,
|
||||
CreditNoteApplySyncInvoicesCreditedAmount,
|
||||
CreditNoteApplySyncCredit,
|
||||
GetCreditNoteAssociatedAppliedInvoices,
|
||||
GetCreditNoteAssociatedInvoicesToApply,
|
||||
// GetCreditNoteAssociatedAppliedInvoices,
|
||||
// GetCreditNoteAssociatedInvoicesToApply
|
||||
],
|
||||
exports: [
|
||||
DeleteCustomerLinkedCreditNoteService,
|
||||
],
|
||||
exports: [DeleteCustomerLinkedCreditNoteService],
|
||||
imports: [PaymentsReceivedModule, forwardRef(() => CreditNotesModule)],
|
||||
controllers: [CreditNotesApplyInvoiceController],
|
||||
})
|
||||
export class CreditNotesApplyInvoiceModule {}
|
||||
|
||||
@@ -7,16 +7,9 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class GetCreditNoteAssociatedAppliedInvoices {
|
||||
/**
|
||||
* @param {TransformerInjectable} transformer - The transformer service.
|
||||
* @param {TenantModelProxy<typeof CreditNoteAppliedInvoice>} creditNoteAppliedInvoiceModel - The credit note applied invoice model.
|
||||
* @param {TenantModelProxy<typeof CreditNote>} creditNoteModel - The credit note model.
|
||||
*/
|
||||
constructor(
|
||||
private readonly transformer: TransformerInjectable,
|
||||
|
||||
@Inject(CreditNoteAppliedInvoice.name)
|
||||
private readonly creditNoteAppliedInvoiceModel: TenantModelProxy<typeof CreditNoteAppliedInvoice>,
|
||||
private readonly creditNoteAppliedInvoiceModel: typeof CreditNoteAppliedInvoice,
|
||||
|
||||
@Inject(CreditNote.name)
|
||||
private readonly creditNoteModel: TenantModelProxy<typeof CreditNote>,
|
||||
@@ -36,7 +29,7 @@ export class GetCreditNoteAssociatedAppliedInvoices {
|
||||
.findById(creditNoteId)
|
||||
.throwIfNotFound();
|
||||
|
||||
const appliedToInvoices = await this.creditNoteAppliedInvoiceModel()
|
||||
const appliedToInvoices = await this.creditNoteAppliedInvoiceModel
|
||||
.query()
|
||||
.where('credit_note_id', creditNoteId)
|
||||
.withGraphFetched('saleInvoice')
|
||||
|
||||
@@ -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 { GetCreditNoteService } from '../../CreditNotes/queries/GetCreditNote.service';
|
||||
import { GetCreditNote } from '../../CreditNotes/queries/GetCreditNote.service';
|
||||
import { CreditNoteWithInvoicesToApplyTransformer } from './CreditNoteWithInvoicesToApplyTransformer';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@@ -10,11 +10,11 @@ export class GetCreditNoteAssociatedInvoicesToApply {
|
||||
/**
|
||||
* @param {TransformerInjectable} transformer - Transformer service.
|
||||
* @param {GetCreditNote} getCreditNote - Get credit note service.
|
||||
* @param {TenantModelProxy<typeof SaleInvoice>} saleInvoiceModel - Sale invoice model.
|
||||
* @param {typeof SaleInvoice} saleInvoiceModel - Sale invoice model.
|
||||
*/
|
||||
constructor(
|
||||
private transformer: TransformerInjectable,
|
||||
private getCreditNote: GetCreditNoteService,
|
||||
private getCreditNote: GetCreditNote,
|
||||
|
||||
@Inject(SaleInvoice.name)
|
||||
private saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
||||
|
||||
@@ -37,15 +37,15 @@ export class CurrenciesController {
|
||||
return this.currenciesApp.createCurrency(dto);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
@Put(':code')
|
||||
@ApiOperation({ summary: 'Edit an existing currency' })
|
||||
@ApiParam({ name: 'id', type: Number, description: 'Currency ID' })
|
||||
@ApiBody({ type: EditCurrencyDto })
|
||||
@ApiOkResponse({ description: 'The currency has been successfully updated.' })
|
||||
@ApiNotFoundResponse({ description: 'Currency not found.' })
|
||||
@ApiBadRequestResponse({ description: 'Invalid input data.' })
|
||||
edit(@Param('id') id: number, @Body() dto: EditCurrencyDto) {
|
||||
return this.currenciesApp.editCurrency(id, dto);
|
||||
edit(@Param('code') code: string, @Body() dto: EditCurrencyDto) {
|
||||
return this.currenciesApp.editCurrency(code, dto);
|
||||
}
|
||||
|
||||
@Delete(':code')
|
||||
|
||||
@@ -27,8 +27,8 @@ export class CurrenciesApplication {
|
||||
/**
|
||||
* Edits an existing currency.
|
||||
*/
|
||||
public editCurrency(currencyId: number, currencyDTO: EditCurrencyDto) {
|
||||
return this.editCurrencyService.editCurrency(currencyId, currencyDTO);
|
||||
public editCurrency(currencyCode: string, currencyDTO: EditCurrencyDto) {
|
||||
return this.editCurrencyService.editCurrency(currencyCode, currencyDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,22 +12,21 @@ export class EditCurrencyService {
|
||||
|
||||
/**
|
||||
* Edit details of the given currency.
|
||||
* @param {number} currencyId - Currency ID.
|
||||
* @param {number} currencyCode - Currency code.
|
||||
* @param {ICurrencyDTO} currencyDTO - Edit currency dto.
|
||||
*/
|
||||
public async editCurrency(
|
||||
currencyId: number,
|
||||
currencyCode: string,
|
||||
currencyDTO: EditCurrencyDto,
|
||||
): Promise<Currency> {
|
||||
const foundCurrency = this.currencyModel()
|
||||
const foundCurrency = await this.currencyModel()
|
||||
.query()
|
||||
.findById(currencyId)
|
||||
.findOne('currencyCode', currencyCode)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Directly use the provided ID to update the currency
|
||||
const currency = await this.currencyModel()
|
||||
.query()
|
||||
.patchAndFetchById(currencyId, {
|
||||
.patchAndFetchById(foundCurrency.id, {
|
||||
...currencyDTO,
|
||||
});
|
||||
return currency;
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import { IsNotEmpty } from "class-validator";
|
||||
import { IsString } from "class-validator";
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class CreateCurrencyDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
currencyName: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
currencyCode: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
currencySign: string;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { IsNotEmpty } from "class-validator";
|
||||
import { IsString } from "class-validator";
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class EditCurrencyDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
currencyName: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
currencySign: string;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
import { TenantModel } from "@/modules/System/models/TenantModel";
|
||||
|
||||
export class Currency extends TenantBaseModel {
|
||||
export class Currency extends TenantModel {
|
||||
public readonly currencySign: string;
|
||||
public readonly currencyName: string;
|
||||
public readonly currencyCode: string;
|
||||
@@ -22,4 +22,4 @@ export class Currency extends TenantBaseModel {
|
||||
static get resourceable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -394,5 +394,7 @@ export abstract class DynamicFilterRoleAbstractor implements IDynamicFilter {
|
||||
/**
|
||||
* Retrieves the response meta.
|
||||
*/
|
||||
getResponseMeta() {}
|
||||
getResponseMeta() {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { ToNumber } from '@/common/decorators/Validators';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsDateString,
|
||||
IsDate,
|
||||
IsInt,
|
||||
IsISO4217CurrencyCode,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
@@ -25,12 +23,10 @@ export class ExpenseCategoryDto {
|
||||
@IsNotEmpty()
|
||||
index: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
expenseAccountId: number;
|
||||
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
amount?: number;
|
||||
@@ -44,7 +40,6 @@ export class ExpenseCategoryDto {
|
||||
@IsOptional()
|
||||
landedCost?: boolean;
|
||||
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
projectId?: number;
|
||||
@@ -60,7 +55,7 @@ export class CommandExpenseDto {
|
||||
})
|
||||
referenceNo?: string;
|
||||
|
||||
@IsDateString()
|
||||
@IsDate()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({
|
||||
description: 'The payment date of the expense',
|
||||
@@ -68,9 +63,8 @@ export class CommandExpenseDto {
|
||||
})
|
||||
paymentDate: Date;
|
||||
|
||||
@IsNotEmpty()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({
|
||||
description: 'The payment account id of the expense',
|
||||
example: 1,
|
||||
@@ -86,22 +80,31 @@ 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({
|
||||
@@ -110,16 +113,14 @@ export class CommandExpenseDto {
|
||||
})
|
||||
publish?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
@ApiProperty({
|
||||
description: 'The payee id of the expense',
|
||||
example: 1,
|
||||
})
|
||||
payeeId?: number;
|
||||
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
@ApiProperty({
|
||||
|
||||
@@ -24,7 +24,7 @@ export class FeaturesConfigure {
|
||||
},
|
||||
{
|
||||
name: Features.BankSyncing,
|
||||
defaultValue: this.configService.get('bankfeed.enabled') ?? false,
|
||||
defaultValue: this.configService.get('bankSync.enabled') ?? false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -6,28 +6,23 @@ import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||
|
||||
@Injectable()
|
||||
export class ImportFileMeta {
|
||||
/**
|
||||
* @param {TransformerInjectable} transformer - Transformer injectable service.
|
||||
* @param {TenancyContext} tenancyContext - Tenancy context service.
|
||||
* @param {typeof ImportModel} importModel - Import model.
|
||||
*/
|
||||
constructor(
|
||||
private readonly transformer: TransformerInjectable,
|
||||
private readonly tenancyContext: TenancyContext,
|
||||
|
||||
@Inject(ImportModel.name)
|
||||
private readonly importModel: typeof ImportModel,
|
||||
private readonly importModel: () => typeof ImportModel,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the import meta of the given import model id.
|
||||
* @param {string} importId - Import id.
|
||||
* @param {number} importId
|
||||
*/
|
||||
async getImportMeta(importId: string) {
|
||||
const tenant = await this.tenancyContext.getTenant();
|
||||
const tenantId = tenant.id;
|
||||
|
||||
const importFile = await this.importModel
|
||||
const importFile = await this.importModel()
|
||||
.query()
|
||||
.where('tenantId', tenantId)
|
||||
.findOne('importId', importId);
|
||||
|
||||
@@ -74,10 +74,11 @@ export class EditItemCategoryService {
|
||||
);
|
||||
// Creates item category under unit-of-work evnirement.
|
||||
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||
const itemCategory = await this.itemCategoryModel()
|
||||
.query(trx)
|
||||
.patchAndFetchById(itemCategoryId, { ...itemCategoryObj });
|
||||
|
||||
//
|
||||
const itemCategory = await ItemCategory.query().patchAndFetchById(
|
||||
itemCategoryId,
|
||||
{ ...itemCategoryObj },
|
||||
);
|
||||
// Triggers `onItemCategoryEdited` event.
|
||||
await this.eventEmitter.emitAsync(events.itemCategory.onEdited, {
|
||||
oldItemCategory,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { IsOptional, ToNumber } from '@/common/decorators/Validators';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNumber } from 'class-validator';
|
||||
import { IsString } from 'class-validator';
|
||||
import { IsOptional, IsString } from 'class-validator';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
class CommandItemCategoryDto {
|
||||
@@ -18,19 +17,16 @@ class CommandItemCategoryDto {
|
||||
})
|
||||
description?: string;
|
||||
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@ApiProperty({ example: 1, description: 'The cost account ID' })
|
||||
costAccountId?: number;
|
||||
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@ApiProperty({ example: 1, description: 'The sell account ID' })
|
||||
sellAccountId?: number;
|
||||
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@ApiProperty({ example: 1, description: 'The inventory account ID' })
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
IsString,
|
||||
IsIn,
|
||||
IsOptional,
|
||||
IsBoolean,
|
||||
IsNumber,
|
||||
IsInt,
|
||||
@@ -8,10 +9,11 @@ import {
|
||||
ValidateIf,
|
||||
MaxLength,
|
||||
Min,
|
||||
Max,
|
||||
IsNotEmpty,
|
||||
} from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsOptional, ToNumber } from '@/common/decorators/Validators';
|
||||
|
||||
export class CommandItemDto {
|
||||
@IsString()
|
||||
@@ -21,7 +23,6 @@ export class CommandItemDto {
|
||||
name: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@IsIn(['service', 'non-inventory', 'inventory'])
|
||||
@ApiProperty({
|
||||
description: 'Item type',
|
||||
@@ -51,7 +52,6 @@ export class CommandItemDto {
|
||||
purchasable?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsNumber({ maxDecimalPlaces: 3 })
|
||||
@Min(0)
|
||||
@ValidateIf((o) => o.purchasable === true)
|
||||
@@ -64,7 +64,6 @@ export class CommandItemDto {
|
||||
costPrice?: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@ValidateIf((o) => o.purchasable === true)
|
||||
@@ -87,7 +86,6 @@ export class CommandItemDto {
|
||||
sellable?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsNumber({ maxDecimalPlaces: 3 })
|
||||
@Min(0)
|
||||
@ValidateIf((o) => o.sellable === true)
|
||||
@@ -100,7 +98,6 @@ export class CommandItemDto {
|
||||
sellPrice?: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@ValidateIf((o) => o.sellable === true)
|
||||
@@ -113,7 +110,6 @@ export class CommandItemDto {
|
||||
sellAccountId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@ValidateIf((o) => o.type === 'inventory')
|
||||
@@ -144,7 +140,6 @@ export class CommandItemDto {
|
||||
purchaseDescription?: string;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@ApiProperty({
|
||||
description: 'ID of the tax rate applied to sales',
|
||||
@@ -154,7 +149,6 @@ export class CommandItemDto {
|
||||
sellTaxRateId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@ApiProperty({
|
||||
description: 'ID of the tax rate applied to purchases',
|
||||
@@ -164,7 +158,6 @@ export class CommandItemDto {
|
||||
purchaseTaxRateId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@ApiProperty({
|
||||
@@ -196,6 +189,7 @@ export class CommandItemDto {
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@Type(() => Number)
|
||||
@IsInt({ each: true })
|
||||
@ApiProperty({
|
||||
description: 'IDs of media files associated with the item',
|
||||
|
||||
@@ -5,12 +5,10 @@ import { ExportableModel } from '@/modules/Export/decorators/ExportableModel.dec
|
||||
import { InjectModelMeta } from '@/modules/Tenancy/TenancyModels/decorators/InjectModelMeta.decorator';
|
||||
import { ItemMeta } from './Item.meta';
|
||||
import { ImportableModel } from '@/modules/Import/decorators/Import.decorator';
|
||||
import { PreventMutateBaseCurrency } from '@/common/decorators/LockMutateBaseCurrency.decorator';
|
||||
|
||||
@ExportableModel()
|
||||
@ImportableModel()
|
||||
@InjectModelMeta(ItemMeta)
|
||||
@PreventMutateBaseCurrency()
|
||||
export class Item extends TenantBaseModel {
|
||||
public readonly quantityOnHand: number;
|
||||
public readonly name: string;
|
||||
|
||||
@@ -108,7 +108,11 @@ 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<IManualJournalsFilter>) {
|
||||
public getManualJournals(
|
||||
@Query() filterDto: Partial<IManualJournalsFilter>
|
||||
) {
|
||||
return this.manualJournalsApplication.getManualJournals(filterDto);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
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,
|
||||
@@ -23,21 +20,18 @@ 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;
|
||||
|
||||
@@ -47,19 +41,16 @@ export class ManualJournalEntryDto {
|
||||
note?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Contact ID' })
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
contactId?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Branch ID' })
|
||||
@ToNumber()
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
branchId?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Project ID' })
|
||||
@ToNumber()
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
projectId?: number;
|
||||
@@ -73,7 +64,8 @@ class AttachmentDto {
|
||||
|
||||
export class CommandManualJournalDto {
|
||||
@ApiProperty({ description: 'Journal date' })
|
||||
@IsDateString()
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
date: Date;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Currency code' })
|
||||
@@ -82,7 +74,6 @@ export class CommandManualJournalDto {
|
||||
currencyCode?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Exchange rate' })
|
||||
@ToNumber()
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@IsPositive()
|
||||
@@ -112,7 +103,6 @@ export class CommandManualJournalDto {
|
||||
description?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Branch ID' })
|
||||
@ToNumber()
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
branchId?: number;
|
||||
|
||||
@@ -11,9 +11,13 @@ import {
|
||||
Put,
|
||||
Get,
|
||||
Body,
|
||||
Req,
|
||||
Res,
|
||||
Next,
|
||||
HttpCode,
|
||||
Param,
|
||||
} from '@nestjs/common';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { BuildOrganizationService } from './commands/BuildOrganization.service';
|
||||
import {
|
||||
BuildOrganizationDto,
|
||||
@@ -84,7 +88,7 @@ export class OrganizationController {
|
||||
const abilities =
|
||||
await this.orgBaseCurrencyLockingService.baseCurrencyMutateLocks();
|
||||
|
||||
return { abilities };
|
||||
return res.status(200).send({ abilities });
|
||||
}
|
||||
|
||||
@Put()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { isEmpty } from 'lodash';
|
||||
// @ts-nocheck
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { getPreventMutateBaseCurrencyModels } from '@/common/decorators/LockMutateBaseCurrency.decorator';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
interface MutateBaseCurrencyLockMeta {
|
||||
modelName: string;
|
||||
@@ -10,27 +9,26 @@ interface MutateBaseCurrencyLockMeta {
|
||||
|
||||
@Injectable()
|
||||
export class OrganizationBaseCurrencyLocking {
|
||||
constructor(private readonly moduleRef: ModuleRef) {}
|
||||
|
||||
/**
|
||||
* Retrieves the tenant models that have prevented mutation base currency.
|
||||
*/
|
||||
private getModelsPreventsMutate() {
|
||||
const lockedModels = getPreventMutateBaseCurrencyModels();
|
||||
private getModelsPreventsMutate = (tenantId: number) => {
|
||||
const Models = this.tenancy.models(tenantId);
|
||||
|
||||
const filteredEntries = Array.from(lockedModels).filter(
|
||||
const filteredEntries = Object.entries(Models).filter(
|
||||
([key, Model]) => !!Model.preventMutateBaseCurrency,
|
||||
);
|
||||
return Object.fromEntries(filteredEntries);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines the mutation base currency model is locked.
|
||||
* @param {Model} Model
|
||||
* @returns {Promise<MutateBaseCurrencyLockMeta | false>}
|
||||
*/
|
||||
private async isModelMutateLocked(
|
||||
private isModelMutateLocked = async (
|
||||
Model,
|
||||
): Promise<MutateBaseCurrencyLockMeta | false> {
|
||||
): Promise<MutateBaseCurrencyLockMeta | false> => {
|
||||
const validateQuery = Model.query();
|
||||
|
||||
if (typeof Model?.modifiers?.preventMutateBaseCurrency !== 'undefined') {
|
||||
@@ -47,24 +45,20 @@ export class OrganizationBaseCurrencyLocking {
|
||||
pluralName: Model.pluralName,
|
||||
}
|
||||
: false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the base currency mutation locks of the tenant models.
|
||||
* @param {number} tenantId
|
||||
* @returns {Promise<MutateBaseCurrencyLockMeta[]>}
|
||||
*/
|
||||
public async baseCurrencyMutateLocks(): Promise<
|
||||
MutateBaseCurrencyLockMeta[]
|
||||
> {
|
||||
const PreventedModels = this.getModelsPreventsMutate();
|
||||
const opers = Object.entries(PreventedModels).map(([ModelName, Model]) => {
|
||||
const InjectedModelProxy = this.moduleRef.get(ModelName, {
|
||||
strict: false,
|
||||
});
|
||||
const InjectedModel = InjectedModelProxy();
|
||||
public async baseCurrencyMutateLocks(
|
||||
): Promise<MutateBaseCurrencyLockMeta[]> {
|
||||
const PreventedModels = this.getModelsPreventsMutate(tenantId);
|
||||
|
||||
return this.isModelMutateLocked(InjectedModel);
|
||||
});
|
||||
const opers = Object.entries(PreventedModels).map(([ModelName, Model]) =>
|
||||
this.isModelMutateLocked(Model),
|
||||
);
|
||||
const results = await Promise.all(opers);
|
||||
|
||||
return results.filter(
|
||||
@@ -74,11 +68,12 @@ export class OrganizationBaseCurrencyLocking {
|
||||
|
||||
/**
|
||||
* Detarmines the base currency mutation locked.
|
||||
* @param {number} tenantId
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
public async isBaseCurrencyMutateLocked() {
|
||||
const locks = await this.baseCurrencyMutateLocks();
|
||||
public isBaseCurrencyMutateLocked = async (tenantId: number) => {
|
||||
const locks = await this.baseCurrencyMutateLocks(tenantId);
|
||||
|
||||
return !isEmpty(locks);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { ObjectId } from 'mongodb';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { SeedMigration } from '@/lib/Seeder/SeedMigration';
|
||||
import { Tenant } from '@/system/models';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import TenantDBManager from '@/services/Tenancy/TenantDBManager';
|
||||
import config from '../../config';
|
||||
import { ERRORS } from './constants';
|
||||
import OrganizationService from './OrganizationService';
|
||||
import TenantsManagerService from '@/services/Tenancy/TenantsManager';
|
||||
|
||||
@Service()
|
||||
export default class OrganizationUpgrade {
|
||||
@Inject()
|
||||
private organizationService: OrganizationService;
|
||||
|
||||
@Inject()
|
||||
private tenantsManager: TenantsManagerService;
|
||||
|
||||
@Inject('agenda')
|
||||
private agenda: any;
|
||||
|
||||
/**
|
||||
* Upgrades the given organization database.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public upgradeJob = async (tenantId: number): Promise<void> => {
|
||||
const tenant = await Tenant.query()
|
||||
.findById(tenantId)
|
||||
.withGraphFetched('metadata');
|
||||
|
||||
// Validate tenant version.
|
||||
this.validateTenantVersion(tenant);
|
||||
|
||||
// Initialize the tenant.
|
||||
const seedContext = this.tenantsManager.getSeedMigrationContext(tenant);
|
||||
|
||||
// Database manager.
|
||||
const dbManager = new TenantDBManager();
|
||||
|
||||
// Migrate the organization database schema.
|
||||
await dbManager.migrate(tenant);
|
||||
|
||||
// Seeds the organization database data.
|
||||
await new SeedMigration(seedContext.knex, seedContext).latest();
|
||||
|
||||
// Update the organization database version.
|
||||
await this.organizationService.flagTenantDBBatch(tenantId);
|
||||
|
||||
// Remove the tenant job id.
|
||||
await Tenant.markAsUpgraded(tenantId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Running organization upgrade job.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public upgrade = async (tenantId: number): Promise<{ jobId: string }> => {
|
||||
const tenant = await Tenant.query().findById(tenantId);
|
||||
|
||||
// Validate tenant version.
|
||||
this.validateTenantVersion(tenant);
|
||||
|
||||
// Validate tenant upgrade is not running.
|
||||
this.validateTenantUpgradeNotRunning(tenant);
|
||||
|
||||
// Send welcome mail to the user.
|
||||
const jobMeta = await this.agenda.now('organization-upgrade', {
|
||||
tenantId,
|
||||
});
|
||||
// Transformes the mangodb id to string.
|
||||
const jobId = new ObjectId(jobMeta.attrs._id).toString();
|
||||
|
||||
// Markes the tenant as currently building.
|
||||
await Tenant.markAsUpgrading(tenantId, jobId);
|
||||
|
||||
return { jobId };
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates the given tenant version.
|
||||
* @param {ITenant} tenant
|
||||
*/
|
||||
private validateTenantVersion(tenant) {
|
||||
if (tenant.databaseBatch >= config.databaseBatch) {
|
||||
throw new ServiceError(ERRORS.TENANT_DATABASE_UPGRADED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given tenant upgrade is not running.
|
||||
* @param tenant
|
||||
*/
|
||||
private validateTenantUpgradeNotRunning(tenant) {
|
||||
if (tenant.isUpgradeRunning) {
|
||||
throw new ServiceError(ERRORS.TENANT_UPGRADE_IS_RUNNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ export class CommandOrganizationValidators {
|
||||
constructor(
|
||||
private readonly baseCurrencyMutateLocking: OrganizationBaseCurrencyLocking,
|
||||
) {}
|
||||
|
||||
|
||||
/**
|
||||
* Validate mutate base currency ability.
|
||||
* @param {Tenant} tenant -
|
||||
@@ -23,7 +23,9 @@ export class CommandOrganizationValidators {
|
||||
) {
|
||||
if (tenant.isReady && newBaseCurrency !== oldBaseCurrency) {
|
||||
const isLocked =
|
||||
await this.baseCurrencyMutateLocking.isBaseCurrencyMutateLocked();
|
||||
await this.baseCurrencyMutateLocking.isBaseCurrencyMutateLocked(
|
||||
tenant.id,
|
||||
);
|
||||
|
||||
if (isLocked) {
|
||||
throw new ServiceError(ERRORS.BASE_CURRENCY_MUTATE_LOCKED);
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
CreatePaymentReceivedDto,
|
||||
EditPaymentReceivedDto,
|
||||
} from './dtos/PaymentReceived.dto';
|
||||
import { PaymentsReceivedPagesService } from './queries/PaymentsReceivedPages.service';
|
||||
|
||||
@Injectable()
|
||||
export class PaymentReceivesApplication {
|
||||
@@ -32,7 +31,6 @@ export class PaymentReceivesApplication {
|
||||
private sendPaymentReceiveMailNotification: SendPaymentReceiveMailNotification,
|
||||
private getPaymentReceivePdfService: GetPaymentReceivedPdfService,
|
||||
private getPaymentReceivedStateService: GetPaymentReceivedStateService,
|
||||
private paymentsReceivedPagesService: PaymentsReceivedPagesService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -149,14 +147,4 @@ export class PaymentReceivesApplication {
|
||||
public getPaymentReceivedState() {
|
||||
return this.getPaymentReceivedStateService.getPaymentReceivedState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the payment received edit page.
|
||||
* @param {number} paymentReceiveId - Payment receive id.
|
||||
*/
|
||||
public getPaymentReceivedEditPage(paymentReceiveId: number) {
|
||||
return this.paymentsReceivedPagesService.getPaymentReceiveEditPage(
|
||||
paymentReceiveId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Headers,
|
||||
HttpCode,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
@@ -14,14 +13,11 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { PaymentReceivesApplication } from './PaymentReceived.application';
|
||||
import {
|
||||
IPaymentReceivedCreateDTO,
|
||||
IPaymentReceivedEditDTO,
|
||||
IPaymentsReceivedFilter,
|
||||
PaymentReceiveMailOptsDTO,
|
||||
} from './types/PaymentReceived.types';
|
||||
import {
|
||||
CreatePaymentReceivedDto,
|
||||
EditPaymentReceivedDto,
|
||||
} from './dtos/PaymentReceived.dto';
|
||||
import { AcceptType } from '@/constants/accept-type';
|
||||
|
||||
@Controller('payments-received')
|
||||
@ApiTags('payments-received')
|
||||
@@ -44,20 +40,6 @@ export class PaymentReceivesController {
|
||||
);
|
||||
}
|
||||
|
||||
@Get(':id/edit-page')
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description:
|
||||
'The payment received edit page has been successfully retrieved.',
|
||||
})
|
||||
public getPaymentReceiveEditPage(
|
||||
@Param('id', ParseIntPipe) paymentReceiveId: number,
|
||||
) {
|
||||
return this.paymentReceivesApplication.getPaymentReceivedEditPage(
|
||||
paymentReceiveId,
|
||||
);
|
||||
}
|
||||
|
||||
@Get(':id/mail')
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
@@ -75,7 +57,7 @@ export class PaymentReceivesController {
|
||||
@Post()
|
||||
@ApiOperation({ summary: 'Create a new payment received.' })
|
||||
public createPaymentReceived(
|
||||
@Body() paymentReceiveDTO: CreatePaymentReceivedDto,
|
||||
@Body() paymentReceiveDTO: IPaymentReceivedCreateDTO,
|
||||
) {
|
||||
return this.paymentReceivesApplication.createPaymentReceived(
|
||||
paymentReceiveDTO,
|
||||
@@ -86,7 +68,7 @@ export class PaymentReceivesController {
|
||||
@ApiOperation({ summary: 'Edit the given payment received.' })
|
||||
public editPaymentReceive(
|
||||
@Param('id', ParseIntPipe) paymentReceiveId: number,
|
||||
@Body() paymentReceiveDTO: EditPaymentReceivedDto,
|
||||
@Body() paymentReceiveDTO: IPaymentReceivedEditDTO,
|
||||
) {
|
||||
return this.paymentReceivesApplication.editPaymentReceive(
|
||||
paymentReceiveId,
|
||||
@@ -106,9 +88,7 @@ export class PaymentReceivesController {
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Retrieves the payment received list.' })
|
||||
public getPaymentsReceived(
|
||||
@Query() filterDTO: Partial<IPaymentsReceivedFilter>,
|
||||
) {
|
||||
public getPaymentsReceived(@Query() filterDTO: Partial<IPaymentsReceivedFilter>) {
|
||||
return this.paymentReceivesApplication.getPaymentsReceived(filterDTO);
|
||||
}
|
||||
|
||||
@@ -146,16 +126,21 @@ export class PaymentReceivesController {
|
||||
})
|
||||
public getPaymentReceive(
|
||||
@Param('id', ParseIntPipe) paymentReceiveId: number,
|
||||
@Headers('accept') acceptHeader: string,
|
||||
) {
|
||||
if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
|
||||
return this.paymentReceivesApplication.getPaymentReceivePdf(
|
||||
paymentReceiveId,
|
||||
);
|
||||
} else {
|
||||
return this.paymentReceivesApplication.getPaymentReceive(
|
||||
paymentReceiveId,
|
||||
);
|
||||
}
|
||||
return this.paymentReceivesApplication.getPaymentReceive(paymentReceiveId);
|
||||
}
|
||||
|
||||
@Get(':id/pdf')
|
||||
@ApiOperation({ summary: 'Retrieves the payment received pdf.' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'The payment received pdf has been successfully retrieved.',
|
||||
})
|
||||
public getPaymentReceivePdf(
|
||||
@Param('id', ParseIntPipe) paymentReceivedId: number,
|
||||
) {
|
||||
return this.paymentReceivesApplication.getPaymentReceivePdf(
|
||||
paymentReceivedId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import { SendPaymentReceivedMailProcessor } from './processors/PaymentReceivedMa
|
||||
import { SEND_PAYMENT_RECEIVED_MAIL_QUEUE } from './constants';
|
||||
import { PaymentsReceivedExportable } from './commands/PaymentsReceivedExportable';
|
||||
import { PaymentsReceivedImportable } from './commands/PaymentsReceivedImportable';
|
||||
import { PaymentsReceivedPagesService } from './queries/PaymentsReceivedPages.service';
|
||||
|
||||
@Module({
|
||||
controllers: [PaymentReceivesController],
|
||||
@@ -64,7 +63,6 @@ import { PaymentsReceivedPagesService } from './queries/PaymentsReceivedPages.se
|
||||
SendPaymentReceivedMailProcessor,
|
||||
PaymentsReceivedExportable,
|
||||
PaymentsReceivedImportable,
|
||||
PaymentsReceivedPagesService
|
||||
],
|
||||
exports: [
|
||||
PaymentReceivesApplication,
|
||||
|
||||
@@ -1,47 +1,32 @@
|
||||
import { AttachmentLinkDto } from '@/modules/Attachments/dtos/Attachment.dto';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
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';
|
||||
import { IsArray, IsNotEmpty, ValidateNested } from 'class-validator';
|
||||
import { IsString } from 'class-validator';
|
||||
import { IsDateString, IsNumber, IsOptional } from 'class-validator';
|
||||
import { IsInt } from 'class-validator';
|
||||
|
||||
export class PaymentReceivedEntryDto {
|
||||
@ToNumber()
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
id?: number;
|
||||
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
index?: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
paymentReceiveId?: number;
|
||||
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
invoiceId: number;
|
||||
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
paymentAmount: number;
|
||||
}
|
||||
|
||||
export class CommandPaymentReceivedDto {
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({ description: 'The id of the customer', example: 1 })
|
||||
@@ -55,7 +40,6 @@ export class CommandPaymentReceivedDto {
|
||||
paymentDate: Date | string;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@ApiProperty({
|
||||
description: 'The amount of the payment received',
|
||||
@@ -64,7 +48,6 @@ export class CommandPaymentReceivedDto {
|
||||
amount?: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
@ApiProperty({
|
||||
description: 'The exchange rate of the payment received',
|
||||
@@ -80,7 +63,6 @@ export class CommandPaymentReceivedDto {
|
||||
})
|
||||
referenceNo?: string;
|
||||
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({
|
||||
@@ -90,7 +72,6 @@ export class CommandPaymentReceivedDto {
|
||||
depositAccountId: number;
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsString()
|
||||
@ApiProperty({
|
||||
description: 'The payment receive number of the payment received',
|
||||
@@ -116,7 +97,6 @@ export class CommandPaymentReceivedDto {
|
||||
entries: PaymentReceivedEntryDto[];
|
||||
|
||||
@IsOptional()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
@ApiProperty({
|
||||
description: 'The id of the branch',
|
||||
|
||||
@@ -41,10 +41,11 @@ export class PaymentsReceivedPagesService {
|
||||
|
||||
/**
|
||||
* Retrieve payment receive new page receivable entries.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} vendorId - Vendor id.
|
||||
* @return {IPaymentReceivePageEntry[]}
|
||||
*/
|
||||
public async getNewPageEntries(customerId: number) {
|
||||
public async getNewPageEntries(tenantId: number, customerId: number) {
|
||||
// Retrieve due invoices.
|
||||
const entries = await this.saleInvoice()
|
||||
.query()
|
||||
@@ -61,7 +62,10 @@ export class PaymentsReceivedPagesService {
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {Integer} paymentReceiveId - Payment receive id.
|
||||
*/
|
||||
public async getPaymentReceiveEditPage(paymentReceiveId: number): Promise<{
|
||||
public async getPaymentReceiveEditPage(
|
||||
tenantId: number,
|
||||
paymentReceiveId: number,
|
||||
): Promise<{
|
||||
paymentReceive: Omit<PaymentReceived, 'entries'>;
|
||||
entries: IPaymentReceivePageEntry[];
|
||||
}> {
|
||||
|
||||
@@ -29,7 +29,7 @@ export class PdfTemplateApplication {
|
||||
* @param {ICreateInvoicePdfTemplateDTO} invoiceTemplateDTO - The data transfer object containing the details for the new PDF template.
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
public createPdfTemplate(
|
||||
public async createPdfTemplate(
|
||||
templateName: string,
|
||||
resource: string,
|
||||
invoiceTemplateDTO: ICreateInvoicePdfTemplateDTO,
|
||||
@@ -45,7 +45,7 @@ export class PdfTemplateApplication {
|
||||
* Deletes a PDF template.
|
||||
* @param {number} templateId - The ID of the template to delete.
|
||||
*/
|
||||
public deletePdfTemplate(templateId: number) {
|
||||
public async deletePdfTemplate(templateId: number) {
|
||||
return this.deletePdfTemplateService.deletePdfTemplate(templateId);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ export class PdfTemplateApplication {
|
||||
* Retrieves a specific PDF template.
|
||||
* @param {number} templateId - The ID of the template to retrieve.
|
||||
*/
|
||||
public getPdfTemplate(templateId: number) {
|
||||
public async getPdfTemplate(templateId: number) {
|
||||
return this.getPdfTemplateService.getPdfTemplate(templateId);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export class PdfTemplateApplication {
|
||||
* Retrieves all PDF templates.
|
||||
* @param {string} resource - The resource type to filter templates.
|
||||
*/
|
||||
public getPdfTemplates(query?: { resource?: string }) {
|
||||
public async getPdfTemplates(query?: { resource?: string }) {
|
||||
return this.getPdfTemplatesService.getPdfTemplates(query);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ export class PdfTemplateApplication {
|
||||
* @param {number} templateId - The ID of the template to edit.
|
||||
* @param {IEditPdfTemplateDTO} editDTO - The data transfer object containing the updates.
|
||||
*/
|
||||
public editPdfTemplate(
|
||||
public async editPdfTemplate(
|
||||
templateId: number,
|
||||
editDTO: IEditPdfTemplateDTO,
|
||||
) {
|
||||
@@ -80,7 +80,7 @@ export class PdfTemplateApplication {
|
||||
/**
|
||||
* Gets the PDF template branding state.
|
||||
*/
|
||||
public getPdfTemplateBrandingState() {
|
||||
public async getPdfTemplateBrandingState() {
|
||||
return this.getPdfTemplateBrandingStateService.execute();
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ export class PdfTemplateApplication {
|
||||
* @param {number} templateId - The ID of the PDF template to assign as default.
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
public assignPdfTemplateAsDefault(templateId: number) {
|
||||
public async assignPdfTemplateAsDefault(templateId: number) {
|
||||
return this.assignPdfTemplateDefaultService.assignDefaultTemplate(
|
||||
templateId,
|
||||
);
|
||||
@@ -99,7 +99,7 @@ export class PdfTemplateApplication {
|
||||
* Retrieves the organization branding attributes.
|
||||
* @returns {Promise<CommonOrganizationBrandingAttributes>} The organization branding attributes.
|
||||
*/
|
||||
public getOrganizationBrandingAttributes() {
|
||||
getOrganizationBrandingAttributes() {
|
||||
return this.getOrganizationBrandingAttributesService.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,17 +47,6 @@ export class PdfTemplatesController {
|
||||
return this.pdfTemplateApplication.deletePdfTemplate(templateId);
|
||||
}
|
||||
|
||||
@Get('/state')
|
||||
@ApiOperation({ summary: 'Retrieves the PDF template branding state.' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description:
|
||||
'The PDF template branding state has been successfully retrieved.',
|
||||
})
|
||||
async getPdfTemplateBrandingState() {
|
||||
return this.pdfTemplateApplication.getPdfTemplateBrandingState();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Retrieves the PDF template details.' })
|
||||
@ApiResponse({
|
||||
|
||||
@@ -4,7 +4,6 @@ import { DeleteRoleService } from './commands/DeleteRole.service';
|
||||
import { EditRoleService } from './commands/EditRole.service';
|
||||
import { GetRoleService } from './queries/GetRole.service';
|
||||
import { GetRolesService } from './queries/GetRoles.service';
|
||||
import { RolePermissionsSchema } from './queries/RolePermissionsSchema';
|
||||
|
||||
@Injectable()
|
||||
export class RolesApplication {
|
||||
@@ -14,7 +13,6 @@ export class RolesApplication {
|
||||
private readonly deleteRoleService: DeleteRoleService,
|
||||
private readonly getRoleService: GetRoleService,
|
||||
private readonly getRolesService: GetRolesService,
|
||||
private readonly getRolePermissionsSchemaService: RolePermissionsSchema,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -61,12 +59,4 @@ export class RolesApplication {
|
||||
async getRoles() {
|
||||
return this.getRolesService.getRoles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the role permissions schema.
|
||||
* @returns The role permissions schema.
|
||||
*/
|
||||
async getRolePermissionsSchema() {
|
||||
return this.getRolePermissionsSchemaService.getRolePermissionsSchema();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,9 @@ export class RolesController {
|
||||
status: HttpStatus.OK,
|
||||
description: 'Role deleted successfully',
|
||||
})
|
||||
async deleteRole(@Param('id', ParseIntPipe) roleId: number) {
|
||||
async deleteRole(
|
||||
@Param('id', ParseIntPipe) roleId: number,
|
||||
) {
|
||||
await this.rolesApp.deleteRole(roleId);
|
||||
|
||||
return {
|
||||
@@ -81,34 +83,24 @@ export class RolesController {
|
||||
};
|
||||
}
|
||||
|
||||
@Get('permissions/schema')
|
||||
@ApiOperation({ summary: 'Get role permissions schema' })
|
||||
@ApiResponse({
|
||||
status: HttpStatus.OK,
|
||||
description: 'Role permissions schema',
|
||||
})
|
||||
async getRolePermissionsSchema() {
|
||||
const schema = await this.rolesApp.getRolePermissionsSchema();
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Get all roles' })
|
||||
@ApiResponse({ status: HttpStatus.OK, description: 'List of all roles' })
|
||||
async getRoles() {
|
||||
const roles = await this.rolesApp.getRoles();
|
||||
|
||||
return roles;
|
||||
return { roles };
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Get a specific role by ID' })
|
||||
@ApiParam({ name: 'id', description: 'Role ID' })
|
||||
@ApiResponse({ status: HttpStatus.OK, description: 'Role details' })
|
||||
async getRole(@Param('id', ParseIntPipe) roleId: number) {
|
||||
async getRole(
|
||||
@Param('id', ParseIntPipe) roleId: number,
|
||||
) {
|
||||
const role = await this.rolesApp.getRole(roleId);
|
||||
|
||||
return role;
|
||||
return { role };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import { Role } from './models/Role.model';
|
||||
import { RolePermission } from './models/RolePermission.model';
|
||||
import { RolesController } from './Roles.controller';
|
||||
import { RolesApplication } from './Roles.application';
|
||||
import { RolePermissionsSchema } from './queries/RolePermissionsSchema';
|
||||
|
||||
const models = [
|
||||
RegisterTenancyModel(Role),
|
||||
@@ -25,7 +24,6 @@ const models = [
|
||||
GetRoleService,
|
||||
GetRolesService,
|
||||
RolesApplication,
|
||||
RolePermissionsSchema
|
||||
],
|
||||
controllers: [RolesController],
|
||||
exports: [...models],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Ability } from '@casl/ability';
|
||||
import * as LruCache from 'lru-cache';
|
||||
import LruCache from 'lru-cache';
|
||||
import { Role } from './models/Role.model';
|
||||
import { RolePermission } from './models/RolePermission.model';
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user