Order-refactoring part II.
This commit is contained in:
parent
f7b9582261
commit
6fd5d825f9
27 changed files with 228 additions and 231 deletions
|
|
@ -76,34 +76,9 @@ class Article < ActiveRecord::Base
|
|||
updated_at > 2.days.ago
|
||||
end
|
||||
|
||||
# Returns how many units of this article need to be ordered given the specified order quantity and tolerance.
|
||||
# This is determined by calculating how many units can be ordered from the given order quantity, using
|
||||
# the tolerance to order an additional unit if the order quantity is not quiet sufficient.
|
||||
# There must always be at least one item in a unit that is an ordered quantity (no units are ever entirely
|
||||
# filled by tolerance items only).
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# unit_quantity | quantity | tolerance | calculate_order_quantity
|
||||
# --------------+----------+-----------+-----------------------
|
||||
# 4 | 0 | 2 | 0
|
||||
# 4 | 0 | 5 | 0
|
||||
# 4 | 2 | 2 | 1
|
||||
# 4 | 4 | 2 | 1
|
||||
# 4 | 4 | 4 | 1
|
||||
# 4 | 5 | 3 | 2
|
||||
# 4 | 5 | 4 | 2
|
||||
#
|
||||
def calculate_order_quantity(quantity, tolerance = 0)
|
||||
unit_size = unit_quantity
|
||||
units = quantity / unit_size
|
||||
remainder = quantity % unit_size
|
||||
units += ((remainder > 0) && (remainder + tolerance >= unit_size) ? 1 : 0)
|
||||
end
|
||||
|
||||
# If the article is used in an open Order, the Order will be returned.
|
||||
def in_open_order
|
||||
order_articles = OrderArticle.all(:conditions => ['order_id IN (?)', Order.open.collect {|o| o.id }])
|
||||
order_articles = OrderArticle.all(:conditions => ['order_id IN (?)', Order.open.collect(&:id)])
|
||||
order_article = order_articles.detect {|oa| oa.article_id == id }
|
||||
order_article ? order_article.order : nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# == Schema Information
|
||||
# Schema version: 20090119155930
|
||||
# Schema version: 20090120184410
|
||||
#
|
||||
# Table name: group_order_articles
|
||||
#
|
||||
|
|
@ -9,13 +9,16 @@
|
|||
# quantity :integer default(0), not null
|
||||
# tolerance :integer default(0), not null
|
||||
# updated_on :datetime not null
|
||||
# quantity_result :integer
|
||||
# tolerance_result :integer
|
||||
#
|
||||
|
||||
# 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
|
||||
|
|
@ -27,6 +30,8 @@ class GroupOrderArticle < ActiveRecord::Base
|
|||
|
||||
attr_accessor :ordergroup_id # To create an new GroupOrder if neccessary
|
||||
|
||||
named_scope :ordered, :conditions => 'quantity_result > 0 OR tolerance_result > 0'
|
||||
|
||||
# Updates the quantity/tolerance for this GroupOrderArticle by updating both GroupOrderArticle properties
|
||||
# and the associated GroupOrderArticleQuantities chronologically.
|
||||
#
|
||||
|
|
@ -99,25 +104,27 @@ class GroupOrderArticle < ActiveRecord::Base
|
|||
# Returns a hash with three keys: :quantity / :tolerance / :total
|
||||
#
|
||||
# See description of the ordering algorithm in the general application documentation for details.
|
||||
def orderResult
|
||||
def calculate_result
|
||||
quantity = tolerance = 0
|
||||
|
||||
# Get total
|
||||
total = order_article.units_to_order * order_article.article.unit_quantity
|
||||
logger.debug("unitsToOrder => items ordered: #{order_article.units_to_order} => #{total}")
|
||||
total = 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...
|
||||
orderArticles = GroupOrderArticle.find(:all, :conditions => ['order_article_id = ? AND group_order_id IN (?)', order_article.id, group_order.order.group_orders.collect { | o | o.id }])
|
||||
orderQuantities = GroupOrderArticleQuantity.find(:all, :conditions => ['group_order_article_id IN (?)', orderArticles.collect { | i | i.id }], :order => 'created_on')
|
||||
logger.debug("GroupOrderArticleQuantity records found: #{orderQuantities.size}")
|
||||
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...
|
||||
totalQuantity = i = 0
|
||||
while (i < orderQuantities.size && totalQuantity < total)
|
||||
q = (orderQuantities[i].quantity <= total - totalQuantity ? orderQuantities[i].quantity : total - totalQuantity)
|
||||
totalQuantity += q
|
||||
if (orderQuantities[i].group_order_article_id == self.id)
|
||||
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
|
||||
|
|
@ -125,13 +132,13 @@ class GroupOrderArticle < ActiveRecord::Base
|
|||
end
|
||||
|
||||
# Determine tolerance to be ordered...
|
||||
if (totalQuantity < total)
|
||||
if (total_quantity < total)
|
||||
logger.debug("determining additional items to be ordered from tolerance")
|
||||
i = 0
|
||||
while (i < orderQuantities.size && totalQuantity < total)
|
||||
q = (orderQuantities[i].tolerance <= total - totalQuantity ? orderQuantities[i].tolerance : total - totalQuantity)
|
||||
totalQuantity += q
|
||||
if (orderQuantities[i].group_order_article_id == self.id)
|
||||
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
|
||||
|
|
@ -139,13 +146,27 @@ class GroupOrderArticle < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
# calculate the sum of quantity and tolerance:
|
||||
sum = quantity + tolerance
|
||||
|
||||
logger.debug("determined quantity/tolerance/total: #{quantity} / #{tolerance} / #{sum}")
|
||||
logger.debug("determined quantity/tolerance/total: #{quantity} / #{tolerance} / #{quantity + tolerance}")
|
||||
end
|
||||
|
||||
{:quantity => quantity, :tolerance => tolerance, :total => sum}
|
||||
{:quantity => quantity, :tolerance => tolerance, :total => quantity + tolerance}
|
||||
end
|
||||
memoize :calculate_result
|
||||
|
||||
# Returns order result,
|
||||
# either calcualted on the fly or fetched from quantity_/tolerance_result
|
||||
def result
|
||||
if quantity_result.nil?
|
||||
calculate_result
|
||||
else
|
||||
{:quantity => quantity_result, :tolerance => tolerance_result, :total => quantity_result + tolerance_result}
|
||||
end
|
||||
end
|
||||
|
||||
# This is used when finishing the order.
|
||||
def save_results!
|
||||
self.quantity_result = calculate_result[:quantity]
|
||||
self.tolerance_result = calculate_result[:tolerance]
|
||||
save!
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -100,11 +100,11 @@ class Order < ActiveRecord::Base
|
|||
# :fc, guess what...
|
||||
def sum(type = :gross)
|
||||
total = 0
|
||||
if type == :clear || type == :gross || type == :fc
|
||||
if type == :net || type == :gross || type == :fc
|
||||
for oa in order_articles.ordered.all(:include => [:article,:article_price])
|
||||
quantity = oa.units_to_order * oa.price.unit_quantity
|
||||
case type
|
||||
when :clear
|
||||
when :net
|
||||
total += quantity * oa.price.price
|
||||
when :gross
|
||||
total += quantity * oa.price.gross_price
|
||||
|
|
@ -133,51 +133,26 @@ class Order < ActiveRecord::Base
|
|||
unless finished?
|
||||
Order.transaction do
|
||||
# Update order_articles. Save the current article_price to keep price consistency
|
||||
# Also save results for each group_order_result
|
||||
order_articles.all(:include => :article).each do |oa|
|
||||
oa.update_attribute(:article_price, oa.article.article_prices.first)
|
||||
oa.group_order_articles.each { |goa| goa.save_results! }
|
||||
end
|
||||
# set new order state (needed by notify_order_finished)
|
||||
update_attributes(:state => 'finished', :ends => Time.now, :updated_by => user)
|
||||
|
||||
# TODO: delete data, which is no longer required ...
|
||||
# group_order_article_quantities... order_articles with units_to_order == 0 ? ...
|
||||
# Clean up
|
||||
# Delete no longer required order-history (group_order_article_quantities) and
|
||||
# TODO: Do we need articles, which aren't ordered? (units_to_order == 0 ?)
|
||||
order_articles.each do |oa|
|
||||
oa.group_order_articles.each { |goa| goa.group_order_article_quantities.clear }
|
||||
end
|
||||
end
|
||||
|
||||
# notify order groups
|
||||
notify_order_finished
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: I can't understand, why its going out from the group_order_articles perspective.
|
||||
# Why we can't just iterate through the order_articles?
|
||||
#
|
||||
# Updates the ordered quantites of all OrderArticles from the GroupOrderArticles.
|
||||
# This method is fired after an ordergroup has saved/updated his order.
|
||||
def update_quantities
|
||||
indexed_order_articles = {} # holds the list of updated OrderArticles indexed by their id
|
||||
# Get all GroupOrderArticles for this order and update OrderArticle.quantity/.tolerance/.units_to_order from them...
|
||||
group_order_articles = GroupOrderArticle.all(:conditions => ['group_order_id IN (?)', group_order_ids],
|
||||
:include => [:order_article])
|
||||
for goa in group_order_articles
|
||||
if (order_article = indexed_order_articles[goa.order_article.id.to_s])
|
||||
# order_article has already been fetched, just update...
|
||||
order_article.quantity = order_article.quantity + goa.quantity
|
||||
order_article.tolerance = order_article.tolerance + goa.tolerance
|
||||
order_article.units_to_order = order_article.article.calculate_order_quantity(order_article.quantity, order_article.tolerance)
|
||||
else
|
||||
# First update to OrderArticle, need to store in orderArticle hash...
|
||||
order_article = goa.order_article
|
||||
order_article.quantity = goa.quantity
|
||||
order_article.tolerance = goa.tolerance
|
||||
order_article.units_to_order = order_article.article.calculate_order_quantity(order_article.quantity, order_article.tolerance)
|
||||
indexed_order_articles[order_article.id.to_s] = order_article
|
||||
end
|
||||
end
|
||||
# Commit changes to database...
|
||||
OrderArticle.transaction do
|
||||
indexed_order_articles.each_value { | value | value.save! }
|
||||
end
|
||||
end
|
||||
|
||||
# Sets order.status to 'close' and updates all Ordergroup.account_balances
|
||||
def close!(user)
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ class OrderArticle < ActiveRecord::Base
|
|||
#
|
||||
# before_validation_on_create :create_new_article
|
||||
|
||||
# This method returns either the Article or the ArticlePrice
|
||||
# The latter will be set, when the the order is finished
|
||||
# This method returns either the ArticlePrice or the Article
|
||||
# The first will be set, when the the order is finished
|
||||
def price
|
||||
article_price || article
|
||||
end
|
||||
|
|
@ -47,6 +47,39 @@ class OrderArticle < ActiveRecord::Base
|
|||
{:quantity => quantity, :price => quantity * price.fc_price}
|
||||
end
|
||||
|
||||
# Update quantity/tolerance/units_to_order from group_order_articles
|
||||
def update_results!
|
||||
quantity = group_order_articles.collect(&:quantity).sum
|
||||
tolerance = group_order_articles.collect(&:tolerance).sum
|
||||
update_attributes(:quantity => quantity, :tolerance => tolerance,
|
||||
:units_to_order => calculate_units_to_order(quantity, tolerance))
|
||||
end
|
||||
|
||||
# Returns how many units of the belonging article need to be ordered given the specified order quantity and tolerance.
|
||||
# This is determined by calculating how many units can be ordered from the given order quantity, using
|
||||
# the tolerance to order an additional unit if the order quantity is not quiet sufficient.
|
||||
# There must always be at least one item in a unit that is an ordered quantity (no units are ever entirely
|
||||
# filled by tolerance items only).
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# unit_quantity | quantity | tolerance | calculate_units_to_order
|
||||
# --------------+----------+-----------+-----------------------
|
||||
# 4 | 0 | 2 | 0
|
||||
# 4 | 0 | 5 | 0
|
||||
# 4 | 2 | 2 | 1
|
||||
# 4 | 4 | 2 | 1
|
||||
# 4 | 4 | 4 | 1
|
||||
# 4 | 5 | 3 | 2
|
||||
# 4 | 5 | 4 | 2
|
||||
#
|
||||
def calculate_units_to_order(quantity, tolerance = 0)
|
||||
unit_size = price.unit_quantity
|
||||
units = quantity / unit_size
|
||||
remainder = quantity % unit_size
|
||||
units += ((remainder > 0) && (remainder + tolerance >= unit_size) ? 1 : 0)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def article_and_price_exist
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class Ordergroup < Group
|
|||
acts_as_paranoid # Avoid deleting the ordergroup for consistency of order-results
|
||||
extend ActiveSupport::Memoizable # Ability to cache method results. Use memoize :expensive_method
|
||||
|
||||
has_many :financial_transactions
|
||||
has_many :financial_transactions, :order => "created_on DESC"
|
||||
has_many :group_orders
|
||||
has_many :orders, :through => :group_orders
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue