mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
Merge pull request #523 from bigcapitalhq/matching-transactions-fixes
fix: Matching transactions bugs
This commit is contained in:
@@ -5,7 +5,8 @@ exports.up = function (knex) {
|
|||||||
.integer('uncategorized_transaction_id')
|
.integer('uncategorized_transaction_id')
|
||||||
.unsigned()
|
.unsigned()
|
||||||
.references('id')
|
.references('id')
|
||||||
.inTable('uncategorized_cashflow_transactions');
|
.inTable('uncategorized_cashflow_transactions')
|
||||||
|
.withKeyName('recognizedBankTransactionsUncategorizedTransIdForeign');
|
||||||
table
|
table
|
||||||
.integer('bank_rule_id')
|
.integer('bank_rule_id')
|
||||||
.unsigned()
|
.unsigned()
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
exports.up = function (knex) {
|
exports.up = function (knex) {
|
||||||
return knex.schema.table('uncategorized_cashflow_transactions', (table) => {
|
return knex.schema.table('uncategorized_cashflow_transactions', (table) => {
|
||||||
table.integer('recognized_transaction_id').unsigned();
|
table
|
||||||
|
.integer('recognized_transaction_id')
|
||||||
|
.unsigned()
|
||||||
|
.references('id')
|
||||||
|
.inTable('recognized_bank_transactions')
|
||||||
|
.withKeyName('uncategorizedCashflowTransRecognizedTranIdForeign');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
exports.up = function (knex) {
|
exports.up = function (knex) {
|
||||||
return knex.schema.createTable('matched_bank_transactions', (table) => {
|
return knex.schema.createTable('matched_bank_transactions', (table) => {
|
||||||
table.increments('id');
|
table.increments('id');
|
||||||
table.integer('uncategorized_transaction_id').unsigned();
|
table
|
||||||
|
.integer('uncategorized_transaction_id')
|
||||||
|
.unsigned()
|
||||||
|
.references('id')
|
||||||
|
.inTable('uncategorized_cashflow_transactions');
|
||||||
table.string('reference_type');
|
table.string('reference_type');
|
||||||
table.integer('reference_id').unsigned();
|
table.integer('reference_id').unsigned();
|
||||||
table.decimal('amount');
|
table.decimal('amount');
|
||||||
|
|||||||
@@ -130,8 +130,9 @@ export interface ICommandCashflowDeletedPayload {
|
|||||||
|
|
||||||
export interface ICashflowTransactionCategorizedPayload {
|
export interface ICashflowTransactionCategorizedPayload {
|
||||||
tenantId: number;
|
tenantId: number;
|
||||||
cashflowTransactionId: number;
|
uncategorizedTransaction: any;
|
||||||
cashflowTransaction: ICashflowTransaction;
|
cashflowTransaction: ICashflowTransaction;
|
||||||
|
categorizeDTO: any;
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
export interface ICashflowTransactionUncategorizingPayload {
|
export interface ICashflowTransactionUncategorizingPayload {
|
||||||
|
|||||||
8
packages/server/src/interfaces/Import.ts
Normal file
8
packages/server/src/interfaces/Import.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { ImportFilePreviewPOJO } from "@/services/Import/interfaces";
|
||||||
|
|
||||||
|
|
||||||
|
export interface IImportFileCommitedEventPayload {
|
||||||
|
tenantId: number;
|
||||||
|
importId: number;
|
||||||
|
meta: ImportFilePreviewPOJO;
|
||||||
|
}
|
||||||
@@ -112,6 +112,7 @@ import { RecognizeSyncedBankTranasctions } from '@/services/Banking/Plaid/subscr
|
|||||||
import { UnlinkBankRuleOnDeleteBankRule } from '@/services/Banking/Rules/events/UnlinkBankRuleOnDeleteBankRule';
|
import { UnlinkBankRuleOnDeleteBankRule } from '@/services/Banking/Rules/events/UnlinkBankRuleOnDeleteBankRule';
|
||||||
import { DecrementUncategorizedTransactionOnMatching } from '@/services/Banking/Matching/events/DecrementUncategorizedTransactionsOnMatch';
|
import { DecrementUncategorizedTransactionOnMatching } from '@/services/Banking/Matching/events/DecrementUncategorizedTransactionsOnMatch';
|
||||||
import { DecrementUncategorizedTransactionOnExclude } from '@/services/Banking/Exclude/events/DecrementUncategorizedTransactionOnExclude';
|
import { DecrementUncategorizedTransactionOnExclude } from '@/services/Banking/Exclude/events/DecrementUncategorizedTransactionOnExclude';
|
||||||
|
import { DecrementUncategorizedTransactionOnCategorize } from '@/services/Cashflow/subscribers/DecrementUncategorizedTransactionOnCategorize';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return new EventPublisher();
|
return new EventPublisher();
|
||||||
@@ -262,6 +263,7 @@ export const susbcribers = () => {
|
|||||||
UnlinkBankRuleOnDeleteBankRule,
|
UnlinkBankRuleOnDeleteBankRule,
|
||||||
DecrementUncategorizedTransactionOnMatching,
|
DecrementUncategorizedTransactionOnMatching,
|
||||||
DecrementUncategorizedTransactionOnExclude,
|
DecrementUncategorizedTransactionOnExclude,
|
||||||
|
DecrementUncategorizedTransactionOnCategorize,
|
||||||
|
|
||||||
// Validate matching
|
// Validate matching
|
||||||
ValidateMatchingOnCashflowDelete,
|
ValidateMatchingOnCashflowDelete,
|
||||||
|
|||||||
@@ -184,56 +184,4 @@ export default class UncategorizedCashflowTransaction extends mixin(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the count of uncategorized transactions for the associated account
|
|
||||||
* based on the specified operation.
|
|
||||||
* @param {QueryContext} queryContext - The query context for the transaction.
|
|
||||||
* @param {boolean} increment - Indicates whether to increment or decrement the count.
|
|
||||||
*/
|
|
||||||
private async updateUncategorizedTransactionCount(
|
|
||||||
queryContext: QueryContext,
|
|
||||||
increment: boolean,
|
|
||||||
amount: number = 1
|
|
||||||
) {
|
|
||||||
const operation = increment ? 'increment' : 'decrement';
|
|
||||||
|
|
||||||
await Account.query(queryContext.transaction)
|
|
||||||
.findById(this.accountId)
|
|
||||||
[operation]('uncategorized_transactions', amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs after insert.
|
|
||||||
* @param {QueryContext} queryContext
|
|
||||||
*/
|
|
||||||
public async $afterInsert(queryContext) {
|
|
||||||
await super.$afterInsert(queryContext);
|
|
||||||
await this.updateUncategorizedTransactionCount(queryContext, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs after update.
|
|
||||||
* @param {ModelOptions} opt
|
|
||||||
* @param {QueryContext} queryContext
|
|
||||||
*/
|
|
||||||
public async $afterUpdate(
|
|
||||||
opt: ModelOptions,
|
|
||||||
queryContext: QueryContext
|
|
||||||
): Promise<any> {
|
|
||||||
await super.$afterUpdate(opt, queryContext);
|
|
||||||
|
|
||||||
if (this.id && this.categorized) {
|
|
||||||
await this.updateUncategorizedTransactionCount(queryContext, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs after delete.
|
|
||||||
* @param {QueryContext} queryContext
|
|
||||||
*/
|
|
||||||
public async $afterDelete(queryContext: QueryContext) {
|
|
||||||
await super.$afterDelete(queryContext);
|
|
||||||
await this.updateUncategorizedTransactionCount(queryContext, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export class DecrementUncategorizedTransactionOnMatching {
|
|||||||
const transaction = await UncategorizedCashflowTransaction.query().findById(
|
const transaction = await UncategorizedCashflowTransaction.query().findById(
|
||||||
uncategorizedTransactionId
|
uncategorizedTransactionId
|
||||||
);
|
);
|
||||||
//
|
|
||||||
await Account.query(trx)
|
await Account.query(trx)
|
||||||
.findById(transaction.accountId)
|
.findById(transaction.accountId)
|
||||||
.decrement('uncategorizedTransactions', 1);
|
.decrement('uncategorizedTransactions', 1);
|
||||||
@@ -60,7 +59,6 @@ export class DecrementUncategorizedTransactionOnMatching {
|
|||||||
const transaction = await UncategorizedCashflowTransaction.query().findById(
|
const transaction = await UncategorizedCashflowTransaction.query().findById(
|
||||||
uncategorizedTransactionId
|
uncategorizedTransactionId
|
||||||
);
|
);
|
||||||
//
|
|
||||||
await Account.query(trx)
|
await Account.query(trx)
|
||||||
.findById(transaction.accountId)
|
.findById(transaction.accountId)
|
||||||
.increment('uncategorizedTransactions', 1);
|
.increment('uncategorizedTransactions', 1);
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ export class RegonizeTransactionsJob {
|
|||||||
* Triggers sending invoice mail.
|
* Triggers sending invoice mail.
|
||||||
*/
|
*/
|
||||||
private handler = async (job, done: Function) => {
|
private handler = async (job, done: Function) => {
|
||||||
const { tenantId } = job.attrs.data;
|
const { tenantId, batch } = job.attrs.data;
|
||||||
const regonizeTransactions = Container.get(RecognizeTranasctionsService);
|
const regonizeTransactions = Container.get(RecognizeTranasctionsService);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await regonizeTransactions.recognizeTransactions(tenantId);
|
await regonizeTransactions.recognizeTransactions(tenantId, batch);
|
||||||
done();
|
done();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import {
|
|||||||
IBankRuleEventDeletedPayload,
|
IBankRuleEventDeletedPayload,
|
||||||
IBankRuleEventEditedPayload,
|
IBankRuleEventEditedPayload,
|
||||||
} from '../../Rules/types';
|
} from '../../Rules/types';
|
||||||
|
import { IImportFileCommitedEventPayload } from '@/interfaces/Import';
|
||||||
|
import { Import } from '@/system/models';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class TriggerRecognizedTransactions {
|
export class TriggerRecognizedTransactions {
|
||||||
@@ -27,6 +29,10 @@ export class TriggerRecognizedTransactions {
|
|||||||
events.bankRules.onDeleted,
|
events.bankRules.onDeleted,
|
||||||
this.recognizedTransactionsOnRuleDeleted.bind(this)
|
this.recognizedTransactionsOnRuleDeleted.bind(this)
|
||||||
);
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.import.onImportCommitted,
|
||||||
|
this.triggerRecognizeTransactionsOnImportCommitted.bind(this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,4 +79,20 @@ export class TriggerRecognizedTransactions {
|
|||||||
const payload = { tenantId };
|
const payload = { tenantId };
|
||||||
await this.agenda.now('recognize-uncategorized-transactions-job', payload);
|
await this.agenda.now('recognize-uncategorized-transactions-job', payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers the recognize bank transactions once the imported file commit.
|
||||||
|
* @param {IImportFileCommitedEventPayload} payload -
|
||||||
|
*/
|
||||||
|
private async triggerRecognizeTransactionsOnImportCommitted({
|
||||||
|
tenantId,
|
||||||
|
importId,
|
||||||
|
meta,
|
||||||
|
}: IImportFileCommitedEventPayload) {
|
||||||
|
const importFile = await Import.query().findOne({ importId });
|
||||||
|
const batch = importFile.paramsParsed.batch;
|
||||||
|
const payload = { tenantId, batch };
|
||||||
|
|
||||||
|
await this.agenda.now('recognize-uncategorized-transactions-job', payload);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,20 +84,23 @@ export class CategorizeCashflowTransaction {
|
|||||||
cashflowTransactionDTO
|
cashflowTransactionDTO
|
||||||
);
|
);
|
||||||
// Updates the uncategorized transaction as categorized.
|
// Updates the uncategorized transaction as categorized.
|
||||||
await UncategorizedCashflowTransaction.query(trx).patchAndFetchById(
|
const uncategorizedTransaction =
|
||||||
uncategorizedTransactionId,
|
await UncategorizedCashflowTransaction.query(trx).patchAndFetchById(
|
||||||
{
|
uncategorizedTransactionId,
|
||||||
categorized: true,
|
{
|
||||||
categorizeRefType: 'CashflowTransaction',
|
categorized: true,
|
||||||
categorizeRefId: cashflowTransaction.id,
|
categorizeRefType: 'CashflowTransaction',
|
||||||
}
|
categorizeRefId: cashflowTransaction.id,
|
||||||
);
|
}
|
||||||
|
);
|
||||||
// Triggers `onCashflowTransactionCategorized` event.
|
// Triggers `onCashflowTransactionCategorized` event.
|
||||||
await this.eventPublisher.emitAsync(
|
await this.eventPublisher.emitAsync(
|
||||||
events.cashflow.onTransactionCategorized,
|
events.cashflow.onTransactionCategorized,
|
||||||
{
|
{
|
||||||
tenantId,
|
tenantId,
|
||||||
// cashflowTransaction,
|
cashflowTransaction,
|
||||||
|
uncategorizedTransaction,
|
||||||
|
categorizeDTO,
|
||||||
trx,
|
trx,
|
||||||
} as ICashflowTransactionCategorizedPayload
|
} as ICashflowTransactionCategorizedPayload
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
|
import uniqid from 'uniqid';
|
||||||
import { Importable } from '../Import/Importable';
|
import { Importable } from '../Import/Importable';
|
||||||
import { CreateUncategorizedTransaction } from './CreateUncategorizedTransaction';
|
import { CreateUncategorizedTransaction } from './CreateUncategorizedTransaction';
|
||||||
import { CreateUncategorizedTransactionDTO } from '@/interfaces';
|
import { CreateUncategorizedTransactionDTO } from '@/interfaces';
|
||||||
@@ -15,6 +16,7 @@ export class UncategorizedTransactionsImportable extends Importable {
|
|||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private tenancy: HasTenancyService;
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Passing the sheet DTO to create uncategorized transaction.
|
* Passing the sheet DTO to create uncategorized transaction.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -43,6 +45,7 @@ export class UncategorizedTransactionsImportable extends Importable {
|
|||||||
return {
|
return {
|
||||||
...createDTO,
|
...createDTO,
|
||||||
accountId: context.import.paramsParsed.accountId,
|
accountId: context.import.paramsParsed.accountId,
|
||||||
|
batch: context.import.paramsParsed.batch,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +57,9 @@ export class UncategorizedTransactionsImportable extends Importable {
|
|||||||
return BankTransactionsSampleData;
|
return BankTransactionsSampleData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
// # Params
|
||||||
|
// ------------------
|
||||||
/**
|
/**
|
||||||
* Params validation schema.
|
* Params validation schema.
|
||||||
* @returns {ValidationSchema[]}
|
* @returns {ValidationSchema[]}
|
||||||
@@ -79,4 +85,17 @@ export class UncategorizedTransactionsImportable extends Importable {
|
|||||||
await Account.query().findById(params.accountId).throwIfNotFound({});
|
await Account.query().findById(params.accountId).throwIfNotFound({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformes the import params before storing them.
|
||||||
|
* @param {Record<string, any>} parmas
|
||||||
|
*/
|
||||||
|
public transformParams(parmas: Record<string, any>) {
|
||||||
|
const batch = uniqid();
|
||||||
|
|
||||||
|
return {
|
||||||
|
...parmas,
|
||||||
|
batch,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
|
import {
|
||||||
|
ICashflowTransactionCategorizedPayload,
|
||||||
|
ICashflowTransactionUncategorizedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class DecrementUncategorizedTransactionOnCategorize {
|
||||||
|
@Inject()
|
||||||
|
private tenancy: HasTenancyService;
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.cashflow.onTransactionCategorized,
|
||||||
|
this.decrementUnCategorizedTransactionsOnCategorized.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.cashflow.onTransactionUncategorized,
|
||||||
|
this.incrementUnCategorizedTransactionsOnUncategorized.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrement the uncategoirzed transactions on the account once categorizing.
|
||||||
|
* @param {ICashflowTransactionCategorizedPayload}
|
||||||
|
*/
|
||||||
|
public async decrementUnCategorizedTransactionsOnCategorized({
|
||||||
|
tenantId,
|
||||||
|
uncategorizedTransaction,
|
||||||
|
}: ICashflowTransactionCategorizedPayload) {
|
||||||
|
const { Account } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
await Account.query()
|
||||||
|
.findById(uncategorizedTransaction.accountId)
|
||||||
|
.decrement('uncategorizedTransactions', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the uncategorized transaction on the given account on uncategorizing.
|
||||||
|
* @param {IManualJournalDeletingPayload}
|
||||||
|
*/
|
||||||
|
public async incrementUnCategorizedTransactionsOnUncategorized({
|
||||||
|
tenantId,
|
||||||
|
uncategorizedTransaction,
|
||||||
|
}: ICashflowTransactionUncategorizedPayload) {
|
||||||
|
const { Account } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
await Account.query()
|
||||||
|
.findById(uncategorizedTransaction.accountId)
|
||||||
|
.increment('uncategorizedTransactions', 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,7 +74,7 @@ export class CashflowAccountTransactionReport extends FinancialSheet {
|
|||||||
const firstMatchedTrans = first(matchedTrans);
|
const firstMatchedTrans = first(matchedTrans);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(firstCategorizedTrans?.id ||
|
firstCategorizedTrans?.id ||
|
||||||
firstMatchedTrans?.uncategorizedTransactionId ||
|
firstMatchedTrans?.uncategorizedTransactionId ||
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,14 +15,10 @@ import { ServiceError } from '@/exceptions';
|
|||||||
import { getUniqueImportableValue, trimObject } from './_utils';
|
import { getUniqueImportableValue, trimObject } from './_utils';
|
||||||
import { ImportableResources } from './ImportableResources';
|
import { ImportableResources } from './ImportableResources';
|
||||||
import ResourceService from '../Resource/ResourceService';
|
import ResourceService from '../Resource/ResourceService';
|
||||||
import HasTenancyService from '../Tenancy/TenancyService';
|
|
||||||
import { Import } from '@/system/models';
|
import { Import } from '@/system/models';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class ImportFileCommon {
|
export class ImportFileCommon {
|
||||||
@Inject()
|
|
||||||
private tenancy: HasTenancyService;
|
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private importFileValidator: ImportFileDataValidator;
|
private importFileValidator: ImportFileDataValidator;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import HasTenancyService from '../Tenancy/TenancyService';
|
||||||
|
import { ImportFilePreviewPOJO } from './interfaces';
|
||||||
|
import { ImportFileProcess } from './ImportFileProcess';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { IImportFileCommitedEventPayload } from '@/interfaces/Import';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class ImportFileProcessCommit {
|
||||||
|
@Inject()
|
||||||
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private importFile: ImportFileProcess;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commits the imported file.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} importId
|
||||||
|
* @returns {Promise<ImportFilePreviewPOJO>}
|
||||||
|
*/
|
||||||
|
public async commit(
|
||||||
|
tenantId: number,
|
||||||
|
importId: number
|
||||||
|
): Promise<ImportFilePreviewPOJO> {
|
||||||
|
const knex = this.tenancy.knex(tenantId);
|
||||||
|
const trx = await knex.transaction({ isolationLevel: 'read uncommitted' });
|
||||||
|
|
||||||
|
const meta = await this.importFile.import(tenantId, importId, trx);
|
||||||
|
|
||||||
|
// Commit the successed transaction.
|
||||||
|
await trx.commit();
|
||||||
|
|
||||||
|
// Triggers `onImportFileCommitted` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.import.onImportCommitted, {
|
||||||
|
meta,
|
||||||
|
importId,
|
||||||
|
tenantId,
|
||||||
|
} as IImportFileCommitedEventPayload);
|
||||||
|
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import { ImportFileProcess } from './ImportFileProcess';
|
|||||||
import { ImportFilePreview } from './ImportFilePreview';
|
import { ImportFilePreview } from './ImportFilePreview';
|
||||||
import { ImportSampleService } from './ImportSample';
|
import { ImportSampleService } from './ImportSample';
|
||||||
import { ImportFileMeta } from './ImportFileMeta';
|
import { ImportFileMeta } from './ImportFileMeta';
|
||||||
|
import { ImportFileProcessCommit } from './ImportFileProcessCommit';
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
export class ImportResourceApplication {
|
export class ImportResourceApplication {
|
||||||
@@ -27,6 +28,9 @@ export class ImportResourceApplication {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private importMetaService: ImportFileMeta;
|
private importMetaService: ImportFileMeta;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private importProcessCommit: ImportFileProcessCommit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the imported file and stores the import file meta under unqiue id.
|
* Reads the imported file and stores the import file meta under unqiue id.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
@@ -74,7 +78,7 @@ export class ImportResourceApplication {
|
|||||||
* @returns {Promise<ImportFilePreviewPOJO>}
|
* @returns {Promise<ImportFilePreviewPOJO>}
|
||||||
*/
|
*/
|
||||||
public async process(tenantId: number, importId: number) {
|
public async process(tenantId: number, importId: number) {
|
||||||
return this.importProcessService.import(tenantId, importId);
|
return this.importProcessCommit.commit(tenantId, importId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -647,4 +647,9 @@ export default {
|
|||||||
onUnexcluding: 'onBankTransactionUnexcluding',
|
onUnexcluding: 'onBankTransactionUnexcluding',
|
||||||
onUnexcluded: 'onBankTransactionUnexcluded',
|
onUnexcluded: 'onBankTransactionUnexcluded',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Import files.
|
||||||
|
import: {
|
||||||
|
onImportCommitted: 'onImportFileCommitted',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,3 +39,12 @@ export const TRANSACRIONS_TYPE = [
|
|||||||
'OtherExpense',
|
'OtherExpense',
|
||||||
'TransferToAccount',
|
'TransferToAccount',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const MoneyCategoryPerCreditAccountRootType = {
|
||||||
|
OwnerContribution: ['equity'],
|
||||||
|
OtherIncome: ['income'],
|
||||||
|
OwnerDrawing: ['equity'],
|
||||||
|
OtherExpense: ['expense'],
|
||||||
|
TransferToAccount: ['asset'],
|
||||||
|
TransferFromAccount: ['asset'],
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
||||||
import { Button, Classes, Intent, Radio, Tag } from '@blueprintjs/core';
|
import { Button, Classes, Intent, Radio, Tag } from '@blueprintjs/core';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
@@ -16,11 +17,11 @@ import {
|
|||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useCreateBankRule, useEditBankRule } from '@/hooks/query/bank-rules';
|
import { useCreateBankRule, useEditBankRule } from '@/hooks/query/bank-rules';
|
||||||
import {
|
import {
|
||||||
AssignTransactionTypeOptions,
|
|
||||||
FieldCondition,
|
FieldCondition,
|
||||||
Fields,
|
Fields,
|
||||||
RuleFormValues,
|
RuleFormValues,
|
||||||
TransactionTypeOptions,
|
TransactionTypeOptions,
|
||||||
|
getAccountRootFromMoneyCategory,
|
||||||
initialValues,
|
initialValues,
|
||||||
} from './_utils';
|
} from './_utils';
|
||||||
import { useRuleFormDialogBoot } from './RuleFormBoot';
|
import { useRuleFormDialogBoot } from './RuleFormBoot';
|
||||||
@@ -31,6 +32,11 @@ import {
|
|||||||
} from '@/utils';
|
} from '@/utils';
|
||||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||||
import { DialogsName } from '@/constants/dialogs';
|
import { DialogsName } from '@/constants/dialogs';
|
||||||
|
import { getAddMoneyInOptions, getAddMoneyOutOptions } from '@/constants';
|
||||||
|
|
||||||
|
// Retrieves the add money in button options.
|
||||||
|
const MoneyInOptions = getAddMoneyInOptions();
|
||||||
|
const MoneyOutOptions = getAddMoneyOutOptions();
|
||||||
|
|
||||||
function RuleFormContentFormRoot({
|
function RuleFormContentFormRoot({
|
||||||
// #withDialogActions
|
// #withDialogActions
|
||||||
@@ -47,7 +53,6 @@ function RuleFormContentFormRoot({
|
|||||||
...initialValues,
|
...initialValues,
|
||||||
...transformToForm(transformToCamelCase(bankRule), initialValues),
|
...transformToForm(transformToCamelCase(bankRule), initialValues),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handles the form submitting.
|
// Handles the form submitting.
|
||||||
const handleSubmit = (
|
const handleSubmit = (
|
||||||
values: RuleFormValues,
|
values: RuleFormValues,
|
||||||
@@ -92,8 +97,9 @@ function RuleFormContentFormRoot({
|
|||||||
label={'Rule Name'}
|
label={'Rule Name'}
|
||||||
labelInfo={<Tag minimal>Required</Tag>}
|
labelInfo={<Tag minimal>Required</Tag>}
|
||||||
style={{ maxWidth: 300 }}
|
style={{ maxWidth: 300 }}
|
||||||
|
fastField
|
||||||
>
|
>
|
||||||
<FInputGroup name={'name'} />
|
<FInputGroup name={'name'} fastField />
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
<FFormGroup
|
<FFormGroup
|
||||||
@@ -101,29 +107,22 @@ function RuleFormContentFormRoot({
|
|||||||
label={'Apply the rule to account'}
|
label={'Apply the rule to account'}
|
||||||
labelInfo={<Tag minimal>Required</Tag>}
|
labelInfo={<Tag minimal>Required</Tag>}
|
||||||
style={{ maxWidth: 350 }}
|
style={{ maxWidth: 350 }}
|
||||||
|
fastField
|
||||||
>
|
>
|
||||||
<AccountsSelect
|
<AccountsSelect
|
||||||
name={'applyIfAccountId'}
|
name={'applyIfAccountId'}
|
||||||
items={accounts}
|
items={accounts}
|
||||||
filterByTypes={['cash', 'bank']}
|
filterByTypes={['cash', 'bank']}
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
<FFormGroup
|
<RuleApplyIfTransactionTypeField />
|
||||||
name={'applyIfTransactionType'}
|
|
||||||
label={'Apply to transactions are'}
|
|
||||||
style={{ maxWidth: 350 }}
|
|
||||||
>
|
|
||||||
<FSelect
|
|
||||||
name={'applyIfTransactionType'}
|
|
||||||
items={TransactionTypeOptions}
|
|
||||||
popoverProps={{ minimal: true, inline: false }}
|
|
||||||
/>
|
|
||||||
</FFormGroup>
|
|
||||||
|
|
||||||
<FFormGroup
|
<FFormGroup
|
||||||
name={'conditionsType'}
|
name={'conditionsType'}
|
||||||
label={'Categorize the transactions when'}
|
label={'Categorize the transactions when'}
|
||||||
|
fastField
|
||||||
>
|
>
|
||||||
<FRadioGroup name={'conditionsType'}>
|
<FRadioGroup name={'conditionsType'}>
|
||||||
<Radio value={'and'} label={'All the following criteria matches'} />
|
<Radio value={'and'} label={'All the following criteria matches'} />
|
||||||
@@ -139,34 +138,16 @@ function RuleFormContentFormRoot({
|
|||||||
Then Assign
|
Then Assign
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<FFormGroup
|
<RuleAssignCategoryField />
|
||||||
name={'assignCategory'}
|
<RuleAssignCategoryAccountField />
|
||||||
label={'Transaction type'}
|
|
||||||
labelInfo={<Tag minimal>Required</Tag>}
|
|
||||||
style={{ maxWidth: 300 }}
|
|
||||||
>
|
|
||||||
<FSelect
|
|
||||||
name={'assignCategory'}
|
|
||||||
items={AssignTransactionTypeOptions}
|
|
||||||
popoverProps={{ minimal: true, inline: false }}
|
|
||||||
/>
|
|
||||||
</FFormGroup>
|
|
||||||
|
|
||||||
<FFormGroup
|
|
||||||
name={'assignAccountId'}
|
|
||||||
label={'Account category'}
|
|
||||||
labelInfo={<Tag minimal>Required</Tag>}
|
|
||||||
style={{ maxWidth: 300 }}
|
|
||||||
>
|
|
||||||
<AccountsSelect name={'assignAccountId'} items={accounts} />
|
|
||||||
</FFormGroup>
|
|
||||||
|
|
||||||
<FFormGroup
|
<FFormGroup
|
||||||
name={'assignRef'}
|
name={'assignRef'}
|
||||||
label={'Reference'}
|
label={'Reference'}
|
||||||
style={{ maxWidth: 300 }}
|
style={{ maxWidth: 300 }}
|
||||||
|
fastField
|
||||||
>
|
>
|
||||||
<FInputGroup name={'assignRef'} />
|
<FInputGroup name={'assignRef'} fastField />
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
<RuleFormActions />
|
<RuleFormActions />
|
||||||
@@ -203,11 +184,13 @@ function RuleFormConditions() {
|
|||||||
name={`conditions[${index}].field`}
|
name={`conditions[${index}].field`}
|
||||||
label={'Field'}
|
label={'Field'}
|
||||||
style={{ marginBottom: 0, flex: '1 0' }}
|
style={{ marginBottom: 0, flex: '1 0' }}
|
||||||
|
fastField
|
||||||
>
|
>
|
||||||
<FSelect
|
<FSelect
|
||||||
name={`conditions[${index}].field`}
|
name={`conditions[${index}].field`}
|
||||||
items={Fields}
|
items={Fields}
|
||||||
popoverProps={{ minimal: true, inline: false }}
|
popoverProps={{ minimal: true, inline: false }}
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
@@ -215,11 +198,13 @@ function RuleFormConditions() {
|
|||||||
name={`conditions[${index}].comparator`}
|
name={`conditions[${index}].comparator`}
|
||||||
label={'Condition'}
|
label={'Condition'}
|
||||||
style={{ marginBottom: 0, flex: '1 0' }}
|
style={{ marginBottom: 0, flex: '1 0' }}
|
||||||
|
fastField
|
||||||
>
|
>
|
||||||
<FSelect
|
<FSelect
|
||||||
name={`conditions[${index}].comparator`}
|
name={`conditions[${index}].comparator`}
|
||||||
items={FieldCondition}
|
items={FieldCondition}
|
||||||
popoverProps={{ minimal: true, inline: false }}
|
popoverProps={{ minimal: true, inline: false }}
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
@@ -227,8 +212,9 @@ function RuleFormConditions() {
|
|||||||
name={`conditions[${index}].value`}
|
name={`conditions[${index}].value`}
|
||||||
label={'Value'}
|
label={'Value'}
|
||||||
style={{ marginBottom: 0, flex: '1 0 ', width: '40%' }}
|
style={{ marginBottom: 0, flex: '1 0 ', width: '40%' }}
|
||||||
|
fastField
|
||||||
>
|
>
|
||||||
<FInputGroup name={`conditions[${index}].value`} />
|
<FInputGroup name={`conditions[${index}].value`} fastField />
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
</Group>
|
</Group>
|
||||||
))}
|
))}
|
||||||
@@ -284,3 +270,104 @@ function RuleFormActionsRoot({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const RuleFormActions = R.compose(withDialogActions)(RuleFormActionsRoot);
|
const RuleFormActions = R.compose(withDialogActions)(RuleFormActionsRoot);
|
||||||
|
|
||||||
|
function RuleApplyIfTransactionTypeField() {
|
||||||
|
const { setFieldValue } = useFormikContext<RuleFormValues>();
|
||||||
|
|
||||||
|
const handleItemChange = useCallback(
|
||||||
|
(item: any) => {
|
||||||
|
setFieldValue('applyIfTransactionType', item.value);
|
||||||
|
setFieldValue('assignCategory', '');
|
||||||
|
setFieldValue('assignAccountId', '');
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FFormGroup
|
||||||
|
name={'applyIfTransactionType'}
|
||||||
|
label={'Apply to transactions are'}
|
||||||
|
style={{ maxWidth: 350 }}
|
||||||
|
fastField
|
||||||
|
>
|
||||||
|
<FSelect
|
||||||
|
name={'applyIfTransactionType'}
|
||||||
|
items={TransactionTypeOptions}
|
||||||
|
popoverProps={{ minimal: true, inline: false }}
|
||||||
|
onItemChange={handleItemChange}
|
||||||
|
fastField
|
||||||
|
/>
|
||||||
|
</FFormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RuleAssignCategoryField() {
|
||||||
|
const { values, setFieldValue } = useFormikContext<RuleFormValues>();
|
||||||
|
|
||||||
|
// Retrieves the transaction types if it is deposit or withdrawal.
|
||||||
|
const transactionTypes = useMemo(
|
||||||
|
() =>
|
||||||
|
values?.applyIfTransactionType === 'deposit'
|
||||||
|
? MoneyInOptions
|
||||||
|
: MoneyOutOptions,
|
||||||
|
[values?.applyIfTransactionType],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handles the select item change.
|
||||||
|
const handleItemChange = useCallback(
|
||||||
|
(item: any) => {
|
||||||
|
setFieldValue('assignCategory', item.value);
|
||||||
|
setFieldValue('assignAccountId', '');
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FFormGroup
|
||||||
|
name={'assignCategory'}
|
||||||
|
label={'Transaction type'}
|
||||||
|
labelInfo={<Tag minimal>Required</Tag>}
|
||||||
|
style={{ maxWidth: 300 }}
|
||||||
|
fastField
|
||||||
|
>
|
||||||
|
<FSelect
|
||||||
|
name={'assignCategory'}
|
||||||
|
items={transactionTypes}
|
||||||
|
popoverProps={{ minimal: true, inline: false }}
|
||||||
|
valueAccessor={'value'}
|
||||||
|
textAccessor={'name'}
|
||||||
|
onItemChange={handleItemChange}
|
||||||
|
fastField
|
||||||
|
/>
|
||||||
|
</FFormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RuleAssignCategoryAccountField() {
|
||||||
|
const { values } = useFormikContext<RuleFormValues>();
|
||||||
|
const { accounts } = useRuleFormDialogBoot();
|
||||||
|
|
||||||
|
const accountRoot = useMemo(
|
||||||
|
() => getAccountRootFromMoneyCategory(values.assignCategory),
|
||||||
|
[values.assignCategory],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FFormGroup
|
||||||
|
name={'assignAccountId'}
|
||||||
|
label={'Account category'}
|
||||||
|
labelInfo={<Tag minimal>Required</Tag>}
|
||||||
|
style={{ maxWidth: 300 }}
|
||||||
|
fastField
|
||||||
|
shouldUpdateDeps={{ accountRoot }}
|
||||||
|
>
|
||||||
|
<AccountsSelect
|
||||||
|
name={'assignAccountId'}
|
||||||
|
items={accounts}
|
||||||
|
filterByRootTypes={accountRoot}
|
||||||
|
shouldUpdateDeps={{ accountRoot }}
|
||||||
|
fastField
|
||||||
|
/>
|
||||||
|
</FFormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
import { camelCase, get, upperFirst } from 'lodash';
|
||||||
|
import { MoneyCategoryPerCreditAccountRootType } from '@/constants/cashflowOptions';
|
||||||
|
|
||||||
export const initialValues = {
|
export const initialValues = {
|
||||||
name: '',
|
name: '',
|
||||||
order: 0,
|
order: 0,
|
||||||
applyIfAccountId: '',
|
applyIfAccountId: '',
|
||||||
applyIfTransactionType: '',
|
applyIfTransactionType: 'deposit',
|
||||||
conditionsType: 'and',
|
conditionsType: 'and',
|
||||||
conditions: [
|
conditions: [
|
||||||
{
|
{
|
||||||
@@ -47,3 +50,9 @@ export const FieldCondition = [
|
|||||||
export const AssignTransactionTypeOptions = [
|
export const AssignTransactionTypeOptions = [
|
||||||
{ value: 'expense', text: 'Expense' },
|
{ value: 'expense', text: 'Expense' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const getAccountRootFromMoneyCategory = (category: string): string[] => {
|
||||||
|
const _category = upperFirst(camelCase(category));
|
||||||
|
|
||||||
|
return get(MoneyCategoryPerCreditAccountRootType, _category) || [];
|
||||||
|
};
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
PopoverInteractionKind,
|
PopoverInteractionKind,
|
||||||
Position,
|
Position,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
MenuDivider,
|
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { Box, FormatDateCell, Icon, MaterialProgressBar } from '@/components';
|
import { Box, FormatDateCell, Icon, MaterialProgressBar } from '@/components';
|
||||||
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
|
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
|
||||||
@@ -213,9 +212,8 @@ export function useAccountUncategorizedTransactionsColumns() {
|
|||||||
{
|
{
|
||||||
id: 'reference_number',
|
id: 'reference_number',
|
||||||
Header: 'Ref.#',
|
Header: 'Ref.#',
|
||||||
accessor: 'reference_number',
|
accessor: 'reference_no',
|
||||||
width: 50,
|
width: 50,
|
||||||
className: 'reference_number',
|
|
||||||
clickable: true,
|
clickable: true,
|
||||||
textOverview: true,
|
textOverview: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
FFormGroup,
|
FFormGroup,
|
||||||
FInputGroup,
|
FInputGroup,
|
||||||
FTextArea,
|
FTextArea,
|
||||||
|
Icon,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useCategorizeTransactionBoot } from '../CategorizeTransactionBoot';
|
import { useCategorizeTransactionBoot } from '../CategorizeTransactionBoot';
|
||||||
import { CategorizeTransactionBranchField } from '../CategorizeTransactionBranchField';
|
import { CategorizeTransactionBranchField } from '../CategorizeTransactionBranchField';
|
||||||
@@ -21,7 +22,7 @@ export default function CategorizeTransactionOtherIncome() {
|
|||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
formatDate={(date) => date.toLocaleDateString()}
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
parseDate={(str) => new Date(str)}
|
parseDate={(str) => new Date(str)}
|
||||||
inputProps={{ fill: true }}
|
inputProps={{ fill: true, leftElement: <Icon icon={'date-range'} /> }}
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
FFormGroup,
|
FFormGroup,
|
||||||
FInputGroup,
|
FInputGroup,
|
||||||
FTextArea,
|
FTextArea,
|
||||||
|
Icon,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useCategorizeTransactionBoot } from '../CategorizeTransactionBoot';
|
import { useCategorizeTransactionBoot } from '../CategorizeTransactionBoot';
|
||||||
import { CategorizeTransactionBranchField } from '../CategorizeTransactionBranchField';
|
import { CategorizeTransactionBranchField } from '../CategorizeTransactionBranchField';
|
||||||
@@ -21,7 +22,7 @@ export default function CategorizeTransactionOwnerContribution() {
|
|||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
formatDate={(date) => date.toLocaleDateString()}
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
parseDate={(str) => new Date(str)}
|
parseDate={(str) => new Date(str)}
|
||||||
inputProps={{ fill: true }}
|
inputProps={{ fill: true, leftElement: <Icon icon={'date-range'} /> }}
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
FFormGroup,
|
FFormGroup,
|
||||||
FInputGroup,
|
FInputGroup,
|
||||||
FTextArea,
|
FTextArea,
|
||||||
|
Icon,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useCategorizeTransactionBoot } from '../CategorizeTransactionBoot';
|
import { useCategorizeTransactionBoot } from '../CategorizeTransactionBoot';
|
||||||
import { CategorizeTransactionBranchField } from '../CategorizeTransactionBranchField';
|
import { CategorizeTransactionBranchField } from '../CategorizeTransactionBranchField';
|
||||||
@@ -21,7 +22,7 @@ export default function CategorizeTransactionTransferFrom() {
|
|||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
formatDate={(date) => date.toLocaleDateString()}
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
parseDate={(str) => new Date(str)}
|
parseDate={(str) => new Date(str)}
|
||||||
inputProps={{ fill: true }}
|
inputProps={{ fill: true, leftElement: <Icon icon={'date-range'} /> }}
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
FFormGroup,
|
FFormGroup,
|
||||||
FInputGroup,
|
FInputGroup,
|
||||||
FTextArea,
|
FTextArea,
|
||||||
|
Icon,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useCategorizeTransactionBoot } from '../CategorizeTransactionBoot';
|
import { useCategorizeTransactionBoot } from '../CategorizeTransactionBoot';
|
||||||
import { CategorizeTransactionBranchField } from '../CategorizeTransactionBranchField';
|
import { CategorizeTransactionBranchField } from '../CategorizeTransactionBranchField';
|
||||||
@@ -21,7 +22,7 @@ export default function CategorizeTransactionOtherExpense() {
|
|||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
formatDate={(date) => date.toLocaleDateString()}
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
parseDate={(str) => new Date(str)}
|
parseDate={(str) => new Date(str)}
|
||||||
inputProps={{ fill: true }}
|
inputProps={{ fill: true, leftElement: <Icon icon={'date-range'} /> }}
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
FFormGroup,
|
FFormGroup,
|
||||||
FInputGroup,
|
FInputGroup,
|
||||||
FTextArea,
|
FTextArea,
|
||||||
|
Icon,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useCategorizeTransactionBoot } from '../CategorizeTransactionBoot';
|
import { useCategorizeTransactionBoot } from '../CategorizeTransactionBoot';
|
||||||
import { CategorizeTransactionBranchField } from '../CategorizeTransactionBranchField';
|
import { CategorizeTransactionBranchField } from '../CategorizeTransactionBranchField';
|
||||||
@@ -21,7 +22,7 @@ export default function CategorizeTransactionOwnerDrawings() {
|
|||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
formatDate={(date) => date.toLocaleDateString()}
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
parseDate={(str) => new Date(str)}
|
parseDate={(str) => new Date(str)}
|
||||||
inputProps={{ fill: true }}
|
inputProps={{ fill: true, leftElement: <Icon icon={'date-range'} /> }}
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
FFormGroup,
|
FFormGroup,
|
||||||
FInputGroup,
|
FInputGroup,
|
||||||
FTextArea,
|
FTextArea,
|
||||||
|
Icon,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useCategorizeTransactionBoot } from '../CategorizeTransactionBoot';
|
import { useCategorizeTransactionBoot } from '../CategorizeTransactionBoot';
|
||||||
import { CategorizeTransactionBranchField } from '../CategorizeTransactionBranchField';
|
import { CategorizeTransactionBranchField } from '../CategorizeTransactionBranchField';
|
||||||
@@ -21,7 +22,7 @@ export default function CategorizeTransactionToAccount() {
|
|||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
formatDate={(date) => date.toLocaleDateString()}
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
parseDate={(str) => new Date(str)}
|
parseDate={(str) => new Date(str)}
|
||||||
inputProps={{ fill: true }}
|
inputProps={{ fill: true, leftElement: <Icon icon={'date-range'} /> }}
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export const MatchingReconcileFormSchema = Yup.object().shape({
|
|||||||
type: Yup.string().required().label('Type'),
|
type: Yup.string().required().label('Type'),
|
||||||
date: Yup.string().required().label('Date'),
|
date: Yup.string().required().label('Date'),
|
||||||
amount: Yup.string().required().label('Amount'),
|
amount: Yup.string().required().label('Amount'),
|
||||||
memo: Yup.string().required().label('Memo'),
|
memo: Yup.string().required().min(3).label('Memo'),
|
||||||
referenceNo: Yup.string().label('Refernece #'),
|
referenceNo: Yup.string().label('Refernece #'),
|
||||||
category: Yup.string().required().label('Categogry'),
|
category: Yup.string().required().label('Categogry'),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { Button, Intent, Position, Tag } from '@blueprintjs/core';
|
import { Button, Intent, Position, Tag } from '@blueprintjs/core';
|
||||||
import {
|
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
||||||
Form,
|
|
||||||
Formik,
|
|
||||||
FormikHelpers,
|
|
||||||
FormikValues,
|
|
||||||
useFormikContext,
|
|
||||||
} from 'formik';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import { round } from 'lodash';
|
||||||
import {
|
import {
|
||||||
AccountsSelect,
|
AccountsSelect,
|
||||||
AppToaster,
|
AppToaster,
|
||||||
@@ -19,6 +14,7 @@ import {
|
|||||||
FInputGroup,
|
FInputGroup,
|
||||||
FMoneyInputGroup,
|
FMoneyInputGroup,
|
||||||
Group,
|
Group,
|
||||||
|
Icon,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { Aside } from '@/components/Aside/Aside';
|
import { Aside } from '@/components/Aside/Aside';
|
||||||
import { momentFormatter } from '@/utils';
|
import { momentFormatter } from '@/utils';
|
||||||
@@ -100,7 +96,7 @@ function MatchingReconcileTransactionFormRoot({
|
|||||||
|
|
||||||
const _initialValues = {
|
const _initialValues = {
|
||||||
...initialValues,
|
...initialValues,
|
||||||
amount: Math.abs(reconcileMatchingTransactionPendingAmount) || 0,
|
amount: round(Math.abs(reconcileMatchingTransactionPendingAmount), 2) || 0,
|
||||||
date: moment().format('YYYY-MM-DD'),
|
date: moment().format('YYYY-MM-DD'),
|
||||||
type:
|
type:
|
||||||
reconcileMatchingTransactionPendingAmount > 0 ? 'deposit' : 'withdrawal',
|
reconcileMatchingTransactionPendingAmount > 0 ? 'deposit' : 'withdrawal',
|
||||||
@@ -179,7 +175,7 @@ function CreateReconcileTransactionContent() {
|
|||||||
},
|
},
|
||||||
boundary: 'viewport',
|
boundary: 'viewport',
|
||||||
}}
|
}}
|
||||||
inputProps={{ fill: true }}
|
inputProps={{ fill: true, leftElement: <Icon icon={'date-range'} /> }}
|
||||||
fill
|
fill
|
||||||
fastField
|
fastField
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -235,6 +235,12 @@ export function useExcludeUncategorizedTransaction(
|
|||||||
queryClient.invalidateQueries(
|
queryClient.invalidateQueries(
|
||||||
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
||||||
);
|
);
|
||||||
|
// Invalidate accounts.
|
||||||
|
queryClient.invalidateQueries(t.ACCOUNTS);
|
||||||
|
queryClient.invalidateQueries(t.ACCOUNT);
|
||||||
|
|
||||||
|
// invalidate bank account summary.
|
||||||
|
queryClient.invalidateQueries(QUERY_KEY.BANK_ACCOUNT_SUMMARY_META);
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
},
|
},
|
||||||
@@ -282,6 +288,12 @@ export function useUnexcludeUncategorizedTransaction(
|
|||||||
queryClient.invalidateQueries(
|
queryClient.invalidateQueries(
|
||||||
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
||||||
);
|
);
|
||||||
|
// Invalidate accounts.
|
||||||
|
queryClient.invalidateQueries(t.ACCOUNTS);
|
||||||
|
queryClient.invalidateQueries(t.ACCOUNT);
|
||||||
|
|
||||||
|
// Invalidate bank account summary.
|
||||||
|
queryClient.invalidateQueries(QUERY_KEY.BANK_ACCOUNT_SUMMARY_META);
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
},
|
},
|
||||||
@@ -323,6 +335,13 @@ export function useMatchUncategorizedTransaction(
|
|||||||
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
||||||
);
|
);
|
||||||
queryClient.invalidateQueries(t.CASHFLOW_ACCOUNT_TRANSACTIONS_INFINITY);
|
queryClient.invalidateQueries(t.CASHFLOW_ACCOUNT_TRANSACTIONS_INFINITY);
|
||||||
|
|
||||||
|
// Invalidate accounts.
|
||||||
|
queryClient.invalidateQueries(t.ACCOUNTS);
|
||||||
|
queryClient.invalidateQueries(t.ACCOUNT);
|
||||||
|
|
||||||
|
// Invalidate bank account summary.
|
||||||
|
queryClient.invalidateQueries(QUERY_KEY.BANK_ACCOUNT_SUMMARY_META);
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
});
|
});
|
||||||
@@ -362,6 +381,13 @@ export function useUnmatchMatchedUncategorizedTransaction(
|
|||||||
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
||||||
);
|
);
|
||||||
queryClient.invalidateQueries(t.CASHFLOW_ACCOUNT_TRANSACTIONS_INFINITY);
|
queryClient.invalidateQueries(t.CASHFLOW_ACCOUNT_TRANSACTIONS_INFINITY);
|
||||||
|
|
||||||
|
// Invalidate accounts.
|
||||||
|
queryClient.invalidateQueries(t.ACCOUNTS);
|
||||||
|
queryClient.invalidateQueries(t.ACCOUNT);
|
||||||
|
|
||||||
|
// Invalidate bank account summary.
|
||||||
|
queryClient.invalidateQueries(QUERY_KEY.BANK_ACCOUNT_SUMMARY_META);
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -253,6 +253,9 @@ export function useCategorizeTransaction(props) {
|
|||||||
queryClient.invalidateQueries(
|
queryClient.invalidateQueries(
|
||||||
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Invalidate bank account summary.
|
||||||
|
queryClient.invalidateQueries('BANK_ACCOUNT_SUMMARY_META');
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
@@ -276,6 +279,9 @@ export function useUncategorizeTransaction(props) {
|
|||||||
queryClient.invalidateQueries(
|
queryClient.invalidateQueries(
|
||||||
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Invalidate bank account summary.
|
||||||
|
queryClient.invalidateQueries('BANK_ACCOUNT_SUMMARY_META');
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user