Module: Finrb::Accounting

Defined in:
lib/finrb/accounting.rb,
sig/finrb.rbs

Overview

Inventory costing and depreciation calculations.

Class Method Summary collapse

Class Method Details

.cogs(uinv:, pinv:, units:, price:, sinv:, method: 'FIFO') ⇒ inventory_result

Cost of goods sold and ending inventory under three methods (FIFO,LIFO,Weighted average)

Examples:

Finrb::Accounting.cogs(uinv=2,pinv=2,units=[3,5],price=[3,5],sinv=7,method="FIFO")
Finrb::Accounting.cogs(uinv=2,pinv=2,units=[3,5],price=[3,5],sinv=7,method="LIFO")
Finrb::Accounting.cogs(uinv=2,pinv=2,units=[3,5],price=[3,5],sinv=7,method="WAC")

Parameters:

  • uinv

    units of beginning inventory

  • pinv

    price of beginning inventory

  • units

    nx1 vector of inventory units. inventory purchased ordered by time (from first to last)

  • price

    nx1 vector of inventory price. same order as units

  • sinv

    units of sold inventory

  • method (defaults to: 'FIFO')

    inventory methods: FIFO (first in first out, permitted under both US and IFRS), LIFO (late in first out, US only), WAC (weighted average cost,US and IFRS)

  • uinv: (number)
  • pinv: (number)
  • units: (number, numbers, nil)
  • price: (number, numbers, nil)
  • sinv: (number)
  • method: (String, Symbol) (defaults to: 'FIFO')

Returns:

  • (inventory_result)


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/finrb/accounting.rb', line 37

def self.cogs(uinv:, pinv:, units:, price:, sinv:, method: 'FIFO')
  uinv, pinv, units, price, sinv, method = inventory_inputs(uinv:, pinv:, units:, price:, sinv:, method:)

  n = units.size
  cost_of_goods = Flt::DecNum(0)
  ending_inventory = Flt::DecNum(0)
  case method
  when 'FIFO'
    if sinv <= uinv
      cost_of_goods = sinv * pinv
      ending_inventory = (uinv - sinv) * pinv
      (0...n).each do |i|
        ending_inventory += (units[i] * price[i])
      end
    else
      cost_of_goods = uinv * pinv
      sinv -= uinv
      (0...n).each do |i|
        if sinv <= units[i]
          cost_of_goods += (sinv * price[i])
          ending_inventory = (units[i] - sinv) * price[i]
          if i < n
            temp = i + 1
            (temp...n).each do |j|
              ending_inventory += (units[j] * price[j])
            end
          end
          sinv = 0
          break
        else
          cost_of_goods += (units[i] * price[i])
          sinv -= units[i]
        end
      end
      raise(DomainError, 'Available inventory is insufficient for the requested sale.') if sinv.positive?
    end
  when 'WAC'
    ending_inventory = uinv * pinv
    tu = uinv
    (0...n).each do |i|
      ending_inventory += (units[i] * price[i])
      tu += units[i]
    end
    if tu.zero? && sinv.zero?
      cost_of_goods = Flt::DecNum(0)
      ending_inventory = Flt::DecNum(0)
    elsif tu >= sinv
      cost_of_goods = ending_inventory / tu * sinv
      ending_inventory = ending_inventory / tu * (tu - sinv)
    else
      raise(DomainError, 'Available inventory is insufficient for the requested sale.')
    end

  when 'LIFO'
    (n - 1).downto(0).each do |i|
      if sinv <= units[i]
        cost_of_goods += (sinv * price[i])
        ending_inventory = (units[i] - sinv) * price[i]
        if i.positive?
          temp = i - 1
          temp.downto(0).each do |j|
            ending_inventory += (units[j] * price[j])
          end
        end
        ending_inventory += (uinv * pinv)
        sinv = 0
        break
      else
        cost_of_goods += (units[i] * price[i])
        sinv -= units[i]
      end
    end
    if sinv.positive?
      if sinv <= uinv
        cost_of_goods += (sinv * pinv)
        ending_inventory += ((uinv - sinv) * pinv)
      else
        raise(DomainError, 'Available inventory is insufficient for the requested sale.')
      end
    end
  end

  {
    cost_of_goods:,
    ending_inventory:
  }
end

.ddb(cost:, rv:, t:) ⇒ { t: Array[Integer], ddb: Array[decimal] }

Depreciation Expense Recognition -- double-declining balance (DDB), the most common declining balance method, which applies two times the straight-line rate to the declining balance.

Examples:

Finrb::Accounting.ddb(cost=1200,rv=200,t=5)

Parameters:

  • cost

    cost of long-lived assets

  • rv

    residual value of the long-lived assets at the end of its useful life. DDB does not explicitly use the asset's residual value in the calculations, but depreciation ends once the estimated residual value has been reached. If the asset is expected to have no residual value, the DB method will never fully depreciate it, so the DB method is typically changed to straight-line at some point in the asset's life.

  • t

    length of the useful life

  • cost: (number)
  • rv: (number)
  • t: (Integer)

Returns:

Raises:



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/finrb/accounting.rb', line 146

def self.ddb(cost:, rv:, t:)
  cost = Validation.non_negative_decimal(cost, name: 'asset cost')
  rv = residual_value(rv, cost:)
  t = Validation.positive_integer(t, name: 'useful life')
  raise(DomainError, 'Useful life must be at least 2 periods for double-declining depreciation.') if t < 2

  ddb = [Flt::DecNum(0)] * t
  ddb[0] = cost * 2 / t
  if cost - ddb.first <= rv
    ddb[0] = cost - rv
  else
    cost -= ddb.first
    (1...t).each do |i|
      ddb[i] = cost * 2 / t
      if cost - ddb[i] <= rv
        ddb[i] = cost - rv
        break
      else
        cost -= ddb[i]
      end
    end
  end
  { t: (0...t).to_a, ddb: }
end

.slde(cost:, rv:, t:) ⇒ decimal

Depreciation Expense Recognition -- Straight-line depreciation (SL) allocates an equal amount of depreciation each year over the asset's useful life

Examples:

Finrb::Accounting.slde(cost=1200,rv=200,t=5)

Parameters:

  • cost

    cost of long-lived assets

  • rv

    residual value of the long-lived assets at the end of its useful life

  • t

    length of the useful life

  • cost: (number)
  • rv: (number)
  • t: (number)

Returns:

  • (decimal)


178
179
180
181
182
183
184
# File 'lib/finrb/accounting.rb', line 178

def self.slde(cost:, rv:, t:)
  cost = Validation.non_negative_decimal(cost, name: 'asset cost')
  rv = residual_value(rv, cost:)
  t = Validation.positive_decimal(t, name: 'useful life', error: DomainError)

  ((cost - rv) / t)
end