mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
add server to monorepo.
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
import { sumBy, defaultTo } from 'lodash';
|
||||
import {
|
||||
ITransactionsByContactsTransaction,
|
||||
ITransactionsByContactsAmount,
|
||||
ITransactionsByContactsFilter,
|
||||
ITransactionsByContactsContact,
|
||||
IContact,
|
||||
ILedger,
|
||||
ILedgerEntry,
|
||||
} from '@/interfaces';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import { allPassedConditionsPass } from 'utils';
|
||||
|
||||
export default class TransactionsByContact extends FinancialSheet {
|
||||
readonly contacts: IContact[];
|
||||
readonly ledger: ILedger;
|
||||
readonly filter: ITransactionsByContactsFilter;
|
||||
readonly accountsGraph: any;
|
||||
|
||||
/**
|
||||
* Customer transaction mapper.
|
||||
* @param {any} transaction -
|
||||
* @return {Omit<ITransactionsByContactsTransaction, 'runningBalance'>}
|
||||
*/
|
||||
protected contactTransactionMapper(
|
||||
entry: ILedgerEntry
|
||||
): Omit<ITransactionsByContactsTransaction, 'runningBalance'> {
|
||||
const account = this.accountsGraph.getNodeData(entry.accountId);
|
||||
const currencyCode = this.baseCurrency;
|
||||
|
||||
return {
|
||||
credit: this.getContactAmount(entry.credit, currencyCode),
|
||||
debit: this.getContactAmount(entry.debit, currencyCode),
|
||||
accountName: account.name,
|
||||
currencyCode: this.baseCurrency,
|
||||
transactionNumber: entry.transactionNumber,
|
||||
transactionType: this.i18n.__(entry.referenceTypeFormatted),
|
||||
date: entry.date,
|
||||
createdAt: entry.createdAt,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Customer transactions mapper with running balance.
|
||||
* @param {number} openingBalance
|
||||
* @param {ITransactionsByContactsTransaction[]} transactions
|
||||
* @returns {ITransactionsByContactsTransaction[]}
|
||||
*/
|
||||
protected contactTransactionRunningBalance(
|
||||
openingBalance: number,
|
||||
accountNormal: 'credit' | 'debit',
|
||||
transactions: Omit<ITransactionsByContactsTransaction, 'runningBalance'>[]
|
||||
): any {
|
||||
let _openingBalance = openingBalance;
|
||||
|
||||
return transactions.map(
|
||||
(transaction: ITransactionsByContactsTransaction) => {
|
||||
_openingBalance +=
|
||||
accountNormal === 'debit'
|
||||
? transaction.debit.amount
|
||||
: -1 * transaction.debit.amount;
|
||||
|
||||
_openingBalance +=
|
||||
accountNormal === 'credit'
|
||||
? transaction.credit.amount
|
||||
: -1 * transaction.credit.amount;
|
||||
|
||||
const runningBalance = this.getTotalAmountMeta(
|
||||
_openingBalance,
|
||||
transaction.currencyCode
|
||||
);
|
||||
return { ...transaction, runningBalance };
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the customer closing balance from the given transactions and opening balance.
|
||||
* @param {number} customerTransactions
|
||||
* @param {number} openingBalance
|
||||
* @returns {number}
|
||||
*/
|
||||
protected getContactClosingBalance(
|
||||
customerTransactions: ITransactionsByContactsTransaction[],
|
||||
contactNormal: 'credit' | 'debit',
|
||||
openingBalance: number
|
||||
): number {
|
||||
const closingBalance = openingBalance;
|
||||
|
||||
const totalCredit = sumBy(customerTransactions, 'credit.amount');
|
||||
const totalDebit = sumBy(customerTransactions, 'debit.amount');
|
||||
|
||||
const total =
|
||||
contactNormal === 'debit'
|
||||
? totalDebit - totalCredit
|
||||
: totalCredit - totalDebit;
|
||||
|
||||
return closingBalance + total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the given customer opening balance from the given customer id.
|
||||
* @param {number} customerId
|
||||
* @returns {number}
|
||||
*/
|
||||
protected getContactOpeningBalance(customerId: number): number {
|
||||
const openingBalanceLedger = this.ledger
|
||||
.whereContactId(customerId)
|
||||
.whereToDate(this.filter.fromDate);
|
||||
|
||||
// Retrieve the closing balance of the ledger.
|
||||
const openingBalance = openingBalanceLedger.getClosingBalance();
|
||||
|
||||
return defaultTo(openingBalance, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the customer amount format meta.
|
||||
* @param {number} amount
|
||||
* @param {string} currencyCode
|
||||
* @returns {ITransactionsByContactsAmount}
|
||||
*/
|
||||
protected getContactAmount(
|
||||
amount: number,
|
||||
currencyCode: string
|
||||
): ITransactionsByContactsAmount {
|
||||
return {
|
||||
amount,
|
||||
formattedAmount: this.formatNumber(amount, { currencyCode }),
|
||||
currencyCode,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the contact total amount format meta.
|
||||
* @param {number} amount - Amount.
|
||||
* @param {string} currencyCode - Currency code./
|
||||
* @returns {ITransactionsByContactsAmount}
|
||||
*/
|
||||
protected getTotalAmountMeta(amount: number, currencyCode: string) {
|
||||
return {
|
||||
amount,
|
||||
formattedAmount: this.formatTotalNumber(amount, { currencyCode }),
|
||||
currencyCode,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter customer section that has no transactions.
|
||||
* @param {ITransactionsByCustomersCustomer} transactionsByCustomer
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private filterContactByNoneTransaction = (
|
||||
transactionsByContact: ITransactionsByContactsContact
|
||||
): boolean => {
|
||||
return transactionsByContact.transactions.length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters customer section has zero closing balnace.
|
||||
* @param {ITransactionsByCustomersCustomer} transactionsByCustomer
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private filterContactNoneZero = (
|
||||
transactionsByContact: ITransactionsByContactsContact
|
||||
): boolean => {
|
||||
return transactionsByContact.closingBalance.amount !== 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters the given customer node;
|
||||
* @param {ICustomerBalanceSummaryCustomer} customer
|
||||
*/
|
||||
private contactNodeFilter = (node: ITransactionsByContactsContact) => {
|
||||
const { noneTransactions, noneZero } = this.filter;
|
||||
|
||||
// Conditions pair filter detarminer.
|
||||
const condsPairFilters = [
|
||||
[noneTransactions, this.filterContactByNoneTransaction],
|
||||
[noneZero, this.filterContactNoneZero],
|
||||
];
|
||||
return allPassedConditionsPass(condsPairFilters)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters the given customers nodes.
|
||||
* @param {ICustomerBalanceSummaryCustomer[]} nodes
|
||||
* @returns {ICustomerBalanceSummaryCustomer[]}
|
||||
*/
|
||||
protected contactsFilter = (
|
||||
nodes: ITransactionsByContactsContact[]
|
||||
): ITransactionsByContactsContact[] => {
|
||||
return nodes.filter(this.contactNodeFilter);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import moment from 'moment';
|
||||
import * as R from 'ramda';
|
||||
import { tableMapper, tableRowMapper } from 'utils';
|
||||
import { ITransactionsByContactsContact, ITableRow } from '@/interfaces';
|
||||
|
||||
enum ROW_TYPE {
|
||||
OPENING_BALANCE = 'OPENING_BALANCE',
|
||||
CLOSING_BALANCE = 'CLOSING_BALANCE',
|
||||
TRANSACTION = 'TRANSACTION',
|
||||
CUSTOMER = 'CUSTOMER',
|
||||
}
|
||||
|
||||
export default class TransactionsByContactsTableRows {
|
||||
private dateAccessor = (value): string => {
|
||||
return moment(value.date).format('YYYY MMM DD');
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the table rows of contact transactions.
|
||||
* @param {ITransactionsByCustomersCustomer} contact
|
||||
* @returns {ITableRow[]}
|
||||
*/
|
||||
protected contactTransactions = (
|
||||
contact: ITransactionsByContactsContact
|
||||
): ITableRow[] => {
|
||||
const columns = [
|
||||
{ key: 'date', accessor: this.dateAccessor },
|
||||
{ key: 'account', accessor: 'accountName' },
|
||||
{ key: 'transactionType', accessor: 'transactionType' },
|
||||
{ key: 'transactionNumber', accessor: 'transactionNumber' },
|
||||
{ key: 'credit', accessor: 'credit.formattedAmount' },
|
||||
{ key: 'debit', accessor: 'debit.formattedAmount' },
|
||||
{ key: 'runningBalance', accessor: 'runningBalance.formattedAmount' },
|
||||
];
|
||||
return tableMapper(contact.transactions, columns, {
|
||||
rowTypes: [ROW_TYPE.TRANSACTION],
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the table row of contact opening balance.
|
||||
* @param {ITransactionsByCustomersCustomer} contact
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
protected contactOpeningBalance = (
|
||||
contact: ITransactionsByContactsContact
|
||||
): ITableRow => {
|
||||
const columns = [
|
||||
{ key: 'openingBalanceLabel', value: this.i18n.__('Opening balance') },
|
||||
...R.repeat({ key: 'empty', value: '' }, 5),
|
||||
{
|
||||
key: 'openingBalanceValue',
|
||||
accessor: 'openingBalance.formattedAmount',
|
||||
},
|
||||
];
|
||||
return tableRowMapper(contact, columns, {
|
||||
rowTypes: [ROW_TYPE.OPENING_BALANCE],
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the table row of contact closing balance.
|
||||
* @param {ITransactionsByCustomersCustomer} contact -
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
protected contactClosingBalance = (
|
||||
contact: ITransactionsByContactsContact
|
||||
): ITableRow => {
|
||||
const columns = [
|
||||
{ key: 'closingBalanceLabel', value: this.i18n.__('Closing balance') },
|
||||
...R.repeat({ key: 'empty', value: '' }, 5),
|
||||
{
|
||||
key: 'closingBalanceValue',
|
||||
accessor: 'closingBalance.formattedAmount',
|
||||
},
|
||||
];
|
||||
return tableRowMapper(contact, columns, {
|
||||
rowTypes: [ROW_TYPE.CLOSING_BALANCE],
|
||||
});
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user