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",
|
||||
"xlsx": "^0.18.5",
|
||||
"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": {
|
||||
"@nestjs/cli": "^10.0.0",
|
||||
|
||||
@@ -9,5 +9,10 @@
|
||||
"net_cash_financing": "Net cash provided by financing activities",
|
||||
"cash_beginning_period": "Cash at beginning of 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 { ThrottlerGuard } from '@nestjs/throttler';
|
||||
import { AppThrottleModule } from './AppThrottle.module';
|
||||
import { AppTrpcModule } from '../Trpc/Trpc.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -256,6 +257,7 @@ import { AppThrottleModule } from './AppThrottle.module';
|
||||
UsersModule,
|
||||
ContactsModule,
|
||||
SocketModule,
|
||||
AppTrpcModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import bluebird from 'bluebird';
|
||||
import * as bluebird from 'bluebird';
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
validateLinkModelEntryExists,
|
||||
@@ -53,7 +53,8 @@ export class LinkAttachment {
|
||||
const foundLinkModel = await LinkModel().query(trx).findById(modelId);
|
||||
validateLinkModelEntryExists(foundLinkModel);
|
||||
|
||||
const foundLinks = await this.documentLinkModel().query(trx)
|
||||
const foundLinks = await this.documentLinkModel()
|
||||
.query(trx)
|
||||
.where('modelRef', modelRef)
|
||||
.where('modelId', modelId)
|
||||
.where('documentId', foundFile.id);
|
||||
@@ -70,7 +71,7 @@ export class LinkAttachment {
|
||||
|
||||
/**
|
||||
* Links the given file keys to the given model type and id.
|
||||
* @param {string[]} filekeys - File keys.
|
||||
* @param {string[]} filekeys - File keys.
|
||||
* @param {string} modelRef - Model reference.
|
||||
* @param {number} modelId - Model id.
|
||||
* @param {Knex.Transaction} trx - Knex transaction.
|
||||
|
||||
@@ -65,7 +65,7 @@ export class AuthController {
|
||||
return this.authApp.signUp(signupDto);
|
||||
}
|
||||
|
||||
@Post('/signup/confirm')
|
||||
@Post('/signup/verify')
|
||||
@ApiOperation({ summary: 'Confirm user signup' })
|
||||
@ApiBody({
|
||||
schema: {
|
||||
|
||||
@@ -20,7 +20,7 @@ export class AuthenticationMailMesssages {
|
||||
* @returns {Mail}
|
||||
*/
|
||||
resetPasswordMessage(user: ModelObject<SystemUser>, token: string) {
|
||||
const baseURL = this.configService.get('baseURL');
|
||||
const baseURL = this.configService.get('app.baseUrl');
|
||||
|
||||
return new Mail()
|
||||
.setSubject('Bigcapital - Password Reset')
|
||||
@@ -54,7 +54,7 @@ export class AuthenticationMailMesssages {
|
||||
* @returns {Mail}
|
||||
*/
|
||||
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}`;
|
||||
|
||||
return new Mail()
|
||||
|
||||
@@ -7,17 +7,13 @@ import {
|
||||
import { GetAuthenticatedAccount } from './queries/GetAuthedAccount.service';
|
||||
import { Controller, Get, Post } from '@nestjs/common';
|
||||
import { Throttle } from '@nestjs/throttler';
|
||||
import { IgnoreTenantSeededRoute } from '../Tenancy/EnsureTenantIsSeeded.guards';
|
||||
import { IgnoreTenantInitializedRoute } from '../Tenancy/EnsureTenantIsInitialized.guard';
|
||||
import { TenantAgnosticRoute } from '../Tenancy/TenancyGlobal.guard';
|
||||
import { AuthenticationApplication } from './AuthApplication.sevice';
|
||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||
import { IgnoreUserVerifiedRoute } from './guards/EnsureUserVerified.guard';
|
||||
|
||||
@Controller('/auth')
|
||||
@ApiTags('Auth')
|
||||
@ApiExcludeController()
|
||||
@IgnoreTenantSeededRoute()
|
||||
@IgnoreTenantInitializedRoute()
|
||||
@TenantAgnosticRoute()
|
||||
@IgnoreUserVerifiedRoute()
|
||||
@Throttle({ auth: {} })
|
||||
export class AuthedController {
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
IAuthSignedUpEventPayload,
|
||||
IAuthSigningUpEventPayload,
|
||||
} from '../Auth.interfaces';
|
||||
import { defaultTo } from 'ramda';
|
||||
import { ERRORS } from '../Auth.constants';
|
||||
import { hashPassword } from '../Auth.utils';
|
||||
import { ClsService } from 'nestjs-cls';
|
||||
@@ -51,10 +50,10 @@ export class AuthSignupService {
|
||||
const signupConfirmation = this.configService.get('signupConfirmation');
|
||||
|
||||
const verifyTokenCrypto = crypto.randomBytes(64).toString('hex');
|
||||
const verifiedEnabed = defaultTo(signupConfirmation.enabled, false);
|
||||
const verifiedEnabed = signupConfirmation.enabled ?? false;
|
||||
const verifyToken = verifiedEnabed ? verifyTokenCrypto : '';
|
||||
const verified = !verifiedEnabed;
|
||||
|
||||
|
||||
const inviteAcceptedAt = moment().format('YYYY-MM-DD');
|
||||
|
||||
// Triggers signin up event.
|
||||
|
||||
@@ -4,7 +4,6 @@ import { SystemUser } from '@/modules/System/models/SystemUser';
|
||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||
import { ERRORS } from '../Auth.constants';
|
||||
import { events } from '@/common/events/events';
|
||||
import { ModelObject } from 'objection';
|
||||
import { ISignUpConfigmResendedEventPayload } from '../Auth.interfaces';
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
@@ -14,6 +15,10 @@ import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||
import { AbilitySubject } from '@/modules/Roles/Roles.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')
|
||||
@ApiTags('Credit Notes Apply Invoice')
|
||||
@@ -22,6 +27,9 @@ import { CreditNoteAction } from '../CreditNotes/types/CreditNotes.types';
|
||||
export class CreditNotesApplyInvoiceController {
|
||||
constructor(
|
||||
private readonly getCreditNoteAssociatedAppliedInvoicesService: GetCreditNoteAssociatedAppliedInvoices,
|
||||
private readonly getCreditNoteAssociatedInvoicesToApplyService: GetCreditNoteAssociatedInvoicesToApply,
|
||||
private readonly creditNoteApplyToInvoicesService: CreditNoteApplyToInvoices,
|
||||
private readonly deleteCreditNoteApplyToInvoicesService: DeleteCreditNoteApplyToInvoices,
|
||||
) {}
|
||||
|
||||
@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')
|
||||
@RequirePermission(CreditNoteAction.Edit, AbilitySubject.CreditNote)
|
||||
@ApiOperation({ summary: 'Apply credit note to invoices' })
|
||||
@@ -48,9 +73,32 @@ export class CreditNotesApplyInvoiceController {
|
||||
})
|
||||
@ApiResponse({ status: 404, description: 'Credit note not found' })
|
||||
@ApiResponse({ status: 400, description: 'Invalid input data' })
|
||||
applyCreditNoteToInvoices(@Param('creditNoteId') creditNoteId: number) {
|
||||
return this.getCreditNoteAssociatedAppliedInvoicesService.getCreditAssociatedAppliedInvoices(
|
||||
applyCreditNoteToInvoices(
|
||||
@Param('creditNoteId') creditNoteId: number,
|
||||
@Body() applyDto: ApplyCreditNoteToInvoicesDto,
|
||||
) {
|
||||
return this.creditNoteApplyToInvoicesService.applyCreditNoteToInvoices(
|
||||
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 { GetCreditNoteAssociatedInvoicesToApply } from './queries/GetCreditNoteAssociatedInvoicesToApply.service';
|
||||
import { CreditNotesApplyInvoiceController } from './CreditNotesApplyInvoice.controller';
|
||||
import { CreditNoteApplySyncCreditSubscriber } from './subscribers/CreditNoteApplySyncCreditSubscriber';
|
||||
import { CreditNoteApplySyncInvoicesCreditedAmountSubscriber } from './subscribers/CreditNoteApplySyncInvoicesSubscriber';
|
||||
|
||||
@Module({
|
||||
providers: [
|
||||
@@ -19,6 +21,8 @@ import { CreditNotesApplyInvoiceController } from './CreditNotesApplyInvoice.con
|
||||
CreditNoteApplySyncCredit,
|
||||
GetCreditNoteAssociatedAppliedInvoices,
|
||||
GetCreditNoteAssociatedInvoicesToApply,
|
||||
CreditNoteApplySyncCreditSubscriber,
|
||||
CreditNoteApplySyncInvoicesCreditedAmountSubscriber,
|
||||
],
|
||||
exports: [DeleteCustomerLinkedCreditNoteService],
|
||||
imports: [PaymentsReceivedModule, forwardRef(() => CreditNotesModule)],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import Bluebird from 'bluebird';
|
||||
import * as Bluebird from 'bluebird';
|
||||
import { ICreditNoteAppliedToInvoice } from '../types/CreditNoteApplyInvoice.types';
|
||||
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
||||
import { CreditNoteAppliedInvoice } from '../models/CreditNoteAppliedInvoice';
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Knex } from 'knex';
|
||||
import { sumBy } from 'lodash';
|
||||
import {
|
||||
ICreditNoteAppliedToInvoice,
|
||||
ICreditNoteAppliedToInvoiceModel,
|
||||
IApplyCreditToInvoicesDTO,
|
||||
IApplyCreditToInvoicesCreatedPayload,
|
||||
@@ -17,6 +18,7 @@ import { CreditNote } from '@/modules/CreditNotes/models/CreditNote';
|
||||
import { CreditNoteAppliedInvoice } from '../models/CreditNoteAppliedInvoice';
|
||||
import { CommandCreditNoteDTOTransform } from '@/modules/CreditNotes/commands/CommandCreditNoteDTOTransform.service';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { ApplyCreditNoteToInvoicesDto } from '../dtos/ApplyCreditNoteToInvoices.dto';
|
||||
|
||||
@Injectable()
|
||||
export class CreditNoteApplyToInvoices {
|
||||
@@ -48,7 +50,7 @@ export class CreditNoteApplyToInvoices {
|
||||
*/
|
||||
public async applyCreditNoteToInvoices(
|
||||
creditNoteId: number,
|
||||
applyCreditToInvoicesDTO: IApplyCreditToInvoicesDTO,
|
||||
applyCreditToInvoicesDTO: ApplyCreditNoteToInvoicesDto,
|
||||
): Promise<CreditNoteAppliedInvoice[]> {
|
||||
// Saves the credit note or throw not found service error.
|
||||
const creditNote = await this.creditNoteModel()
|
||||
@@ -71,7 +73,7 @@ export class CreditNoteApplyToInvoices {
|
||||
// Validate invoices has remaining amount to apply.
|
||||
this.validateInvoicesRemainingAmount(
|
||||
appliedInvoicesEntries,
|
||||
creditNoteAppliedModel.amount,
|
||||
creditNoteAppliedModel.entries,
|
||||
);
|
||||
// Validate the credit note remaining amount.
|
||||
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 {number} amount
|
||||
* @param {ICreditNoteAppliedToInvoice[]} entries
|
||||
*/
|
||||
private validateInvoicesRemainingAmount = (
|
||||
invoices: SaleInvoice[],
|
||||
amount: number,
|
||||
entries: ICreditNoteAppliedToInvoice[],
|
||||
) => {
|
||||
const invalidInvoices = invoices.filter(
|
||||
(invoice) => invoice.dueAmount < amount,
|
||||
);
|
||||
if (invalidInvoices.length > 0) {
|
||||
const invoiceMap = new Map(invoices.map((inv) => [inv.id, inv]));
|
||||
const invalidEntries = entries.filter((entry) => {
|
||||
const invoice = invoiceMap.get(entry.invoiceId);
|
||||
return invoice != null && invoice.dueAmount < entry.amount;
|
||||
});
|
||||
if (invalidEntries.length > 0) {
|
||||
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';
|
||||
|
||||
@Injectable()
|
||||
export default class CreditNoteApplySyncInvoicesCreditedAmountSubscriber {
|
||||
export class CreditNoteApplySyncInvoicesCreditedAmountSubscriber {
|
||||
constructor(
|
||||
private readonly syncInvoicesWithCreditNote: CreditNoteApplySyncInvoicesCreditedAmount,
|
||||
) {}
|
||||
|
||||
@@ -29,6 +29,7 @@ export interface IApplyCreditToInvoicesDeletedPayload {
|
||||
export interface ICreditNoteAppliedToInvoice {
|
||||
amount: number;
|
||||
creditNoteId: number;
|
||||
invoiceId: number;
|
||||
}
|
||||
export interface ICreditNoteAppliedToInvoiceModel {
|
||||
amount: number;
|
||||
|
||||
@@ -239,7 +239,7 @@ export class CashFlowTable {
|
||||
section: ICashFlowStatementSection,
|
||||
): ICashFlowStatementSection => {
|
||||
const label = section.footerLabel
|
||||
? section.footerLabel
|
||||
? this.i18n.t(section.footerLabel)
|
||||
: this.i18n.t('financial_sheet.total_row', {
|
||||
args: { value: section.label },
|
||||
});
|
||||
@@ -302,7 +302,7 @@ export class CashFlowTable {
|
||||
* @returns {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[] => {
|
||||
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.always(this.isDisplayColumnsBy(DISPLAY_COLUMNS_BY.DATE_PERIODS)),
|
||||
R.concat(this.datePeriodsColumns()),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as moment from 'moment';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { I18nService } from 'nestjs-i18n';
|
||||
import { FinancialSheetMeta } from '../../common/FinancialSheetMeta';
|
||||
import {
|
||||
ICashFlowStatementMeta,
|
||||
@@ -8,7 +9,10 @@ import {
|
||||
|
||||
@Injectable()
|
||||
export class CashflowSheetMeta {
|
||||
constructor(private readonly financialSheetMeta: FinancialSheetMeta) {}
|
||||
constructor(
|
||||
private readonly financialSheetMeta: FinancialSheetMeta,
|
||||
private readonly i18n: I18nService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Cashflow sheet meta.
|
||||
@@ -21,9 +25,11 @@ export class CashflowSheetMeta {
|
||||
const meta = await this.financialSheetMeta.meta();
|
||||
const formattedToDate = moment(query.toDate).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 {
|
||||
...meta,
|
||||
|
||||
@@ -91,7 +91,7 @@ export class CustomerBalanceSummaryTable {
|
||||
*/
|
||||
private getTotalColumnsAccessor = (): IColumnMapperMeta[] => {
|
||||
const columns = [
|
||||
{ key: 'name', value: this.i18n.t('Total') },
|
||||
{ key: 'name', value: this.i18n.t('contact_summary_balance.total') },
|
||||
{ key: 'total', accessor: 'total.formattedAmount' },
|
||||
];
|
||||
// @ts-ignore
|
||||
|
||||
@@ -93,7 +93,7 @@ export class InventoryItemDetailsTable {
|
||||
): ITableRow => {
|
||||
const columns: Array<IColumnMapperMeta> = [
|
||||
{ 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: 'quantity', accessor: 'quantity.formattedNumber' },
|
||||
{ key: 'empty', value: '' },
|
||||
@@ -115,7 +115,7 @@ export class InventoryItemDetailsTable {
|
||||
): ITableRow => {
|
||||
const columns: Array<IColumnMapperMeta> = [
|
||||
{ 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: 'quantity', accessor: 'quantity.formattedNumber' },
|
||||
{ key: 'empty', value: '' },
|
||||
@@ -193,16 +193,16 @@ export class InventoryItemDetailsTable {
|
||||
*/
|
||||
public tableColumns = (): ITableColumn[] => {
|
||||
return [
|
||||
{ key: 'date', label: this.i18n.t('Date') },
|
||||
{ key: 'transaction_type', label: this.i18n.t('Transaction type') },
|
||||
{ key: 'transaction_id', label: this.i18n.t('Transaction #') },
|
||||
{ key: 'quantity', label: this.i18n.t('Quantity') },
|
||||
{ key: 'rate', label: this.i18n.t('Rate') },
|
||||
{ key: 'total', label: this.i18n.t('Total') },
|
||||
{ key: 'value', label: this.i18n.t('Value') },
|
||||
{ key: 'profit_margin', label: this.i18n.t('Profit Margin') },
|
||||
{ key: 'running_quantity', label: this.i18n.t('Running quantity') },
|
||||
{ key: 'running_value', label: this.i18n.t('Running Value') },
|
||||
{ key: 'date', label: this.i18n.t('inventory_item_details.date') },
|
||||
{ key: 'transaction_type', label: this.i18n.t('inventory_item_details.transaction_type') },
|
||||
{ key: 'transaction_id', label: this.i18n.t('inventory_item_details.transaction_number') },
|
||||
{ key: 'quantity', label: this.i18n.t('inventory_item_details.quantity') },
|
||||
{ key: 'rate', label: this.i18n.t('inventory_item_details.rate') },
|
||||
{ key: 'total', label: this.i18n.t('inventory_item_details.total') },
|
||||
{ key: 'value', label: this.i18n.t('inventory_item_details.value') },
|
||||
{ key: 'profit_margin', label: this.i18n.t('inventory_item_details.profit_margin') },
|
||||
{ key: 'running_quantity', label: this.i18n.t('inventory_item_details.running_quantity') },
|
||||
{ key: 'running_value', label: this.i18n.t('inventory_item_details.running_value') },
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export class TransactionsByContactsTableRows {
|
||||
const columns = [
|
||||
{
|
||||
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),
|
||||
{
|
||||
@@ -76,7 +76,7 @@ export class TransactionsByContactsTableRows {
|
||||
const columns = [
|
||||
{
|
||||
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),
|
||||
{
|
||||
|
||||
@@ -141,10 +141,10 @@ export class TrialBalanceSheetTable extends R.compose(
|
||||
return R.compose(
|
||||
this.tableColumnsCellIndexing,
|
||||
R.concat([
|
||||
{ key: 'account', label: 'Account' },
|
||||
{ key: 'debit', label: 'Debit' },
|
||||
{ key: 'credit', label: 'Credit' },
|
||||
{ key: 'total', label: 'Total' },
|
||||
{ key: 'account', label: this.i18n.t('trial_balance_sheet.account') },
|
||||
{ key: 'debit', label: this.i18n.t('trial_balance_sheet.debit') },
|
||||
{ key: 'credit', label: this.i18n.t('trial_balance_sheet.credit') },
|
||||
{ key: 'total', label: this.i18n.t('trial_balance_sheet.total') },
|
||||
]),
|
||||
)([]);
|
||||
};
|
||||
|
||||
@@ -91,7 +91,7 @@ export class VendorBalanceSummaryTable {
|
||||
*/
|
||||
private getTotalColumnsAccessor = (): IColumnMapperMeta[] => {
|
||||
const columns = [
|
||||
{ key: 'name', value: this.i18n.t('Total') },
|
||||
{ key: 'name', value: this.i18n.t('contact_summary_balance.total') },
|
||||
{ key: 'total', accessor: 'total.formattedAmount' },
|
||||
];
|
||||
return R.compose(
|
||||
|
||||
@@ -28,6 +28,7 @@ import { UpdateOrganizationService } from './commands/UpdateOrganization.service
|
||||
import { IgnoreTenantInitializedRoute } from '../Tenancy/EnsureTenantIsInitialized.guard';
|
||||
import { IgnoreTenantSeededRoute } from '../Tenancy/EnsureTenantIsSeeded.guards';
|
||||
import { IgnoreTenantModelsInitialize } from '../Tenancy/TenancyInitializeModels.guard';
|
||||
import { IgnoreUserVerifiedRoute } from '../Auth/guards/EnsureUserVerified.guard';
|
||||
import { GetBuildOrganizationBuildJob } from './commands/GetBuildOrganizationJob.service';
|
||||
import { OrganizationBaseCurrencyLocking } from './Organization/OrganizationBaseCurrencyLocking.service';
|
||||
import {
|
||||
@@ -93,6 +94,7 @@ export class OrganizationController {
|
||||
|
||||
@Get('current')
|
||||
@HttpCode(200)
|
||||
@IgnoreUserVerifiedRoute()
|
||||
@ApiOperation({ summary: 'Get current organization' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
import { TenancyContext } from './TenancyContext.service';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
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 IgnoreTenantInitializedRoute = () =>
|
||||
@@ -35,8 +36,12 @@ export class EnsureTenantIsInitializedGuard implements CanActivate {
|
||||
IS_PUBLIC_ROUTE,
|
||||
[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.
|
||||
if (isPublic || isIgnoreEnsureTenantInitialized) {
|
||||
if (isPublic || isIgnoreEnsureTenantInitialized || isTenantAgnostic) {
|
||||
return true;
|
||||
}
|
||||
const tenant = await this.tenancyContext.getTenant();
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
import { TenancyContext } from './TenancyContext.service';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
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 IgnoreTenantSeededRoute = () =>
|
||||
@@ -36,7 +37,12 @@ export class EnsureTenantIsSeededGuard implements CanActivate {
|
||||
context.getHandler(),
|
||||
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;
|
||||
}
|
||||
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 { InviteTenantUserService } from './commands/InviteUser.service';
|
||||
import { UsersInviteController } from './UsersInvite.controller';
|
||||
import { UsersInvitePublicController } from './UsersInvitePublic.controller';
|
||||
import { InjectSystemModel } from '../System/SystemModels/SystemModels.module';
|
||||
import { SendInviteUserMailQueue } from './Users.constants';
|
||||
import InviteSendMainNotificationSubscribe from './subscribers/InviteSendMailNotification.subscriber';
|
||||
@@ -60,6 +61,6 @@ const models = [InjectSystemModel(UserInvite)];
|
||||
SendInviteUsersMailMessage,
|
||||
UsersApplication
|
||||
],
|
||||
controllers: [UsersController, UsersInviteController],
|
||||
controllers: [UsersController, UsersInviteController, UsersInvitePublicController],
|
||||
})
|
||||
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 { UsersApplication } from './Users.application';
|
||||
import { InviteUserDto, SendInviteUserDto } from './dtos/InviteUser.dto';
|
||||
import { SendInviteUserDto } from './dtos/InviteUser.dto';
|
||||
|
||||
@Controller('invite')
|
||||
@ApiTags('Users')
|
||||
export class UsersInviteController {
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -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 * as moment from 'moment';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { ClsService } from 'nestjs-cls';
|
||||
import {
|
||||
IAcceptInviteEventPayload,
|
||||
ICheckInviteEventPayload,
|
||||
@@ -15,6 +16,11 @@ import { UserInvite } from '../models/InviteUser.model';
|
||||
import { ModelObject } from 'objection';
|
||||
import { InviteUserDto } from '../dtos/InviteUser.dto';
|
||||
|
||||
interface InviteAcceptResponseDto {
|
||||
inviteToken: { email: string, token: string, createdAt: Date };
|
||||
orgName: string
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AcceptInviteUserService {
|
||||
constructor(
|
||||
@@ -27,6 +33,7 @@ export class AcceptInviteUserService {
|
||||
@Inject(UserInvite.name)
|
||||
private readonly userInviteModel: typeof UserInvite,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly cls: ClsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -62,6 +69,16 @@ export class AcceptInviteUserService {
|
||||
// Clear invite token by the given user id.
|
||||
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.
|
||||
await this.eventEmitter.emitAsync(events.inviteUser.acceptInvite, {
|
||||
inviteToken,
|
||||
@@ -77,7 +94,7 @@ export class AcceptInviteUserService {
|
||||
*/
|
||||
public async checkInvite(
|
||||
token: string,
|
||||
): Promise<{ inviteToken: ModelObject<UserInvite>; orgName: string }> {
|
||||
): Promise<InviteAcceptResponseDto> {
|
||||
const inviteToken = await this.getInviteTokenOrThrowError(token);
|
||||
|
||||
// Find the tenant that associated to the given token.
|
||||
@@ -92,7 +109,16 @@ export class AcceptInviteUserService {
|
||||
tenant,
|
||||
} 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 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()
|
||||
.setSubject(`${fromUser.firstName} has invited you to join a Bigcapital`)
|
||||
|
||||
@@ -6,6 +6,7 @@ export class UserInvite extends BaseModel {
|
||||
userId!: number;
|
||||
tenantId!: number;
|
||||
email!: string;
|
||||
createdAt!: Date;
|
||||
|
||||
/**
|
||||
* 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 { Inject, Injectable } from '@nestjs/common';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
@@ -22,13 +22,12 @@ export class SyncTenantAcceptInviteSubscriber {
|
||||
async syncTenantAcceptInvite({
|
||||
inviteToken,
|
||||
user,
|
||||
inviteUserDTO,
|
||||
}: IAcceptInviteEventPayload) {
|
||||
await this.tenantUserModel()
|
||||
.query()
|
||||
.where('systemUserId', inviteToken.userId)
|
||||
.update({
|
||||
...omit(inviteUserDTO, ['password']),
|
||||
...pick(user, ['firstName', 'lastName', 'email', 'active']),
|
||||
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 { Knex } from 'knex';
|
||||
import { IVendorCreditAppliedBill } from '../types/VendorCreditApplyBills.types';
|
||||
|
||||
@@ -95,6 +95,10 @@
|
||||
"react-plaid-link": "^3.2.1",
|
||||
"react-query": "^3.6.0",
|
||||
"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-router": "5.3.4",
|
||||
"react-router-breadcrumbs-hoc": "^3.2.10",
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Router, Switch, Route } from 'react-router';
|
||||
import { createBrowserHistory } from 'history';
|
||||
import { QueryClientProvider, QueryClient } from 'react-query';
|
||||
import { ReactQueryDevtools } from 'react-query/devtools';
|
||||
import { trpc, trpcClient, queryClient } from '@/trpc';
|
||||
|
||||
import '@/style/App.scss';
|
||||
import 'moment/locale/ar-ly';
|
||||
@@ -86,14 +87,16 @@ export default function App() {
|
||||
const queryClient = new QueryClient(queryConfig);
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<SplashScreen />
|
||||
<trpc.Provider client={trpcClient} queryClient={queryClient}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<SplashScreen />
|
||||
|
||||
<AppIntlLoader>
|
||||
<AppInsider history={history} />
|
||||
</AppIntlLoader>
|
||||
<AppIntlLoader>
|
||||
<AppInsider history={history} />
|
||||
</AppIntlLoader>
|
||||
|
||||
<ReactQueryDevtools initialIsOpen />
|
||||
</QueryClientProvider>
|
||||
<ReactQueryDevtools initialIsOpen />
|
||||
</QueryClientProvider>
|
||||
</trpc.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Features } from '@/constants';
|
||||
import { useFeatureCan } from '@/hooks/state';
|
||||
import { DashboardInsider } from '@/components';
|
||||
import {
|
||||
useAccounts,
|
||||
useAutoCompleteContacts,
|
||||
useCurrencies,
|
||||
useJournal,
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
useBranches,
|
||||
useSettingsManualJournals,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
import { useProjects } from '@/containers/Projects/hooks';
|
||||
|
||||
const MakeJournalFormContext = createContext();
|
||||
@@ -27,7 +27,7 @@ function MakeJournalProvider({ journalId, query, ...props }) {
|
||||
const isProjectFeatureCan = featureCan(Features.Projects);
|
||||
|
||||
// Load the accounts list.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
// Load the customers list.
|
||||
const { data: contacts, isLoading: isContactsLoading } =
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// @ts-nocheck
|
||||
import React, { createContext } from 'react';
|
||||
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';
|
||||
|
||||
const AccountsChartContext = createContext();
|
||||
@@ -26,7 +27,7 @@ function AccountsChartProvider({ query, tableStateChanged, ...props }) {
|
||||
data: accounts,
|
||||
isFetching: isAccountsFetching,
|
||||
isLoading: isAccountsLoading,
|
||||
} = useAccounts(query, { keepPreviousData: true });
|
||||
} = useAccountsTrpc(query, { placeholderData: (previousData) => previousData });
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// @ts-nocheck
|
||||
import { DialogsName } from '@/constants/dialogs';
|
||||
import { useValidateBulkDeleteAccounts } from '@/hooks/query/accounts';
|
||||
import { useValidateBulkDeleteAccountsTrpc } from '@/hooks/trpc';
|
||||
import { useBulkDeleteDialog } from '@/hooks/dialogs/useBulkDeleteDialog';
|
||||
|
||||
export const useBulkDeleteAccountsDialog = () => {
|
||||
const validateBulkDeleteMutation = useValidateBulkDeleteAccounts();
|
||||
const validateBulkDeleteMutation = useValidateBulkDeleteAccountsTrpc();
|
||||
const {
|
||||
openBulkDeleteDialog,
|
||||
closeBulkDeleteDialog,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AppToaster, FormattedMessage as T } from '@/components';
|
||||
import { withAlertStoreConnect } from '@/containers/Alert/withAlertStoreConnect';
|
||||
import { withAlertActions } from '@/containers/Alert/withAlertActions';
|
||||
|
||||
import { useActivateAccount } from '@/hooks/query';
|
||||
import { useActivateAccountTrpc } from '@/hooks/trpc';
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
@@ -24,8 +24,8 @@ function AccountActivateAlert({
|
||||
|
||||
const {
|
||||
mutateAsync: activateAccount,
|
||||
isLoading
|
||||
} = useActivateAccount();
|
||||
isLoading
|
||||
} = useActivateAccountTrpc();
|
||||
|
||||
// Handle alert cancel.
|
||||
const handleCancel = () => {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { withAlertStoreConnect } from '@/containers/Alert/withAlertStoreConnect'
|
||||
import { withAlertActions } from '@/containers/Alert/withAlertActions';
|
||||
import { withDrawerActions } from '@/containers/Drawer/withDrawerActions';
|
||||
|
||||
import { useDeleteAccount } from '@/hooks/query';
|
||||
import { useDeleteAccountTrpc } from '@/hooks/trpc';
|
||||
import { compose } from '@/utils';
|
||||
import { DRAWERS } from '@/constants/drawers';
|
||||
|
||||
@@ -34,7 +34,7 @@ function AccountDeleteAlert({
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { isLoading, mutateAsync: deleteAccount } = useDeleteAccount();
|
||||
const { isLoading, mutateAsync: deleteAccount } = useDeleteAccountTrpc();
|
||||
|
||||
// handle cancel delete account alert.
|
||||
const handleCancelAccountDelete = () => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { withAlertStoreConnect } from '@/containers/Alert/withAlertStoreConnect'
|
||||
import { withAlertActions } from '@/containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
import { useInactivateAccount } from '@/hooks/query';
|
||||
import { useInactivateAccountTrpc } from '@/hooks/trpc';
|
||||
|
||||
/**
|
||||
* Account inactivate alert.
|
||||
@@ -23,7 +23,7 @@ function AccountInactivateAlert({
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const { mutateAsync: inactivateAccount, isLoading } = useInactivateAccount();
|
||||
const { mutateAsync: inactivateAccount, isLoading } = useInactivateAccountTrpc();
|
||||
|
||||
const handleCancelInactiveAccount = () => {
|
||||
closeAlert('account-inactivate');
|
||||
|
||||
@@ -58,7 +58,7 @@ export default function InviteAcceptForm() {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
if (errors.find((e) => e.type === 'INVITE.TOKEN.NOT.FOUND')) {
|
||||
if (errors.find((e) => e.type === 'INVITE_TOKEN_INVALID')) {
|
||||
AppToaster.show({
|
||||
message: intl.get('an_unexpected_error_occurred'),
|
||||
intent: Intent.DANGER,
|
||||
@@ -71,14 +71,6 @@ export default function InviteAcceptForm() {
|
||||
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);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -29,14 +29,22 @@ function InviteAcceptProvider({ token, ...props }) {
|
||||
if (inviteMetaError) { history.push('/auth/login'); }
|
||||
}, [history, inviteMetaError]);
|
||||
|
||||
// Transform the backend response to match frontend expectations.
|
||||
const transformedInviteMeta = inviteMeta
|
||||
? {
|
||||
email: inviteMeta.inviteToken?.email,
|
||||
organizationName: inviteMeta.orgName,
|
||||
}
|
||||
: null;
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
token,
|
||||
inviteMeta,
|
||||
inviteMeta: transformedInviteMeta,
|
||||
inviteMetaError,
|
||||
isInviteMetaError,
|
||||
isInviteMetaLoading,
|
||||
inviteAcceptMutate
|
||||
inviteAcceptMutate,
|
||||
};
|
||||
|
||||
if (inviteMetaError) {
|
||||
@@ -45,7 +53,6 @@ function InviteAcceptProvider({ token, ...props }) {
|
||||
|
||||
return (
|
||||
<InviteAcceptLoading isLoading={isInviteMetaLoading}>
|
||||
{ isInviteMetaError }
|
||||
<InviteAcceptContext.Provider value={provider} {...props} />
|
||||
</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
|
||||
import { Button, Intent } from '@blueprintjs/core';
|
||||
import { x } from '@xstyled/emotion';
|
||||
import AuthInsider from './AuthInsider';
|
||||
import { AuthInsiderCard } from './_components';
|
||||
import styles from './RegisterVerify.module.scss';
|
||||
import { AppToaster, Stack } from '@/components';
|
||||
import { useAuthActions, useAuthUserVerifyEmail } from '@/hooks/state';
|
||||
import { useAuthSignUpVerifyResendMail } from '@/hooks/query';
|
||||
import { AuthContainer } from './AuthContainer';
|
||||
import { useIsDarkMode } from '@/hooks/useDarkMode';
|
||||
|
||||
export default function RegisterVerify() {
|
||||
const { setLogout } = useAuthActions();
|
||||
@@ -14,6 +15,7 @@ export default function RegisterVerify() {
|
||||
useAuthSignUpVerifyResendMail();
|
||||
|
||||
const emailAddress = useAuthUserVerifyEmail();
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
const handleResendMailBtnClick = () => {
|
||||
resendSignUpVerifyMail()
|
||||
@@ -37,12 +39,24 @@ export default function RegisterVerify() {
|
||||
return (
|
||||
<AuthContainer>
|
||||
<AuthInsider>
|
||||
<AuthInsiderCard className={styles.root}>
|
||||
<h2 className={styles.title}>Please verify your email</h2>
|
||||
<p className={styles.description}>
|
||||
<AuthInsiderCard textAlign="center">
|
||||
<x.h2
|
||||
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
|
||||
inside to get started.
|
||||
</p>
|
||||
</x.p>
|
||||
|
||||
<Stack spacing={4}>
|
||||
<Button
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { DialogContent } from '@/components';
|
||||
import { useBankRule } from '@/hooks/query/bank-rules';
|
||||
import { useAccounts } from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
|
||||
interface RuleFormBootValues {
|
||||
bankRule?: null;
|
||||
@@ -27,7 +27,7 @@ function RuleFormBoot({ bankRuleId, ...props }: RuleFormBootProps) {
|
||||
enabled: !!bankRuleId,
|
||||
},
|
||||
);
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts({}, {});
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
const isNewMode = !bankRuleId;
|
||||
const isEditMode = !isNewMode;
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
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 { useGetBankAccountSummaryMeta } from '@/hooks/query/bank-rules';
|
||||
|
||||
@@ -33,7 +34,7 @@ function AccountTransactionsProvider({ query, ...props }) {
|
||||
data: currentAccount,
|
||||
isFetching: isCurrentAccountFetching,
|
||||
isLoading: isCurrentAccountLoading,
|
||||
} = useAccount(accountId, { keepPreviousData: true });
|
||||
} = useAccountTrpc(accountId, { placeholderData: (previousData) => previousData });
|
||||
|
||||
// Retrieves the bank account meta summary.
|
||||
const {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { first } from 'lodash';
|
||||
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 { Features } from '@/constants';
|
||||
import { Spinner } from '@blueprintjs/core';
|
||||
@@ -43,7 +44,7 @@ function CategorizeTransactionBoot({
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Fetches accounts list.
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||
|
||||
// Fetches the branches list.
|
||||
const { data: branches, isLoading: isBranchesLoading } = useBranches(
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Spinner } from '@blueprintjs/core';
|
||||
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';
|
||||
|
||||
interface MatchingReconcileTransactionBootProps {
|
||||
@@ -21,7 +22,7 @@ export function MatchingReconcileTransactionBoot({
|
||||
const { featureCan } = useFeatureCan();
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts({}, {});
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
const { data: branches, isLoading: isBranchesLoading } = useBranches(
|
||||
{},
|
||||
{
|
||||
|
||||
@@ -5,11 +5,11 @@ import { Features } from '@/constants';
|
||||
import { useFeatureCan } from '@/hooks/state';
|
||||
import {
|
||||
useCreateCashflowTransaction,
|
||||
useAccounts,
|
||||
useBranches,
|
||||
useCashflowAccounts,
|
||||
useSettingCashFlow,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
|
||||
const MoneyInDialogContent = React.createContext();
|
||||
|
||||
@@ -30,7 +30,7 @@ function MoneyInDialogProvider({
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Fetches accounts list.
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||
|
||||
// Fetches the branches list.
|
||||
const {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { DialogContent } from '@/components';
|
||||
import { useAccount } from '@/hooks/query';
|
||||
import { useAccountTrpc } from '@/hooks/trpc';
|
||||
import { useMoneyInDailogContext } from './MoneyInDialogProvider';
|
||||
|
||||
const MoneyInFieldsContext = React.createContext();
|
||||
@@ -13,7 +13,7 @@ function MoneyInFieldsProvider({ ...props }) {
|
||||
const { accountId } = useMoneyInDailogContext();
|
||||
|
||||
// Fetches the specific account details.
|
||||
const { data: account, isLoading: isAccountLoading } = useAccount(accountId, {
|
||||
const { data: account, isLoading: isAccountLoading } = useAccountTrpc(accountId, {
|
||||
enabled: !!accountId,
|
||||
});
|
||||
// Provider data.
|
||||
|
||||
@@ -4,12 +4,12 @@ import { DialogContent } from '@/components';
|
||||
import { Features } from '@/constants';
|
||||
import { useFeatureCan } from '@/hooks/state';
|
||||
import {
|
||||
useAccounts,
|
||||
useBranches,
|
||||
useCreateCashflowTransaction,
|
||||
useCashflowAccounts,
|
||||
useSettingCashFlow,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
|
||||
const MoneyInDialogContent = React.createContext();
|
||||
|
||||
@@ -30,7 +30,7 @@ function MoneyOutProvider({
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Fetches accounts list.
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||
|
||||
// Fetches the branches list.
|
||||
const {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { DialogContent } from '@/components';
|
||||
import { useAccount } from '@/hooks/query';
|
||||
import { useAccountTrpc } from '@/hooks/trpc';
|
||||
import { useMoneyOutDialogContext } from './MoneyOutDialogProvider';
|
||||
|
||||
const MoneyOutFieldsContext = React.createContext();
|
||||
@@ -13,7 +13,7 @@ function MoneyOutFieldsProvider({ ...props }) {
|
||||
const { accountId } = useMoneyOutDialogContext();
|
||||
|
||||
// Fetches the specific account details.
|
||||
const { data: account, isLoading: isAccountLoading } = useAccount(accountId, {
|
||||
const { data: account, isLoading: isAccountLoading } = useAccountTrpc(accountId, {
|
||||
enabled: !!accountId,
|
||||
});
|
||||
// Provider data.
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import { DialogContent } from '@/components';
|
||||
import {
|
||||
useCreateAccount,
|
||||
useAccountsTypes,
|
||||
useCurrencies,
|
||||
useAccount,
|
||||
useAccounts,
|
||||
useEditAccount,
|
||||
} from '@/hooks/query';
|
||||
import {
|
||||
useCreateAccountTrpc,
|
||||
useAccountsTypesTrpc,
|
||||
useAccountTrpc,
|
||||
useAccountsTrpc,
|
||||
useEditAccountTrpc,
|
||||
} from '@/hooks/trpc';
|
||||
import { AccountDialogAction, getDisabledFormFields } from './utils';
|
||||
|
||||
const AccountDialogContext = createContext();
|
||||
@@ -18,18 +20,18 @@ const AccountDialogContext = createContext();
|
||||
*/
|
||||
function AccountDialogProvider({ dialogName, payload, ...props }) {
|
||||
// Create and edit account mutations.
|
||||
const { mutateAsync: createAccountMutate } = useCreateAccount();
|
||||
const { mutateAsync: editAccountMutate } = useEditAccount();
|
||||
const { mutateAsync: createAccountMutate } = useCreateAccountTrpc();
|
||||
const { mutateAsync: editAccountMutate } = useEditAccountTrpc();
|
||||
|
||||
// Fetches accounts list.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
// Fetches accounts types.
|
||||
const { data: accountsTypes, isLoading: isAccountsTypesLoading } =
|
||||
useAccountsTypes();
|
||||
useAccountsTypesTrpc();
|
||||
|
||||
// Fetches the specific account details.
|
||||
const { data: account, isLoading: isAccountLoading } = useAccount(
|
||||
const { data: account, isLoading: isAccountLoading } = useAccountTrpc(
|
||||
payload.accountId,
|
||||
{
|
||||
enabled:
|
||||
|
||||
@@ -5,7 +5,7 @@ import { FormattedMessage as T, AppToaster } from '@/components';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import BulkDeleteDialogContent from '@/containers/Dialogs/components/BulkDeleteDialogContent';
|
||||
import { useBulkDeleteAccounts } from '@/hooks/query/accounts';
|
||||
import { useBulkDeleteAccountsTrpc } from '@/hooks/trpc';
|
||||
import withDialogRedux from '@/components/DialogReduxConnect';
|
||||
import { withDialogActions } from '@/containers/Dialog/withDialogActions';
|
||||
import { withAccountsTableActions } from '@/containers/Accounts/withAccountsTableActions';
|
||||
@@ -28,7 +28,7 @@ function AccountBulkDeleteDialog({
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
}) {
|
||||
const { mutateAsync: bulkDeleteAccounts, isLoading } = useBulkDeleteAccounts();
|
||||
const { mutateAsync: bulkDeleteAccounts, isLoading } = useBulkDeleteAccountsTrpc();
|
||||
|
||||
const handleCancel = () => {
|
||||
closeDialog(dialogName);
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
import React from 'react';
|
||||
|
||||
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();
|
||||
|
||||
@@ -11,7 +12,7 @@ const BadDebtContext = React.createContext();
|
||||
*/
|
||||
function BadDebtFormProvider({ invoiceId, dialogName, ...props }) {
|
||||
// Handle fetch accounts data.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
// Handle fetch invoice data.
|
||||
const { data: invoice, isLoading: isInvoiceLoading } = useInvoice(invoiceId, {
|
||||
|
||||
@@ -5,11 +5,11 @@ import { Features } from '@/constants';
|
||||
import { useFeatureCan } from '@/hooks/state';
|
||||
import {
|
||||
useItem,
|
||||
useAccounts,
|
||||
useBranches,
|
||||
useWarehouses,
|
||||
useCreateInventoryAdjustment,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
|
||||
const InventoryAdjustmentContext = createContext();
|
||||
|
||||
@@ -23,7 +23,7 @@ function InventoryAdjustmentFormProvider({ itemId, dialogName, ...props }) {
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Fetches accounts list.
|
||||
const { isFetching: isAccountsLoading, data: accounts } = useAccounts();
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||
|
||||
// Fetches the item details.
|
||||
const { isFetching: isItemLoading, data: item } = useItem(itemId);
|
||||
|
||||
@@ -3,10 +3,10 @@ import React, { useMemo } from 'react';
|
||||
import { DialogContent } from '@/components';
|
||||
import {
|
||||
useBill,
|
||||
useAccounts,
|
||||
useBranches,
|
||||
useCreatePaymentMade,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
import { Features } from '@/constants';
|
||||
import { useFeatureCan } from '@/hooks/state';
|
||||
import { pick } from 'lodash';
|
||||
@@ -27,7 +27,7 @@ function QuickPaymentMadeFormProvider({ query, billId, dialogName, ...props }) {
|
||||
});
|
||||
|
||||
// Handle fetch accounts data.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
// Create payment made mutations.
|
||||
const { mutateAsync: createPaymentMadeMutate } = useCreatePaymentMade();
|
||||
|
||||
@@ -5,12 +5,12 @@ import { DialogContent } from '@/components';
|
||||
import { Features } from '@/constants';
|
||||
import { useFeatureCan } from '@/hooks/state';
|
||||
import {
|
||||
useAccounts,
|
||||
useInvoice,
|
||||
useBranches,
|
||||
useSettingsPaymentReceives,
|
||||
useCreatePaymentReceive,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
|
||||
const QuickPaymentReceiveContext = createContext();
|
||||
|
||||
@@ -28,7 +28,7 @@ function QuickPaymentReceiveFormProvider({
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Handle fetch accounts data.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
// Handle fetch invoice data.
|
||||
const { data: invoice, isLoading: isInvoiceLoading } = useInvoice(invoiceId, {
|
||||
|
||||
@@ -5,11 +5,11 @@ import { pick } from 'lodash';
|
||||
import { Features } from '@/constants';
|
||||
import { useFeatureCan } from '@/hooks/state';
|
||||
import {
|
||||
useAccounts,
|
||||
useCreditNote,
|
||||
useBranches,
|
||||
useCreateRefundCreditNote,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
|
||||
const RefundCreditNoteContext = React.createContext();
|
||||
|
||||
@@ -27,7 +27,7 @@ function RefundCreditNoteFormProvider({
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Handle fetch accounts data.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
// Handle fetch credit note data.
|
||||
const { data: creditNote, isLoading: isCreditNoteLoading } = useCreditNote(
|
||||
|
||||
@@ -5,11 +5,11 @@ import { pick } from 'lodash';
|
||||
import { Features } from '@/constants';
|
||||
import { useFeatureCan } from '@/hooks/state';
|
||||
import {
|
||||
useAccounts,
|
||||
useVendorCredit,
|
||||
useBranches,
|
||||
useCreateRefundVendorCredit,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
|
||||
const RefundVendorCreditContext = React.createContext();
|
||||
|
||||
@@ -24,7 +24,7 @@ function RefundVendorCreditFormProvider({
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Handle fetch accounts data.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
// Fetches the branches list.
|
||||
const {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { useAccount, useAccountTransactions } from '@/hooks/query';
|
||||
import { useAccountTrpc, useAccountTransactionsTrpc } from '@/hooks/trpc';
|
||||
import { DrawerHeaderContent, DrawerLoading } from '@/components';
|
||||
import { DRAWERS } from '@/constants/drawers';
|
||||
|
||||
@@ -11,13 +11,13 @@ const AccountDrawerContext = React.createContext();
|
||||
*/
|
||||
function AccountDrawerProvider({ accountId, name, ...props }) {
|
||||
// Fetches the specific account details.
|
||||
const { data: account, isLoading: isAccountLoading } = useAccount(accountId, {
|
||||
const { data: account, isLoading: isAccountLoading } = useAccountTrpc(accountId, {
|
||||
enabled: !!accountId,
|
||||
});
|
||||
|
||||
// Load the specific account transactions.
|
||||
const { data: accounts, isLoading: isAccountsLoading } =
|
||||
useAccountTransactions(accountId, {
|
||||
useAccountTransactionsTrpc(accountId, {}, {
|
||||
enabled: !!accountId,
|
||||
});
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ import {
|
||||
useCurrencies,
|
||||
useCustomers,
|
||||
useExpense,
|
||||
useAccounts,
|
||||
useBranches,
|
||||
useCreateExpense,
|
||||
useEditExpense,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
import { useProjects } from '@/containers/Projects/hooks';
|
||||
|
||||
const ExpenseFormPageContext = createContext();
|
||||
@@ -47,7 +47,7 @@ function ExpenseFormPageProvider({ query, expenseId, ...props }) {
|
||||
} = useBranches(query, { enabled: isBranchFeatureCan });
|
||||
|
||||
// Fetch accounts list.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
// Fetch the projects list.
|
||||
const {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React, { createContext, useContext } from 'react';
|
||||
|
||||
import { useAccounts } from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
import { FinancialHeaderLoadingSkeleton } from '../FinancialHeaderLoadingSkeleton';
|
||||
|
||||
const GLHeaderGeneralPanelContext = createContext();
|
||||
@@ -11,7 +11,7 @@ const GLHeaderGeneralPanelContext = createContext();
|
||||
*/
|
||||
function GLHeaderGeneralPanelProvider({ ...props }) {
|
||||
// Accounts list.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
// Provider
|
||||
const provider = {
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
useItemsCategories,
|
||||
useCreateItem,
|
||||
useEditItem,
|
||||
useAccounts,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
import { useWatchItemError } from './utils';
|
||||
import { useTaxRates } from '@/hooks/query/taxRates';
|
||||
|
||||
@@ -23,7 +23,7 @@ function ItemFormProvider({ itemId, ...props }) {
|
||||
const duplicateId = state?.action;
|
||||
|
||||
// Fetches the accounts list.
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||
|
||||
// Fetches the items categories list.
|
||||
const {
|
||||
|
||||
@@ -5,7 +5,8 @@ import styled from 'styled-components';
|
||||
|
||||
import { Card } from '@/components';
|
||||
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';
|
||||
|
||||
const AccountantFormContext = React.createContext();
|
||||
@@ -15,7 +16,7 @@ const AccountantFormContext = React.createContext();
|
||||
*/
|
||||
function AccountantFormProvider({ ...props }) {
|
||||
// Fetches the accounts list.
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||
|
||||
// Fetches Organization Settings.
|
||||
const { isLoading: isSettingsLoading } = useSettings();
|
||||
|
||||
@@ -5,7 +5,8 @@ import styled from 'styled-components';
|
||||
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
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';
|
||||
|
||||
const ItemFormContext = createContext();
|
||||
@@ -16,7 +17,7 @@ const ItemFormContext = createContext();
|
||||
|
||||
function ItemPreferencesFormProvider({ ...props }) {
|
||||
// Fetches the accounts list.
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
|
||||
const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc();
|
||||
|
||||
const {
|
||||
isLoading: isItemsSettingsLoading,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import { Spinner } from '@blueprintjs/core';
|
||||
import { useAccounts } from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
import { useGetPaymentMethod } from '@/hooks/query/payment-services';
|
||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||
|
||||
@@ -40,7 +40,7 @@ export const StripeIntegrationEditBoot: React.FC<
|
||||
payload: { stripePaymentMethodId },
|
||||
} = useDrawerContext();
|
||||
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts({}, {});
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
const { data: paymentMethod, isLoading: isPaymentMethodLoading } =
|
||||
useGetPaymentMethod(stripePaymentMethodId, {
|
||||
enabled: !!stripePaymentMethodId,
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useFeatureCan } from '@/hooks/state';
|
||||
import { DashboardInsider } from '@/components/Dashboard';
|
||||
import { useProjects } from '@/containers/Projects/hooks';
|
||||
import {
|
||||
useAccounts,
|
||||
useVendors,
|
||||
useItems,
|
||||
useBill,
|
||||
@@ -15,6 +14,7 @@ import {
|
||||
useCreateBill,
|
||||
useEditBill,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
import { useTaxRates } from '@/hooks/query/taxRates';
|
||||
|
||||
const BillFormContext = createContext();
|
||||
@@ -48,7 +48,7 @@ function BillFormProvider({ billId, ...props }) {
|
||||
const isProjectsFeatureCan = featureCan(Features.Projects);
|
||||
|
||||
// Handle fetch accounts.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
// Handle fetch vendors data table
|
||||
const {
|
||||
|
||||
@@ -3,7 +3,6 @@ import React, { createContext, useContext, useState } from 'react';
|
||||
import { Features } from '@/constants';
|
||||
import { useFeatureCan } from '@/hooks/state';
|
||||
import {
|
||||
useAccounts,
|
||||
useVendors,
|
||||
useItems,
|
||||
useBranches,
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
useCreatePaymentMade,
|
||||
useEditPaymentMade,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
import { DashboardInsider } from '@/components';
|
||||
|
||||
// Payment made form context.
|
||||
@@ -29,7 +29,7 @@ function PaymentMadeFormProvider({ query, paymentMadeId, ...props }) {
|
||||
const isBranchFeatureCan = featureCan(Features.Branches);
|
||||
|
||||
// Handle fetch accounts data.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
// Handle fetch Items data table or list.
|
||||
const {
|
||||
|
||||
@@ -7,7 +7,6 @@ import { useProjects } from '@/containers/Projects/hooks';
|
||||
import {
|
||||
useSettingsPaymentReceives,
|
||||
usePaymentReceiveEditPage,
|
||||
useAccounts,
|
||||
useCustomers,
|
||||
useBranches,
|
||||
useCreatePaymentReceive,
|
||||
@@ -15,6 +14,7 @@ import {
|
||||
usePaymentReceivedState,
|
||||
PaymentReceivedStateResponse,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
|
||||
|
||||
interface PaymentReceivedFormContextValue {
|
||||
@@ -52,7 +52,7 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) {
|
||||
const paymentEntriesEditPage = paymentReceivedEditData?.entries
|
||||
|
||||
// Handle fetch accounts data.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
// Fetch payment made settings.
|
||||
const fetchSettings = useSettingsPaymentReceives();
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useFeatureCan } from '@/hooks/state';
|
||||
import { DashboardInsider } from '@/components/Dashboard';
|
||||
import {
|
||||
useReceipt,
|
||||
useAccounts,
|
||||
useSettingsReceipts,
|
||||
useCustomers,
|
||||
useWarehouses,
|
||||
@@ -16,6 +15,7 @@ import {
|
||||
useGetReceiptState,
|
||||
IGetReceiptStateResponse,
|
||||
} from '@/hooks/query';
|
||||
import { useAccountsTrpc } from '@/hooks/trpc';
|
||||
import { useProjects } from '@/containers/Projects/hooks';
|
||||
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
|
||||
|
||||
@@ -43,7 +43,7 @@ function ReceiptFormProvider({ receiptId, ...props }) {
|
||||
enabled: !!receiptId,
|
||||
});
|
||||
// Fetch accounts list.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc();
|
||||
|
||||
// Fetch customers list.
|
||||
const {
|
||||
|
||||
@@ -128,7 +128,7 @@ export const useAuthMetadata = (props = {}) => {
|
||||
* Resend the mail of signup verification.
|
||||
*/
|
||||
export const useAuthSignUpVerifyResendMail = (props) => {
|
||||
const apiRequest = useAuthApiRequest();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
() => apiRequest.post(AuthRoute.SignupVerifyResend),
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// @ts-nocheck
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
// Query client config.
|
||||
export const queryConfig = {
|
||||
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 apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(values) => apiRequest.post('credit-notes', values),
|
||||
{
|
||||
onSuccess: (res, values) => {
|
||||
// Common invalidate queries.
|
||||
commonInvalidateQueries(queryClient);
|
||||
},
|
||||
...props,
|
||||
return useMutation((values) => apiRequest.post('credit-notes', values), {
|
||||
onSuccess: (res, values) => {
|
||||
// Common invalidate queries.
|
||||
commonInvalidateQueries(queryClient);
|
||||
},
|
||||
);
|
||||
...props,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,8 +215,7 @@ export function useCreateRefundCreditNote(props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) =>
|
||||
apiRequest.post(`credit-notes/${id}/refunds`, values),
|
||||
([id, values]) => apiRequest.post(`credit-notes/${id}/refunds`, values),
|
||||
{
|
||||
onSuccess: (res, [id, values]) => {
|
||||
// Common invalidate queries.
|
||||
@@ -240,19 +236,16 @@ export function useDeleteRefundCreditNote(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(id) => apiRequest.delete(`credit-notes/refunds/${id}`),
|
||||
{
|
||||
onSuccess: (res, id) => {
|
||||
// Common invalidate queries.
|
||||
commonInvalidateQueries(queryClient);
|
||||
return useMutation((id) => apiRequest.delete(`credit-notes/refunds/${id}`), {
|
||||
onSuccess: (res, id) => {
|
||||
// Common invalidate queries.
|
||||
commonInvalidateQueries(queryClient);
|
||||
|
||||
// Invalidate vendor credit query.
|
||||
queryClient.invalidateQueries([t.CREDIT_NOTE, id]);
|
||||
},
|
||||
...props,
|
||||
// Invalidate vendor credit query.
|
||||
queryClient.invalidateQueries([t.CREDIT_NOTE, id]);
|
||||
},
|
||||
);
|
||||
...props,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,7 +294,7 @@ export function useReconcileCreditNote(id, props, requestProps) {
|
||||
[t.RECONCILE_CREDIT_NOTE, id],
|
||||
{
|
||||
method: 'get',
|
||||
url: `credit-notes/${id}/applied-invoices`,
|
||||
url: `credit-notes/${id}/apply-invoices`,
|
||||
...requestProps,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { useMutation } from 'react-query';
|
||||
import { useRequestQuery } from '../useQueryRequest';
|
||||
import useApiRequest from '../useRequest';
|
||||
import { transformToCamelCase } from '@/utils';
|
||||
|
||||
/**
|
||||
* Authentication invite accept.
|
||||
@@ -22,9 +23,9 @@ export const useAuthInviteAccept = (props) => {
|
||||
export const useInviteMetaByToken = (token, props) => {
|
||||
return useRequestQuery(
|
||||
['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
|
||||
}
|
||||
);
|
||||
|
||||
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"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@trpc/server": "^11.10.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './countries';
|
||||
export * from './trpc';
|
||||
|
||||
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