fix: customer balance and transactions report.

This commit is contained in:
a.bouhuolia
2021-05-08 01:26:42 +02:00
parent b5ed7af7eb
commit 531949e090
6 changed files with 95 additions and 57 deletions

View File

@@ -1,7 +1,7 @@
import { get } from 'lodash';
import * as R from 'ramda';
import {
IJournalPoster,
ILedger,
ICustomer,
ICustomerBalanceSummaryCustomer,
ICustomerBalanceSummaryQuery,
@@ -11,7 +11,7 @@ import {
import { ContactBalanceSummaryReport } from '../ContactBalanceSummary/ContactBalanceSummary';
export class CustomerBalanceSummaryReport extends ContactBalanceSummaryReport {
readonly receivableLedger: IJournalPoster;
readonly ledger: ILedger;
readonly baseCurrency: string;
readonly customers: ICustomer[];
readonly filter: ICustomerBalanceSummaryQuery;
@@ -25,14 +25,14 @@ export class CustomerBalanceSummaryReport extends ContactBalanceSummaryReport {
* @param {string} baseCurrency
*/
constructor(
receivableLedger: IJournalPoster,
ledger: ILedger,
customers: ICustomer[],
filter: ICustomerBalanceSummaryQuery,
baseCurrency: string
) {
super();
this.receivableLedger = receivableLedger;
this.ledger = ledger;
this.baseCurrency = baseCurrency;
this.customers = customers;
this.filter = filter;
@@ -45,12 +45,13 @@ export class CustomerBalanceSummaryReport extends ContactBalanceSummaryReport {
* @returns {ICustomerBalanceSummaryCustomer}
*/
private customerMapper(customer: ICustomer): ICustomerBalanceSummaryCustomer {
const customerBalance = this.receivableLedger.get(customer.id);
const balanceAmount = get(customerBalance, 'balance', 0);
const closingBalance = this.ledger
.whereContactId(customer.id)
.getClosingBalance();
return {
customerName: customer.displayName,
total: this.getContactTotalFormat(balanceAmount),
total: this.getContactTotalFormat(closingBalance),
};
}
@@ -96,7 +97,11 @@ export class CustomerBalanceSummaryReport extends ContactBalanceSummaryReport {
};
}
reportColumns() {
/**
* Retrieve the report statement columns
* @returns
*/
public reportColumns() {
return [];
}
}

View File

@@ -1,16 +1,17 @@
import { Inject } from 'typedi';
import moment from 'moment';
import { map } from 'lodash';
import { isEmpty, map } from 'lodash';
import TenancyService from 'services/Tenancy/TenancyService';
import * as R from 'ramda';
import { transformToMap } from 'utils';
import {
ICustomerBalanceSummaryService,
ICustomerBalanceSummaryQuery,
ICustomerBalanceSummaryStatement,
ICustomer
} from 'interfaces';
import { CustomerBalanceSummaryReport } from './CustomerBalanceSummary';
import { ACCOUNT_TYPE } from 'data/AccountTypes';
import Ledger from 'services/Accounting/Ledger';
export default class CustomerBalanceSummaryService
implements ICustomerBalanceSummaryService {
@@ -42,39 +43,64 @@ export default class CustomerBalanceSummaryService
};
}
customersBalancesQuery(query) {
query.groupBy('contactId');
query.sum('credit as credit');
query.sum('debit as debit');
query.select('contactId');
/**
* Retrieve the A/R accounts.
* @param tenantId
* @returns
*/
private getReceivableAccounts(tenantId: number) {
const { Account } = this.tenancy.models(tenantId);
return Account.query().where(
'accountType',
ACCOUNT_TYPE.ACCOUNTS_RECEIVABLE
);
}
/**
* Retrieve the customers credit/debit totals
* @param {number} tenantId
* @returns
* @param {number} tenantId
* @returns
*/
async getCustomersCreditDebitTotals(tenantId: number) {
const { AccountTransaction, Account } = this.tenancy.models(tenantId);
private async getReportCustomersTransactions(tenantId: number, asDate: any) {
const { AccountTransaction } = this.tenancy.models(tenantId);
const receivableAccounts = await Account.query().where(
'accountType',
ACCOUNT_TYPE.ACCOUNTS_RECEIVABLE
);
// Retrieve the receivable accounts A/R.
const receivableAccounts = await this.getReceivableAccounts(tenantId);
const receivableAccountsIds = map(receivableAccounts, 'id');
const customersTotals = await AccountTransaction.query().onBuild((query) => {
query.whereIn('accountId', receivableAccountsIds);
this.customersBalancesQuery(query);
});
// Retrieve the customers transactions of A/R accounts.
const customersTranasctions = await AccountTransaction.query().onBuild(
(query) => {
query.whereIn('accountId', receivableAccountsIds);
query.modify('filterDateRange', null, asDate);
query.groupBy('contactId');
query.sum('credit as credit');
query.sum('debit as debit');
query.select('contactId');
}
);
const commonProps = { accountNormal: 'debit', date: asDate };
return R.compose(
(customers) => transformToMap(customers, 'contactId'),
(customers) => customers.map((customer) => ({
...customer,
balance: customer.debit - customer.credit
})),
)(customersTotals);
return R.map(R.merge(commonProps))(customersTranasctions);
}
/**
* Retrieve the report customers.
* @param {number} tenantId
* @param {number[]} customersIds
* @returns {ICustomer[]}
*/
private getReportCustomers(tenantId: number, customersIds: number[]): ICustomer[] {
const { Customer } = this.tenancy.models(tenantId);
return Customer.query()
.orderBy('displayName')
.onBuild((query) => {
if (!isEmpty(customersIds)) {
query.whereIn('id', customersIds);
}
});
}
/**
@@ -87,19 +113,15 @@ export default class CustomerBalanceSummaryService
tenantId: number,
query: ICustomerBalanceSummaryQuery
): Promise<ICustomerBalanceSummaryStatement> {
const { Customer } = this.tenancy.models(tenantId);
// Settings tenant service.
const settings = this.tenancy.settings(tenantId);
const baseCurrency = settings.get({
group: 'organization',
key: 'base_currency',
});
// Merges the default query and request query.
const filter = { ...this.defaultQuery, ...query };
const filter = {
...this.defaultQuery,
...query,
};
this.logger.info(
'[customer_balance_summary] trying to calculate the report.',
{
@@ -108,27 +130,30 @@ export default class CustomerBalanceSummaryService
}
);
// Retrieve the customers list ordered by the display name.
const customers = await Customer.query().orderBy('displayName');
const customers = await this.getReportCustomers(
tenantId,
query.customersIds
);
// Retrieve the customers debit/credit totals.
const customersBalances = await this.getCustomersCreditDebitTotals(tenantId);
const customersTransactions = await this.getReportCustomersTransactions(
tenantId,
filter.asDate
);
// Ledger query.
const ledger = new Ledger(customersTransactions);
// Report instance.
const reportInstance = new CustomerBalanceSummaryReport(
customersBalances,
const report = new CustomerBalanceSummaryReport(
ledger,
customers,
filter,
baseCurrency
);
// Retrieve the report statement.
const reportData = reportInstance.reportData();
// Retrieve the report columns.
const reportColumns = reportInstance.reportColumns();
return {
data: reportData,
columns: reportColumns,
data: report.reportData(),
columns: report.reportColumns(),
query: filter
};
}
}

View File

@@ -142,7 +142,6 @@ export default class TransactionsByCustomersService
...this.defaultQuery,
...query,
};
const accountsGraph = await accountRepository.getDependencyGraph();
const customers = await Customer.query().orderBy('displayName');
@@ -181,6 +180,7 @@ export default class TransactionsByCustomersService
return {
data: reportData,
columns: reportColumns,
query: filter,
};
}
}

View File

@@ -17,7 +17,11 @@ export default class TransactionsByCustomersTableRows extends TransactionsByCont
* @returns {ITableRow[]}
*/
private customerDetails(customer: ITransactionsByCustomersCustomer) {
const columns = [{ key: 'customerName', accessor: 'customerName' }];
const columns = [
{ key: 'customerName', accessor: 'customerName' },
...R.repeat({ key: 'empty', value: '' }, 5),
{ key: 'closingBalanceValue', accessor: 'closingBalance.formattedAmount' },
];
return {
...tableRowMapper(customer, columns, { rowTypes: [ROW_TYPE.CUSTOMER] }),