Class: FinModeling::ReformulatedIncomeStatement

Inherits:
Object
  • Object
show all
Defined in:
lib/finmodeling/reformulated_income_statement.rb

Direct Known Subclasses

ForecastedReformulatedIncomeStatement

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(period, net_income_summary, comprehensive_income_summary, tax_rate = 0.35) ⇒ ReformulatedIncomeStatement

FIXME: clarify naming. This is effective tax rate? marginal? statutory?



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
# File 'lib/finmodeling/reformulated_income_statement.rb', line 6

def initialize(period, net_income_summary, comprehensive_income_summary, tax_rate=0.35) # FIXME: clarify naming. This is effective tax rate? marginal? statutory?
  @period   = period
  @tax_rate = tax_rate
 
  @operating_revenues = net_income_summary.filter_by_type(:or  )
  @cost_of_revenues   = net_income_summary.filter_by_type(:cogs)
  @operating_expenses = net_income_summary.filter_by_type(:oe  )
  @oibt      = net_income_summary.filter_by_type(:oibt     )
  @fibt      = net_income_summary.filter_by_type(:fibt     )
  @tax       = net_income_summary.filter_by_type(:tax      )
  @ooiat     = net_income_summary.filter_by_type(:ooiat    )
  @ooiat_nci = net_income_summary.filter_by_type(:ooiat_nci)
  @fiat      = net_income_summary.filter_by_type(:fiat     )

  # If there is a CI statement:
  #   Strip O-NCI-OCI out of the NI calculation (***)
  #   Double-check that the NI portion of the CI calculation matches (NI minus O-NCI-OCI)
  #   Add OOCI + 1/2 of the UNKOCI in as an OOIAT
  #   Add FOCI + 1/2 of the UNKOCI in as a FIAT
  if comprehensive_income_summary
    @ooiat.rows += comprehensive_income_summary.filter_by_type(:ooci).rows
    @ooiat.rows += comprehensive_income_summary.filter_by_type(:ooci_nci).rows
    @fiat.rows  += comprehensive_income_summary.filter_by_type(:foci).rows
    comprehensive_income_summary.filter_by_type(:unkoci).rows.each do |row|
      row.vals = row.vals.map{ |val| val / 2.0 }
      @ooiat.rows << row
      @fiat.rows << row
    end
  end

  @fibt_tax_effect = (@fibt.total * @tax_rate).round.to_f
  @nfi = @fibt.total + -@fibt_tax_effect + @fiat.total

  @oibt_tax_effect = (@oibt.total * @tax_rate).round.to_f

  @gm = @operating_revenues.total + @cost_of_revenues.total
  @oisbt = @gm + @operating_expenses.total

  @oisat = @oisbt + @tax.total + @fibt_tax_effect + @oibt_tax_effect

  @oi = @oisat + @oibt.total - @oibt_tax_effect + @ooiat.total

  @ci = @nfi + @oi
end

Instance Attribute Details

#cost_of_revenuesObject

Returns the value of attribute cost_of_revenues.



4
5
6
# File 'lib/finmodeling/reformulated_income_statement.rb', line 4

def cost_of_revenues
  @cost_of_revenues
end

#operating_expensesObject

Returns the value of attribute operating_expenses.



4
5
6
# File 'lib/finmodeling/reformulated_income_statement.rb', line 4

def operating_expenses
  @operating_expenses
end

#operating_revenuesObject

Returns the value of attribute operating_revenues.



4
5
6
# File 'lib/finmodeling/reformulated_income_statement.rb', line 4

def operating_revenues
  @operating_revenues
end

#periodObject

Returns the value of attribute period.



3
4
5
# File 'lib/finmodeling/reformulated_income_statement.rb', line 3

def period
  @period
end

Class Method Details

.empty_analysisObject



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/finmodeling/reformulated_income_statement.rb', line 159

