9919183cb0
Also add eager loading for associated objects.
183 lines
7.8 KiB
Ruby
183 lines
7.8 KiB
Ruby
# 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 GroupOrderArticle < ActiveRecord::Base
|
|
extend ActiveSupport::Memoizable # Ability to cache method results. Use memoize :expensive_method
|
|
|
|
belongs_to :group_order
|
|
belongs_to :order_article
|
|
has_many :group_order_article_quantities, :dependent => :destroy
|
|
|
|
validates_presence_of :group_order, :order_article
|
|
validates_inclusion_of :quantity, :in => 0..99
|
|
validates_inclusion_of :result, :in => 0..99, :allow_nil => true
|
|
validates_inclusion_of :tolerance, :in => 0..99
|
|
validates_uniqueness_of :order_article_id, :scope => :group_order_id # just once an article per group order
|
|
|
|
scope :ordered, :conditions => 'result > 0'
|
|
|
|
localize_input_of :result
|
|
|
|
# Setter used in group_order_article#new
|
|
# We have to create an group_order, if the ordergroup wasn't involved in the order yet
|
|
def ordergroup_id=(id)
|
|
self.group_order = GroupOrder.find_or_initialize_by_order_id_and_ordergroup_id(order_article.order_id, id)
|
|
end
|
|
|
|
def ordergroup_id
|
|
group_order.try(:ordergroup_id)
|
|
end
|
|
|
|
# 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.
|
|
def update_quantities(quantity, tolerance)
|
|
logger.debug("GroupOrderArticle[#{id}].update_quantities(#{quantity}, #{tolerance})")
|
|
logger.debug("Current quantity = #{self.quantity}, tolerance = #{self.tolerance}")
|
|
|
|
# Get quantities ordered with the newest item first.
|
|
quantities = group_order_article_quantities.find(:all, :order => 'created_on desc')
|
|
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, self.tolerance = quantity, 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 'Invalid state: unable to update GroupOrderArticle/-Quantities to desired quantities!'
|
|
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
|
|
|
|
# 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.
|
|
def calculate_result
|
|
quantity = tolerance = 0
|
|
stockit = order_article.article.is_a?(StockArticle)
|
|
|
|
# Get total
|
|
total = stockit ? order_article.article.quantity : order_article.units_to_order * order_article.price.unit_quantity
|
|
logger.debug("<#{order_article.article.name}>.unitsToOrder => items ordered: #{order_article.units_to_order} => #{total}")
|
|
|
|
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.all(
|
|
:conditions => ["group_order_article_id IN (?)", order_article.group_order_article_ids], :order => 'created_on')
|
|
logger.debug("GroupOrderArticleQuantity records found: #{order_quantities.size}")
|
|
|
|
# Determine quantities to be ordered...
|
|
total_quantity = i = 0
|
|
while (i < order_quantities.size && total_quantity < total)
|
|
q = (order_quantities[i].quantity <= total - total_quantity ? order_quantities[i].quantity : total - total_quantity)
|
|
total_quantity += q
|
|
if (order_quantities[i].group_order_article_id == self.id)
|
|
logger.debug("increasing quantity by #{q}")
|
|
quantity += q
|
|
end
|
|
i += 1
|
|
end
|
|
|
|
# Determine tolerance to be ordered...
|
|
if (total_quantity < total)
|
|
logger.debug("determining additional items to be ordered from tolerance")
|
|
i = 0
|
|
while (i < order_quantities.size && total_quantity < total)
|
|
q = (order_quantities[i].tolerance <= total - total_quantity ? order_quantities[i].tolerance : total - total_quantity)
|
|
total_quantity += q
|
|
if (order_quantities[i].group_order_article_id == self.id)
|
|
logger.debug("increasing tolerance by #{q}")
|
|
tolerance += q
|
|
end
|
|
i += 1
|
|
end
|
|
end
|
|
|
|
logger.debug("determined quantity/tolerance/total: #{quantity} / #{tolerance} / #{quantity + tolerance}")
|
|
end
|
|
|
|
{:quantity => quantity, :tolerance => tolerance, :total => quantity + tolerance}
|
|
end
|
|
memoize :calculate_result
|
|
|
|
# Returns order result,
|
|
# either calcualted on the fly or fetched from result attribute
|
|
# Result is set when finishing the order.
|
|
def result(type = :total)
|
|
self[:result] || calculate_result[type]
|
|
end
|
|
|
|
# This is used during order.finish!.
|
|
def save_results!
|
|
self.update_attribute(:result, calculate_result[:total])
|
|
end
|
|
|
|
# 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.
|
|
def total_price(order_article = self.order_article)
|
|
unless order_article.order.finished?
|
|
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
|
|
|
|
end
|
|
|
|
|