Order-refactoring part II.

This commit is contained in:
Benjamin Meichsner 2009-02-03 21:14:48 +01:00
parent f7b9582261
commit 6fd5d825f9
27 changed files with 228 additions and 231 deletions

View File

@ -53,7 +53,7 @@ class ApplicationController < ActionController::Base
def deny_access def deny_access
self.return_to = request.request_uri self.return_to = request.request_uri
redirect_to :controller => 'login', :action => 'denied' redirect_to :controller => '/login', :action => 'denied'
return false return false
end end

View File

@ -4,9 +4,6 @@ class HomeController < ApplicationController
def index def index
@currentOrders = Order.open @currentOrders = Order.open
@ordergroup = @current_user.ordergroup @ordergroup = @current_user.ordergroup
if @ordergroup
@financial_transactions = @ordergroup.financial_transactions.find(:all, :order => 'created_on desc', :limit => 3)
end
# unaccepted tasks # unaccepted tasks
@unaccepted_tasks = @current_user.unaccepted_tasks @unaccepted_tasks = @current_user.unaccepted_tasks
# task in next week # task in next week

View File

@ -5,7 +5,7 @@ class OrderingController < ApplicationController
before_filter :ensure_ordergroup_member before_filter :ensure_ordergroup_member
before_filter :ensure_open_order, :only => [:order, :saveOrder] before_filter :ensure_open_order, :only => [:order, :saveOrder]
verify :method => :post, :only => [:saveOrder], :redirect_to => { :action => :index } verify :method => :post, :only => [:saveOrder], :redirect_to => {:action => :index}
# Index page. # Index page.
def index def index
@ -23,8 +23,8 @@ class OrderingController < ApplicationController
if @group_order if @group_order
# Group has already ordered, so get the results... # Group has already ordered, so get the results...
for article in @group_order.group_order_articles for article in @group_order.group_order_articles
result = article.orderResult result = article.result
ordered_articles[article.order_article_id] = { 'quantity' => article.quantity, ordered_articles[article.order_article_id] = {'quantity' => article.quantity,
'tolerance' => article.tolerance, 'tolerance' => article.tolerance,
'quantity_result' => result[:quantity], 'quantity_result' => result[:quantity],
'tolerance_result' => result[:tolerance]} 'tolerance_result' => result[:tolerance]}
@ -61,53 +61,53 @@ class OrderingController < ApplicationController
# Update changes to a current order. # Update changes to a current order.
def saveOrder def saveOrder
order = @order # Get the object through before_filter if (params[:total_balance].to_i < 0) #TODO: Better use a real test on sufficiant funds
if (params[:total_balance].to_i < 0)
flash[:error] = 'Der Bestellwert übersteigt das verfügbare Guthaben.' flash[:error] = 'Der Bestellwert übersteigt das verfügbare Guthaben.'
redirect_to :action => 'order' redirect_to :action => 'order'
elsif (ordered = params[:ordered]) elsif (ordered = params[:ordered])
begin begin
Order.transaction do Order.transaction do
# Create group order if necessary... # Try to find group_order
if (groupOrder = order.group_orders.find(:first, :conditions => "ordergroup_id = #{@ordergroup.id}", :include => [:group_order_articles])) group_order = @order.group_orders.first :conditions => "ordergroup_id = #{@ordergroup.id}",
if (params[:version].to_i != groupOrder.lock_version) # check for conflicts well ahead :include => [:group_order_articles]
raise ActiveRecord::StaleObjectError # Create group order if necessary...
end unless group_order.nil?
else # check for conflicts well ahead
groupOrder = GroupOrder.new(:ordergroup => @ordergroup, :order => order, :updated_by => @current_user, :price => 0) if (params[:version].to_i != group_order.lock_version)
groupOrder.save! raise ActiveRecord::StaleObjectError
end end
# Create/update GroupOrderArticles... else
newGroupOrderArticles = Array.new group_order = @ordergroup.group_orders.create!(:order => @order, :updated_by => @current_user, :price => 0)
for article in order.order_articles end
# Find the GroupOrderArticle, create a new one if necessary...
groupOrderArticles = groupOrder.group_order_articles.select{ |v| v.order_article_id == article.id } # Create/update group_order_articles...
unless (groupOrderArticle = groupOrderArticles[0]) for order_article in @order.order_articles
groupOrderArticle = GroupOrderArticle.create(:group_order => groupOrder, :order_article_id => article.id, :quantity => 0, :tolerance => 0)
end # Find the group_order_article, create a new one if necessary...
# Get ordered quantities and update GroupOrderArticle/-Quantities... group_order_article = group_order.group_order_articles.detect { |v| v.order_article_id == order_article.id }
unless (quantities = ordered.delete(article.id.to_s)) && (quantity = quantities['quantity']) && (tolerance = quantities['tolerance']) if group_order_article.nil?
quantity = tolerance = 0 group_order_article = group_order.group_order_articles.create(:order_article_id => order_article.id)
end end
groupOrderArticle.update_quantities(quantity.to_i, tolerance.to_i)
# Add to new list of GroupOrderArticles: # Get ordered quantities and update group_order_articles/_quantities...
newGroupOrderArticles.push(groupOrderArticle) quantities = ordered.fetch(order_article.id.to_s, {'quantity' => 0, 'tolerance' => 0})
end group_order_article.update_quantities(quantities['quantity'].to_i, quantities['tolerance'].to_i)
groupOrder.group_order_articles = newGroupOrderArticles
groupOrder.update_price! # Also update results for the order_article
groupOrder.updated_by = @current_user order_article.update_results!
groupOrder.save! end
order.update_quantities
order.save! group_order.update_price!
end group_order.update_attribute(:updated_by, @current_user)
flash[:notice] = 'Die Bestellung wurde gespeichert.' end
rescue ActiveRecord::StaleObjectError flash[:notice] = 'Die Bestellung wurde gespeichert.'
flash[:error] = 'In der Zwischenzeit hat jemand anderes auch bestellt, daher konnte die Bestellung nicht aktualisiert werden.' rescue ActiveRecord::StaleObjectError
rescue => exception flash[:error] = 'In der Zwischenzeit hat jemand anderes auch bestellt, daher konnte die Bestellung nicht aktualisiert werden.'
logger.error('Failed to update order: ' + exception.message) rescue => exception
flash[:error] = 'Die Bestellung konnte nicht aktualisiert werden, da ein Fehler auftrat.' logger.error('Failed to update order: ' + exception.message)
end flash[:error] = 'Die Bestellung konnte nicht aktualisiert werden, da ein Fehler auftrat.'
redirect_to :action => 'my_order_result', :id => order end
redirect_to :action => 'my_order_result', :id => @order
end end
end end

