refactor(nestjs): bank transactions matching

This commit is contained in:
Ahmed Bouhuolia
2025-06-05 14:41:26 +02:00
parent f87bd341e9
commit 51988dba3b
43 changed files with 484 additions and 105 deletions

View File

@@ -0,0 +1,4 @@
{
"decrement": "Decrement",
"increment": "Increment"
}

View File

@@ -2,14 +2,21 @@ import { Body, Controller, Delete, Param, Post, Query } from '@nestjs/common';
import { castArray, omit } from 'lodash'; import { castArray, omit } from 'lodash';
import { BankingCategorizeApplication } from './BankingCategorize.application'; import { BankingCategorizeApplication } from './BankingCategorize.application';
import { CategorizeBankTransactionRouteDto } from './dtos/CategorizeBankTransaction.dto'; import { CategorizeBankTransactionRouteDto } from './dtos/CategorizeBankTransaction.dto';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
@Controller('banking/categorize') @Controller('banking/categorize')
@ApiTags('banking-categorization')
export class BankingCategorizeController { export class BankingCategorizeController {
constructor( constructor(
private readonly bankingCategorizeApplication: BankingCategorizeApplication, private readonly bankingCategorizeApplication: BankingCategorizeApplication,
) {} ) {}
@Post() @Post()
@ApiOperation({ summary: 'Categorize bank transactions.' })
@ApiResponse({
status: 200,
description: 'The bank transactions have been categorized successfully.',
})
public categorizeTransaction( public categorizeTransaction(
@Body() body: CategorizeBankTransactionRouteDto, @Body() body: CategorizeBankTransactionRouteDto,
) { ) {
@@ -20,6 +27,11 @@ export class BankingCategorizeController {
} }
@Delete('/bulk') @Delete('/bulk')
@ApiOperation({ summary: 'Uncategorize bank transactions.' })
@ApiResponse({
status: 200,
description: 'The bank transactions have been uncategorized successfully.',
})
public uncategorizeTransactionsBulk( public uncategorizeTransactionsBulk(
@Query() uncategorizedTransactionIds: number[] | number, @Query() uncategorizedTransactionIds: number[] | number,
) { ) {
@@ -29,6 +41,11 @@ export class BankingCategorizeController {
} }
@Delete('/:id') @Delete('/:id')
@ApiOperation({ summary: 'Uncategorize a bank transaction.' })
@ApiResponse({
status: 200,
description: 'The bank transaction has been uncategorized successfully.',
})
public uncategorizeTransaction( public uncategorizeTransaction(
@Param('id') uncategorizedTransactionId: number, @Param('id') uncategorizedTransactionId: number,
) { ) {

View File

@@ -8,48 +8,106 @@ import {
IsOptional, IsOptional,
IsString, IsString,
} from 'class-validator'; } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
/**
* DTO for categorizing bank transactions
*/
export class CategorizeBankTransactionDto { export class CategorizeBankTransactionDto {
@ApiProperty({
description: 'The date of the bank transaction',
type: Date,
example: '2023-01-01T00:00:00.000Z',
})
@IsDateString() @IsDateString()
@IsNotEmpty() @IsNotEmpty()
date: Date; date: Date;
@ApiProperty({
description: 'ID of the credit account associated with this transaction',
type: Number,
example: 1001,
})
@IsInt() @IsInt()
@ToNumber() @ToNumber()
@IsNotEmpty() @IsNotEmpty()
creditAccountId: number; creditAccountId: number;
@ApiPropertyOptional({
description: 'Optional external reference number',
type: String,
example: 'REF-001',
})
@IsString() @IsString()
@IsOptional() @IsOptional()
referenceNo: string; referenceNo: string;
@ApiPropertyOptional({
description: 'Optional transaction number or reference',
type: String,
example: 'TRX-001',
})
@IsString() @IsString()
@IsOptional() @IsOptional()
transactionNumber: string; transactionNumber: string;
@ApiProperty({
description: 'Type of bank transaction (e.g., deposit, withdrawal)',
type: String,
example: 'deposit',
})
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
transactionType: string; transactionType: string;
@ApiPropertyOptional({
description: 'Exchange rate for currency conversion',
type: Number,
default: 1,
example: 1.15,
})
@IsNumber() @IsNumber()
@ToNumber() @ToNumber()
@IsOptional() @IsOptional()
exchangeRate: number = 1; exchangeRate: number = 1;
@ApiPropertyOptional({
description: 'Currency code for the transaction',
type: String,
example: 'USD',
})
@IsString() @IsString()
@IsOptional() @IsOptional()
currencyCode: string; currencyCode: string;
@ApiPropertyOptional({
description: 'Description of the bank transaction',
type: String,
example: 'Monthly rent payment',
})
@IsString() @IsString()
@IsOptional() @IsOptional()
description: string; description: string;
@ApiPropertyOptional({
description: 'ID of the branch where the transaction occurred',
type: Number,
example: 101,
})
@IsNumber() @IsNumber()
@IsOptional() @IsOptional()
branchId: number; branchId: number;
} }
/**
* Extended DTO for categorizing bank transactions with IDs of uncategorized transactions
*/
export class CategorizeBankTransactionRouteDto extends CategorizeBankTransactionDto { export class CategorizeBankTransactionRouteDto extends CategorizeBankTransactionDto {
@ApiProperty({
description: 'Array of uncategorized transaction IDs to be categorized',
type: [Number],
example: [1001, 1002, 1003],
})
@IsArray() @IsArray()
uncategorizedTransactionIds: Array<number>; uncategorizedTransactionIds: Array<number>;
} }

View File

@@ -23,16 +23,12 @@ export class BankingMatchingController {
); );
} }
@Post('/match/:uncategorizedTransactionId') @Post('/match')
@ApiOperation({ summary: 'Match the given uncategorized transaction.' }) @ApiOperation({ summary: 'Match the given uncategorized transaction.' })
async matchTransaction( async matchTransaction(@Body() matchedTransactions: MatchBankTransactionDto) {
@Param('uncategorizedTransactionId')
uncategorizedTransactionId: number | number[],
@Body() matchedTransactions: MatchBankTransactionDto,
) {
return this.bankingMatchingApplication.matchTransaction( return this.bankingMatchingApplication.matchTransaction(
uncategorizedTransactionId, matchedTransactions.uncategorizedTransactions,
matchedTransactions, matchedTransactions.matchedTransactions,
); );
} }

View File

@@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common';
import { GetMatchedTransactions } from './queries/GetMatchedTransactions.service'; import { GetMatchedTransactions } from './queries/GetMatchedTransactions.service';
import { MatchBankTransactions } from './commands/MatchTransactions'; import { MatchBankTransactions } from './commands/MatchTransactions';
import { UnmatchMatchedBankTransaction } from './commands/UnmatchMatchedTransaction.service'; import { UnmatchMatchedBankTransaction } from './commands/UnmatchMatchedTransaction.service';
import { GetMatchedTransactionsFilter, IMatchTransactionDTO } from './types'; import { GetMatchedTransactionsFilter } from './types';
import { MatchBankTransactionDto } from './dtos/MatchBankTransaction.dto'; import { MatchTransactionEntryDto } from './dtos/MatchBankTransaction.dto';
@Injectable() @Injectable()
export class BankingMatchingApplication { export class BankingMatchingApplication {
@@ -31,17 +31,18 @@ export class BankingMatchingApplication {
/** /**
* Matches the given uncategorized transaction with the given system transaction. * Matches the given uncategorized transaction with the given system transaction.
* @param {number} uncategorizedTransactionId * @param {IMatchBankTransactionDto} matchedTransactionsDTO
* @param {IMatchTransactionDTO} matchTransactionsDTO
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public matchTransaction( public matchTransaction(
uncategorizedTransactionId: number | Array<number>, uncategorizedTransactionId: number | Array<number>,
matchedTransactions: MatchBankTransactionDto, matchedTransactionsDto:
| MatchTransactionEntryDto
| Array<MatchTransactionEntryDto>,
): Promise<void> { ): Promise<void> {
return this.matchTransactionService.matchTransaction( return this.matchTransactionService.matchTransaction(
uncategorizedTransactionId, uncategorizedTransactionId,
matchedTransactions, matchedTransactionsDto,
); );
} }

View File

@@ -21,7 +21,7 @@ import { ServiceError } from '@/modules/Items/ServiceError';
import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction'; import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction';
import { events } from '@/common/events/events'; import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { MatchBankTransactionDto } from '../dtos/MatchBankTransaction.dto'; import { MatchTransactionEntryDto } from '../dtos/MatchBankTransaction.dto';
@Injectable() @Injectable()
export class MatchBankTransactions { export class MatchBankTransactions {
@@ -107,16 +107,15 @@ export class MatchBankTransactions {
/** /**
* Matches the given uncategorized transaction to the given references. * Matches the given uncategorized transaction to the given references.
* @param {number} tenantId
* @param {number} uncategorizedTransactionId * @param {number} uncategorizedTransactionId
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async matchTransaction( public async matchTransaction(
uncategorizedTransactionId: number | Array<number>, uncategorizedTransactionId: number | Array<number>,
matchedTransactionsDto: MatchBankTransactionDto, matchedTransactionsDto: MatchTransactionEntryDto | Array<MatchTransactionEntryDto>,
): Promise<void> { ): Promise<void> {
const uncategorizedTransactionIds = castArray(uncategorizedTransactionId); const uncategorizedTransactionIds = castArray(uncategorizedTransactionId);
const matchedTransactions = matchedTransactionsDto.entries; const matchedTransactions = castArray(matchedTransactionsDto);
// Validates the given matching transactions DTO. // Validates the given matching transactions DTO.
await this.validate(uncategorizedTransactionIds, matchedTransactions); await this.validate(uncategorizedTransactionIds, matchedTransactions);
@@ -131,7 +130,7 @@ export class MatchBankTransactions {
// Matches the given transactions under promise pool concurrency controlling. // Matches the given transactions under promise pool concurrency controlling.
await PromisePool.withConcurrency(10) await PromisePool.withConcurrency(10)
.for(matchedTransactions) .for(matchedTransactions)
.process(async (matchedTransaction) => { .process(async (matchedTransaction: MatchTransactionEntryDto) => {
const getMatchedTransactionsService = const getMatchedTransactionsService =
this.matchedBankTransactions.registry.get( this.matchedBankTransactions.registry.get(
matchedTransaction.referenceType, matchedTransaction.referenceType,

View File

@@ -1,4 +1,5 @@
import { import {
ArrayMinSize,
IsArray, IsArray,
IsNotEmpty, IsNotEmpty,
IsNumber, IsNumber,
@@ -27,6 +28,10 @@ export class MatchTransactionEntryDto {
} }
export class MatchBankTransactionDto { export class MatchBankTransactionDto {
@IsArray()
@ArrayMinSize(1)
uncategorizedTransactions: Array<number>
@IsArray() @IsArray()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => MatchTransactionEntryDto) @Type(() => MatchTransactionEntryDto)
@@ -37,5 +42,5 @@ export class MatchBankTransactionDto {
{ referenceType: 'SaleInvoice', referenceId: 2 }, { referenceType: 'SaleInvoice', referenceId: 2 },
], ],
}) })
entries: MatchTransactionEntryDto[]; matchedTransactions: MatchTransactionEntryDto[];
} }

View File

@@ -1,14 +1,14 @@
import PromisePool from '@supercharge/promise-pool';
import { OnEvent } from '@nestjs/event-emitter';
import { Inject, Injectable } from '@nestjs/common';
import { import {
IBankTransactionMatchedEventPayload, IBankTransactionMatchedEventPayload,
IBankTransactionUnmatchedEventPayload, IBankTransactionUnmatchedEventPayload,
} from '../types'; } from '../types';
import PromisePool from '@supercharge/promise-pool';
import { OnEvent } from '@nestjs/event-emitter';
import { Account } from '@/modules/Accounts/models/Account.model'; import { Account } from '@/modules/Accounts/models/Account.model';
import { Inject, Injectable } from '@nestjs/common'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction'; import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction';
import { events } from '@/common/events/events'; import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable() @Injectable()
export class DecrementUncategorizedTransactionOnMatchingSubscriber { export class DecrementUncategorizedTransactionOnMatchingSubscriber {

View File

@@ -1,11 +1,12 @@
import { Body, Controller, Post } from '@nestjs/common'; import { Body, Controller, Post } from '@nestjs/common';
import { PlaidWebhookDto } from './dtos/PlaidItem.dto'; import { PlaidWebhookDto } from './dtos/PlaidItem.dto';
import { ApiOperation } from '@nestjs/swagger'; import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { PlaidApplication } from './PlaidApplication'; import { PlaidApplication } from './PlaidApplication';
import { PublicRoute } from '../Auth/guards/jwt.guard'; import { PublicRoute } from '../Auth/guards/jwt.guard';
import { SetupPlaidItemTenantService } from './command/SetupPlaidItemTenant.service'; import { SetupPlaidItemTenantService } from './command/SetupPlaidItemTenant.service';
@Controller('banking/plaid') @Controller('banking/plaid')
@ApiTags('banking-plaid')
@PublicRoute() @PublicRoute()
export class BankingPlaidWebhooksController { export class BankingPlaidWebhooksController {
constructor( constructor(

View File

@@ -1,13 +1,13 @@
import { events } from '@/common/events/events'; import { events } from '@/common/events/events';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter'; import { OnEvent } from '@nestjs/event-emitter';
import { Queue } from 'bullmq';
import { InjectQueue } from '@nestjs/bullmq';
import { import {
IPlaidItemCreatedEventPayload, IPlaidItemCreatedEventPayload,
UpdateBankingPlaidTransitionsJob, UpdateBankingPlaidTransitionsJob,
UpdateBankingPlaidTransitionsQueueJob, UpdateBankingPlaidTransitionsQueueJob,
} from '../types/BankingPlaid.types'; } from '../types/BankingPlaid.types';
import { Queue } from 'bullmq';
import { InjectQueue } from '@nestjs/bullmq';
@Injectable() @Injectable()
export class PlaidUpdateTransactionsOnItemCreatedSubscriber { export class PlaidUpdateTransactionsOnItemCreatedSubscriber {

View File

@@ -8,67 +8,145 @@ import {
IsOptional, IsOptional,
IsString, IsString,
} from 'class-validator'; } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
export class CreateBankTransactionDto { export class CreateBankTransactionDto {
@ApiProperty({
description: 'The date of the bank transaction',
type: Date,
example: '2023-01-01T00:00:00.000Z',
})
@IsDateString() @IsDateString()
@IsNotEmpty() @IsNotEmpty()
date: Date; date: Date;
@ApiPropertyOptional({
description: 'Optional transaction number or reference',
type: String,
example: 'TRX-001',
})
@IsString() @IsString()
@IsOptional() @IsOptional()
transactionNumber?: string; transactionNumber?: string;
@ApiPropertyOptional({
description: 'Optional external reference number',
type: String,
example: 'REF-001',
})
@IsString() @IsString()
@IsOptional() @IsOptional()
referenceNo?: string; referenceNo?: string;
@ApiProperty({
description: 'Type of bank transaction (e.g., deposit, withdrawal)',
type: String,
example: 'deposit',
})
@IsNotEmpty() @IsNotEmpty()
@IsString() @IsString()
transactionType: string; transactionType: string;
@ApiProperty({
description: 'Description of the bank transaction',
type: String,
example: 'Monthly rent payment',
})
@IsString() @IsString()
description: string; description: string;
@ApiProperty({
description: 'Transaction amount',
type: Number,
example: 1000.5,
})
@IsNotEmpty() @IsNotEmpty()
@ToNumber() @ToNumber()
@IsNumber() @IsNumber()
amount: number; amount: number;
@ApiProperty({
description: 'Exchange rate for currency conversion',
type: Number,
default: 1,
example: 1.15,
})
@ToNumber() @ToNumber()
@IsNumber() @IsNumber()
exchangeRate: number = 1; exchangeRate: number = 1;
@ApiPropertyOptional({
description: 'Currency code for the transaction',
type: String,
example: 'USD',
})
@IsString() @IsString()
@IsOptional() @IsOptional()
currencyCode: string; currencyCode: string;
@ApiProperty({
description: 'ID of the credit account associated with this transaction',
type: Number,
example: 1001,
})
@IsNotEmpty() @IsNotEmpty()
@ToNumber() @ToNumber()
@IsInt() @IsInt()
creditAccountId: number; creditAccountId: number;
@ApiProperty({
description: 'ID of the cashflow account associated with this transaction',
type: Number,
example: 2001,
})
@IsNotEmpty() @IsNotEmpty()
@ToNumber() @ToNumber()
@IsInt() @IsInt()
cashflowAccountId: number; cashflowAccountId: number;
@ApiProperty({
description: 'Whether the transaction should be published',
type: Boolean,
default: true,
})
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
publish: boolean = true; publish: boolean = true;
@ApiPropertyOptional({
description: 'ID of the branch where the transaction occurred',
type: Number,
example: 101,
})
@IsOptional() @IsOptional()
@ToNumber() @ToNumber()
@IsInt() @IsInt()
branchId?: number; branchId?: number;
@ApiPropertyOptional({
description: 'Plaid transaction ID if imported from Plaid',
type: String,
example: 'plaid_trx_12345',
})
@IsOptional() @IsOptional()
@IsString() @IsString()
plaidTransactionId?: string; plaidTransactionId?: string;
@ApiPropertyOptional({
description: 'Plaid account ID if imported from Plaid',
type: String,
example: 'plaid_acc_67890',
})
@IsOptional() @IsOptional()
@IsString() @IsString()
plaidAccountId?: string; plaidAccountId?: string;
@ApiPropertyOptional({
description:
'ID of the uncategorized transaction if this is categorizing an existing transaction',
type: Number,
example: 5001,
})
@IsOptional() @IsOptional()
@IsInt() @IsInt()
uncategorizedTransactionId?: number; uncategorizedTransactionId?: number;

View File

@@ -1,14 +1,8 @@
import * as moment from 'moment';
import * as R from 'ramda';
import type { Knex } from 'knex'; import type { Knex } from 'knex';
import { Model, raw } from 'objection'; import { Model, raw } from 'objection';
import { castArray, difference, defaultTo } from 'lodash'; import { castArray, difference, defaultTo } from 'lodash';
import * as moment from 'moment';
import * as R from 'ramda';
// import TenantModel from 'models/TenantModel';
// import BillSettings from './Bill.Settings';
// import ModelSetting from './ModelSetting';
// import CustomViewBaseModel from './CustomViewBaseModel';
// import { DEFAULT_VIEWS } from '@/services/Purchases/Bills/constants';
// import ModelSearchable from './ModelSearchable';
import { BaseModel, PaginationQueryBuilderType } from '@/models/Model'; import { BaseModel, PaginationQueryBuilderType } from '@/models/Model';
import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry'; import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry';
import { BillLandedCost } from '@/modules/BillLandedCosts/models/BillLandedCost'; import { BillLandedCost } from '@/modules/BillLandedCosts/models/BillLandedCost';

View File

@@ -0,0 +1,33 @@
import { ToNumber } from '@/common/decorators/Validators';
import { IsArray, IsEnum, IsInt, IsOptional, IsString } from 'class-validator';
import { IFilterRole, ISortOrder } from '../DynamicFilter/DynamicFilter.types';
export class DynamicFilterQueryDto {
@IsOptional()
@ToNumber()
customViewId?: number;
@IsArray()
@IsOptional()
filterRoles?: IFilterRole[];
@IsOptional()
@IsString()
columnSortBy: string;
@IsString()
@IsOptional()
sortOrder: ISortOrder;
@IsString()
@IsOptional()
stringifiedFilterRoles?: string;
@IsString()
@IsOptional()
searchKeyword?: string;
@IsString()
@IsOptional()
viewSlug?: string;
}

View File

@@ -52,8 +52,6 @@ export class ExportResourceService {
const data = await this.getExportableData(resource); const data = await this.getExportableData(resource);
const transformed = this.transformExportedData(resource, data); const transformed = this.transformExportedData(resource, data);
console.log(format);
// Returns the csv, xlsx format. // Returns the csv, xlsx format.
if (format === ExportFormat.Csv || format === ExportFormat.Xlsx) { if (format === ExportFormat.Csv || format === ExportFormat.Xlsx) {
const exportableColumns = this.getExportableColumns(resourceColumns); const exportableColumns = this.getExportableColumns(resourceColumns);

View File

@@ -17,8 +17,8 @@ export class InventoryAdjustmentTransformer extends Transformer {
*/ */
formattedType(inventoryAdjustment: InventoryAdjustment) { formattedType(inventoryAdjustment: InventoryAdjustment) {
const types = { const types = {
increment: 'inventory_adjustment.type.increment', increment: 'inventory_adjustment.increment',
decrement: 'inventory_adjustment.type.decrement', decrement: 'inventory_adjustment.decrement',
}; };
return this.context.i18n.t(types[inventoryAdjustment.type] || ''); return this.context.i18n.t(types[inventoryAdjustment.type] || '');
} }

View File

@@ -1,15 +1,17 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { import {
IsBoolean, IsBoolean,
IsDate, IsDateString,
IsEnum, IsEnum,
IsInt,
IsNotEmpty, IsNotEmpty,
IsNumber, IsNumber,
IsOptional,
IsPositive, IsPositive,
IsString, IsString,
} from 'class-validator'; } from 'class-validator';
import { Type } from 'class-transformer'; import { Transform } from 'class-transformer';
import { IsOptional, ToNumber } from '@/common/decorators/Validators';
import { parseBoolean } from '@/utils/parse-boolean';
enum IAdjustmentTypes { enum IAdjustmentTypes {
INCREMENT = 'increment', INCREMENT = 'increment',
@@ -19,8 +21,7 @@ enum IAdjustmentTypes {
export class CreateQuickInventoryAdjustmentDto { export class CreateQuickInventoryAdjustmentDto {
@ApiProperty({ description: 'Date of the inventory adjustment' }) @ApiProperty({ description: 'Date of the inventory adjustment' })
@IsNotEmpty() @IsNotEmpty()
@IsDate() @IsDateString()
@Type(() => Date)
date: Date; date: Date;
@ApiProperty({ description: 'Type of adjustment', enum: IAdjustmentTypes }) @ApiProperty({ description: 'Type of adjustment', enum: IAdjustmentTypes })
@@ -30,7 +31,8 @@ export class CreateQuickInventoryAdjustmentDto {
@ApiProperty({ description: 'ID of the adjustment account' }) @ApiProperty({ description: 'ID of the adjustment account' })
@IsNotEmpty() @IsNotEmpty()
@IsNumber() @ToNumber()
@IsInt()
@IsPositive() @IsPositive()
adjustmentAccountId: number; adjustmentAccountId: number;
@@ -40,47 +42,52 @@ export class CreateQuickInventoryAdjustmentDto {
reason: string; reason: string;
@ApiProperty({ description: 'Description of the adjustment' }) @ApiProperty({ description: 'Description of the adjustment' })
@IsNotEmpty() @IsOptional()
@IsString() @IsString()
description: string; description: string;
@ApiProperty({ description: 'Reference number' }) @ApiProperty({ description: 'Reference number' })
@IsNotEmpty() @IsOptional()
@IsString() @IsString()
referenceNo: string; referenceNo: string;
@ApiProperty({ description: 'ID of the item being adjusted' }) @ApiProperty({ description: 'ID of the item being adjusted' })
@IsNotEmpty() @IsNotEmpty()
@ToNumber()
@IsNumber() @IsNumber()
@IsPositive() @IsPositive()
itemId: number; itemId: number;
@ApiProperty({ description: 'Quantity to adjust' }) @ApiProperty({ description: 'Quantity to adjust' })
@IsNotEmpty() @IsNotEmpty()
@ToNumber()
@IsNumber() @IsNumber()
@IsPositive() @IsPositive()
quantity: number; quantity: number;
@ApiProperty({ description: 'Cost of the item' }) @ApiProperty({ description: 'Cost of the item' })
@IsNotEmpty() @IsOptional()
@ToNumber()
@IsNumber() @IsNumber()
@IsPositive()
cost: number; cost: number;
@ApiProperty({ description: 'Whether to publish the adjustment immediately' }) @ApiProperty({ description: 'Whether to publish the adjustment immediately' })
@IsNotEmpty() @IsNotEmpty()
@Transform((param) => parseBoolean(param.value, false))
@IsBoolean() @IsBoolean()
publish: boolean; publish: boolean;
@ApiPropertyOptional({ description: 'ID of the warehouse (optional)' }) @ApiPropertyOptional({ description: 'ID of the warehouse (optional)' })
@IsOptional() @IsOptional()
@IsNumber() @ToNumber()
@IsInt()
@IsPositive() @IsPositive()
warehouseId?: number; warehouseId?: number;
@ApiPropertyOptional({ description: 'ID of the branch (optional)' }) @ApiPropertyOptional({ description: 'ID of the branch (optional)' })
@IsOptional() @IsOptional()
@IsNumber() @ToNumber()
@IsInt()
@IsPositive() @IsPositive()
branchId?: number; branchId?: number;
} }

View File

@@ -0,0 +1,24 @@
import { Controller, Get, Query } from '@nestjs/common';
import { GetItemsInventoryValuationListService } from './queries/GetItemsInventoryValuationList.service';
import { GetInventoyItemsCostQueryDto } from './dtos/GetInventoryItemsCostQuery.dto';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
@Controller('inventory-cost')
@ApiTags('inventory-cost')
export class InventoryCostController {
constructor(
private readonly inventoryItemCost: GetItemsInventoryValuationListService,
) {}
@Get('items')
@ApiOperation({ summary: 'Get items inventory valuation list' })
async getItemsCost(
@Query() itemsCostsQueryDto: GetInventoyItemsCostQueryDto,
) {
const costs = await this.inventoryItemCost.getItemsInventoryValuationList(
itemsCostsQueryDto.itemsIds,
itemsCostsQueryDto.date,
);
return { costs };
}
}

View File

@@ -23,6 +23,8 @@ import { InventoryItemOpeningAvgCostService } from './commands/InventoryItemOpen
import { InventoryCostSubscriber } from './subscribers/InventoryCost.subscriber'; import { InventoryCostSubscriber } from './subscribers/InventoryCost.subscriber';
import { SaleInvoicesModule } from '../SaleInvoices/SaleInvoices.module'; import { SaleInvoicesModule } from '../SaleInvoices/SaleInvoices.module';
import { ImportModule } from '../Import/Import.module'; import { ImportModule } from '../Import/Import.module';
import { GetItemsInventoryValuationListService } from './queries/GetItemsInventoryValuationList.service';
import { InventoryCostController } from './InventoryCost.controller';
const models = [ const models = [
RegisterTenancyModel(InventoryCostLotTracker), RegisterTenancyModel(InventoryCostLotTracker),
@@ -54,6 +56,7 @@ const models = [
InventoryItemCostService, InventoryItemCostService,
InventoryItemOpeningAvgCostService, InventoryItemOpeningAvgCostService,
InventoryCostSubscriber, InventoryCostSubscriber,
GetItemsInventoryValuationListService
], ],
exports: [ exports: [
...models, ...models,
@@ -61,5 +64,6 @@ const models = [
InventoryItemCostService, InventoryItemCostService,
InventoryComputeCostService, InventoryComputeCostService,
], ],
controllers: [InventoryCostController]
}) })
export class InventoryCostModule {} export class InventoryCostModule {}

View File

@@ -0,0 +1,26 @@
import {
ArrayMinSize,
IsArray,
IsDateString,
IsNotEmpty,
} from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class GetInventoyItemsCostQueryDto {
@IsDateString()
@IsNotEmpty()
@ApiProperty({
description: 'The date to get the inventory cost for',
example: '2021-01-01',
})
date: Date;
@IsArray()
@IsNotEmpty()
@ArrayMinSize(1)
@ApiProperty({
description: 'The ids of the items to get the inventory cost for',
example: [1, 2, 3],
})
itemsIds: Array<number>;
}

View File

@@ -0,0 +1,25 @@
import { Injectable } from '@nestjs/common';
import { InventoryItemCostService } from '../commands/InventoryCosts.service';
import { IInventoryItemCostMeta } from '../types/InventoryCost.types';
@Injectable()
export class GetItemsInventoryValuationListService {
constructor(private readonly inventoryCost: InventoryItemCostService) {}
/**
* Retrieves the items inventory valuation list.
* @param {number[]} itemsId
* @param {Date} date
* @returns {Promise<IInventoryItemCostMeta[]>}
*/
public getItemsInventoryValuationList = async (
itemsId: number[],
date: Date,
): Promise<IInventoryItemCostMeta[]> => {
const itemsMap = await this.inventoryCost.getItemsInventoryValuation(
itemsId,
date,
);
return [...itemsMap.values()];
};
}

View File

@@ -7,6 +7,7 @@ import { IItemsFilter } from './types/Items.types';
import { ItemTransformer } from './Item.transformer'; import { ItemTransformer } from './Item.transformer';
import { TenantModelProxy } from '../System/models/TenantBaseModel'; import { TenantModelProxy } from '../System/models/TenantBaseModel';
import { ISortOrder } from '../DynamicListing/DynamicFilter/DynamicFilter.types'; import { ISortOrder } from '../DynamicListing/DynamicFilter/DynamicFilter.types';
import { GetItemsQueryDto } from './dtos/GetItemsQuery.dto';
@Injectable() @Injectable()
export class GetItemsService { export class GetItemsService {
@@ -32,7 +33,7 @@ export class GetItemsService {
* Retrieves items datatable list. * Retrieves items datatable list.
* @param {IItemsFilter} itemsFilter - Items filter. * @param {IItemsFilter} itemsFilter - Items filter.
*/ */
public async getItems(filterDto: Partial<IItemsFilter>) { public async getItems(filterDto: Partial<GetItemsQueryDto>) {
const _filterDto = { const _filterDto = {
sortOrder: ISortOrder.DESC, sortOrder: ISortOrder.DESC,
columnSortBy: 'created_at', columnSortBy: 'created_at',

View File

@@ -23,6 +23,7 @@ import {
} from '@nestjs/swagger'; } from '@nestjs/swagger';
import { IItemsFilter } from './types/Items.types'; import { IItemsFilter } from './types/Items.types';
import { CreateItemDto, EditItemDto } from './dtos/Item.dto'; import { CreateItemDto, EditItemDto } from './dtos/Item.dto';
import { GetItemsQueryDto } from './dtos/GetItemsQuery.dto';
@Controller('/items') @Controller('/items')
@UseGuards(SubscriptionGuard) @UseGuards(SubscriptionGuard)
@@ -99,7 +100,7 @@ export class ItemsController extends TenantController {
type: Boolean, type: Boolean,
description: 'Filter for inactive items', description: 'Filter for inactive items',
}) })
async getItems(@Query() filterDTO: IItemsFilter): Promise<any> { async getItems(@Query() filterDTO: GetItemsQueryDto): Promise<any> {
return this.itemsApplication.getItems(filterDTO); return this.itemsApplication.getItems(filterDTO);
} }

View File

@@ -12,6 +12,7 @@ import { Injectable } from '@nestjs/common';
import { GetItemsService } from './GetItems.service'; import { GetItemsService } from './GetItems.service';
import { IItemsFilter } from './types/Items.types'; import { IItemsFilter } from './types/Items.types';
import { EditItemDto, CreateItemDto } from './dtos/Item.dto'; import { EditItemDto, CreateItemDto } from './dtos/Item.dto';
import { GetItemsQueryDto } from './dtos/GetItemsQuery.dto';
@Injectable() @Injectable()
export class ItemsApplicationService { export class ItemsApplicationService {
@@ -94,7 +95,7 @@ export class ItemsApplicationService {
* Retrieves the paginated filterable items list. * Retrieves the paginated filterable items list.
* @param {Partial<IItemsFilter>} filterDTO * @param {Partial<IItemsFilter>} filterDTO
*/ */
async getItems(filterDTO: Partial<IItemsFilter>) { async getItems(filterDTO: Partial<GetItemsQueryDto>) {
return this.getItemsService.getItems(filterDTO); return this.getItemsService.getItems(filterDTO);
} }

View File

@@ -0,0 +1,22 @@
import { ToNumber } from '@/common/decorators/Validators';
import { DynamicFilterQueryDto } from '@/modules/DynamicListing/dtos/DynamicFilterQuery.dto';
import { parseBoolean } from '@/utils/parse-boolean';
import { Transform, Type } from 'class-transformer';
import { IsBoolean, IsInt, IsOptional } from 'class-validator';
export class GetItemsQueryDto extends DynamicFilterQueryDto {
@IsOptional()
@IsInt()
@ToNumber()
page?: number;
@IsOptional()
@IsInt()
@ToNumber()
pageSize?: number;
@IsOptional()
@Transform((param) => parseBoolean(param.value, false))
@IsBoolean()
inactiveMode?: boolean;
}

View File

@@ -1,4 +1,4 @@
import async from 'async'; import * as async from 'async';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { import {
ILedger, ILedger,

View File

@@ -1,5 +1,5 @@
import { Knex } from 'knex'; import { Knex } from 'knex';
import async from 'async'; import * as async from 'async';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { transformLedgerEntryToTransaction } from './utils'; import { transformLedgerEntryToTransaction } from './utils';
import { import {
@@ -33,7 +33,7 @@ export class LedgerEntriesStorageService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public saveEntries = async (ledger: ILedger, trx?: Knex.Transaction) => { public saveEntries = async (ledger: ILedger, trx?: Knex.Transaction) => {
const saveEntryQueue = async.queue(this.saveEntryTask, 10); const saveEntryQueue = async.queue(this.saveEntryTask.bind(this), 10);
const entries = ledger.filter(filterBlankEntry).getEntries(); const entries = ledger.filter(filterBlankEntry).getEntries();
entries.forEach((entry) => { entries.forEach((entry) => {

View File

@@ -1,4 +1,4 @@
import async from 'async'; import * as async from 'async';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { uniq } from 'lodash'; import { uniq } from 'lodash';
import { import {

View File

@@ -1,3 +1,4 @@
import * as moment from 'moment';
import { AccountTransaction } from "../Accounts/models/AccountTransaction.model"; import { AccountTransaction } from "../Accounts/models/AccountTransaction.model";
import { ILedgerEntry } from "./types/Ledger.types"; import { ILedgerEntry } from "./types/Ledger.types";

View File

@@ -1,5 +1,5 @@
import { Knex } from 'knex'; import { Knex } from 'knex';
import async from 'async'; import * as async from 'async';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { PaymentReceivedGLEntries } from '../PaymentReceived/commands/PaymentReceivedGLEntries'; import { PaymentReceivedGLEntries } from '../PaymentReceived/commands/PaymentReceivedGLEntries';
import { TenantModelProxy } from '../System/models/TenantBaseModel'; import { TenantModelProxy } from '../System/models/TenantBaseModel';

View File

@@ -1,7 +1,7 @@
import { Model, raw } from 'objection';
import { castArray } from 'lodash';
import * as moment from 'moment'; import * as moment from 'moment';
import * as R from 'ramda'; import * as R from 'ramda';
import { Model, raw } from 'objection';
import { castArray } from 'lodash';
import { MomentInput, unitOfTime } from 'moment'; import { MomentInput, unitOfTime } from 'moment';
import { defaultTo } from 'ramda'; import { defaultTo } from 'ramda';
import { TaxRateTransaction } from '@/modules/TaxRates/models/TaxRateTransaction.model'; import { TaxRateTransaction } from '@/modules/TaxRates/models/TaxRateTransaction.model';

View File

@@ -1,4 +1,4 @@
import moment from 'moment'; import * as moment from 'moment';
import { BaseModel } from '@/models/Model'; import { BaseModel } from '@/models/Model';
export class UserInvite extends BaseModel { export class UserInvite extends BaseModel {

View File

@@ -1,6 +1,5 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { IEditWarehouseDTO, IWarehouse } from '../Warehouse.types';
import { WarehouseValidator } from './WarehouseValidator.service'; import { WarehouseValidator } from './WarehouseValidator.service';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service'; import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';

View File

@@ -0,0 +1,18 @@
import { ToNumber } from '@/common/decorators/Validators';
import { IsInt, IsOptional, IsString } from 'class-validator';
export class GetWarehouseTransfersQueryDto {
@IsInt()
@ToNumber()
@IsOptional()
page: number;
@IsInt()
@ToNumber()
@IsOptional()
pageSize: number;
@IsString()
@IsOptional()
searchKeyword: string;
}

View File

@@ -13,6 +13,7 @@ import {
CreateWarehouseTransferDto, CreateWarehouseTransferDto,
EditWarehouseTransferDto, EditWarehouseTransferDto,
} from './dtos/WarehouseTransfer.dto'; } from './dtos/WarehouseTransfer.dto';
import { GetWarehouseTransfersQueryDto } from '../Warehouses/dtos/GetWarehouseTransfersQuery.dto';
@Injectable() @Injectable()
export class WarehouseTransferApplication { export class WarehouseTransferApplication {
@@ -86,7 +87,7 @@ export class WarehouseTransferApplication {
* @returns {Promise<IWarehouseTransfer>} * @returns {Promise<IWarehouseTransfer>}
*/ */
public getWarehousesTransfers = ( public getWarehousesTransfers = (
filterDTO: IGetWarehousesTransfersFilterDTO, filterDTO: GetWarehouseTransfersQueryDto,
) => { ) => {
return this.getWarehousesTransfersService.getWarehouseTransfers(filterDTO); return this.getWarehousesTransfersService.getWarehouseTransfers(filterDTO);
}; };

View File

@@ -15,6 +15,7 @@ import {
CreateWarehouseTransferDto, CreateWarehouseTransferDto,
EditWarehouseTransferDto, EditWarehouseTransferDto,
} from './dtos/WarehouseTransfer.dto'; } from './dtos/WarehouseTransfer.dto';
import { GetWarehouseTransfersQueryDto } from '../Warehouses/dtos/GetWarehouseTransfersQuery.dto';
@Controller('warehouse-transfers') @Controller('warehouse-transfers')
@ApiTags('warehouse-transfers') @ApiTags('warehouse-transfers')
@@ -129,7 +130,7 @@ export class WarehouseTransfersController {
description: description:
'The warehouse transfer transactions have been retrieved successfully.', 'The warehouse transfer transactions have been retrieved successfully.',
}) })
async getWarehousesTransfers(@Query() query: any) { async getWarehousesTransfers(@Query() query: GetWarehouseTransfersQueryDto) {
const { warehousesTransfers, pagination, filter } = const { warehousesTransfers, pagination, filter } =
await this.warehouseTransferApplication.getWarehousesTransfers(query); await this.warehouseTransferApplication.getWarehousesTransfers(query);

View File

@@ -1,18 +1,18 @@
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,
IsDate,
IsDecimal, IsDecimal,
IsInt, IsInt,
IsNotEmpty, IsNotEmpty,
IsNumber,
IsOptional, IsOptional,
IsPositive, IsPositive,
IsString, IsString,
ValidateNested, ValidateNested,
ArrayMinSize, ArrayMinSize,
IsDateString,
} from 'class-validator'; } from 'class-validator';
export class WarehouseTransferEntryDto { export class WarehouseTransferEntryDto {
@@ -39,6 +39,7 @@ export class WarehouseTransferEntryDto {
export class CommandWarehouseTransferDto { export class CommandWarehouseTransferDto {
@IsNotEmpty() @IsNotEmpty()
@ToNumber()
@IsInt() @IsInt()
@ApiProperty({ @ApiProperty({
description: 'The id of the warehouse to transfer from', description: 'The id of the warehouse to transfer from',
@@ -47,6 +48,7 @@ export class CommandWarehouseTransferDto {
fromWarehouseId: number; fromWarehouseId: number;
@IsNotEmpty() @IsNotEmpty()
@ToNumber()
@IsInt() @IsInt()
@ApiProperty({ @ApiProperty({
description: 'The id of the warehouse to transfer to', description: 'The id of the warehouse to transfer to',
@@ -55,7 +57,7 @@ export class CommandWarehouseTransferDto {
toWarehouseId: number; toWarehouseId: number;
@IsNotEmpty() @IsNotEmpty()
@IsDate() @IsDateString()
@ApiProperty({ @ApiProperty({
description: 'The date of the warehouse transfer', description: 'The date of the warehouse transfer',
example: '2021-01-01', example: '2021-01-01',

View File

@@ -0,0 +1,70 @@
function StatusFieldFilterQuery(query, role) {
query.modify('filterByStatus', role.value);
}
export const WarehouseTransferMeta = {
defaultFilterField: 'name',
defaultSort: {
sortField: 'name',
sortOrder: 'DESC',
},
columns: {
date: {
name: 'warehouse_transfer.field.date',
type: 'date',
exportable: true,
},
transaction_number: {
name: 'warehouse_transfer.field.transaction_number',
type: 'text',
exportable: true,
},
status: {
name: 'warehouse_transfer.field.status',
fieldType: 'enumeration',
options: [
{ key: 'draft', label: 'Draft' },
{ key: 'in-transit', label: 'In Transit' },
{ key: 'transferred', label: 'Transferred' },
],
sortable: false,
},
created_at: {
name: 'warehouse_transfer.field.created_at',
column: 'created_at',
columnType: 'date',
fieldType: 'date',
},
},
fields: {
date: {
name: 'warehouse_transfer.field.date',
column: 'date',
columnType: 'date',
fieldType: 'date',
},
transaction_number: {
name: 'warehouse_transfer.field.transaction_number',
column: 'transaction_number',
fieldType: 'text',
},
status: {
name: 'warehouse_transfer.field.status',
fieldType: 'enumeration',
options: [
{ key: 'draft', label: 'Draft' },
{ key: 'in-transit', label: 'In Transit' },
{ key: 'transferred', label: 'Transferred' },
],
filterCustomQuery: StatusFieldFilterQuery,
sortable: false,
},
created_at: {
name: 'warehouse_transfer.field.created_at',
column: 'created_at',
columnType: 'date',
fieldType: 'date',
},
},
};

View File

@@ -2,7 +2,10 @@ import { Model, mixin } from 'objection';
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel'; import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
import { Warehouse } from '@/modules/Warehouses/models/Warehouse.model'; import { Warehouse } from '@/modules/Warehouses/models/Warehouse.model';
import { WarehouseTransferEntry } from './WarehouseTransferEntry'; import { WarehouseTransferEntry } from './WarehouseTransferEntry';
import { InjectModelMeta } from '@/modules/Tenancy/TenancyModels/decorators/InjectModelMeta.decorator';
import { WarehouseTransferMeta } from './WarehouseTransfer.meta';
@InjectModelMeta(WarehouseTransferMeta)
export class WarehouseTransfer extends TenantBaseModel { export class WarehouseTransfer extends TenantBaseModel {
public date!: Date; public date!: Date;
public transferInitiatedAt!: Date; public transferInitiatedAt!: Date;
@@ -14,7 +17,6 @@ export class WarehouseTransfer extends TenantBaseModel {
public fromWarehouse!: Warehouse; public fromWarehouse!: Warehouse;
public toWarehouse!: Warehouse; public toWarehouse!: Warehouse;
/** /**
* Table name. * Table name.
*/ */
@@ -126,27 +128,13 @@ export class WarehouseTransfer extends TenantBaseModel {
}; };
} }
/**
* Model settings.
*/
// static get meta() {
// return WarehouseTransferSettings;
// }
// /**
// * Retrieve the default custom views, roles and columns.
// */
// static get defaultViews() {
// return DEFAULT_VIEWS;
// }
/** /**
* Model search roles. * Model search roles.
*/ */
static get searchRoles() { static get searchRoles() {
return [ return [
// { fieldKey: 'name', comparator: 'contains' }, { fieldKey: 'name', comparator: 'contains' },
// { condition: 'or', fieldKey: 'code', comparator: 'like' }, { condition: 'or', fieldKey: 'code', comparator: 'like' },
]; ];
} }
} }

View File

@@ -6,6 +6,7 @@ import { TransformerInjectable } from '../../Transformer/TransformerInjectable.s
import { DynamicListService } from '../../DynamicListing/DynamicList.service'; import { DynamicListService } from '../../DynamicListing/DynamicList.service';
import { TenantModelProxy } from '../../System/models/TenantBaseModel'; import { TenantModelProxy } from '../../System/models/TenantBaseModel';
import { WarehouseTransfer } from '../models/WarehouseTransfer'; import { WarehouseTransfer } from '../models/WarehouseTransfer';
import { GetWarehouseTransfersQueryDto } from '@/modules/Warehouses/dtos/GetWarehouseTransfersQuery.dto';
@Injectable() @Injectable()
export class GetWarehouseTransfers { export class GetWarehouseTransfers {
@@ -30,16 +31,19 @@ export class GetWarehouseTransfers {
/** /**
* Retrieves warehouse transfers paginated list. * Retrieves warehouse transfers paginated list.
* @param {number} tenantId
* @param {IGetWarehousesTransfersFilterDTO} filterDTO * @param {IGetWarehousesTransfersFilterDTO} filterDTO
* @returns {}
*/ */
public getWarehouseTransfers = async ( public getWarehouseTransfers = async (
filterDTO: IGetWarehousesTransfersFilterDTO, filterDTO: GetWarehouseTransfersQueryDto,
) => { ) => {
// Parses stringified filter roles. // Parses stringified filter roles.
const filter = this.parseListFilterDTO(filterDTO); const filter = this.parseListFilterDTO({
sortOrder: 'desc',
columnSortBy: 'created_at',
page: 1,
pageSize: 12,
...filterDTO,
});
// Dynamic list service. // Dynamic list service.
const dynamicFilter = await this.dynamicListService.dynamicList( const dynamicFilter = await this.dynamicListService.dynamicList(
this.warehouseTransferModel(), this.warehouseTransferModel(),

View File

@@ -463,7 +463,7 @@ export function useMatchUncategorizedTransaction(
MatchUncategorizedTransactionRes, MatchUncategorizedTransactionRes,
Error, Error,
MatchUncategorizedTransactionValues MatchUncategorizedTransactionValues
>((value) => apiRequest.post('/banking/matches/match', value), { >((value) => apiRequest.post('/banking/matching/match', value), {
onSuccess: (res, id) => { onSuccess: (res, id) => {
queryClient.invalidateQueries( queryClient.invalidateQueries(
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY, t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,

View File

@@ -7,7 +7,7 @@ import t from './types';
const commonInvalidateQueries = (queryClient) => { const commonInvalidateQueries = (queryClient) => {
// Invalidate inventory adjustments. // Invalidate inventory adjustments.
queryClient.invalidateQueries(t.inventory-adjustments); queryClient.invalidateQueries(t.INVENTORY_ADJUSTMENTS);
queryClient.invalidateQueries(t.INVENTORY_ADJUSTMENT); queryClient.invalidateQueries(t.INVENTORY_ADJUSTMENT);
// Invalidate items. // Invalidate items.
@@ -120,7 +120,7 @@ export function useInventoryAdjustment(id, props, requestProps) {
[t.INVENTORY_ADJUSTMENT, id], [t.INVENTORY_ADJUSTMENT, id],
{ method: 'get', url: `inventory-adjustments/${id}`, ...requestProps }, { method: 'get', url: `inventory-adjustments/${id}`, ...requestProps },
{ {
select: (res) => res.data.data, select: (res) => res.data,
defaultData: {}, defaultData: {},
...props, ...props,
}, },

View File

@@ -260,7 +260,7 @@ export function useItemInventoryCost(query, props) {
[t.ITEM_INVENTORY_COST, query], [t.ITEM_INVENTORY_COST, query],
{ {
method: 'get', method: 'get',
url: `inventory/items-cost`, url: `inventory-cost/items`,
params: { ...query }, params: { ...query },
}, },
{ {

View File

@@ -26,7 +26,7 @@ export function useCreateWarehouseTransfer(props) {
const apiRequest = useApiRequest(); const apiRequest = useApiRequest();
return useMutation( return useMutation(
(values) => apiRequest.post('warehouses/transfers', values), (values) => apiRequest.post('warehouse-transfers', values),
{ {
onSuccess: (res, values) => { onSuccess: (res, values) => {
// Common invalidate queries. // Common invalidate queries.
@@ -45,7 +45,7 @@ export function useEditWarehouseTransfer(props) {
const apiRequest = useApiRequest(); const apiRequest = useApiRequest();
return useMutation( return useMutation(
([id, values]) => apiRequest.post(`warehouses/transfers/${id}`, values), ([id, values]) => apiRequest.post(`warehouse-transfers/${id}`, values),
{ {
onSuccess: (res, [id, values]) => { onSuccess: (res, [id, values]) => {
// Invalidate specific sale invoice. // Invalidate specific sale invoice.
@@ -66,7 +66,7 @@ export function useDeleteWarehouseTransfer(props) {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const apiRequest = useApiRequest(); const apiRequest = useApiRequest();
return useMutation((id) => apiRequest.delete(`warehouses/transfers/${id}`), { return useMutation((id) => apiRequest.delete(`warehouse-transfers/${id}`), {
onSuccess: (res, id) => { onSuccess: (res, id) => {
// Common invalidate queries. // Common invalidate queries.
commonInvalidateQueries(queryClient); commonInvalidateQueries(queryClient);
@@ -87,7 +87,7 @@ const transformWarehousesTransfer = (res) => ({
export function useWarehousesTransfers(query, props) { export function useWarehousesTransfers(query, props) {
return useRequestQuery( return useRequestQuery(
[t.WAREHOUSE_TRANSFERS, query], [t.WAREHOUSE_TRANSFERS, query],
{ method: 'get', url: 'warehouses/transfers', params: query }, { method: 'get', url: 'warehouse-transfers', params: query },
{ {
select: transformWarehousesTransfer, select: transformWarehousesTransfer,
defaultData: { defaultData: {
@@ -111,7 +111,7 @@ export function useWarehousesTransfers(query, props) {
export function useWarehouseTransfer(id, props, requestProps) { export function useWarehouseTransfer(id, props, requestProps) {
return useRequestQuery( return useRequestQuery(
[t.WAREHOUSE_TRANSFER, id], [t.WAREHOUSE_TRANSFER, id],
{ method: 'get', url: `warehouses/transfers/${id}`, ...requestProps }, { method: 'get', url: `warehouse-transfers/${id}`, ...requestProps },
{ {
select: (res) => res.data.data, select: (res) => res.data.data,
defaultData: {}, defaultData: {},
@@ -130,7 +130,7 @@ export function useInitiateWarehouseTransfer(props) {
const apiRequest = useApiRequest(); const apiRequest = useApiRequest();
return useMutation( return useMutation(
(id) => apiRequest.put(`warehouses/transfers/${id}/initiate`), (id) => apiRequest.put(`warehouse-transfers/${id}/initiate`),
{ {
onSuccess: (res, id) => { onSuccess: (res, id) => {
queryClient.invalidateQueries([t.WAREHOUSE_TRANSFER, id]); queryClient.invalidateQueries([t.WAREHOUSE_TRANSFER, id]);
@@ -153,7 +153,7 @@ export function useTransferredWarehouseTransfer(props) {
const apiRequest = useApiRequest(); const apiRequest = useApiRequest();
return useMutation( return useMutation(
(id) => apiRequest.put(`warehouses/transfers/${id}/transferred`), (id) => apiRequest.put(`warehouse-transfers/${id}/transferred`),
{ {
onSuccess: (res, id) => { onSuccess: (res, id) => {
queryClient.invalidateQueries([t.WAREHOUSE_TRANSFER, id]); queryClient.invalidateQueries([t.WAREHOUSE_TRANSFER, id]);