def self.empty_analysis
  analysis = CalculationSummary.new
  analysis.title = ""
  analysis.rows = []

  analysis.header_row = CalculationHeader.new(:key => "",    :vals => ["Unknown..."])

  analysis.rows << CalculationRow.new(:key => "Revenue ($MM)",  :vals => [nil])
  if Config.income_detail_enabled?
    analysis.rows << CalculationRow.new(:key => "COGS ($MM)",   :vals => [nil])
    analysis.rows << CalculationRow.new(:key => "GM ($MM)",     :vals => [nil])
    analysis.rows << CalculationRow.new(:key => "OE ($MM)",     :vals => [nil])
    analysis.rows << CalculationRow.new(:key => "OISBT ($MM)",  :vals => [nil])
  end
  analysis.rows << CalculationRow.new(:key => "Core OI ($MM)",  :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "OI ($MM)",       :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "FI ($MM)",       :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "NI ($MM)",       :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "Gross Margin",   :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "Sales PM",       :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "Operating PM",   :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "FI / Sales",     :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "NI / Sales",     :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "Sales / NOA",    :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "FI / NFA",       :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "Revenue Growth", :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "Core OI Growth", :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "OI Growth",      :vals => [nil])
  analysis.rows << CalculationRow.new(:key => "ReOI ($MM)",     :vals => [nil])

  return analysis
end

.forecast_next(period, policy, last_re_bs, last_re_is) ⇒ Object



239
240
241
242
243
244
245
246
247
248
# File 'lib/finmodeling/reformulated_income_statement.rb', line 239

def self.forecast_next(period, policy, last_re_bs, last_re_is)
  operating_revenues = policy.revenue_on(period.value["end_date"])
  income_from_sales_after_tax = operating_revenues * policy.sales_pm_on(period.value["end_date"])
  net_financing_income = last_re_bs.net_financial_assets.total * Ratio.new(policy.fi_over_nfa_on(period.value["end_date"])).yearly_to_quarterly
  comprehensive_income = income_from_sales_after_tax + net_financing_income

  ForecastedReformulatedIncomeStatement.new(period, operating_revenues, 
                                            income_from_sales_after_tax,
                                            net_financing_income, comprehensive_income)
end

Instance Method Details

#-(ris2) ⇒ Object



51
52
53
54
# File 'lib/finmodeling/reformulated_income_statement.rb', line 51

def -(ris2)
  net_income_summary = NetIncomeSummaryFromDifferences.new(self, ris2)
  return ReformulatedIncomeStatement.new(@period, net_income_summary, nil, @tax_rate)
end

#analysis(re_bs, prev_re_is, prev_re_bs, expected_rate_of_return) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/finmodeling/reformulated_income_statement.rb', line 192

def analysis(re_bs, prev_re_is, prev_re_bs, expected_rate_of_return)
  analysis = CalculationSummary.new
  analysis.title = ""
  analysis.rows = []
  
  if re_bs.nil?
    analysis.header_row = CalculationHeader.new(:key => "",   :vals => ["Unknown..."])
  else
    analysis.header_row = CalculationHeader.new(:key => "",   :vals => [re_bs.period.to_pretty_s])
  end
  
  analysis.rows << CalculationRow.new(:key => "Revenue ($MM)",   :vals => [@operating_revenues.total.to_nearest_million])
  if Config.income_detail_enabled?
    analysis.rows << CalculationRow.new(:key => "COGS ($MM)",    :vals => [@cost_of_revenues.total.to_nearest_million])
    analysis.rows << CalculationRow.new(:key => "GM ($MM)",      :vals => [@gm.to_nearest_million])
    analysis.rows << CalculationRow.new(:key => "OE ($MM)",      :vals => [@operating_expenses.total.to_nearest_million])
    analysis.rows << CalculationRow.new(:key => "OISBT ($MM)",   :vals => [income_from_sales_before_tax.total.to_nearest_million])
  end
  analysis.rows << CalculationRow.new(:key => "Core OI ($MM)",   :vals => [income_from_sales_after_tax.total.to_nearest_million])
  analysis.rows << CalculationRow.new(:key => "OI ($MM)",        :vals => [operating_income_after_tax.total.to_nearest_million])
  analysis.rows << CalculationRow.new(:key => "FI ($MM)",        :vals => [net_financing_income.total.to_nearest_million])
  analysis.rows << CalculationRow.new(:key => "NI ($MM)",        :vals => [comprehensive_income.total.to_nearest_million])
  analysis.rows << CalculationRow.new(:key => "Gross Margin",    :vals => [gross_margin])
  analysis.rows << CalculationRow.new(:key => "Sales PM",        :vals => [sales_profit_margin])
  analysis.rows << CalculationRow.new(:key => "Operating PM",    :vals => [operating_profit_margin])
  analysis.rows << CalculationRow.new(:key => "FI / Sales",      :vals => [fi_over_sales])
  analysis.rows << CalculationRow.new(:key => "NI / Sales",      :vals => [ni_over_sales])

  if !prev_re_bs.nil? && !prev_re_is.nil?
    analysis.rows << CalculationRow.new(:key => "Sales / NOA",   :vals => [sales_over_noa(prev_re_bs)])
    analysis.rows << CalculationRow.new(:key => "FI / NFA",      :vals => [fi_over_nfa(   prev_re_bs)])
    analysis.rows << CalculationRow.new(:key => "Revenue Growth",:vals => [revenue_growth(prev_re_is)])
    analysis.rows << CalculationRow.new(:key => "Core OI Growth",:vals => [core_oi_growth(prev_re_is)])
    analysis.rows << CalculationRow.new(:key => "OI Growth",     :vals => [oi_growth(     prev_re_is)])
    analysis.rows << CalculationRow.new(:key => "ReOI ($MM)",    :vals => [re_oi(prev_re_bs, expected_rate_of_return).to_nearest_million])
  else
    analysis.rows << CalculationRow.new(:key => "Sales / NOA",   :vals => [nil])
    analysis.rows << CalculationRow.new(:key => "FI / NFA",      :vals => [nil])
    analysis.rows << CalculationRow.new(:key => "Revenue Growth",:vals => [nil])
    analysis.rows << CalculationRow.new(:key => "Core OI Growth",:vals => [nil])
    analysis.rows << CalculationRow.new(:key => "OI Growth",     :vals => [nil])
    analysis.rows << CalculationRow.new(:key => "ReOI ($MM)",    :vals => [nil])
  end
  
  return analysis
