Module: BudgetHelper

Defined in:
app/helpers/budget_helper.rb

Overview

View helper: budget.

Renders the budget vs. actual comparison report cells for
Analytic::BudgetFact rows, linking amounts into the budget and
ledger-entry drill-downs on BudgetsController.

Constant Summary collapse

ZERO_DISPLAY =

Em dash for zero values - cleaner for management reports

''.freeze

Instance Method Summary collapse

Instance Method Details

#budget_cell(amount, fact, accumulated, additional_class = nil) ⇒ ActiveSupport::SafeBuffer

Renders a budget-amount cell linking to the budget drill-down.

Parameters:

  • amount (Numeric)

    the budgeted amount to display

  • fact (Analytic::BudgetFact)

    the fact the budget entries belong to

  • accumulated (Boolean)

    whether the drill-down shows accumulated figures

  • additional_class (String, nil) (defaults to: nil)

    optional extra CSS class for the cell

Returns:

  • (ActiveSupport::SafeBuffer)

    the <td> element

See Also:



112
113
114
115
116
117
118
119
120
121
122
123
# File 'app/helpers/budget_helper.rb', line 112

def budget_cell(amount, fact, accumulated, additional_class = nil)
  css_classes = [additional_class]
  css_classes << 'danger' if amount < 0
  css_classes << 'zero-value' if amount.zero?

  cell_content = if amount.zero?
                   ZERO_DISPLAY
                 else
                   link_to(number_with_delimiter_acc(amount), show_budgets_for_fact_budgets_path(fact_id: fact.id, accumulated: accumulated))
                 end
  ('td', cell_content, class: css_classes.compact.join(' '))
end

#fact_cells(fact, _dimension) ⇒ ActiveSupport::SafeBuffer

Builds the sixteen budget/actual table cells for one report row.

Renders eight current-year cells (budget, actual, diff, percent for
both month and accumulated) and eight previous-year cells. When no
fact exists, renders sixteen placeholder -- cells instead.

Parameters:

  • fact (Analytic::BudgetFact, nil)

    the budget fact row to render

  • _dimension (Object)

    unused report dimension, kept for partial signature compatibility

Returns:

  • (ActiveSupport::SafeBuffer)

    the concatenated <td> cells



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'app/helpers/budget_helper.rb', line 23

def fact_cells(fact, _dimension)
  cells = +''
  if fact.nil?
    8.times do
      cells << ('td', '--', class: 'budget-current zero-value')
    end
    8.times do
      cells << ('td', '--', class: 'previous-year zero-value')
    end
  else
    cells << budget_cell(fact.budget_month, fact, false, 'budget-current')
    cells << linked_cell(fact.actual_month, fact, 'current-month', 'budget-current')
    cells << standard_cell(fact.budget_month_diff, 'budget-current')
    cells << percentage_cell(fact.budget_month_percent, fact.is_revenue?, 'budget-current')

    cells << budget_cell(fact.budget_accumulated, fact, true, 'budget-current')
    cells << linked_cell(fact.actual_accumulated, fact, 'current-accumulated', 'budget-current')
    cells << standard_cell(fact.budget_accumulated_diff, 'budget-current')
    cells << percentage_cell(fact.budget_accumulated_percent, fact.is_revenue?, 'budget-current')

    cells << linked_cell(fact.actual_month_previous, fact, 'previous-month', 'previous-year')
    cells << linked_cell(fact.actual_month, fact, 'current-month', 'previous-year')
    cells << standard_cell(fact.actual_month_previous_diff, 'previous-year')
    cells << percentage_cell(fact.actual_month_previous_percent, fact.is_revenue?, 'previous-year')

    cells << linked_cell(fact.actual_accumulated_previous, fact, 'previous-accumulated', 'previous-year')
    cells << linked_cell(fact.actual_accumulated, fact, 'current-accumulated', 'previous-year')
    cells << standard_cell(fact.actual_accumulated_previous_diff, 'previous-year')
    cells << percentage_cell(fact.actual_accumulated_previous_percent, fact.is_revenue?, 'previous-year')
  end
  cells.html_safe
end

#format_amount(amount) ⇒ String

Format number for display, showing em dash for zeros

Parameters:

  • amount (Numeric)

    the amount to format

