Class: GroupOrderArticle

Inherits:
ApplicationRecord show all
Includes:
LocalizeInput
Defined in:
app/models/group_order_article.rb

Overview

A GroupOrderArticle stores the sum of how many items of an OrderArticle are ordered as part of a GroupOrder. The chronologically order of the Ordergroup - activity are stored in GroupOrderArticleQuantity

Class Method Summary collapse

Instance Method Summary collapse

Methods included from LocalizeInput

parse

Class Method Details

.ransackable_associations(_auth_object = nil) ⇒ Object



24
25
26
# File 'app/models/group_order_article.rb', line 24

def self.ransackable_associations(_auth_object = nil)
  %w[order_article group_order]
end

.ransackable_attributes(_auth_object = nil) ⇒ Object



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

def self.ransackable_attributes(_auth_object = nil)
  %w[id quantity tolerance result]
end

Instance Method Details

#calculate_result(total = nil) ⇒ Object

Determines how many items of this article the Ordergroup receives. Returns a hash with three keys: :quantity / :tolerance / :total

See description of the ordering algorithm in the general application documentation for details.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'app/models/group_order_article.rb', line 120

def calculate_result(total = nil)
  # return memoized result unless a total is given
  return @calculate_result if total.nil? && !@calculate_result.nil?

  quantity = tolerance = total_quantity = 0

  # Get total
  if !total.nil?
    logger.debug "<#{order_article.article.name}> => #{total} (given)"
  elsif order_article.article.is_a?(StockArticle)
    total = order_article.article.quantity
    logger.debug "<#{order_article.article.name}> (stock) => #{total}"
  else
    total = order_article.units_to_order * order_article.price.unit_quantity
    logger.debug "<#{order_article.article.name}> units_to_order #{order_article.units_to_order} => #{total}"
  end

  if total > 0
    # In total there are enough units ordered. Now check the individual result for the ordergroup (group_order).
    #
    # Get all GroupOrderArticleQuantities for this OrderArticle...
    order_quantities = GroupOrderArticleQuantity.where(group_order_article_id: order_article.group_order_article_ids).order('created_on')
    logger.debug "GroupOrderArticleQuantity records found: #{order_quantities.size}"

    first_order_first_serve = (FoodsoftConfig[:distribution_strategy] == FoodsoftConfig::DistributionStrategy::FIRST_ORDER_FIRST_SERVE)

    # Determine quantities to be ordered...
    order_quantities.each do |goaq|
      q = goaq.quantity
      q = [q, total - total_quantity].min if first_order_first_serve
      total_quantity += q
      if goaq.group_order_article_id == id
        logger.debug "increasing quantity by #{q}"
        quantity += q
      end
      break if total_quantity >= total && first_order_first_serve
    end

    # Determine tolerance to be ordered...
    if total_quantity < total
      logger.debug 'determining additional items to be ordered from tolerance'
      order_quantities.each do |goaq|
        q = [goaq.tolerance, total - total_quantity].min
        total_quantity += q
        if goaq.group_order_article_id == id
          logger.debug "increasing tolerance by #{q}"
          tolerance += q
        end
        break if total_quantity >= total
      end
    end

    logger.debug "determined quantity/tolerance/total: #{quantity} / #{tolerance} / #{quantity + tolerance}"
  end

  # memoize result unless a total is given
  r = { quantity: quantity, tolerance: tolerance, total: quantity + tolerance }
  @calculate_result = r if total.nil?
  r
end

#ordergroup_idObject



34
35
36
# File 'app/models/group_order_article.rb', line 34

def ordergroup_id
  group_order&.ordergroup_id
end

#ordergroup_id=(id) ⇒ Object

Setter used in group_order_article#new We have to create an group_order, if the ordergroup wasn’t involved in the order yet



30
31
32
# File 'app/models/group_order_article.rb', line 30

def ordergroup_id=(id)
  self.group_order = GroupOrder.where(order_id: order_article.order_id, ordergroup_id: id).first_or_initialize
end

#result(type = :total) ⇒ Object

Returns order result, either calcualted on the fly or fetched from result attribute Result is set when finishing the order.



184
185
186
# File 'app/models/group_order_article.rb', line 184

def result(type = :total)
  self[:result] || calculate_result[type]
end

#result_manually_changed?Boolean

Check if the result deviates from the result_computed

Returns:

  • (Boolean)


212
213
214
# File 'app/models/group_order_article.rb', line 212

def result_manually_changed?
  result != result_computed unless result.nil?