end

#comprehensive_incomeObject



101
102
103
104
105
106
107
# File 'lib/finmodeling/reformulated_income_statement.rb', line 101

def comprehensive_income
  cs = FinModeling::CalculationSummary.new
  cs.title = "Comprehensive income (CI)"
  cs.rows = [ CalculationRow.new(:key => "Operating income, after tax (OI)", :vals => [@oi] ),
              CalculationRow.new(:key => "Net financing income, after tax (NFI)", :vals => [@nfi] ) ]
  return cs
end

#core_oi_growth(prev) ⇒ Object



144
145
146
147
# File 'lib/finmodeling/reformulated_income_statement.rb', line 144

def core_oi_growth(prev)
  rate = (income_from_sales_after_tax.total - prev.income_from_sales_after_tax.total) / prev.income_from_sales_after_tax.total
  return annualize_rate(prev, rate)
end

#fi_over_nfa(reformed_bal_sheet) ⇒ Object



134
135
136
137
# File 'lib/finmodeling/reformulated_income_statement.rb', line 134

def fi_over_nfa(reformed_bal_sheet)
  ratio = net_financing_income.total / reformed_bal_sheet.net_financial_assets.total
  Ratio.new(ratio).annualize(from_days=@period.days, to_days=365.0)
end

#fi_over_salesObject



121
122
123
# File 'lib/finmodeling/reformulated_income_statement.rb', line 121

def fi_over_sales
  net_financing_income.total / operating_revenues.total
end

#gross_marginObject



109
110
111
# File 'lib/finmodeling/reformulated_income_statement.rb', line 109

def gross_margin
  gross_revenue.total / operating_revenues.total
end

#gross_revenueObject



56
57
58
59
60
61
62
# File 'lib/finmodeling/reformulated_income_statement.rb', line 56

def gross_revenue
  cs = FinModeling::CalculationSummary.new
  cs.title = "Gross Revenue"
  cs.rows = [ CalculationRow.new(:key => "Operating Revenues (OR)",   :vals => [@operating_revenues.total] ),
              CalculationRow.new(:key => "Cost of Goods Sold (COGS)", :vals => [@cost_of_revenues.total] ) ]
  return cs
end

#income_from_sales_after_taxObject



72
73
74
75
76
77
78
79
80
# File 'lib/finmodeling/reformulated_income_statement.rb', line 72

def income_from_sales_after_tax
  cs = FinModeling::CalculationSummary.new
  cs.title = "Operating Income from sales, after tax (OISAT)"
  cs.rows = [ CalculationRow.new(:key => "Operating income from sales (before tax)", :vals => [@oisbt] ),
              CalculationRow.new(:key => "Reported taxes", :vals => [@tax.total] ),
              CalculationRow.new(:key => "Taxes on net financing income", :vals => [@fibt_tax_effect] ),
              CalculationRow.new(:key => "Taxes on other operating income", :vals => [@oibt_tax_effect] ) ]
  return cs