Returns:

  • (String)

    ZERO_DISPLAY for zero, otherwise the delimited number



67
68
69
# File 'app/helpers/budget_helper.rb', line 67

def format_amount(amount)
  amount.zero? ? ZERO_DISPLAY : number_with_delimiter_acc(amount)
end

#linked_cell(amount, fact, period, additional_class = nil) ⇒ ActiveSupport::SafeBuffer

Renders an actual-amount cell linking to the ledger-entry drill-down.

Parameters:

  • amount (Numeric)

    the actual amount to display

  • fact (Analytic::BudgetFact)

    the fact the ledger entries belong to

  • period (String)

    the drill-down period (e.g. 'current-month', 'previous-accumulated')

  • additional_class (String, nil) (defaults to: nil)

    optional extra CSS class for the cell

Returns:

  • (ActiveSupport::SafeBuffer)

    the <td> element

See Also:



91
92
93
94
95
96
97
98
99
100
101
102
# File 'app/helpers/budget_helper.rb', line 91

def linked_cell(amount, fact, period, additional_class = nil)
  css_classes = [additional_class]
  css_classes << 'danger' if amount < 0
  css_classes << 'zero-value' if amount.zero?

  cell_content = if amount.zero?
                   ZERO_DISPLAY
                 else
                   link_to(number_with_delimiter_acc(amount), show_ledger_entries_for_fact_budgets_path(fact_id: fact.id, period: period))
                 end
  ('td', cell_content, class: css_classes.compact.join(' '))
end

#number_with_delimiter_acc(number) ⇒ String

Formats a number with thousands delimiters, wrapping negatives in parentheses.

Parameters:

  • number (Numeric)

    the amount to format

Returns:

  • (String)

    the delimited number, e.g. "1,234" or "(1,234)" for negatives



60
61
62
# File 'app/helpers/budget_helper.rb', line 60

def number_with_delimiter_acc(number)
  number < 0 ? "(#{number_with_delimiter(-number)})" : number_with_delimiter(number)
end

#percentage_cell(percentage, is_revenue, additional_class = nil) ⇒ ActiveSupport::SafeBuffer

Renders a percentage-variance cell with a direction arrow.

Logic Details

Arrows are relative to whether the row is revenue: for revenue rows a
negative variance is unfavorable (down arrow, danger), while for
expense rows a positive variance is unfavorable.

Parameters:

  • percentage (Numeric)

    the variance percentage

  • is_revenue (Boolean)

    whether the row represents revenue

  • additional_class (String, nil) (defaults to: nil)

    optional extra CSS class for the cell

Returns:

  • (ActiveSupport::SafeBuffer)

    the <td> element



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'app/helpers/budget_helper.rb', line 136

def percentage_cell(percentage, is_revenue, additional_class = nil)
  css_classes = [additional_class]

  if percentage.zero?
    css_classes << 'zero-value'
    return ('td', ZERO_DISPLAY, class: css_classes.compact.join(' '))
  end

  if ((is_revenue == true) && (percentage < 0)) || ((is_revenue == false) && (percentage > 0))
    fa_icon = ('i', '', class: 'fa fa-arrow-down')
    css_classes << 'danger'
  elsif ((is_revenue == true) && (percentage > 0)) || ((is_revenue == false) && (percentage < 0))
    fa_icon = ('i', '', class: 'fa fa-arrow-up')
    css_classes << 'ok'
  else
    fa_icon = ''
  end
  ('td', number_to_percentage(percentage, precision: 1) + fa_icon, class: css_classes.compact.join(' '))
end

#standard_cell(amount, additional_class = nil) ⇒ ActiveSupport::SafeBuffer

Renders a plain amount cell with danger/zero-value styling.

Parameters:

  • amount (Numeric)

    the amount to display

  • additional_class (String, nil) (defaults to: nil)

    optional extra CSS class for the cell

Returns:

  • (ActiveSupport::SafeBuffer)

    the <td> element



76
77
78
79
80
81
# File 'app/helpers/budget_helper.rb', line 76

def standard_cell(amount, additional_class = nil)
  css_classes = [additional_class]
  css_classes << 'danger' if amount < 0
  css_classes << 'zero-value' if amount.zero?
  ('td', format_amount(amount), class: css_classes.compact.join(' '))
end