feat: uncategorize the cashflow transaction

This commit is contained in:
Ahmed Bouhuolia
2024-03-10 02:53:57 +02:00
parent 2baf407814
commit b71c79fef5
16 changed files with 246 additions and 3 deletions

View File

@@ -93,6 +93,19 @@ export default class DeleteCashflowTransactionController extends BaseController
],
});
}
if (
error.errorType ===
'CANNOT_DELETE_TRANSACTION_CONVERTED_FROM_UNCATEGORIZED'
) {
return res.boom.badRequest(null, {
errors: [
{
type: 'CANNOT_DELETE_TRANSACTION_CONVERTED_FROM_UNCATEGORIZED',
code: 4100,
},
],
});
}
}
next(error);
}

View File

@@ -0,0 +1,15 @@
exports.up = function (knex) {
return knex.schema.table('cashflow_transactions', (table) => {
table
.integer('uncategorized_transaction_id')
.unsigned()
.references('id')
.inTable('uncategorized_cashflow_transactions');
});
};
exports.down = function (knex) {
return knex.schema.table('cashflow_transactions', (table) => {
table.dropColumn('uncategorized_transaction_id');
});
};

View File

@@ -51,6 +51,7 @@ export interface ICashflowCommandDTO {
export interface ICashflowNewCommandDTO extends ICashflowCommandDTO {
plaidAccountId?: string;
uncategorizedTransactionId?: number;
}
export interface ICashflowTransaction {
@@ -83,6 +84,8 @@ export interface ICashflowTransaction {
isCashDebit?: boolean;
isCashCredit?: boolean;
uncategorizedTransactionId?: number;
}
export interface ICashflowTransactionLine {

View File

@@ -89,6 +89,7 @@ import { InvoiceChangeStatusOnMailSentSubscriber } from '@/services/Sales/Invoic
import { SaleReceiptMarkClosedOnMailSentSubcriber } from '@/services/Sales/Receipts/subscribers/SaleReceiptMarkClosedOnMailSentSubcriber';
import { SaleEstimateMarkApprovedOnMailSent } from '@/services/Sales/Estimates/subscribers/SaleEstimateMarkApprovedOnMailSent';
import { DeleteCashflowTransactionOnUncategorize } from '@/services/Cashflow/subscribers/DeleteCashflowTransactionOnUncategorize';
import { PreventDeleteTransactionOnDelete } from '@/services/Cashflow/subscribers/PreventDeleteTransactionsOnDelete'; }
export default () => {
return new EventPublisher();
@@ -217,5 +218,6 @@ export const susbcribers = () => {
// Cashflow
DeleteCashflowTransactionOnUncategorize,
PreventDeleteTransactionOnDelete
];
};

View File

@@ -13,6 +13,7 @@ export default class CashflowTransaction extends TenantModel {
amount: number;
exchangeRate: number;
uncategorize: boolean;
uncategorizedTransaction!: boolean;
/**
* Table name.
@@ -86,6 +87,14 @@ export default class CashflowTransaction extends TenantModel {
return this.typeMeta?.direction === CASHFLOW_DIRECTION.IN;
}
/**
* Detarmines whether the transaction imported from uncategorized transaction.
* @returns {boolean}
*/
get isCategroizedTranasction() {
return !!this.uncategorizedTransaction;
}
/**
* Relationship mapping.
*/

View File

@@ -86,6 +86,7 @@ export default class NewCashflowTransactionService {
'creditAccountId',
'branchId',
'plaidTransactionId',
'uncategorizedTransactionId',
]);
// Retreive the next invoice number.
const autoNextNumber =

View File

@@ -11,7 +11,8 @@ export const ERRORS = {
ACCOUNT_HAS_ASSOCIATED_TRANSACTIONS: 'account_has_associated_transactions',
TRANSACTION_ALREADY_CATEGORIZED: 'TRANSACTION_ALREADY_CATEGORIZED',
TRANSACTION_ALREADY_UNCATEGORIZED: 'TRANSACTION_ALREADY_UNCATEGORIZED',
UNCATEGORIZED_TRANSACTION_TYPE_INVALID: 'UNCATEGORIZED_TRANSACTION_TYPE_INVALID'
UNCATEGORIZED_TRANSACTION_TYPE_INVALID: 'UNCATEGORIZED_TRANSACTION_TYPE_INVALID',
CANNOT_DELETE_TRANSACTION_CONVERTED_FROM_UNCATEGORIZED: 'CANNOT_DELETE_TRANSACTION_CONVERTED_FROM_UNCATEGORIZED'
};
export enum CASHFLOW_DIRECTION {

View File

@@ -2,12 +2,16 @@ import { Inject, Service } from 'typedi';
import events from '@/subscribers/events';
import { ICashflowTransactionUncategorizedPayload } from '@/interfaces';
import { DeleteCashflowTransaction } from '../DeleteCashflowTransactionService';
import HasTenancyService from '@/services/Tenancy/TenancyService';
@Service()
export class DeleteCashflowTransactionOnUncategorize {
@Inject()
private deleteCashflowTransactionService: DeleteCashflowTransaction;
@Inject()
private tenancy: HasTenancyService;
/**
* Attaches events with handlers.
*/
@@ -27,10 +31,18 @@ export class DeleteCashflowTransactionOnUncategorize {
oldUncategorizedTransaction,
trx,
}: ICashflowTransactionUncategorizedPayload) {
const { CashflowTransaction } = this.tenancy.models(tenantId);
// Deletes the cashflow transaction.
if (
oldUncategorizedTransaction.categorizeRefType === 'CashflowTransaction'
) {
await CashflowTransaction.query()
.findById(oldUncategorizedTransaction.categorizeRefId)
.patch({
uncategorizedTransactionId: null,
});
await this.deleteCashflowTransactionService.deleteCashflowTransaction(
tenantId,
oldUncategorizedTransaction.categorizeRefId

View File

@@ -0,0 +1,37 @@
import { Service } from 'typedi';
import events from '@/subscribers/events';
import { ICommandCashflowDeletingPayload } from '@/interfaces';
import { ServiceError } from '@/exceptions';
import { ERRORS } from '../constants';
@Service()
export class PreventDeleteTransactionOnDelete {
/**
* Attaches events with handlers.
*/
public attach = (bus) => {
bus.subscribe(
events.cashflow.onTransactionDeleting,
this.preventDeleteCashflowTransactionHasUncategorizedTransaction.bind(
this
)
);
};
/**
* Prevent delete cashflow transaction has converted from uncategorized transaction.
* @param {ICommandCashflowDeletingPayload} payload
*/
public async preventDeleteCashflowTransactionHasUncategorizedTransaction({
tenantId,
oldCashflowTransaction,
trx,
}: ICommandCashflowDeletingPayload) {
if (oldCashflowTransaction.uncategorizedTransactionId) {
throw new ServiceError(
ERRORS.CANNOT_DELETE_TRANSACTION_CONVERTED_FROM_UNCATEGORIZED,
'Cannot delete cashflow transaction converted from uncategorized transaction.'
);
}
}
}

View File

@@ -61,6 +61,7 @@ export const transformCategorizeTransToCashflow = (
amount: uncategorizeModel.amount,
transactionNumber: categorizeDTO.transactionNumber,
transactionType: categorizeDTO.transactionType,
uncategorizedTransactionId: uncategorizeModel.id,
publish: true,
};
};