end

#income_from_sales_before_taxObject



64
65
66
67
68
69
70
# File 'lib/finmodeling/reformulated_income_statement.rb', line 64

def income_from_sales_before_tax
  cs = FinModeling::CalculationSummary.new
  cs.title = "Operating Income from sales, before tax (OISBT)"
  cs.rows = [ CalculationRow.new(:key => "Gross Margin (GM)", :vals => [@gm] ),
              CalculationRow.new(:key => "Operating Expense (OE)", :vals => [@operating_expenses.total] ) ]
  return cs
end

#net_financing_incomeObject



92
93
94
95
96
97
98
99
# File 'lib/finmodeling/reformulated_income_statement.rb', line 92

def net_financing_income
  cs = FinModeling::CalculationSummary.new
  cs.title = "Net financing income, after tax (NFI)"
  cs.rows = [ CalculationRow.new(:key => "Financing income, before tax (FIBT)", :vals => [@fibt.total] ),
              CalculationRow.new(:key => "Tax effect (FIBT_TAX_EFFECT)", :vals => [-@fibt_tax_effect] ),
              CalculationRow.new(:key => "Financing income, after tax (FIAT)", :vals => [@fiat.total] ) ]
  return cs
end

#ni_over_salesObject



125
126
127
# File 'lib/finmodeling/reformulated_income_statement.rb', line 125

def ni_over_sales
  comprehensive_income.total / operating_revenues.total
end

#oi_growth(prev) ⇒ Object



149
150
151
152
# File 'lib/finmodeling/reformulated_income_statement.rb', line 149

def oi_growth(prev)
  rate = (operating_income_after_tax.total - prev.operating_income_after_tax.total) / prev.operating_income_after_tax.total
  return annualize_rate(prev, rate)
end

#operating_income_after_taxObject



82
83
84
85
86
87
88
89
90
# File 'lib/finmodeling/reformulated_income_statement.rb', line 82

def operating_income_after_tax
  cs = FinModeling::CalculationSummary.new
  cs.title = "Operating income, after tax (OI)"
  cs.rows = [ CalculationRow.new(:key => "Operating income after sales, after tax (OISAT)", :vals => [@oisat] ),
              CalculationRow.new(:key => "Other operating income, before tax (OIBT)", :vals => [@oibt.total] ),
              CalculationRow.new(:key => "Tax on other operating income", :vals => [-@oibt_tax_effect] ),
              CalculationRow.new(:key => "Other operating income, after tax (OOIAT)", :vals => [@ooiat.total] ) ]
  return cs
end

#operating_profit_marginObject



117
118
119
# File 'lib/finmodeling/reformulated_income_statement.rb', line 117

def operating_profit_margin
  operating_income_after_tax.total / operating_revenues.total
end

#re_oi(prev_bal_sheet, expected_rate_of_return) ⇒ Object



154
155
156
157
# File 'lib/finmodeling/reformulated_income_statement.rb', line 154

def re_oi(prev_bal_sheet, expected_rate_of_return)
  e_ror = deannualize_rate(prev_bal_sheet, expected_rate_of_return)
  return (operating_income_after_tax.total - (e_ror * prev_bal_sheet.net_operating_assets.total))
end

#revenue_growth(prev) ⇒ Object



139
140
141
142
# File 'lib/finmodeling/reformulated_income_statement.rb', line 139

def revenue_growth(prev)
  rate = (operating_revenues.total - prev.operating_revenues.total) / prev.operating_revenues.total
  return annualize_rate(prev, rate)
end

#sales_over_noa(reformed_bal_sheet) ⇒ Object



129
130
131
132
# File 'lib/finmodeling/reformulated_income_statement.rb', line 129

def sales_over_noa(reformed_bal_sheet)
  ratio = operating_revenues.total / reformed_bal_sheet.net_operating_assets.total
  Ratio.new(ratio).annualize(from_days=@period.days, to_days=365.0)
end

#sales_profit_marginObject



113
114
115
# File 'lib/finmodeling/reformulated_income_statement.rb', line 113

def sales_profit_margin
  income_from_sales_after_tax.total / operating_revenues.total
end