View File

@ -138,7 +138,7 @@ class OrdersController < ApplicationController
text += "****** " + "Artikel" + "\n\n" text += "****** " + "Artikel" + "\n\n"
text += "Nummer" + " " + "Menge" + " " + "Name" + "\n" text += "Nummer" + " " + "Menge" + " " + "Name" + "\n"
# now display all ordered articles # now display all ordered articles
order.order_articles.all(:include => [:article, :article_price]).each do |oa| order.order_articles.ordered.all(:include => [:article, :article_price]).each do |oa|
number = oa.article.order_number number = oa.article.order_number
(8 - number.size).times { number += " " } (8 - number.size).times { number += " " }
quantity = oa.units_to_order.to_i.to_s quantity = oa.units_to_order.to_i.to_s

View File

@ -76,34 +76,9 @@ class Article < ActiveRecord::Base
updated_at > 2.days.ago updated_at > 2.days.ago
end 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. # If the article is used in an open Order, the Order will be returned.
def in_open_order 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_articles.detect {|oa| oa.article_id == id }
order_article ? order_article.order : nil order_article ? order_article.order : nil
end end

View File

@ -1,5 +1,5 @@
# == Schema Information # == Schema Information
# Schema version: 20090119155930 # Schema version: 20090120184410
# #
# Table name: group_order_articles # Table name: group_order_articles
# #
@ -9,13 +9,16 @@
# quantity :integer default(0), not null # quantity :integer default(0), not null
# tolerance :integer default(0), not null # tolerance :integer default(0), not null
# updated_on :datetime 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. # 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 # The chronologically order of the Ordergroup - activity are stored in GroupOrderArticleQuantity
# #
class GroupOrderArticle < ActiveRecord::Base class GroupOrderArticle < ActiveRecord::Base
extend ActiveSupport::Memoizable # Ability to cache method results. Use memoize :expensive_method
belongs_to :group_order belongs_to :group_order
belongs_to :order_article belongs_to :order_article
has_many :group_order_article_quantities, :dependent => :destroy 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 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 # Updates the quantity/tolerance for this GroupOrderArticle by updating both GroupOrderArticle properties
# and the associated GroupOrderArticleQuantities chronologically. # and the associated GroupOrderArticleQuantities chronologically.
# #
@ -99,25 +104,27 @@ class GroupOrderArticle < ActiveRecord::Base
# Returns a hash with three keys: :quantity / :tolerance / :total # Returns a hash with three keys: :quantity / :tolerance / :total
# #
# See description of the ordering algorithm in the general application documentation for details. # See description of the ordering algorithm in the general application documentation for details.
def orderResult def calculate_result
quantity = tolerance = 0 quantity = tolerance = 0
# Get total # Get total
total = order_article.units_to_order * order_article.article.unit_quantity total = order_article.units_to_order * order_article.price.unit_quantity
logger.debug("unitsToOrder => items ordered: #{order_article.units_to_order} => #{total}") logger.debug("<#{order_article.article.name}>.unitsToOrder => items ordered: #{order_article.units_to_order} => #{total}")
if (total > 0) 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... # 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 }]) order_quantities = GroupOrderArticleQuantity.all(
orderQuantities = GroupOrderArticleQuantity.find(:all, :conditions => ['group_order_article_id IN (?)', orderArticles.collect { | i | i.id }], :order => 'created_on') :conditions => ["group_order_article_id IN (?)", order_article.group_order_article_ids], :order => 'created_on')
logger.debug("GroupOrderArticleQuantity records found: #{orderQuantities.size}") logger.debug("GroupOrderArticleQuantity records found: #{order_quantities.size}")
# Determine quantities to be ordered... # Determine quantities to be ordered...
totalQuantity = i = 0 total_quantity = i = 0
while (i < orderQuantities.size && totalQuantity < total) while (i < order_quantities.size && total_quantity < total)
q = (orderQuantities[i].quantity <= total - totalQuantity ? orderQuantities[i].quantity : total - totalQuantity) q = (order_quantities[i].quantity <= total - total_quantity ? order_quantities[i].quantity : total - total_quantity)
totalQuantity += q total_quantity += q
if (orderQuantities[i].group_order_article_id == self.id) if (order_quantities[i].group_order_article_id == self.id)
logger.debug("increasing quantity by #{q}") logger.debug("increasing quantity by #{q}")
quantity += q quantity += q
end end
@ -125,13 +132,13 @@ class GroupOrderArticle < ActiveRecord::Base
end end
# Determine tolerance to be ordered... # Determine tolerance to be ordered...
if (totalQuantity < total) if (total_quantity < total)
logger.debug("determining additional items to be ordered from tolerance") logger.debug("determining additional items to be ordered from tolerance")
i = 0 i = 0
while (i < orderQuantities.size && totalQuantity < total) while (i < order_quantities.size && total_quantity < total)
q = (orderQuantities[i].tolerance <= total - totalQuantity ? orderQuantities[i].tolerance : total - totalQuantity) q = (order_quantities[i].tolerance <= total - total_quantity ? order_quantities[i].tolerance : total - total_quantity)
totalQuantity += q total_quantity += q
if (orderQuantities[i].group_order_article_id == self.id) if (order_quantities[i].group_order_article_id == self.id)
logger.debug("increasing tolerance by #{q}") logger.debug("increasing tolerance by #{q}")
tolerance += q tolerance += q
end end
@ -139,13 +146,27 @@ class GroupOrderArticle < ActiveRecord::Base
end end
end end
# calculate the sum of quantity and tolerance: logger.debug("determined quantity/tolerance/total: #{quantity} / #{tolerance} / #{quantity + tolerance}")
sum = quantity + tolerance
logger.debug("determined quantity/tolerance/total: #{quantity} / #{tolerance} / #{sum}")
end 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 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 end

