refactor: bank rules e2e test cases

This commit is contained in:
Ahmed Bouhuolia
2025-01-06 18:10:24 +02:00
parent 2bf58d9cb4
commit 385d84d654
27 changed files with 357 additions and 91 deletions

View File

@@ -1,5 +1,4 @@
import { QueryBuilder } from 'objection'; import { QueryBuilder, Model } from 'objection';
import Objection, { Model, Page } from 'objection';
interface PaginationResult<M extends Model> { interface PaginationResult<M extends Model> {
results: M[]; results: M[];
@@ -10,13 +9,19 @@ interface PaginationResult<M extends Model> {
}; };
} }
class PaginationQueryBuilder< export type PaginationQueryBuilderType<M extends Model> = QueryBuilder<
M extends Model, M,
R = M[], PaginationResult<M>
> extends Model.QueryBuilder<M, R> { >;
pagination(page: number, pageSize: number): QueryBuilder<M, PaginationResult<M>> {
// @ts-ignore class PaginationQueryBuilder<M extends Model, R = M[]> extends QueryBuilder<
return super.page(page, pageSize).runAfter(({ results, total }) => { M,
R
> {
pagination(page: number, pageSize: number): PaginationQueryBuilderType<M> {
const query = super.page(page, pageSize);
return query.runAfter(({ results, total }) => {
return { return {
results, results,
pagination: { pagination: {
@@ -25,7 +30,7 @@ class PaginationQueryBuilder<
pageSize, pageSize,
}, },
}; };
}); }) as unknown as PaginationQueryBuilderType<M>;
} }
} }

View File

