diff --git a/Asset/css/finance.css b/Asset/css/finance.css index a36d7cd..d2dc4e7 100644 --- a/Asset/css/finance.css +++ b/Asset/css/finance.css @@ -1,9 +1,30 @@ /* * FinanceBuddy styles. - * - * The per-column total (.fb-column-total) styling is added in a later version. */ +/* Per-column net total in the column header (its own line, subtle pill). */ +.fb-column-total { + display: inline-block; + margin-top: 3px; + padding: 0 5px; + font-weight: bold; + font-size: 0.95em; + background: rgba(0, 0, 0, 0.05); + border-radius: 3px; +} + +.fb-total-debit { + color: #b94a48; +} + +.fb-total-credit { + color: #468847; +} + +.fb-total-zero { + color: #777; +} + /* Line-3 money badge on board cards. Its own background guarantees contrast on any card color. */ .fb-line { margin: 2px 0 4px; diff --git a/Helper/FinanceHelper.php b/Helper/FinanceHelper.php index 3d5c7af..ddb5e24 100644 --- a/Helper/FinanceHelper.php +++ b/Helper/FinanceHelper.php @@ -62,6 +62,54 @@ class FinanceHelper extends Base ); } + /** + * Net money for a board column: credits positive, debits negative, summed over the cards in + * the column. One grouped query over the column's task ids (the task rows carry no metadata). + * + * @param array $column A board column as passed to template:board:column:header ('tasks'). + * @return float + */ + public function columnNet(array $column) + { + if (empty($column['tasks'])) { + return 0.0; + } + + $task_ids = array(); + foreach ($column['tasks'] as $task) { + $task_ids[] = (int) $task['id']; + } + + $rows = $this->db->table('task_has_metadata') + ->columns('task_id', 'name', 'value') + ->in('task_id', $task_ids) + ->in('name', array(self::AMOUNT_KEY, self::DIRECTION_KEY)) + ->findAll(); + + $amounts = array(); + $directions = array(); + + foreach ($rows as $row) { + if ($row['name'] === self::AMOUNT_KEY) { + $amounts[$row['task_id']] = (float) $row['value']; + } else { + $directions[$row['task_id']] = $row['value']; + } + } + + $net = 0.0; + foreach ($amounts as $task_id => $amount) { + if ($amount <= 0) { + continue; + } + + $is_credit = isset($directions[$task_id]) && $directions[$task_id] === 'credit'; + $net += $is_credit ? $amount : -$amount; + } + + return $net; + } + /** * Values to prefill the task-form fields: a re-post (after a validation error) wins, then the * stored metadata for an existing task, otherwise blanks for a new task. diff --git a/Plugin.php b/Plugin.php index 9cef282..0f372ca 100644 --- a/Plugin.php +++ b/Plugin.php @@ -33,6 +33,9 @@ class Plugin extends Base // "private" is the normal board, "public" is the read-only board shared by token. $this->template->hook->attach('template:board:private:task:after-title', 'financeBuddy:board/task_money'); $this->template->hook->attach('template:board:public:task:after-title', 'financeBuddy:board/task_money'); + + // Per-column net total (credits - debits) in the column header. + $this->template->hook->attach('template:board:column:header', 'financeBuddy:board/column_total'); } public function getHelpers() @@ -59,7 +62,7 @@ class Plugin extends Base public function getPluginVersion() { - return '0.3.1'; + return '0.4.0'; } public function getPluginHomepage() diff --git a/VERSION b/VERSION index e5062c9..4f55b7c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -FinanceBuddy v0.3.1 +FinanceBuddy v0.4