View File

@ -100,11 +100,11 @@ class Order < ActiveRecord::Base
# :fc, guess what... # :fc, guess what...
def sum(type = :gross) def sum(type = :gross)
total = 0 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]) for oa in order_articles.ordered.all(:include => [:article,:article_price])
quantity = oa.units_to_order * oa.price.unit_quantity quantity = oa.units_to_order * oa.price.unit_quantity
case type case type
when :clear when :net
total += quantity * oa.price.price total += quantity * oa.price.price
when :gross when :gross
total += quantity * oa.price.gross_price total += quantity * oa.price.gross_price
@ -133,51 +133,26 @@ class Order < ActiveRecord::Base
unless finished? unless finished?
Order.transaction do Order.transaction do
# Update order_articles. Save the current article_price to keep price consistency # 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| order_articles.all(:include => :article).each do |oa|
oa.update_attribute(:article_price, oa.article.article_prices.first) oa.update_attribute(:article_price, oa.article.article_prices.first)
oa.group_order_articles.each { |goa| goa.save_results! }
end end
# set new order state (needed by notify_order_finished) # set new order state (needed by notify_order_finished)
update_attributes(:state => 'finished', :ends => Time.now, :updated_by => user) update_attributes(:state => 'finished', :ends => Time.now, :updated_by => user)
# TODO: delete data, which is no longer required ... # Clean up
# group_order_article_quantities... order_articles with units_to_order == 0 ? ... # 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 end
# notify order groups # notify order groups
notify_order_finished notify_order_finished
end end
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 # Sets order.status to 'close' and updates all Ordergroup.account_balances
def close!(user) def close!(user)

View File

@ -34,8 +34,8 @@ class OrderArticle < ActiveRecord::Base
# #
# before_validation_on_create :create_new_article # before_validation_on_create :create_new_article
# This method returns either the Article or the ArticlePrice # This method returns either the ArticlePrice or the Article
# The latter will be set, when the the order is finished # The first will be set, when the the order is finished
def price def price
article_price || article article_price || article
end end
@ -47,6 +47,39 @@ class OrderArticle < ActiveRecord::Base
{:quantity => quantity, :price => quantity * price.fc_price} {:quantity => quantity, :price => quantity * price.fc_price}
end 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 private
def article_and_price_exist def article_and_price_exist

View File