@@ -217,7 +217,7 @@ export class Account extends TenantModel {
// const ExpenseEntry = require('models/ExpenseCategory'); // const ExpenseEntry = require('models/ExpenseCategory');
// const ItemEntry = require('models/ItemEntry'); // const ItemEntry = require('models/ItemEntry');
// const UncategorizedTransaction = require('models/UncategorizedCashflowTransaction'); // const UncategorizedTransaction = require('models/UncategorizedCashflowTransaction');
const { PlaidItem } = require('../../Banking/models/PlaidItem.model'); const { PlaidItem } = require('../../BankingPlaid/models/PlaidItem');
return { return {
/** /**

View File

@@ -140,10 +140,10 @@ import { BankingTransactionsModule } from '../BankingTransactions/BankingTransac
LedgerModule, LedgerModule,
BankAccountsModule, BankAccountsModule,
BankRulesModule, BankRulesModule,
BankingTransactionsExcludeModule,
BankingTransactionsRegonizeModule,
BankingMatchingModule,
BankingTransactionsModule, BankingTransactionsModule,
// BankingTransactionsExcludeModule,
// BankingTransactionsRegonizeModule,
// BankingMatchingModule,
], ],
controllers: [AppController], controllers: [AppController],
providers: [ providers: [

View File

@@ -9,15 +9,18 @@ import {
} from '@nestjs/common'; } from '@nestjs/common';
import { BankRulesApplication } from './BankRulesApplication'; import { BankRulesApplication } from './BankRulesApplication';
import { ICreateBankRuleDTO, IEditBankRuleDTO } from './types'; import { ICreateBankRuleDTO, IEditBankRuleDTO } from './types';
import { PublicRoute } from '../Auth/Jwt.guard';
import { BankRule } from './models/BankRule';
@Controller('banking/rules') @Controller('banking/rules')
@PublicRoute()
export class BankRulesController { export class BankRulesController {
constructor(private readonly bankRulesApplication: BankRulesApplication) {} constructor(private readonly bankRulesApplication: BankRulesApplication) {}
@Post() @Post()
async createBankRule( async createBankRule(
@Body() createRuleDTO: ICreateBankRuleDTO, @Body() createRuleDTO: ICreateBankRuleDTO,
): Promise<void> { ): Promise<BankRule> {
return this.bankRulesApplication.createBankRule(createRuleDTO); return this.bankRulesApplication.createBankRule(createRuleDTO);
} }

View File

@@ -5,6 +5,7 @@ import { EditBankRuleService } from './commands/EditBankRule.service';
import { GetBankRuleService } from './queries/GetBankRule.service'; import { GetBankRuleService } from './queries/GetBankRule.service';
import { GetBankRulesService } from './queries/GetBankRules.service'; import { GetBankRulesService } from './queries/GetBankRules.service';
import { ICreateBankRuleDTO, IEditBankRuleDTO } from './types'; import { ICreateBankRuleDTO, IEditBankRuleDTO } from './types';
import { BankRule } from './models/BankRule';
@Injectable() @Injectable()
export class BankRulesApplication { export class BankRulesApplication {
@@ -21,7 +22,9 @@ export class BankRulesApplication {
* @param {ICreateBankRuleDTO} createRuleDTO - Bank rule data. * @param {ICreateBankRuleDTO} createRuleDTO - Bank rule data.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public createBankRule(createRuleDTO: ICreateBankRuleDTO): Promise<void> { public createBankRule(
createRuleDTO: ICreateBankRuleDTO,
): Promise<BankRule> {
return this.createBankRuleService.createBankRule(createRuleDTO); return this.createBankRuleService.createBankRule(createRuleDTO);
} }

View File

@@ -22,9 +22,8 @@ export class CreateBankRuleService {
/** /**
* Transforms the DTO to model. * Transforms the DTO to model.
* @param {ICreateBankRuleDTO} createDTO * @param {ICreateBankRuleDTO} createDTO
* @returns
*/ */
private transformDTO(createDTO: ICreateBankRuleDTO): Partial<BankRule> { private transformDTO(createDTO: ICreateBankRuleDTO) {
return { return {
...createDTO, ...createDTO,
}; };
@@ -33,19 +32,21 @@ export class CreateBankRuleService {
/** /**
* Creates a new bank rule. * Creates a new bank rule.
* @param {ICreateBankRuleDTO} createRuleDTO * @param {ICreateBankRuleDTO} createRuleDTO
* @returns {Promise<void>} * @returns {Promise<BankRule>}
*/ */
public async createBankRule(createRuleDTO: ICreateBankRuleDTO): Promise<void> { public async createBankRule(
createRuleDTO: ICreateBankRuleDTO,
): Promise<BankRule> {
const transformDTO = this.transformDTO(createRuleDTO); const transformDTO = this.transformDTO(createRuleDTO);
await this.uow.withTransaction(async (trx: Knex.Transaction) => { return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onBankRuleCreating` event. // Triggers `onBankRuleCreating` event.
await this.eventPublisher.emitAsync(events.bankRules.onCreating, { await this.eventPublisher.emitAsync(events.bankRules.onCreating, {
createRuleDTO, createRuleDTO,
trx, trx,
} as IBankRuleEventCreatingPayload); } as IBankRuleEventCreatingPayload);
const bankRule = await this.bankRuleModel.query(trx).upsertGraph({ const bankRule = await this.bankRuleModel.query(trx).upsertGraphAndFetch({
...transformDTO, ...transformDTO,
}); });
// Triggers `onBankRuleCreated` event. // Triggers `onBankRuleCreated` event.

View File

@@ -20,7 +20,8 @@ export class GetBankRuleService {
const bankRule = await this.bankRuleModel const bankRule = await this.bankRuleModel
.query() .query()
.findById(ruleId) .findById(ruleId)
.withGraphFetched('conditions'); .withGraphFetched('conditions')
.withGraphFetched('assignAccount');
return this.transformer.transform( return this.transformer.transform(
bankRule, bankRule,

View File

@@ -80,10 +80,9 @@ export class PlaidSyncDb {
item: PlaidItem, item: PlaidItem,
trx?: Knex.Transaction, trx?: Knex.Transaction,
): Promise<void> { ): Promise<void> {
const transformToPlaidAccounts = transformPlaidAccountToCreateAccount( const transformToPlaidAccounts = R.curry(
item, transformPlaidAccountToCreateAccount,
institution, )(item, institution);
);
const accountCreateDTOs = R.map(transformToPlaidAccounts)(plaidAccounts); const accountCreateDTOs = R.map(transformToPlaidAccounts)(plaidAccounts);
await bluebird.map( await bluebird.map(
@@ -112,7 +111,7 @@ export class PlaidSyncDb {
.throwIfNotFound(); .throwIfNotFound();
// Transformes the Plaid transactions to cashflow create DTOs. // Transformes the Plaid transactions to cashflow create DTOs.
const transformTransaction = transformPlaidTrxsToCashflowCreate( const transformTransaction = R.curry(transformPlaidTrxsToCashflowCreate)(
cashflowAccount.id, cashflowAccount.id,
); );
const uncategorizedTransDTOs = const uncategorizedTransDTOs =

View File

@@ -16,7 +16,7 @@ import { CreateUncategorizedTransactionDTO } from '../BankingCategorize/types/Ba
* @returns {string} * @returns {string}
*/ */
const getAccountTypeFromPlaidAccountType = ( const getAccountTypeFromPlaidAccountType = (
plaidAccountType: PlaidAccountType plaidAccountType: PlaidAccountType,
) => { ) => {
if (plaidAccountType === PlaidAccountType.Credit) { if (plaidAccountType === PlaidAccountType.Credit) {
return ACCOUNT_TYPE.CREDIT_CARD; return ACCOUNT_TYPE.CREDIT_CARD;
@@ -31,12 +31,11 @@ const getAccountTypeFromPlaidAccountType = (
* @param {PlaidAccount} plaidAccount - Plaid account. * @param {PlaidAccount} plaidAccount - Plaid account.
* @returns {IAccountCreateDTO} * @returns {IAccountCreateDTO}
*/ */
export const transformPlaidAccountToCreateAccount = R.curry( export const transformPlaidAccountToCreateAccount = (
(
item: PlaidItem, item: PlaidItem,
institution: PlaidInstitution, institution: PlaidInstitution,
plaidAccount: PlaidAccount plaidAccount: PlaidAccount,
): IAccountCreateDTO => { ): IAccountCreateDTO => {
return { return {
name: `${institution.name} - ${plaidAccount.name}`, name: `${institution.name} - ${plaidAccount.name}`,
code: '', code: '',
@@ -49,8 +48,7 @@ export const transformPlaidAccountToCreateAccount = R.curry(
plaidAccountId: plaidAccount.account_id, plaidAccountId: plaidAccount.account_id,
plaidItemId: item.item_id, plaidItemId: item.item_id,
}; };
} };
);
/** /**
* Transformes the plaid transaction to cashflow create DTO. * Transformes the plaid transaction to cashflow create DTO.
@@ -59,10 +57,9 @@ export const transformPlaidAccountToCreateAccount = R.curry(
* @param {PlaidTransaction} plaidTranasction - Plaid transaction. * @param {PlaidTransaction} plaidTranasction - Plaid transaction.
* @returns {CreateUncategorizedTransactionDTO} * @returns {CreateUncategorizedTransactionDTO}
*/ */
export const transformPlaidTrxsToCashflowCreate = R.curry( export const transformPlaidTrxsToCashflowCreate = (
(
cashflowAccountId: number, cashflowAccountId: number,
plaidTranasction: PlaidTransactionBase plaidTranasction: PlaidTransactionBase,
): CreateUncategorizedTransactionDTO => { ): CreateUncategorizedTransactionDTO => {
return { return {
date: plaidTranasction.date, date: plaidTranasction.date,
@@ -81,5 +78,4 @@ export const transformPlaidTrxsToCashflowCreate = R.curry(
pending: plaidTranasction.pending, pending: plaidTranasction.pending,
pendingPlaidTransactionId: plaidTranasction.pending_transaction_id, pendingPlaidTransactionId: plaidTranasction.pending_transaction_id,
}; };
} };
);

View File

@@ -34,9 +34,11 @@ export class RecognizedBankTransaction extends BaseModel {
* Relationship mapping. * Relationship mapping.
*/ */
static get relationMappings() { static get relationMappings() {
const UncategorizedCashflowTransaction = require('./UncategorizedCashflowTransaction'); const {
const Account = require('./Account'); UncategorizedBankTransaction,
const { BankRule } = require('./BankRule'); } = require('../../BankingTransactions/models/UncategorizedBankTransaction');
const { Account } = require('../../Accounts/models/Account.model');
const { BankRule } = require('../../BankRules/models/BankRule');
return { return {
/** /**
@@ -44,7 +46,7 @@ export class RecognizedBankTransaction extends BaseModel {
*/ */
uncategorizedTransactions: { uncategorizedTransactions: {
relation: Model.HasManyRelation, relation: Model.HasManyRelation,
modelClass: UncategorizedCashflowTransaction.default, modelClass: UncategorizedBankTransaction,
join: { join: {
from: 'recognized_bank_transactions.uncategorizedTransactionId', from: 'recognized_bank_transactions.uncategorizedTransactionId',
to: 'uncategorized_cashflow_transactions.id', to: 'uncategorized_cashflow_transactions.id',
@@ -56,7 +58,7 @@ export class RecognizedBankTransaction extends BaseModel {
*/ */
assignAccount: { assignAccount: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: Account.default, modelClass: Account,
join: { join: {
from: 'recognized_bank_transactions.assignedAccountId', from: 'recognized_bank_transactions.assignedAccountId',
to: 'accounts.id', to: 'accounts.id',

View File

@@ -1,8 +1,10 @@
import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common'; import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
import { BankingTransactionsApplication } from './BankingTransactionsApplication.service'; import { BankingTransactionsApplication } from './BankingTransactionsApplication.service';
import { ICashflowNewCommandDTO } from './types/BankingTransactions.types'; import { ICashflowNewCommandDTO } from './types/BankingTransactions.types';
import { PublicRoute } from '../Auth/Jwt.guard';
@Controller('banking/transactions') @Controller('banking/transactions')
@PublicRoute()
export class BankingTransactionsController { export class BankingTransactionsController {
constructor( constructor(
private readonly bankingTransactionsApplication: BankingTransactionsApplication, private readonly bankingTransactionsApplication: BankingTransactionsApplication,

View File

@@ -7,7 +7,10 @@ import { Model } from 'objection';
// import { CASHFLOW_DIRECTION } from '@/services/Cashflow/constants'; // import { CASHFLOW_DIRECTION } from '@/services/Cashflow/constants';
// import { getCashflowTransactionFormattedType } from '@/utils/transactions-types'; // import { getCashflowTransactionFormattedType } from '@/utils/transactions-types';
import { BaseModel } from '@/models/Model'; import { BaseModel } from '@/models/Model';
import { getCashflowAccountTransactionsTypes, getCashflowTransactionType } from '../utils'; import {
getCashflowAccountTransactionsTypes,
getCashflowTransactionType,
} from '../utils';
import { CASHFLOW_DIRECTION, CASHFLOW_TRANSACTION_TYPE } from '../constants'; import { CASHFLOW_DIRECTION, CASHFLOW_TRANSACTION_TYPE } from '../constants';
import { BankTransactionLine } from './BankTransactionLine'; import { BankTransactionLine } from './BankTransactionLine';
import { Account } from '@/modules/Accounts/models/Account.model'; import { Account } from '@/modules/Accounts/models/Account.model';
@@ -159,10 +162,14 @@ export class BankTransaction extends BaseModel {
* Relationship mapping. * Relationship mapping.
*/ */
static get relationMappings() { static get relationMappings() {
const CashflowTransactionLine = require('models/CashflowTransactionLine'); const { BankTransactionLine } = require('./BankTransactionLine');
const AccountTransaction = require('models/AccountTransaction'); const {
const Account = require('models/Account'); AccountTransaction,
const { MatchedBankTransaction } = require('models/MatchedBankTransaction'); } = require('../../Accounts/models/AccountTransaction.model');
const { Account } = require('../../Accounts/models/Account.model');
const {
MatchedBankTransaction,
} = require('../../BankingMatching/models/MatchedBankTransaction');
return { return {
/** /**
@@ -170,7 +177,7 @@ export class BankTransaction extends BaseModel {
*/ */
entries: { entries: {
relation: Model.HasManyRelation, relation: Model.HasManyRelation,
modelClass: CashflowTransactionLine.default, modelClass: BankTransactionLine,
join: { join: {
from: 'cashflow_transactions.id', from: 'cashflow_transactions.id',
to: 'cashflow_transaction_lines.cashflowTransactionId', to: 'cashflow_transaction_lines.cashflowTransactionId',
@@ -185,7 +192,7 @@ export class BankTransaction extends BaseModel {
*/ */
transactions: { transactions: {
relation: Model.HasManyRelation, relation: Model.HasManyRelation,
modelClass: AccountTransaction.default, modelClass: AccountTransaction,
join: { join: {
from: 'cashflow_transactions.id', from: 'cashflow_transactions.id',
to: 'accounts_transactions.referenceId', to: 'accounts_transactions.referenceId',
@@ -200,7 +207,7 @@ export class BankTransaction extends BaseModel {
*/ */
cashflowAccount: { cashflowAccount: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: Account.default, modelClass: Account,
join: { join: {
from: 'cashflow_transactions.cashflowAccountId', from: 'cashflow_transactions.cashflowAccountId',
to: 'accounts.id', to: 'accounts.id',
@@ -212,7 +219,7 @@ export class BankTransaction extends BaseModel {
*/ */
creditAccount: { creditAccount: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: Account.default, modelClass: Account,
join: { join: {
from: 'cashflow_transactions.creditAccountId', from: 'cashflow_transactions.creditAccountId',
to: 'accounts.id', to: 'accounts.id',

View File

@@ -22,12 +22,12 @@ export class BankTransactionLine extends BaseModel{
* Relationship mapping. * Relationship mapping.
*/ */
static get relationMappings() { static get relationMappings() {
const Account = require('models/Account'); const { Account } = require('../../Accounts/models/Account.model');
return { return {
cashflowAccount: { cashflowAccount: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: Account.default, modelClass: Account,
join: { join: {
from: 'cashflow_transaction_lines.cashflowAccountId', from: 'cashflow_transaction_lines.cashflowAccountId',
to: 'accounts.id', to: 'accounts.id',
@@ -35,7 +35,7 @@ export class BankTransactionLine extends BaseModel{
}, },
creditAccount: { creditAccount: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: Account.default, modelClass: Account,
join: { join: {
from: 'cashflow_transaction_lines.creditAccountId', from: 'cashflow_transaction_lines.creditAccountId',
to: 'accounts.id', to: 'accounts.id',

View File

@@ -195,11 +195,11 @@ export class UncategorizedBankTransaction extends BaseModel {
* Relationship mapping. * Relationship mapping.
*/ */
static get relationMappings() { static get relationMappings() {
const Account = require('models/Account'); const { Account } = require('../../Accounts/models/Account.model');
const { const {
RecognizedBankTransaction, RecognizedBankTransaction,
} = require('models/RecognizedBankTransaction'); } = require('../../BankingTranasctionsRegonize/models/RecognizedBankTransaction');
const { MatchedBankTransaction } = require('models/MatchedBankTransaction'); const { MatchedBankTransaction } = require('../../BankingMatching/models/MatchedBankTransaction');
return { return {
/** /**
@@ -207,7 +207,7 @@ export class UncategorizedBankTransaction extends BaseModel {
*/ */
account: { account: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: Account.default, modelClass: Account,
join: { join: {
from: 'uncategorized_cashflow_transactions.accountId', from: 'uncategorized_cashflow_transactions.accountId',
to: 'accounts.id', to: 'accounts.id',

View File

@@ -32,7 +32,7 @@ export class BankTransactionTransformer extends Transformer {
* @returns {string} * @returns {string}
*/ */
protected transactionTypeFormatted = (transaction) => { protected transactionTypeFormatted = (transaction) => {
return this.context.i18n.t(transaction.transactionTypeFormatted); return this.context.i18n.t(transaction.transactionType);
}; };
/** /**

View File

@@ -1,8 +1,8 @@
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service'; import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { initialize } from 'objection';
import { UncategorizedBankTransaction } from '../models/UncategorizedBankTransaction'; import { UncategorizedBankTransaction } from '../models/UncategorizedBankTransaction';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { UncategorizedTransactionTransformer } from '../../BankingCategorize/commands/UncategorizedTransaction.transformer'; import { UncategorizedTransactionTransformer } from '../../BankingCategorize/commands/UncategorizedTransaction.transformer';
import { IGetUncategorizedTransactionsQuery } from '../types/BankingTransactions.types';
@Injectable() @Injectable()
export class GetUncategorizedTransactions { export class GetUncategorizedTransactions {

View File

@@ -9,13 +9,13 @@ export interface IBillPaymentEntryDTO {
export interface IBillPaymentDTO { export interface IBillPaymentDTO {
vendorId: number; vendorId: number;
amount: number; amount?: number;
paymentAccountId: number; paymentAccountId: number;
paymentNumber?: string; paymentNumber?: string;
paymentDate: Date | string; paymentDate: Date | string;
exchangeRate?: number; exchangeRate?: number;
statement: string; statement?: string;
reference: string; reference?: string;
entries: IBillPaymentEntryDTO[]; entries: IBillPaymentEntryDTO[];
branchId?: number; branchId?: number;
attachments?: AttachmentLinkDTO[]; attachments?: AttachmentLinkDTO[];

View File

@@ -8,11 +8,11 @@ import * as R from 'ramda';
// import CustomViewBaseModel from './CustomViewBaseModel'; // import CustomViewBaseModel from './CustomViewBaseModel';
// import { DEFAULT_VIEWS } from '@/services/Purchases/Bills/constants'; // import { DEFAULT_VIEWS } from '@/services/Purchases/Bills/constants';
// import ModelSearchable from './ModelSearchable'; // import ModelSearchable from './ModelSearchable';
import { BaseModel } from '@/models/Model'; import { BaseModel, PaginationQueryBuilderType } from '@/models/Model';
import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry'; import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry';
import { BillLandedCost } from '@/modules/BillLandedCosts/models/BillLandedCost'; import { BillLandedCost } from '@/modules/BillLandedCosts/models/BillLandedCost';
import { DiscountType } from '@/common/types/Discount'; import { DiscountType } from '@/common/types/Discount';
import { Knex } from 'knex'; import type { Knex, QueryBuilder } from 'knex';
export class Bill extends BaseModel { export class Bill extends BaseModel {
public amount: number; public amount: number;
@@ -482,6 +482,7 @@ export class Bill extends BaseModel {
const { Branch } = require('../../Branches/models/Branch.model'); const { Branch } = require('../../Branches/models/Branch.model');
const { Warehouse } = require('../../Warehouses/models/Warehouse.model'); const { Warehouse } = require('../../Warehouses/models/Warehouse.model');
const { TaxRateModel } = require('../../TaxRates/models/TaxRate.model'); const { TaxRateModel } = require('../../TaxRates/models/TaxRate.model');
const { TaxRateTransaction } = require('../../TaxRates/models/TaxRateTransaction.model');
const { Document } = require('../../ChromiumlyTenancy/models/Document'); const { Document } = require('../../ChromiumlyTenancy/models/Document');
// const { MatchedBankTransaction } = require('models/MatchedBankTransaction'); // const { MatchedBankTransaction } = require('models/MatchedBankTransaction');
@@ -549,7 +550,7 @@ export class Bill extends BaseModel {
*/ */
taxes: { taxes: {
relation: Model.HasManyRelation, relation: Model.HasManyRelation,
modelClass: TaxRateModel, modelClass: TaxRateTransaction,
join: { join: {
from: 'bills.id', from: 'bills.id',
to: 'tax_rate_transactions.referenceId', to: 'tax_rate_transactions.referenceId',
@@ -616,8 +617,13 @@ export class Bill extends BaseModel {
return notFoundBillsIds; return notFoundBillsIds;
} }
static changePaymentAmount(billId, amount, trx: Knex.Transaction) { static changePaymentAmount(
billId: number,
amount: number,
trx: Knex.Transaction,
) {
const changeMethod = amount > 0 ? 'increment' : 'decrement'; const changeMethod = amount > 0 ? 'increment' : 'decrement';
return this.query(trx) return this.query(trx)
.where('id', billId) .where('id', billId)
[changeMethod]('payment_amount', Math.abs(amount)); [changeMethod]('payment_amount', Math.abs(amount));

View File

@@ -1,4 +1,5 @@
import { IModelMetaField2 } from "@/interfaces/Model"; import { IModelMetaField2 } from "@/interfaces/Model";
import { Import } from "./models/Import";
export interface ImportMappingAttr { export interface ImportMappingAttr {
from: string; from: string;

View File

@@ -0,0 +1,87 @@
import { Model, ModelObject } from 'objection';
// import SystemModel from './SystemModel';
import { BaseModel } from '@/models/Model';
export class Import extends BaseModel {
resource: string;
tenantId: number;
mapping!: string;
columns!: string;
params!: string;
/**
* Table name.
*/
static get tableName() {
return 'imports';
}
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return ['mappingParsed'];
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Detarmines whether the import is mapped.
* @returns {boolean}
*/
public get isMapped() {
return Boolean(this.mapping);
}
public get columnsParsed() {
try {
return JSON.parse(this.columns);
} catch {
return [];
}
}
public get paramsParsed() {
try {
return JSON.parse(this.params);
} catch {
return [];
}
}
public get mappingParsed() {
try {
return JSON.parse(this.mapping);
} catch {
return [];
}
}
/**
* Relationship mapping.
*/
static get relationMappings() {
const Tenant = require('system/models/Tenant');
return {
/**
* System user may belongs to tenant model.
*/
tenant: {
relation: Model.BelongsToOneRelation,
modelClass: Tenant.default,
join: {
from: 'imports.tenantId',
to: 'tenants.id',
},
},
};
}
}
export type ImportShape = ModelObject<Import>;

View File

@@ -213,7 +213,7 @@ export class ItemsEntriesService {
/** /**
* Sets the cost/sell accounts to the invoice entries. * Sets the cost/sell accounts to the invoice entries.
*/ */
public setItemsEntriesDefaultAccounts = async (entries: ItemEntry[]) => { public setItemsEntriesDefaultAccounts = async (entries: IItemEntryDTO[]) => {
const entriesItemsIds = entries.map((e) => e.itemId); const entriesItemsIds = entries.map((e) => e.itemId);
const items = await this.itemModel.query().whereIn('id', entriesItemsIds); const items = await this.itemModel.query().whereIn('id', entriesItemsIds);

View File

@@ -5,12 +5,12 @@ import { PaymentReceived } from '../models/PaymentReceived';
export interface IPaymentReceivedCreateDTO { export interface IPaymentReceivedCreateDTO {
customerId: number; customerId: number;
paymentDate: Date | string; paymentDate: Date | string;
amount: number; amount?: number;
exchangeRate: number; exchangeRate?: number;
referenceNo: string; referenceNo?: string;
depositAccountId: number; depositAccountId: number;
paymentReceiveNo?: string; paymentReceiveNo?: string;
statement: string; statement?: string;
entries: IPaymentReceivedEntryDTO[]; entries: IPaymentReceivedEntryDTO[];
branchId?: number; branchId?: number;

View File

@@ -7,6 +7,9 @@ export interface IItemEntryDTO {
landedCost?: boolean; landedCost?: boolean;
warehouseId?: number; warehouseId?: number;
sellAccountId?: number;
costAccountId?: number;
projectRefId?: number; projectRefId?: number;
projectRefType?: ProjectLinkRefType; projectRefType?: ProjectLinkRefType;
projectRefInvoicedAmount?: number; projectRefInvoicedAmount?: number;

View File

@@ -1,5 +1,5 @@
import { groupBy } from 'lodash'; import { groupBy } from 'lodash';
export const transformToMapBy = (collection, key) => { export const transformToMapBy = <T>(collection: T[], key: keyof T): Map<string, T[]> => {
return new Map(Object.entries(groupBy(collection, key))); return new Map(Object.entries(groupBy(collection, key)));
}; };

View File

@@ -0,0 +1,88 @@
import * as request from 'supertest';
import { faker } from '@faker-js/faker';
import { app } from './init-app-test';
const requestBankRule = () => ({
name: faker.company.name(),
order: 1,
applyIfAccountId: 1001,
applyIfTransactionType: 'deposit',
conditions: [
{
field: 'description',
comparator: 'contains',
value: faker.finance.transactionDescription(),
},
],
assignCategory: 'Deposit',
assignAccountId: 1002,
assignPayee: faker.company.name(),
assignMemo: faker.lorem.sentence(),
});
describe('Bank Rules (e2e)', () => {
it('/banking/rules (POST)', () => {
return request(app.getHttpServer())
.post('/banking/rules')
.set('organization-id', '4064541lv40nhca')
.send(requestBankRule())
.expect(201);
});
it('/banking/rules/:id (PUT)', async () => {
const response = await request(app.getHttpServer())
.post('/banking/rules')
.set('organization-id', '4064541lv40nhca')
.send(requestBankRule());
const ruleId = response.body.id;
return request(app.getHttpServer())
.put(`/banking/rules/${ruleId}`)
.set('organization-id', '4064541lv40nhca')
.send(requestBankRule())
.expect(200);
});
it('/banking/rules/:id (DELETE)', async () => {
const response = await request(app.getHttpServer())
.post('/banking/rules')
.set('organization-id', '4064541lv40nhca')
.send(requestBankRule());
const ruleId = response.body.id;
return request(app.getHttpServer())
.delete(`/banking/rules/${ruleId}`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
it('/banking/rules/:id (GET)', async () => {
const response = await request(app.getHttpServer())
.post('/banking/rules')
.set('organization-id', '4064541lv40nhca')
.send(requestBankRule());
const ruleId = response.body.id;
return request(app.getHttpServer())
.get(`/banking/rules/${ruleId}`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
it('/banking/rules (GET)', async () => {
const response = await request(app.getHttpServer())
.post('/banking/rules')
.set('organization-id', '4064541lv40nhca')
.send(requestBankRule());
const ruleId = response.body.id;
return request(app.getHttpServer())
.get(`/banking/rules/${ruleId}`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
});

View File

@@ -0,0 +1,61 @@
import * as request from 'supertest';
import { faker } from '@faker-js/faker';
import { app } from './init-app-test';
const createOwnerContributionTransaction = () => ({
date: '2024-01-01',
transactionNumber: faker.string.alphanumeric(10),
referenceNo: faker.string.alphanumeric(8),
transactionType: 'owner_contribution',
description: faker.lorem.sentence(),
amount: faker.number.float({ min: 100, max: 10000, precision: 2 }),
// exchangeRate: 1,
// currencyCode: 'USD',
creditAccountId: 1014,
cashflowAccountId: 1000,
publish: true,
branchId: 1,
// plaidTransactionId: faker.string.uuid()
});
describe('Banking Transactions (e2e)', () => {
it('/banking/transactions (POST)', () => {
return request(app.getHttpServer())
.post('/banking/transactions')
.set('organization-id', '4064541lv40nhca')
.send(createOwnerContributionTransaction())
.expect(201);
});
it('/banking/transactions/:id (GET)', async () => {
const transaction = createOwnerContributionTransaction();
const response = await request(app.getHttpServer())
.post('/banking/transactions')
.set('organization-id', '4064541lv40nhca')
.send(transaction)
.expect(201);
const transactionId = response.body.id;
return request(app.getHttpServer())
.get(`/banking/transactions/${transactionId}`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
it('/banking/transactions/:id (DELETE)', async () => {
const transaction = createOwnerContributionTransaction();
const response = await request(app.getHttpServer())
.post('/banking/transactions')
.set('organization-id', '4064541lv40nhca')
.send(transaction)
.expect(201);
const transactionId = response.body.id;
return request(app.getHttpServer())
.delete(`/banking/transactions/${transactionId}`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
});

View File

@@ -23,7 +23,8 @@ export class GetBankRuleService {
const bankRule = await BankRule.query() const bankRule = await BankRule.query()
.findById(ruleId) .findById(ruleId)
.withGraphFetched('conditions'); .withGraphFetched('conditions')
.withGraphFetched('assignAccount');
return this.transformer.transform( return this.transformer.transform(
tenantId, tenantId,