mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-24 16:49:48 +00:00
Compare commits
16 Commits
v0.24.9
...
feat/trpc-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bf2803360 | ||
|
|
688b1bfb56 | ||
|
|
0f8147daff | ||
|
|
96b24d4fb9 | ||
|
|
2a87103bc8 | ||
|
|
238b60144f | ||
|
|
fcee85e358 | ||
|
|
64a10053e3 | ||
|
|
ce9f2a238f | ||
|
|
75b98c39d8 | ||
|
|
80e545072d | ||
|
|
de3d4698ea | ||
|
|
171091e0e0 | ||
|
|
78032d7bfc | ||
|
|
06b8a836c5 | ||
|
|
37fa9f9bc6 |
@@ -127,7 +127,10 @@
|
|||||||
"uuid": "^10.0.0",
|
"uuid": "^10.0.0",
|
||||||
"xlsx": "^0.18.5",
|
"xlsx": "^0.18.5",
|
||||||
"yup": "^0.28.1",
|
"yup": "^0.28.1",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8",
|
||||||
|
"@trpc/server": "^11.0.0-rc.648",
|
||||||
|
"nestjs-trpc": "^1.6.1",
|
||||||
|
"superjson": "^2.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^10.0.0",
|
"@nestjs/cli": "^10.0.0",
|
||||||
|
|||||||
@@ -9,5 +9,10 @@
|
|||||||
"net_cash_financing": "Net cash provided by financing activities",
|
"net_cash_financing": "Net cash provided by financing activities",
|
||||||
"cash_beginning_period": "Cash at beginning of period",
|
"cash_beginning_period": "Cash at beginning of period",
|
||||||
"net_cash_increase": "NET CASH INCREASE FOR PERIOD",
|
"net_cash_increase": "NET CASH INCREASE FOR PERIOD",
|
||||||
"cash_end_period": "CASH AT END OF PERIOD"
|
"cash_end_period": "CASH AT END OF PERIOD",
|
||||||
|
"account_name": "Account name",
|
||||||
|
"total": "Total",
|
||||||
|
"sheet_name": "Statement of Cash Flow",
|
||||||
|
"from_date": "From",
|
||||||
|
"to_date": "To"
|
||||||
}
|
}
|
||||||
|
|||||||
5
packages/server/src/i18n/en/contact_summary_balance.json
Normal file
5
packages/server/src/i18n/en/contact_summary_balance.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"account_name": "Account name",
|
||||||
|
"total": "Total",
|
||||||
|
"percentage_column": "% of Column"
|
||||||
|
}
|
||||||
14
packages/server/src/i18n/en/inventory_item_details.json
Normal file
14
packages/server/src/i18n/en/inventory_item_details.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"opening_balance": "Opening balance",
|
||||||
|
"closing_balance": "Closing balance",
|
||||||
|
"date": "Date",
|
||||||
|
"transaction_type": "Transaction type",
|
||||||
|
"transaction_number": "Transaction #",
|
||||||
|
"quantity": "Quantity",
|
||||||
|
"rate": "Rate",
|
||||||
|
"total": "Total",
|
||||||
|
"value": "Value",
|
||||||
|
"profit_margin": "Profit Margin",
|
||||||
|
"running_quantity": "Running quantity",
|
||||||
|
"running_value": "Running Value"
|
||||||
|
}
|
||||||
4
packages/server/src/i18n/en/transactions_by_contact.json
Normal file
4
packages/server/src/i18n/en/transactions_by_contact.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"opening_balance": "Opening balance",
|
||||||
|
"closing_balance": "Closing balance"
|
||||||
|
}
|
||||||
6
packages/server/src/i18n/en/trial_balance_sheet.json
Normal file
6
packages/server/src/i18n/en/trial_balance_sheet.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"account": "Account",
|
||||||
|
"debit": "Debit",
|
||||||
|
"credit": "Credit",
|
||||||
|
"total": "Total"
|
||||||
|
}
|
||||||
@@ -104,6 +104,7 @@ import { BillLandedCostsModule } from '../BillLandedCosts/BillLandedCosts.module
|
|||||||
import { SocketModule } from '../Socket/Socket.module';
|
import { SocketModule } from '../Socket/Socket.module';
|
||||||
import { ThrottlerGuard } from '@nestjs/throttler';
|
import { ThrottlerGuard } from '@nestjs/throttler';
|
||||||
import { AppThrottleModule } from './AppThrottle.module';
|
import { AppThrottleModule } from './AppThrottle.module';
|
||||||
|
import { AppTrpcModule } from '../Trpc/Trpc.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -256,6 +257,7 @@ import { AppThrottleModule } from './AppThrottle.module';
|
|||||||
UsersModule,
|
UsersModule,
|
||||||
ContactsModule,
|
ContactsModule,
|
||||||
SocketModule,
|
SocketModule,
|
||||||
|
AppTrpcModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ModuleRef } from '@nestjs/core';
|
import { ModuleRef } from '@nestjs/core';
|
||||||
import bluebird from 'bluebird';
|
import * as bluebird from 'bluebird';
|
||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import {
|
import {
|
||||||
validateLinkModelEntryExists,
|
validateLinkModelEntryExists,
|
||||||
@@ -53,7 +53,8 @@ export class LinkAttachment {
|
|||||||
const foundLinkModel = await LinkModel().query(trx).findById(modelId);
|
const foundLinkModel = await LinkModel().query(trx).findById(modelId);
|
||||||
validateLinkModelEntryExists(foundLinkModel);
|
validateLinkModelEntryExists(foundLinkModel);
|
||||||
|
|
||||||
const foundLinks = await this.documentLinkModel().query(trx)
|
const foundLinks = await this.documentLinkModel()
|
||||||
|
.query(trx)
|
||||||
.where('modelRef', modelRef)
|
.where('modelRef', modelRef)
|
||||||
.where('modelId', modelId)
|
.where('modelId', modelId)
|
||||||
.where('documentId', foundFile.id);
|
.where('documentId', foundFile.id);
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export class AuthController {
|
|||||||
return this.authApp.signUp(signupDto);
|
return this.authApp.signUp(signupDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('/signup/confirm')
|
@Post('/signup/verify')
|
||||||
@ApiOperation({ summary: 'Confirm user signup' })
|
@ApiOperation({ summary: 'Confirm user signup' })
|
||||||
@ApiBody({
|
@ApiBody({
|
||||||
schema: {
|
schema: {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export class AuthenticationMailMesssages {
|
|||||||
* @returns {Mail}
|
* @returns {Mail}
|
||||||
*/
|
*/
|
||||||
resetPasswordMessage(user: ModelObject<SystemUser>, token: string) {
|
resetPasswordMessage(user: ModelObject<SystemUser>, token: string) {
|
||||||
const baseURL = this.configService.get('baseURL');
|
const baseURL = this.configService.get('app.baseUrl');
|
||||||
|
|
||||||
return new Mail()
|
return new Mail()
|
||||||
.setSubject('Bigcapital - Password Reset')
|
.setSubject('Bigcapital - Password Reset')
|
||||||
@@ -54,7 +54,7 @@ export class AuthenticationMailMesssages {
|
|||||||
* @returns {Mail}
|
* @returns {Mail}
|
||||||
*/
|
*/
|
||||||
signupVerificationMail(email: string, fullName: string, token: string) {
|
signupVerificationMail(email: string, fullName: string, token: string) {
|
||||||
const baseURL = this.configService.get('baseURL');
|
const baseURL = this.configService.get('app.baseUrl');
|
||||||
const verifyUrl = `${baseURL}/auth/email_confirmation?token=${token}&email=${email}`;
|
const verifyUrl = `${baseURL}/auth/email_confirmation?token=${token}&email=${email}`;
|
||||||
|
|
||||||
return new Mail()
|
return new Mail()
|
||||||
|
|||||||
@@ -7,17 +7,13 @@ import {
|
|||||||
import { GetAuthenticatedAccount } from './queries/GetAuthedAccount.service';
|
import { GetAuthenticatedAccount } from './queries/GetAuthedAccount.service';
|
||||||
import { Controller, Get, Post } from '@nestjs/common';
|
import { Controller, Get, Post } from '@nestjs/common';
|
||||||
import { Throttle } from '@nestjs/throttler';
|
import { Throttle } from '@nestjs/throttler';
|
||||||
import { IgnoreTenantSeededRoute } from '../Tenancy/EnsureTenantIsSeeded.guards';
|
import { TenantAgnosticRoute } from '../Tenancy/TenancyGlobal.guard';
|
||||||
import { IgnoreTenantInitializedRoute } from '../Tenancy/EnsureTenantIsInitialized.guard';
|
|
||||||
import { AuthenticationApplication } from './AuthApplication.sevice';
|
import { AuthenticationApplication } from './AuthApplication.sevice';
|
||||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
|
||||||
import { IgnoreUserVerifiedRoute } from './guards/EnsureUserVerified.guard';
|
import { IgnoreUserVerifiedRoute } from './guards/EnsureUserVerified.guard';
|
||||||
|
|
||||||
@Controller('/auth')
|
@Controller('/auth')
|
||||||
@ApiTags('Auth')
|
@ApiTags('Auth')
|
||||||
@ApiExcludeController()
|
@TenantAgnosticRoute()
|
||||||
@IgnoreTenantSeededRoute()
|
|
||||||
@IgnoreTenantInitializedRoute()
|
|
||||||
@IgnoreUserVerifiedRoute()
|
@IgnoreUserVerifiedRoute()
|
||||||
@Throttle({ auth: {} })
|
@Throttle({ auth: {} })
|
||||||
export class AuthedController {
|
export class AuthedController {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
IAuthSignedUpEventPayload,
|
IAuthSignedUpEventPayload,
|
||||||
IAuthSigningUpEventPayload,
|
IAuthSigningUpEventPayload,
|
||||||
} from '../Auth.interfaces';
|
} from '../Auth.interfaces';
|
||||||
import { defaultTo } from 'ramda';
|
|
||||||
import { ERRORS } from '../Auth.constants';
|
import { ERRORS } from '../Auth.constants';
|
||||||
import { hashPassword } from '../Auth.utils';
|
import { hashPassword } from '../Auth.utils';
|
||||||
import { ClsService } from 'nestjs-cls';
|
import { ClsService } from 'nestjs-cls';
|
||||||
@@ -51,7 +50,7 @@ export class AuthSignupService {
|
|||||||
const signupConfirmation = this.configService.get('signupConfirmation');
|
const signupConfirmation = this.configService.get('signupConfirmation');
|
||||||
|
|
||||||
const verifyTokenCrypto = crypto.randomBytes(64).toString('hex');
|
const verifyTokenCrypto = crypto.randomBytes(64).toString('hex');
|
||||||
const verifiedEnabed = defaultTo(signupConfirmation.enabled, false);
|
const verifiedEnabed = signupConfirmation.enabled ?? false;
|
||||||
const verifyToken = verifiedEnabed ? verifyTokenCrypto : '';
|
const verifyToken = verifiedEnabed ? verifyTokenCrypto : '';
|
||||||
const verified = !verifiedEnabed;
|
const verified = !verifiedEnabed;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { SystemUser } from '@/modules/System/models/SystemUser';
|
|||||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
import { ERRORS } from '../Auth.constants';
|
import { ERRORS } from '../Auth.constants';
|
||||||
import { events } from '@/common/events/events';
|
import { events } from '@/common/events/events';
|
||||||
import { ModelObject } from 'objection';
|
|
||||||
import { ISignUpConfigmResendedEventPayload } from '../Auth.interfaces';
|
import { ISignUpConfigmResendedEventPayload } from '../Auth.interfaces';
|
||||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
|
Delete,
|
||||||
Get,
|
Get,
|
||||||
Param,
|
Param,
|
||||||
Post,
|
Post,
|
||||||
@@ -14,6 +15,10 @@ import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
|||||||
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
import { CreditNoteAction } from '../CreditNotes/types/CreditNotes.types';
|
import { CreditNoteAction } from '../CreditNotes/types/CreditNotes.types';
|
||||||
|
import { GetCreditNoteAssociatedInvoicesToApply } from './queries/GetCreditNoteAssociatedInvoicesToApply.service';
|
||||||
|
import { CreditNoteApplyToInvoices } from './commands/CreditNoteApplyToInvoices.service';
|
||||||
|
import { DeleteCreditNoteApplyToInvoices } from './commands/DeleteCreditNoteApplyToInvoices.service';
|
||||||
|
import { ApplyCreditNoteToInvoicesDto } from './dtos/ApplyCreditNoteToInvoices.dto';
|
||||||
|
|
||||||
@Controller('credit-notes')
|
@Controller('credit-notes')
|
||||||
@ApiTags('Credit Notes Apply Invoice')
|
@ApiTags('Credit Notes Apply Invoice')
|
||||||
@@ -22,6 +27,9 @@ import { CreditNoteAction } from '../CreditNotes/types/CreditNotes.types';
|
|||||||
export class CreditNotesApplyInvoiceController {
|
export class CreditNotesApplyInvoiceController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly getCreditNoteAssociatedAppliedInvoicesService: GetCreditNoteAssociatedAppliedInvoices,
|
private readonly getCreditNoteAssociatedAppliedInvoicesService: GetCreditNoteAssociatedAppliedInvoices,
|
||||||
|
private readonly getCreditNoteAssociatedInvoicesToApplyService: GetCreditNoteAssociatedInvoicesToApply,
|
||||||
|
private readonly creditNoteApplyToInvoicesService: CreditNoteApplyToInvoices,
|
||||||
|
private readonly deleteCreditNoteApplyToInvoicesService: DeleteCreditNoteApplyToInvoices,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get(':creditNoteId/applied-invoices')
|
@Get(':creditNoteId/applied-invoices')
|
||||||
@@ -39,6 +47,23 @@ export class CreditNotesApplyInvoiceController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get(':creditNoteId/apply-invoices')
|
||||||
|
@RequirePermission(CreditNoteAction.View, AbilitySubject.CreditNote)
|
||||||
|
@ApiOperation({ summary: 'Get credit note associated invoices to apply' })
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: 'Credit note associated invoices to apply',
|
||||||
|
})
|
||||||
|
@ApiResponse({ status: 404, description: 'Credit note not found' })
|
||||||
|
@ApiResponse({ status: 400, description: 'Invalid input data' })
|
||||||
|
getCreditNoteAssociatedInvoicesToApply(
|
||||||
|
@Param('creditNoteId') creditNoteId: number,
|
||||||
|
) {
|
||||||
|
return this.getCreditNoteAssociatedInvoicesToApplyService.getCreditAssociatedInvoicesToApply(
|
||||||
|
creditNoteId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Post(':creditNoteId/apply-invoices')
|
@Post(':creditNoteId/apply-invoices')
|
||||||
@RequirePermission(CreditNoteAction.Edit, AbilitySubject.CreditNote)
|
@RequirePermission(CreditNoteAction.Edit, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Apply credit note to invoices' })
|
@ApiOperation({ summary: 'Apply credit note to invoices' })
|
||||||
@@ -48,9 +73,32 @@ export class CreditNotesApplyInvoiceController {
|
|||||||
})
|
})
|
||||||
@ApiResponse({ status: 404, description: 'Credit note not found' })
|
@ApiResponse({ status: 404, description: 'Credit note not found' })
|
||||||
@ApiResponse({ status: 400, description: 'Invalid input data' })
|
@ApiResponse({ status: 400, description: 'Invalid input data' })
|
||||||
applyCreditNoteToInvoices(@Param('creditNoteId') creditNoteId: number) {
|
applyCreditNoteToInvoices(
|
||||||
return this.getCreditNoteAssociatedAppliedInvoicesService.getCreditAssociatedAppliedInvoices(
|
@Param('creditNoteId') creditNoteId: number,
|
||||||
|
@Body() applyDto: ApplyCreditNoteToInvoicesDto,
|
||||||
|
) {
|
||||||
|
return this.creditNoteApplyToInvoicesService.applyCreditNoteToInvoices(
|
||||||
creditNoteId,
|
creditNoteId,
|
||||||
|
applyDto,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete('applied-invoices/:applyCreditToInvoicesId')
|
||||||
|
@RequirePermission(CreditNoteAction.Edit, AbilitySubject.CreditNote)
|
||||||
|
@ApiOperation({ summary: 'Delete applied credit note to invoice' })
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: 'Credit note application successfully deleted',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 404,
|
||||||
|
description: 'Credit note application not found',
|
||||||
|
})
|
||||||
|
deleteApplyCreditNoteToInvoices(
|
||||||
|
@Param('applyCreditToInvoicesId') applyCreditToInvoicesId: number,
|
||||||
|
) {
|
||||||
|
return this.deleteCreditNoteApplyToInvoicesService.deleteApplyCreditNoteToInvoices(
|
||||||
|
applyCreditToInvoicesId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ 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';
|
import { CreditNotesApplyInvoiceController } from './CreditNotesApplyInvoice.controller';
|
||||||
|
import { CreditNoteApplySyncCreditSubscriber } from './subscribers/CreditNoteApplySyncCreditSubscriber';
|
||||||
|
import { CreditNoteApplySyncInvoicesCreditedAmountSubscriber } from './subscribers/CreditNoteApplySyncInvoicesSubscriber';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
providers: [
|
providers: [
|
||||||
@@ -19,6 +21,8 @@ import { CreditNotesApplyInvoiceController } from './CreditNotesApplyInvoice.con
|
|||||||
CreditNoteApplySyncCredit,
|
CreditNoteApplySyncCredit,
|
||||||
GetCreditNoteAssociatedAppliedInvoices,
|
GetCreditNoteAssociatedAppliedInvoices,
|
||||||
GetCreditNoteAssociatedInvoicesToApply,
|
GetCreditNoteAssociatedInvoicesToApply,
|
||||||
|
CreditNoteApplySyncCreditSubscriber,
|
||||||
|
CreditNoteApplySyncInvoicesCreditedAmountSubscriber,
|
||||||
],
|
],
|
||||||
exports: [DeleteCustomerLinkedCreditNoteService],
|
exports: [DeleteCustomerLinkedCreditNoteService],
|
||||||
imports: [PaymentsReceivedModule, forwardRef(() => CreditNotesModule)],
|
imports: [PaymentsReceivedModule, forwardRef(() => CreditNotesModule)],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { Injectable, Inject } from '@nestjs/common';
|
import { Injectable, Inject } from '@nestjs/common';
|
||||||
import Bluebird from 'bluebird';
|
import * as Bluebird from 'bluebird';
|
||||||
import { ICreditNoteAppliedToInvoice } from '../types/CreditNoteApplyInvoice.types';
|
import { ICreditNoteAppliedToInvoice } from '../types/CreditNoteApplyInvoice.types';
|
||||||
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
||||||
import { CreditNoteAppliedInvoice } from '../models/CreditNoteAppliedInvoice';
|
import { CreditNoteAppliedInvoice } from '../models/CreditNoteAppliedInvoice';
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { sumBy } from 'lodash';
|
import { sumBy } from 'lodash';
|
||||||
import {
|
import {
|
||||||
|
ICreditNoteAppliedToInvoice,
|
||||||
ICreditNoteAppliedToInvoiceModel,
|
ICreditNoteAppliedToInvoiceModel,
|
||||||
IApplyCreditToInvoicesDTO,
|
IApplyCreditToInvoicesDTO,
|
||||||
IApplyCreditToInvoicesCreatedPayload,
|
IApplyCreditToInvoicesCreatedPayload,
|
||||||
@@ -17,6 +18,7 @@ import { CreditNote } from '@/modules/CreditNotes/models/CreditNote';
|
|||||||
import { CreditNoteAppliedInvoice } from '../models/CreditNoteAppliedInvoice';
|
import { CreditNoteAppliedInvoice } from '../models/CreditNoteAppliedInvoice';
|
||||||
import { CommandCreditNoteDTOTransform } from '@/modules/CreditNotes/commands/CommandCreditNoteDTOTransform.service';
|
import { CommandCreditNoteDTOTransform } from '@/modules/CreditNotes/commands/CommandCreditNoteDTOTransform.service';
|
||||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { ApplyCreditNoteToInvoicesDto } from '../dtos/ApplyCreditNoteToInvoices.dto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CreditNoteApplyToInvoices {
|
export class CreditNoteApplyToInvoices {
|
||||||
@@ -48,7 +50,7 @@ export class CreditNoteApplyToInvoices {
|
|||||||
*/
|
*/
|
||||||
public async applyCreditNoteToInvoices(
|
public async applyCreditNoteToInvoices(
|
||||||
creditNoteId: number,
|
creditNoteId: number,
|
||||||
applyCreditToInvoicesDTO: IApplyCreditToInvoicesDTO,
|
applyCreditToInvoicesDTO: ApplyCreditNoteToInvoicesDto,
|
||||||
): Promise<CreditNoteAppliedInvoice[]> {
|
): Promise<CreditNoteAppliedInvoice[]> {
|
||||||
// Saves the credit note or throw not found service error.
|
// Saves the credit note or throw not found service error.
|
||||||
const creditNote = await this.creditNoteModel()
|
const creditNote = await this.creditNoteModel()
|
||||||
@@ -71,7 +73,7 @@ export class CreditNoteApplyToInvoices {
|
|||||||
// Validate invoices has remaining amount to apply.
|
// Validate invoices has remaining amount to apply.
|
||||||
this.validateInvoicesRemainingAmount(
|
this.validateInvoicesRemainingAmount(
|
||||||
appliedInvoicesEntries,
|
appliedInvoicesEntries,
|
||||||
creditNoteAppliedModel.amount,
|
creditNoteAppliedModel.entries,
|
||||||
);
|
);
|
||||||
// Validate the credit note remaining amount.
|
// Validate the credit note remaining amount.
|
||||||
this.creditNoteDTOTransform.validateCreditRemainingAmount(
|
this.creditNoteDTOTransform.validateCreditRemainingAmount(
|
||||||
@@ -122,18 +124,20 @@ export class CreditNoteApplyToInvoices {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate the invoice remaining amount.
|
* Validate each invoice has sufficient remaining amount for the applied credit.
|
||||||
* @param {ISaleInvoice[]} invoices
|
* @param {ISaleInvoice[]} invoices
|
||||||
* @param {number} amount
|
* @param {ICreditNoteAppliedToInvoice[]} entries
|
||||||
*/
|
*/
|
||||||
private validateInvoicesRemainingAmount = (
|
private validateInvoicesRemainingAmount = (
|
||||||
invoices: SaleInvoice[],
|
invoices: SaleInvoice[],
|
||||||
amount: number,
|
entries: ICreditNoteAppliedToInvoice[],
|
||||||
) => {
|
) => {
|
||||||
const invalidInvoices = invoices.filter(
|
const invoiceMap = new Map(invoices.map((inv) => [inv.id, inv]));
|
||||||
(invoice) => invoice.dueAmount < amount,
|
const invalidEntries = entries.filter((entry) => {
|
||||||
);
|
const invoice = invoiceMap.get(entry.invoiceId);
|
||||||
if (invalidInvoices.length > 0) {
|
return invoice != null && invoice.dueAmount < entry.amount;
|
||||||
|
});
|
||||||
|
if (invalidEntries.length > 0) {
|
||||||
throw new ServiceError(ERRORS.INVOICES_HAS_NO_REMAINING_AMOUNT);
|
throw new ServiceError(ERRORS.INVOICES_HAS_NO_REMAINING_AMOUNT);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
|
import {
|
||||||
|
ArrayMinSize,
|
||||||
|
IsArray,
|
||||||
|
IsInt,
|
||||||
|
IsNotEmpty,
|
||||||
|
IsNumber,
|
||||||
|
ValidateNested,
|
||||||
|
} from 'class-validator';
|
||||||
|
|
||||||
|
export class ApplyCreditNoteInvoiceEntryDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsInt()
|
||||||
|
@ApiProperty({ description: 'Invoice ID to apply credit to', example: 1 })
|
||||||
|
invoiceId: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsNumber()
|
||||||
|
@ApiProperty({ description: 'Amount to apply', example: 100.5 })
|
||||||
|
amount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ApplyCreditNoteToInvoicesDto {
|
||||||
|
@IsArray()
|
||||||
|
@ArrayMinSize(1)
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => ApplyCreditNoteInvoiceEntryDto)
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Entries of invoice ID and amount to apply',
|
||||||
|
type: [ApplyCreditNoteInvoiceEntryDto],
|
||||||
|
example: [
|
||||||
|
{ invoice_id: 1, amount: 100.5 },
|
||||||
|
{ invoice_id: 2, amount: 50 },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
entries: ApplyCreditNoteInvoiceEntryDto[];
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import { CreditNoteApplySyncInvoicesCreditedAmount } from '../commands/CreditNot
|
|||||||
import { events } from '@/common/events/events';
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class CreditNoteApplySyncInvoicesCreditedAmountSubscriber {
|
export class CreditNoteApplySyncInvoicesCreditedAmountSubscriber {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly syncInvoicesWithCreditNote: CreditNoteApplySyncInvoicesCreditedAmount,
|
private readonly syncInvoicesWithCreditNote: CreditNoteApplySyncInvoicesCreditedAmount,
|
||||||
) {}
|
) {}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export interface IApplyCreditToInvoicesDeletedPayload {
|
|||||||
export interface ICreditNoteAppliedToInvoice {
|
export interface ICreditNoteAppliedToInvoice {
|
||||||
amount: number;
|
amount: number;
|
||||||
creditNoteId: number;
|
creditNoteId: number;
|
||||||
|
invoiceId: number;
|
||||||
}
|
}
|
||||||
export interface ICreditNoteAppliedToInvoiceModel {
|
export interface ICreditNoteAppliedToInvoiceModel {
|
||||||
amount: number;
|
amount: number;
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ export class CashFlowTable {
|
|||||||
section: ICashFlowStatementSection,
|
section: ICashFlowStatementSection,
|
||||||
): ICashFlowStatementSection => {
|
): ICashFlowStatementSection => {
|
||||||
const label = section.footerLabel
|
const label = section.footerLabel
|
||||||
? section.footerLabel
|
? this.i18n.t(section.footerLabel)
|
||||||
: this.i18n.t('financial_sheet.total_row', {
|
: this.i18n.t('financial_sheet.total_row', {
|
||||||
args: { value: section.label },
|
args: { value: section.label },
|
||||||
});
|
});
|
||||||
@@ -302,7 +302,7 @@ export class CashFlowTable {
|
|||||||
* @returns {ITableColumn}
|
* @returns {ITableColumn}
|
||||||
*/
|
*/
|
||||||
private totalColumns = (): ITableColumn[] => {
|
private totalColumns = (): ITableColumn[] => {
|
||||||
return [{ key: 'total', label: this.i18n.t('Total') }];
|
return [{ key: 'total', label: this.i18n.t('cash_flow_statement.total') }];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -366,7 +366,7 @@ export class CashFlowTable {
|
|||||||
*/
|
*/
|
||||||
public tableColumns = (): ITableColumn[] => {
|
public tableColumns = (): ITableColumn[] => {
|
||||||
return R.compose(
|
return R.compose(
|
||||||
R.concat([{ key: 'name', label: this.i18n.t('Account name') }]),
|
R.concat([{ key: 'name', label: this.i18n.t('cash_flow_statement.account_name') }]),
|
||||||
R.when(
|
R.when(
|
||||||
R.always(this.isDisplayColumnsBy(DISPLAY_COLUMNS_BY.DATE_PERIODS)),
|
R.always(this.isDisplayColumnsBy(DISPLAY_COLUMNS_BY.DATE_PERIODS)),
|
||||||
R.concat(this.datePeriodsColumns()),
|
R.concat(this.datePeriodsColumns()),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { I18nService } from 'nestjs-i18n';
|
||||||
import { FinancialSheetMeta } from '../../common/FinancialSheetMeta';
|
import { FinancialSheetMeta } from '../../common/FinancialSheetMeta';
|
||||||
import {
|
import {
|
||||||
ICashFlowStatementMeta,
|
ICashFlowStatementMeta,
|
||||||
@@ -8,7 +9,10 @@ import {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CashflowSheetMeta {
|
export class CashflowSheetMeta {
|
||||||
constructor(private readonly financialSheetMeta: FinancialSheetMeta) {}
|
constructor(
|
||||||
|
private readonly financialSheetMeta: FinancialSheetMeta,
|
||||||
|
private readonly i18n: I18nService,
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cashflow sheet meta.
|
* Cashflow sheet meta.
|
||||||
@@ -21,9 +25,11 @@ export class CashflowSheetMeta {
|
|||||||
const meta = await this.financialSheetMeta.meta();
|
const meta = await this.financialSheetMeta.meta();
|
||||||
const formattedToDate = moment(query.toDate).format('YYYY/MM/DD');
|
const formattedToDate = moment(query.toDate).format('YYYY/MM/DD');
|
||||||
const formattedFromDate = moment(query.fromDate).format('YYYY/MM/DD');
|
const formattedFromDate = moment(query.fromDate).format('YYYY/MM/DD');
|
||||||
const formattedDateRange = `From ${formattedFromDate} | To ${formattedToDate}`;
|
const fromLabel = this.i18n.t('cash_flow_statement.from_date');
|
||||||
|
const toLabel = this.i18n.t('cash_flow_statement.to_date');
|
||||||
|
const formattedDateRange = `${fromLabel} ${formattedFromDate} | ${toLabel} ${formattedToDate}`;
|
||||||
|
|
||||||
const sheetName = 'Statement of Cash Flow';
|
const sheetName = this.i18n.t('cash_flow_statement.sheet_name');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...meta,
|
...meta,
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export class CustomerBalanceSummaryTable {
|
|||||||
*/
|
*/
|
||||||
private getTotalColumnsAccessor = (): IColumnMapperMeta[] => {
|
private getTotalColumnsAccessor = (): IColumnMapperMeta[] => {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ key: 'name', value: this.i18n.t('Total') },
|
{ key: 'name', value: this.i18n.t('contact_summary_balance.total') },
|
||||||
{ key: 'total', accessor: 'total.formattedAmount' },
|
{ key: 'total', accessor: 'total.formattedAmount' },
|
||||||
];
|
];
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export class InventoryItemDetailsTable {
|
|||||||
): ITableRow => {
|
): ITableRow => {
|
||||||
const columns: Array<IColumnMapperMeta> = [
|
const columns: Array<IColumnMapperMeta> = [
|
||||||
{ key: 'date', accessor: 'date.formattedDate' },
|
{ key: 'date', accessor: 'date.formattedDate' },
|
||||||
{ key: 'closing', value: this.i18n.t('Opening balance') },
|
{ key: 'closing', value: this.i18n.t('inventory_item_details.opening_balance') },
|
||||||
{ key: 'empty', value: '' },
|
{ key: 'empty', value: '' },
|
||||||
{ key: 'quantity', accessor: 'quantity.formattedNumber' },
|
{ key: 'quantity', accessor: 'quantity.formattedNumber' },
|
||||||
{ key: 'empty', value: '' },
|
{ key: 'empty', value: '' },
|
||||||
@@ -115,7 +115,7 @@ export class InventoryItemDetailsTable {
|
|||||||
): ITableRow => {
|
): ITableRow => {
|
||||||
const columns: Array<IColumnMapperMeta> = [
|
const columns: Array<IColumnMapperMeta> = [
|
||||||
{ key: 'date', accessor: 'date.formattedDate' },
|
{ key: 'date', accessor: 'date.formattedDate' },
|
||||||
{ key: 'closing', value: this.i18n.t('Closing balance') },
|
{ key: 'closing', value: this.i18n.t('inventory_item_details.closing_balance') },
|
||||||
{ key: 'empty', value: '' },
|
{ key: 'empty', value: '' },
|
||||||
{ key: 'quantity', accessor: 'quantity.formattedNumber' },
|
{ key: 'quantity', accessor: 'quantity.formattedNumber' },
|
||||||
{ key: 'empty', value: '' },
|
{ key: 'empty', value: '' },
|
||||||
@@ -193,16 +193,16 @@ export class InventoryItemDetailsTable {
|
|||||||
*/
|
*/
|
||||||
public tableColumns = (): ITableColumn[] => {
|
public tableColumns = (): ITableColumn[] => {
|
||||||
return [
|
return [
|
||||||
{ key: 'date', label: this.i18n.t('Date') },
|
{ key: 'date', label: this.i18n.t('inventory_item_details.date') },
|
||||||
{ key: 'transaction_type', label: this.i18n.t('Transaction type') },
|
{ key: 'transaction_type', label: this.i18n.t('inventory_item_details.transaction_type') },
|
||||||
{ key: 'transaction_id', label: this.i18n.t('Transaction #') },
|
{ key: 'transaction_id', label: this.i18n.t('inventory_item_details.transaction_number') },
|
||||||
{ key: 'quantity', label: this.i18n.t('Quantity') },
|
{ key: 'quantity', label: this.i18n.t('inventory_item_details.quantity') },
|
||||||
{ key: 'rate', label: this.i18n.t('Rate') },
|
{ key: 'rate', label: this.i18n.t('inventory_item_details.rate') },
|
||||||
{ key: 'total', label: this.i18n.t('Total') },
|
{ key: 'total', label: this.i18n.t('inventory_item_details.total') },
|
||||||
{ key: 'value', label: this.i18n.t('Value') },
|
{ key: 'value', label: this.i18n.t('inventory_item_details.value') },
|
||||||
{ key: 'profit_margin', label: this.i18n.t('Profit Margin') },
|
{ key: 'profit_margin', label: this.i18n.t('inventory_item_details.profit_margin') },
|
||||||
{ key: 'running_quantity', label: this.i18n.t('Running quantity') },
|
{ key: 'running_quantity', label: this.i18n.t('inventory_item_details.running_quantity') },
|
||||||
{ key: 'running_value', label: this.i18n.t('Running Value') },
|
{ key: 'running_value', label: this.i18n.t('inventory_item_details.running_value') },
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export class TransactionsByContactsTableRows {
|
|||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
key: 'openingBalanceLabel',
|
key: 'openingBalanceLabel',
|
||||||
value: this.i18n.t('Opening balance') as string,
|
value: this.i18n.t('transactions_by_contact.opening_balance') as string,
|
||||||
},
|
},
|
||||||
...R.repeat({ key: 'empty', value: '' }, 5),
|
...R.repeat({ key: 'empty', value: '' }, 5),
|
||||||
{
|
{
|
||||||
@@ -76,7 +76,7 @@ export class TransactionsByContactsTableRows {
|
|||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
key: 'closingBalanceLabel',
|
key: 'closingBalanceLabel',
|
||||||
value: this.i18n.t('Closing balance') as string,
|
value: this.i18n.t('transactions_by_contact.closing_balance') as string,
|
||||||
},
|
},
|
||||||
...R.repeat({ key: 'empty', value: '' }, 5),
|
...R.repeat({ key: 'empty', value: '' }, 5),
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -141,10 +141,10 @@ export class TrialBalanceSheetTable extends R.compose(
|
|||||||
return R.compose(
|
return R.compose(
|
||||||
this.tableColumnsCellIndexing,
|
this.tableColumnsCellIndexing,
|
||||||
R.concat([
|
R.concat([
|
||||||
{ key: 'account', label: 'Account' },
|
{ key: 'account', label: this.i18n.t('trial_balance_sheet.account') },
|
||||||
{ key: 'debit', label: 'Debit' },
|
{ key: 'debit', label: this.i18n.t('trial_balance_sheet.debit') },
|
||||||
{ key: 'credit', label: 'Credit' },
|
{ key: 'credit', label: this.i18n.t('trial_balance_sheet.credit') },
|
||||||
{ key: 'total', label: 'Total' },
|
{ key: 'total', label: this.i18n.t('trial_balance_sheet.total') },
|
||||||
]),
|
]),
|
||||||
)([]);
|
)([]);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export class VendorBalanceSummaryTable {
|
|||||||
*/
|
*/
|
||||||
private getTotalColumnsAccessor = (): IColumnMapperMeta[] => {
|
private getTotalColumnsAccessor = (): IColumnMapperMeta[] => {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ key: 'name', value: this.i18n.t('Total') },
|
{ key: 'name', value: this.i18n.t('contact_summary_balance.total') },
|
||||||
{ key: 'total', accessor: 'total.formattedAmount' },
|
{ key: 'total', accessor: 'total.formattedAmount' },
|
||||||
];
|
];
|
||||||
return R.compose(
|
return R.compose(
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import { UpdateOrganizationService } from './commands/UpdateOrganization.service
|
|||||||
import { IgnoreTenantInitializedRoute } from '../Tenancy/EnsureTenantIsInitialized.guard';
|
import { IgnoreTenantInitializedRoute } from '../Tenancy/EnsureTenantIsInitialized.guard';
|
||||||
import { IgnoreTenantSeededRoute } from '../Tenancy/EnsureTenantIsSeeded.guards';
|
import { IgnoreTenantSeededRoute } from '../Tenancy/EnsureTenantIsSeeded.guards';
|
||||||
import { IgnoreTenantModelsInitialize } from '../Tenancy/TenancyInitializeModels.guard';
|
import { IgnoreTenantModelsInitialize } from '../Tenancy/TenancyInitializeModels.guard';
|
||||||
|
import { IgnoreUserVerifiedRoute } from '../Auth/guards/EnsureUserVerified.guard';
|
||||||
import { GetBuildOrganizationBuildJob } from './commands/GetBuildOrganizationJob.service';
|
import { GetBuildOrganizationBuildJob } from './commands/GetBuildOrganizationJob.service';
|
||||||
import { OrganizationBaseCurrencyLocking } from './Organization/OrganizationBaseCurrencyLocking.service';
|
import { OrganizationBaseCurrencyLocking } from './Organization/OrganizationBaseCurrencyLocking.service';
|
||||||
import {
|
import {
|
||||||
@@ -93,6 +94,7 @@ export class OrganizationController {
|
|||||||
|
|
||||||
@Get('current')
|
@Get('current')
|
||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
|
@IgnoreUserVerifiedRoute()
|
||||||
@ApiOperation({ summary: 'Get current organization' })
|
@ApiOperation({ summary: 'Get current organization' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
import { TenancyContext } from './TenancyContext.service';
|
import { TenancyContext } from './TenancyContext.service';
|
||||||
import { Reflector } from '@nestjs/core';
|
import { Reflector } from '@nestjs/core';
|
||||||
import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants';
|
import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants';
|
||||||
|
import { IS_TENANT_AGNOSTIC } from './TenancyGlobal.guard';
|
||||||
|
|
||||||
export const IS_IGNORE_TENANT_INITIALIZED = 'IS_IGNORE_TENANT_INITIALIZED';
|
export const IS_IGNORE_TENANT_INITIALIZED = 'IS_IGNORE_TENANT_INITIALIZED';
|
||||||
export const IgnoreTenantInitializedRoute = () =>
|
export const IgnoreTenantInitializedRoute = () =>
|
||||||
@@ -35,8 +36,12 @@ export class EnsureTenantIsInitializedGuard implements CanActivate {
|
|||||||
IS_PUBLIC_ROUTE,
|
IS_PUBLIC_ROUTE,
|
||||||
[context.getHandler(), context.getClass()],
|
[context.getHandler(), context.getClass()],
|
||||||
);
|
);
|
||||||
|
const isTenantAgnostic = this.reflector.getAllAndOverride<boolean>(
|
||||||
|
IS_TENANT_AGNOSTIC,
|
||||||
|
[context.getHandler(), context.getClass()],
|
||||||
|
);
|
||||||
// Skip the guard early if the route marked as public or ignored.
|
// Skip the guard early if the route marked as public or ignored.
|
||||||
if (isPublic || isIgnoreEnsureTenantInitialized) {
|
if (isPublic || isIgnoreEnsureTenantInitialized || isTenantAgnostic) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const tenant = await this.tenancyContext.getTenant();
|
const tenant = await this.tenancyContext.getTenant();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
import { TenancyContext } from './TenancyContext.service';
|
import { TenancyContext } from './TenancyContext.service';
|
||||||
import { Reflector } from '@nestjs/core';
|
import { Reflector } from '@nestjs/core';
|
||||||
import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants';
|
import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants';
|
||||||
|
import { IS_TENANT_AGNOSTIC } from './TenancyGlobal.guard';
|
||||||
|
|
||||||
export const IS_IGNORE_TENANT_SEEDED = 'IS_IGNORE_TENANT_SEEDED';
|
export const IS_IGNORE_TENANT_SEEDED = 'IS_IGNORE_TENANT_SEEDED';
|
||||||
export const IgnoreTenantSeededRoute = () =>
|
export const IgnoreTenantSeededRoute = () =>
|
||||||
@@ -36,7 +37,12 @@ export class EnsureTenantIsSeededGuard implements CanActivate {
|
|||||||
context.getHandler(),
|
context.getHandler(),
|
||||||
context.getClass(),
|
context.getClass(),
|
||||||
]);
|
]);
|
||||||
if (isPublic || isIgnoreEnsureTenantSeeded) {
|
const isTenantAgnostic = this.reflector.getAllAndOverride<boolean>(
|
||||||
|
IS_TENANT_AGNOSTIC,
|
||||||
|
[context.getHandler(), context.getClass()],
|
||||||
|
);
|
||||||
|
// Skip the guard early if the route marked as public, tenant agnostic or ignored.
|
||||||
|
if (isPublic || isIgnoreEnsureTenantSeeded || isTenantAgnostic) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const tenant = await this.tenancyContext.getTenant();
|
const tenant = await this.tenancyContext.getTenant();
|
||||||
|
|||||||
19
packages/server/src/modules/Trpc/Trpc.context.ts
Normal file
19
packages/server/src/modules/Trpc/Trpc.context.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { TRPCContext, ContextOptions } from 'nestjs-trpc';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TrpcContext implements TRPCContext {
|
||||||
|
async create(opts: ContextOptions): Promise<Record<string, unknown>> {
|
||||||
|
const { req } = opts;
|
||||||
|
|
||||||
|
// Extract auth token and organization from headers
|
||||||
|
const token = req.headers['x-access-token'];
|
||||||
|
const organizationId = req.headers['organization-id'];
|
||||||
|
|
||||||
|
return {
|
||||||
|
token,
|
||||||
|
organizationId: organizationId ? parseInt(organizationId as string, 10) : null,
|
||||||
|
req,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
19
packages/server/src/modules/Trpc/Trpc.module.ts
Normal file
19
packages/server/src/modules/Trpc/Trpc.module.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TRPCModule } from 'nestjs-trpc';
|
||||||
|
import { TrpcService } from './Trpc.service';
|
||||||
|
import { TrpcContext } from './Trpc.context';
|
||||||
|
import { AccountsTrpcRouter } from './routers/Accounts.router';
|
||||||
|
import { AccountsModule } from '@/modules/Accounts/Accounts.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
TRPCModule.forRoot({
|
||||||
|
basePath: '/api/trpc',
|
||||||
|
context: TrpcContext,
|
||||||
|
}),
|
||||||
|
AccountsModule,
|
||||||
|
],
|
||||||
|
providers: [TrpcService, TrpcContext, AccountsTrpcRouter],
|
||||||
|
exports: [TrpcService],
|
||||||
|
})
|
||||||
|
export class AppTrpcModule {}
|
||||||
13
packages/server/src/modules/Trpc/Trpc.service.ts
Normal file
13
packages/server/src/modules/Trpc/Trpc.service.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
|
||||||
|
export interface TrpcContext {
|
||||||
|
req: Request;
|
||||||
|
res: Response;
|
||||||
|
user: any;
|
||||||
|
organizationId: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TrpcService {
|
||||||
|
}
|
||||||
192
packages/server/src/modules/Trpc/routers/Accounts.router.ts
Normal file
192
packages/server/src/modules/Trpc/routers/Accounts.router.ts
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Router, Query, Mutation } from 'nestjs-trpc';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { AccountsApplication } from '@/modules/Accounts/AccountsApplication.service';
|
||||||
|
import { CreateAccountDTO } from '@/modules/Accounts/CreateAccount.dto';
|
||||||
|
import { EditAccountDTO } from '@/modules/Accounts/EditAccount.dto';
|
||||||
|
import { IAccountsStructureType } from '@/modules/Accounts/Accounts.types';
|
||||||
|
|
||||||
|
const accountResponseSchema = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
name: z.string(),
|
||||||
|
slug: z.string(),
|
||||||
|
code: z.string(),
|
||||||
|
index: z.number(),
|
||||||
|
accountType: z.string(),
|
||||||
|
accountTypeLabel: z.string(),
|
||||||
|
parentAccountId: z.number().nullable(),
|
||||||
|
predefined: z.boolean(),
|
||||||
|
currencyCode: z.string(),
|
||||||
|
active: z.boolean(),
|
||||||
|
bankBalance: z.number(),
|
||||||
|
bankBalanceFormatted: z.string(),
|
||||||
|
lastFeedsUpdatedAt: z.union([z.string(), z.date(), z.null()]),
|
||||||
|
lastFeedsUpdatedAtFormatted: z.string(),
|
||||||
|
amount: z.number(),
|
||||||
|
formattedAmount: z.string(),
|
||||||
|
plaidItemId: z.string(),
|
||||||
|
plaidAccountId: z.string().nullable(),
|
||||||
|
isFeedsActive: z.boolean(),
|
||||||
|
isSyncingOwner: z.boolean(),
|
||||||
|
isFeedsPaused: z.boolean(),
|
||||||
|
accountNormal: z.string(),
|
||||||
|
accountNormalFormatted: z.string(),
|
||||||
|
flattenName: z.string(),
|
||||||
|
accountLevel: z.number().optional(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const accountTypeSchema = z.object({
|
||||||
|
label: z.string(),
|
||||||
|
key: z.string(),
|
||||||
|
normal: z.string(),
|
||||||
|
parentType: z.string(),
|
||||||
|
rootType: z.string(),
|
||||||
|
multiCurrency: z.boolean(),
|
||||||
|
balanceSheet: z.boolean(),
|
||||||
|
incomeSheet: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const getAccountsQuerySchema = z.object({
|
||||||
|
onlyInactive: z.boolean().optional(),
|
||||||
|
structure: z.nativeEnum(IAccountsStructureType).optional(),
|
||||||
|
page: z.number().optional(),
|
||||||
|
pageSize: z.number().optional(),
|
||||||
|
searchKeyword: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const getAccountsResponseSchema = z.object({
|
||||||
|
accounts: z.array(z.any()),
|
||||||
|
filterMeta: z.object({
|
||||||
|
count: z.number(),
|
||||||
|
total: z.number(),
|
||||||
|
page: z.number(),
|
||||||
|
pageSize: z.number(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const getAccountTransactionsQuerySchema = z.object({
|
||||||
|
accountId: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const createAccountInputSchema = z.object({
|
||||||
|
name: z.string().min(3).max(255),
|
||||||
|
code: z.string().min(3).max(6).optional(),
|
||||||
|
currencyCode: z.string().optional(),
|
||||||
|
accountType: z.string().min(3).max(255),
|
||||||
|
description: z.string().max(65535).optional(),
|
||||||
|
parentAccountId: z.number().optional(),
|
||||||
|
active: z.boolean().optional(),
|
||||||
|
plaidAccountId: z.string().optional(),
|
||||||
|
plaidItemId: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const editAccountInputSchema = createAccountInputSchema.partial();
|
||||||
|
|
||||||
|
const bulkDeleteInputSchema = z.object({
|
||||||
|
ids: z.array(z.number()),
|
||||||
|
skipUndeletable: z.boolean().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const validateBulkDeleteResponseSchema = z.object({
|
||||||
|
deletableIds: z.array(z.number()),
|
||||||
|
nonDeletableIds: z.array(z.number()),
|
||||||
|
deletableCount: z.number(),
|
||||||
|
nonDeletableCount: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
@Router({ alias: 'accounts' })
|
||||||
|
export class AccountsTrpcRouter {
|
||||||
|
constructor(private readonly accountsApplication: AccountsApplication) {}
|
||||||
|
|
||||||
|
@Query({
|
||||||
|
input: getAccountsQuerySchema,
|
||||||
|
output: getAccountsResponseSchema,
|
||||||
|
})
|
||||||
|
async getAccounts(input: z.infer<typeof getAccountsQuerySchema>) {
|
||||||
|
return this.accountsApplication.getAccounts(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Query({
|
||||||
|
input: z.object({ id: z.number() }),
|
||||||
|
output: accountResponseSchema,
|
||||||
|
})
|
||||||
|
async getAccount(input: { id: number }) {
|
||||||
|
return this.accountsApplication.getAccount(input.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Query({
|
||||||
|
output: z.array(accountTypeSchema),
|
||||||
|
})
|
||||||
|
async getAccountTypes() {
|
||||||
|
return this.accountsApplication.getAccountTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Query({
|
||||||
|
input: getAccountTransactionsQuerySchema,
|
||||||
|
output: z.array(z.any()),
|
||||||
|
})
|
||||||
|
async getAccountTransactions(input: z.infer<typeof getAccountTransactionsQuerySchema>) {
|
||||||
|
return this.accountsApplication.getAccountsTransactions({
|
||||||
|
accountId: input.accountId,
|
||||||
|
limit: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation({
|
||||||
|
input: createAccountInputSchema,
|
||||||
|
})
|
||||||
|
async createAccount(input: z.infer<typeof createAccountInputSchema>) {
|
||||||
|
return this.accountsApplication.createAccount(input as CreateAccountDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation({
|
||||||
|
input: z.object({
|
||||||
|
id: z.number(),
|
||||||
|
data: editAccountInputSchema,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
async editAccount(input: { id: number; data: any }) {
|
||||||
|
return this.accountsApplication.editAccount(input.id, input.data as EditAccountDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation({
|
||||||
|
input: z.object({ id: z.number() }),
|
||||||
|
})
|
||||||
|
async deleteAccount(input: { id: number }) {
|
||||||
|
return this.accountsApplication.deleteAccount(input.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation({
|
||||||
|
input: z.object({ id: z.number() }),
|
||||||
|
})
|
||||||
|
async activateAccount(input: { id: number }) {
|
||||||
|
return this.accountsApplication.activateAccount(input.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation({
|
||||||
|
input: z.object({ id: z.number() }),
|
||||||
|
})
|
||||||
|
async inactivateAccount(input: { id: number }) {
|
||||||
|
return this.accountsApplication.inactivateAccount(input.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation({
|
||||||
|
input: bulkDeleteInputSchema,
|
||||||
|
})
|
||||||
|
async bulkDeleteAccounts(input: z.infer<typeof bulkDeleteInputSchema>) {
|
||||||
|
return this.accountsApplication.bulkDeleteAccounts(input.ids, {
|
||||||
|
skipUndeletable: input.skipUndeletable ?? false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation({
|
||||||
|
input: z.object({ ids: z.array(z.number()) }),
|
||||||
|
output: validateBulkDeleteResponseSchema,
|
||||||
|
})
|
||||||
|
async validateBulkDeleteAccounts(input: { ids: number[] }) {
|
||||||
|
return this.accountsApplication.validateBulkDeleteAccounts(input.ids);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ import { GetUsersService } from './queries/GetUsers.service';
|
|||||||
import { AcceptInviteUserService } from './commands/AcceptInviteUser.service';
|
import { AcceptInviteUserService } from './commands/AcceptInviteUser.service';
|
||||||
import { InviteTenantUserService } from './commands/InviteUser.service';
|
import { InviteTenantUserService } from './commands/InviteUser.service';
|
||||||
import { UsersInviteController } from './UsersInvite.controller';
|
import { UsersInviteController } from './UsersInvite.controller';
|
||||||
|
import { UsersInvitePublicController } from './UsersInvitePublic.controller';
|
||||||
import { InjectSystemModel } from '../System/SystemModels/SystemModels.module';
|
import { InjectSystemModel } from '../System/SystemModels/SystemModels.module';
|
||||||
import { SendInviteUserMailQueue } from './Users.constants';
|
import { SendInviteUserMailQueue } from './Users.constants';
|
||||||
import InviteSendMainNotificationSubscribe from './subscribers/InviteSendMailNotification.subscriber';
|
import InviteSendMainNotificationSubscribe from './subscribers/InviteSendMailNotification.subscriber';
|
||||||
@@ -60,6 +61,6 @@ const models = [InjectSystemModel(UserInvite)];
|
|||||||
SendInviteUsersMailMessage,
|
SendInviteUsersMailMessage,
|
||||||
UsersApplication
|
UsersApplication
|
||||||
],
|
],
|
||||||
controllers: [UsersController, UsersInviteController],
|
controllers: [UsersController, UsersInviteController, UsersInvitePublicController],
|
||||||
})
|
})
|
||||||
export class UsersModule {}
|
export class UsersModule {}
|
||||||
|
|||||||
@@ -1,40 +1,13 @@
|
|||||||
import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common';
|
import { Body, Controller, Param, Patch, Post } from '@nestjs/common';
|
||||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
import { UsersApplication } from './Users.application';
|
import { UsersApplication } from './Users.application';
|
||||||
import { InviteUserDto, SendInviteUserDto } from './dtos/InviteUser.dto';
|
import { SendInviteUserDto } from './dtos/InviteUser.dto';
|
||||||
|
|
||||||
@Controller('invite')
|
@Controller('invite')
|
||||||
@ApiTags('Users')
|
@ApiTags('Users')
|
||||||
export class UsersInviteController {
|
export class UsersInviteController {
|
||||||
constructor(private readonly usersApplication: UsersApplication) {}
|
constructor(private readonly usersApplication: UsersApplication) {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Accept a user invitation.
|
|
||||||
*/
|
|
||||||
@Post('accept/:token')
|
|
||||||
@ApiOperation({ summary: 'Accept a user invitation.' })
|
|
||||||
async acceptInvite(
|
|
||||||
@Param('token') token: string,
|
|
||||||
@Body() inviteUserDTO: InviteUserDto,
|
|
||||||
) {
|
|
||||||
await this.usersApplication.acceptInvite(token, inviteUserDTO);
|
|
||||||
|
|
||||||
return {
|
|
||||||
message: 'The invitation has been accepted successfully.',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if an invitation token is valid.
|
|
||||||
*/
|
|
||||||
@Get('check/:token')
|
|
||||||
@ApiOperation({ summary: 'Check if an invitation token is valid.' })
|
|
||||||
async checkInvite(@Param('token') token: string) {
|
|
||||||
const inviteDetails = await this.usersApplication.checkInvite(token);
|
|
||||||
|
|
||||||
return inviteDetails;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send an invitation to a new user.
|
* Send an invitation to a new user.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
|
||||||
|
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
|
import { PublicRoute } from '@/modules/Auth/guards/jwt.guard';
|
||||||
|
import { UsersApplication } from './Users.application';
|
||||||
|
import { InviteUserDto } from './dtos/InviteUser.dto';
|
||||||
|
|
||||||
|
@Controller('invite')
|
||||||
|
@ApiTags('Users')
|
||||||
|
@PublicRoute()
|
||||||
|
export class UsersInvitePublicController {
|
||||||
|
constructor(private readonly usersApplication: UsersApplication) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept a user invitation.
|
||||||
|
*/
|
||||||
|
@Post('accept/:token')
|
||||||
|
@ApiOperation({ summary: 'Accept a user invitation.' })
|
||||||
|
async acceptInvite(
|
||||||
|
@Param('token') token: string,
|
||||||
|
@Body() inviteUserDTO: InviteUserDto,
|
||||||
|
) {
|
||||||
|
await this.usersApplication.acceptInvite(token, inviteUserDTO);
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: 'The invitation has been accepted successfully.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an invitation token is valid.
|
||||||
|
*/
|
||||||
|
@Get('check/:token')
|
||||||
|
@ApiOperation({ summary: 'Check if an invitation token is valid.' })
|
||||||
|
async checkInvite(@Param('token') token: string) {
|
||||||
|
const inviteDetails = await this.usersApplication.checkInvite(token);
|
||||||
|
|
||||||
|
return inviteDetails;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { ClsService } from 'nestjs-cls';
|
||||||
import {
|
import {
|
||||||
IAcceptInviteEventPayload,
|
IAcceptInviteEventPayload,
|
||||||
ICheckInviteEventPayload,
|
ICheckInviteEventPayload,
|
||||||
@@ -15,6 +16,11 @@ import { UserInvite } from '../models/InviteUser.model';
|
|||||||
import { ModelObject } from 'objection';
|
import { ModelObject } from 'objection';
|
||||||
import { InviteUserDto } from '../dtos/InviteUser.dto';
|
import { InviteUserDto } from '../dtos/InviteUser.dto';
|
||||||
|
|
||||||
|
interface InviteAcceptResponseDto {
|
||||||
|
inviteToken: { email: string, token: string, createdAt: Date };
|
||||||
|
orgName: string
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AcceptInviteUserService {
|
export class AcceptInviteUserService {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -27,6 +33,7 @@ export class AcceptInviteUserService {
|
|||||||
@Inject(UserInvite.name)
|
@Inject(UserInvite.name)
|
||||||
private readonly userInviteModel: typeof UserInvite,
|
private readonly userInviteModel: typeof UserInvite,
|
||||||
private readonly eventEmitter: EventEmitter2,
|
private readonly eventEmitter: EventEmitter2,
|
||||||
|
private readonly cls: ClsService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,6 +69,16 @@ export class AcceptInviteUserService {
|
|||||||
// Clear invite token by the given user id.
|
// Clear invite token by the given user id.
|
||||||
await this.clearInviteTokensByUserId(inviteToken.userId);
|
await this.clearInviteTokensByUserId(inviteToken.userId);
|
||||||
|
|
||||||
|
// Retrieve the tenant to get the organizationId for CLS.
|
||||||
|
const tenant = await this.tenantModel
|
||||||
|
.query()
|
||||||
|
.findById(inviteToken.tenantId);
|
||||||
|
|
||||||
|
// Set CLS values for tenant context before triggering sync events.
|
||||||
|
this.cls.set('tenantId', inviteToken.tenantId);
|
||||||
|
this.cls.set('userId', systemUser.id);
|
||||||
|
this.cls.set('organizationId', tenant.organizationId);
|
||||||
|
|
||||||
// Triggers `onUserAcceptInvite` event.
|
// Triggers `onUserAcceptInvite` event.
|
||||||
await this.eventEmitter.emitAsync(events.inviteUser.acceptInvite, {
|
await this.eventEmitter.emitAsync(events.inviteUser.acceptInvite, {
|
||||||
inviteToken,
|
inviteToken,
|
||||||
@@ -77,7 +94,7 @@ export class AcceptInviteUserService {
|
|||||||
*/
|
*/
|
||||||
public async checkInvite(
|
public async checkInvite(
|
||||||
token: string,
|
token: string,
|
||||||
): Promise<{ inviteToken: ModelObject<UserInvite>; orgName: string }> {
|
): Promise<InviteAcceptResponseDto> {
|
||||||
const inviteToken = await this.getInviteTokenOrThrowError(token);
|
const inviteToken = await this.getInviteTokenOrThrowError(token);
|
||||||
|
|
||||||
// Find the tenant that associated to the given token.
|
// Find the tenant that associated to the given token.
|
||||||
@@ -92,7 +109,16 @@ export class AcceptInviteUserService {
|
|||||||
tenant,
|
tenant,
|
||||||
} as ICheckInviteEventPayload);
|
} as ICheckInviteEventPayload);
|
||||||
|
|
||||||
return { inviteToken, orgName: tenant.metadata.name };
|
// Explicitly convert to plain object to ensure all fields are serialized
|
||||||
|
const result = {
|
||||||
|
inviteToken: {
|
||||||
|
email: inviteToken.email,
|
||||||
|
token: inviteToken.token,
|
||||||
|
createdAt: inviteToken.createdAt,
|
||||||
|
},
|
||||||
|
orgName: tenant.metadata.name,
|
||||||
|
};
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export class SendInviteUsersMailMessage {
|
|||||||
) {
|
) {
|
||||||
const tenant = await this.tenancyContext.getTenant(true);
|
const tenant = await this.tenancyContext.getTenant(true);
|
||||||
const root = path.join(global.__images_dirname, '/bigcapital.png');
|
const root = path.join(global.__images_dirname, '/bigcapital.png');
|
||||||
const baseURL = this.configService.get('baseURL');
|
const baseURL = this.configService.get('app.baseUrl');
|
||||||
|
|
||||||
const mail = new Mail()
|
const mail = new Mail()
|
||||||
.setSubject(`${fromUser.firstName} has invited you to join a Bigcapital`)
|
.setSubject(`${fromUser.firstName} has invited you to join a Bigcapital`)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export class UserInvite extends BaseModel {
|
|||||||
userId!: number;
|
userId!: number;
|
||||||
tenantId!: number;
|
tenantId!: number;
|
||||||
email!: string;
|
email!: string;
|
||||||
|
createdAt!: Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
@@ -32,4 +33,11 @@ export class UserInvite extends BaseModel {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called before inserting a new record.
|
||||||
|
*/
|
||||||
|
$beforeInsert() {
|
||||||
|
this.createdAt = new Date();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { omit } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { OnEvent } from '@nestjs/event-emitter';
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
@@ -22,13 +22,12 @@ export class SyncTenantAcceptInviteSubscriber {
|
|||||||
async syncTenantAcceptInvite({
|
async syncTenantAcceptInvite({
|
||||||
inviteToken,
|
inviteToken,
|
||||||
user,
|
user,
|
||||||
inviteUserDTO,
|
|
||||||
}: IAcceptInviteEventPayload) {
|
}: IAcceptInviteEventPayload) {
|
||||||
await this.tenantUserModel()
|
await this.tenantUserModel()
|
||||||
.query()
|
.query()
|
||||||
.where('systemUserId', inviteToken.userId)
|
.where('systemUserId', inviteToken.userId)
|
||||||
.update({
|
.update({
|
||||||
...omit(inviteUserDTO, ['password']),
|
...pick(user, ['firstName', 'lastName', 'email', 'active']),
|
||||||
inviteAcceptedAt: moment().format('YYYY-MM-DD'),
|
inviteAcceptedAt: moment().format('YYYY-MM-DD'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import Bluebird from 'bluebird';
|
import * as Bluebird from 'bluebird';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { IVendorCreditAppliedBill } from '../types/VendorCreditApplyBills.types';
|
import { IVendorCreditAppliedBill } from '../types/VendorCreditApplyBills.types';
|
||||||
|
|||||||
@@ -95,6 +95,10 @@
|
|||||||
"react-plaid-link": "^3.2.1",
|
"react-plaid-link": "^3.2.1",
|
||||||
"react-query": "^3.6.0",
|
"react-query": "^3.6.0",
|
||||||
"react-query-devtools": "^2.1.1",
|
"react-query-devtools": "^2.1.1",
|
||||||
|
"@trpc/client": "^11.0.0-rc.648",
|
||||||
|
"@trpc/react-query": "^11.0.0-rc.648",
|
||||||
|
"@tanstack/react-query": "^5.62.0",
|
||||||
|
"superjson": "^2.2.2",
|
||||||
"react-redux": "^7.2.9",
|
"react-redux": "^7.2.9",
|
||||||
"react-router": "5.3.4",
|
"react-router": "5.3.4",
|
||||||
"react-router-breadcrumbs-hoc": "^3.2.10",
|
"react-router-breadcrumbs-hoc": "^3.2.10",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Router, Switch, Route } from 'react-router';
|
|||||||
import { createBrowserHistory } from 'history';
|
import { createBrowserHistory } from 'history';
|
||||||
import { QueryClientProvider, QueryClient } from 'react-query';
|
import { QueryClientProvider, QueryClient } from 'react-query';
|
||||||
import { ReactQueryDevtools } from 'react-query/devtools';
|
import { ReactQueryDevtools } from 'react-query/devtools';
|
||||||
|
import { trpc, trpcClient, queryClient } from '@/trpc';
|
||||||
|
|
||||||
import '@/style/App.scss';
|
import '@/style/App.scss';
|
||||||
import 'moment/locale/ar-ly';
|
import 'moment/locale/ar-ly';
|
||||||
@@ -86,6 +87,7 @@ export default function App() {
|
|||||||
const queryClient = new QueryClient(queryConfig);
|
const queryClient = new QueryClient(queryConfig);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<trpc.Provider client={trpcClient} queryClient={queryClient}>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<SplashScreen />
|
<SplashScreen />
|
||||||
|
|
||||||
@@ -95,5 +97,6 @@ export default function App() {
|
|||||||
|
|
||||||
<ReactQueryDevtools initialIsOpen />
|
<ReactQueryDevtools initialIsOpen />
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
|
</trpc.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { Features } from '@/constants';
|
|||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
import { DashboardInsider } from '@/components';
|
import { DashboardInsider } from '@/components';
|
||||||
import {
|
import {
|
||||||
useAccounts,
|
|
||||||
useAutoCompleteContacts,
|
useAutoCompleteContacts,
|
||||||
useCurrencies,
|
useCurrencies,
|
||||||
useJournal,
|
useJournal,
|
||||||
@@ -13,6 +12,7 @@ import {
|
|||||||
useBranches,
|
useBranches,
|
||||||
useSettingsManualJournals,
|
useSettingsManualJournals,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { useProjects } from '@/containers/Projects/hooks';
|
import { useProjects } from '@/containers/Projects/hooks';
|
||||||
|
|
||||||
const MakeJournalFormContext = createContext();
|
const MakeJournalFormContext = createContext();
|
||||||
@@ -27,7 +27,7 @@ function MakeJournalProvider({ journalId, query, ...props }) {
|
|||||||
const isProjectFeatureCan = featureCan(Features.Projects);
|
const isProjectFeatureCan = featureCan(Features.Projects);
|
||||||
|
|
||||||
// Load the accounts list.
|
// Load the accounts list.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
// Load the customers list.
|
// Load the customers list.
|
||||||
const { data: contacts, isLoading: isContactsLoading } =
|
const { data: contacts, isLoading: isContactsLoading } =
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { createContext } from 'react';
|
import React, { createContext } from 'react';
|
||||||
import { DashboardInsider } from '@/components';
|
import { DashboardInsider } from '@/components';
|
||||||
import { useResourceViews, useResourceMeta, useAccounts } from '@/hooks/query';
|
import { useResourceViews, useResourceMeta } from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { getFieldsFromResourceMeta } from '@/utils';
|
import { getFieldsFromResourceMeta } from '@/utils';
|
||||||
|
|
||||||
const AccountsChartContext = createContext();
|
const AccountsChartContext = createContext();
|
||||||
@@ -26,7 +27,7 @@ function AccountsChartProvider({ query, tableStateChanged, ...props }) {
|
|||||||
data: accounts,
|
data: accounts,
|
||||||
isFetching: isAccountsFetching,
|
isFetching: isAccountsFetching,
|
||||||
isLoading: isAccountsLoading,
|
isLoading: isAccountsLoading,
|
||||||
} = useAccounts(query, { keepPreviousData: true });
|
} = useAccountsTrpc(query, { placeholderData: (previousData) => previousData });
|
||||||
|
|
||||||
// Provider payload.
|
// Provider payload.
|
||||||
const provider = {
|
const provider = {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { DialogsName } from '@/constants/dialogs';
|
import { DialogsName } from '@/constants/dialogs';
|
||||||
import { useValidateBulkDeleteAccounts } from '@/hooks/query/accounts';
|
import { useValidateBulkDeleteAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { useBulkDeleteDialog } from '@/hooks/dialogs/useBulkDeleteDialog';
|
import { useBulkDeleteDialog } from '@/hooks/dialogs/useBulkDeleteDialog';
|
||||||
|
|
||||||
export const useBulkDeleteAccountsDialog = () => {
|
export const useBulkDeleteAccountsDialog = () => {
|
||||||
const validateBulkDeleteMutation = useValidateBulkDeleteAccounts();
|
const validateBulkDeleteMutation = useValidateBulkDeleteAccountsTrpc();
|
||||||
const {
|
const {
|
||||||
openBulkDeleteDialog,
|
openBulkDeleteDialog,
|
||||||
closeBulkDeleteDialog,
|
closeBulkDeleteDialog,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { AppToaster, FormattedMessage as T } from '@/components';
|
|||||||
import { withAlertStoreConnect } from '@/containers/Alert/withAlertStoreConnect';
|
import { withAlertStoreConnect } from '@/containers/Alert/withAlertStoreConnect';
|
||||||
import { withAlertActions } from '@/containers/Alert/withAlertActions';
|
import { withAlertActions } from '@/containers/Alert/withAlertActions';
|
||||||
|
|
||||||
import { useActivateAccount } from '@/hooks/query';
|
import { useActivateAccountTrpc } from '@/hooks/trpc';
|
||||||
import { compose } from '@/utils';
|
import { compose } from '@/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,7 +25,7 @@ function AccountActivateAlert({
|
|||||||
const {
|
const {
|
||||||
mutateAsync: activateAccount,
|
mutateAsync: activateAccount,
|
||||||
isLoading
|
isLoading
|
||||||
} = useActivateAccount();
|
} = useActivateAccountTrpc();
|
||||||
|
|
||||||
// Handle alert cancel.
|
// Handle alert cancel.
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { withAlertStoreConnect } from '@/containers/Alert/withAlertStoreConnect'
|
|||||||
import { withAlertActions } from '@/containers/Alert/withAlertActions';
|
import { withAlertActions } from '@/containers/Alert/withAlertActions';
|
||||||
import { withDrawerActions } from '@/containers/Drawer/withDrawerActions';
|
import { withDrawerActions } from '@/containers/Drawer/withDrawerActions';
|
||||||
|
|
||||||
import { useDeleteAccount } from '@/hooks/query';
|
import { useDeleteAccountTrpc } from '@/hooks/trpc';
|
||||||
import { compose } from '@/utils';
|
import { compose } from '@/utils';
|
||||||
import { DRAWERS } from '@/constants/drawers';
|
import { DRAWERS } from '@/constants/drawers';
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ function AccountDeleteAlert({
|
|||||||
// #withDrawerActions
|
// #withDrawerActions
|
||||||
closeDrawer,
|
closeDrawer,
|
||||||
}) {
|
}) {
|
||||||
const { isLoading, mutateAsync: deleteAccount } = useDeleteAccount();
|
const { isLoading, mutateAsync: deleteAccount } = useDeleteAccountTrpc();
|
||||||
|
|
||||||
// handle cancel delete account alert.
|
// handle cancel delete account alert.
|
||||||
const handleCancelAccountDelete = () => {
|
const handleCancelAccountDelete = () => {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { withAlertStoreConnect } from '@/containers/Alert/withAlertStoreConnect'
|
|||||||
import { withAlertActions } from '@/containers/Alert/withAlertActions';
|
import { withAlertActions } from '@/containers/Alert/withAlertActions';
|
||||||
|
|
||||||
import { compose } from '@/utils';
|
import { compose } from '@/utils';
|
||||||
import { useInactivateAccount } from '@/hooks/query';
|
import { useInactivateAccountTrpc } from '@/hooks/trpc';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Account inactivate alert.
|
* Account inactivate alert.
|
||||||
@@ -23,7 +23,7 @@ function AccountInactivateAlert({
|
|||||||
// #withAlertActions
|
// #withAlertActions
|
||||||
closeAlert,
|
closeAlert,
|
||||||
}) {
|
}) {
|
||||||
const { mutateAsync: inactivateAccount, isLoading } = useInactivateAccount();
|
const { mutateAsync: inactivateAccount, isLoading } = useInactivateAccountTrpc();
|
||||||
|
|
||||||
const handleCancelInactiveAccount = () => {
|
const handleCancelInactiveAccount = () => {
|
||||||
closeAlert('account-inactivate');
|
closeAlert('account-inactivate');
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export default function InviteAcceptForm() {
|
|||||||
data: { errors },
|
data: { errors },
|
||||||
},
|
},
|
||||||
}) => {
|
}) => {
|
||||||
if (errors.find((e) => e.type === 'INVITE.TOKEN.NOT.FOUND')) {
|
if (errors.find((e) => e.type === 'INVITE_TOKEN_INVALID')) {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: intl.get('an_unexpected_error_occurred'),
|
message: intl.get('an_unexpected_error_occurred'),
|
||||||
intent: Intent.DANGER,
|
intent: Intent.DANGER,
|
||||||
@@ -71,14 +71,6 @@ export default function InviteAcceptForm() {
|
|||||||
phone_number: 'This phone number is used in another account.',
|
phone_number: 'This phone number is used in another account.',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (errors.find((e) => e.type === 'INVITE.TOKEN.NOT.FOUND')) {
|
|
||||||
AppToaster.show({
|
|
||||||
message: intl.get('an_unexpected_error_occurred'),
|
|
||||||
intent: Intent.DANGER,
|
|
||||||
position: Position.BOTTOM,
|
|
||||||
});
|
|
||||||
history.push('/auth/login');
|
|
||||||
}
|
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,14 +29,22 @@ function InviteAcceptProvider({ token, ...props }) {
|
|||||||
if (inviteMetaError) { history.push('/auth/login'); }
|
if (inviteMetaError) { history.push('/auth/login'); }
|
||||||
}, [history, inviteMetaError]);
|
}, [history, inviteMetaError]);
|
||||||
|
|
||||||
|
// Transform the backend response to match frontend expectations.
|
||||||
|
const transformedInviteMeta = inviteMeta
|
||||||
|
? {
|
||||||
|
email: inviteMeta.inviteToken?.email,
|
||||||
|
organizationName: inviteMeta.orgName,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
// Provider payload.
|
// Provider payload.
|
||||||
const provider = {
|
const provider = {
|
||||||
token,
|
token,
|
||||||
inviteMeta,
|
inviteMeta: transformedInviteMeta,
|
||||||
inviteMetaError,
|
inviteMetaError,
|
||||||
isInviteMetaError,
|
isInviteMetaError,
|
||||||
isInviteMetaLoading,
|
isInviteMetaLoading,
|
||||||
inviteAcceptMutate
|
inviteAcceptMutate,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (inviteMetaError) {
|
if (inviteMetaError) {
|
||||||
@@ -45,7 +53,6 @@ function InviteAcceptProvider({ token, ...props }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InviteAcceptLoading isLoading={isInviteMetaLoading}>
|
<InviteAcceptLoading isLoading={isInviteMetaLoading}>
|
||||||
{ isInviteMetaError }
|
|
||||||
<InviteAcceptContext.Provider value={provider} {...props} />
|
<InviteAcceptContext.Provider value={provider} {...props} />
|
||||||
</InviteAcceptLoading>
|
</InviteAcceptLoading>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
|
|
||||||
.root {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title{
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
color: #252A31;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description{
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
font-size: 15px;
|
|
||||||
line-height: 1.45;
|
|
||||||
color: #404854;
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { Button, Intent } from '@blueprintjs/core';
|
import { Button, Intent } from '@blueprintjs/core';
|
||||||
|
import { x } from '@xstyled/emotion';
|
||||||
import AuthInsider from './AuthInsider';
|
import AuthInsider from './AuthInsider';
|
||||||
import { AuthInsiderCard } from './_components';
|
import { AuthInsiderCard } from './_components';
|
||||||
import styles from './RegisterVerify.module.scss';
|
|
||||||
import { AppToaster, Stack } from '@/components';
|
import { AppToaster, Stack } from '@/components';
|
||||||
import { useAuthActions, useAuthUserVerifyEmail } from '@/hooks/state';
|
import { useAuthActions, useAuthUserVerifyEmail } from '@/hooks/state';
|
||||||
import { useAuthSignUpVerifyResendMail } from '@/hooks/query';
|
import { useAuthSignUpVerifyResendMail } from '@/hooks/query';
|
||||||
import { AuthContainer } from './AuthContainer';
|
import { AuthContainer } from './AuthContainer';
|
||||||
|
import { useIsDarkMode } from '@/hooks/useDarkMode';
|
||||||
|
|
||||||
export default function RegisterVerify() {
|
export default function RegisterVerify() {
|
||||||
const { setLogout } = useAuthActions();
|
const { setLogout } = useAuthActions();
|
||||||
@@ -14,6 +15,7 @@ export default function RegisterVerify() {
|
|||||||
useAuthSignUpVerifyResendMail();
|
useAuthSignUpVerifyResendMail();
|
||||||
|
|
||||||
const emailAddress = useAuthUserVerifyEmail();
|
const emailAddress = useAuthUserVerifyEmail();
|
||||||
|
const isDarkMode = useIsDarkMode();
|
||||||
|
|
||||||
const handleResendMailBtnClick = () => {
|
const handleResendMailBtnClick = () => {
|
||||||
resendSignUpVerifyMail()
|
resendSignUpVerifyMail()
|
||||||
@@ -37,12 +39,24 @@ export default function RegisterVerify() {
|
|||||||
return (
|
return (
|
||||||
<AuthContainer>
|
<AuthContainer>
|
||||||
<AuthInsider>
|
<AuthInsider>
|
||||||
<AuthInsiderCard className={styles.root}>
|
<AuthInsiderCard textAlign="center">
|
||||||
<h2 className={styles.title}>Please verify your email</h2>
|
<x.h2
|
||||||
<p className={styles.description}>
|
fontSize="18px"
|
||||||
|
fontWeight={600}
|
||||||
|
mb="0.5rem"
|
||||||
|
color={isDarkMode ? 'rgba(255, 255, 255, 0.85)' : '#252A31'}
|
||||||
|
>
|
||||||
|
Please verify your email
|
||||||
|
</x.h2>
|
||||||
|
<x.p
|
||||||
|
mb="1rem"
|
||||||
|
fontSize="15px"
|
||||||
|
lineHeight="1.45"
|
||||||
|
color={isDarkMode ? 'rgba(255, 255, 255, 0.7)' : '#404854'}
|
||||||
|
>
|
||||||
We sent an email to <strong>{emailAddress}</strong> Click the link
|
We sent an email to <strong>{emailAddress}</strong> Click the link
|
||||||
inside to get started.
|
inside to get started.
|
||||||
</p>
|
</x.p>
|
||||||
|
|
||||||
<Stack spacing={4}>
|
<Stack spacing={4}>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { createContext } from 'react';
|
import React, { createContext } from 'react';
|
||||||
import { DialogContent } from '@/components';
|
import { DialogContent } from '@/components';
|
||||||
import { useBankRule } from '@/hooks/query/bank-rules';
|
import { useBankRule } from '@/hooks/query/bank-rules';
|
||||||
import { useAccounts } from '@/hooks/query';
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
|
|
||||||
interface RuleFormBootValues {
|
interface RuleFormBootValues {
|
||||||
bankRule?: null;
|
bankRule?: null;
|
||||||
@@ -27,7 +27,7 @@ function RuleFormBoot({ bankRuleId, ...props }: RuleFormBootProps) {
|
|||||||
enabled: !!bankRuleId,
|
enabled: !!bankRuleId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts({}, {});
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
const isNewMode = !bankRuleId;
|
const isNewMode = !bankRuleId;
|
||||||
const isEditMode = !isNewMode;
|
const isEditMode = !isNewMode;
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { DashboardInsider } from '@/components';
|
import { DashboardInsider } from '@/components';
|
||||||
import { useCashflowAccounts, useAccount } from '@/hooks/query';
|
import { useCashflowAccounts } from '@/hooks/query';
|
||||||
|
import { useAccountTrpc } from '@/hooks/trpc';
|
||||||
import { useAppQueryString } from '@/hooks';
|
import { useAppQueryString } from '@/hooks';
|
||||||
import { useGetBankAccountSummaryMeta } from '@/hooks/query/bank-rules';
|
import { useGetBankAccountSummaryMeta } from '@/hooks/query/bank-rules';
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ function AccountTransactionsProvider({ query, ...props }) {
|
|||||||
data: currentAccount,
|
data: currentAccount,
|
||||||
isFetching: isCurrentAccountFetching,
|
isFetching: isCurrentAccountFetching,
|
||||||
isLoading: isCurrentAccountLoading,
|
isLoading: isCurrentAccountLoading,
|
||||||
} = useAccount(accountId, { keepPreviousData: true });
|
} = useAccountTrpc(accountId, { placeholderData: (previousData) => previousData });
|
||||||
|
|
||||||
// Retrieves the bank account meta summary.
|
// Retrieves the bank account meta summary.
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { first } from 'lodash';
|
import { first } from 'lodash';
|
||||||
import { DrawerLoading } from '@/components';
|
import { DrawerLoading } from '@/components';
|
||||||
import { useAccounts, useBranches } from '@/hooks/query';
|
import { useBranches } from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import { Spinner } from '@blueprintjs/core';
|
import { Spinner } from '@blueprintjs/core';
|
||||||
@@ -43,7 +44,7 @@ function CategorizeTransactionBoot({
|
|||||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||||
|
|
||||||
// Fetches accounts list.
|
// Fetches accounts list.
|
||||||
const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
|
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||||
|
|
||||||
// Fetches the branches list.
|
// Fetches the branches list.
|
||||||
const { data: branches, isLoading: isBranchesLoading } = useBranches(
|
const { data: branches, isLoading: isBranchesLoading } = useBranches(
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Spinner } from '@blueprintjs/core';
|
import { Spinner } from '@blueprintjs/core';
|
||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import { useAccounts, useBranches } from '@/hooks/query';
|
import { useBranches } from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
|
|
||||||
interface MatchingReconcileTransactionBootProps {
|
interface MatchingReconcileTransactionBootProps {
|
||||||
@@ -21,7 +22,7 @@ export function MatchingReconcileTransactionBoot({
|
|||||||
const { featureCan } = useFeatureCan();
|
const { featureCan } = useFeatureCan();
|
||||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||||
|
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts({}, {});
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
const { data: branches, isLoading: isBranchesLoading } = useBranches(
|
const { data: branches, isLoading: isBranchesLoading } = useBranches(
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { Features } from '@/constants';
|
|||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
import {
|
import {
|
||||||
useCreateCashflowTransaction,
|
useCreateCashflowTransaction,
|
||||||
useAccounts,
|
|
||||||
useBranches,
|
useBranches,
|
||||||
useCashflowAccounts,
|
useCashflowAccounts,
|
||||||
useSettingCashFlow,
|
useSettingCashFlow,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
|
|
||||||
const MoneyInDialogContent = React.createContext();
|
const MoneyInDialogContent = React.createContext();
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ function MoneyInDialogProvider({
|
|||||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||||
|
|
||||||
// Fetches accounts list.
|
// Fetches accounts list.
|
||||||
const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
|
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||||
|
|
||||||
// Fetches the branches list.
|
// Fetches the branches list.
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DialogContent } from '@/components';
|
import { DialogContent } from '@/components';
|
||||||
import { useAccount } from '@/hooks/query';
|
import { useAccountTrpc } from '@/hooks/trpc';
|
||||||
import { useMoneyInDailogContext } from './MoneyInDialogProvider';
|
import { useMoneyInDailogContext } from './MoneyInDialogProvider';
|
||||||
|
|
||||||
const MoneyInFieldsContext = React.createContext();
|
const MoneyInFieldsContext = React.createContext();
|
||||||
@@ -13,7 +13,7 @@ function MoneyInFieldsProvider({ ...props }) {
|
|||||||
const { accountId } = useMoneyInDailogContext();
|
const { accountId } = useMoneyInDailogContext();
|
||||||
|
|
||||||
// Fetches the specific account details.
|
// Fetches the specific account details.
|
||||||
const { data: account, isLoading: isAccountLoading } = useAccount(accountId, {
|
const { data: account, isLoading: isAccountLoading } = useAccountTrpc(accountId, {
|
||||||
enabled: !!accountId,
|
enabled: !!accountId,
|
||||||
});
|
});
|
||||||
// Provider data.
|
// Provider data.
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import { DialogContent } from '@/components';
|
|||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
import {
|
import {
|
||||||
useAccounts,
|
|
||||||
useBranches,
|
useBranches,
|
||||||
useCreateCashflowTransaction,
|
useCreateCashflowTransaction,
|
||||||
useCashflowAccounts,
|
useCashflowAccounts,
|
||||||
useSettingCashFlow,
|
useSettingCashFlow,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
|
|
||||||
const MoneyInDialogContent = React.createContext();
|
const MoneyInDialogContent = React.createContext();
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ function MoneyOutProvider({
|
|||||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||||
|
|
||||||
// Fetches accounts list.
|
// Fetches accounts list.
|
||||||
const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
|
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||||
|
|
||||||
// Fetches the branches list.
|
// Fetches the branches list.
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DialogContent } from '@/components';
|
import { DialogContent } from '@/components';
|
||||||
import { useAccount } from '@/hooks/query';
|
import { useAccountTrpc } from '@/hooks/trpc';
|
||||||
import { useMoneyOutDialogContext } from './MoneyOutDialogProvider';
|
import { useMoneyOutDialogContext } from './MoneyOutDialogProvider';
|
||||||
|
|
||||||
const MoneyOutFieldsContext = React.createContext();
|
const MoneyOutFieldsContext = React.createContext();
|
||||||
@@ -13,7 +13,7 @@ function MoneyOutFieldsProvider({ ...props }) {
|
|||||||
const { accountId } = useMoneyOutDialogContext();
|
const { accountId } = useMoneyOutDialogContext();
|
||||||
|
|
||||||
// Fetches the specific account details.
|
// Fetches the specific account details.
|
||||||
const { data: account, isLoading: isAccountLoading } = useAccount(accountId, {
|
const { data: account, isLoading: isAccountLoading } = useAccountTrpc(accountId, {
|
||||||
enabled: !!accountId,
|
enabled: !!accountId,
|
||||||
});
|
});
|
||||||
// Provider data.
|
// Provider data.
|
||||||
|
|||||||
@@ -2,13 +2,15 @@
|
|||||||
import React, { createContext, useContext } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
import { DialogContent } from '@/components';
|
import { DialogContent } from '@/components';
|
||||||
import {
|
import {
|
||||||
useCreateAccount,
|
|
||||||
useAccountsTypes,
|
|
||||||
useCurrencies,
|
useCurrencies,
|
||||||
useAccount,
|
|
||||||
useAccounts,
|
|
||||||
useEditAccount,
|
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import {
|
||||||
|
useCreateAccountTrpc,
|
||||||
|
useAccountsTypesTrpc,
|
||||||
|
useAccountTrpc,
|
||||||
|
useAccountsTrpc,
|
||||||
|
useEditAccountTrpc,
|
||||||
|
} from '@/hooks/trpc';
|
||||||
import { AccountDialogAction, getDisabledFormFields } from './utils';
|
import { AccountDialogAction, getDisabledFormFields } from './utils';
|
||||||
|
|
||||||
const AccountDialogContext = createContext();
|
const AccountDialogContext = createContext();
|
||||||
@@ -18,18 +20,18 @@ const AccountDialogContext = createContext();
|
|||||||
*/
|
*/
|
||||||
function AccountDialogProvider({ dialogName, payload, ...props }) {
|
function AccountDialogProvider({ dialogName, payload, ...props }) {
|
||||||
// Create and edit account mutations.
|
// Create and edit account mutations.
|
||||||
const { mutateAsync: createAccountMutate } = useCreateAccount();
|
const { mutateAsync: createAccountMutate } = useCreateAccountTrpc();
|
||||||
const { mutateAsync: editAccountMutate } = useEditAccount();
|
const { mutateAsync: editAccountMutate } = useEditAccountTrpc();
|
||||||
|
|
||||||
// Fetches accounts list.
|
// Fetches accounts list.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
// Fetches accounts types.
|
// Fetches accounts types.
|
||||||
const { data: accountsTypes, isLoading: isAccountsTypesLoading } =
|
const { data: accountsTypes, isLoading: isAccountsTypesLoading } =
|
||||||
useAccountsTypes();
|
useAccountsTypesTrpc();
|
||||||
|
|
||||||
// Fetches the specific account details.
|
// Fetches the specific account details.
|
||||||
const { data: account, isLoading: isAccountLoading } = useAccount(
|
const { data: account, isLoading: isAccountLoading } = useAccountTrpc(
|
||||||
payload.accountId,
|
payload.accountId,
|
||||||
{
|
{
|
||||||
enabled:
|
enabled:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { FormattedMessage as T, AppToaster } from '@/components';
|
|||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import BulkDeleteDialogContent from '@/containers/Dialogs/components/BulkDeleteDialogContent';
|
import BulkDeleteDialogContent from '@/containers/Dialogs/components/BulkDeleteDialogContent';
|
||||||
import { useBulkDeleteAccounts } from '@/hooks/query/accounts';
|
import { useBulkDeleteAccountsTrpc } from '@/hooks/trpc';
|
||||||
import withDialogRedux from '@/components/DialogReduxConnect';
|
import withDialogRedux from '@/components/DialogReduxConnect';
|
||||||
import { withDialogActions } from '@/containers/Dialog/withDialogActions';
|
import { withDialogActions } from '@/containers/Dialog/withDialogActions';
|
||||||
import { withAccountsTableActions } from '@/containers/Accounts/withAccountsTableActions';
|
import { withAccountsTableActions } from '@/containers/Accounts/withAccountsTableActions';
|
||||||
@@ -28,7 +28,7 @@ function AccountBulkDeleteDialog({
|
|||||||
// #withDialogActions
|
// #withDialogActions
|
||||||
closeDialog,
|
closeDialog,
|
||||||
}) {
|
}) {
|
||||||
const { mutateAsync: bulkDeleteAccounts, isLoading } = useBulkDeleteAccounts();
|
const { mutateAsync: bulkDeleteAccounts, isLoading } = useBulkDeleteAccountsTrpc();
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
closeDialog(dialogName);
|
closeDialog(dialogName);
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { DialogContent } from '@/components';
|
import { DialogContent } from '@/components';
|
||||||
import { useAccounts, useInvoice, useCreateBadDebt } from '@/hooks/query';
|
import { useInvoice, useCreateBadDebt } from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
|
|
||||||
const BadDebtContext = React.createContext();
|
const BadDebtContext = React.createContext();
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ const BadDebtContext = React.createContext();
|
|||||||
*/
|
*/
|
||||||
function BadDebtFormProvider({ invoiceId, dialogName, ...props }) {
|
function BadDebtFormProvider({ invoiceId, dialogName, ...props }) {
|
||||||
// Handle fetch accounts data.
|
// Handle fetch accounts data.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
// Handle fetch invoice data.
|
// Handle fetch invoice data.
|
||||||
const { data: invoice, isLoading: isInvoiceLoading } = useInvoice(invoiceId, {
|
const { data: invoice, isLoading: isInvoiceLoading } = useInvoice(invoiceId, {
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { Features } from '@/constants';
|
|||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
import {
|
import {
|
||||||
useItem,
|
useItem,
|
||||||
useAccounts,
|
|
||||||
useBranches,
|
useBranches,
|
||||||
useWarehouses,
|
useWarehouses,
|
||||||
useCreateInventoryAdjustment,
|
useCreateInventoryAdjustment,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
|
|
||||||
const InventoryAdjustmentContext = createContext();
|
const InventoryAdjustmentContext = createContext();
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ function InventoryAdjustmentFormProvider({ itemId, dialogName, ...props }) {
|
|||||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||||
|
|
||||||
// Fetches accounts list.
|
// Fetches accounts list.
|
||||||
const { isFetching: isAccountsLoading, data: accounts } = useAccounts();
|
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||||
|
|
||||||
// Fetches the item details.
|
// Fetches the item details.
|
||||||
const { isFetching: isItemLoading, data: item } = useItem(itemId);
|
const { isFetching: isItemLoading, data: item } = useItem(itemId);
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import React, { useMemo } from 'react';
|
|||||||
import { DialogContent } from '@/components';
|
import { DialogContent } from '@/components';
|
||||||
import {
|
import {
|
||||||
useBill,
|
useBill,
|
||||||
useAccounts,
|
|
||||||
useBranches,
|
useBranches,
|
||||||
useCreatePaymentMade,
|
useCreatePaymentMade,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
@@ -27,7 +27,7 @@ function QuickPaymentMadeFormProvider({ query, billId, dialogName, ...props }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Handle fetch accounts data.
|
// Handle fetch accounts data.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
// Create payment made mutations.
|
// Create payment made mutations.
|
||||||
const { mutateAsync: createPaymentMadeMutate } = useCreatePaymentMade();
|
const { mutateAsync: createPaymentMadeMutate } = useCreatePaymentMade();
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import { DialogContent } from '@/components';
|
|||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
import {
|
import {
|
||||||
useAccounts,
|
|
||||||
useInvoice,
|
useInvoice,
|
||||||
useBranches,
|
useBranches,
|
||||||
useSettingsPaymentReceives,
|
useSettingsPaymentReceives,
|
||||||
useCreatePaymentReceive,
|
useCreatePaymentReceive,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
|
|
||||||
const QuickPaymentReceiveContext = createContext();
|
const QuickPaymentReceiveContext = createContext();
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ function QuickPaymentReceiveFormProvider({
|
|||||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||||
|
|
||||||
// Handle fetch accounts data.
|
// Handle fetch accounts data.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
// Handle fetch invoice data.
|
// Handle fetch invoice data.
|
||||||
const { data: invoice, isLoading: isInvoiceLoading } = useInvoice(invoiceId, {
|
const { data: invoice, isLoading: isInvoiceLoading } = useInvoice(invoiceId, {
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { pick } from 'lodash';
|
|||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
import {
|
import {
|
||||||
useAccounts,
|
|
||||||
useCreditNote,
|
useCreditNote,
|
||||||
useBranches,
|
useBranches,
|
||||||
useCreateRefundCreditNote,
|
useCreateRefundCreditNote,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
|
|
||||||
const RefundCreditNoteContext = React.createContext();
|
const RefundCreditNoteContext = React.createContext();
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ function RefundCreditNoteFormProvider({
|
|||||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||||
|
|
||||||
// Handle fetch accounts data.
|
// Handle fetch accounts data.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
// Handle fetch credit note data.
|
// Handle fetch credit note data.
|
||||||
const { data: creditNote, isLoading: isCreditNoteLoading } = useCreditNote(
|
const { data: creditNote, isLoading: isCreditNoteLoading } = useCreditNote(
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { pick } from 'lodash';
|
|||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
import {
|
import {
|
||||||
useAccounts,
|
|
||||||
useVendorCredit,
|
useVendorCredit,
|
||||||
useBranches,
|
useBranches,
|
||||||
useCreateRefundVendorCredit,
|
useCreateRefundVendorCredit,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
|
|
||||||
const RefundVendorCreditContext = React.createContext();
|
const RefundVendorCreditContext = React.createContext();
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ function RefundVendorCreditFormProvider({
|
|||||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||||
|
|
||||||
// Handle fetch accounts data.
|
// Handle fetch accounts data.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
// Fetches the branches list.
|
// Fetches the branches list.
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useAccount, useAccountTransactions } from '@/hooks/query';
|
import { useAccountTrpc, useAccountTransactionsTrpc } from '@/hooks/trpc';
|
||||||
import { DrawerHeaderContent, DrawerLoading } from '@/components';
|
import { DrawerHeaderContent, DrawerLoading } from '@/components';
|
||||||
import { DRAWERS } from '@/constants/drawers';
|
import { DRAWERS } from '@/constants/drawers';
|
||||||
|
|
||||||
@@ -11,13 +11,13 @@ const AccountDrawerContext = React.createContext();
|
|||||||
*/
|
*/
|
||||||
function AccountDrawerProvider({ accountId, name, ...props }) {
|
function AccountDrawerProvider({ accountId, name, ...props }) {
|
||||||
// Fetches the specific account details.
|
// Fetches the specific account details.
|
||||||
const { data: account, isLoading: isAccountLoading } = useAccount(accountId, {
|
const { data: account, isLoading: isAccountLoading } = useAccountTrpc(accountId, {
|
||||||
enabled: !!accountId,
|
enabled: !!accountId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load the specific account transactions.
|
// Load the specific account transactions.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } =
|
const { data: accounts, isLoading: isAccountsLoading } =
|
||||||
useAccountTransactions(accountId, {
|
useAccountTransactionsTrpc(accountId, {}, {
|
||||||
enabled: !!accountId,
|
enabled: !!accountId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import {
|
|||||||
useCurrencies,
|
useCurrencies,
|
||||||
useCustomers,
|
useCustomers,
|
||||||
useExpense,
|
useExpense,
|
||||||
useAccounts,
|
|
||||||
useBranches,
|
useBranches,
|
||||||
useCreateExpense,
|
useCreateExpense,
|
||||||
useEditExpense,
|
useEditExpense,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { useProjects } from '@/containers/Projects/hooks';
|
import { useProjects } from '@/containers/Projects/hooks';
|
||||||
|
|
||||||
const ExpenseFormPageContext = createContext();
|
const ExpenseFormPageContext = createContext();
|
||||||
@@ -47,7 +47,7 @@ function ExpenseFormPageProvider({ query, expenseId, ...props }) {
|
|||||||
} = useBranches(query, { enabled: isBranchFeatureCan });
|
} = useBranches(query, { enabled: isBranchFeatureCan });
|
||||||
|
|
||||||
// Fetch accounts list.
|
// Fetch accounts list.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
// Fetch the projects list.
|
// Fetch the projects list.
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { createContext, useContext } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
|
|
||||||
import { useAccounts } from '@/hooks/query';
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { FinancialHeaderLoadingSkeleton } from '../FinancialHeaderLoadingSkeleton';
|
import { FinancialHeaderLoadingSkeleton } from '../FinancialHeaderLoadingSkeleton';
|
||||||
|
|
||||||
const GLHeaderGeneralPanelContext = createContext();
|
const GLHeaderGeneralPanelContext = createContext();
|
||||||
@@ -11,7 +11,7 @@ const GLHeaderGeneralPanelContext = createContext();
|
|||||||
*/
|
*/
|
||||||
function GLHeaderGeneralPanelProvider({ ...props }) {
|
function GLHeaderGeneralPanelProvider({ ...props }) {
|
||||||
// Accounts list.
|
// Accounts list.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
// Provider
|
// Provider
|
||||||
const provider = {
|
const provider = {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {
|
|||||||
useItemsCategories,
|
useItemsCategories,
|
||||||
useCreateItem,
|
useCreateItem,
|
||||||
useEditItem,
|
useEditItem,
|
||||||
useAccounts,
|
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { useWatchItemError } from './utils';
|
import { useWatchItemError } from './utils';
|
||||||
import { useTaxRates } from '@/hooks/query/taxRates';
|
import { useTaxRates } from '@/hooks/query/taxRates';
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ function ItemFormProvider({ itemId, ...props }) {
|
|||||||
const duplicateId = state?.action;
|
const duplicateId = state?.action;
|
||||||
|
|
||||||
// Fetches the accounts list.
|
// Fetches the accounts list.
|
||||||
const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
|
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||||
|
|
||||||
// Fetches the items categories list.
|
// Fetches the items categories list.
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import { Card } from '@/components';
|
import { Card } from '@/components';
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
import { useAccounts, useSaveSettings, useSettings } from '@/hooks/query';
|
import { useSaveSettings, useSettings } from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import PreferencesPageLoader from '../PreferencesPageLoader';
|
import PreferencesPageLoader from '../PreferencesPageLoader';
|
||||||
|
|
||||||
const AccountantFormContext = React.createContext();
|
const AccountantFormContext = React.createContext();
|
||||||
@@ -15,7 +16,7 @@ const AccountantFormContext = React.createContext();
|
|||||||
*/
|
*/
|
||||||
function AccountantFormProvider({ ...props }) {
|
function AccountantFormProvider({ ...props }) {
|
||||||
// Fetches the accounts list.
|
// Fetches the accounts list.
|
||||||
const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
|
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||||
|
|
||||||
// Fetches Organization Settings.
|
// Fetches Organization Settings.
|
||||||
const { isLoading: isSettingsLoading } = useSettings();
|
const { isLoading: isSettingsLoading } = useSettings();
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
import { Card } from '@/components';
|
import { Card } from '@/components';
|
||||||
import { useSettingsItems, useAccounts, useSaveSettings } from '@/hooks/query';
|
import { useSettingsItems, useSaveSettings } from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import PreferencesPageLoader from '../PreferencesPageLoader';
|
import PreferencesPageLoader from '../PreferencesPageLoader';
|
||||||
|
|
||||||
const ItemFormContext = createContext();
|
const ItemFormContext = createContext();
|
||||||
@@ -16,7 +17,7 @@ const ItemFormContext = createContext();
|
|||||||
|
|
||||||
function ItemPreferencesFormProvider({ ...props }) {
|
function ItemPreferencesFormProvider({ ...props }) {
|
||||||
// Fetches the accounts list.
|
// Fetches the accounts list.
|
||||||
const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
|
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isLoading: isItemsSettingsLoading,
|
isLoading: isItemsSettingsLoading,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { createContext, useContext } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
import { Spinner } from '@blueprintjs/core';
|
import { Spinner } from '@blueprintjs/core';
|
||||||
import { useAccounts } from '@/hooks/query';
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { useGetPaymentMethod } from '@/hooks/query/payment-services';
|
import { useGetPaymentMethod } from '@/hooks/query/payment-services';
|
||||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ export const StripeIntegrationEditBoot: React.FC<
|
|||||||
payload: { stripePaymentMethodId },
|
payload: { stripePaymentMethodId },
|
||||||
} = useDrawerContext();
|
} = useDrawerContext();
|
||||||
|
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts({}, {});
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
const { data: paymentMethod, isLoading: isPaymentMethodLoading } =
|
const { data: paymentMethod, isLoading: isPaymentMethodLoading } =
|
||||||
useGetPaymentMethod(stripePaymentMethodId, {
|
useGetPaymentMethod(stripePaymentMethodId, {
|
||||||
enabled: !!stripePaymentMethodId,
|
enabled: !!stripePaymentMethodId,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { useFeatureCan } from '@/hooks/state';
|
|||||||
import { DashboardInsider } from '@/components/Dashboard';
|
import { DashboardInsider } from '@/components/Dashboard';
|
||||||
import { useProjects } from '@/containers/Projects/hooks';
|
import { useProjects } from '@/containers/Projects/hooks';
|
||||||
import {
|
import {
|
||||||
useAccounts,
|
|
||||||
useVendors,
|
useVendors,
|
||||||
useItems,
|
useItems,
|
||||||
useBill,
|
useBill,
|
||||||
@@ -15,6 +14,7 @@ import {
|
|||||||
useCreateBill,
|
useCreateBill,
|
||||||
useEditBill,
|
useEditBill,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { useTaxRates } from '@/hooks/query/taxRates';
|
import { useTaxRates } from '@/hooks/query/taxRates';
|
||||||
|
|
||||||
const BillFormContext = createContext();
|
const BillFormContext = createContext();
|
||||||
@@ -48,7 +48,7 @@ function BillFormProvider({ billId, ...props }) {
|
|||||||
const isProjectsFeatureCan = featureCan(Features.Projects);
|
const isProjectsFeatureCan = featureCan(Features.Projects);
|
||||||
|
|
||||||
// Handle fetch accounts.
|
// Handle fetch accounts.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
// Handle fetch vendors data table
|
// Handle fetch vendors data table
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import React, { createContext, useContext, useState } from 'react';
|
|||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import { useFeatureCan } from '@/hooks/state';
|
import { useFeatureCan } from '@/hooks/state';
|
||||||
import {
|
import {
|
||||||
useAccounts,
|
|
||||||
useVendors,
|
useVendors,
|
||||||
useItems,
|
useItems,
|
||||||
useBranches,
|
useBranches,
|
||||||
@@ -12,6 +11,7 @@ import {
|
|||||||
useCreatePaymentMade,
|
useCreatePaymentMade,
|
||||||
useEditPaymentMade,
|
useEditPaymentMade,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { DashboardInsider } from '@/components';
|
import { DashboardInsider } from '@/components';
|
||||||
|
|
||||||
// Payment made form context.
|
// Payment made form context.
|
||||||
@@ -29,7 +29,7 @@ function PaymentMadeFormProvider({ query, paymentMadeId, ...props }) {
|
|||||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||||
|
|
||||||
// Handle fetch accounts data.
|
// Handle fetch accounts data.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
// Handle fetch Items data table or list.
|
// Handle fetch Items data table or list.
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { useProjects } from '@/containers/Projects/hooks';
|
|||||||
import {
|
import {
|
||||||
useSettingsPaymentReceives,
|
useSettingsPaymentReceives,
|
||||||
usePaymentReceiveEditPage,
|
usePaymentReceiveEditPage,
|
||||||
useAccounts,
|
|
||||||
useCustomers,
|
useCustomers,
|
||||||
useBranches,
|
useBranches,
|
||||||
useCreatePaymentReceive,
|
useCreatePaymentReceive,
|
||||||
@@ -15,6 +14,7 @@ import {
|
|||||||
usePaymentReceivedState,
|
usePaymentReceivedState,
|
||||||
PaymentReceivedStateResponse,
|
PaymentReceivedStateResponse,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
|
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
|
||||||
|
|
||||||
interface PaymentReceivedFormContextValue {
|
interface PaymentReceivedFormContextValue {
|
||||||
@@ -52,7 +52,7 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) {
|
|||||||
const paymentEntriesEditPage = paymentReceivedEditData?.entries
|
const paymentEntriesEditPage = paymentReceivedEditData?.entries
|
||||||
|
|
||||||
// Handle fetch accounts data.
|
// Handle fetch accounts data.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
// Fetch payment made settings.
|
// Fetch payment made settings.
|
||||||
const fetchSettings = useSettingsPaymentReceives();
|
const fetchSettings = useSettingsPaymentReceives();
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { useFeatureCan } from '@/hooks/state';
|
|||||||
import { DashboardInsider } from '@/components/Dashboard';
|
import { DashboardInsider } from '@/components/Dashboard';
|
||||||
import {
|
import {
|
||||||
useReceipt,
|
useReceipt,
|
||||||
useAccounts,
|
|
||||||
useSettingsReceipts,
|
useSettingsReceipts,
|
||||||
useCustomers,
|
useCustomers,
|
||||||
useWarehouses,
|
useWarehouses,
|
||||||
@@ -16,6 +15,7 @@ import {
|
|||||||
useGetReceiptState,
|
useGetReceiptState,
|
||||||
IGetReceiptStateResponse,
|
IGetReceiptStateResponse,
|
||||||
} from '@/hooks/query';
|
} from '@/hooks/query';
|
||||||
|
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||||
import { useProjects } from '@/containers/Projects/hooks';
|
import { useProjects } from '@/containers/Projects/hooks';
|
||||||
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
|
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ function ReceiptFormProvider({ receiptId, ...props }) {
|
|||||||
enabled: !!receiptId,
|
enabled: !!receiptId,
|
||||||
});
|
});
|
||||||
// Fetch accounts list.
|
// Fetch accounts list.
|
||||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||||
|
|
||||||
// Fetch customers list.
|
// Fetch customers list.
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export const useAuthMetadata = (props = {}) => {
|
|||||||
* Resend the mail of signup verification.
|
* Resend the mail of signup verification.
|
||||||
*/
|
*/
|
||||||
export const useAuthSignUpVerifyResendMail = (props) => {
|
export const useAuthSignUpVerifyResendMail = (props) => {
|
||||||
const apiRequest = useAuthApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation(
|
||||||
() => apiRequest.post(AuthRoute.SignupVerifyResend),
|
() => apiRequest.post(AuthRoute.SignupVerifyResend),
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
import { QueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
// Query client config.
|
// Query client config.
|
||||||
export const queryConfig = {
|
export const queryConfig = {
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
@@ -8,3 +10,6 @@ export const queryConfig = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Create a new QueryClient instance for tRPC
|
||||||
|
export const tanstackQueryClient = new QueryClient(queryConfig);
|
||||||
|
|||||||
@@ -58,16 +58,13 @@ export function useCreateCreditNote(props) {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation((values) => apiRequest.post('credit-notes', values), {
|
||||||
(values) => apiRequest.post('credit-notes', values),
|
|
||||||
{
|
|
||||||
onSuccess: (res, values) => {
|
onSuccess: (res, values) => {
|
||||||
// Common invalidate queries.
|
// Common invalidate queries.
|
||||||
commonInvalidateQueries(queryClient);
|
commonInvalidateQueries(queryClient);
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -218,8 +215,7 @@ export function useCreateRefundCreditNote(props) {
|
|||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation(
|
||||||
([id, values]) =>
|
([id, values]) => apiRequest.post(`credit-notes/${id}/refunds`, values),
|
||||||
apiRequest.post(`credit-notes/${id}/refunds`, values),
|
|
||||||
{
|
{
|
||||||
onSuccess: (res, [id, values]) => {
|
onSuccess: (res, [id, values]) => {
|
||||||
// Common invalidate queries.
|
// Common invalidate queries.
|
||||||
@@ -240,9 +236,7 @@ export function useDeleteRefundCreditNote(props) {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation((id) => apiRequest.delete(`credit-notes/refunds/${id}`), {
|
||||||
(id) => apiRequest.delete(`credit-notes/refunds/${id}`),
|
|
||||||
{
|
|
||||||
onSuccess: (res, id) => {
|
onSuccess: (res, id) => {
|
||||||
// Common invalidate queries.
|
// Common invalidate queries.
|
||||||
commonInvalidateQueries(queryClient);
|
commonInvalidateQueries(queryClient);
|
||||||
@@ -251,8 +245,7 @@ export function useDeleteRefundCreditNote(props) {
|
|||||||
queryClient.invalidateQueries([t.CREDIT_NOTE, id]);
|
queryClient.invalidateQueries([t.CREDIT_NOTE, id]);
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -301,7 +294,7 @@ export function useReconcileCreditNote(id, props, requestProps) {
|
|||||||
[t.RECONCILE_CREDIT_NOTE, id],
|
[t.RECONCILE_CREDIT_NOTE, id],
|
||||||
{
|
{
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: `credit-notes/${id}/applied-invoices`,
|
url: `credit-notes/${id}/apply-invoices`,
|
||||||
...requestProps,
|
...requestProps,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query';
|
||||||
import { useRequestQuery } from '../useQueryRequest';
|
import { useRequestQuery } from '../useQueryRequest';
|
||||||
import useApiRequest from '../useRequest';
|
import useApiRequest from '../useRequest';
|
||||||
|
import { transformToCamelCase } from '@/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authentication invite accept.
|
* Authentication invite accept.
|
||||||
@@ -22,9 +23,9 @@ export const useAuthInviteAccept = (props) => {
|
|||||||
export const useInviteMetaByToken = (token, props) => {
|
export const useInviteMetaByToken = (token, props) => {
|
||||||
return useRequestQuery(
|
return useRequestQuery(
|
||||||
['INVITE_META', token],
|
['INVITE_META', token],
|
||||||
{ method: 'get', url: `invite/invited/${token}` },
|
{ method: 'get', url: `invite/check/${token}` },
|
||||||
{
|
{
|
||||||
select: (res) => res.data,
|
select: (res) => transformToCamelCase(res.data),
|
||||||
...props
|
...props
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
2
packages/webapp/src/hooks/trpc/index.ts
Normal file
2
packages/webapp/src/hooks/trpc/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// tRPC hooks for accounts module
|
||||||
|
export * from './useAccounts';
|
||||||
188
packages/webapp/src/hooks/trpc/useAccounts.ts
Normal file
188
packages/webapp/src/hooks/trpc/useAccounts.ts
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
import { trpc } from '@/trpc';
|
||||||
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
// Query keys for cache invalidation
|
||||||
|
const accountQueryKeys = {
|
||||||
|
accounts: ['accounts'],
|
||||||
|
account: (id: number) => ['account', id],
|
||||||
|
accountTypes: ['accountTypes'],
|
||||||
|
accountTransactions: (id: number) => ['accountTransactions', id],
|
||||||
|
cashFlowAccounts: ['cashFlowAccounts'],
|
||||||
|
financialReport: ['financialReport'],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve accounts list using tRPC.
|
||||||
|
*/
|
||||||
|
export function useAccountsTrpc(query?: Record<string, any>, options = {}) {
|
||||||
|
return trpc.accounts.getAccounts.useQuery(query || {}, {
|
||||||
|
select: (res) => res.accounts,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the given account details using tRPC.
|
||||||
|
*/
|
||||||
|
export function useAccountTrpc(id: number, options = {}) {
|
||||||
|
return trpc.accounts.getAccount.useQuery(
|
||||||
|
{ id },
|
||||||
|
{
|
||||||
|
enabled: !!id,
|
||||||
|
...options,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve accounts types list using tRPC.
|
||||||
|
*/
|
||||||
|
export function useAccountsTypesTrpc(options = {}) {
|
||||||
|
return trpc.accounts.getAccountTypes.useQuery(undefined, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve account transactions using tRPC.
|
||||||
|
*/
|
||||||
|
export function useAccountTransactionsTrpc(
|
||||||
|
accountId: number,
|
||||||
|
filters?: { fromDate?: string; toDate?: string },
|
||||||
|
options = {}
|
||||||
|
) {
|
||||||
|
return trpc.accounts.getAccountTransactions.useQuery(
|
||||||
|
{
|
||||||
|
accountId,
|
||||||
|
fromDate: filters?.fromDate,
|
||||||
|
toDate: filters?.toDate,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!accountId,
|
||||||
|
...options,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates account using tRPC.
|
||||||
|
*/
|
||||||
|
export function useCreateAccountTrpc(options = {}) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return trpc.accounts.createAccount.useMutation({
|
||||||
|
onSuccess: () => {
|
||||||
|
// Invalidate related queries
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts });
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.cashFlowAccounts });
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.financialReport });
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits the given account using tRPC.
|
||||||
|
*/
|
||||||
|
export function useEditAccountTrpc(options = {}) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return trpc.accounts.editAccount.useMutation({
|
||||||
|
onSuccess: (_, variables) => {
|
||||||
|
// Invalidate related queries
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts });
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: accountQueryKeys.account(variables.id),
|
||||||
|
});
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.cashFlowAccounts });
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.financialReport });
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given account using tRPC.
|
||||||
|
*/
|
||||||
|
export function useDeleteAccountTrpc(options = {}) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return trpc.accounts.deleteAccount.useMutation({
|
||||||
|
onSuccess: () => {
|
||||||
|
// Invalidate related queries
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts });
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.cashFlowAccounts });
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.financialReport });
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activates the given account using tRPC.
|
||||||
|
*/
|
||||||
|
export function useActivateAccountTrpc(options = {}) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return trpc.accounts.activateAccount.useMutation({
|
||||||
|
onSuccess: () => {
|
||||||
|
// Invalidate related queries
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts });
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.cashFlowAccounts });
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.financialReport });
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inactivates the given account using tRPC.
|
||||||
|
*/
|
||||||
|
export function useInactivateAccountTrpc(options = {}) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return trpc.accounts.inactivateAccount.useMutation({
|
||||||
|
onSuccess: () => {
|
||||||
|
// Invalidate related queries
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts });
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.cashFlowAccounts });
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.financialReport });
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates which accounts can be deleted in bulk using tRPC.
|
||||||
|
*/
|
||||||
|
export function useValidateBulkDeleteAccountsTrpc(options = {}) {
|
||||||
|
return trpc.accounts.validateBulkDeleteAccounts.useMutation(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes multiple accounts in bulk using tRPC.
|
||||||
|
*/
|
||||||
|
export function useBulkDeleteAccountsTrpc(options = {}) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return trpc.accounts.bulkDeleteAccounts.useMutation({
|
||||||
|
onSuccess: () => {
|
||||||
|
// Invalidate related queries
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts });
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.cashFlowAccounts });
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.financialReport });
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to refresh accounts list.
|
||||||
|
*/
|
||||||
|
export function useRefreshAccountsTrpc() {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return {
|
||||||
|
refresh: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
42
packages/webapp/src/trpc.ts
Normal file
42
packages/webapp/src/trpc.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { createTRPCReact } from '@trpc/react-query';
|
||||||
|
import { httpBatchLink } from '@trpc/client';
|
||||||
|
import superjson from 'superjson';
|
||||||
|
import { store } from '@/store/createStore';
|
||||||
|
import { tanstackQueryClient } from '@/hooks/query/base';
|
||||||
|
|
||||||
|
// Define the AppRouter type - this will be imported from the server package
|
||||||
|
// For now, we use any until the server exports the proper types
|
||||||
|
export type AppRouter = any;
|
||||||
|
|
||||||
|
export const trpc = createTRPCReact<AppRouter>();
|
||||||
|
|
||||||
|
export function getAuthHeaders() {
|
||||||
|
const state = store.getState();
|
||||||
|
const { token, organizationId } = state.authentication;
|
||||||
|
const headers: Record<string, string> = {};
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
headers['x-access-token'] = token;
|
||||||
|
}
|
||||||
|
if (organizationId) {
|
||||||
|
headers['organization-id'] = organizationId.toString();
|
||||||
|
}
|
||||||
|
headers['Accept-Language'] = 'en';
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const trpcClient = trpc.createClient({
|
||||||
|
transformer: superjson,
|
||||||
|
links: [
|
||||||
|
httpBatchLink({
|
||||||
|
url: '/api/trpc',
|
||||||
|
headers() {
|
||||||
|
return getAuthHeaders();
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Export the QueryClient for use in the TRPCProvider
|
||||||
|
export const queryClient = tanstackQueryClient;
|
||||||
@@ -19,5 +19,8 @@
|
|||||||
"dev": "npm run build -- --watch"
|
"dev": "npm run build -- --watch"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC"
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@trpc/server": "^11.10.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export * from './countries';
|
export * from './countries';
|
||||||
|
export * from './trpc';
|
||||||
|
|
||||||
export const test = () => {};
|
export const test = () => {};
|
||||||
32
shared/bigcapital-utils/src/trpc.ts
Normal file
32
shared/bigcapital-utils/src/trpc.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* tRPC Router Types
|
||||||
|
* This file exports the types for the tRPC router that are shared between
|
||||||
|
* the server and the webapp (frontend).
|
||||||
|
*
|
||||||
|
* Note: This file only contains TYPE definitions. It does not import any
|
||||||
|
* runtime code from the server to avoid bundle bloat in the webapp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// We define the router type structure here to avoid importing the actual router
|
||||||
|
// from the server package, which would cause issues with bundling.
|
||||||
|
|
||||||
|
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
|
||||||
|
|
||||||
|
// This is a placeholder type that will be replaced by the actual router type
|
||||||
|
// when the server builds and exports it.
|
||||||
|
// The webapp will import the actual type from the server package during development
|
||||||
|
// but will use the built type declaration during production.
|
||||||
|
|
||||||
|
export type AppRouter = any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inference helpers for input types
|
||||||
|
* @example type MyInput = RouterInputs['accounts']['getAccount']
|
||||||
|
*/
|
||||||
|
export type RouterInputs = inferRouterInputs<AppRouter>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inference helpers for output types
|
||||||
|
* @example type MyOutput = RouterOutputs['accounts']['getAccount']
|
||||||
|
*/
|
||||||
|
export type RouterOutputs = inferRouterOutputs<AppRouter>;
|
||||||
Reference in New Issue
Block a user