Class: Crm::Reports::BudgetController

Inherits:
ReportsController
  • Object
show all
Defined in:
app/controllers/crm/reports/budget_controller.rb

Overview

Controller: budget.

Constant Summary collapse

MONETARY_FACT_FIELDS =

Fact fields that represent monetary amounts (integers) and must be
multiplied by the exchange rate when converting currencies.
Percentage fields are ratios and stay unchanged.

%i[
  budget_month actual_month budget_month_diff
  budget_accumulated actual_accumulated budget_accumulated_diff
  actual_month_previous actual_month_previous_diff
  actual_accumulated_previous actual_accumulated_previous_diff
].freeze
PERCENTAGE_FORMULAS =

Formulas for recalculating percentage fields after merging facts.
{ percentage_field => [diff_field, base_field] }

{
  budget_month_percent: %i[budget_month_diff budget_month],
  budget_accumulated_percent: %i[budget_accumulated_diff budget_accumulated],
  actual_month_previous_percent: %i[actual_month_previous_diff actual_month],
  actual_accumulated_previous_percent: %i[actual_accumulated_previous_diff actual_accumulated]
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.company_currenciesObject

Maps company ID (string) → native currency code, built from the database.
e.g. { "1" => "USD", "2" => "CAD", "4" => "EUR" }



25
26
27
28
# File 'app/controllers/crm/reports/budget_controller.rb', line 25

def self.company_currencies
  @company_currencies ||= Company.sales_companies.pluck(:id, :currency)
                                 .to_h { |id, cur| [id.to_s, cur] }
end

.consolidated_currencyObject

The consolidated (reporting) currency used when viewing multiple companies.
Reads from Company#consolidated_currency, falls back to the global constant.



32
33
34
# File 'app/controllers/crm/reports/budget_controller.rb', line 32

def self.consolidated_currency
  @consolidated_currency ||= Company.sales_companies.pick(:consolidated_currency).presence || CONSOLIDATED_CURRENCY
end

Instance Method Details

#childrenObject

Turbo Frame endpoint: returns child dimension rows on-demand when the
user expands a drill-down row. Avoids pre-rendering ~6,500 hidden
supplier-level rows in the initial HTML.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'app/controllers/crm/reports/budget_controller.rb', line 65

def children
  authorize!(:report, Budget)
  set_filter_params

  dimension_id = params[:dimension_id].to_i
  level = (params[:level] || 1).to_i
  parent_row_id = params[:parent_row_id]

  # Load budget facts for the current period
  @budget_facts = Analytic::BudgetFact
                  .where(month: @month, year: @year)
                  .index_by(&:budget_dimension_id)

  if @merge_companies
    merge_company_facts!
  elsif @convert_currency
    apply_currency_conversion!
  end

  # Load children of this dimension (select only needed columns)
  children = Analytic::BudgetDimension
             .where(company_id: @data_company_id, parent_id: dimension_id)
             .select(:id, :description, :parent_id, :budget_group_id, :supplier_id)
             .order(:description)
             .to_a

  # Check grandchildren existence for expand buttons (single query)
  child_ids = children.map(&:id)
  @dimension_children_by_parent = if child_ids.any?
                                    Analytic::BudgetDimension
                                      .where(company_id: @data_company_id, parent_id: child_ids)
                                      .select(:id, :description, :parent_id, :budget_group_id, :supplier_id)
                                      .order(:description)
                                      .group_by(&:parent_id)
                                  else
                                    {}
                                  end

  render partial: "budget_children_rows",
         locals: { children: children, level: level, parent_row_id: parent_row_id }
end

#showObject



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'app/controllers/crm/reports/budget_controller.rb', line 36

def show
  authorize!(:report, Budget)
  set_filter_params

  # Build cache key BEFORE loading heavy data.
  # On cache hit the view skips re-rendering the table, but we still need
  # the data for filter dropdowns and the "Last generated" badge.
  @budget_table_cache_key = budget_table_cache_key

  # Always load lightweight "last generated" timestamp for the badge
  @last_generated_at = Analytic::BudgetFact
                       .where(month: @month, year: @year)
                       .maximum(:created_at)

  # Only load heavy report data when the fragment cache is cold.
  # On cache hit this skips ~40k AR object instantiations + hash building.
  return if fragment_exist?(@budget_table_cache_key)

  load_budget_data
  if @merge_companies
    merge_company_facts!
  elsif @convert_currency
    apply_currency_conversion!
  end
end