Class: Report::ChannelRevenue::Command
- Inherits:
-
BaseCommand
- Object
- BaseCommand
- Report::ChannelRevenue::Command
- Defined in:
- app/services/report/channel_revenue/command.rb
Overview
Criteria + orchestration for the Channel Revenue report
(/reports/channel_revenue).
The report compares gross revenue per sales channel across two date
ranges: period 1 (the baseline) and period 2 (the current period). Two
preset selects drive the ranges so an in-progress month/quarter/year is
never silently compared against a full prior span: the current period
ends at Date.current and the comparison defaults to the same period last
year with matched day counts.
Constant Summary collapse
- PERIOD_PRESETS =
Period shortcut presets; they drive period 2 (the "current" period).
The to-date presets end at Date.current so an open month/quarter/year is
never silently compared against a full prior span. { 'month_to_date' => 'Month to date', 'last_month' => 'Last month', 'quarter_to_date' => 'Quarter to date', 'last_quarter' => 'Last quarter', 'year_to_date' => 'Year to date', 'last_year' => 'Last year' }.freeze
- COMPARISON_PRESETS =
Comparison presets; they drive period 1 (the baseline period).
"Matched days" keeps the same calendar day count in both periods,
"matched business days" keeps the same working-day count (Mon-Fri minus
US company holidays), and "full period" compares against the complete
calendar unit (month/quarter/year) the current period belongs to. { 'same_period_last_year' => 'Same period last year (matched days)', 'same_period_last_year_business_days' => 'Same period last year (matched business days)', 'same_period_last_year_full' => 'Same period last year (full period)', 'previous_period' => 'Previous period (matched days)', 'previous_period_full' => 'Previous period (full period)' }.freeze
- PERIOD_PRESET_UNITS =
Calendar unit each period preset belongs to. Needed for the "full
period" comparisons: quarter to date and month to date produce the same
dates early in a quarter, so the unit cannot always be inferred from
the dates alone. { 'month_to_date' => :month, 'last_month' => :month, 'quarter_to_date' => :quarter, 'last_quarter' => :quarter, 'year_to_date' => :year, 'last_year' => :year }.freeze
- CALENDAR_COMPANY_IDS =
Sales companies whose calendars appear in the business-day breakdown.
[Company::USA, Company::CAN, Company::NLD].freeze
Constants inherited from BaseCommand
Instance Attribute Summary
Attributes inherited from BaseCommand
Class Method Summary collapse
-
.comparison_preset_options ⇒ Array<Array(String, String)>
Options for the "Compare to" select in the criteria form.
-
.period_preset_options ⇒ Array<Array(String, String)>
Options for the "Period" select in the criteria form.
Instance Method Summary collapse
-
#business_day_breakdown(range) ⇒ Hash
Structured breakdown of business days in +range+ for the UI: count, named holidays on the matching calendar, per-day kind chips, and per-company counts so multi-company holiday differences are visible.
-
#calendar_companies ⇒ Array<Hash>
{ 'id' => Integer, 'label' => String } for the calendars shown in the per-company breakdown.
-
#execute ⇒ Hash?
Runs the report query when the criteria are valid.
-
#holiday_payload ⇒ Array<Hash>
Holidays for USA/CAN/NLD — all rows, not a sliding window.
-
#invoice_types=(new_invoice_types) ⇒ Array<String>
Dedupe and drop blank invoice types on assignment.
-
#matching_company_id ⇒ Integer
Holiday calendar that drives matched-business-day comparisons.
-
#matching_company_label ⇒ String
Short label for the matching calendar (e.g. "WY-CA").
-
#period1_humanized ⇒ String
Baseline range rendered for the results header.
-
#period1_locked? ⇒ Boolean
Whether the comparison date row is locked to the selected comparison preset (editable only when "Custom" is chosen).
-
#period1_range ⇒ Range
Baseline (comparison) date range.
-
#period1_working_days ⇒ Integer
Working days (Mon-Fri, US company holidays excluded) inside the baseline period.
-
#period2_humanized ⇒ String
Current period range rendered for the results header.
-
#period2_locked? ⇒ Boolean
Whether the current-period date row is locked to the selected period preset (editable only when "Custom" is chosen).
-
#period2_range ⇒ Range
Current period date range.
-
#period2_working_days ⇒ Integer
Working days (Mon-Fri, US company holidays excluded) inside the current period.
-
#success? ⇒ Boolean
True when the criteria are valid and the query ran.
-
#us_holiday_dates ⇒ Array<Date>
Matching-company holiday dates — used for matched-business-day comparisons.
Methods inherited from BaseCommand
#[], #attributes, #email_parameters, #email_partial, #initialize
Methods included from Heatwave::AttributeNormalizing
Constructor Details
This class inherits a constructor from Report::BaseCommand
Class Method Details
.comparison_preset_options ⇒ Array<Array(String, String)>
Options for the "Compare to" select in the criteria form.
89 90 91 |
# File 'app/services/report/channel_revenue/command.rb', line 89 def self. COMPARISON_PRESETS.map { |value, label| [label, value] } end |
.period_preset_options ⇒ Array<Array(String, String)>
Options for the "Period" select in the criteria form.
82 83 84 |
# File 'app/services/report/channel_revenue/command.rb', line 82 def self. PERIOD_PRESETS.map { |value, label| [label, value] } end |
Instance Method Details
#business_day_breakdown(range) ⇒ Hash
Structured breakdown of business days in +range+ for the UI: count,
named holidays on the matching calendar, per-day kind chips, and
per-company counts so multi-company holiday differences are visible.
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'app/services/report/channel_revenue/command.rb', line 228 def business_day_breakdown(range) empty = { 'count' => 0, 'matching_label' => matching_company_label, 'holidays' => [], 'days' => [], 'by_company' => {} } # begin/end, not first/last — first raises RangeError on a beginless # range (AppSignal #4933) return empty if range&.begin.blank? || range&.end.blank? match_id = matching_company_id matching_holidays = holiday_payload.select do |h| h['company_id'] == match_id && range.cover?(Date.iso8601(h['date'])) end holiday_by_date = matching_holidays.index_by { |h| h['date'] } days = range.map do |date| iso = date.iso8601 hol = holiday_by_date[iso] kind = if hol then 'holiday' elsif date.on_weekend? then 'weekend' else 'business' end { 'date' => iso, 'day' => date.day, 'kind' => kind, 'label' => hol ? hol['name'] : date.strftime('%a') } end # Only show companies that are selected on the report (or all calendars # when the multi-select is empty / defaults). selected = Array(company_ids).map(&:to_i) shown = calendar_companies.select { |c| selected.blank? || selected.include?(c['id']) } shown = calendar_companies if shown.empty? by_company = shown.to_h do |company| [company['label'], working_days_for_company(range, company['id'])] end { 'count' => working_days_in(range), 'matching_label' => matching_company_label, 'holidays' => matching_holidays, 'days' => days, 'by_company' => by_company } end |
#calendar_companies ⇒ Array<Hash>
Returns { 'id' => Integer, 'label' => String } for the
calendars shown in the per-company breakdown.
193 194 195 196 197 |
# File 'app/services/report/channel_revenue/command.rb', line 193 def calendar_companies @calendar_companies ||= Company.where(id: CALENDAR_COMPANY_IDS).map do |company| { 'id' => company.id, 'label' => company.short_name.presence || company.country_iso3 } end end |
#execute ⇒ Hash?
Runs the report query when the criteria are valid.
104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'app/services/report/channel_revenue/command.rb', line 104 def execute return unless valid? @results = Report::ChannelRevenue::Query.report( period1_range: period1_range, period2_range: period2_range, primary_sales_rep_ids: primary_sales_rep_ids.presence, invoice_types: invoice_types, company_ids: company_ids, consolidated_currency: consolidated_currency ) end |
#holiday_payload ⇒ Array<Hash>
Holidays for USA/CAN/NLD — all rows, not a sliding window. Custom report
ranges are unbounded, so truncating would desync the breakdown from
working_days_for_company (which reads the full calendar).
178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'app/services/report/channel_revenue/command.rb', line 178 def holiday_payload @holiday_payload ||= Company.where(id: CALENDAR_COMPANY_IDS).includes(:company_holidays).flat_map do |company| company.company_holidays.map do |holiday| { 'date' => holiday.holiday_date.iso8601, 'name' => holiday.holiday_name.to_s, 'company_id' => company.id, 'company' => company.short_name.presence || company.country_iso3 } end end end |
#invoice_types=(new_invoice_types) ⇒ Array<String>
Dedupe and drop blank invoice types on assignment.
97 98 99 |
# File 'app/services/report/channel_revenue/command.rb', line 97 def invoice_types=(new_invoice_types) super((new_invoice_types || []).filter_map(&:presence).uniq) end |
#matching_company_id ⇒ Integer
Holiday calendar that drives matched-business-day comparisons.
Sole selected company → that company's calendar; multiple → USA if
included, otherwise the first selected sales company.
204 205 206 207 208 209 210 211 |
# File 'app/services/report/channel_revenue/command.rb', line 204 def matching_company_id ids = Array(company_ids).map(&:to_i).select { |id| CALENDAR_COMPANY_IDS.include?(id) } ids = CALENDAR_COMPANY_IDS.dup if ids.empty? return ids.first if ids.size == 1 return Company::USA if ids.include?(Company::USA) ids.first end |
#matching_company_label ⇒ String
Returns short label for the matching calendar (e.g. "WY-CA").
275 276 277 |
# File 'app/services/report/channel_revenue/command.rb', line 275 def matching_company_label calendar_companies.find { |c| c['id'] == matching_company_id }&.dig('label') || 'USA' end |
#period1_humanized ⇒ String
Returns baseline range rendered for the results header.
128 129 130 |
# File 'app/services/report/channel_revenue/command.rb', line 128 def period1_humanized "From #{period1_range.first} until #{period1_range.last}" end |
#period1_locked? ⇒ Boolean
Whether the comparison date row is locked to the selected comparison
preset (editable only when "Custom" is chosen).
158 159 160 |
# File 'app/services/report/channel_revenue/command.rb', line 158 def period1_locked? COMPARISON_PRESETS.key?(comparison_preset) end |
#period1_range ⇒ Range
Returns baseline (comparison) date range.
118 119 120 |
# File 'app/services/report/channel_revenue/command.rb', line 118 def period1_range (period1_gteq && period1_lteq && period1_gteq)..period1_lteq end |
#period1_working_days ⇒ Integer
Returns working days (Mon-Fri, US company holidays excluded)
inside the baseline period.
139 140 141 |
# File 'app/services/report/channel_revenue/command.rb', line 139 def period1_working_days @period1_working_days ||= working_days_in(period1_range) end |
#period2_humanized ⇒ String
Returns current period range rendered for the results header.
133 134 135 |
# File 'app/services/report/channel_revenue/command.rb', line 133 def period2_humanized "From #{period2_range.first} until #{period2_range.last}" end |
#period2_locked? ⇒ Boolean
Whether the current-period date row is locked to the selected period
preset (editable only when "Custom" is chosen).
166 167 168 |
# File 'app/services/report/channel_revenue/command.rb', line 166 def period2_locked? PERIOD_PRESETS.key?(period_preset) end |
#period2_range ⇒ Range
Returns current period date range.
123 124 125 |
# File 'app/services/report/channel_revenue/command.rb', line 123 def period2_range (period2_gteq && period2_lteq && period2_gteq)..period2_lteq end |
#period2_working_days ⇒ Integer
Returns working days (Mon-Fri, US company holidays excluded)
inside the current period.
145 146 147 |
# File 'app/services/report/channel_revenue/command.rb', line 145 def period2_working_days @period2_working_days ||= working_days_in(period2_range) end |
#success? ⇒ Boolean
Returns true when the criteria are valid and the query ran.
150 151 152 |
# File 'app/services/report/channel_revenue/command.rb', line 150 def success? valid? && @results.present? end |
#us_holiday_dates ⇒ Array<Date>
Matching-company holiday dates — used for matched-business-day comparisons.
216 217 218 219 220 |
# File 'app/services/report/channel_revenue/command.rb', line 216 def us_holiday_dates holiday_payload .select { |h| h['company_id'] == matching_company_id } .map { |h| Date.iso8601(h['date']) } end |