@ -35,7 +35,7 @@ class Ordergroup < Group
acts_as_paranoid # Avoid deleting the ordergroup for consistency of order-results acts_as_paranoid # Avoid deleting the ordergroup for consistency of order-results
extend ActiveSupport::Memoizable # Ability to cache method results. Use memoize :expensive_method 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 :group_orders
has_many :orders, :through => :group_orders has_many :orders, :through => :group_orders

View File

@ -54,7 +54,7 @@
| |
= link_to _('Upload articles'), upload_supplier_articles_path(@supplier) = link_to _('Upload articles'), upload_supplier_articles_path(@supplier)
| |
= link_to_if @current_user.role_orders?, _('Create order'), {:controller => 'orders', :action => 'new', :id => @supplier } = link_to_if @current_user.role_orders?, _('Create order'), {:controller => 'orders', :action => 'new', :supplier_id => @supplier }
#article_filter #article_filter
#article_search_form{:style=>"display:inline;"} #article_search_form{:style=>"display:inline;"}

View File

@ -5,7 +5,7 @@
%table %table
%tr %tr
%td Nettobetrag: %td Nettobetrag:
%td= number_to_currency(order.sum(:clear)) %td= number_to_currency(order.sum(:net))
%tr %tr
%td Bruttobetrag: %td Bruttobetrag:
%td= number_to_currency(order.sum(:gross)) %td= number_to_currency(order.sum(:gross))

View File

@ -57,7 +57,7 @@
%th=_ "Who" %th=_ "Who"
%th=_ "Note" %th=_ "Note"
%th=_ "Amount" %th=_ "Amount"
- for ft in @financial_transactions - for ft in @ordergroup.financial_transactions.all(:limit => 5)
%tr{:class => cycle('even','odd')} %tr{:class => cycle('even','odd')}
%td= format_time(ft.created_on) %td= format_time(ft.created_on)
%td= h(ft.user.nil? ? '?' : ft.user.nick) %td= h(ft.user.nil? ? '?' : ft.user.nick)

View File

