2012-04-16 08:48:01 +02:00
# encoding: utf-8
#
2009-01-06 11:49:19 +01:00
class Order < ActiveRecord :: Base
2009-01-29 01:57:51 +01:00
# Associations
2009-01-06 11:49:19 +01:00
has_many :order_articles , :dependent = > :destroy
has_many :articles , :through = > :order_articles
has_many :group_orders , :dependent = > :destroy
2009-01-14 12:46:01 +01:00
has_many :ordergroups , :through = > :group_orders
2009-01-29 01:57:51 +01:00
has_one :invoice
has_many :comments , :class_name = > " OrderComment " , :order = > " created_at "
2009-02-06 16:26:35 +01:00
has_many :stock_changes
2013-03-16 17:53:24 +01:00
belongs_to :supplier
2012-11-12 14:24:49 +01:00
belongs_to :updated_by , :class_name = > 'User' , :foreign_key = > 'updated_by_user_id'
belongs_to :created_by , :class_name = > 'User' , :foreign_key = > 'created_by_user_id'
2009-01-06 11:49:19 +01:00
2009-01-29 01:57:51 +01:00
# Validations
2009-02-05 16:40:02 +01:00
validates_presence_of :starts
validate :starts_before_ends , :include_articles
2013-09-03 12:09:33 +02:00
validate :keep_ordered_articles
2009-01-10 22:22:16 +01:00
2009-01-29 01:57:51 +01:00
# Callbacks
2013-03-10 19:22:26 +01:00
after_save :save_order_articles , :update_price_of_group_orders
2012-10-30 00:20:47 +01:00
2009-01-29 01:57:51 +01:00
# Finders
2012-09-30 21:15:55 +02:00
scope :open , where ( state : 'open' ) . order ( 'ends DESC' )
2012-12-16 13:48:15 +01:00
scope :finished , where ( " orders.state = 'finished' OR orders.state = 'closed' " ) . order ( 'ends DESC' )
2012-09-30 21:15:55 +02:00
scope :finished_not_closed , where ( state : 'finished' ) . order ( 'ends DESC' )
scope :closed , where ( state : 'closed' ) . order ( 'ends DESC' )
scope :stockit , where ( supplier_id : 0 ) . order ( 'ends DESC' )
2009-02-05 16:40:02 +01:00
def stockit?
supplier_id == 0
end
def name
2013-06-15 18:43:42 +02:00
stockit? ? I18n . t ( 'orders.model.stock' ) : supplier . name
2009-02-05 16:40:02 +01:00
end
def articles_for_ordering
2010-02-09 21:45:57 +01:00
if stockit?
2013-03-15 08:50:55 +01:00
# make sure to include those articles which are no longer available
# but which have already been ordered in this stock order
2011-06-10 13:53:51 +02:00
StockArticle . available . all ( :include = > :article_category ,
2010-02-09 21:45:57 +01:00
:order = > 'article_categories.name, articles.name' ) . reject { | a |
2013-03-15 08:50:55 +01:00
a . quantity_available < = 0 and not a . ordered_in_order? ( self )
2010-02-09 21:45:57 +01:00
} . group_by { | a | a . article_category . name }
else
2011-06-10 13:53:51 +02:00
supplier . articles . available . all . group_by { | a | a . article_category . name }
2010-02-09 21:45:57 +01:00
end
2009-02-05 16:40:02 +01:00
end
2012-10-30 00:20:47 +01:00
# Save ids, and create/delete order_articles after successfully saved the order
def article_ids = ( ids )
@article_ids = ids
2009-02-05 16:40:02 +01:00
end
2009-01-10 22:22:16 +01:00
2012-10-30 00:20:47 +01:00
def article_ids
2013-09-03 12:09:33 +02:00
@article_ids || = order_articles . map { | a | a . article_id . to_s }
2009-01-06 11:49:19 +01:00
end
2009-01-29 01:57:51 +01:00
2013-09-03 17:37:49 +02:00
# Returns an array of article ids that lead to a validation error.
def erroneous_article_ids
@erroneous_article_ids || = [ ]
end
2009-01-29 01:57:51 +01:00
def open?
state == " open "
2009-01-06 11:49:19 +01:00
end
2009-01-29 01:57:51 +01:00
def finished?
state == " finished "
2009-01-06 11:49:19 +01:00
end
2009-01-29 01:57:51 +01:00
def closed?
state == " closed "
2009-01-06 11:49:19 +01:00
end
2010-06-19 12:23:18 +02:00
def expired?
! ends . nil? && ends < Time . now
end
2012-10-30 00:20:47 +01:00
2009-01-14 12:46:01 +01:00
# search GroupOrder of given Ordergroup
2009-01-06 11:49:19 +01:00
def group_order ( ordergroup )
2011-06-19 15:30:33 +02:00
group_orders . where ( :ordergroup_id = > ordergroup . id ) . first
2009-01-06 11:49:19 +01:00
end
2012-10-30 00:20:47 +01:00
2009-01-06 11:49:19 +01:00
# 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]]]
2009-02-04 16:41:01 +01:00
def articles_grouped_by_category
2013-01-26 15:18:35 +01:00
@articles_grouped_by_category || = order_articles .
includes ( [ :article_price , :group_order_articles , :article = > :article_category ] ) .
2012-12-16 16:50:09 +01:00
order ( 'articles.name' ) .
2013-01-26 15:18:35 +01:00
group_by { | a | a . article . article_category . name } .
2011-06-19 15:30:33 +02:00
sort { | a , b | a [ 0 ] < = > b [ 0 ] }
2009-01-06 11:49:19 +01:00
end
2009-02-04 16:41:01 +01:00
def articles_sort_by_category
order_articles . all ( :include = > [ :article ] , :order = > 'articles.name' ) . sort do | a , b |
a . article . article_category . name < = > b . article . article_category . name
end
end
2012-10-30 00:20:47 +01:00
2009-01-06 11:49:19 +01:00
# Returns the defecit/benefit for the foodcoop
2009-01-29 21:28:22 +01:00
# Requires a valid invoice, belonging to this order
2009-03-17 19:43:41 +01:00
#FIXME: Consider order.foodcoop_result
2009-01-29 21:28:22 +01:00
def profit ( options = { } )
2009-03-17 19:43:41 +01:00
markup = options [ :without_markup ] || false
2009-01-29 21:28:22 +01:00
if invoice
2009-03-17 19:43:41 +01:00
groups_sum = markup ? sum ( :groups_without_markup ) : sum ( :groups )
2009-01-29 21:28:22 +01:00
groups_sum - invoice . net_amount
end
2009-01-06 11:49:19 +01:00
end
2012-10-30 00:20:47 +01:00
2009-01-06 11:49:19 +01:00
# Returns the all round price of a finished order
2009-01-29 01:57:51 +01:00
# :groups returns the sum of all GroupOrders
# :clear returns the price without tax, deposit and markup
# :gross includes tax and deposit. this amount should be equal to suppliers bill
# :fc, guess what...
def sum ( type = :gross )
total = 0
2009-02-03 21:14:48 +01:00
if type == :net || type == :gross || type == :fc
2013-03-17 15:12:14 +01:00
for oa in order_articles . ordered . includes ( :article , :article_price )
2009-01-29 01:57:51 +01:00
quantity = oa . units_to_order * oa . price . unit_quantity
case type
2013-03-17 15:12:14 +01:00
when :net
total += quantity * oa . price . price
when :gross
total += quantity * oa . price . gross_price
when :fc
total += quantity * oa . price . fc_price
2009-01-06 11:49:19 +01:00
end
2009-01-29 01:57:51 +01:00
end
elsif type == :groups || type == :groups_without_markup
2013-03-17 15:12:14 +01:00
for go in group_orders . includes ( group_order_articles : { order_article : [ :article , :article_price ] } )
for goa in go . group_order_articles
2009-01-06 11:49:19 +01:00
case type
2013-03-17 15:12:14 +01:00
when :groups
total += goa . result * goa . order_article . price . fc_price
when :groups_without_markup
total += goa . result * goa . order_article . price . gross_price
2009-01-06 11:49:19 +01:00
end
end
end
end
2009-01-29 01:57:51 +01:00
total
2009-01-06 11:49:19 +01:00
end
2009-01-29 01:57:51 +01:00
# Finishes this order. This will set the order state to "finish" and the end property to the current time.
2009-01-06 11:49:19 +01:00
# Ignored if the order is already finished.
2009-01-29 01:57:51 +01:00
def finish! ( user )
unless finished?
Order . transaction do
2012-08-24 14:24:36 +02:00
# set new order state (needed by notify_order_finished)
update_attributes ( :state = > 'finished' , :ends = > Time . now , :updated_by = > user )
2009-01-29 01:57:51 +01:00
# Update order_articles. Save the current article_price to keep price consistency
2009-02-03 21:14:48 +01:00
# Also save results for each group_order_result
2012-08-24 14:24:36 +02:00
# Clean up
2009-01-29 01:57:51 +01:00
order_articles . all ( :include = > :article ) . each do | oa |
oa . update_attribute ( :article_price , oa . article . article_prices . first )
2012-08-24 14:24:36 +02:00
oa . group_order_articles . each do | goa |
goa . save_results!
# Delete no longer required order-history (group_order_article_quantities) and
# TODO: Do we need articles, which aren't ordered? (units_to_order == 0 ?)
2013-04-08 01:00:49 +02:00
#goa.group_order_article_quantities.clear
2012-08-24 14:24:36 +02:00
end
2009-01-06 11:49:19 +01:00
end
2009-03-17 10:47:00 +01:00
2012-08-24 14:24:36 +02:00
# Update GroupOrder prices
2012-12-16 21:46:24 +01:00
group_orders . each ( & :update_price! )
2012-08-24 14:24:36 +02:00
# Stats
2012-12-16 21:46:24 +01:00
ordergroups . each ( & :update_stats! )
2012-08-24 14:24:36 +02:00
# Notifications
2012-12-23 17:38:04 +01:00
Resque . enqueue ( UserNotifier , FoodsoftConfig . scope , 'finished_order' , self . id )
2009-01-29 01:57:51 +01:00
end
2009-01-06 11:49:19 +01:00
end
end
2012-10-30 00:20:47 +01:00
2009-01-30 22:27:55 +01:00
# Sets order.status to 'close' and updates all Ordergroup.account_balances
def close! ( user )
2013-03-22 01:21:44 +01:00
raise I18n . t ( 'orders.model.error_closed' ) if closed?
transaction_note = I18n . t ( 'orders.model.notice_close' , :name = > name ,
:ends = > ends . strftime ( I18n . t ( 'date.formats.default' ) ) )
2009-01-30 22:27:55 +01:00
gos = group_orders . all ( :include = > :ordergroup ) # Fetch group_orders
gos . each { | group_order | group_order . update_price! } # Update prices of group_orders
transaction do # Start updating account balances
for group_order in gos
price = group_order . price * - 1 # decrease! account balance
2011-06-09 21:35:05 +02:00
group_order . ordergroup . add_financial_transaction! ( price , transaction_note , user )
2009-01-06 11:49:19 +01:00
end
2009-02-06 16:26:35 +01:00
if stockit? # Decreases the quantity of stock_articles
for oa in order_articles . all ( :include = > :article )
oa . update_results! # Update units_to_order of order_article
stock_changes . create! :stock_article = > oa . article , :quantity = > oa . units_to_order * - 1
end
end
2009-03-17 19:43:41 +01:00
self . update_attributes! :state = > 'closed' , :updated_by = > user , :foodcoop_result = > profit
2009-01-06 11:49:19 +01:00
end
end
2009-03-11 16:58:31 +01:00
2012-07-27 18:03:46 +02:00
# Close the order directly, without automaticly updating ordergroups account balances
def close_direct! ( user )
2013-03-22 01:21:44 +01:00
raise I18n . t ( 'orders.model.error_closed' ) if closed?
2012-07-27 18:03:46 +02:00
update_attributes! state : 'closed' , updated_by : user
end
2009-01-06 11:49:19 +01:00
protected
2009-01-29 01:57:51 +01:00
def starts_before_ends
2013-03-22 01:21:44 +01:00
errors . add ( :ends , I18n . t ( 'articles.model.error_starts_before_ends' ) ) if ( ends && starts && ends < = starts )
2009-01-29 01:57:51 +01:00
end
def include_articles
2013-03-22 01:21:44 +01:00
errors . add ( :articles , I18n . t ( 'articles.model.error_nosel' ) ) if article_ids . empty?
2012-10-30 00:20:47 +01:00
end
2013-09-03 12:09:33 +02:00
def keep_ordered_articles
2013-09-03 17:37:49 +02:00
chosen_order_articles = order_articles . find_all_by_article_id ( article_ids )
to_be_removed = order_articles - chosen_order_articles
to_be_removed_but_ordered = to_be_removed . select { | a | a . quantity > 0 or a . tolerance > 0 }
unless to_be_removed_but_ordered . empty?
2013-09-03 12:09:33 +02:00
errors . add ( :articles , " Die markierten Artikel wurden in der laufenden Bestellung bereits bestellt. Wenn Du sie hier abwählst, werden alle bestehenden Bestellungen dieses Artikels gelöscht. Bei Lagerbestellungen kann dies je nach Verwendung bedeuten, dass bereits gekaufte Artikel nicht abgerechnet werden! " )
2013-09-03 17:37:49 +02:00
@erroneous_article_ids = to_be_removed_but_ordered . map { | a | a . article_id }
2013-09-03 12:09:33 +02:00
end
end
2013-03-12 11:26:14 +01:00
2013-09-03 12:09:33 +02:00
def save_order_articles
2013-03-12 11:26:14 +01:00
# fetch selected articles
articles_list = Article . find ( article_ids )
# create new order_articles
( articles_list - articles ) . each { | article | order_articles . create ( :article = > article ) }
# delete old order_articles
articles . reject { | article | articles_list . include? ( article ) } . each do | article |
order_articles . detect { | order_article | order_article . article_id == article . id } . destroy
end
2009-01-29 01:57:51 +01:00
end
2009-01-06 11:49:19 +01:00
private
2012-10-30 00:20:47 +01:00
2009-01-29 01:57:51 +01:00
# Updates the "price" attribute of GroupOrders or GroupOrderResults
# This will be either the maximum value of a current order or the actual order value of a finished order.
def update_price_of_group_orders
group_orders . each { | group_order | group_order . update_price! }
end
2009-01-06 11:49:19 +01:00
end
2011-05-07 21:55:24 +02:00