Module: Models::Profitable

Extended by:
ActiveSupport::Concern
Includes:
Memery
Included in:
Delivery, Invoice, Order, Quote
Defined in:
app/concerns/models/profitable.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#min_profit_markupObject (readonly)



10
# File 'app/concerns/models/profitable.rb', line 10

validates :min_profit_markup, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, if: :validate_min_profit_markup?

Instance Method Details

#default_sales_markupObject



20
21
22
# File 'app/concerns/models/profitable.rb', line 20

def default_sales_markup
  30
end

#profit_margins_met?Boolean

Returns:

  • (Boolean)


102
103
104
105
106
107
108
# File 'app/concerns/models/profitable.rb', line 102

def profit_margins_met?
  return true unless track_profit?
  return true if min_profit_markup.zero?

  pm = (profitable_total_profit_markup * 100).to_i
  pm >= min_profit_markup
end

#profitable_line_itemsObject



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'app/concerns/models/profitable.rb', line 24

def profitable_line_items
  items = line_items.where("line_items.tax_class = 'g' OR (line_items.tax_class = 'shp' and line_items.price > 0)")
                    .parents_only
                    .includes({ catalog_item: :store_item }, :item, :shipping_cost, :line_discounts)

  # Exclude shipping line items where charged shipping >= actual shipping cost
  # Only include shipping in profit calculation when we're losing money on it
  items.reject do |li|
    next false unless li.is_shipping?

    actual_cost = li.shipping_cost&.rate_data&.dig('actual_cost').to_f
    charged = li.discounted_price.to_f
    actual_cost > 0 && charged >= actual_cost
  end
end

#profitable_statusObject



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'app/concerns/models/profitable.rb', line 81

def profitable_status
  # If there's nothing to calculate 0 /0 then its ok
  return :ok if profitable_total_profit.zero? && profitable_total_discounted.zero?
  # Shortcut, if we don't meet the profit margins, its always an alert
  return :alert unless profit_margins_met?

  tm = profitable_total_profit_markup
  if tm >= 0.60
    :ok
  elsif (tm * 100).to_i > default_sales_markup
    :warning
  else
    :alert
  end
end

#profitable_total_discountedObject



40
41
42
# File 'app/concerns/models/profitable.rb', line 40

def profitable_total_discounted
  profitable_line_items.map(&:discounted_total).compact.sum
end

#profitable_total_estimated_costObject



45
46
47
# File 'app/concerns/models/profitable.rb', line 45

def profitable_total_estimated_cost
  profitable_line_items.map(&:estimated_line_cost).compact.sum
end

#profitable_total_estimated_line_costObject



55
56
57
# File 'app/concerns/models/profitable.rb', line 55

def profitable_total_estimated_line_cost
  profitable_line_items.map(&:estimated_line_cost).compact.sum
end

#profitable_total_profitObject

AKA Gross Profit



50
51
52
# File 'app/concerns/models/profitable.rb', line 50

def profitable_total_profit # AKA Gross Profit
  profitable_line_items.map(&:current_line_profit).compact.sum
end

#profitable_total_profit_marginObject

To find the margin, divide gross profit by the revenue.



61
62
63
64
65
66
67
68
# File 'app/concerns/models/profitable.rb', line 61

def profitable_total_profit_margin
  gross_profit = profitable_total_profit
  revenue = profitable_total_discounted
  return 0.0 if revenue.zero? && gross_profit == 0
  return -1.0 if revenue == 0

  (gross_profit / revenue).round(4)
end

#profitable_total_profit_markupObject

To write the markup as a percentage, divide the gross profit by the COGS.



72
73
74
75
76
77
78
# File 'app/concerns/models/profitable.rb', line 72

def profitable_total_profit_markup
  gross_profit = profitable_total_profit
  cogs = profitable_total_estimated_line_cost
  return 0.0 if cogs.zero? # Should not happen item never cost nothing but you never know

  (gross_profit / cogs).round(4)
end

#track_profit?Boolean

Returns:

  • (Boolean)


98
99
100
# File 'app/concerns/models/profitable.rb', line 98

def track_profit?
  has_attribute?(:min_profit_markup) && profitable_line_items.present? && (is_a?(Order) || is_a?(Quote))
end

#validate_min_profit_markup?Boolean

Returns:

  • (Boolean)


13
14
15
16
17
18
# File 'app/concerns/models/profitable.rb', line 13

def validate_min_profit_markup?
  return false unless has_attribute?(:min_profit_markup)
  return false if try(:invoiced?)

  true
end