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



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

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

.ransackable_attributes(_auth_object = nil) ⇒ Object



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

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.



119
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 119

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_version.name}> => #{total} (given)"
  elsif order_article.article_version.is_a?(StockArticle)
    total = order_article.article_version.quantity
    logger.debug "<#{order_article.article_version.name}> (stock) => #{total}"
  else
    total = order_article.article_version.convert_quantity(order_article.units_to_order,
                                                           order_article.article_version.supplier_order_unit, order_article.article_version.group_order_unit)
    logger.debug "<#{order_article.article_version.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



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

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



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

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)


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

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

def total_price(order_article = self.order_article)
  group_order_price = order_article.article_version.fc_group_order_price
  if order_article.order.open?
    if FoodsoftConfig[:tolerance_is_costly]
      group_order_price * (quantity + tolerance)
    else
      group_order_price * quantity
    end
  else
    group_order_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.



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

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 ? quantities[i].quantity : delta)
        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 ? quantities[i].tolerance : delta)
        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