mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 05:10:31 +00:00
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 jwt from './jwt';
|
||||||
import mail from './mail';
|
import mail from './mail';
|
||||||
import loops from './loops';
|
import loops from './loops';
|
||||||
import bankfeed from './bankfeed';
|
|
||||||
|
|
||||||
export const config = [
|
export const config = [
|
||||||
systemDatabase,
|
systemDatabase,
|
||||||
@@ -30,6 +29,5 @@ export const config = [
|
|||||||
signupRestrictions,
|
signupRestrictions,
|
||||||
jwt,
|
jwt,
|
||||||
mail,
|
mail,
|
||||||
loops,
|
loops
|
||||||
bankfeed,
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -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>,
|
next: CallHandler<any>,
|
||||||
): Observable<any> {
|
): Observable<any> {
|
||||||
const request = context.switchToHttp().getRequest();
|
const request = context.switchToHttp().getRequest();
|
||||||
|
|
||||||
// Transform both body and query parameters
|
|
||||||
request.body = this.strategy.in(request.body);
|
request.body = this.strategy.in(request.body);
|
||||||
request.query = this.strategy.in(request.query);
|
|
||||||
|
|
||||||
// handle returns stream..
|
// handle returns stream..
|
||||||
return next.handle().pipe(map(this.strategy.out));
|
return next.handle().pipe(map(this.strategy.out));
|
||||||
|
|||||||
@@ -14,15 +14,12 @@ export class ValidationPipe implements PipeTransform<any> {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
const object = plainToInstance(metatype, value);
|
const object = plainToInstance(metatype, value);
|
||||||
const errors = await validate(object, {
|
const errors = await validate(object);
|
||||||
// Strip validated object of any properties that do not have any decorators.
|
|
||||||
whitelist: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
throw new BadRequestException(errors);
|
throw new BadRequestException(errors);
|
||||||
}
|
}
|
||||||
return object;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private toValidate(metatype: Function): boolean {
|
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 { GotenbergUtils } from './GotenbergUtils';
|
||||||
import { PageProperties } from './_types';
|
import { PageProperties } from './_types';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as FormData from 'form-data';
|
import FormData from 'form-data';
|
||||||
import { Axios } from 'axios';
|
import Axios from 'axios';
|
||||||
|
|
||||||
export class GotenbergUtils {
|
export class GotenbergUtils {
|
||||||
public static assert(condition: boolean, message: string): asserts condition {
|
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> {
|
public static async fetch(endpoint: string, data: FormData): Promise<Buffer> {
|
||||||
try {
|
try {
|
||||||
const response = await new Axios({
|
const response = await Axios.post(endpoint, data, {
|
||||||
headers: {
|
headers: {
|
||||||
...data.getHeaders(),
|
...data.getHeaders(),
|
||||||
},
|
},
|
||||||
responseType: 'arraybuffer', // This ensures you get a Buffer bac
|
responseType: 'arraybuffer', // This ensures you get a Buffer bac
|
||||||
}).post(endpoint, data);
|
});
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { constants, createReadStream, PathLike, promises } from 'fs';
|
import { constants, createReadStream, PathLike, promises } from 'fs';
|
||||||
import * as FormData from 'form-data';
|
import FormData from 'form-data';
|
||||||
import { GotenbergUtils } from './GotenbergUtils';
|
import { GotenbergUtils } from './GotenbergUtils';
|
||||||
import { IConverter, PageProperties } from './_types';
|
import { IConverter, PageProperties } from './_types';
|
||||||
import { PdfFormat, ChromiumRoute } 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 { IConverter, PageProperties, PdfFormat, ChromiumRoute } from './_types';
|
||||||
import { ConverterUtils } from './ConvertUtils';
|
import { ConverterUtils } from './ConvertUtils';
|
||||||
import { Converter } from './Converter';
|
import { Converter } from './Converter';
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ export class MutateBaseCurrencyAccounts {
|
|||||||
* Mutates the all accounts or the organziation.
|
* Mutates the all accounts or the organziation.
|
||||||
* @param {string} currencyCode
|
* @param {string} currencyCode
|
||||||
*/
|
*/
|
||||||
async mutateAllAccountsCurrency(currencyCode: string) {
|
mutateAllAccountsCurrency = async (
|
||||||
await this.accountModel().query().update({ currencyCode });
|
currencyCode: string,
|
||||||
}
|
) => {
|
||||||
|
await Account.query().update({ currencyCode });
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,8 +88,6 @@ import { ViewsModule } from '../Views/Views.module';
|
|||||||
import { CurrenciesModule } from '../Currencies/Currencies.module';
|
import { CurrenciesModule } from '../Currencies/Currencies.module';
|
||||||
import { MiscellaneousModule } from '../Miscellaneous/Miscellaneous.module';
|
import { MiscellaneousModule } from '../Miscellaneous/Miscellaneous.module';
|
||||||
import { UsersModule } from '../UsersModule/Users.module';
|
import { UsersModule } from '../UsersModule/Users.module';
|
||||||
import { ContactsModule } from '../Contacts/Contacts.module';
|
|
||||||
import { BankingPlaidModule } from '../BankingPlaid/BankingPlaid.module';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -187,7 +185,6 @@ import { BankingPlaidModule } from '../BankingPlaid/BankingPlaid.module';
|
|||||||
BankingTransactionsExcludeModule,
|
BankingTransactionsExcludeModule,
|
||||||
BankingTransactionsRegonizeModule,
|
BankingTransactionsRegonizeModule,
|
||||||
BankingMatchingModule,
|
BankingMatchingModule,
|
||||||
BankingPlaidModule,
|
|
||||||
TransactionsLockingModule,
|
TransactionsLockingModule,
|
||||||
SettingsModule,
|
SettingsModule,
|
||||||
FeaturesModule,
|
FeaturesModule,
|
||||||
@@ -213,8 +210,7 @@ import { BankingPlaidModule } from '../BankingPlaid/BankingPlaid.module';
|
|||||||
ViewsModule,
|
ViewsModule,
|
||||||
CurrenciesModule,
|
CurrenciesModule,
|
||||||
MiscellaneousModule,
|
MiscellaneousModule,
|
||||||
UsersModule,
|
UsersModule
|
||||||
ContactsModule
|
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ const models = [
|
|||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [S3Module, ...models],
|
imports: [S3Module, ...models],
|
||||||
exports: [...models],
|
|
||||||
controllers: [AttachmentsController],
|
controllers: [AttachmentsController],
|
||||||
providers: [
|
providers: [
|
||||||
DeleteAttachment,
|
DeleteAttachment,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as path from 'path';
|
import path from 'path';
|
||||||
// import config from '@/config';
|
// import config from '@/config';
|
||||||
|
|
||||||
export const getUploadedObjectUri = (objectKey: string) => {
|
export const getUploadedObjectUri = (objectKey: string) => {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { BankRuleComparator } from '../types';
|
import { BankRuleComparator } from '../types';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { ToNumber } from '@/common/decorators/Validators';
|
|
||||||
|
|
||||||
class BankRuleConditionDto {
|
class BankRuleConditionDto {
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@@ -45,8 +44,6 @@ export class CommandBankRuleDto {
|
|||||||
})
|
})
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@IsNotEmpty()
|
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(0)
|
@Min(0)
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
@@ -56,7 +53,6 @@ export class CommandBankRuleDto {
|
|||||||
order: number;
|
order: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(0)
|
@Min(0)
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
@@ -65,7 +61,6 @@ export class CommandBankRuleDto {
|
|||||||
})
|
})
|
||||||
applyIfAccountId?: number;
|
applyIfAccountId?: number;
|
||||||
|
|
||||||
@IsNotEmpty()
|
|
||||||
@IsIn(['deposit', 'withdrawal'])
|
@IsIn(['deposit', 'withdrawal'])
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The transaction type to apply the rule if',
|
description: 'The transaction type to apply the rule if',
|
||||||
@@ -87,14 +82,11 @@ export class CommandBankRuleDto {
|
|||||||
@Type(() => BankRuleConditionDto)
|
@Type(() => BankRuleConditionDto)
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The conditions to apply the rule if',
|
description: 'The conditions to apply the rule if',
|
||||||
example: [
|
example: [{ field: 'description', comparator: 'contains', value: 'Salary' }],
|
||||||
{ field: 'description', comparator: 'contains', value: 'Salary' },
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
conditions: BankRuleConditionDto[];
|
conditions: BankRuleConditionDto[];
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The category to assign the rule if',
|
description: 'The category to assign the rule if',
|
||||||
example: 'Income:Salary',
|
example: 'Income:Salary',
|
||||||
@@ -103,8 +95,6 @@ export class CommandBankRuleDto {
|
|||||||
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(0)
|
@Min(0)
|
||||||
@ToNumber()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The account ID to assign the rule if',
|
description: 'The account ID to assign the rule if',
|
||||||
example: 1,
|
example: 1,
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Account } from '@/modules/Accounts/models/Account.model';
|
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||||
import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction';
|
import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction';
|
||||||
|
import { BaseModel } from '@/models/Model';
|
||||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
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()
|
@Injectable()
|
||||||
export class GetBankAccountSummary {
|
export class GetBankAccountSummary {
|
||||||
@@ -19,19 +14,6 @@ export class GetBankAccountSummary {
|
|||||||
private readonly uncategorizedBankTransactionModel: TenantModelProxy<
|
private readonly uncategorizedBankTransactionModel: TenantModelProxy<
|
||||||
typeof UncategorizedBankTransaction
|
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)
|
.findById(bankAccountId)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
await initialize(this.tenantDb(), [
|
|
||||||
this.uncategorizedBankTransactionModel(),
|
|
||||||
this.matchedBankTransactionModel(),
|
|
||||||
this.recognizedBankTransaction(),
|
|
||||||
]);
|
|
||||||
const commonQuery = (q) => {
|
const commonQuery = (q) => {
|
||||||
// Include just the given account.
|
// Include just the given account.
|
||||||
q.where('accountId', bankAccountId);
|
q.where('accountId', bankAccountId);
|
||||||
@@ -60,6 +37,11 @@ export class GetBankAccountSummary {
|
|||||||
// Only the not categorized.
|
// Only the not categorized.
|
||||||
q.modify('notCategorized');
|
q.modify('notCategorized');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface UncategorizedTransactionsCount {
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieves the uncategorized transactions count of the given bank account.
|
// Retrieves the uncategorized transactions count of the given bank account.
|
||||||
const uncategorizedTranasctionsCount =
|
const uncategorizedTranasctionsCount =
|
||||||
await this.uncategorizedBankTransactionModel()
|
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 { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||||
import { InjectSystemModel } from '../System/SystemModels/SystemModels.module';
|
import { InjectSystemModel } from '../System/SystemModels/SystemModels.module';
|
||||||
import { SystemPlaidItem } from './models/SystemPlaidItem';
|
import { SystemPlaidItem } from './models/SystemPlaidItem';
|
||||||
import { BankingPlaidController } from './BankingPlaid.controller';
|
|
||||||
|
|
||||||
const models = [RegisterTenancyModel(PlaidItem)];
|
const models = [RegisterTenancyModel(PlaidItem)];
|
||||||
|
|
||||||
@@ -39,6 +38,5 @@ const models = [RegisterTenancyModel(PlaidItem)];
|
|||||||
TenancyContext,
|
TenancyContext,
|
||||||
],
|
],
|
||||||
exports: [...models],
|
exports: [...models],
|
||||||
controllers: [BankingPlaidController]
|
|
||||||
})
|
})
|
||||||
export class BankingPlaidModule {}
|
export class BankingPlaidModule {}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { PlaidItemService } from './command/PlaidItem';
|
|||||||
import { PlaidWebooks } from './command/PlaidWebhooks';
|
import { PlaidWebooks } from './command/PlaidWebhooks';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { PlaidItemDTO } from './types/BankingPlaid.types';
|
import { PlaidItemDTO } from './types/BankingPlaid.types';
|
||||||
import { PlaidItemDto } from './dtos/PlaidItem.dto';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PlaidApplication {
|
export class PlaidApplication {
|
||||||
@@ -26,7 +25,7 @@ export class PlaidApplication {
|
|||||||
* @param {PlaidItemDTO} itemDTO
|
* @param {PlaidItemDTO} itemDTO
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public exchangeToken(itemDTO: PlaidItemDto): Promise<void> {
|
public exchangeToken(itemDTO: PlaidItemDTO): Promise<void> {
|
||||||
return this.plaidItemService.item(itemDTO);
|
return this.plaidItemService.item(itemDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
|
|||||||
import { events } from '@/common/events/events';
|
import { events } from '@/common/events/events';
|
||||||
import { SystemPlaidItem } from '../models/SystemPlaidItem';
|
import { SystemPlaidItem } from '../models/SystemPlaidItem';
|
||||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
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 { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
import { PlaidItemDto } from '../dtos/PlaidItem.dto';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PlaidItemService {
|
export class PlaidItemService {
|
||||||
@@ -31,10 +33,10 @@ export class PlaidItemService {
|
|||||||
/**
|
/**
|
||||||
* Exchanges the public token to get access token and item id and then creates
|
* Exchanges the public token to get access token and item id and then creates
|
||||||
* a new Plaid item.
|
* a new Plaid item.
|
||||||
* @param {PlaidItemDto} itemDTO - Plaid item data transfer object.
|
* @param {PlaidItemDTO} itemDTO - Plaid item data transfer object.
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public async item(itemDTO: PlaidItemDto): Promise<void> {
|
public async item(itemDTO: PlaidItemDTO): Promise<void> {
|
||||||
const { publicToken, institutionId } = itemDTO;
|
const { publicToken, institutionId } = itemDTO;
|
||||||
|
|
||||||
const tenant = await this.tenancyContext.getTenant();
|
const tenant = await this.tenancyContext.getTenant();
|
||||||
|
|||||||
@@ -15,13 +15,6 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PlaidUpdateTransactions {
|
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(
|
constructor(
|
||||||
private readonly plaidSync: PlaidSyncDb,
|
private readonly plaidSync: PlaidSyncDb,
|
||||||
private readonly uow: UnitOfWork,
|
private readonly uow: UnitOfWork,
|
||||||
@@ -35,7 +28,8 @@ export class PlaidUpdateTransactions {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles sync the Plaid item to Bigcaptial under UOW.
|
* 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; }>}
|
* @returns {Promise<{ addedCount: number; modifiedCount: number; removedCount: number; }>}
|
||||||
*/
|
*/
|
||||||
public async updateTransactions(plaidItemId: string) {
|
public async updateTransactions(plaidItemId: string) {
|
||||||
@@ -50,9 +44,9 @@ export class PlaidUpdateTransactions {
|
|||||||
* - New bank accounts.
|
* - New bank accounts.
|
||||||
* - Last accounts feeds updated at.
|
* - Last accounts feeds updated at.
|
||||||
* - Turn on the accounts feed flag.
|
* - Turn on the accounts feed flag.
|
||||||
|
* @param {number} tenantId - Tenant ID.
|
||||||
* @param {string} plaidItemId - The Plaid ID for the item.
|
* @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(
|
public async updateTransactionsWork(
|
||||||
plaidItemId: string,
|
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 -
|
* @param {IBankRuleEventEditedPayload} payload -
|
||||||
*/
|
*/
|
||||||
@OnEvent(events.bankRules.onEdited)
|
@OnEvent(events.bankRules.onEdited)
|
||||||
async recognizedTransactionsOnRuleEdited({
|
private async recognizedTransactionsOnRuleEdited({
|
||||||
editRuleDTO,
|
editRuleDTO,
|
||||||
oldBankRule,
|
oldBankRule,
|
||||||
bankRule,
|
bankRule,
|
||||||
|
|||||||
@@ -9,8 +9,11 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
import { BankingTransactionsApplication } from './BankingTransactionsApplication.service';
|
import { BankingTransactionsApplication } from './BankingTransactionsApplication.service';
|
||||||
|
import {
|
||||||
|
IBankAccountsFilter,
|
||||||
|
ICashflowAccountTransactionsQuery,
|
||||||
|
} from './types/BankingTransactions.types';
|
||||||
import { CreateBankTransactionDto } from './dtos/CreateBankTransaction.dto';
|
import { CreateBankTransactionDto } from './dtos/CreateBankTransaction.dto';
|
||||||
import { GetBankTransactionsQueryDto } from './dtos/GetBankTranasctionsQuery.dto';
|
|
||||||
|
|
||||||
@Controller('banking/transactions')
|
@Controller('banking/transactions')
|
||||||
@ApiTags('banking-transactions')
|
@ApiTags('banking-transactions')
|
||||||
@@ -21,7 +24,7 @@ export class BankingTransactionsController {
|
|||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
async getBankAccountTransactions(
|
async getBankAccountTransactions(
|
||||||
@Query() query: GetBankTransactionsQueryDto,
|
@Query() query: ICashflowAccountTransactionsQuery,
|
||||||
) {
|
) {
|
||||||
return this.bankingTransactionsApplication.getBankAccountTransactions(
|
return this.bankingTransactionsApplication.getBankAccountTransactions(
|
||||||
query,
|
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 */
|
/* eslint-disable global-require */
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { Model } from 'objection';
|
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 amount!: number;
|
||||||
readonly date!: Date | string;
|
readonly date!: Date | string;
|
||||||
readonly categorized!: boolean;
|
readonly categorized!: boolean;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import * as R from 'ramda';
|
import R from 'ramda';
|
||||||
import * as moment from 'moment';
|
import moment from 'moment';
|
||||||
import { first, isEmpty } from 'lodash';
|
import { first, isEmpty } from 'lodash';
|
||||||
import {
|
import {
|
||||||
ICashflowAccountTransaction,
|
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 { ICashflowAccountTransactionsQuery } from '../../types/BankingTransactions.types';
|
||||||
import {
|
import {
|
||||||
groupMatchedBankTransactions,
|
groupMatchedBankTransactions,
|
||||||
groupUncategorizedTransactions,
|
groupUncategorizedTransactions,
|
||||||
} from './_utils';
|
} 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 })
|
@Injectable({ scope: Scope.REQUEST })
|
||||||
export class GetBankAccountTransactionsRepository {
|
export class GetBankAccountTransactionsRepository {
|
||||||
|
private models: any;
|
||||||
public query: ICashflowAccountTransactionsQuery;
|
public query: ICashflowAccountTransactionsQuery;
|
||||||
public transactions: any;
|
public transactions: any;
|
||||||
public uncategorizedTransactions: any;
|
public uncategorizedTransactions: any;
|
||||||
@@ -20,28 +17,6 @@ export class GetBankAccountTransactionsRepository {
|
|||||||
public pagination: any;
|
public pagination: any;
|
||||||
public openingBalance: 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) {
|
setQuery(query: ICashflowAccountTransactionsQuery) {
|
||||||
this.query = query;
|
this.query = query;
|
||||||
}
|
}
|
||||||
@@ -62,8 +37,9 @@ export class GetBankAccountTransactionsRepository {
|
|||||||
* @param {ICashflowAccountTransactionsQuery} query -
|
* @param {ICashflowAccountTransactionsQuery} query -
|
||||||
*/
|
*/
|
||||||
async initCashflowAccountTransactions() {
|
async initCashflowAccountTransactions() {
|
||||||
const { results, pagination } = await this.accountTransactionModel()
|
const { AccountTransaction } = this.models;
|
||||||
.query()
|
|
||||||
|
const { results, pagination } = await AccountTransaction.query()
|
||||||
.where('account_id', this.query.accountId)
|
.where('account_id', this.query.accountId)
|
||||||
.orderBy([
|
.orderBy([
|
||||||
{ column: 'date', order: 'desc' },
|
{ column: 'date', order: 'desc' },
|
||||||
@@ -83,9 +59,10 @@ export class GetBankAccountTransactionsRepository {
|
|||||||
* @return {Promise<number>}
|
* @return {Promise<number>}
|
||||||
*/
|
*/
|
||||||
async initCashflowAccountOpeningBalance(): Promise<void> {
|
async initCashflowAccountOpeningBalance(): Promise<void> {
|
||||||
|
const { AccountTransaction } = this.models;
|
||||||
|
|
||||||
// Retrieve the opening balance of credit and debit balances.
|
// Retrieve the opening balance of credit and debit balances.
|
||||||
const openingBalancesSubquery = this.accountTransactionModel()
|
const openingBalancesSubquery = AccountTransaction.query()
|
||||||
.query()
|
|
||||||
.where('account_id', this.query.accountId)
|
.where('account_id', this.query.accountId)
|
||||||
.orderBy([
|
.orderBy([
|
||||||
{ column: 'date', order: 'desc' },
|
{ column: 'date', order: 'desc' },
|
||||||
@@ -95,8 +72,7 @@ export class GetBankAccountTransactionsRepository {
|
|||||||
.offset(this.pagination.pageSize * (this.pagination.page - 1));
|
.offset(this.pagination.pageSize * (this.pagination.page - 1));
|
||||||
|
|
||||||
// Sumation of credit and debit balance.
|
// Sumation of credit and debit balance.
|
||||||
const openingBalances = await this.accountTransactionModel()
|
const openingBalances = await AccountTransaction.query()
|
||||||
.query()
|
|
||||||
.sum('credit as credit')
|
.sum('credit as credit')
|
||||||
.sum('debit as debit')
|
.sum('debit as debit')
|
||||||
.from(openingBalancesSubquery.as('T'))
|
.from(openingBalancesSubquery.as('T'))
|
||||||
@@ -111,11 +87,14 @@ export class GetBankAccountTransactionsRepository {
|
|||||||
* Initialize the uncategorized transactions of the bank account.
|
* Initialize the uncategorized transactions of the bank account.
|
||||||
*/
|
*/
|
||||||
async initCategorizedTransactions() {
|
async initCategorizedTransactions() {
|
||||||
|
const { UncategorizedCashflowTransaction } = this.models;
|
||||||
const refs = this.transactions.map((t) => [t.referenceType, t.referenceId]);
|
const refs = this.transactions.map((t) => [t.referenceType, t.referenceId]);
|
||||||
|
|
||||||
const uncategorizedTransactions =
|
const uncategorizedTransactions =
|
||||||
await this.uncategorizedBankTransactionModel()
|
await UncategorizedCashflowTransaction.query().whereIn(
|
||||||
.query()
|
['categorizeRefType', 'categorizeRefId'],
|
||||||
.whereIn(['categorizeRefType', 'categorizeRefId'], refs);
|
refs,
|
||||||
|
);
|
||||||
|
|
||||||
this.uncategorizedTransactions = uncategorizedTransactions;
|
this.uncategorizedTransactions = uncategorizedTransactions;
|
||||||
this.uncategorizedTransactionsMapByRef = groupUncategorizedTransactions(
|
this.uncategorizedTransactionsMapByRef = groupUncategorizedTransactions(
|
||||||
@@ -127,11 +106,14 @@ export class GetBankAccountTransactionsRepository {
|
|||||||
* Initialize the matched bank transactions of the bank account.
|
* Initialize the matched bank transactions of the bank account.
|
||||||
*/
|
*/
|
||||||
async initMatchedTransactions(): Promise<void> {
|
async initMatchedTransactions(): Promise<void> {
|
||||||
|
const { MatchedBankTransaction } = this.models;
|
||||||
const refs = this.transactions.map((t) => [t.referenceType, t.referenceId]);
|
const refs = this.transactions.map((t) => [t.referenceType, t.referenceId]);
|
||||||
|
|
||||||
const matchedBankTransactions = await this.matchedBankTransactionModel()
|
const matchedBankTransactions =
|
||||||
.query()
|
await MatchedBankTransaction.query().whereIn(
|
||||||
.whereIn(['referenceType', 'referenceId'], refs);
|
['referenceType', 'referenceId'],
|
||||||
|
refs,
|
||||||
|
);
|
||||||
this.matchedBankTransactions = matchedBankTransactions;
|
this.matchedBankTransactions = matchedBankTransactions;
|
||||||
this.matchedBankTransactionsMapByRef = groupMatchedBankTransactions(
|
this.matchedBankTransactionsMapByRef = groupMatchedBankTransactions(
|
||||||
matchedBankTransactions,
|
matchedBankTransactions,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
Param,
|
Param,
|
||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
Query,
|
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { BillPaymentsApplication } from './BillPaymentsApplication.service';
|
import { BillPaymentsApplication } from './BillPaymentsApplication.service';
|
||||||
import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
|
import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
|
||||||
@@ -14,16 +13,11 @@ import {
|
|||||||
CreateBillPaymentDto,
|
CreateBillPaymentDto,
|
||||||
EditBillPaymentDto,
|
EditBillPaymentDto,
|
||||||
} from './dtos/BillPayment.dto';
|
} from './dtos/BillPayment.dto';
|
||||||
import { GetBillPaymentsFilterDto } from './dtos/GetBillPaymentsFilter.dto';
|
|
||||||
import { BillPaymentsPages } from './commands/BillPaymentsPages.service';
|
|
||||||
|
|
||||||
@Controller('bill-payments')
|
@Controller('bill-payments')
|
||||||
@ApiTags('bill-payments')
|
@ApiTags('bill-payments')
|
||||||
export class BillPaymentsController {
|
export class BillPaymentsController {
|
||||||
constructor(
|
constructor(private billPaymentsApplication: BillPaymentsApplication) {}
|
||||||
private billPaymentsApplication: BillPaymentsApplication,
|
|
||||||
private billPaymentsPagesService: BillPaymentsPages,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@ApiOperation({ summary: 'Create a new bill payment.' })
|
@ApiOperation({ summary: 'Create a new bill payment.' })
|
||||||
@@ -63,22 +57,16 @@ export class BillPaymentsController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('/new-page/entries')
|
@Get(':billPaymentId')
|
||||||
@ApiOperation({
|
@ApiOperation({ summary: 'Retrieves the bill payment details.' })
|
||||||
summary:
|
|
||||||
'Retrieves the payable entries of the new page once vendor be selected.',
|
|
||||||
})
|
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'vendorId',
|
name: 'billPaymentId',
|
||||||
required: true,
|
required: true,
|
||||||
type: Number,
|
type: Number,
|
||||||
description: 'The vendor id',
|
description: 'The bill payment id',
|
||||||
})
|
})
|
||||||
async getBillPaymentNewPageEntries(@Query('vendorId') vendorId: number) {
|
public getBillPayment(@Param('billPaymentId') billPaymentId: string) {
|
||||||
const entries =
|
return this.billPaymentsApplication.getBillPayment(Number(billPaymentId));
|
||||||
await this.billPaymentsPagesService.getNewPageEntries(vendorId);
|
|
||||||
|
|
||||||
return entries;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':billPaymentId/bills')
|
@Get(':billPaymentId/bills')
|
||||||
@@ -92,47 +80,4 @@ export class BillPaymentsController {
|
|||||||
public getPaymentBills(@Param('billPaymentId') billPaymentId: string) {
|
public getPaymentBills(@Param('billPaymentId') billPaymentId: string) {
|
||||||
return this.billPaymentsApplication.getPaymentBills(Number(billPaymentId));
|
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 { LedgerModule } from '../Ledger/Ledger.module';
|
||||||
import { AccountsModule } from '../Accounts/Accounts.module';
|
import { AccountsModule } from '../Accounts/Accounts.module';
|
||||||
import { BillPaymentsExportable } from './queries/BillPaymentsExportable';
|
import { BillPaymentsExportable } from './queries/BillPaymentsExportable';
|
||||||
|
import { GetBillPayments } from '../Bills/queries/GetBillPayments';
|
||||||
import { BillPaymentsImportable } from './commands/BillPaymentsImportable';
|
import { BillPaymentsImportable } from './commands/BillPaymentsImportable';
|
||||||
import { GetBillPaymentsService } from './queries/GetBillPayments.service';
|
|
||||||
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
|
|
||||||
import { BillPaymentsPages } from './commands/BillPaymentsPages.service';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [LedgerModule, AccountsModule, DynamicListModule],
|
imports: [LedgerModule, AccountsModule],
|
||||||
providers: [
|
providers: [
|
||||||
BillPaymentsApplication,
|
BillPaymentsApplication,
|
||||||
CreateBillPaymentService,
|
CreateBillPaymentService,
|
||||||
@@ -39,10 +37,9 @@ import { BillPaymentsPages } from './commands/BillPaymentsPages.service';
|
|||||||
TenancyContext,
|
TenancyContext,
|
||||||
BillPaymentGLEntries,
|
BillPaymentGLEntries,
|
||||||
BillPaymentGLEntriesSubscriber,
|
BillPaymentGLEntriesSubscriber,
|
||||||
|
GetBillPayments,
|
||||||
BillPaymentsExportable,
|
BillPaymentsExportable,
|
||||||
BillPaymentsImportable,
|
BillPaymentsImportable,
|
||||||
GetBillPaymentsService,
|
|
||||||
BillPaymentsPages,
|
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
BillPaymentValidators,
|
BillPaymentValidators,
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { CreateBillPaymentService } from './commands/CreateBillPayment.service';
|
import { CreateBillPaymentService } from './commands/CreateBillPayment.service';
|
||||||
import { DeleteBillPayment } from './commands/DeleteBillPayment.service';
|
import { DeleteBillPayment } from './commands/DeleteBillPayment.service';
|
||||||
import { EditBillPayment } from './commands/EditBillPayment.service';
|
import { EditBillPayment } from './commands/EditBillPayment.service';
|
||||||
|
// import { GetBillPayments } from './GetBillPayments';
|
||||||
import { GetBillPayment } from './queries/GetBillPayment.service';
|
import { GetBillPayment } from './queries/GetBillPayment.service';
|
||||||
import { GetPaymentBills } from './queries/GetPaymentBills.service';
|
import { GetPaymentBills } from './queries/GetPaymentBills.service';
|
||||||
|
import { GetBillPayments } from '../Bills/queries/GetBillPayments';
|
||||||
import { CreateBillPaymentDto, EditBillPaymentDto } from './dtos/BillPayment.dto';
|
import { CreateBillPaymentDto, EditBillPaymentDto } from './dtos/BillPayment.dto';
|
||||||
import { GetBillPaymentsService } from './queries/GetBillPayments.service';
|
|
||||||
import { GetBillPaymentsFilterDto } from './dtos/GetBillPaymentsFilter.dto';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bill payments application.
|
* Bill payments application.
|
||||||
@@ -20,7 +20,7 @@ export class BillPaymentsApplication {
|
|||||||
private deleteBillPaymentService: DeleteBillPayment,
|
private deleteBillPaymentService: DeleteBillPayment,
|
||||||
private getBillPaymentService: GetBillPayment,
|
private getBillPaymentService: GetBillPayment,
|
||||||
private getPaymentBillsService: GetPaymentBills,
|
private getPaymentBillsService: GetPaymentBills,
|
||||||
private getBillPaymentsService: GetBillPaymentsService,
|
private getBillPaymentsService: GetBillPayments,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,10 +58,9 @@ export class BillPaymentsApplication {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves bill payments list.
|
* Retrieves bill payments list.
|
||||||
* @param {GetBillPaymentsFilterDto} filterDTO - The given bill payments filter dto.
|
|
||||||
*/
|
*/
|
||||||
public getBillPayments(filterDTO: GetBillPaymentsFilterDto) {
|
public getBillPayments() {
|
||||||
return this.getBillPaymentsService.getBillPayments(filterDTO);
|
// 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';
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class BillPaymentsPages {
|
export default class BillPaymentsPages {
|
||||||
/**
|
/**
|
||||||
* @param {TenantModelProxy<typeof Bill>} billModel - Bill model.
|
* @param {TenantModelProxy<typeof Bill>} billModel - Bill model.
|
||||||
* @param {TenantModelProxy<typeof BillPayment>} billPaymentModel - Bill payment model.
|
* @param {TenantModelProxy<typeof BillPayment>} billPaymentModel - Bill payment model.
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
IsDateString,
|
IsDate,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsNumber,
|
IsNumber,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
@@ -10,33 +9,25 @@ import {
|
|||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { AttachmentLinkDto } from '@/modules/Attachments/dtos/Attachment.dto';
|
import { AttachmentLinkDto } from '@/modules/Attachments/dtos/Attachment.dto';
|
||||||
import { ToNumber } from '@/common/decorators/Validators';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
export class BillPaymentEntryDto {
|
export class BillPaymentEntryDto {
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsNotEmpty()
|
|
||||||
@ApiProperty({ description: 'The id of the bill', example: 1 })
|
|
||||||
billId: number;
|
billId: number;
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsNotEmpty()
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'The payment amount of the bill payment',
|
|
||||||
example: 100,
|
|
||||||
})
|
|
||||||
paymentAmount: number;
|
paymentAmount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommandBillPaymentDTO {
|
export class CommandBillPaymentDTO {
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@ApiProperty({ description: 'The id of the vendor', example: 1 })
|
@ApiProperty({
|
||||||
|
description: 'The id of the vendor',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
vendorId: number;
|
vendorId: number;
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
@@ -45,10 +36,12 @@ export class CommandBillPaymentDTO {
|
|||||||
})
|
})
|
||||||
amount?: number;
|
amount?: number;
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@ApiProperty({ description: 'The id of the payment account', example: 1 })
|
@ApiProperty({
|
||||||
|
description: 'The id of the payment account',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
paymentAccountId: number;
|
paymentAccountId: number;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@@ -59,8 +52,8 @@ export class CommandBillPaymentDTO {
|
|||||||
})
|
})
|
||||||
paymentNumber?: string;
|
paymentNumber?: string;
|
||||||
|
|
||||||
@IsDateString()
|
@IsDate()
|
||||||
@IsNotEmpty()
|
@Type(() => Date)
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The payment date of the bill payment',
|
description: 'The payment date of the bill payment',
|
||||||
example: '2021-01-01',
|
example: '2021-01-01',
|
||||||
@@ -83,6 +76,7 @@ export class CommandBillPaymentDTO {
|
|||||||
})
|
})
|
||||||
statement?: string;
|
statement?: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@@ -98,10 +92,12 @@ export class CommandBillPaymentDTO {
|
|||||||
})
|
})
|
||||||
entries: BillPaymentEntryDto[];
|
entries: BillPaymentEntryDto[];
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ApiProperty({ description: 'The id of the branch', example: 1 })
|
@ApiProperty({
|
||||||
|
description: 'The id of the branch',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
branchId?: number;
|
branchId?: number;
|
||||||
|
|
||||||
@IsArray()
|
@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 { InjectModelMeta } from '@/modules/Tenancy/TenancyModels/decorators/InjectModelMeta.decorator';
|
||||||
import { BillPaymentMeta } from './BillPayment.meta';
|
import { BillPaymentMeta } from './BillPayment.meta';
|
||||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||||
import { Model } from 'objection';
|
|
||||||
|
|
||||||
@ImportableModel()
|
@ImportableModel()
|
||||||
@ExportableModel()
|
@ExportableModel()
|
||||||
@@ -62,97 +61,102 @@ export class BillPayment extends TenantBaseModel {
|
|||||||
return this.amount * this.exchangeRate;
|
return this.amount * this.exchangeRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model settings.
|
||||||
|
*/
|
||||||
|
// static get meta() {
|
||||||
|
// return BillPaymentSettings;
|
||||||
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Relationship mapping.
|
* Relationship mapping.
|
||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
// static get relationMappings() {
|
||||||
const { BillPaymentEntry } = require('./BillPaymentEntry');
|
// const BillPaymentEntry = require('models/BillPaymentEntry');
|
||||||
const {
|
// const AccountTransaction = require('models/AccountTransaction');
|
||||||
AccountTransaction,
|
// const Vendor = require('models/Vendor');
|
||||||
} = require('../../Accounts/models/AccountTransaction.model');
|
// const Account = require('models/Account');
|
||||||
const { Vendor } = require('../../Vendors/models/Vendor');
|
// const Branch = require('models/Branch');
|
||||||
const { Account } = require('../../Accounts/models/Account.model');
|
// const Document = require('models/Document');
|
||||||
const { Branch } = require('../../Branches/models/Branch.model');
|
|
||||||
const { Document } = require('../../ChromiumlyTenancy/models/Document');
|
|
||||||
|
|
||||||
return {
|
// return {
|
||||||
entries: {
|
// entries: {
|
||||||
relation: Model.HasManyRelation,
|
// relation: Model.HasManyRelation,
|
||||||
modelClass: BillPaymentEntry,
|
// modelClass: BillPaymentEntry.default,
|
||||||
join: {
|
// join: {
|
||||||
from: 'bills_payments.id',
|
// from: 'bills_payments.id',
|
||||||
to: 'bills_payments_entries.billPaymentId',
|
// to: 'bills_payments_entries.billPaymentId',
|
||||||
},
|
// },
|
||||||
filter: (query) => {
|
// filter: (query) => {
|
||||||
query.orderBy('index', 'ASC');
|
// query.orderBy('index', 'ASC');
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
|
|
||||||
vendor: {
|
// vendor: {
|
||||||
relation: Model.BelongsToOneRelation,
|
// relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Vendor,
|
// modelClass: Vendor.default,
|
||||||
join: {
|
// join: {
|
||||||
from: 'bills_payments.vendorId',
|
// from: 'bills_payments.vendorId',
|
||||||
to: 'contacts.id',
|
// to: 'contacts.id',
|
||||||
},
|
// },
|
||||||
filter(query) {
|
// filter(query) {
|
||||||
query.where('contact_service', 'vendor');
|
// query.where('contact_service', 'vendor');
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
|
|
||||||
paymentAccount: {
|
// paymentAccount: {
|
||||||
relation: Model.BelongsToOneRelation,
|
// relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Account,
|
// modelClass: Account.default,
|
||||||
join: {
|
// join: {
|
||||||
from: 'bills_payments.paymentAccountId',
|
// from: 'bills_payments.paymentAccountId',
|
||||||
to: 'accounts.id',
|
// to: 'accounts.id',
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
|
|
||||||
transactions: {
|
// transactions: {
|
||||||
relation: Model.HasManyRelation,
|
// relation: Model.HasManyRelation,
|
||||||
modelClass: AccountTransaction,
|
// modelClass: AccountTransaction.default,
|
||||||
join: {
|
// join: {
|
||||||
from: 'bills_payments.id',
|
// from: 'bills_payments.id',
|
||||||
to: 'accounts_transactions.referenceId',
|
// to: 'accounts_transactions.referenceId',
|
||||||
},
|
// },
|
||||||
filter(builder) {
|
// filter(builder) {
|
||||||
builder.where('reference_type', 'BillPayment');
|
// builder.where('reference_type', 'BillPayment');
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Bill payment may belongs to branch.
|
// * Bill payment may belongs to branch.
|
||||||
*/
|
// */
|
||||||
branch: {
|
// branch: {
|
||||||
relation: Model.BelongsToOneRelation,
|
// relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Branch,
|
// modelClass: Branch.default,
|
||||||
join: {
|
// join: {
|
||||||
from: 'bills_payments.branchId',
|
// from: 'bills_payments.branchId',
|
||||||
to: 'branches.id',
|
// to: 'branches.id',
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Bill payment may has many attached attachments.
|
// * Bill payment may has many attached attachments.
|
||||||
*/
|
// */
|
||||||
attachments: {
|
// attachments: {
|
||||||
relation: Model.ManyToManyRelation,
|
// relation: Model.ManyToManyRelation,
|
||||||
modelClass: Document,
|
// modelClass: Document.default,
|
||||||
join: {
|
// join: {
|
||||||
from: 'bills_payments.id',
|
// from: 'bills_payments.id',
|
||||||
through: {
|
// through: {
|
||||||
from: 'document_links.modelId',
|
// from: 'document_links.modelId',
|
||||||
to: 'document_links.documentId',
|
// to: 'document_links.documentId',
|
||||||
},
|
// },
|
||||||
to: 'documents.id',
|
// to: 'documents.id',
|
||||||
},
|
// },
|
||||||
filter(query) {
|
// filter(query) {
|
||||||
query.where('model_ref', 'BillPayment');
|
// query.where('model_ref', 'BillPayment');
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the default custom views, roles and columns.
|
* Retrieve the default custom views, roles and columns.
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from "@nestjs/common";
|
||||||
import { BillPaymentsApplication } from '../BillPaymentsApplication.service';
|
import { BillPaymentsApplication } from "../BillPaymentsApplication.service";
|
||||||
import { Exportable } from '@/modules/Export/Exportable';
|
import { Exportable } from "@/modules/Export/Exportable";
|
||||||
import { EXPORT_SIZE_LIMIT } from '@/modules/Export/constants';
|
import { EXPORT_SIZE_LIMIT } from "@/modules/Export/constants";
|
||||||
import { ExportableService } from '@/modules/Export/decorators/ExportableModel.decorator';
|
import { ExportableService } from "@/modules/Export/decorators/ExportableModel.decorator";
|
||||||
import { BillPayment } from '../models/BillPayment';
|
import { BillPayment } from "../models/BillPayment";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ExportableService({ name: BillPayment.name })
|
@ExportableService({ name: BillPayment.name })
|
||||||
export class BillPaymentsExportable extends Exportable {
|
export class BillPaymentsExportable extends Exportable {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly billPaymentsApplication: BillPaymentsApplication,
|
private readonly billPaymentsApplication: BillPaymentsApplication
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@@ -30,11 +30,13 @@ export class BillPaymentsExportable extends Exportable {
|
|||||||
...query,
|
...query,
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: EXPORT_SIZE_LIMIT,
|
pageSize: EXPORT_SIZE_LIMIT,
|
||||||
filterQuery,
|
filterQuery
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
return this.billPaymentsApplication
|
return [];
|
||||||
.getBillPayments(parsedQuery)
|
|
||||||
.then((output) => output.billPayments);
|
// 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.
|
* Handle bill payment writing journal entries once created.
|
||||||
*/
|
*/
|
||||||
@OnEvent(events.billPayment.onCreated)
|
@OnEvent(events.billPayment.onCreated)
|
||||||
async handleWriteJournalEntries({
|
private async handleWriteJournalEntries({
|
||||||
billPayment,
|
billPayment,
|
||||||
trx,
|
trx,
|
||||||
}: IBillPaymentEventCreatedPayload) {
|
}: IBillPaymentEventCreatedPayload) {
|
||||||
@@ -34,7 +34,7 @@ export class BillPaymentGLEntriesSubscriber {
|
|||||||
* Handle bill payment re-writing journal entries once the payment transaction be edited.
|
* Handle bill payment re-writing journal entries once the payment transaction be edited.
|
||||||
*/
|
*/
|
||||||
@OnEvent(events.billPayment.onEdited)
|
@OnEvent(events.billPayment.onEdited)
|
||||||
async handleRewriteJournalEntriesOncePaymentEdited({
|
private async handleRewriteJournalEntriesOncePaymentEdited({
|
||||||
billPayment,
|
billPayment,
|
||||||
trx,
|
trx,
|
||||||
}: IBillPaymentEventEditedPayload) {
|
}: IBillPaymentEventEditedPayload) {
|
||||||
@@ -48,7 +48,7 @@ export class BillPaymentGLEntriesSubscriber {
|
|||||||
* Reverts journal entries once bill payment deleted.
|
* Reverts journal entries once bill payment deleted.
|
||||||
*/
|
*/
|
||||||
@OnEvent(events.billPayment.onDeleted)
|
@OnEvent(events.billPayment.onDeleted)
|
||||||
async handleRevertJournalEntries({
|
private async handleRevertJournalEntries({
|
||||||
billPaymentId,
|
billPaymentId,
|
||||||
trx,
|
trx,
|
||||||
}: IBillPaymentEventDeletedPayload) {
|
}: IBillPaymentEventDeletedPayload) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { ToNumber } from '@/common/decorators/Validators';
|
|
||||||
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
|
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
@@ -6,14 +5,14 @@ import {
|
|||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsDate,
|
IsDate,
|
||||||
IsDateString,
|
|
||||||
IsEnum,
|
IsEnum,
|
||||||
IsInt,
|
IsInt,
|
||||||
IsNotEmpty,
|
|
||||||
IsNumber,
|
IsNumber,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsPositive,
|
IsPositive,
|
||||||
IsString,
|
IsString,
|
||||||
|
Min,
|
||||||
|
MinLength,
|
||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
|
||||||
@@ -30,12 +29,10 @@ export class BillEntryDto extends ItemEntryDto {
|
|||||||
|
|
||||||
class AttachmentDto {
|
class AttachmentDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
|
||||||
key: string;
|
key: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommandBillDto {
|
export class CommandBillDto {
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
@IsString()
|
||||||
billNumber: string;
|
billNumber: string;
|
||||||
|
|
||||||
@@ -43,36 +40,32 @@ export class CommandBillDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
referenceNo?: string;
|
referenceNo?: string;
|
||||||
|
|
||||||
@IsNotEmpty()
|
@IsDate()
|
||||||
@IsDateString()
|
@Type(() => Date)
|
||||||
billDate: Date;
|
billDate: Date;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsDateString()
|
@IsDate()
|
||||||
|
@Type(() => Date)
|
||||||
dueDate?: Date;
|
dueDate?: Date;
|
||||||
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsNotEmpty()
|
|
||||||
vendorId: number;
|
vendorId: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsPositive()
|
@IsPositive()
|
||||||
exchangeRate?: number;
|
exchangeRate?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
warehouseId?: number;
|
warehouseId?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
branchId?: number;
|
branchId?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
projectId?: number;
|
projectId?: number;
|
||||||
|
|
||||||
@@ -81,11 +74,9 @@ export class CommandBillDto {
|
|||||||
note?: string;
|
note?: string;
|
||||||
|
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
@IsOptional()
|
|
||||||
open: boolean = false;
|
open: boolean = false;
|
||||||
|
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
@IsOptional()
|
|
||||||
isInclusiveTax: boolean = false;
|
isInclusiveTax: boolean = false;
|
||||||
|
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@@ -101,17 +92,13 @@ export class CommandBillDto {
|
|||||||
attachments?: AttachmentDto[];
|
attachments?: AttachmentDto[];
|
||||||
|
|
||||||
@IsEnum(DiscountType)
|
@IsEnum(DiscountType)
|
||||||
@IsOptional()
|
|
||||||
discountType: DiscountType = DiscountType.Amount;
|
discountType: DiscountType = DiscountType.Amount;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsPositive()
|
|
||||||
discount?: number;
|
discount?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
adjustment?: number;
|
adjustment?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,13 +76,8 @@ export class BranchesController {
|
|||||||
status: 200,
|
status: 200,
|
||||||
description: 'The branches feature has been successfully activated.',
|
description: 'The branches feature has been successfully activated.',
|
||||||
})
|
})
|
||||||
async activateBranches() {
|
activateBranches() {
|
||||||
await this.branchesApplication.activateBranches();
|
return this.branchesApplication.activateBranches();
|
||||||
|
|
||||||
return {
|
|
||||||
code: 200,
|
|
||||||
message: 'The branches activated successfully.',
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id/mark-as-primary')
|
@Put(':id/mark-as-primary')
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ export class BranchesSettingsService {
|
|||||||
const settingsStore = await this.settingsStore();
|
const settingsStore = await this.settingsStore();
|
||||||
|
|
||||||
settingsStore.set({ group: 'features', key: Features.BRANCHES, value: 1 });
|
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 { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
import {
|
import {
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsEmail,
|
IsEmail,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
|
IsOptional,
|
||||||
IsString,
|
IsString,
|
||||||
IsUrl,
|
IsUrl,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as path from 'path';
|
import path from 'path';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import { PageProperties, PdfFormat } from '@/libs/Chromiumly/_types';
|
import { PageProperties, PdfFormat } from '@/libs/Chromiumly/_types';
|
||||||
import { UrlConverter } from '@/libs/Chromiumly/UrlConvert';
|
import { UrlConverter } from '@/libs/Chromiumly/UrlConvert';
|
||||||
@@ -40,7 +40,7 @@ export class ChromiumlyHtmlConvert {
|
|||||||
|
|
||||||
const cleanup = async () => {
|
const cleanup = async () => {
|
||||||
await fs.unlink(filePath);
|
await fs.unlink(filePath);
|
||||||
await this.documentModel().query().where('key', filename).delete();
|
await Document.query().where('key', filename).delete();
|
||||||
};
|
};
|
||||||
return [filename, cleanup];
|
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_SUB_DIR = '/pdf';
|
||||||
export const PDF_FILE_EXPIRE_IN = 40; // ms
|
export const PDF_FILE_EXPIRE_IN = 40; // ms
|
||||||
@@ -9,5 +9,6 @@ export const getPdfFilesStorageDir = (filename: string) => {
|
|||||||
|
|
||||||
export const getPdfFilePath = (filename: string) => {
|
export const getPdfFilePath = (filename: string) => {
|
||||||
const storageDir = getPdfFilesStorageDir(filename);
|
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 { 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 { ICreditNoteRefundDTO } from '../CreditNotes/types/CreditNotes.types';
|
||||||
import { CreditNotesRefundsApplication } from './CreditNotesRefundsApplication.service';
|
import { CreditNotesRefundsApplication } from './CreditNotesRefundsApplication.service';
|
||||||
import { RefundCreditNote } from './models/RefundCreditNote';
|
import { RefundCreditNote } from './models/RefundCreditNote';
|
||||||
@@ -12,14 +12,6 @@ export class CreditNoteRefundsController {
|
|||||||
private readonly creditNotesRefundsApplication: CreditNotesRefundsApplication,
|
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.
|
* Create a refund credit note.
|
||||||
* @param {number} creditNoteId - The credit note ID.
|
* @param {number} creditNoteId - The credit note ID.
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { RefundSyncCreditNoteBalanceService } from './commands/RefundSyncCreditN
|
|||||||
import { CreditNotesRefundsApplication } from './CreditNotesRefundsApplication.service';
|
import { CreditNotesRefundsApplication } from './CreditNotesRefundsApplication.service';
|
||||||
import { CreditNoteRefundsController } from './CreditNoteRefunds.controller';
|
import { CreditNoteRefundsController } from './CreditNoteRefunds.controller';
|
||||||
import { CreditNotesModule } from '../CreditNotes/CreditNotes.module';
|
import { CreditNotesModule } from '../CreditNotes/CreditNotes.module';
|
||||||
import { GetCreditNoteRefundsService } from './queries/GetCreditNoteRefunds.service';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [forwardRef(() => CreditNotesModule)],
|
imports: [forwardRef(() => CreditNotesModule)],
|
||||||
@@ -16,9 +15,10 @@ import { GetCreditNoteRefundsService } from './queries/GetCreditNoteRefunds.serv
|
|||||||
RefundCreditNoteService,
|
RefundCreditNoteService,
|
||||||
RefundSyncCreditNoteBalanceService,
|
RefundSyncCreditNoteBalanceService,
|
||||||
CreditNotesRefundsApplication,
|
CreditNotesRefundsApplication,
|
||||||
GetCreditNoteRefundsService,
|
|
||||||
],
|
],
|
||||||
exports: [RefundSyncCreditNoteBalanceService],
|
exports: [
|
||||||
|
RefundSyncCreditNoteBalanceService
|
||||||
|
],
|
||||||
controllers: [CreditNoteRefundsController],
|
controllers: [CreditNoteRefundsController],
|
||||||
})
|
})
|
||||||
export class CreditNoteRefundsModule {}
|
export class CreditNoteRefundsModule {}
|
||||||
|
|||||||
@@ -5,27 +5,16 @@ import { DeleteRefundCreditNoteService } from './commands/DeleteRefundCreditNote
|
|||||||
import { RefundCreditNoteService } from './commands/RefundCreditNote.service';
|
import { RefundCreditNoteService } from './commands/RefundCreditNote.service';
|
||||||
import { RefundSyncCreditNoteBalanceService } from './commands/RefundSyncCreditNoteBalance';
|
import { RefundSyncCreditNoteBalanceService } from './commands/RefundSyncCreditNoteBalance';
|
||||||
import { CreditNoteRefundDto } from './dto/CreditNoteRefund.dto';
|
import { CreditNoteRefundDto } from './dto/CreditNoteRefund.dto';
|
||||||
import { GetCreditNoteRefundsService } from './queries/GetCreditNoteRefunds.service';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CreditNotesRefundsApplication {
|
export class CreditNotesRefundsApplication {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly createRefundCreditNoteService: CreateRefundCreditNoteService,
|
private readonly createRefundCreditNoteService: CreateRefundCreditNoteService,
|
||||||
private readonly deleteRefundCreditNoteService: DeleteRefundCreditNoteService,
|
private readonly deleteRefundCreditNoteService: DeleteRefundCreditNoteService,
|
||||||
private readonly getCreditNoteRefundsService: GetCreditNoteRefundsService,
|
|
||||||
private readonly refundCreditNoteService: RefundCreditNoteService,
|
private readonly refundCreditNoteService: RefundCreditNoteService,
|
||||||
private readonly refundSyncCreditNoteBalanceService: RefundSyncCreditNoteBalanceService,
|
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.
|
* Create a refund credit note.
|
||||||
* @param {number} creditNoteId - The credit note ID.
|
* @param {number} creditNoteId - The credit note ID.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { IRefundCreditNotePOJO } from '../types/CreditNoteRefunds.types';
|
|||||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GetCreditNoteRefundsService {
|
export class ListCreditNoteRefunds {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly transformer: TransformerInjectable,
|
private readonly transformer: TransformerInjectable,
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ export class GetCreditNoteRefundsService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the credit note graph.
|
* Retrieve the credit note graph.
|
||||||
* @param {number} creditNoteId - Credit note id.
|
* @param {number} creditNoteId
|
||||||
* @returns {Promise<IRefundCreditNotePOJO[]>}
|
* @returns {Promise<IRefundCreditNotePOJO[]>}
|
||||||
*/
|
*/
|
||||||
public async getCreditNoteRefunds(
|
public async getCreditNoteRefunds(
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import { GetCreditNotePdf } from './queries/GetCreditNotePdf.serivce';
|
|||||||
import { ICreditNotesQueryDTO } from './types/CreditNotes.types';
|
import { ICreditNotesQueryDTO } from './types/CreditNotes.types';
|
||||||
import { GetCreditNotesService } from './queries/GetCreditNotes.service';
|
import { GetCreditNotesService } from './queries/GetCreditNotes.service';
|
||||||
import { CreateCreditNoteDto, EditCreditNoteDto } from './dtos/CreditNote.dto';
|
import { CreateCreditNoteDto, EditCreditNoteDto } from './dtos/CreditNote.dto';
|
||||||
import { GetCreditNoteState } from './queries/GetCreditNoteState.service';
|
|
||||||
import { GetCreditNoteService } from './queries/GetCreditNote.service';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CreditNoteApplication {
|
export class CreditNoteApplication {
|
||||||
@@ -19,8 +17,6 @@ export class CreditNoteApplication {
|
|||||||
private readonly deleteCreditNoteService: DeleteCreditNoteService,
|
private readonly deleteCreditNoteService: DeleteCreditNoteService,
|
||||||
private readonly getCreditNotePdfService: GetCreditNotePdf,
|
private readonly getCreditNotePdfService: GetCreditNotePdf,
|
||||||
private readonly getCreditNotesService: GetCreditNotesService,
|
private readonly getCreditNotesService: GetCreditNotesService,
|
||||||
private readonly getCreditNoteStateService: GetCreditNoteState,
|
|
||||||
private readonly getCreditNoteService: GetCreditNoteService
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,21 +76,4 @@ export class CreditNoteApplication {
|
|||||||
getCreditNotes(creditNotesQuery: ICreditNotesQueryDTO) {
|
getCreditNotes(creditNotesQuery: ICreditNotesQueryDTO) {
|
||||||
return this.getCreditNotesService.getCreditNotesList(creditNotesQuery);
|
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 {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
@@ -11,6 +10,7 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { CreditNoteApplication } from './CreditNoteApplication.service';
|
import { CreditNoteApplication } from './CreditNoteApplication.service';
|
||||||
import { ICreditNotesQueryDTO } from './types/CreditNotes.types';
|
import { ICreditNotesQueryDTO } from './types/CreditNotes.types';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
import { CreateCreditNoteDto, EditCreditNoteDto } from './dtos/CreditNote.dto';
|
import { CreateCreditNoteDto, EditCreditNoteDto } from './dtos/CreditNote.dto';
|
||||||
|
|
||||||
@Controller('credit-notes')
|
@Controller('credit-notes')
|
||||||
@@ -22,42 +22,16 @@ export class CreditNotesController {
|
|||||||
constructor(private creditNoteApplication: CreditNoteApplication) {}
|
constructor(private creditNoteApplication: CreditNoteApplication) {}
|
||||||
|
|
||||||
@Post()
|
@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) {
|
createCreditNote(@Body() creditNoteDTO: CreateCreditNoteDto) {
|
||||||
return this.creditNoteApplication.createCreditNote(creditNoteDTO);
|
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()
|
@Get()
|
||||||
@ApiOperation({ summary: 'Get all credit notes' })
|
|
||||||
@ApiResponse({ status: 200, description: 'Returns a list of credit notes' })
|
|
||||||
getCreditNotes(@Query() creditNotesQuery: ICreditNotesQueryDTO) {
|
getCreditNotes(@Query() creditNotesQuery: ICreditNotesQueryDTO) {
|
||||||
return this.creditNoteApplication.getCreditNotes(creditNotesQuery);
|
return this.creditNoteApplication.getCreditNotes(creditNotesQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@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(
|
editCreditNote(
|
||||||
@Param('id') creditNoteId: number,
|
@Param('id') creditNoteId: number,
|
||||||
@Body() creditNoteDTO: EditCreditNoteDto,
|
@Body() creditNoteDTO: EditCreditNoteDto,
|
||||||
@@ -69,19 +43,11 @@ export class CreditNotesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id/open')
|
@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) {
|
openCreditNote(@Param('id') creditNoteId: number) {
|
||||||
return this.creditNoteApplication.openCreditNote(creditNoteId);
|
return this.creditNoteApplication.openCreditNote(creditNoteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@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) {
|
deleteCreditNote(@Param('id') creditNoteId: number) {
|
||||||
return this.creditNoteApplication.deleteCreditNote(creditNoteId);
|
return this.creditNoteApplication.deleteCreditNote(creditNoteId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { WarehousesModule } from '../Warehouses/Warehouses.module';
|
|||||||
import { PdfTemplatesModule } from '../PdfTemplate/PdfTemplates.module';
|
import { PdfTemplatesModule } from '../PdfTemplate/PdfTemplates.module';
|
||||||
import { ChromiumlyTenancyModule } from '../ChromiumlyTenancy/ChromiumlyTenancy.module';
|
import { ChromiumlyTenancyModule } from '../ChromiumlyTenancy/ChromiumlyTenancy.module';
|
||||||
import { TemplateInjectableModule } from '../TemplateInjectable/TemplateInjectable.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 { CreditNoteBrandingTemplate } from './queries/CreditNoteBrandingTemplate.service';
|
||||||
import { AutoIncrementOrdersModule } from '../AutoIncrementOrders/AutoIncrementOrders.module';
|
import { AutoIncrementOrdersModule } from '../AutoIncrementOrders/AutoIncrementOrders.module';
|
||||||
import { CreditNoteGLEntries } from './commands/CreditNoteGLEntries';
|
import { CreditNoteGLEntries } from './commands/CreditNoteGLEntries';
|
||||||
@@ -52,7 +52,7 @@ import { CreditNotesApplyInvoiceModule } from '../CreditNotesApplyInvoice/Credit
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
CreateCreditNoteService,
|
CreateCreditNoteService,
|
||||||
GetCreditNoteService,
|
GetCreditNote,
|
||||||
CommandCreditNoteDTOTransform,
|
CommandCreditNoteDTOTransform,
|
||||||
EditCreditNoteService,
|
EditCreditNoteService,
|
||||||
OpenCreditNoteService,
|
OpenCreditNoteService,
|
||||||
@@ -74,7 +74,7 @@ import { CreditNotesApplyInvoiceModule } from '../CreditNotesApplyInvoice/Credit
|
|||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
CreateCreditNoteService,
|
CreateCreditNoteService,
|
||||||
GetCreditNoteService,
|
GetCreditNote,
|
||||||
CommandCreditNoteDTOTransform,
|
CommandCreditNoteDTOTransform,
|
||||||
EditCreditNoteService,
|
EditCreditNoteService,
|
||||||
OpenCreditNoteService,
|
OpenCreditNoteService,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { ToNumber } from '@/common/decorators/Validators';
|
|
||||||
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
|
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
@@ -6,10 +5,9 @@ import {
|
|||||||
ArrayMinSize,
|
ArrayMinSize,
|
||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsDateString,
|
IsDate,
|
||||||
IsEnum,
|
IsEnum,
|
||||||
IsInt,
|
IsInt,
|
||||||
IsNotEmpty,
|
|
||||||
IsNumber,
|
IsNumber,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsPositive,
|
IsPositive,
|
||||||
@@ -27,25 +25,21 @@ export class CreditNoteEntryDto extends ItemEntryDto {}
|
|||||||
|
|
||||||
class AttachmentDto {
|
class AttachmentDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
|
||||||
key: string;
|
key: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommandCreditNoteDto {
|
export class CommandCreditNoteDto {
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsNotEmpty()
|
|
||||||
@ApiProperty({ example: 1, description: 'The customer ID' })
|
@ApiProperty({ example: 1, description: 'The customer ID' })
|
||||||
customerId: number;
|
customerId: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsPositive()
|
@IsPositive()
|
||||||
@ApiProperty({ example: 3.43, description: 'The exchange rate' })
|
@ApiProperty({ example: 3.43, description: 'The exchange rate' })
|
||||||
exchangeRate?: number;
|
exchangeRate?: number;
|
||||||
|
|
||||||
@IsNotEmpty()
|
@IsDate()
|
||||||
@IsDateString()
|
@Type(() => Date)
|
||||||
@ApiProperty({ example: '2021-09-01', description: 'The credit note date' })
|
@ApiProperty({ example: '2021-09-01', description: 'The credit note date' })
|
||||||
creditNoteDate: Date;
|
creditNoteDate: Date;
|
||||||
|
|
||||||
@@ -70,19 +64,26 @@ export class CommandCreditNoteDto {
|
|||||||
termsConditions?: string;
|
termsConditions?: string;
|
||||||
|
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
@ApiProperty({ example: false, description: 'The credit note is open' })
|
@ApiProperty({
|
||||||
|
example: false,
|
||||||
|
description: 'The credit note is open',
|
||||||
|
})
|
||||||
open: boolean = false;
|
open: boolean = false;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@ApiProperty({ example: 1, description: 'The warehouse ID' })
|
@ApiProperty({
|
||||||
|
example: 1,
|
||||||
|
description: 'The warehouse ID',
|
||||||
|
})
|
||||||
warehouseId?: number;
|
warehouseId?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@ApiProperty({ example: 1, description: 'The branch ID' })
|
@ApiProperty({
|
||||||
|
example: 1,
|
||||||
|
description: 'The branch ID',
|
||||||
|
})
|
||||||
branchId?: number;
|
branchId?: number;
|
||||||
|
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@@ -90,7 +91,14 @@ export class CommandCreditNoteDto {
|
|||||||
@Type(() => CreditNoteEntryDto)
|
@Type(() => CreditNoteEntryDto)
|
||||||
@ArrayMinSize(1)
|
@ArrayMinSize(1)
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: [{ itemId: 1, quantity: 1, rate: 10, taxRateId: 1 }],
|
example: [
|
||||||
|
{
|
||||||
|
itemId: 1,
|
||||||
|
quantity: 1,
|
||||||
|
rate: 10,
|
||||||
|
taxRateId: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
description: 'The credit note entries',
|
description: 'The credit note entries',
|
||||||
})
|
})
|
||||||
entries: CreditNoteEntryDto[];
|
entries: CreditNoteEntryDto[];
|
||||||
@@ -102,15 +110,19 @@ export class CommandCreditNoteDto {
|
|||||||
attachments?: AttachmentDto[];
|
attachments?: AttachmentDto[];
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@ApiProperty({ example: 1, description: 'The pdf template ID' })
|
@ApiProperty({
|
||||||
|
example: 1,
|
||||||
|
description: 'The pdf template ID',
|
||||||
|
})
|
||||||
pdfTemplateId?: number;
|
pdfTemplateId?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@ApiProperty({ example: 10, description: 'The discount amount' })
|
@ApiProperty({
|
||||||
|
example: 10,
|
||||||
|
description: 'The discount amount',
|
||||||
|
})
|
||||||
discount?: number;
|
discount?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -123,7 +135,6 @@ export class CommandCreditNoteDto {
|
|||||||
discountType?: DiscountType;
|
discountType?: DiscountType;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
adjustment?: number;
|
adjustment?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { ServiceError } from '@/modules/Items/ServiceError';
|
|||||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GetCreditNoteService {
|
export class GetCreditNote {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly transformer: TransformerInjectable,
|
private readonly transformer: TransformerInjectable,
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { GetCreditNoteService } from './GetCreditNote.service';
|
import { GetCreditNote } from './GetCreditNote.service';
|
||||||
import { CreditNoteBrandingTemplate } from './CreditNoteBrandingTemplate.service';
|
import { CreditNoteBrandingTemplate } from './CreditNoteBrandingTemplate.service';
|
||||||
import { transformCreditNoteToPdfTemplate } from '../utils';
|
import { transformCreditNoteToPdfTemplate } from '../utils';
|
||||||
import { CreditNote } from '../models/CreditNote';
|
import { CreditNote } from '../models/CreditNote';
|
||||||
@@ -25,7 +25,7 @@ export class GetCreditNotePdf {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly chromiumlyTenancy: ChromiumlyTenancy,
|
private readonly chromiumlyTenancy: ChromiumlyTenancy,
|
||||||
private readonly templateInjectable: TemplateInjectable,
|
private readonly templateInjectable: TemplateInjectable,
|
||||||
private readonly getCreditNoteService: GetCreditNoteService,
|
private readonly getCreditNoteService: GetCreditNote,
|
||||||
private readonly creditNoteBrandingTemplate: CreditNoteBrandingTemplate,
|
private readonly creditNoteBrandingTemplate: CreditNoteBrandingTemplate,
|
||||||
private readonly eventPublisher: EventEmitter2,
|
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 { CreditNotesModule } from '../CreditNotes/CreditNotes.module';
|
||||||
import { GetCreditNoteAssociatedAppliedInvoices } from './queries/GetCreditNoteAssociatedAppliedInvoices.service';
|
import { GetCreditNoteAssociatedAppliedInvoices } from './queries/GetCreditNoteAssociatedAppliedInvoices.service';
|
||||||
import { GetCreditNoteAssociatedInvoicesToApply } from './queries/GetCreditNoteAssociatedInvoicesToApply.service';
|
import { GetCreditNoteAssociatedInvoicesToApply } from './queries/GetCreditNoteAssociatedInvoicesToApply.service';
|
||||||
import { CreditNotesApplyInvoiceController } from './CreditNotesApplyInvoice.controller';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
providers: [
|
providers: [
|
||||||
@@ -17,11 +16,12 @@ import { CreditNotesApplyInvoiceController } from './CreditNotesApplyInvoice.con
|
|||||||
CreditNoteApplyToInvoices,
|
CreditNoteApplyToInvoices,
|
||||||
CreditNoteApplySyncInvoicesCreditedAmount,
|
CreditNoteApplySyncInvoicesCreditedAmount,
|
||||||
CreditNoteApplySyncCredit,
|
CreditNoteApplySyncCredit,
|
||||||
GetCreditNoteAssociatedAppliedInvoices,
|
// GetCreditNoteAssociatedAppliedInvoices,
|
||||||
GetCreditNoteAssociatedInvoicesToApply,
|
// GetCreditNoteAssociatedInvoicesToApply
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
DeleteCustomerLinkedCreditNoteService,
|
||||||
],
|
],
|
||||||
exports: [DeleteCustomerLinkedCreditNoteService],
|
|
||||||
imports: [PaymentsReceivedModule, forwardRef(() => CreditNotesModule)],
|
imports: [PaymentsReceivedModule, forwardRef(() => CreditNotesModule)],
|
||||||
controllers: [CreditNotesApplyInvoiceController],
|
|
||||||
})
|
})
|
||||||
export class CreditNotesApplyInvoiceModule {}
|
export class CreditNotesApplyInvoiceModule {}
|
||||||
|
|||||||
@@ -7,16 +7,9 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GetCreditNoteAssociatedAppliedInvoices {
|
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(
|
constructor(
|
||||||
private readonly transformer: TransformerInjectable,
|
private readonly transformer: TransformerInjectable,
|
||||||
|
private readonly creditNoteAppliedInvoiceModel: typeof CreditNoteAppliedInvoice,
|
||||||
@Inject(CreditNoteAppliedInvoice.name)
|
|
||||||
private readonly creditNoteAppliedInvoiceModel: TenantModelProxy<typeof CreditNoteAppliedInvoice>,
|
|
||||||
|
|
||||||
@Inject(CreditNote.name)
|
@Inject(CreditNote.name)
|
||||||
private readonly creditNoteModel: TenantModelProxy<typeof CreditNote>,
|
private readonly creditNoteModel: TenantModelProxy<typeof CreditNote>,
|
||||||
@@ -36,7 +29,7 @@ export class GetCreditNoteAssociatedAppliedInvoices {
|
|||||||
.findById(creditNoteId)
|
.findById(creditNoteId)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
const appliedToInvoices = await this.creditNoteAppliedInvoiceModel()
|
const appliedToInvoices = await this.creditNoteAppliedInvoiceModel
|
||||||
.query()
|
.query()
|
||||||
.where('credit_note_id', creditNoteId)
|
.where('credit_note_id', creditNoteId)
|
||||||
.withGraphFetched('saleInvoice')
|
.withGraphFetched('saleInvoice')
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||||
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
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 { CreditNoteWithInvoicesToApplyTransformer } from './CreditNoteWithInvoicesToApplyTransformer';
|
||||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
|
||||||
@@ -10,11 +10,11 @@ export class GetCreditNoteAssociatedInvoicesToApply {
|
|||||||
/**
|
/**
|
||||||
* @param {TransformerInjectable} transformer - Transformer service.
|
* @param {TransformerInjectable} transformer - Transformer service.
|
||||||
* @param {GetCreditNote} getCreditNote - Get credit note service.
|
* @param {GetCreditNote} getCreditNote - Get credit note service.
|
||||||
* @param {TenantModelProxy<typeof SaleInvoice>} saleInvoiceModel - Sale invoice model.
|
* @param {typeof SaleInvoice} saleInvoiceModel - Sale invoice model.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private transformer: TransformerInjectable,
|
private transformer: TransformerInjectable,
|
||||||
private getCreditNote: GetCreditNoteService,
|
private getCreditNote: GetCreditNote,
|
||||||
|
|
||||||
@Inject(SaleInvoice.name)
|
@Inject(SaleInvoice.name)
|
||||||
private saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
private saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
||||||
|
|||||||
@@ -37,15 +37,15 @@ export class CurrenciesController {
|
|||||||
return this.currenciesApp.createCurrency(dto);
|
return this.currenciesApp.createCurrency(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':code')
|
||||||
@ApiOperation({ summary: 'Edit an existing currency' })
|
@ApiOperation({ summary: 'Edit an existing currency' })
|
||||||
@ApiParam({ name: 'id', type: Number, description: 'Currency ID' })
|
@ApiParam({ name: 'id', type: Number, description: 'Currency ID' })
|
||||||
@ApiBody({ type: EditCurrencyDto })
|
@ApiBody({ type: EditCurrencyDto })
|
||||||
@ApiOkResponse({ description: 'The currency has been successfully updated.' })
|
@ApiOkResponse({ description: 'The currency has been successfully updated.' })
|
||||||
@ApiNotFoundResponse({ description: 'Currency not found.' })
|
@ApiNotFoundResponse({ description: 'Currency not found.' })
|
||||||
@ApiBadRequestResponse({ description: 'Invalid input data.' })
|
@ApiBadRequestResponse({ description: 'Invalid input data.' })
|
||||||
edit(@Param('id') id: number, @Body() dto: EditCurrencyDto) {
|
edit(@Param('code') code: string, @Body() dto: EditCurrencyDto) {
|
||||||
return this.currenciesApp.editCurrency(id, dto);
|
return this.currenciesApp.editCurrency(code, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':code')
|
@Delete(':code')
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ export class CurrenciesApplication {
|
|||||||
/**
|
/**
|
||||||
* Edits an existing currency.
|
* Edits an existing currency.
|
||||||
*/
|
*/
|
||||||
public editCurrency(currencyId: number, currencyDTO: EditCurrencyDto) {
|
public editCurrency(currencyCode: string, currencyDTO: EditCurrencyDto) {
|
||||||
return this.editCurrencyService.editCurrency(currencyId, currencyDTO);
|
return this.editCurrencyService.editCurrency(currencyCode, currencyDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -12,22 +12,21 @@ export class EditCurrencyService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit details of the given currency.
|
* Edit details of the given currency.
|
||||||
* @param {number} currencyId - Currency ID.
|
* @param {number} currencyCode - Currency code.
|
||||||
* @param {ICurrencyDTO} currencyDTO - Edit currency dto.
|
* @param {ICurrencyDTO} currencyDTO - Edit currency dto.
|
||||||
*/
|
*/
|
||||||
public async editCurrency(
|
public async editCurrency(
|
||||||
currencyId: number,
|
currencyCode: string,
|
||||||
currencyDTO: EditCurrencyDto,
|
currencyDTO: EditCurrencyDto,
|
||||||
): Promise<Currency> {
|
): Promise<Currency> {
|
||||||
const foundCurrency = this.currencyModel()
|
const foundCurrency = await this.currencyModel()
|
||||||
.query()
|
.query()
|
||||||
.findById(currencyId)
|
.findOne('currencyCode', currencyCode)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
// Directly use the provided ID to update the currency
|
|
||||||
const currency = await this.currencyModel()
|
const currency = await this.currencyModel()
|
||||||
.query()
|
.query()
|
||||||
.patchAndFetchById(currencyId, {
|
.patchAndFetchById(foundCurrency.id, {
|
||||||
...currencyDTO,
|
...currencyDTO,
|
||||||
});
|
});
|
||||||
return currency;
|
return currency;
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
import { IsNotEmpty } from "class-validator";
|
import { IsString } from 'class-validator';
|
||||||
import { IsString } from "class-validator";
|
|
||||||
|
|
||||||
export class CreateCurrencyDto {
|
export class CreateCurrencyDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
|
||||||
currencyName: string;
|
currencyName: string;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
|
||||||
currencyCode: string;
|
currencyCode: string;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
|
||||||
currencySign: string;
|
currencySign: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import { IsNotEmpty } from "class-validator";
|
import { IsString } from 'class-validator';
|
||||||
import { IsString } from "class-validator";
|
|
||||||
|
|
||||||
export class EditCurrencyDto {
|
export class EditCurrencyDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
|
||||||
currencyName: string;
|
currencyName: string;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
|
||||||
currencySign: string;
|
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 currencySign: string;
|
||||||
public readonly currencyName: string;
|
public readonly currencyName: string;
|
||||||
public readonly currencyCode: string;
|
public readonly currencyCode: string;
|
||||||
|
|||||||
@@ -394,5 +394,7 @@ export abstract class DynamicFilterRoleAbstractor implements IDynamicFilter {
|
|||||||
/**
|
/**
|
||||||
* Retrieves the response meta.
|
* 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 { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsDateString,
|
IsDate,
|
||||||
IsInt,
|
IsInt,
|
||||||
IsISO4217CurrencyCode,
|
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsNumber,
|
IsNumber,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
@@ -25,12 +23,10 @@ export class ExpenseCategoryDto {
|
|||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
index: number;
|
index: number;
|
||||||
|
|
||||||
@IsNotEmpty()
|
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
|
@IsNotEmpty()
|
||||||
expenseAccountId: number;
|
expenseAccountId: number;
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
amount?: number;
|
amount?: number;
|
||||||
@@ -44,7 +40,6 @@ export class ExpenseCategoryDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
landedCost?: boolean;
|
landedCost?: boolean;
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
projectId?: number;
|
projectId?: number;
|
||||||
@@ -60,7 +55,7 @@ export class CommandExpenseDto {
|
|||||||
})
|
})
|
||||||
referenceNo?: string;
|
referenceNo?: string;
|
||||||
|
|
||||||
@IsDateString()
|
@IsDate()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The payment date of the expense',
|
description: 'The payment date of the expense',
|
||||||
@@ -68,9 +63,8 @@ export class CommandExpenseDto {
|
|||||||
})
|
})
|
||||||
paymentDate: Date;
|
paymentDate: Date;
|
||||||
|
|
||||||
@IsNotEmpty()
|
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
|
@IsNotEmpty()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The payment account id of the expense',
|
description: 'The payment account id of the expense',
|
||||||
example: 1,
|
example: 1,
|
||||||
@@ -86,22 +80,31 @@ export class CommandExpenseDto {
|
|||||||
})
|
})
|
||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ApiProperty({ description: 'The exchange rate of the expense', example: 1 })
|
@ApiProperty({
|
||||||
|
description: 'The exchange rate of the expense',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
exchangeRate?: number;
|
exchangeRate?: number;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@MaxLength(3)
|
@MaxLength(3)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsISO4217CurrencyCode()
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The currency code of the expense',
|
description: 'The currency code of the expense',
|
||||||
example: 'USD',
|
example: 'USD',
|
||||||
})
|
})
|
||||||
currencyCode?: string;
|
currencyCode?: string;
|
||||||
|
|
||||||
|
@IsNumber()
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'The exchange rate of the expense',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
|
exchange_rate?: number;
|
||||||
|
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
@@ -110,16 +113,14 @@ export class CommandExpenseDto {
|
|||||||
})
|
})
|
||||||
publish?: boolean;
|
publish?: boolean;
|
||||||
|
|
||||||
@IsOptional()
|
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
|
@IsOptional()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The payee id of the expense',
|
description: 'The payee id of the expense',
|
||||||
example: 1,
|
example: 1,
|
||||||
})
|
})
|
||||||
payeeId?: number;
|
payeeId?: number;
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export class FeaturesConfigure {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: Features.BankSyncing,
|
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()
|
@Injectable()
|
||||||
export class ImportFileMeta {
|
export class ImportFileMeta {
|
||||||
/**
|
|
||||||
* @param {TransformerInjectable} transformer - Transformer injectable service.
|
|
||||||
* @param {TenancyContext} tenancyContext - Tenancy context service.
|
|
||||||
* @param {typeof ImportModel} importModel - Import model.
|
|
||||||
*/
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly transformer: TransformerInjectable,
|
private readonly transformer: TransformerInjectable,
|
||||||
private readonly tenancyContext: TenancyContext,
|
private readonly tenancyContext: TenancyContext,
|
||||||
|
|
||||||
@Inject(ImportModel.name)
|
@Inject(ImportModel.name)
|
||||||
private readonly importModel: typeof ImportModel,
|
private readonly importModel: () => typeof ImportModel,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the import meta of the given import model id.
|
* Retrieves the import meta of the given import model id.
|
||||||
* @param {string} importId - Import id.
|
* @param {number} importId
|
||||||
*/
|
*/
|
||||||
async getImportMeta(importId: string) {
|
async getImportMeta(importId: string) {
|
||||||
const tenant = await this.tenancyContext.getTenant();
|
const tenant = await this.tenancyContext.getTenant();
|
||||||
const tenantId = tenant.id;
|
const tenantId = tenant.id;
|
||||||
|
|
||||||
const importFile = await this.importModel
|
const importFile = await this.importModel()
|
||||||
.query()
|
.query()
|
||||||
.where('tenantId', tenantId)
|
.where('tenantId', tenantId)
|
||||||
.findOne('importId', importId);
|
.findOne('importId', importId);
|
||||||
|
|||||||
@@ -74,10 +74,11 @@ export class EditItemCategoryService {
|
|||||||
);
|
);
|
||||||
// Creates item category under unit-of-work evnirement.
|
// Creates item category under unit-of-work evnirement.
|
||||||
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
const itemCategory = await this.itemCategoryModel()
|
//
|
||||||
.query(trx)
|
const itemCategory = await ItemCategory.query().patchAndFetchById(
|
||||||
.patchAndFetchById(itemCategoryId, { ...itemCategoryObj });
|
itemCategoryId,
|
||||||
|
{ ...itemCategoryObj },
|
||||||
|
);
|
||||||
// Triggers `onItemCategoryEdited` event.
|
// Triggers `onItemCategoryEdited` event.
|
||||||
await this.eventEmitter.emitAsync(events.itemCategory.onEdited, {
|
await this.eventEmitter.emitAsync(events.itemCategory.onEdited, {
|
||||||
oldItemCategory,
|
oldItemCategory,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { IsOptional, ToNumber } from '@/common/decorators/Validators';
|
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsNumber } from 'class-validator';
|
import { IsNumber } from 'class-validator';
|
||||||
import { IsString } from 'class-validator';
|
import { IsOptional, IsString } from 'class-validator';
|
||||||
import { IsNotEmpty } from 'class-validator';
|
import { IsNotEmpty } from 'class-validator';
|
||||||
|
|
||||||
class CommandItemCategoryDto {
|
class CommandItemCategoryDto {
|
||||||
@@ -18,19 +17,16 @@ class CommandItemCategoryDto {
|
|||||||
})
|
})
|
||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ApiProperty({ example: 1, description: 'The cost account ID' })
|
@ApiProperty({ example: 1, description: 'The cost account ID' })
|
||||||
costAccountId?: number;
|
costAccountId?: number;
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ApiProperty({ example: 1, description: 'The sell account ID' })
|
@ApiProperty({ example: 1, description: 'The sell account ID' })
|
||||||
sellAccountId?: number;
|
sellAccountId?: number;
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ApiProperty({ example: 1, description: 'The inventory account ID' })
|
@ApiProperty({ example: 1, description: 'The inventory account ID' })
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
IsString,
|
IsString,
|
||||||
IsIn,
|
IsIn,
|
||||||
|
IsOptional,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsNumber,
|
IsNumber,
|
||||||
IsInt,
|
IsInt,
|
||||||
@@ -8,10 +9,11 @@ import {
|
|||||||
ValidateIf,
|
ValidateIf,
|
||||||
MaxLength,
|
MaxLength,
|
||||||
Min,
|
Min,
|
||||||
|
Max,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsOptional, ToNumber } from '@/common/decorators/Validators';
|
|
||||||
|
|
||||||
export class CommandItemDto {
|
export class CommandItemDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@@ -21,7 +23,6 @@ export class CommandItemDto {
|
|||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
|
||||||
@IsIn(['service', 'non-inventory', 'inventory'])
|
@IsIn(['service', 'non-inventory', 'inventory'])
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Item type',
|
description: 'Item type',
|
||||||
@@ -51,7 +52,6 @@ export class CommandItemDto {
|
|||||||
purchasable?: boolean;
|
purchasable?: boolean;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsNumber({ maxDecimalPlaces: 3 })
|
@IsNumber({ maxDecimalPlaces: 3 })
|
||||||
@Min(0)
|
@Min(0)
|
||||||
@ValidateIf((o) => o.purchasable === true)
|
@ValidateIf((o) => o.purchasable === true)
|
||||||
@@ -64,7 +64,6 @@ export class CommandItemDto {
|
|||||||
costPrice?: number;
|
costPrice?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(0)
|
@Min(0)
|
||||||
@ValidateIf((o) => o.purchasable === true)
|
@ValidateIf((o) => o.purchasable === true)
|
||||||
@@ -87,7 +86,6 @@ export class CommandItemDto {
|
|||||||
sellable?: boolean;
|
sellable?: boolean;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsNumber({ maxDecimalPlaces: 3 })
|
@IsNumber({ maxDecimalPlaces: 3 })
|
||||||
@Min(0)
|
@Min(0)
|
||||||
@ValidateIf((o) => o.sellable === true)
|
@ValidateIf((o) => o.sellable === true)
|
||||||
@@ -100,7 +98,6 @@ export class CommandItemDto {
|
|||||||
sellPrice?: number;
|
sellPrice?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(0)
|
@Min(0)
|
||||||
@ValidateIf((o) => o.sellable === true)
|
@ValidateIf((o) => o.sellable === true)
|
||||||
@@ -113,7 +110,6 @@ export class CommandItemDto {
|
|||||||
sellAccountId?: number;
|
sellAccountId?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(0)
|
@Min(0)
|
||||||
@ValidateIf((o) => o.type === 'inventory')
|
@ValidateIf((o) => o.type === 'inventory')
|
||||||
@@ -144,7 +140,6 @@ export class CommandItemDto {
|
|||||||
purchaseDescription?: string;
|
purchaseDescription?: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'ID of the tax rate applied to sales',
|
description: 'ID of the tax rate applied to sales',
|
||||||
@@ -154,7 +149,6 @@ export class CommandItemDto {
|
|||||||
sellTaxRateId?: number;
|
sellTaxRateId?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'ID of the tax rate applied to purchases',
|
description: 'ID of the tax rate applied to purchases',
|
||||||
@@ -164,7 +158,6 @@ export class CommandItemDto {
|
|||||||
purchaseTaxRateId?: number;
|
purchaseTaxRateId?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(0)
|
@Min(0)
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
@@ -196,6 +189,7 @@ export class CommandItemDto {
|
|||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsArray()
|
@IsArray()
|
||||||
|
@Type(() => Number)
|
||||||
@IsInt({ each: true })
|
@IsInt({ each: true })
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'IDs of media files associated with the item',
|
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 { InjectModelMeta } from '@/modules/Tenancy/TenancyModels/decorators/InjectModelMeta.decorator';
|
||||||
import { ItemMeta } from './Item.meta';
|
import { ItemMeta } from './Item.meta';
|
||||||
import { ImportableModel } from '@/modules/Import/decorators/Import.decorator';
|
import { ImportableModel } from '@/modules/Import/decorators/Import.decorator';
|
||||||
import { PreventMutateBaseCurrency } from '@/common/decorators/LockMutateBaseCurrency.decorator';
|
|
||||||
|
|
||||||
@ExportableModel()
|
@ExportableModel()
|
||||||
@ImportableModel()
|
@ImportableModel()
|
||||||
@InjectModelMeta(ItemMeta)
|
@InjectModelMeta(ItemMeta)
|
||||||
@PreventMutateBaseCurrency()
|
|
||||||
export class Item extends TenantBaseModel {
|
export class Item extends TenantBaseModel {
|
||||||
public readonly quantityOnHand: number;
|
public readonly quantityOnHand: number;
|
||||||
public readonly name: string;
|
public readonly name: string;
|
||||||
|
|||||||
@@ -108,7 +108,11 @@ export class ManualJournalsController {
|
|||||||
description: 'The manual journal details have been successfully retrieved.',
|
description: 'The manual journal details have been successfully retrieved.',
|
||||||
})
|
})
|
||||||
@ApiResponse({ status: 404, description: 'The manual journal not found.' })
|
@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);
|
return this.manualJournalsApplication.getManualJournals(filterDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import { ToNumber } from '@/common/decorators/Validators';
|
|
||||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsDate,
|
IsDate,
|
||||||
IsDateString,
|
|
||||||
IsInt,
|
IsInt,
|
||||||
IsNotEmpty,
|
|
||||||
IsNumber,
|
IsNumber,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsPositive,
|
IsPositive,
|
||||||
@@ -23,21 +20,18 @@ export class ManualJournalEntryDto {
|
|||||||
index: number;
|
index: number;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'Credit amount' })
|
@ApiPropertyOptional({ description: 'Credit amount' })
|
||||||
@ToNumber()
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@Min(0)
|
@Min(0)
|
||||||
credit?: number;
|
credit?: number;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'Debit amount' })
|
@ApiPropertyOptional({ description: 'Debit amount' })
|
||||||
@ToNumber()
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@Min(0)
|
@Min(0)
|
||||||
debit?: number;
|
debit?: number;
|
||||||
|
|
||||||
@ApiProperty({ description: 'Account ID' })
|
@ApiProperty({ description: 'Account ID' })
|
||||||
@IsNotEmpty()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
accountId: number;
|
accountId: number;
|
||||||
|
|
||||||
@@ -47,19 +41,16 @@ export class ManualJournalEntryDto {
|
|||||||
note?: string;
|
note?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'Contact ID' })
|
@ApiPropertyOptional({ description: 'Contact ID' })
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@IsInt()
|
||||||
contactId?: number;
|
contactId?: number;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'Branch ID' })
|
@ApiPropertyOptional({ description: 'Branch ID' })
|
||||||
@ToNumber()
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsInt()
|
@IsInt()
|
||||||
branchId?: number;
|
branchId?: number;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'Project ID' })
|
@ApiPropertyOptional({ description: 'Project ID' })
|
||||||
@ToNumber()
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsInt()
|
@IsInt()
|
||||||
projectId?: number;
|
projectId?: number;
|
||||||
@@ -73,7 +64,8 @@ class AttachmentDto {
|
|||||||
|
|
||||||
export class CommandManualJournalDto {
|
export class CommandManualJournalDto {
|
||||||
@ApiProperty({ description: 'Journal date' })
|
@ApiProperty({ description: 'Journal date' })
|
||||||
@IsDateString()
|
@IsDate()
|
||||||
|
@Type(() => Date)
|
||||||
date: Date;
|
date: Date;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'Currency code' })
|
@ApiPropertyOptional({ description: 'Currency code' })
|
||||||
@@ -82,7 +74,6 @@ export class CommandManualJournalDto {
|
|||||||
currencyCode?: string;
|
currencyCode?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'Exchange rate' })
|
@ApiPropertyOptional({ description: 'Exchange rate' })
|
||||||
@ToNumber()
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsPositive()
|
@IsPositive()
|
||||||
@@ -112,7 +103,6 @@ export class CommandManualJournalDto {
|
|||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'Branch ID' })
|
@ApiPropertyOptional({ description: 'Branch ID' })
|
||||||
@ToNumber()
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsInt()
|
@IsInt()
|
||||||
branchId?: number;
|
branchId?: number;
|
||||||
|
|||||||
@@ -11,9 +11,13 @@ import {
|
|||||||
Put,
|
Put,
|
||||||
Get,
|
Get,
|
||||||
Body,
|
Body,
|
||||||
|
Req,
|
||||||
|
Res,
|
||||||
|
Next,
|
||||||
HttpCode,
|
HttpCode,
|
||||||
Param,
|
Param,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
import { BuildOrganizationService } from './commands/BuildOrganization.service';
|
import { BuildOrganizationService } from './commands/BuildOrganization.service';
|
||||||
import {
|
import {
|
||||||
BuildOrganizationDto,
|
BuildOrganizationDto,
|
||||||
@@ -84,7 +88,7 @@ export class OrganizationController {
|
|||||||
const abilities =
|
const abilities =
|
||||||
await this.orgBaseCurrencyLockingService.baseCurrencyMutateLocks();
|
await this.orgBaseCurrencyLockingService.baseCurrencyMutateLocks();
|
||||||
|
|
||||||
return { abilities };
|
return res.status(200).send({ abilities });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put()
|
@Put()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { isEmpty } from 'lodash';
|
// @ts-nocheck
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { getPreventMutateBaseCurrencyModels } from '@/common/decorators/LockMutateBaseCurrency.decorator';
|
import { isEmpty } from 'lodash';
|
||||||
import { ModuleRef } from '@nestjs/core';
|
|
||||||
|
|
||||||
interface MutateBaseCurrencyLockMeta {
|
interface MutateBaseCurrencyLockMeta {
|
||||||
modelName: string;
|
modelName: string;
|
||||||
@@ -10,27 +9,26 @@ interface MutateBaseCurrencyLockMeta {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OrganizationBaseCurrencyLocking {
|
export class OrganizationBaseCurrencyLocking {
|
||||||
constructor(private readonly moduleRef: ModuleRef) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the tenant models that have prevented mutation base currency.
|
* Retrieves the tenant models that have prevented mutation base currency.
|
||||||
*/
|
*/
|
||||||
private getModelsPreventsMutate() {
|
private getModelsPreventsMutate = (tenantId: number) => {
|
||||||
const lockedModels = getPreventMutateBaseCurrencyModels();
|
const Models = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const filteredEntries = Array.from(lockedModels).filter(
|
const filteredEntries = Object.entries(Models).filter(
|
||||||
([key, Model]) => !!Model.preventMutateBaseCurrency,
|
([key, Model]) => !!Model.preventMutateBaseCurrency,
|
||||||
);
|
);
|
||||||
return Object.fromEntries(filteredEntries);
|
return Object.fromEntries(filteredEntries);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detarmines the mutation base currency model is locked.
|
* Detarmines the mutation base currency model is locked.
|
||||||
|
* @param {Model} Model
|
||||||
* @returns {Promise<MutateBaseCurrencyLockMeta | false>}
|
* @returns {Promise<MutateBaseCurrencyLockMeta | false>}
|
||||||
*/
|
*/
|
||||||
private async isModelMutateLocked(
|
private isModelMutateLocked = async (
|
||||||
Model,
|
Model,
|
||||||
): Promise<MutateBaseCurrencyLockMeta | false> {
|
): Promise<MutateBaseCurrencyLockMeta | false> => {
|
||||||
const validateQuery = Model.query();
|
const validateQuery = Model.query();
|
||||||
|
|
||||||
if (typeof Model?.modifiers?.preventMutateBaseCurrency !== 'undefined') {
|
if (typeof Model?.modifiers?.preventMutateBaseCurrency !== 'undefined') {
|
||||||
@@ -47,24 +45,20 @@ export class OrganizationBaseCurrencyLocking {
|
|||||||
pluralName: Model.pluralName,
|
pluralName: Model.pluralName,
|
||||||
}
|
}
|
||||||
: false;
|
: false;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the base currency mutation locks of the tenant models.
|
* Retrieves the base currency mutation locks of the tenant models.
|
||||||
|
* @param {number} tenantId
|
||||||
* @returns {Promise<MutateBaseCurrencyLockMeta[]>}
|
* @returns {Promise<MutateBaseCurrencyLockMeta[]>}
|
||||||
*/
|
*/
|
||||||
public async baseCurrencyMutateLocks(): Promise<
|
public async baseCurrencyMutateLocks(
|
||||||
MutateBaseCurrencyLockMeta[]
|
): Promise<MutateBaseCurrencyLockMeta[]> {
|
||||||
> {
|
const PreventedModels = this.getModelsPreventsMutate(tenantId);
|
||||||
const PreventedModels = this.getModelsPreventsMutate();
|
|
||||||
const opers = Object.entries(PreventedModels).map(([ModelName, Model]) => {
|
|
||||||
const InjectedModelProxy = this.moduleRef.get(ModelName, {
|
|
||||||
strict: false,
|
|
||||||
});
|
|
||||||
const InjectedModel = InjectedModelProxy();
|
|
||||||
|
|
||||||
return this.isModelMutateLocked(InjectedModel);
|
const opers = Object.entries(PreventedModels).map(([ModelName, Model]) =>
|
||||||
});
|
this.isModelMutateLocked(Model),
|
||||||
|
);
|
||||||
const results = await Promise.all(opers);
|
const results = await Promise.all(opers);
|
||||||
|
|
||||||
return results.filter(
|
return results.filter(
|
||||||
@@ -74,11 +68,12 @@ export class OrganizationBaseCurrencyLocking {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Detarmines the base currency mutation locked.
|
* Detarmines the base currency mutation locked.
|
||||||
|
* @param {number} tenantId
|
||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
public async isBaseCurrencyMutateLocked() {
|
public isBaseCurrencyMutateLocked = async (tenantId: number) => {
|
||||||
const locks = await this.baseCurrencyMutateLocks();
|
const locks = await this.baseCurrencyMutateLocks(tenantId);
|
||||||
|
|
||||||
return !isEmpty(locks);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,9 @@ export class CommandOrganizationValidators {
|
|||||||
) {
|
) {
|
||||||
if (tenant.isReady && newBaseCurrency !== oldBaseCurrency) {
|
if (tenant.isReady && newBaseCurrency !== oldBaseCurrency) {
|
||||||
const isLocked =
|
const isLocked =
|
||||||
await this.baseCurrencyMutateLocking.isBaseCurrencyMutateLocked();
|
await this.baseCurrencyMutateLocking.isBaseCurrencyMutateLocked(
|
||||||
|
tenant.id,
|
||||||
|
);
|
||||||
|
|
||||||
if (isLocked) {
|
if (isLocked) {
|
||||||
throw new ServiceError(ERRORS.BASE_CURRENCY_MUTATE_LOCKED);
|
throw new ServiceError(ERRORS.BASE_CURRENCY_MUTATE_LOCKED);
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import {
|
|||||||
CreatePaymentReceivedDto,
|
CreatePaymentReceivedDto,
|
||||||
EditPaymentReceivedDto,
|
EditPaymentReceivedDto,
|
||||||
} from './dtos/PaymentReceived.dto';
|
} from './dtos/PaymentReceived.dto';
|
||||||
import { PaymentsReceivedPagesService } from './queries/PaymentsReceivedPages.service';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PaymentReceivesApplication {
|
export class PaymentReceivesApplication {
|
||||||
@@ -32,7 +31,6 @@ export class PaymentReceivesApplication {
|
|||||||
private sendPaymentReceiveMailNotification: SendPaymentReceiveMailNotification,
|
private sendPaymentReceiveMailNotification: SendPaymentReceiveMailNotification,
|
||||||
private getPaymentReceivePdfService: GetPaymentReceivedPdfService,
|
private getPaymentReceivePdfService: GetPaymentReceivedPdfService,
|
||||||
private getPaymentReceivedStateService: GetPaymentReceivedStateService,
|
private getPaymentReceivedStateService: GetPaymentReceivedStateService,
|
||||||
private paymentsReceivedPagesService: PaymentsReceivedPagesService,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -149,14 +147,4 @@ export class PaymentReceivesApplication {
|
|||||||
public getPaymentReceivedState() {
|
public getPaymentReceivedState() {
|
||||||
return this.getPaymentReceivedStateService.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,
|
Controller,
|
||||||
Delete,
|
Delete,
|
||||||
Get,
|
Get,
|
||||||
Headers,
|
|
||||||
HttpCode,
|
HttpCode,
|
||||||
Param,
|
Param,
|
||||||
ParseIntPipe,
|
ParseIntPipe,
|
||||||
@@ -14,14 +13,11 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { PaymentReceivesApplication } from './PaymentReceived.application';
|
import { PaymentReceivesApplication } from './PaymentReceived.application';
|
||||||
import {
|
import {
|
||||||
|
IPaymentReceivedCreateDTO,
|
||||||
|
IPaymentReceivedEditDTO,
|
||||||
IPaymentsReceivedFilter,
|
IPaymentsReceivedFilter,
|
||||||
PaymentReceiveMailOptsDTO,
|
PaymentReceiveMailOptsDTO,
|
||||||
} from './types/PaymentReceived.types';
|
} from './types/PaymentReceived.types';
|
||||||
import {
|
|
||||||
CreatePaymentReceivedDto,
|
|
||||||
EditPaymentReceivedDto,
|
|
||||||
} from './dtos/PaymentReceived.dto';
|
|
||||||
import { AcceptType } from '@/constants/accept-type';
|
|
||||||
|
|
||||||
@Controller('payments-received')
|
@Controller('payments-received')
|
||||||
@ApiTags('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')
|
@Get(':id/mail')
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -75,7 +57,7 @@ export class PaymentReceivesController {
|
|||||||
@Post()
|
@Post()
|
||||||
@ApiOperation({ summary: 'Create a new payment received.' })
|
@ApiOperation({ summary: 'Create a new payment received.' })
|
||||||
public createPaymentReceived(
|
public createPaymentReceived(
|
||||||
@Body() paymentReceiveDTO: CreatePaymentReceivedDto,
|
@Body() paymentReceiveDTO: IPaymentReceivedCreateDTO,
|
||||||
) {
|
) {
|
||||||
return this.paymentReceivesApplication.createPaymentReceived(
|
return this.paymentReceivesApplication.createPaymentReceived(
|
||||||
paymentReceiveDTO,
|
paymentReceiveDTO,
|
||||||
@@ -86,7 +68,7 @@ export class PaymentReceivesController {
|
|||||||
@ApiOperation({ summary: 'Edit the given payment received.' })
|
@ApiOperation({ summary: 'Edit the given payment received.' })
|
||||||
public editPaymentReceive(
|
public editPaymentReceive(
|
||||||
@Param('id', ParseIntPipe) paymentReceiveId: number,
|
@Param('id', ParseIntPipe) paymentReceiveId: number,
|
||||||
@Body() paymentReceiveDTO: EditPaymentReceivedDto,
|
@Body() paymentReceiveDTO: IPaymentReceivedEditDTO,
|
||||||
) {
|
) {
|
||||||
return this.paymentReceivesApplication.editPaymentReceive(
|
return this.paymentReceivesApplication.editPaymentReceive(
|
||||||
paymentReceiveId,
|
paymentReceiveId,
|
||||||
@@ -106,9 +88,7 @@ export class PaymentReceivesController {
|
|||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: 'Retrieves the payment received list.' })
|
@ApiOperation({ summary: 'Retrieves the payment received list.' })
|
||||||
public getPaymentsReceived(
|
public getPaymentsReceived(@Query() filterDTO: Partial<IPaymentsReceivedFilter>) {
|
||||||
@Query() filterDTO: Partial<IPaymentsReceivedFilter>,
|
|
||||||
) {
|
|
||||||
return this.paymentReceivesApplication.getPaymentsReceived(filterDTO);
|
return this.paymentReceivesApplication.getPaymentsReceived(filterDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,16 +126,21 @@ export class PaymentReceivesController {
|
|||||||
})
|
})
|
||||||
public getPaymentReceive(
|
public getPaymentReceive(
|
||||||
@Param('id', ParseIntPipe) paymentReceiveId: number,
|
@Param('id', ParseIntPipe) paymentReceiveId: number,
|
||||||
@Headers('accept') acceptHeader: string,
|
|
||||||
) {
|
) {
|
||||||
if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
|
return this.paymentReceivesApplication.getPaymentReceive(paymentReceiveId);
|
||||||
return this.paymentReceivesApplication.getPaymentReceivePdf(
|
}
|
||||||
paymentReceiveId,
|
|
||||||
);
|
@Get(':id/pdf')
|
||||||
} else {
|
@ApiOperation({ summary: 'Retrieves the payment received pdf.' })
|
||||||
return this.paymentReceivesApplication.getPaymentReceive(
|
@ApiResponse({
|
||||||
paymentReceiveId,
|
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 { SEND_PAYMENT_RECEIVED_MAIL_QUEUE } from './constants';
|
||||||
import { PaymentsReceivedExportable } from './commands/PaymentsReceivedExportable';
|
import { PaymentsReceivedExportable } from './commands/PaymentsReceivedExportable';
|
||||||
import { PaymentsReceivedImportable } from './commands/PaymentsReceivedImportable';
|
import { PaymentsReceivedImportable } from './commands/PaymentsReceivedImportable';
|
||||||
import { PaymentsReceivedPagesService } from './queries/PaymentsReceivedPages.service';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [PaymentReceivesController],
|
controllers: [PaymentReceivesController],
|
||||||
@@ -64,7 +63,6 @@ import { PaymentsReceivedPagesService } from './queries/PaymentsReceivedPages.se
|
|||||||
SendPaymentReceivedMailProcessor,
|
SendPaymentReceivedMailProcessor,
|
||||||
PaymentsReceivedExportable,
|
PaymentsReceivedExportable,
|
||||||
PaymentsReceivedImportable,
|
PaymentsReceivedImportable,
|
||||||
PaymentsReceivedPagesService
|
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
PaymentReceivesApplication,
|
PaymentReceivesApplication,
|
||||||
|
|||||||
@@ -1,47 +1,32 @@
|
|||||||
|
import { AttachmentLinkDto } from '@/modules/Attachments/dtos/Attachment.dto';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import {
|
import { IsArray, IsNotEmpty, ValidateNested } from 'class-validator';
|
||||||
IsString,
|
import { IsString } from 'class-validator';
|
||||||
IsDateString,
|
import { IsDateString, IsNumber, IsOptional } from 'class-validator';
|
||||||
IsNumber,
|
import { IsInt } from 'class-validator';
|
||||||
IsOptional,
|
|
||||||
IsArray,
|
|
||||||
IsNotEmpty,
|
|
||||||
IsInt,
|
|
||||||
ValidateNested,
|
|
||||||
} from 'class-validator';
|
|
||||||
import { ToNumber } from '@/common/decorators/Validators';
|
|
||||||
import { AttachmentLinkDto } from '@/modules/Attachments/dtos/Attachment.dto';
|
|
||||||
|
|
||||||
export class PaymentReceivedEntryDto {
|
export class PaymentReceivedEntryDto {
|
||||||
@ToNumber()
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsInt()
|
@IsInt()
|
||||||
id?: number;
|
id?: number;
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@IsInt()
|
||||||
index?: number;
|
index?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
paymentReceiveId?: number;
|
paymentReceiveId?: number;
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsNotEmpty()
|
|
||||||
invoiceId: number;
|
invoiceId: number;
|
||||||
|
|
||||||
@ToNumber()
|
@IsNumber()
|
||||||
@IsInt()
|
|
||||||
@IsNotEmpty()
|
|
||||||
paymentAmount: number;
|
paymentAmount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommandPaymentReceivedDto {
|
export class CommandPaymentReceivedDto {
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@ApiProperty({ description: 'The id of the customer', example: 1 })
|
@ApiProperty({ description: 'The id of the customer', example: 1 })
|
||||||
@@ -55,7 +40,6 @@ export class CommandPaymentReceivedDto {
|
|||||||
paymentDate: Date | string;
|
paymentDate: Date | string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The amount of the payment received',
|
description: 'The amount of the payment received',
|
||||||
@@ -64,7 +48,6 @@ export class CommandPaymentReceivedDto {
|
|||||||
amount?: number;
|
amount?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The exchange rate of the payment received',
|
description: 'The exchange rate of the payment received',
|
||||||
@@ -80,7 +63,6 @@ export class CommandPaymentReceivedDto {
|
|||||||
})
|
})
|
||||||
referenceNo?: string;
|
referenceNo?: string;
|
||||||
|
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
@@ -90,7 +72,6 @@ export class CommandPaymentReceivedDto {
|
|||||||
depositAccountId: number;
|
depositAccountId: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The payment receive number of the payment received',
|
description: 'The payment receive number of the payment received',
|
||||||
@@ -116,7 +97,6 @@ export class CommandPaymentReceivedDto {
|
|||||||
entries: PaymentReceivedEntryDto[];
|
entries: PaymentReceivedEntryDto[];
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ToNumber()
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The id of the branch',
|
description: 'The id of the branch',
|
||||||
|
|||||||
@@ -41,10 +41,11 @@ export class PaymentsReceivedPagesService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve payment receive new page receivable entries.
|
* Retrieve payment receive new page receivable entries.
|
||||||
|
* @param {number} tenantId - Tenant id.
|
||||||
* @param {number} vendorId - Vendor id.
|
* @param {number} vendorId - Vendor id.
|
||||||
* @return {IPaymentReceivePageEntry[]}
|
* @return {IPaymentReceivePageEntry[]}
|
||||||
*/
|
*/
|
||||||
public async getNewPageEntries(customerId: number) {
|
public async getNewPageEntries(tenantId: number, customerId: number) {
|
||||||
// Retrieve due invoices.
|
// Retrieve due invoices.
|
||||||
const entries = await this.saleInvoice()
|
const entries = await this.saleInvoice()
|
||||||
.query()
|
.query()
|
||||||
@@ -61,7 +62,10 @@ export class PaymentsReceivedPagesService {
|
|||||||
* @param {number} tenantId - Tenant id.
|
* @param {number} tenantId - Tenant id.
|
||||||
* @param {Integer} paymentReceiveId - Payment receive 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'>;
|
paymentReceive: Omit<PaymentReceived, 'entries'>;
|
||||||
entries: IPaymentReceivePageEntry[];
|
entries: IPaymentReceivePageEntry[];
|
||||||
}> {
|
}> {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class PdfTemplateApplication {
|
|||||||
* @param {ICreateInvoicePdfTemplateDTO} invoiceTemplateDTO - The data transfer object containing the details for the new PDF template.
|
* @param {ICreateInvoicePdfTemplateDTO} invoiceTemplateDTO - The data transfer object containing the details for the new PDF template.
|
||||||
* @returns {Promise<any>}
|
* @returns {Promise<any>}
|
||||||
*/
|
*/
|
||||||
public createPdfTemplate(
|
public async createPdfTemplate(
|
||||||
templateName: string,
|
templateName: string,
|
||||||
resource: string,
|
resource: string,
|
||||||
invoiceTemplateDTO: ICreateInvoicePdfTemplateDTO,
|
invoiceTemplateDTO: ICreateInvoicePdfTemplateDTO,
|
||||||
@@ -45,7 +45,7 @@ export class PdfTemplateApplication {
|
|||||||
* Deletes a PDF template.
|
* Deletes a PDF template.
|
||||||
* @param {number} templateId - The ID of the template to delete.
|
* @param {number} templateId - The ID of the template to delete.
|
||||||
*/
|
*/
|
||||||
public deletePdfTemplate(templateId: number) {
|
public async deletePdfTemplate(templateId: number) {
|
||||||
return this.deletePdfTemplateService.deletePdfTemplate(templateId);
|
return this.deletePdfTemplateService.deletePdfTemplate(templateId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ export class PdfTemplateApplication {
|
|||||||
* Retrieves a specific PDF template.
|
* Retrieves a specific PDF template.
|
||||||
* @param {number} templateId - The ID of the template to retrieve.
|
* @param {number} templateId - The ID of the template to retrieve.
|
||||||
*/
|
*/
|
||||||
public getPdfTemplate(templateId: number) {
|
public async getPdfTemplate(templateId: number) {
|
||||||
return this.getPdfTemplateService.getPdfTemplate(templateId);
|
return this.getPdfTemplateService.getPdfTemplate(templateId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ export class PdfTemplateApplication {
|
|||||||
* Retrieves all PDF templates.
|
* Retrieves all PDF templates.
|
||||||
* @param {string} resource - The resource type to filter 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);
|
return this.getPdfTemplatesService.getPdfTemplates(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ export class PdfTemplateApplication {
|
|||||||
* @param {number} templateId - The ID of the template to edit.
|
* @param {number} templateId - The ID of the template to edit.
|
||||||
* @param {IEditPdfTemplateDTO} editDTO - The data transfer object containing the updates.
|
* @param {IEditPdfTemplateDTO} editDTO - The data transfer object containing the updates.
|
||||||
*/
|
*/
|
||||||
public editPdfTemplate(
|
public async editPdfTemplate(
|
||||||
templateId: number,
|
templateId: number,
|
||||||
editDTO: IEditPdfTemplateDTO,
|
editDTO: IEditPdfTemplateDTO,
|
||||||
) {
|
) {
|
||||||
@@ -80,7 +80,7 @@ export class PdfTemplateApplication {
|
|||||||
/**
|
/**
|
||||||
* Gets the PDF template branding state.
|
* Gets the PDF template branding state.
|
||||||
*/
|
*/
|
||||||
public getPdfTemplateBrandingState() {
|
public async getPdfTemplateBrandingState() {
|
||||||
return this.getPdfTemplateBrandingStateService.execute();
|
return this.getPdfTemplateBrandingStateService.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ export class PdfTemplateApplication {
|
|||||||
* @param {number} templateId - The ID of the PDF template to assign as default.
|
* @param {number} templateId - The ID of the PDF template to assign as default.
|
||||||
* @returns {Promise<any>}
|
* @returns {Promise<any>}
|
||||||
*/
|
*/
|
||||||
public assignPdfTemplateAsDefault(templateId: number) {
|
public async assignPdfTemplateAsDefault(templateId: number) {
|
||||||
return this.assignPdfTemplateDefaultService.assignDefaultTemplate(
|
return this.assignPdfTemplateDefaultService.assignDefaultTemplate(
|
||||||
templateId,
|
templateId,
|
||||||
);
|
);
|
||||||
@@ -99,7 +99,7 @@ export class PdfTemplateApplication {
|
|||||||
* Retrieves the organization branding attributes.
|
* Retrieves the organization branding attributes.
|
||||||
* @returns {Promise<CommonOrganizationBrandingAttributes>} The organization branding attributes.
|
* @returns {Promise<CommonOrganizationBrandingAttributes>} The organization branding attributes.
|
||||||
*/
|
*/
|
||||||
public getOrganizationBrandingAttributes() {
|
getOrganizationBrandingAttributes() {
|
||||||
return this.getOrganizationBrandingAttributesService.execute();
|
return this.getOrganizationBrandingAttributesService.execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,17 +47,6 @@ export class PdfTemplatesController {
|
|||||||
return this.pdfTemplateApplication.deletePdfTemplate(templateId);
|
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')
|
@Get(':id')
|
||||||
@ApiOperation({ summary: 'Retrieves the PDF template details.' })
|
@ApiOperation({ summary: 'Retrieves the PDF template details.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { DeleteRoleService } from './commands/DeleteRole.service';
|
|||||||
import { EditRoleService } from './commands/EditRole.service';
|
import { EditRoleService } from './commands/EditRole.service';
|
||||||
import { GetRoleService } from './queries/GetRole.service';
|
import { GetRoleService } from './queries/GetRole.service';
|
||||||
import { GetRolesService } from './queries/GetRoles.service';
|
import { GetRolesService } from './queries/GetRoles.service';
|
||||||
import { RolePermissionsSchema } from './queries/RolePermissionsSchema';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RolesApplication {
|
export class RolesApplication {
|
||||||
@@ -14,7 +13,6 @@ export class RolesApplication {
|
|||||||
private readonly deleteRoleService: DeleteRoleService,
|
private readonly deleteRoleService: DeleteRoleService,
|
||||||
private readonly getRoleService: GetRoleService,
|
private readonly getRoleService: GetRoleService,
|
||||||
private readonly getRolesService: GetRolesService,
|
private readonly getRolesService: GetRolesService,
|
||||||
private readonly getRolePermissionsSchemaService: RolePermissionsSchema,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,12 +59,4 @@ export class RolesApplication {
|
|||||||
async getRoles() {
|
async getRoles() {
|
||||||
return this.getRolesService.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,
|
status: HttpStatus.OK,
|
||||||
description: 'Role deleted successfully',
|
description: 'Role deleted successfully',
|
||||||
})
|
})
|
||||||
async deleteRole(@Param('id', ParseIntPipe) roleId: number) {
|
async deleteRole(
|
||||||
|
@Param('id', ParseIntPipe) roleId: number,
|
||||||
|
) {
|
||||||
await this.rolesApp.deleteRole(roleId);
|
await this.rolesApp.deleteRole(roleId);
|
||||||
|
|
||||||
return {
|
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()
|
@Get()
|
||||||
@ApiOperation({ summary: 'Get all roles' })
|
@ApiOperation({ summary: 'Get all roles' })
|
||||||
@ApiResponse({ status: HttpStatus.OK, description: 'List of all roles' })
|
@ApiResponse({ status: HttpStatus.OK, description: 'List of all roles' })
|
||||||
async getRoles() {
|
async getRoles() {
|
||||||
const roles = await this.rolesApp.getRoles();
|
const roles = await this.rolesApp.getRoles();
|
||||||
|
|
||||||
return roles;
|
return { roles };
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
@ApiOperation({ summary: 'Get a specific role by ID' })
|
@ApiOperation({ summary: 'Get a specific role by ID' })
|
||||||
@ApiParam({ name: 'id', description: 'Role ID' })
|
@ApiParam({ name: 'id', description: 'Role ID' })
|
||||||
@ApiResponse({ status: HttpStatus.OK, description: 'Role details' })
|
@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);
|
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 { RolePermission } from './models/RolePermission.model';
|
||||||
import { RolesController } from './Roles.controller';
|
import { RolesController } from './Roles.controller';
|
||||||
import { RolesApplication } from './Roles.application';
|
import { RolesApplication } from './Roles.application';
|
||||||
import { RolePermissionsSchema } from './queries/RolePermissionsSchema';
|
|
||||||
|
|
||||||
const models = [
|
const models = [
|
||||||
RegisterTenancyModel(Role),
|
RegisterTenancyModel(Role),
|
||||||
@@ -25,7 +24,6 @@ const models = [
|
|||||||
GetRoleService,
|
GetRoleService,
|
||||||
GetRolesService,
|
GetRolesService,
|
||||||
RolesApplication,
|
RolesApplication,
|
||||||
RolePermissionsSchema
|
|
||||||
],
|
],
|
||||||
controllers: [RolesController],
|
controllers: [RolesController],
|
||||||
exports: [...models],
|
exports: [...models],
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Ability } from '@casl/ability';
|
import { Ability } from '@casl/ability';
|
||||||
import * as LruCache from 'lru-cache';
|
import LruCache from 'lru-cache';
|
||||||
import { Role } from './models/Role.model';
|
import { Role } from './models/Role.model';
|
||||||
import { RolePermission } from './models/RolePermission.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