Skip to content

Commit

Permalink
feat(General Ledger): prune empty accounts
Browse files Browse the repository at this point in the history
This commit makes sure empty accounts are pruned from the General Ledger
report.
  • Loading branch information
jniles committed Jan 26, 2018
1 parent 22f4cc1 commit ceec405
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 16 deletions.
41 changes: 26 additions & 15 deletions server/controllers/finance/generalLedger/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,46 +113,57 @@ function listAccounts(req, res, next) {
.done();
}

const PERIODS = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
];


/**
* @function getAccountTotalsMatrix
*
* @description
* This function gets the period totals for all general ledger accounts from the
* period totals table.
* This function gets the period totals for all accounts in a single fiscal
* year. Returns only accounts (and their parents) that contain balances.
*/
function getAccountTotalsMatrix(fiscalYearId) {
const periodNumbers = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
];

// this creates a series of columns that only sum values if they are in the
// correct periodNumber.
const periodColumns = periodNumbers.reduce(
// correct period.
const columns = PERIODS.reduce(
(q, number) => `${q}, SUM(IF(p.number = ${number}, pt.debit - pt.credit, 0)) AS balance${number}`,
''
);

const outerColumns =
PERIODS.map(number => `IFNULL(s.balance${number}, 0) AS balance${number}`)
.join(', ');

// we want to show every single account, so we do a left join of the account
// table
const sql = `
SELECT a.id, a.number, a.label, a.type_id, a.label, a.parent,
SUM(pt.debit - pt.credit) AS balance ${periodColumns}
FROM account AS a
LEFT JOIN period_total AS pt ON a.id = pt.account_id
IFNULL(s.balance, 0) AS balance, ${outerColumns}
FROM account AS a LEFT JOIN (
SELECT SUM(pt.debit - pt.credit) AS balance, pt.account_id ${columns}
FROM period_total AS pt
JOIN period AS p ON p.id = pt.period_id
WHERE pt.fiscal_year_id = ?
GROUP BY a.id
WHERE pt.fiscal_year_id = ?
GROUP BY pt.account_id
)s ON a.id = s.account_id
ORDER BY a.number;
`;

// returns true if all the balances are 0
const isEmptyRow = (row) => row.balance === 0;

return db.exec(sql, [fiscalYearId])
.then(accounts => {
const accountsTree = new Tree(accounts);

// compute the values of the title accounts as the values of their children
accountsTree.sumOnProperty('balance');
periodNumbers.forEach(number => accountsTree.sumOnProperty(`balance${number}`));
PERIODS.forEach(number => accountsTree.sumOnProperty(`balance${number}`));

return accountsTree.toArray();
// prune empty rows
return accountsTree.prune(isEmptyRow);
});
}
36 changes: 35 additions & 1 deletion server/lib/Tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* in the tree.
*/
const _ = require('lodash');
const debug = require('debug')('TreeBuilder');

/**
* @function buildTreeFromArray
Expand All @@ -16,6 +17,8 @@ const _ = require('lodash');
* This function makes a tree data structure from a properly formatted array.
*/
function buildTreeFromArray(nodes, parentId, parentKey) {
debug(`#builtTreeFromArray() called with (Array(${nodes.length}), ${parentId}, ${parentKey}.`);

// recursion base-case: return nothing if empty array
if (nodes.length === 0) { return null; }

Expand Down Expand Up @@ -67,7 +70,7 @@ function flatten(tree, depth, pruneChildren = true) {
* of their children for a given property.
*/
function sumOnProperty(node, prop) {
if (node.children.length > 0) {
if (hasChildren(node)) {
// recursively compute the value of node[prop] by summing all child[prop]s
node[prop] = node.children.reduce((value, child) =>
value + sumOnProperty(child, prop), 0);
Expand All @@ -76,6 +79,20 @@ function sumOnProperty(node, prop) {
return node[prop];
}

function hasChildren(node) {
return node.children.length > 0;
}

function markNodeToPrune(node, fn) {
if (hasChildren(node)) {
node.children.forEach(child => markNodeToPrune(child, fn));
}

if (fn(node)) {
node._toPrune = true;
}
}

class Tree {
constructor(data = [], options = {
parentKey : 'parent',
Expand All @@ -86,8 +103,25 @@ class Tree {
this._parentKey = options.parentKey;
this._rootId = options.rootId;


// build the tree with the provided root id and parentKey
this._tree = buildTreeFromArray(_.cloneDeep(data), this._rootId, this._parentKey);

debug(`#constructor() built tree with ${this._data.length} nodes.`);
}

prune(fn) {
debug('#prune() called on tree strucure.');
// walk down the tree, marking nodes to be pruned.
this._tree.forEach(node => markNodeToPrune(node, fn));

const prev = this.toArray();
const pruned = prev.filter(node => !node._toPrune);

debug(`#prune() removed ${prev.length - pruned.length} nodes from the tree`);

// return an array missing the pruned values
return pruned;
}

toArray() {
Expand Down

0 comments on commit ceec405

Please sign in to comment.