end

#save_results!(article_total = nil) ⇒ Object

This is used for automatic distribution, e.g., in order.finish! or when receiving orders



189
190
191
192
193
# File 'app/models/group_order_article.rb', line 189

def save_results!(article_total = nil)
  new_result = calculate_result(article_total)[:total]
  update_attribute(:result_computed, new_result)
  update_attribute(:result, new_result)
end

#total_price(order_article = self.order_article) ⇒ Object

Returns total price for this individual article Until the order is finished this will be the maximum price or the minimum price depending on configuration. When the order is finished it will be the value depending of the article results.



199
200
201
202
203
204
205
206
207
208
209
# File 'app/models/group_order_article.rb', line 199

def total_price(order_article = self.order_article)
  if order_article.order.open?
    if FoodsoftConfig[:tolerance_is_costly]
      order_article.article.fc_price * (quantity + tolerance)
    else
      order_article.article.fc_price * quantity
    end
  else
    order_article.price.fc_price * result
  end
end

#update_quantities(quantity, tolerance) ⇒ Object

Updates the quantity/tolerance for this GroupOrderArticle by updating both GroupOrderArticle properties and the associated GroupOrderArticleQuantities chronologically.

See description of the ordering algorithm in the general application documentation for details.



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
# File 'app/models/group_order_article.rb', line 42

def update_quantities(quantity, tolerance)
  logger.debug("GroupOrderArticle[#{id}].update_quantities(#{quantity}, #{tolerance})")
  logger.debug("Current quantity = #{self.quantity}, tolerance = #{self.tolerance}")

  # When quantity and tolerance are zero, we don't serve any purpose
  if quantity == 0 && tolerance == 0
    logger.debug('Self-destructing since requested quantity and tolerance are zero')
    destroy!
    return
  end

  # Get quantities ordered with the newest item first.
  quantities = group_order_article_quantities.order('created_on DESC').to_a
  logger.debug("GroupOrderArticleQuantity items found: #{quantities.size}")

  if quantities.size == 0
    # There is no GroupOrderArticleQuantity item yet, just insert with desired quantities...
    logger.debug('No quantities entry at all, inserting a new one with the desired quantities')
    quantities.push(GroupOrderArticleQuantity.new(group_order_article: self, quantity: quantity,
                                                  tolerance: tolerance))
    self.quantity = quantity
    self.tolerance = tolerance
  else
    # Decrease quantity/tolerance if necessary by going through the existing items and decreasing their values...
    i = 0
    while i < quantities.size && (quantity < self.quantity || tolerance < self.tolerance)
      logger.debug("Need to decrease quantities for GroupOrderArticleQuantity[#{quantities[i].id}]")
      if quantity < self.quantity && quantities[i].quantity > 0
        delta = self.quantity - quantity
        delta = [delta, quantities[i].quantity].min
        logger.debug("Decreasing quantity by #{delta}")
        quantities[i].quantity -= delta
        self.quantity -= delta
      end
      if tolerance < self.tolerance && quantities[i].tolerance > 0
        delta = self.tolerance - tolerance
        delta = [delta, quantities[i].tolerance].min
        logger.debug("Decreasing tolerance by #{delta}")
        quantities[i].tolerance -= delta
        self.tolerance -= delta
      end
      i += 1
    end
    # If there is at least one increased value: insert a new GroupOrderArticleQuantity object
    if quantity > self.quantity || tolerance > self.tolerance
      logger.debug('Inserting a new GroupOrderArticleQuantity')
      quantities.insert(0, GroupOrderArticleQuantity.new(
                             group_order_article: self,
                             quantity: (quantity > self.quantity ? quantity - self.quantity : 0),
                             tolerance: (tolerance > self.tolerance ? tolerance - self.tolerance : 0)
                           ))
      # Recalc totals:
      self.quantity += quantities[0].quantity
      self.tolerance += quantities[0].tolerance
    end
  end

  # Check if something went terribly wrong and quantites have not been adjusted as desired.
  if self.quantity != quantity || self.tolerance != tolerance
    raise ActiveRecord::RecordNotSaved.new('Unable to update GroupOrderArticle/-Quantities to desired quantities!',
                                           self)
  end

  # Remove zero-only items.
  quantities = quantities.reject { |q| q.quantity == 0 && q.tolerance == 0 }

  # Save
  transaction do
    quantities.each { |i| i.save! }
    self.group_order_article_quantities = quantities
    save!
  end
end