@ -20,22 +20,22 @@
:subnav => [ :subnav => [
{ :name => "Order", :url => "/ordering" }, { :name => "Order", :url => "/ordering" },
{ :name => "My orders", :url => "/ordering/myOrders" }, { :name => "My orders", :url => "/ordering/myOrders" },
{ :name => "Manage orders", :url => "/orders", :access? => (u.role_orders?) } { :name => "Manage orders", :url => "/orders", :access_denied? => (!u.role_orders?) }
] ]
}, },
{ :name => "Articles", :url => "/suppliers", { :name => "Articles", :url => "/suppliers",
:active => ["articles", "suppliers", "deliveries", "article_categories", "stockit"], :active => ["articles", "suppliers", "deliveries", "article_categories", "stockit"],
:access? => (u.role_article_meta? || u.role_suppliers?), :access_denied? => (!u.role_article_meta? && !u.role_suppliers?),
:subnav => [ :subnav => [
{ :name => "Artikel", :url => supplier_articles_path(Supplier.first) }, { :name => "Artikel", :url => supplier_articles_path(Supplier.first) },
{ :name => "Lager", :url => "/stockit" }, { :name => "Lager", :url => "/stockit" },
{ :name => "Lieferantinnen", :url => suppliers_path, :access? => (u.role_suppliers?) }, { :name => "Lieferantinnen", :url => suppliers_path, :access_denied? => (!u.role_suppliers?) },
{ :name => "Kategorien", :url => "/article_categories"} { :name => "Kategorien", :url => "/article_categories"}
] ]
}, },
{ :name => "Finance", :url => "/finance", { :name => "Finance", :url => "/finance",
:active => ["finance/invoices", "finance/transactions", "finance/balancing"], :active => ["finance/invoices", "finance/transactions", "finance/balancing"],
:access? => (u.role_finance?), :access_denied? => (!u.role_finance?),
:subnav => [ :subnav => [
{ :name => "Manage accounts", :url => "/finance/transactions" }, { :name => "Manage accounts", :url => "/finance/transactions" },
{ :name => "Balance orders", :url => "/finance/balancing/list" }, { :name => "Balance orders", :url => "/finance/balancing/list" },
@ -44,7 +44,7 @@
}, },
{ :name => "Administration", :url => "/admin", { :name => "Administration", :url => "/admin",
:active => ["admin", "admin/users", "admin/ordergroups", "admin/workgroups"], :active => ["admin", "admin/users", "admin/ordergroups", "admin/workgroups"],
:access? => (u.role_admin?), :access_denied? => (!u.role_admin?),
:subnav => [ :subnav => [
{ :name => "Users", :url => admin_users_path }, { :name => "Users", :url => admin_users_path },
{ :name => "Ordergroups", :url => admin_ordergroups_path }, { :name => "Ordergroups", :url => admin_ordergroups_path },
@ -55,12 +55,12 @@
-%> -%>
<ul> <ul>
<% for tab in tabs -%> <% for tab in tabs -%>
<% unless tab[:access?] and tab[:access?] == false -%> <% unless tab[:access_denied?] -%>
<li class="<%= 'current' if tab_is_active?(tab) %>"> <li class="<%= 'current' if tab_is_active?(tab) %>">
<%= link_to tab[:name], tab[:url] %> <%= link_to tab[:name], tab[:url] %>
<ul> <ul>
<% for subtab in tab[:subnav] -%> <% for subtab in tab[:subnav] -%>
<% unless subtab[:access?] and subtab[:access?] == false -%> <% unless subtab[:access_denied?] -%>
<li><%= link_to subtab[:name], subtab[:url] %></li> <li><%= link_to subtab[:name], subtab[:url] %></li>
<% end -%> <% end -%>
<% end -%> <% end -%>

View File

@ -17,7 +17,7 @@
%p %p
Lieferant: Lieferant:
%b=h @order.supplier.name %b=h @order.supplier.name
- unless @order.note.empty? - unless @order.note.blank?
%p %p
Notiz: Notiz:
=h @order.note =h @order.note
@ -76,7 +76,7 @@
- if goa - if goa
- quantity = goa.quantity - quantity = goa.quantity
- tolerance = goa.tolerance - tolerance = goa.tolerance
- result = goa.orderResult[:quantity] + goa.orderResult[:tolerance] - result = goa.result[:total]
- sub_total = oa.price.fc_price * (quantity + tolerance) - sub_total = oa.price.fc_price * (quantity + tolerance)
- else - else
- quantity, tolerance, result, sub_total = 0, 0, 0, 0 - quantity, tolerance, result, sub_total = 0, 0, 0, 0
@ -88,7 +88,7 @@
%tr{:class => cycle('even', 'odd', :name => 'articles'), :style => "color:#{style}"} %tr{:class => cycle('even', 'odd', :name => 'articles'), :style => "color:#{style}"}
%td{:style => "width:40%"} %td{:style => "width:40%"}
=h oa.article.name =h oa.article.name
- unless oa.article.note.empty? - unless oa.article.note.blank?
= image_tag("lamp_grey.png", {:alt => "Notiz anzeigen", :size => "15x16", :border => "0", :onmouseover => "$('note_#{oa.id}').show();", :onmouseout => "$('note_#{oa.id}').hide();"}) = image_tag("lamp_grey.png", {:alt => "Notiz anzeigen", :size => "15x16", :border => "0", :onmouseover => "$('note_#{oa.id}').show();", :onmouseout => "$('note_#{oa.id}').hide();"})
%td= "#{oa.price.unit_quantity} x #{oa.article.unit}" %td= "#{oa.price.unit_quantity} x #{oa.article.unit}"
%td= number_to_currency(oa.price.fc_price) %td= number_to_currency(oa.price.fc_price)
@ -97,7 +97,7 @@
= "+ #{tolerance}" if oa.price.unit_quantity > 1 = "+ #{tolerance}" if oa.price.unit_quantity > 1
%td= result > 0 ? result : "0" %td= result > 0 ? result : "0"
%td= number_to_currency(sub_total) %td= number_to_currency(sub_total)
- unless oa.article.note.empty? - unless oa.article.note.blank?
%tr{:id => "note_#{oa.id}", :class => "note even", :style => "display:none"} %tr{:id => "note_#{oa.id}", :class => "note even", :style => "display:none"}
%td{:colspan => "6"}=h oa.article.note %td{:colspan => "6"}=h oa.article.note
%tr{:class => cycle('even', 'odd', :name => 'articles')} %tr{:class => cycle('even', 'odd', :name => 'articles')}

View File

@ -96,14 +96,14 @@
-%> -%>
<tr class="<%= cycle('even', 'odd', :name => 'articles') %>" valign="top"> <tr class="<%= cycle('even', 'odd', :name => 'articles') %>" valign="top">
<td class="name"> <td class="name">
<% unless order_article.article.note.empty? %> <% unless order_article.article.note.blank? %>
<%= order_article.article.name %> <%= image_tag "lamp_grey.png", {:alt => _("Show note"), :size => "15x16", :border => "0", :onmouseover => "$('note_#{i}').show();", :onmouseout => "$('note_#{i}').hide();" }%> <%= order_article.article.name %> <%= image_tag "lamp_grey.png", {:alt => _("Show note"), :size => "15x16", :border => "0", :onmouseover => "$('note_#{i}').show();", :onmouseout => "$('note_#{i}').hide();" }%>
<% else %> <% else %>
<%= order_article.article.name %> <%= order_article.article.name %>
<% end %> <% end %>
</td> </td>
<td><%=h order_article.article.origin %></td> <td><%=h order_article.article.origin %></td>
<td><%=h truncate order_article.article.manufacturer, 11 %></td> <td><%=h truncate order_article.article.manufacturer, :length => 11 %></td>
<td><%= @unit[i] %> * <%=h order_article.article.unit %></td> <td><%= @unit[i] %> * <%=h order_article.article.unit %></td>
<td><%= number_to_currency(@price[i]) %></td> <td><%= number_to_currency(@price[i]) %></td>
<td id="units_<%= i %>"><%= order_article.units_to_order %></td> <td id="units_<%= i %>"><%= order_article.units_to_order %></td>
@ -127,7 +127,7 @@
</td> </td>
<td id="td_price_<%= i %>" style="text-align:right; padding-right:10px; width:4em"><span id="price_<%= i %>_display"><%= number_to_currency(article_total, :unit => "") %></span> €</td> <td id="td_price_<%= i %>" style="text-align:right; padding-right:10px; width:4em"><span id="price_<%= i %>_display"><%= number_to_currency(article_total, :unit => "") %></span> €</td>
</tr> </tr>
<% unless order_article.article.note.empty? -%> <% unless order_article.article.note.blank? -%>
<tr id="note_<%= i %>" class="note" style="display:none"> <tr id="note_<%= i %>" class="note" style="display:none">
<td colspan="10"><%=h order_article.article.note %> | <%=h order_article.article.manufacturer %></td> <td colspan="10"><%=h order_article.article.note %> | <%=h order_article.article.manufacturer %></td>
</tr> </tr>

View File

@ -2,7 +2,7 @@
%tr %tr
%th Name %th Name
%th Gebinde %th Gebinde
%th Netto/FC-Preis %th Netto-/Bruttopreis
%th Bestellte Einheiten %th Bestellte Einheiten
%th Volle Gebinde %th Volle Gebinde
- total_net, total_gross, counter = 0, 0, 0 - total_net, total_gross, counter = 0, 0, 0
@ -12,19 +12,19 @@
%td{:colspan => "9"} %td{:colspan => "9"}
- order_articles.each do |order_article| - order_articles.each do |order_article|
- net_price = order_article.price.price - net_price = order_article.price.price
- fc_price = order_article.price.fc_price - gross_price = order_article.price.gross_price
- units = order_article.units_to_order - units = order_article.units_to_order
- unit_quantity = order_article.price.unit_quantity - unit_quantity = order_article.price.unit_quantity
- total_net += units * unit_quantity * net_price - total_net += units * unit_quantity * net_price
- total_gross += units * unit_quantity * fc_price - total_gross += units * unit_quantity * gross_price
%tr{:class => cycle('even', 'odd', :name => 'articles'), :style => "color: #{units > 0 ? 'green' : 'red'}"} %tr{:class => cycle('even', 'odd', :name => 'articles'), :style => "color: #{units > 0 ? 'green' : 'red'}"}
%td=h order_article.article.name %td=h order_article.article.name
%td= "#{unit_quantity} x #{order_article.article.unit}" %td= "#{unit_quantity} x #{order_article.article.unit}"
%td= "#{number_to_currency(net_price)} / #{number_to_currency(fc_price)}" %td= "#{number_to_currency(net_price)} / #{number_to_currency(gross_price)}"
%td= "#{order_article.quantity} + #{order_article.tolerance}" if unit_quantity > 1 %td= "#{order_article.quantity} + #{order_article.tolerance}" if unit_quantity > 1
%td= units %td= units
%p %p
Summe (Netto/FC-Preise): Summe (Netto/Brutto-Preise):
= "#{number_to_currency(total_net)} / #{number_to_currency(total_gross)}" = "#{number_to_currency(total_net)} / #{number_to_currency(total_gross)}"
%p %p
Bestellte Artikel. Bestellte Artikel.

View File

@ -41,7 +41,7 @@ pdf.text "Ansprechpartner: " + @order.supplier.contact_person
pdf.move_down 10 pdf.move_down 10
# Articles # Articles
data = @order.order_articles.all(:include => :article).collect do |a| data = @order.order_articles.ordered.all(:include => :article).collect do |a|
[a.article.order_number, a.units_to_order, a.article.name, [a.article.order_number, a.units_to_order, a.article.name,
a.price.unit_quantity, a.article.unit, a.price.price] a.price.unit_quantity, a.article.unit, a.price.price]
end end

View File

@ -22,7 +22,7 @@ for group_order in @order.group_orders
total = 0 total = 0
data = [] data = []
group_order.group_order_articles.each do |goa| group_order.group_order_articles.ordered.each do |goa|
price = goa.order_article.price.fc_price price = goa.order_article.price.fc_price
quantity = goa.quantity quantity = goa.quantity
sub_total = price * quantity sub_total = price * quantity

View File

@ -10,7 +10,7 @@ pdf.footer [pdf.margin_box.left, pdf.margin_box.bottom-5] do
end end
max_order_articles_per_page = 17 # How many order_articles shoud written on a page max_order_articles_per_page = 17 # How many order_articles shoud written on a page
order_articles = @order.order_articles order_articles = @order.order_articles.ordered
pdf.text "Artikelübersicht", :style => :bold pdf.text "Artikelübersicht", :style => :bold
pdf.move_down 5 pdf.move_down 5

View File

@ -1,10 +1,10 @@
- title _("Show order") - title _("Show order")
#element_navigation #element_navigation
= link_to_unless @order.previous == @order, _("Previous order"), @order.previous = link_to_unless @order.previous == @order, "<< #{@order.previous.supplier.name}", @order.previous
| |
= link_to _("Overview"), orders_path = link_to _("Overview"), orders_path
| |
= link_to_unless @order.next == @order, _("Next order"), @order.next = link_to_unless @order.next == @order, "#{@order.next.supplier.name} >>", @order.next
// Order summary // Order summary
.left_column{:style => "width:65em"} .left_column{:style => "width:65em"}
@ -33,18 +33,21 @@
= "[#{@order.group_orders.find(:all, :include => :ordergroup).collect{|g| g.ordergroup.name}.join(', ')}]" = "[#{@order.group_orders.find(:all, :include => :ordergroup).collect{|g| g.ordergroup.name}.join(', ')}]"
%p %p
Bruttosummer aller Artikel Netto/Bruttosumme aller Artikel
%b= number_to_currency(@order.sum(:gross)) %b= "#{number_to_currency(@order.sum(:net))} / #{number_to_currency(@order.sum(:gross))}"
- if @order.finished? %p
| Bestellte Artikel:
Bestellte Artikel: %b= @order.order_articles.ordered.count
%b= @order.order_articles.ordered.count
- if @order.open?
%p %p
= link_to icon(:edit), edit_order_path(@order) Aktionen:
= link_to icon(:delete), @order, :confirm => "Willst du wirklich die Bestellung löschen?", :method => :delete - if @order.open?
= link_to '[beenden]', finish_order_path(@order), :method => :post = link_to "Bearbeiten", edit_order_path(@order)
= link_to 'Beenden!', finish_order_path(@order), :method => :post, :confirm => "Willst Du wirklich die Bestellung beenden?\nEs gibt kein zurück.."
- unless @order.closed?
= link_to "Löschen", @order, :confirm => "Willst du wirklich die Bestellung löschen?", :method => :delete
- unless @order.open? - unless @order.open?
%p %p

View File

@ -4,20 +4,23 @@
%th{:colspan => '3'} Legende %th{:colspan => '3'} Legende
%tr %tr
%th{:style => 'width:70%'} Bestellgruppe %th{:style => 'width:70%'} Bestellgruppe
%th Menge %th Bestellt
%th Bekommen
%th Gesamtpreis %th Gesamtpreis
- for order_article in order.order_articles.all(:include => [:article, :article_price]) - for order_article in order.order_articles.ordered.all(:include => [:article, :article_price])
%table{:style => "margin-bottom:1em"} %table{:style => "margin-bottom:1em"}
%thead %thead
%tr %tr
%th{:colspan => "3"} %th{:colspan => "4"}
= order_article.article.name = order_article.article.name
= "(#{order_article.article.unit} | #{order_article.price.unit_quantity} | #{number_to_currency(order_article.price.fc_price)})" = "(#{order_article.article.unit} | #{order_article.price.unit_quantity} | #{number_to_currency(order_article.price.gross_price)})"
%tbody %tbody
- for goa in order_article.group_order_articles - for goa in order_article.group_order_articles
%tr{:class => cycle('even', 'odd', :name => 'groups')} %tr{:class => cycle('even', 'odd', :name => 'groups')}
%td{:style => "width:70%"}=h goa.group_order.ordergroup.name %td{:style => "width:70%"}=h goa.group_order.ordergroup.name
%td= "#{goa.quantity} (#{goa.tolerance})" %td= "#{goa.quantity} (#{goa.tolerance})"
%td
%b= "#{goa.quantity_result} + #{goa.tolerance_result}"
%td= number_to_currency(order_article.price.fc_price * goa.quantity) %td= number_to_currency(order_article.price.fc_price * goa.quantity)
- reset_cycle('groups') - reset_cycle('groups')

View File

@ -18,13 +18,13 @@
%th{:colspan => "6"}=h group_order.ordergroup.name %th{:colspan => "6"}=h group_order.ordergroup.name
%tbody %tbody
- total = 0 - total = 0
- for goa in group_order.group_order_articles.all(:include => :order_article) - for goa in group_order.group_order_articles.ordered.all(:include => :order_article)
- fc_price = goa.order_article.price.fc_price - fc_price = goa.order_article.price.fc_price
- subTotal = fc_price * goa.quantity - subTotal = fc_price * goa.quantity
- total += subTotal - total += subTotal
%tr{:class => cycle('even', 'odd', :name => 'articles')} %tr{:class => cycle('even', 'odd', :name => 'articles')}
%td{:style => "width:40%"}=h goa.order_article.article.name %td{:style => "width:40%"}=h goa.order_article.article.name
%td= "#{goa.quantity} (#{goa.tolerance})" %td= "#{goa.result[:quantity]} (#{goa.result[:tolerance]})"
%td= number_to_currency(fc_price) %td= number_to_currency(fc_price)
%td= goa.order_article.price.unit_quantity %td= goa.order_article.price.unit_quantity
%td= goa.order_article.article.unit %td= goa.order_article.article.unit

View File

@ -3,21 +3,21 @@ class RefactorOrderLogic < ActiveRecord::Migration
# TODO: Combine migrations since foodsoft3-development into one file # TODO: Combine migrations since foodsoft3-development into one file
# and try to build a migration path from old data. # and try to build a migration path from old data.
# == Ordergroups # # == Ordergroups
add_column :groups, :deleted_at, :datetime # acts_as_paranoid # add_column :groups, :deleted_at, :datetime # acts_as_paranoid
remove_column :groups, :actual_size # Useless, desposits are better stored within a transaction.note # remove_column :groups, :actual_size # Useless, desposits are better stored within a transaction.note
# move contact-infos from users to ordergroups # # move contact-infos from users to ordergroups
add_column :groups, :contact_person, :string # add_column :groups, :contact_person, :string
add_column :groups, :contact_phone, :string # add_column :groups, :contact_phone, :string
add_column :groups, :contact_address, :string # add_column :groups, :contact_address, :string
Ordergroup.all.each do |ordergroup| # Ordergroup.all.each do |ordergroup|
contact = ordergroup.users.first # contact = ordergroup.users.first
if contact # if contact
ordergroup.update_attributes :contact_person => contact.name, # ordergroup.update_attributes :contact_person => contact.name,
:contact_phone => contact.phone, :contact_address => contact.address # :contact_phone => contact.phone, :contact_address => contact.address
end # end
end # end
remove_column :users, :address # remove_column :users, :address
# #
# # == Article # # == Article
# rename_column :articles, :net_price, :price # rename_column :articles, :net_price, :price
@ -77,6 +77,11 @@ class RefactorOrderLogic < ActiveRecord::Migration
# #
# # == GroupOrder # # == GroupOrder
# change_column :group_orders, :updated_by_user_id, :integer, :default => nil, :null => true # change_column :group_orders, :updated_by_user_id, :integer, :default => nil, :null => true
# == GroupOrderArticle
# The order result in ordergroup is now saved!
add_column :group_order_articles, :quantity_result, :integer, :default => nil
add_column :group_order_articles, :tolerance_result, :integer, :default => nil
end end
def self.down def self.down

View File

@ -9,7 +9,7 @@
# #
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20090120184410) do ActiveRecord::Schema.define(:version => 20090119155930) do
create_table "article_categories", :force => true do |t| create_table "article_categories", :force => true do |t|
t.string "name", :default => "", :null => false t.string "name", :default => "", :null => false

View File

@ -41,7 +41,7 @@ und OrderArticle miteinander verbunden.
Bei jeder Bestellung wird außerdem die Summe der Menge, Toleranz in GroupOrderArticle Bei jeder Bestellung wird außerdem die Summe der Menge, Toleranz in GroupOrderArticle
abgelegt. Allerdings muss jede Aenderung dieser Mengen mit protokolliert werden. abgelegt. Allerdings muss jede Aenderung dieser Mengen mit protokolliert werden.
Dies ist wichtig, weil spaeter die Zuteilung der Einheiten pro bestellten Artikel Dies ist wichtig, weil spaeter die Zuteilung der Einheiten pro bestellten Artikel
nach der chronoligischen Reihenfolge erfolgt. (s.u.) nach der chronologischen Reihenfolge erfolgt. (s.u.)
Das passiert dann in der Tabelle group_order_article_quantities. Das passiert dann in der Tabelle group_order_article_quantities.
(GroupOrderArticleQuantity) (GroupOrderArticleQuantity)

View File

@ -3,19 +3,22 @@
# #
# Table name: suppliers # Table name: suppliers
# #
# id :integer(4) not null, primary key # id :integer not null, primary key
# name :string(255) not null # name :string(255) default(""), not null
# address :string(255) not null # address :string(255) default(""), not null
# phone :string(255) not null # phone :string(255) default(""), not null
# phone2 :string(255) # phone2 :string(255)
# fax :string(255) # fax :string(255)
# email :string(255) # email :string(255)
# url :string(255) # url :string(255)
# delivery_days :string(255) # contact_person :string(255)
# note :string(255) # customer_number :string(255)
# created_on :datetime # delivery_days :string(255)
# updated_on :datetime # order_howto :string(255)
# lists :string(255) # note :string(255)
# shared_supplier_id :integer
# min_order_quantity :string(255)
# deleted_at :datetime
# #
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html

View File

@ -1,18 +0,0 @@
require File.dirname(__FILE__) + '/../test_helper'
require 'finance_controller'
# Re-raise errors caught by the controller.
class FinanceController; def rescue_action(e) raise e end; end
class FinanceControllerTest < Test::Unit::TestCase
def setup
@controller = FinanceController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
end
# Replace this with your real tests.
def test_truth
assert true
end
end