Ordering refactored: Made everything RESTful.

This commit is contained in:
benni 2011-06-19 15:30:33 +02:00
parent 45e0048490
commit eb4705857b
19 changed files with 340 additions and 222 deletions

View file

@ -1,6 +1,8 @@
# A GroupOrder represents an Order placed by an Ordergroup.
class GroupOrder < ActiveRecord::Base
attr_accessor :group_order_articles_attributes
belongs_to :order
belongs_to :ordergroup
has_many :group_order_articles, :dependent => :destroy
@ -14,27 +16,74 @@ class GroupOrder < ActiveRecord::Base
scope :open, lambda { {:conditions => ["order_id IN (?)", Order.open.collect(&:id)]} }
scope :finished, lambda { {:conditions => ["order_id IN (?)", Order.finished_not_closed.collect(&:id)]} }
# Updates the "price" attribute.
# 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 update_price!
total = 0
for article in group_order_articles.find(:all, :include => :order_article)
unless order.finished?
if Foodsoft.config[:tolerance_is_costly]
total += article.order_article.article.fc_price * (article.quantity + article.tolerance)
else
total += article.order_article.article.fc_price * article.quantity
end
else
total += article.order_article.price.fc_price * article.result
# Generate some data for the javascript methods in ordering view
def load_data
data = {}
data[:available_funds] = ordergroup.get_available_funds(self)
unless new_record?
# Group has already ordered, so get the results...
goas = {}
group_order_articles.all.each do |goa|
goas[goa.order_article_id] = {:quantity => goa.quantity,
:tolerance => goa.tolerance,
:quantity_result => goa.result(:quantity),
:tolerance_result => goa.result(:tolerance)}
end
end
# load prices ....
data[:order_articles] = {}
order.order_articles.each do |order_article|
data[:order_articles][order_article.id] = {
:price => order_article.article.fc_price,
:unit => order_article.article.unit_quantity,
:quantity => (new_record? ? 0 : goas[order_article.id][:quantity]),
:others_quantity => order_article.quantity - (new_record? ? 0 : goas[order_article.id][:quantity]),
:used_quantity => (new_record? ? 0 : goas[order_article.id][:quantity_result]),
:tolerance => (new_record? ? 0 : goas[order_article.id][:tolerance]),
:others_tolerance => order_article.tolerance - (new_record? ? 0 : goas[order_article.id][:tolerance]),
:used_tolerance => (new_record? ? 0 : goas[order_article.id][:tolerance_result])
}
end
data
end
def save_group_order_articles
for order_article in order.order_articles
# Find the group_order_article, create a new one if necessary...
group_order_article = group_order_articles.find_or_create_by_order_article_id(order_article.id)
# Get ordered quantities and update group_order_articles/_quantities...
quantities = group_order_articles_attributes.fetch(order_article.id.to_s, {:quantity => 0, :tolerance => 0})
group_order_article.update_quantities(quantities[:quantity].to_i, quantities[:tolerance].to_i)
# Also update results for the order_article
logger.debug "[save_group_order_articles] update order_article.results!"
order_article.update_results!
end
# set attributes to nil to avoid and infinite loop of
end
# Updates the "price" attribute.
def update_price!
total = group_order_articles.includes(:order_article => :article).all.map(&:total_price).sum
update_attribute(:price, total)
end
# Save GroupOrder and updates group_order_articles/quantities accordingly
def save_ordering!
transaction do
save!
save_group_order_articles
update_price!
end
end
end
# == Schema Information

View file

@ -14,15 +14,10 @@ class GroupOrderArticle < ActiveRecord::Base
validates_inclusion_of :tolerance, :in => 0..99
validates_uniqueness_of :order_article_id, :scope => :group_order_id # just once an article per group order
attr_accessor :ordergroup_id # To create an new GroupOrder if neccessary
scope :ordered, :conditions => 'result > 0'
# Custom attribute setter that accepts decimal numbers using localized decimal separator.
def result=(result)
self[:result] = String.delocalized_decimal(result)
end
localize_input_of :result
# Updates the quantity/tolerance for this GroupOrderArticle by updating both GroupOrderArticle properties
# and the associated GroupOrderArticleQuantities chronologically.
#
@ -30,17 +25,17 @@ class GroupOrderArticle < ActiveRecord::Base
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)
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
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))
@ -50,31 +45,31 @@ class GroupOrderArticle < ActiveRecord::Base
delta = (delta > quantities[i].quantity ? quantities[i].quantity : delta)
logger.debug("Decreasing quantity by #{delta}")
quantities[i].quantity -= delta
self.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
self.tolerance -= delta
end
i += 1
end
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)
: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
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!'
@ -82,7 +77,7 @@ class GroupOrderArticle < ActiveRecord::Base
# Remove zero-only items.
quantities = quantities.reject { | q | q.quantity == 0 && q.tolerance == 0}
# Save
transaction do
quantities.each { | i | i.save! }
@ -90,7 +85,7 @@ class GroupOrderArticle < ActiveRecord::Base
save!
end
end
# Determines how many items of this article the Ordergroup receives.
# Returns a hash with three keys: :quantity / :tolerance / :total
#
@ -102,15 +97,15 @@ class GroupOrderArticle < ActiveRecord::Base
# 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')
: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)
@ -137,10 +132,10 @@ class GroupOrderArticle < ActiveRecord::Base
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
@ -156,7 +151,23 @@ class GroupOrderArticle < ActiveRecord::Base
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
unless order_article.order.finished?
if Foodsoft.config[: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

View file

@ -85,16 +85,16 @@ class Order < ActiveRecord::Base
# search GroupOrder of given Ordergroup
def group_order(ordergroup)
group_orders.first :conditions => { :ordergroup_id => ordergroup.id }
group_orders.where(:ordergroup_id => ordergroup.id).first
end
# Returns OrderArticles in a nested Array, grouped by category and ordered by article name.
# The array has the following form:
# e.g: [["drugs",[teethpaste, toiletpaper]], ["fruits" => [apple, banana, lemon]]]
def articles_grouped_by_category
order_articles.all(:include => [:article, :article_price], :order => 'articles.name').group_by { |a|
a.article.article_category.name
}.sort { |a, b| a[0] <=> b[0] }
order_articles.includes(:article, :article_price).order('articles.name').
group_by { |a| a.article.article_category.name }.
sort { |a, b| a[0] <=> b[0] }
end
memoize :articles_grouped_by_category