mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 21:00:31 +00:00
760 lines
20 KiB
TypeScript
760 lines
20 KiB
TypeScript
import { omit, get, chain } from 'lodash';
|
|
import moment from 'moment';
|
|
import { Container } from 'typedi';
|
|
import async from 'async';
|
|
import JournalEntry from '@/services/Accounting/JournalEntry';
|
|
import TenancyService from '@/services/Tenancy/TenancyService';
|
|
import {
|
|
IJournalEntry,
|
|
IJournalPoster,
|
|
IAccountChange,
|
|
IAccountsChange,
|
|
TEntryType,
|
|
} from '@/interfaces';
|
|
import Knex from 'knex';
|
|
|
|
const CONTACTS_CONFIG = [
|
|
{
|
|
accountBySlug: 'accounts-receivable',
|
|
contactService: 'customer',
|
|
assignRequired: true,
|
|
},
|
|
{
|
|
accountBySlug: 'accounts-payable',
|
|
contactService: 'vendor',
|
|
assignRequired: true,
|
|
},
|
|
];
|
|
|
|
export default class JournalPoster implements IJournalPoster {
|
|
tenantId: number;
|
|
tenancy: TenancyService;
|
|
logger: any;
|
|
models: any;
|
|
repositories: any;
|
|
|
|
deletedEntriesIds: number[] = [];
|
|
entries: IJournalEntry[] = [];
|
|
balancesChange: IAccountsChange = {};
|
|
accountsDepGraph: IAccountsChange;
|
|
|
|
accountsBalanceTable: { [key: number]: number } = {};
|
|
contactsBalanceTable: {
|
|
[key: number]: { credit: number; debit: number }[];
|
|
} = {};
|
|
saveContactBalanceQueue: any;
|
|
|
|
/**
|
|
* Journal poster constructor.
|
|
* @param {number} tenantId -
|
|
*/
|
|
constructor(tenantId: number, accountsGraph?: any, trx?: Knex.Transaction) {
|
|
this.initTenancy();
|
|
|
|
this.tenantId = tenantId;
|
|
this.models = this.tenancy.models(tenantId);
|
|
this.repositories = this.tenancy.repositories(tenantId);
|
|
|
|
if (accountsGraph) {
|
|
this.accountsDepGraph = accountsGraph;
|
|
}
|
|
this.trx = trx;
|
|
this.saveContactBalanceQueue = async.queue(
|
|
this.saveContactBalanceChangeTask.bind(this),
|
|
10
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Initial tenancy.
|
|
* @private
|
|
*/
|
|
private initTenancy() {
|
|
try {
|
|
this.tenancy = Container.get(TenancyService);
|
|
this.logger = Container.get('logger');
|
|
} catch (exception) {
|
|
throw new Error('Should execute this class inside tenancy area.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Async initialize accounts dependency graph.
|
|
* @private
|
|
* @returns {Promise<void>}
|
|
*/
|
|
public async initAccountsDepGraph(): Promise<void> {
|
|
const { accountRepository } = this.repositories;
|
|
|
|
if (!this.accountsDepGraph) {
|
|
const accountsDepGraph = await accountRepository.getDependencyGraph();
|
|
this.accountsDepGraph = accountsDepGraph;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Detarmines the ledger is empty.
|
|
*/
|
|
public isEmpty() {
|
|
return this.entries.length === 0;
|
|
}
|
|
|
|
/**
|
|
* Writes the credit entry for the given account.
|
|
* @param {IJournalEntry} entry -
|
|
*/
|
|
public credit(entryModel: IJournalEntry): void {
|
|
if (entryModel instanceof JournalEntry === false) {
|
|
throw new Error('The entry is not instance of JournalEntry.');
|
|
}
|
|
this.entries.push(entryModel.entry);
|
|
this.setAccountBalanceChange(entryModel.entry);
|
|
this.setContactBalanceChange(entryModel.entry);
|
|
}
|
|
|
|
/**
|
|
* Writes the debit entry for the given account.
|
|
* @param {JournalEntry} entry -
|
|
*/
|
|
public debit(entryModel: IJournalEntry): void {
|
|
if (entryModel instanceof JournalEntry === false) {
|
|
throw new Error('The entry is not instance of JournalEntry.');
|
|
}
|
|
this.entries.push(entryModel.entry);
|
|
this.setAccountBalanceChange(entryModel.entry);
|
|
this.setContactBalanceChange(entryModel.entry);
|
|
}
|
|
|
|
/**
|
|
* Sets the contact balance change.
|
|
*/
|
|
private setContactBalanceChange(entry) {
|
|
if (!entry.contactId) {
|
|
return;
|
|
}
|
|
const change = {
|
|
debit: entry.debit || 0,
|
|
credit: entry.credit || 0,
|
|
account: entry.account,
|
|
};
|
|
if (!this.contactsBalanceTable[entry.contactId]) {
|
|
this.contactsBalanceTable[entry.contactId] = [];
|
|
}
|
|
this.contactsBalanceTable[entry.contactId].push(change);
|
|
}
|
|
|
|
/**
|
|
* Save contacts balance change.
|
|
*/
|
|
async saveContactsBalance() {
|
|
await this.initAccountsDepGraph();
|
|
|
|
const balanceChanges = Object.entries(this.contactsBalanceTable).map(
|
|
([contactId, entries]) => ({
|
|
contactId,
|
|
entries: entries.filter((entry) => {
|
|
const account = this.accountsDepGraph.getNodeData(entry.account);
|
|
|
|
return (
|
|
account &&
|
|
CONTACTS_CONFIG.some((config) => {
|
|
return config.accountBySlug === account.slug;
|
|
})
|
|
);
|
|
}),
|
|
})
|
|
);
|
|
|
|
const balanceEntries = chain(balanceChanges)
|
|
.map((change) =>
|
|
change.entries.map((entry) => ({
|
|
...entry,
|
|
contactId: change.contactId,
|
|
}))
|
|
)
|
|
.flatten()
|
|
.value();
|
|
|
|
return this.saveContactBalanceQueue.pushAsync(balanceEntries);
|
|
}
|
|
|
|
/**
|
|
* Saves contact balance change task.
|
|
* @param {number} contactId - Contact id.
|
|
* @param {number} credit - Credit amount.
|
|
* @param {number} debit - Debit amount.
|
|
*/
|
|
async saveContactBalanceChangeTask({ contactId, credit, debit }) {
|
|
const { contactRepository } = this.repositories;
|
|
|
|
const contact = await contactRepository.findOneById(contactId);
|
|
let balanceChange = 0;
|
|
|
|
if (contact.contactNormal === 'credit') {
|
|
balanceChange += credit - debit;
|
|
} else {
|
|
balanceChange += debit - credit;
|
|
}
|
|
// Contact change balance.
|
|
await contactRepository.changeNumber(
|
|
{ id: contactId },
|
|
'balance',
|
|
balanceChange,
|
|
this.trx
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Sets account balance change.
|
|
* @param {JournalEntry} entry
|
|
* @param {String} type
|
|
*/
|
|
private setAccountBalanceChange(entry: IJournalEntry): void {
|
|
const accountChange: IAccountChange = {
|
|
debit: entry.debit,
|
|
credit: entry.credit,
|
|
};
|
|
this._setAccountBalanceChange(entry.account, accountChange);
|
|
}
|
|
|
|
/**
|
|
* Sets account balance change.
|
|
* @private
|
|
* @param {number} accountId -
|
|
* @param {IAccountChange} accountChange
|
|
*/
|
|
private _setAccountBalanceChange(
|
|
accountId: number,
|
|
accountChange: IAccountChange
|
|
) {
|
|
this.balancesChange = this.accountBalanceChangeReducer(
|
|
this.balancesChange,
|
|
accountId,
|
|
accountChange
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Accounts balance change reducer.
|
|
* @param {IAccountsChange} balancesChange
|
|
* @param {number} accountId
|
|
* @param {IAccountChange} accountChange
|
|
* @return {IAccountChange}
|
|
*/
|
|
private accountBalanceChangeReducer(
|
|
balancesChange: IAccountsChange,
|
|
accountId: number,
|
|
accountChange: IAccountChange
|
|
) {
|
|
const change = { ...balancesChange };
|
|
|
|
if (!change[accountId]) {
|
|
change[accountId] = { credit: 0, debit: 0 };
|
|
}
|
|
if (accountChange.credit) {
|
|
change[accountId].credit += accountChange.credit;
|
|
}
|
|
if (accountChange.debit) {
|
|
change[accountId].debit += accountChange.debit;
|
|
}
|
|
return change;
|
|
}
|
|
|
|
/**
|
|
* Converts balance changes to array.
|
|
* @private
|
|
* @param {IAccountsChange} accountsChange -
|
|
* @return {Promise<{ account: number, change: number }>}
|
|
*/
|
|
private async convertBalanceChangesToArr(
|
|
accountsChange: IAccountsChange
|
|
): Promise<{ account: number; change: number }[]> {
|
|
const mappedList: { account: number; change: number }[] = [];
|
|
const accountsIds: number[] = Object.keys(accountsChange).map((id) =>
|
|
parseInt(id, 10)
|
|
);
|
|
|
|
await Promise.all(
|
|
accountsIds.map(async (account: number) => {
|
|
const accountChange = accountsChange[account];
|
|
const accountNode = this.accountsDepGraph.getNodeData(account);
|
|
const normal = accountNode.accountNormal;
|
|
|
|
let change = 0;
|
|
|
|
if (accountChange.credit) {
|
|
change +=
|
|
normal === 'credit'
|
|
? accountChange.credit
|
|
: -1 * accountChange.credit;
|
|
}
|
|
if (accountChange.debit) {
|
|
change +=
|
|
normal === 'debit' ? accountChange.debit : -1 * accountChange.debit;
|
|
}
|
|
mappedList.push({ account, change });
|
|
})
|
|
);
|
|
return mappedList;
|
|
}
|
|
|
|
/**
|
|
* Saves the balance change of journal entries.
|
|
* @returns {Promise<void>}
|
|
*/
|
|
public async saveBalance() {
|
|
await this.initAccountsDepGraph();
|
|
|
|
const { Account } = this.models;
|
|
const accountsChange = this.balanceChangeWithDepends(this.balancesChange);
|
|
const balancesList = await this.convertBalanceChangesToArr(accountsChange);
|
|
const balancesAccounts = balancesList.map((b) => b.account);
|
|
|
|
// Ensure the accounts has atleast zero in amount.
|
|
await Account.query(this.trx)
|
|
.where('amount', null)
|
|
.whereIn('id', balancesAccounts)
|
|
.patch({ amount: 0 });
|
|
|
|
const balanceUpdateOpers: Promise<void>[] = [];
|
|
|
|
balancesList.forEach((balance: { account: number; change: number }) => {
|
|
const method: string = balance.change < 0 ? 'decrement' : 'increment';
|
|
|
|
this.logger.info(
|
|
'[journal_poster] increment/decrement account balance.',
|
|
{
|
|
balance,
|
|
tenantId: this.tenantId,
|
|
}
|
|
);
|
|
const query = Account.query(this.trx)
|
|
[method]('amount', Math.abs(balance.change))
|
|
.where('id', balance.account);
|
|
|
|
balanceUpdateOpers.push(query);
|
|
});
|
|
|
|
await Promise.all(balanceUpdateOpers);
|
|
this.resetAccountsBalanceChange();
|
|
}
|
|
|
|
/**
|
|
* Changes all accounts that dependencies of changed accounts.
|
|
* @param {IAccountsChange} accountsChange
|
|
* @returns {IAccountsChange}
|
|
*/
|
|
private balanceChangeWithDepends(
|
|
accountsChange: IAccountsChange
|
|
): IAccountsChange {
|
|
const accountsIds = Object.keys(accountsChange);
|
|
let changes: IAccountsChange = {};
|
|
|
|
accountsIds.forEach((accountId) => {
|
|
const accountChange = accountsChange[accountId];
|
|
const depAccountsIds = this.accountsDepGraph.dependantsOf(accountId);
|
|
|
|
[accountId, ...depAccountsIds].forEach((account) => {
|
|
changes = this.accountBalanceChangeReducer(
|
|
changes,
|
|
account,
|
|
accountChange
|
|
);
|
|
});
|
|
});
|
|
return changes;
|
|
}
|
|
|
|
/**
|
|
* Resets accounts balance change.
|
|
* @private
|
|
*/
|
|
private resetAccountsBalanceChange() {
|
|
this.balancesChange = {};
|
|
}
|
|
|
|
/**
|
|
* Saves the stacked journal entries to the storage.
|
|
* @returns {Promise<void>}
|
|
*/
|
|
public async saveEntries() {
|
|
const { transactionsRepository } = this.repositories;
|
|
const saveOperations: Promise<void>[] = [];
|
|
|
|
this.logger.info('[journal] trying to insert accounts transactions.');
|
|
|
|
this.entries.forEach((entry) => {
|
|
const oper = transactionsRepository.create(
|
|
{
|
|
accountId: entry.account,
|
|
...omit(entry, ['account']),
|
|
},
|
|
this.trx
|
|
);
|
|
saveOperations.push(oper);
|
|
});
|
|
await Promise.all(saveOperations);
|
|
}
|
|
|
|
/**
|
|
* Reverses the stacked journal entries.
|
|
*/
|
|
public reverseEntries() {
|
|
const reverseEntries: IJournalEntry[] = [];
|
|
|
|
this.entries.forEach((entry) => {
|
|
const reverseEntry = { ...entry };
|
|
|
|
if (entry.credit) {
|
|
reverseEntry.debit = entry.credit;
|
|
}
|
|
if (entry.debit) {
|
|
reverseEntry.credit = entry.debit;
|
|
}
|
|
reverseEntries.push(reverseEntry);
|
|
});
|
|
this.entries = reverseEntries;
|
|
}
|
|
|
|
/**
|
|
* Removes all stored entries or by the given in ids.
|
|
* @param {Array} ids -
|
|
*/
|
|
removeEntries(ids: number[] = []) {
|
|
const targetIds = ids.length <= 0 ? this.entries.map((e) => e.id) : ids;
|
|
const removeEntries = this.entries.filter(
|
|
(e) => targetIds.indexOf(e.id) !== -1
|
|
);
|
|
this.entries = this.entries.filter((e) => targetIds.indexOf(e.id) === -1);
|
|
|
|
removeEntries.forEach((entry) => {
|
|
entry.credit = -1 * entry.credit;
|
|
entry.debit = -1 * entry.debit;
|
|
|
|
this.setAccountBalanceChange(entry);
|
|
this.setContactBalanceChange(entry);
|
|
});
|
|
this.deletedEntriesIds.push(...removeEntries.map((entry) => entry.id));
|
|
}
|
|
|
|
/**
|
|
* Delete all the stacked entries.
|
|
* @return {Promise<void>}
|
|
*/
|
|
public async deleteEntries() {
|
|
const { transactionsRepository } = this.repositories;
|
|
|
|
if (this.deletedEntriesIds.length > 0) {
|
|
await transactionsRepository.deleteWhereIdIn(
|
|
this.deletedEntriesIds,
|
|
this.trx
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load fetched accounts journal entries.
|
|
* @param {IJournalEntry[]} entries -
|
|
*/
|
|
fromTransactions(transactions) {
|
|
transactions.forEach((transaction) => {
|
|
this.entries.push({
|
|
...transaction,
|
|
referenceTypeFormatted: transaction.referenceTypeFormatted,
|
|
account: transaction.accountId,
|
|
accountNormal: get(transaction, 'account.accountNormal'),
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Calculates the entries balance change.
|
|
* @public
|
|
*/
|
|
public calculateEntriesBalanceChange() {
|
|
this.entries.forEach((entry) => {
|
|
if (entry.credit) {
|
|
this.setAccountBalanceChange(entry, 'credit');
|
|
}
|
|
if (entry.debit) {
|
|
this.setAccountBalanceChange(entry, 'debit');
|
|
}
|
|
});
|
|
}
|
|
|
|
static fromTransactions(entries, ...args: [number, ...any]) {
|
|
const journal = new this(...args);
|
|
journal.fromTransactions(entries);
|
|
|
|
return journal;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the closing balance for the given account and closing date.
|
|
* @param {Number} accountId -
|
|
* @param {Date} closingDate -
|
|
* @param {string} dataType? -
|
|
* @return {number}
|
|
*/
|
|
getClosingBalance(
|
|
accountId: number,
|
|
closingDate: Date | string,
|
|
dateType: string = 'day'
|
|
): number {
|
|
let closingBalance = 0;
|
|
const momentClosingDate = moment(closingDate);
|
|
|
|
this.entries.forEach((entry) => {
|
|
// Can not continue if not before or event same closing date.
|
|
if (
|
|
(!momentClosingDate.isAfter(entry.date, dateType) &&
|
|
!momentClosingDate.isSame(entry.date, dateType)) ||
|
|
(entry.account !== accountId && accountId)
|
|
) {
|
|
return;
|
|
}
|
|
if (entry.accountNormal === 'credit') {
|
|
closingBalance += entry.credit ? entry.credit : -1 * entry.debit;
|
|
} else if (entry.accountNormal === 'debit') {
|
|
closingBalance += entry.debit ? entry.debit : -1 * entry.credit;
|
|
}
|
|
});
|
|
return closingBalance;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the given account balance with dependencies accounts.
|
|
* @param {Number} accountId -
|
|
* @param {Date} closingDate -
|
|
* @param {String} dateType -
|
|
* @return {Number}
|
|
*/
|
|
getAccountBalance(
|
|
accountId: number,
|
|
closingDate: Date | string,
|
|
dateType: string
|
|
) {
|
|
const accountNode = this.accountsDepGraph.getNodeData(accountId);
|
|
const depAccountsIds = this.accountsDepGraph.dependenciesOf(accountId);
|
|
const depAccounts = depAccountsIds.map((id) =>
|
|
this.accountsDepGraph.getNodeData(id)
|
|
);
|
|
|
|
let balance: number = 0;
|
|
|
|
[...depAccounts, accountNode].forEach((account) => {
|
|
const closingBalance = this.getClosingBalance(
|
|
account.id,
|
|
closingDate,
|
|
dateType
|
|
);
|
|
this.accountsBalanceTable[account.id] = closingBalance;
|
|
balance += this.accountsBalanceTable[account.id];
|
|
});
|
|
return balance;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the credit/debit sumation for the given account and date.
|
|
* @param {Number} account -
|
|
* @param {Date|String} closingDate -
|
|
*/
|
|
getTrialBalance(accountId, closingDate) {
|
|
const momentClosingDate = moment(closingDate);
|
|
const result = {
|
|
credit: 0,
|
|
debit: 0,
|
|
balance: 0,
|
|
};
|
|
this.entries.forEach((entry) => {
|
|
if (
|
|
(!momentClosingDate.isAfter(entry.date, 'day') &&
|
|
!momentClosingDate.isSame(entry.date, 'day')) ||
|
|
(entry.account !== accountId && accountId)
|
|
) {
|
|
return;
|
|
}
|
|
result.credit += entry.credit;
|
|
result.debit += entry.debit;
|
|
|
|
if (entry.accountNormal === 'credit') {
|
|
result.balance += entry.credit - entry.debit;
|
|
} else if (entry.accountNormal === 'debit') {
|
|
result.balance += entry.debit - entry.credit;
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Retrieve trial balance of the given account with depends.
|
|
* @param {Number} accountId
|
|
* @param {Date} closingDate
|
|
* @param {String} dateType
|
|
* @return {Number}
|
|
*/
|
|
|
|
getTrialBalanceWithDepands(
|
|
accountId: number,
|
|
closingDate: Date,
|
|
dateType: string
|
|
) {
|
|
const accountNode = this.accountsDepGraph.getNodeData(accountId);
|
|
const depAccountsIds = this.accountsDepGraph.dependenciesOf(accountId);
|
|
const depAccounts = depAccountsIds.map((id) =>
|
|
this.accountsDepGraph.getNodeData(id)
|
|
);
|
|
const trialBalance = { credit: 0, debit: 0, balance: 0 };
|
|
|
|
[...depAccounts, accountNode].forEach((account) => {
|
|
const _trialBalance = this.getTrialBalance(
|
|
account.id,
|
|
closingDate,
|
|
dateType
|
|
);
|
|
|
|
trialBalance.credit += _trialBalance.credit;
|
|
trialBalance.debit += _trialBalance.debit;
|
|
trialBalance.balance += _trialBalance.balance;
|
|
});
|
|
return trialBalance;
|
|
}
|
|
|
|
getContactTrialBalance(
|
|
accountId: number,
|
|
contactId: number,
|
|
contactType: string,
|
|
closingDate?: Date | string,
|
|
openingDate?: Date | string
|
|
) {
|
|
const momentClosingDate = moment(closingDate);
|
|
const momentOpeningDate = moment(openingDate);
|
|
const trial = {
|
|
credit: 0,
|
|
debit: 0,
|
|
balance: 0,
|
|
};
|
|
|
|
this.entries.forEach((entry) => {
|
|
if (
|
|
(closingDate &&
|
|
!momentClosingDate.isAfter(entry.date, 'day') &&
|
|
!momentClosingDate.isSame(entry.date, 'day')) ||
|
|
(openingDate &&
|
|
!momentOpeningDate.isBefore(entry.date, 'day') &&
|
|
!momentOpeningDate.isSame(entry.date)) ||
|
|
(accountId && entry.account !== accountId) ||
|
|
(contactId && entry.contactId !== contactId) ||
|
|
entry.contactType !== contactType
|
|
) {
|
|
return;
|
|
}
|
|
if (entry.credit) {
|
|
trial.balance -= entry.credit;
|
|
trial.credit += entry.credit;
|
|
}
|
|
if (entry.debit) {
|
|
trial.balance += entry.debit;
|
|
trial.debit += entry.debit;
|
|
}
|
|
});
|
|
return trial;
|
|
}
|
|
|
|
/**
|
|
* Retrieve total balnace of the given customer/vendor contact.
|
|
* @param {Number} accountId
|
|
* @param {Number} contactId
|
|
* @param {String} contactType
|
|
* @param {Date} closingDate
|
|
*/
|
|
getContactBalance(
|
|
accountId: number,
|
|
contactId: number,
|
|
contactType: string,
|
|
closingDate: Date,
|
|
openingDate: Date
|
|
) {
|
|
const momentClosingDate = moment(closingDate);
|
|
let balance = 0;
|
|
|
|
this.entries.forEach((entry) => {
|
|
if (
|
|
(closingDate &&
|
|
!momentClosingDate.isAfter(entry.date, 'day') &&
|
|
!momentClosingDate.isSame(entry.date, 'day')) ||
|
|
(entry.account !== accountId && accountId) ||
|
|
(contactId && entry.contactId !== contactId) ||
|
|
entry.contactType !== contactType
|
|
) {
|
|
return;
|
|
}
|
|
if (entry.credit) {
|
|
balance -= entry.credit;
|
|
}
|
|
if (entry.debit) {
|
|
balance += entry.debit;
|
|
}
|
|
});
|
|
return balance;
|
|
}
|
|
|
|
getAccountEntries(accountId: number) {
|
|
return this.entries.filter((entry) => entry.account === accountId);
|
|
}
|
|
|
|
/**
|
|
* Retrieve account entries with depents accounts.
|
|
* @param {number} accountId -
|
|
*/
|
|
getAccountEntriesWithDepents(accountId: number) {
|
|
const depAccountsIds = this.accountsDepGraph.dependenciesOf(accountId);
|
|
const accountsIds = [accountId, ...depAccountsIds];
|
|
|
|
return this.entries.filter(
|
|
(entry) => accountsIds.indexOf(entry.account) !== -1
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Retrieve total balnace of the given customer/vendor contact.
|
|
* @param {Number} accountId
|
|
* @param {Number} contactId
|
|
* @param {String} contactType
|
|
* @param {Date} closingDate
|
|
*/
|
|
getEntriesBalance(entries) {
|
|
let balance = 0;
|
|
|
|
entries.forEach((entry) => {
|
|
if (entry.credit) {
|
|
balance -= entry.credit;
|
|
}
|
|
if (entry.debit) {
|
|
balance += entry.debit;
|
|
}
|
|
});
|
|
return balance;
|
|
}
|
|
|
|
getContactEntries(contactId: number, openingDate: Date, closingDate?: Date) {
|
|
const momentClosingDate = moment(closingDate);
|
|
const momentOpeningDate = moment(openingDate);
|
|
|
|
return this.entries.filter((entry) => {
|
|
if (
|
|
(closingDate &&
|
|
!momentClosingDate.isAfter(entry.date, 'day') &&
|
|
!momentClosingDate.isSame(entry.date, 'day')) ||
|
|
(openingDate &&
|
|
!momentOpeningDate.isBefore(entry.date, 'day') &&
|
|
!momentOpeningDate.isSame(entry.date)) ||
|
|
entry.contactId === contactId
|
|
) {
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
}
|