Initial commit of foodsoft 2

This commit is contained in:
Benjamin Meichsner 2009-01-06 11:49:19 +01:00
commit 5b9a7e05df
657 changed files with 70444 additions and 0 deletions

View file

@ -0,0 +1,239 @@
class AdminController < ApplicationController
before_filter :authenticate_admin
filter_parameter_logging :password, :password_confirmation # do not log passwort parameters
verify :method => :post, :only => [ :destroyUser, :createUser, :updateUser, :destroyGroup, :createGroup, :updateGroup], :redirect_to => { :action => :index }
# Messages
MSG_USER_CREATED = 'Benutzer_in wurde erfolgreich angelegt.'
MSG_USER_UPDATED = 'Änderungen wurden gespeichert'
MSG_USER_DELETED = 'Benutzer_in wurde gelöscht'
ERR_NO_SELF_DELETE = 'Du darfst Dich nicht selbst löschen'
MESG_NO_ADMIN_ANYMORE = "Du bist nun kein Admin mehr"
def index
@user = self.current_user
@groups = Group.find(:all, :limit => 5, :order => 'created_on DESC')
@users = User.find(:all, :limit => 5, :order => 'created_on DESC')
end
# ************************** group actions **************************
def listGroups
if (params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 100)
@per_page = params[:per_page].to_i
else
@per_page = 20
end
# if the search field is used
conditions = "name LIKE '%#{params[:query]}%'" unless params[:query].nil?
@total = Group.count(:conditions => conditions)
@groups = Group.paginate(:conditions => conditions, :page => params[:page], :per_page => @per_page, :order => 'type DESC, name')
respond_to do |format|
format.html # index.html.erb
format.js do
render :update do |page|
page.replace_html 'table', :partial => "listGroups"
end
end
end
end
def showGroup
@group = Group.find(params[:id])
end
def newGroup
@group = Group.new
render :action => 'newGroup'
end
def newOrderGroup
@group = OrderGroup.new
render :action => 'newGroup'
end
def createGroup
@group = Group.new(params[:group])
if @group.save
flash[:notice] = 'Neue Gruppe wurde angelegt.'
redirect_to :action => 'members', :id => @group.id
else
flash[:error] = 'Gruppe konnte nicht angelegt werden.'
render :action => 'newGroup'
end
end
def createOrderGroup
@group = OrderGroup.new(params[:group])
@group.account_balance = 0
@group.account_updated = Time.now
if @group.save
flash[:notice] = 'Neue Bestellgruppe wurde angelegt.'
redirect_to :action => 'members', :id => @group.id
else
flash[:error] = 'Gruppe konnte nicht angelegt werden.'
render :action => 'newGroup'
end
end
def editGroup
@group = Group.find(params[:id])
render :template => 'groups/edit'
end
def updateGroup
@group = Group.find(params[:id])
if @group.update_attributes(params[:group])
flash[:notice] = 'Group was successfully updated.'
redirect_to :action => 'showGroup', :id => @group
else
render :template => 'groups/edit'
end
end
def destroyGroup
begin
group = Group.find(params[:id])
group.destroy
redirect_to :action => 'listGroups'
rescue => error
flash[:error] = error.to_s
redirect_to :action => "showGroup", :id => group
end
end
# ************************** Membership methods ******************************
# loads the membership-page
def members
@group = Group.find(params[:id])
end
# adds a new member to the group
def addMember
@group = Group.find(params[:id])
user = User.find(params[:user])
Membership.create(:group => @group, :user => user)
redirect_to :action => 'memberships_reload', :id => @group
end
# the membership will find an end....
def dropMember
begin
group = Group.find(params[:group])
Membership.find(params[:membership]).destroy
if User.find(@current_user.id).role_admin?
redirect_to :action => 'memberships_reload', :id => group
else
# If the user drops himself from admin group
flash[:notice] = MESG_NO_ADMIN_ANYMORE
render(:update) {|page| page.redirect_to :controller => "index"}
end
rescue => error
flash[:error] = error.to_s
redirect_to :action => 'memberships_reload', :id => group
end
end
# the two boxes 'members' and 'non members' will be reload through ajax
def memberships_reload
@group = Group.find(params[:id])
render :update do |page|
page.replace_html 'members', :partial => 'groups/members', :object => @group
page.replace_html 'non_members', :partial => 'groups/non_members', :object => @group
end
end
# ****************************** User methdos ******************************
def listUsers
if (params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 100)
@per_page = params[:per_page].to_i
else
@per_page = 20
end
# if the search field is used
conditions = "first_name LIKE '%#{params[:query]}%' OR last_name LIKE '%#{params[:query]}%'" unless params[:query].nil?
@total = User.count(:conditions => conditions)
@users = User.paginate :page => params[:page], :conditions => conditions, :per_page => @per_page, :order => 'nick'
respond_to do |format|
format.html # listUsers.haml
format.js do
render :update do |page|
page.replace_html 'table', :partial => "listUsers"
end
end
end
end
def showUser
@user = User.find(params[:id])
end
def newUser
@user = User.new
if request.xml_http_request?
render :update do |page|
page.replace_html 'userForm', :partial => "newUser"
page['newUser'].show
end
end
end
def createUser
@user = User.new(params[:user])
@user.set_password({:required => true}, params[:user][:password], params[:user][:password_confirmation])
if @user.errors.empty? && @user.save
for setting in User::setting_keys.keys
@user.settings[setting] = (params[:user][:settings] && params[:user][:settings][setting] == '1' ? '1' : nil)
end
flash[:notice] = MSG_USER_CREATED
redirect_to :action => 'listUsers'
else
render :action => 'newUser'
end
end
def editUser
@user = User.find(params[:id])
if request.xml_http_request?
render :update do |page|
page.replace_html 'userForm', :partial => "newUser"
page['newUser'].show
end
end
end
def updateUser
@user = User.find(params[:id])
@user.set_password({:required => false}, params[:user][:password], params[:user][:password_confirmation])
@user.attributes = params[:user]
for setting in User::setting_keys.keys
@user.settings[setting] = (params[:user][:settings] && params[:user][:settings][setting] == '1' ? '1' : nil)
end
if @user.errors.empty? && @user.save
flash[:notice] = MSG_USER_UPDATED
redirect_to :action => 'showUser', :id => @user
else
render :action => 'editUser'
end
end
def destroyUser
user = User.find(params[:id])
if user.nick == @current_user.nick
# deny destroying logged-in-user
flash[:error] = ERR_NO_SELF_DELETE
else
user.destroy
flash[:notice] = MSG_USER_DELETED
end
redirect_to :action => 'listUsers'
end
end

View file

@ -0,0 +1,136 @@
require 'user'
class ApplicationController < ActionController::Base
# Gettext For i18n
# locale is chosen through browser http request
init_gettext "foodsoft"
before_filter :select_foodcoop, :authenticate, :store_controller
# before_filter :ensureUTF8
after_filter :send_email_messages, :remove_controller
# sends a mail, when an error occurs
# see plugins/exception_notification
include ExceptionNotifiable
# Returns the controller handling the current request.
def self.current
Thread.current[:application_controller]
end
protected
def current_user
begin
# check if there is a valid session and return the logged-in user (its object)
if session['user_and_subdomain']
id, subdomain = session['user_and_subdomain'].split
# for shared-host installations. check if the cookie-subdomain fits to request.
return User.current_user = User.find(id) if request.subdomains.first == subdomain
end
rescue
reset_session
flash[:error]= _("An error has occurred. Please login again.")
redirect_to :controller => 'login'
end
end
def current_user=(user)
session['user_and_subdomain'] = [user.id, request.subdomains.first].join(" ")
end
def return_to
session['return_to']
end
def return_to=(uri)
session['return_to'] = uri
end
def deny_access
self.return_to = request.request_uri
redirect_to :controller => 'login', :action => 'denied'
return false
end
private
# selects the foodcoop depending on the subdomain
def select_foodcoop
# get subdomain and set FoodSoft-class-variable (for later config-requests)
FoodSoft.subdomain = request.subdomains.first
# set database-connection
ActiveRecord::Base.establish_connection(FoodSoft.get_database)
end
# Ensures the HTTP content-type encoding is set to "UTF-8" for "text/html" contents.
def ensureUTF8
content_type = headers["Content-Type"] || "text/html"
if /^text\//.match(content_type)
headers["Content-Type"] = "#{content_type}; charset=utf-8"
end
end
def authenticate(role = 'any')
# Attempt to retrieve authenticated user from controller instance or session...
if !(user = current_user)
# No user at all: redirect to login page.
self.return_to = request.request_uri
redirect_to :controller => 'login'
return false
else
# We have an authenticated user, now check role...
# Roles gets the user through his memberships.
hasRole = case role
when "admin" then user.role_admin?
when "finance" then user.role_finance?
when "article_meta" then user.role_article_meta?
when "suppliers" then user.role_suppliers?
when "orders" then user.role_orders?
when "any" then true # no role required
else false # any unknown role will always fail
end
if hasRole
@current_user = user
else
deny_access
end
end
end
def authenticate_admin
authenticate('admin')
end
def authenticate_finance
authenticate('finance')
end
def authenticate_article_meta
authenticate('article_meta')
end
def authenticate_suppliers
authenticate('suppliers')
end
def authenticate_orders
authenticate('orders')
end
# Stores this controller instance as a thread local varibale to be accessible from outside ActionController/ActionView.
def store_controller
Thread.current[:application_controller] = self
end
# Sets the thread local variable that holds a reference to the current controller to nil.
def remove_controller
Thread.current[:application_controller] = nil
end
# Sends any pending emails that were created during this request.
def send_email_messages
Message.send_emails if Message.pending?
end
end

View file

@ -0,0 +1,418 @@
class ArticlesController < ApplicationController
before_filter :authenticate_article_meta
verify :method => :post,
:only => [ :destroyArticle, :createArticle, :updateArticle,
:update_all, :createArticlesFromFile, :update_selected_articles ],
:redirect_to => { :action => :list }
# messages
ERROR_NO_SELECTED_ARTICLES = 'Du hast keine Artikel ausgewählt'
MSG_ALL_CHECKED_DESTROYED = 'Alle gewählten Artikel wurden gelöscht'
MSG_ALL_CHECKED_UNAVAILABLE = 'Alle gewählten Artikel wurden auf "nicht verfügbar" gesetzt'
MSG_ALL_CHECKED_AVAILABLE = 'Alle gewählten Artikel wurden auf "verfügbar" gesetzt'
ERROR_NO_SELECTED_ACTION = 'Keine Aktion ausgewählt!'
ERROR_UPDATE_ARTICLES = 'Ein Fehler ist aufgetreten: '
def index
@suppliers = Supplier.find(:all)
end
def list
if params[:id]
@supplier = Supplier.find(params[:id])
@suppliers = Supplier.find(:all)
if (params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 500)
@per_page = params[:per_page].to_i
else
@per_page = 30
end
if params['sort']
sort = case params['sort']
when "name" then "articles.name"
when "unit" then "articles.unit"
when "category" then "article_categories.name"
when "note" then "articles.note"
when "availability" then "articles.availability"
when "name_reverse" then "articles.name DESC"
when "unit_reverse" then "articles.unit DESC"
when "category_reverse" then "article_categories.name DESC"
when "note_reverse" then "articles.note DESC"
when "availability_reverse" then "articles.availability DESC"
end
else
sort = "article_categories.name, articles.name"
end
# if somebody uses the search field:
conditions = ["articles.name LIKE ?", "%#{params[:query]}%"] unless params[:query].nil?
@total = @supplier.articles.count(:conditions => conditions)
@articles = @supplier.articles.paginate(:order => sort,
:conditions => conditions,
:page => params[:page],
:per_page => @per_page,
:include => :article_category)
respond_to do |format|
format.html # list.haml
format.js do
render :update do |page|
page.replace_html 'table', :partial => "list"
end
end
end
else
redirect_to :action => 'index'
end
end
def newArticle
@supplier = Supplier.find(params[:supplier])
@article = Article.new(:supplier => @supplier, :tax => 7.0)
render :update do |page|
page["edit_article"].replace_html :partial => 'new'
page["edit_article"].show
end
end
def createArticle
@article = Article.new(params[:article])
if @article.valid? and @article.save
render :update do |page|
page.Element.hide('edit_article')
page.insert_html :top, 'listbody', :partial => 'new_article_row'
page[@article.id.to_s].visual_effect(:highlight,
:duration => 2)
# highlights article
if !@article.availability
page[@article.id.to_s].addClassName 'unavailable'
else
page[@article.id.to_s].addClassName 'just_updated'
end
end
else
render :update do |page|
page.replace_html 'edit_article', :partial => "new"
end
end
end
# edit an article and its price
def editArticle
@article = Article.find(params[:id])
render :update do |page|
page["edit_article"].replace_html :partial => 'edit'
page["edit_article"].show
end
#render :partial => "quick_edit", :layout => false
end
# Updates one Article and highlights the line if succeded
def updateArticle
@article = Article.find(params[:id])
if @article.update_attributes(params[:article])
render :update do |page|
page["edit_article"].hide
page[@article.id.to_s].replace_html :partial => 'article_row'
# hilights an updated article if the article ist available
page[@article.id.to_s].addClassName 'just_updated' if @article.availability
# highlights an available article and de-highlights in other case
if !@article.availability
page[@article.id.to_s].addClassName 'unavailable'
# remove updated-class
page[@article.id.to_s].removeClassName 'just_updated'
else
page[@article.id.to_s].removeClassName 'unavailable'
end
page[@article.id.to_s].visual_effect(:highlight, :delay => 0.5, :duration => 2)
end
else
render :update do |page|
page["edit_article"].replace_html :partial => "edit"
end
end
end
# Deletes article from database. send error msg, if article is used in a current order
def destroyArticle
@article = Article.find(params[:id])
@order = @article.inUse #if article is in an active Order, the Order will be returned
if @order
render :update do |page|
page.insert_html :after, @article.id.to_s, :partial => 'destroyActiveArticle'
end
else
@article.destroy
render :update do |page|
page[@article.id.to_s].remove
end
end
end
# Renders a form for editing all articles from a supplier
def edit_all
@supplier = Supplier.find(params[:id])
end
# Updates all article of specific supplier
# deletes all articles from params[outlisted_articles]
def update_all
currentArticle = nil # used to find out which article caused a validation exception
begin
Article.transaction do
@supplier = Supplier.find(params[:supplier][:id]) if params[:supplier][:id]
unless params[:article].blank?
# Update other article attributes...
i = 0
for id in params[:article].keys
currentArticle = Article.find(id)
currentArticle.update_attributes!(params[:article].values[i])
i += 1
end
end
# delete articles
if params[:outlisted_articles]
params[:outlisted_articles].keys.each {|id| Article.find(id).destroy }
end
end
# Successfully done.
flash[:notice] = 'Alle Artikel und Preise wurden aktalisiert'
redirect_to :action => 'list', :id => @supplier
rescue => e
# An error has occurred, transaction has been rolled back.
if currentArticle
@failedArticle = currentArticle
flash[:error] = "Es trat ein Fehler beim Aktualisieren des Artikels '#{currentArticle.name}' auf: #{e.message}"
params[:sync] ? redirect_to(:action => "list", :id => @supplier) : render(:action => 'edit_all')
else
flash[:error] = "Es trat ein Fehler beim Aktualisieren der Artikel auf: #{e.message}"
redirect_to :action => "index"
end
end
end
# makes different actions on selected articles
def update_selected_articles
@supplier = Supplier.find(params[:supplier])
articles = Array.new
begin
raise ERROR_NO_SELECTED_ARTICLES if params[:selected_articles].nil?
params[:selected_articles].each do |article|
articles << Article.find(article) # put selected articles in an array
end
case params[:selected_action].to_s
when 'destroy'
articles.each {|a| a.destroy }
flash[:notice] = MSG_ALL_CHECKED_DESTROYED
when 'setNotAvailable'
articles.each {|a| a.update_attribute(:availability, false) }
flash[:notice] = MSG_ALL_CHECKED_UNAVAILABLE
when 'setAvailable'
articles.each {|a| a.update_attribute(:availability, true) }
flash[:notice] = MSG_ALL_CHECKED_AVAILABLE
else
flash[:error] = ERROR_NO_SELECTED_ACTION
end
# action succeded
redirect_to :action => 'list', :id => @supplier
rescue => e
flash[:error] = ERROR_UPDATE_ARTICLES + e
redirect_to :action => 'list', :id => @supplier
end
end
#************** start article categories ************************
def newCategory
@article_category = ArticleCategory.new
render :update do |page|
page['category_form'].replace_html :partial => 'article_categories/new'
page['category_form'].show
end
end
def createCategory
@article_category = ArticleCategory.new(params[:article_category])
if @article_category.save
render :update do |page|
page['category_form'].hide
page['category_list'].replace_html :partial => 'article_categories/list'
page['category_'+@article_category.id.to_s].visual_effect(:highlight,
:duration => 2)
end
else
render :update do |page|
page['category_form'].replace_html :partial => 'article_categories/new'
end
end
end
def editCategory
@article_category = ArticleCategory.find(params[:id])
render :update do |page|
page['category_form'].replace_html :partial => 'article_categories/edit'
page['category_form'].show
end
end
def updateCategory
@article_category = ArticleCategory.find(params[:id])
if @article_category.update_attributes(params[:article_category])
render :update do |page|
page['category_form'].hide
page['category_list'].replace_html :partial => 'article_categories/list'
page['category_'+@article_category.id.to_s].visual_effect(:highlight,
:duration => 2)
end
else
render :update do |page|
page['category_form'].replace_html :partial => 'article_categories/edit'
end
end
end
def destroyCategory
@article_category = ArticleCategory.find(params[:id])
id = @article_category.id.to_s #save the id before destroying the object
if @article_category.destroy
render :update do |page|
page['category_'+id].visual_effect :drop_out
end
end
end
# lets start with parsing articles from uploaded file, yeah
# Renders the upload form
def upload_articles
end
# parses the articles from a csv and creates a form-table with the parsed data.
# the csv must have the following format:
# status | number | name | note | manufacturer | origin | unit | clear price | unit_quantity | tax | deposit | scale quantity | scale price | category
# the first line will be ignored.
# field-seperator: ";"
# text-seperator: ""
def parse_articles
begin
@articles = Array.new
articles, outlisted_articles = FoodsoftFile::parse(params[:articles]["file"])
articles.each do |row|
# creates a new article and price
article = Article.new( :name => row[:name],
:note => row[:note],
:manufacturer => row[:manufacturer],
:origin => row[:origin],
:unit => row[:unit],
:article_category => ArticleCategory.find_by_name(row[:category]),
:net_price => row[:price],
:unit_quantity => row[:unit_quantity],
:order_number => row[:number],
:deposit => row[:deposit],
:tax => row[:tax])
# stop parsing, when an article isn't valid
unless article.valid?
raise article.errors.full_messages.join(", ") + _(" ..in line ") + (articles.index(row) + 2).to_s
end
@articles << article
end
flash.now[:notice] = @articles.size.to_s + _(" articles are parsed successfully.")
rescue => e
flash[:error] = _("An error has occurred: ") + e.message
redirect_to :action => 'upload_articles'
end
end
# creates articles from form
def create_articles_from_file
@supplier = Supplier.find(params[:supplier][:id])
begin
Article.transaction do
i = 0
params[:article].each do
@article = Article.new(params[:article][i])
@article.supplier = @supplier
@article.save!
i += 1
end
end
# Successfully done.
flash[:notice] = _("The articles are saved successfully")
redirect_to :action => 'list', :id => @supplier
rescue => e
# An error has occurred, transaction has been rolled back.
flash[:error] = _("An error occured: ") + " #{e.message}"
redirect_to :action => 'upload_articles'
end
end
# renders a view to import articles in local database
#
def list_shared_articles
@supplier = Supplier.find(params[:id])
conditions = []
conditions << "supplier_id = #{@supplier.shared_supplier.id}"
# check for keywords
conditions << params[:import_query].split(' ').collect { |keyword| "name LIKE '%#{keyword}%'" }.join(' AND ') unless params[:import_query].blank?
# check for selected lists
conditions << "(" + params[:lists].collect {|list| "list = '#{list[0]}'"}.join(" OR ") + ")" if params[:lists]
# check for regional articles
conditions << "origin = 'REG'" if params[:regional]
@articles = SharedArticle.paginate :page => params[:page], :per_page => 10, :conditions => conditions.join(" AND ")
render :update do |page|
page.replace_html 'search_results', :partial => "import_search_results"
end
end
# fills a form whith values of the selected shared_article
def new_import
shared_article = SharedArticle.find(params[:id])
@article = Article.new(
:supplier_id => params[:supplier_id],
:name => shared_article.name,
:unit => shared_article.unit,
:note => shared_article.note,
:manufacturer => shared_article.manufacturer,
:origin => shared_article.origin,
:net_price => shared_article.price,
:tax => shared_article.tax,
:deposit => shared_article.deposit,
:unit_quantity => shared_article.unit_quantity,
:order_number => shared_article.number,
# convert to db-compatible-string
:shared_updated_on => shared_article.updated_on.to_formatted_s(:db))
render :update do |page|
page["edit_article"].replace_html :partial => 'new'
page["edit_article"].show
end
end
# sync all articles with the external database
# renders a form with articles, which should be updated
def sync_articles
@supplier = Supplier.find(params[:id])
# check if there is an shared_supplier
unless @supplier.shared_supplier
flash[:error]= @supplier.name + _(" ist not assigned to an external database.")
redirect_to :action => "list", :id => @supplier
end
# sync articles against external database
@updated_articles, @outlisted_articles = @supplier.sync_all
# convert to db-compatible-string
@updated_articles.each {|a, b| a.shared_updated_on = a.shared_updated_on.to_formatted_s(:db)}
if @updated_articles.empty? && @outlisted_articles.empty?
flash[:notice] = _("The database is up to date.")
redirect_to :action => 'list', :id => @supplier
end
end
end

View file

@ -0,0 +1,375 @@
class FinanceController < ApplicationController
before_filter :authenticate_finance
# Messages
MSG_TRANSACTION_SUCCESS = 'Transaktion erfolgreich angelegt.'
ERROR_TRANSACTION_FAILED = 'Transaktion konnte nicht angelegt werden!'
MSG_ORDER_SET_BOOKED = 'Die Bestellung wurde auf "gebucht" gesetzt.'
ERROR_ORDER_NOT_FINISHED = 'Die Bestellung ist noch nicht beendet.'
MSG_ORDER_BALANCED = "Bestellung wurde erfolgreich abgerechnet, die Kontostände aktualisiert."
ERROR_BALANCE_ORDER = "Ein Fehler ist beim Abrechnen aufgetreten: "
def index
@financial_transactions = FinancialTransaction.find(:all, :order => "created_on DESC", :limit => 8)
@orders = Order.find(:all, :conditions => 'finished = 1 AND booked = 0', :order => 'ends DESC')
end
#list all ordergroups
def listOrdergroups
@user = @current_user
if (params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 100)
@per_page = params[:per_page].to_i
else
@per_page = 20
end
if params["sort"]
sort = case params["sort"]
when "name" then "name"
when "size" then "actual_size"
when "account_balance" then "account_balance"
when "name_reverse" then "name DESC"
when "size_reverse" then "actual_size DESC"
when "account_balance_reverse" then "account_balance DESC"
end
else
sort = "name"
end
conditions = "name LIKE '%#{params[:query]}%'" unless params[:query].nil?
@total = OrderGroup.count(:conditions => conditions)
@groups = OrderGroup.paginate :conditions => conditions, :page => params[:page], :per_page => @per_page, :order => sort
respond_to do |format|
format.html # listOrdergroups.haml
format.js do
render :update do |page|
page.replace_html 'table', :partial => "listOrdergroups" # _listOrdergroups.haml
end
end
end
end
#new financial transactions (ordergroups)
def newTransaction
@group = OrderGroup.find(params[:id])
@financial_transaction = FinancialTransaction.new(:order_group => @group)
render :template => 'financial_transactions/new'
end
#save the new financial transaction and update the account_balance of the ordergroup
def createTransaction
@group = OrderGroup.find(params[:id])
amount = params[:financial_transaction][:amount]
note = params[:financial_transaction][:note]
begin
@group.addFinancialTransaction(amount, note, @current_user)
flash[:notice] = MSG_TRANSACTION_SUCCESS
redirect_to :action => 'listTransactions', :id => @group
rescue => e
@financial_transaction = FinancialTransaction.new(params[:financial_transaction])
flash.now[:error] = ERROR_TRANSACTION_FAILED + ' (' + e.message + ')'
render :template => 'financial_transactions/new'
end
end
# list transactions of a specific ordergroup
def listTransactions
@group = Group.find(params[:id])
if params['sort']
sort = case params['sort']
when "date" then "created_on"
when "note" then "note"
when "amount" then "amount"
when "date_reverse" then "created_on DESC"
when "note_reverse" then "note DESC"
when "amount_reverse" then "amount DESC"
end
else
sort = "created_on DESC"
end
conditions = ["note LIKE ?", "%#{params[:query]}%"] unless params[:query].nil?
@total = @group.financial_transactions.count(:conditions => conditions)
@financial_transactions = @group.financial_transactions.paginate(:page => params[:page],
:per_page => 10,
:conditions => conditions,
:order => sort)
respond_to do |format|
format.html {render :template => 'financial_transactions/list'}
format.js do
render :update do |page|
page.replace_html 'table', :partial => "financial_transactions/list"
end
end
end
end
# gives a view to add multiple transactions to different groups
# e.g. this is helpful when updating multiple accounts in case of a new statement
def new_transactions
end
# creates multiple transactions at once
def create_transactions
begin
note = params[:note]
raise _("Note is required!") if note.blank?
params[:financial_transactions].each do |trans|
# ignore empty amount fields ...
unless trans[:amount].blank?
OrderGroup.find(trans[:order_group_id]).addFinancialTransaction trans[:amount], note, @current_user
end
end
flash[:notice] = _('Saved all transactions successfully')
redirect_to :action => 'index'
rescue => error
flash[:error] = _("An error occured: ") + error.to_s
redirect_to :action => 'new_transactions'
end
end
# list finished orders for the order-clearing
def listOrders
@orders = Order.paginate_all_by_finished true, :page => params[:page], :per_page => 10, :order => 'ends DESC'
end
def editOrder
@order = Order.find(params[:id])
@comments = @order.comments
case params[:view]
when 'editResults'
render :partial => 'editResults'
when 'groupsOverview'
render :partial => 'groupsOverview'
when 'articlesOverview'
render :partial => 'articlesOverview'
when "editNote"
render :partial => "editNote"
end
end
def newArticleResult
@order = Order.find(params[:id])
@article = @order.order_article_results.build(:tax => 7, :deposit => 0)
render :update do |page|
page["edit_box"].replace_html :partial => "newArticleResult"
page["edit_box"].show
end
end
def createArticleResult
render :update do |page|
@article = OrderArticleResult.new(params[:order_article_result])
@article.fc_markup = FoodSoft::getPriceMarkup
@article.make_gross if @article.tax && @article.deposit && @article.net_price
if @article.valid?
@article.save
@order = @article.order
page["edit_box"].hide
page["order_summary"].replace_html :partial => 'summary'
page.insert_html :bottom, "result_table", :partial => "articleResults"
page["order_article_result_#{@article.id}"].visual_effect :highlight, :duration => 2
page["group_order_article_results_#{@article.id}"].show
else
page["edit_box"].replace_html :partial => "newArticleResult"
end
end
end
def editArticleResult
@article = OrderArticleResult.find(params[:id])
render :update do |page|
page["edit_box"].replace_html :partial => 'editArticleResult'
page["edit_box"].show
end
end
def updateArticleResult
@article = OrderArticleResult.find(params[:id])
@article.attributes=(params[:order_article_result]) # update attributes but doesn't save
@article.make_gross
@order = @article.order
@ordered_articles = @order.order_article_results
@group_orders = @order.group_order_results
render :update do |page|
if @article.save
page["edit_box"].hide
page["order_summary"].replace_html :partial => 'summary'
page["order_summary"].visual_effect :highlight, :duration => 2
page["order_article_result_#{@article.id}"].replace_html :partial => 'articleResult'
page['order_article_result_'+@article.id.to_s].visual_effect :highlight, :delay => 0.5, :duration => 2
page["group_order_article_results_#{@article.id}"].replace_html :partial => "groupOrderArticleResults"
else
page['edit_box'].replace_html :partial => 'editArticleResult'
end
end
end
def destroyArticleResult
if @article = OrderArticleResult.find(params[:id]).destroy
@order = @article.order
render :update do |page|
page["order_article_result_#{@article.id}"].remove
page["group_order_article_results_#{@article.id}"].remove
page["order_summary"].replace_html :partial => 'summary'
page["order_summary"].visual_effect :highlight, :duration => 2
end
end
end
def newGroupResult
@result = OrderArticleResult.find(params[:id]).group_order_article_results.build
render :update do |page|
page["edit_box"].replace_html :partial => "newGroupResult"
page["edit_box"].show
end
end
# Creates a new GroupOrderArticleResult
# If the the chosen OrderGroup hasn't ordered yet, a GroupOrderResult will created
def createGroupResult
@result = GroupOrderArticleResult.new(params[:group_order_article_result])
order = @result.order_article_result.order
orderGroup = OrderGroup.find(params[:group_order_article_result][:group_order_result_id])
# creates a new GroupOrderResult if necessary
unless @result.group_order_result = GroupOrderResult.find(:first,
:conditions => ["group_order_results.group_name = ? AND group_order_results.order_id = ?", orderGroup.name, order.id ])
@result.group_order_result = GroupOrderResult.create(:order => order, :group_name => orderGroup.name)
end
render :update do |page|
if @result.valid? && @result.save
@result.group_order_result.updatePrice #updates the price attribute
article = @result.order_article_result
page["edit_box"].hide
page.insert_html :after, "groups_results_#{article.id}", :partial => "groupResults"
page["group_order_article_result_#{@result.id}"].visual_effect :highlight, :duration => 2
page["groups_amount"].replace_html number_to_currency(article.order.sumPrice('groups'))
page["fcProfit"].replace_html number_to_currency(article.order.fcProfit)
page["fcProfit"].visual_effect :highlight, :duration => 2
# get the new sums for quantity and price and replace it
total = article.total
page["totalArticleQuantity_#{article.id}"].replace_html total[:quantity]
page["totalArticlePrice_#{article.id}"].replace_html number_to_currency(total[:price])
else
page["edit_box"].replace_html :partial => "newGroupResult"
end
end
end
def updateGroupResult
@result = GroupOrderArticleResult.find(params[:id])
render :update do |page|
if params[:group_order_article_result]
if @result.update_attribute(:quantity, params[:group_order_article_result][:quantity])
order = @result.group_order_result.order
groups_amount = order.sumPrice("groups")
article = @result.order_article_result
total = article.total
page["edit_box"].hide
page["groups_amount"].replace_html number_to_currency(groups_amount)
page["fcProfit"].replace_html number_to_currency(order.fcProfit)
page["groups_amount"].visual_effect :highlight, :duration => 2
page["fcProfit"].visual_effect :highlight, :duration => 2
page["group_order_article_result_#{@result.id}"].replace_html :partial => "groupResult"
page["group_order_article_result_#{@result.id}"].visual_effect :highlight, :duration => 2
page["totalArticleQuantity_#{article.id}"].replace_html total[:quantity]
page["totalArticlePrice_#{article.id}"].replace_html total[:price]
page["sum_of_article_#{article.id}"].visual_effect :highlight, :duration => 2
end
else
page["edit_box"].replace_html :partial => 'editGroupResult'
page["edit_box"].show
end
end
end
def destroyGroupResult
@result = GroupOrderArticleResult.find(params[:id])
if @result.destroy
render :update do |page|
article = @result.order_article_result
page["group_order_article_result_#{@result.id}"].remove
page["groups_amount"].replace_html number_to_currency(article.order.sumPrice('groups'))
page["fcProfit"].replace_html number_to_currency(article.order.fcProfit)
page["fcProfit"].visual_effect :highlight, :duration => 2
total = article.total # get total quantity and price for the ArticleResult
page["totalArticleQuantity_#{article.id}"].replace_html total[:quantity]
page["totalArticleQuantity_#{article.id}"].visual_effect :highlight, :duration => 2
page["totalArticlePrice_#{article.id}"].replace_html number_to_currency(total[:price])
page["totalArticlePrice_#{article.id}"].visual_effect :highlight, :duration => 2
end
end
end
def editOrderSummary
@order = Order.find(params[:id])
render :update do |page|
page["edit_box"].replace_html :partial => 'editSummary'
page["edit_box"].show
end
end
def updateOrderSummary
@order = Order.find(params[:id])
render :update do |page|
if @order.update_attributes(params[:order])
page["edit_box"].hide
page["order_summary"].replace_html :partial => "summary"
page["clear_invoice"].visual_effect :highlight, :duration => 2
else
page["edit_box"].replace_html :partial => 'editSummary'
end
end
end
def updateOrderNote
@order = Order.find(params[:id])
render :update do |page|
if @order.update_attribute(:note, params[:order][:note])
page["note"].replace_html simple_format(@order.note)
page["results"].replace_html :partial => "groupsOverview"
else
page["results"].replace_html :partial => "editNote"
end
end
end
# before the order will booked, a view lists all OrderGroups and its order_prices
def confirmOrder
@order = Order.find(params[:id])
end
# Balances the Order, Update of the OrderGroup.account_balances
def balanceOrder
@order = Order.find(params[:id])
begin
@order.balance(@current_user)
flash[:notice] = MSG_ORDER_BALANCED
redirect_to :action => "index"
rescue => e
flash[:error] = ERROR_BALANCE_ORDER + e
redirect_to :action =>"editOrder", :id => @order
end
end
# Set all GroupOrders that belong to this finished order to status 'booked'.
def setAllBooked
@order = Order.find(params[:id])
if (@order.finished?)
@order.booked = true
@order.updated_by = @current_user
@order.save!
flash[:notice] = MSG_ORDER_SET_BOOKED
redirect_to :action => 'listOrders', :id => @order
else
flash[:error] = ERROR_ORDER_NOT_FINISHED
redirect_to :action => 'listOrders', :id => @order
end
end
end

View file

@ -0,0 +1,261 @@
class IndexController < ApplicationController
# Messages
MSG_USER_UPDATED = 'Benutzeränderungen wurden gespeichert'
ERROR_NO_GROUP_MEMBER = 'Du bist kein Gruppenmitglied.'
MSG_GROUP_UPDATED = 'Gruppe wurde erfolgreich bearbeitet'
ERR_LAST_MEMBER = "Eine Benutzerin muss der Bestellgruppe erhalten bleiben"
MSG_MEMBERSHIP_ENDS = 'Du bist nun nicht mehr Mitglied der Gruppe '
ERR_CANNOT_INVITE = 'Du kannst niemanden in diese Gruppe einladen.'
MSG_INVITE_SUCCESS = 'Es wurde eine Einladung an %s geschickt.'
def index
@currentOrders = Order.find_current
@orderGroup = @current_user.find_ordergroup
if @orderGroup
@financial_transactions = @orderGroup.financial_transactions.find(:all, :order => 'created_on desc', :limit => 3)
end
# unread messages
@messages = Message.find_all_by_recipient_id_and_read(@current_user.id, false, :order => 'messages.created_on desc', :include => :sender)
# unaccepted tasks
@unaccepted_tasks = @current_user.unaccepted_tasks
# task in next week
@next_tasks = @current_user.next_tasks
# count tasks with no responsible person
# tasks for groups the current user is not a member are ignored
tasks = Task.find(:all, :conditions => ["assigned = ? and done = ?", false, false])
@unassigned_tasks_number = 0
for task in tasks
(@unassigned_tasks_number += 1) unless task.group && !current_user.is_member_of(task.group)
end
end
def myProfile
@user = @current_user
@user_columns = ["first_name", "last_name", "email", "phone", "address"]
end
def editProfile
@user = @current_user
end
def updateProfile
@user = @current_user
@user.set_password({:required => false}, params[:user][:password], params[:user][:password_confirmation])
@user.attributes = params[:user]
for setting in User::setting_keys.keys
@user.settings[setting] = (params[:user][:settings] && params[:user][:settings][setting] == '1' ? '1' : nil)
end
if @user.errors.empty? && @user.save
flash[:notice] = MSG_USER_UPDATED
redirect_to :action => 'myProfile'
else
render :action => 'editProfile'
end
end
def myOrdergroup
@user = @current_user
@ordergroup = @user.find_ordergroup
@ordergroup_column_names = ["Description", "Actual Size", "Balance", "Updated"]
@ordergroup_columns = ["description", "actual_size", "account_balance", "account_updated"]
#listing the financial transactions with ajax...
if params['sort']
sort = case params['sort']
when "date" then "created_on"
when "note" then "note"
when "amount" then "amount"
when "date_reverse" then "created_on DESC"
when "note_reverse" then "note DESC"
when "amount_reverse" then "amount DESC"
end
else
sort = "created_on DESC"
end
# or if somebody uses the search field:
conditions = ["note LIKE ?", "%#{params[:query]}%"] unless params[:query].nil?
@total = @ordergroup.financial_transactions.count(:conditions => conditions)
@financial_transactions = @ordergroup.financial_transactions.paginate(:page => params[:page],
:per_page => 10,
:conditions => conditions,
:order => sort)
respond_to do |format|
format.html # myOrdergroup.haml
format.js do
render :update do |page|
page.replace_html 'table', :partial => "financial_transactions/list"
end
end
end
end
def showGroup
@user = @current_user
@group = Group.find(params[:id])
end
def showUser
@user = User.find(params[:id])
end
def editGroup
@group = Group.find(params[:id])
authenticate_membership(@group)
# unless @group.member?(@current_user)
# flash[:error] = ERROR_NO_GROUP_MEMBER
# redirect_to :action => 'index'
# end
end
# update the Group
# only access to description for OrderGroups
def updateGroup
@group = Group.find(params[:id])
authenticate_membership(@group)
if @group.is_a?(OrderGroup)
@group.update_attribute(:description, params[:group][:description])
else
@group.update_attributes(params[:group])
end
if @group.errors.empty?
flash[:notice] = MSG_GROUP_UPDATED
redirect_to :action => 'showGroup', :id => @group
else
render :action => 'editGroup'
end
end
def members
@group = Group.find(params[:id])
authenticate_membership(@group)
end
# adds a new member to the group
def addMember
@group = Group.find(params[:id])
authenticate_membership(@group)
user = User.find(params[:user])
Membership.create(:group => @group, :user => user)
redirect_to :action => 'memberships_reload', :id => @group
end
# the membership will find an end....
def dropMember
begin
group = Group.find(params[:group])
authenticate_membership(group)
membership = Membership.find(params[:membership])
if group.is_a?(OrderGroup) && group.memberships.size == 1
# Deny dropping member if the group is an OrderGroup and there is only one member left.
flash[:error] = ERR_LAST_MEMBER
else
membership.destroy
end
redirect_to :action => 'memberships_reload', :id => group
rescue => error
flash[:error] = error.to_s
redirect_to :action => 'memberships_reload', :id => group
end
end
# the two boxes 'members' and 'non members' will be reload through ajax
def memberships_reload
@group = Group.find(params[:id])
unless @group.member?(@current_user)
flash[:error] = ERROR_NO_GROUP_MEMBER
render(:update) {|page| page.redirect_to :action => "myProfile"}
else
render :update do |page|
page.replace_html 'members', :partial => 'groups/members', :object => @group
page.replace_html 'non_members', :partial => 'groups/non_members', :object => @group
end
end
end
# checks if the current_user is member of given group.
# if fails the user will redirected to startpage
# method used while group/memberships beeing edit
def authenticate_membership(group)
unless group.member?(@current_user)
flash[:error] = ERROR_NO_GROUP_MEMBER
if request.xml_http_request?
render(:update) {|page| page.redirect_to :action => "index"}
else
redirect_to :action => 'index'
end
end
end
# gives a view to list all members of the foodcoop
def foodcoop_members
# sort by ordergroups
if params[:sort_by_ordergroups]
@users = []
OrderGroup.find(:all, :order => "name").each do |group|
group.users.each do |user|
@users << user
end
end
@total = @users.size
else
# sort by nick, thats default
if (params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 100)
@per_page = params[:per_page].to_i
else
@per_page = 20
end
# if somebody uses the search field:
conditions = "first_name LIKE '%#{params[:query]}%' OR last_name LIKE '%#{params[:query]}%'" unless params[:query].blank?
@total = User.count(:conditions => conditions)
@users = User.paginate(:page => params[:page], :per_page => @per_page, :conditions => conditions, :order => "nick", :include => "groups")
respond_to do |format|
format.html # index.html.erb
format.js do
render :update do |page|
page.replace_html 'user_table', :partial => "list_members"
end
end
end
end
end
# gives an overview for the workgroups and its members
def workgroups
@groups = Group.find :all, :conditions => "type != 'OrderGroup'", :order => "name"
end
# Invites a new user to join foodsoft in this group.
def invite
@group = Group.find(params[:id])
if (!@group || (!@current_user.is_member_of(@group) && !@current_user.role_admin?))
flash[:error] = ERR_CANNOT_INVITE
redirect_to(:action => "index")
elsif (request.post?)
@invite = Invite.new(:user => @current_user, :group => @group, :email => params[:invite][:email])
if @invite.save
flash[:notice] = format(MSG_INVITE_SUCCESS, @invite.email)
redirect_to(:action => 'index')
end
end
end
# cancel personal memberships direct from the myProfile-page
def cancel_membership
membership = Membership.find(params[:id])
if membership.user == current_user
membership.destroy
flash[:notice] = _("The membership was cancelled.")
else
flash[:error] = _("You are not allowed to cancel this membership")
end
redirect_to my_profile_path
end
end

View file

@ -0,0 +1,132 @@
class LoginController < ApplicationController
skip_before_filter :authenticate # no authentication since this is the login page
filter_parameter_logging "password" # do not log "password" parameter
verify :method => :post, :only => [:login, :reset_password, :new], :redirect_to => { :action => :index }
# Redirects to the login action.
def index
render :action => 'login'
end
# Logout the current user and deletes the session
def logout
self.return_to = nil
current_user = nil
reset_session
flash[:notice] = _("Logged out.")
render :action => 'login'
end
# Displays a "denied due to insufficient privileges" message and provides the login form.
def denied
flash[:error] = _("You are not authorized to do this. Please log in as another user or go back.")
render :action => 'login'
end
# Login to the foodsoft.
def login
user = User.find_by_nick(params[:login][:user])
if user && user.has_password(params[:login][:password])
# Set last_login to Now()
user.update_attribute(:last_login, Time.now)
self.current_user = user
if (redirect = return_to)
self.return_to = nil
redirect_to redirect
else
redirect_to :controller => 'index'
end
else
current_user = nil
flash[:error] = _("Sorry, login is not possible.")
end
end
# Display the form to enter an email address requesting a token to set a new password.
def forgot_password
end
# Sends an email to a user with the token that allows setting a new password through action "password".
def reset_password
if (user = User.find_by_email(params[:login][:email]))
user.reset_password_token = user.new_random_password(16)
user.reset_password_expires = Time.now.advance(:days => 2)
if user.save
email = Mailer.deliver_password(user)
logger.debug("Sent password reset email to #{user.email}.")
end
end
flash[:notice] = _("If your email address is listed in our system, you will now receive an email with the instructions how to change your password.")
render :action => 'login'
end
# Set a new password with a token from the password reminder email.
# Called with params :id => User.id and :token => User.reset_password_token to specify a new password.
def password
@user = User.find_by_id_and_reset_password_token(params[:id], params[:token])
if (@user.nil? || @user.reset_password_expires < Time.now)
flash[:error] = _("Invalid or expired token, password cannot be changed.")
render :action => 'forgot_password'
end
end
# Sets a new password.
# Called with params :id => User.id and :token => User.reset_password_token to specify a new password.
def new
@user = User.find_by_id_and_reset_password_token(params[:id], params[:token])
if (@user.nil? || @user.reset_password_expires < Time.now)
flash[:error] = _("Invalid or expired token, password cannot be changed.")
redirect_to :action => 'forgot_password'
else
@user.set_password({:required => true}, params[:user][:password], params[:user][:password_confirmation])
if @user.errors.empty?
@user.reset_password_token = nil
@user.reset_password_expires = nil
if @user.save
flash[:notice] = _("New password has been saved, please log in.")
render :action => 'login'
else
@user = User.find(@user.id) # reload to refetch token
flash[:error] = _("When trying to save your new password an error has occured. Please try again.")
render :action => 'password'
end
else
flash[:error] = _("Error: #{@user.errors.on_base}.")
render :action => 'password'
end
end
end
# Invited users.
def invite
@invite = Invite.find_by_token(params[:id])
if (@invite.nil? || @invite.expires_at < Time.now)
flash[:error] = _("Your invitation is invalid or has expired, sorry!")
render :action => 'login'
elsif @invite.group.nil?
flash[:error] = _("The group you are invited to join doesn't exist any more!")
render :action => 'login'
elsif (request.post?)
User.transaction do
@user = User.new(params[:user])
@user.email = @invite.email
@user.set_password({:required => true}, params[:user][:password], params[:user][:password_confirmation])
if (@user.errors.empty? && @user.save)
Membership.new(:user => @user, :group => @invite.group).save!
for setting in User::setting_keys.keys
@user.settings[setting] = (params[:user][:settings] && params[:user][:settings][setting] == '1' ? '1' : nil)
end
@invite.destroy
flash[:notice] = _("Congratulations, your account has been created successfully. You can log in now.")
render(:action => 'login')
end
end
else
@user = User.new(:email => @invite.email)
end
rescue
flash[:error] = _("An error has occured. Please try again.")
end
end

View file

@ -0,0 +1,170 @@
class MessagesController < ApplicationController
verify :method => :post, :only => [:create, :destroy], :redirect_to => { :action => :index }
MESSAGE_SEND_SUCCESS = 'Nachricht erfolgreich abgeschickt.'
MESSAGE_DELETE_SUCCESS = 'Nachricht gelöscht.'
MESSAGES_DELETE_SUCCESS = 'Nachrichten gelöscht.'
ERROR_SEND_FAILED = 'Nachricht konnte nicht verschickt werden.'
ERROR_CANNOT_DELETE = 'Nachricht kann nicht gelöscht werden.'
ERROR_CANNOT_REPLY = 'Auf diese Nachricht kann nicht geantwortet werden.'
ERROR_UNKNOWN_USER = 'Unbekannte_r Empfänger_in.'
ERROR_INVALID_GROUP = 'Empfängergruppe ist unbekannt oder hat keine Mitglieder.'
ERROR_NO_RECIPIENTS = 'Es sind keine Empfänger_innen ausgewählt.'
# Renders the "inbox" action.
def index
inbox
render :action => 'inbox'
end
# Shows the user's message inbox.
def inbox
@messages = Message.find_all_by_recipient_id(@current_user.id, :order => 'messages.created_on desc', :include => :sender)
end
# Creates a new message object.
def new
@message = Message.new
end
# Creates a new message.
def create
# Determine recipient(s)...
@recipient_nicks = ''
if (params[:everyone] == 'yes')
@everyone = true
recipients = User.find(:all)
else
recipients = Array.new
# users
for nick in params[:recipient][:nicks].split(%r{,\s*})
if (user = User.find_by_nick(nick))
recipients << user
@recipient_nicks += "#{nick}, "
end
end
@recipient_nicks = @recipient_nicks[0..-3] unless @recipient_nicks.empty?
# group
group = Group.find_by_id(params[:recipient][:group_id]) if params[:recipient][:group_id]
recipients = recipients | group.users if group
end
# Construct message(s) and save them...
if recipients.empty?
@message = Message.new(params[:message])
@message.sender = @current_user
@group = group
flash[:error] = ERROR_NO_RECIPIENTS
render :action => 'new'
else
begin
if (@everyone)
recipients_text = 'alle'
else
recipients_text = @recipient_nicks
recipients_text += (group.nil? ? '' : (recipients_text.empty? ? group.name : ", #{group.name}"))
end
Message.transaction do
for recipient in recipients
@message = Message.new(
:subject => params[:message][:subject],
:body => params[:message][:body],
:recipient => recipient,
:recipients => recipients_text
)
@message.sender = @current_user
@message.save!
end
end
flash[:notice] = MESSAGE_SEND_SUCCESS
redirect_to :action=> 'index'
rescue
@group = group
flash[:error] = ERROR_SEND_FAILED
render :action => 'new'
end
end
end
# Deletes the message(s) specified by the id/ids param if the current user is the recipient.
def destroy
ids = Array.new
ids << params[:id] if params[:id]
ids = ids + params[:ids] if (params[:ids] && params[:ids].is_a?(Array))
for id in ids
message = Message.find(id)
if (message && message.recipient && message.recipient == @current_user)
message.destroy
else
flash[:error] = ERROR_CANNOT_DELETE
break
end
end
flash[:notice] = MESSAGE_DELETE_SUCCESS if (flash[:error].blank? && ids.size == 1)
flash[:notice] = "#{ids.size} #{MESSAGES_DELETE_SUCCESS}" if (flash[:error].blank? && ids.size > 1)
redirect_to :action=> 'index'
end
# Shows a single message.
def show
@message = Message.find_by_id_and_recipient_id(params[:id], @current_user.id)
@message.update_attribute('read', true) if (@message && !@message.read?)
end
# Replys to the message specified through :id.
def reply
message = Message.find(params[:id])
if (message && message.recipient && message.recipient == @current_user && message.sender && message.sender.nick)
@message = Message.new(
:recipient => message.sender,
:subject => "Re: #{message.subject}",
:body => "#{message.sender.nick} schrieb am #{FoodSoft::format_date(message.created_on)} um #{FoodSoft::format_time(message.created_on)}:\n"
)
if (message.body)
message.body.each_line{|l| @message.body += "> #{l}"}
end
@recipient_nicks = message.sender.nick
render :action => 'new'
else
flash[:error] = ERROR_CANNOT_REPLY
redirect_to :action=> 'index'
end
end
# Shows new-message form with the recipient user specified through :id.
def user
if (recipient = User.find(params[:id]))
@recipient_nicks = recipient.nick
@message = Message.new
render :action => 'new'
else
flash[:error] = ERROR_UNKNOWN_USER
redirect_to :action=> 'index'
end
end
# Shows new-message form with the recipient user specified through :id.
def group
recipient = Group.find(params[:id], :include => :memberships)
if (recipient && !recipient.memberships.empty?)
@message = Message.new
@group = recipient
render :action => 'new'
else
flash[:error] = ERROR_INVALID_GROUP
redirect_to :action=> 'index'
end
end
# Auto-complete for recipient user list.
def auto_complete_for_recipient_nicks
@users = User.find(:all, :conditions => ['LOWER(nick) LIKE ?', '%' + params[:recipient][:nicks].downcase + '%'], :order => :nick, :limit => 8)
render :partial => '/shared/auto_complete_users'
end
# Returns list of all users as auto-completion hint.
def user_list
@users = User.find(:all, :order => :nick)
render :partial => '/shared/auto_complete_users'
end
end

View file

@ -0,0 +1,220 @@
# Controller for all ordering-actions that are performed by a user who is member of an OrderGroup.
# Management actions that require the "orders" role are handled by the OrdersController.
class OrderingController < ApplicationController
# Security
before_filter :ensureOrderGroupMember
verify :method => :post, :only => [:saveOrder], :redirect_to => { :action => :index }
# Messages
ERROR_ALREADY_FINISHED = 'Diese Bestellung ist bereits abgeschlossen.'
ERROR_NO_ORDERGROUP = 'Sie gehören keiner Bestellgruppe an.'
ERROR_INSUFFICIENT_FUNDS = 'Der Bestellwert übersteigt das verfügbare Guthaben.'
MSG_ORDER_UPDATED = 'Die Bestellung wurde gespeichert.'
MSG_ORDER_CREATED = 'Die Bestellung wurde angelegt.'
ERROR_UPDATE_FAILED = 'Die Bestellung konnte nicht aktualisiert werden, da ein Fehler auftrat.'
ERROR_UPDATE_CONFLICT = 'In der Zwischenzeit hat jemand anderes auch bestellt, daher konnte die Bestellung nicht aktualisiert werden.'
# Index page.
def index
@orderGroup = @current_user.find_ordergroup
@currentOrders = Order.find_current
@finishedOrders = @orderGroup.findExpiredOrders + @orderGroup.findFinishedNotBooked
@bookedOrders = @orderGroup.findBookedOrders(5)
# Calculate how much the order group has spent on open or nonbooked orders:
@currentOrdersValue, @nonbookedOrdersValue = 0, 0
@orderGroup.findCurrent.each { |groupOrder| @currentOrdersValue += groupOrder.price}
@finishedOrders.each { |groupOrder| @nonbookedOrdersValue += groupOrder.price}
end
# Edit a current order.
def order
@order = Order.find(params[:id], :include => [:supplier, :order_articles])
if !@order.current?
flash[:notice] = ERROR_ALREADY_FINISHED
redirect_to :action => 'index'
elsif !(@order_group = @current_user.find_ordergroup)
flash[:notice] = ERROR_NO_ORDERGROUP
redirect_to :controller => 'index'
else
@current_orders = Order.find_current
@other_orders = @current_orders.reject{|order| order == @order}
# Load order article data...
@articles_by_category = @order.get_articles
# save results of earlier orders in array
ordered_articles = Array.new
@group_order = @order.group_orders.find(:first, :conditions => "order_group_id = #{@order_group.id}", :include => :group_order_articles)
if @group_order
# Group has already ordered, so get the results...
for article in @group_order.group_order_articles
result = article.orderResult
ordered_articles[article.order_article_id] = { 'quantity' => article.quantity,
'tolerance' => article.tolerance,
'quantity_result' => result[:quantity],
'tolerance_result' => result[:tolerance]}
end
@version = @group_order.lock_version
@availableFunds = @order_group.getAvailableFunds(@group_order)
else
@version = 0
@availableFunds = @order_group.getAvailableFunds
end
# load prices ....
@price = Array.new; @unit = Array.new;
@others_quantity = Array.new; @quantity = Array.new; @quantity_result = Array.new; @used_quantity = Array.new; @unused_quantity = Array.new
@others_tolerance = Array.new; @tolerance = Array.new; @tolerance_result = Array.new; @used_tolerance = Array.new; @unused_tolerance = Array.new
i = 0;
@articles_by_category.each do |category, order_articles|
for order_article in order_articles
# price/unit size
@price[i] = order_article.article.gross_price
@unit[i] = order_article.article.unit_quantity
# quantity
@quantity[i] = (ordered_articles[order_article.id] ? ordered_articles[order_article.id]['quantity'] : 0)
@others_quantity[i] = order_article.quantity - @quantity[i]
@used_quantity[i] = (ordered_articles[order_article.id] ? ordered_articles[order_article.id]['quantity_result'] : 0)
# tolerance
@tolerance[i] = (ordered_articles[order_article.id] ? ordered_articles[order_article.id]['tolerance'] : 0)
@others_tolerance[i] = order_article.tolerance - @tolerance[i]
@used_tolerance[i] = (ordered_articles[order_article.id] ? ordered_articles[order_article.id]['tolerance_result'] : 0)
i += 1
end
end
end
end
# Update changes to a current order.
def saveOrder
order = Order.find(params[:id], :include => [:supplier, :order_articles])
if (!order.current?)
flash[:error] = ERROR_ALREADY_FINISHED
redirect_to :action => 'index'
elsif !(order_group = @current_user.find_ordergroup)
flash[:error] = ERROR_NO_ORDERGROUP
redirect_to :controller => 'index'
elsif (params[:total_balance].to_i < 0)
flash[:error] = ERROR_INSUFFICIENT_FUNDS
redirect_to :action => 'order'
elsif (ordered = params[:ordered])
begin
Order.transaction do
# Create group order if necessary...
if (groupOrder = order.group_orders.find(:first, :conditions => "order_group_id = #{order_group.id}", :include => [:group_order_articles]))
if (params[:version].to_i != groupOrder.lock_version) # check for conflicts well ahead
raise ActiveRecord::StaleObjectError
end
else
groupOrder = GroupOrder.new(:order_group => order_group, :order => order, :updated_by => @current_user, :price => 0)
groupOrder.save!
end
# Create/update GroupOrderArticles...
newGroupOrderArticles = Array.new
for article in order.order_articles
# Find the GroupOrderArticle, create a new one if necessary...
groupOrderArticles = groupOrder.group_order_articles.select{ |v| v.order_article_id == article.id }
unless (groupOrderArticle = groupOrderArticles[0])
groupOrderArticle = GroupOrderArticle.create(:group_order => groupOrder, :order_article_id => article.id, :quantity => 0, :tolerance => 0)
end
# Get ordered quantities and update GroupOrderArticle/-Quantities...
unless (quantities = ordered.delete(article.id.to_s)) && (quantity = quantities['quantity']) && (tolerance = quantities['tolerance'])
quantity = tolerance = 0
end
groupOrderArticle.updateQuantities(quantity.to_i, tolerance.to_i)
# Add to new list of GroupOrderArticles:
newGroupOrderArticles.push(groupOrderArticle)
end
groupOrder.group_order_articles = newGroupOrderArticles
groupOrder.updatePrice
groupOrder.updated_by = @current_user
groupOrder.save!
order.updateQuantities
order.save!
end
flash[:notice] = MSG_ORDER_UPDATED
rescue ActiveRecord::StaleObjectError
flash[:error] = ERROR_UPDATE_CONFLICT
rescue => exception
logger.error('Failed to update order: ' + exception.message)
flash[:error] = ERROR_UPDATE_FAILED
end
redirect_to :action => 'my_order_result', :id => order
end
end
# Shows the Result for the OrderGroup the current user belongs to
# this method decides between finished and unfinished orders
def my_order_result
@order= Order.find(params[:id])
@current_orders = Order.find_current #.reject{|order| order == @order}
if @order.finished?
@finished= true
@groupOrderResult= @order.group_order_results.find_by_group_name(@current_user.find_ordergroup.name)
@order_value= @groupOrderResult.price if @groupOrderResult
@comments= @order.comments
else
@group_order = @order.group_orders.find_by_order_group_id(@current_user.find_ordergroup.id)
@order_value= @group_order.price if @group_order
end
end
# Shows all Orders of the Ordergroup
# if selected, it shows all orders of the foodcoop
def myOrders
@orderGroup = @current_user.find_ordergroup
unless params[:show_all] == "1"
# get only orders belonging to the ordergroup
@finishedOrders = @orderGroup.findExpiredOrders + @orderGroup.findFinishedNotBooked
@bookedOrders = GroupOrderResult.paginate :page => params[:page], :per_page => 10,
:include => :order,
:conditions => ["group_order_results.group_name = ? AND group_order_results.order_id = orders.id AND orders.finished = ? AND orders.booked = ? ", @orderGroup.name, true, true],
:order => "orders.ends DESC"
else
# get all orders, take care of different models in @finishedOrders
@show_all = true
@finishedOrders = Order.find_finished
@bookedOrders = Order.paginate_all_by_booked true, :page => params[:page], :per_page => 10, :order => 'ends desc'
end
respond_to do |format|
format.html # myOrders.haml
format.js do
render :update do |page|
page.replace_html 'bookedOrders', :partial => "bookedOrders"
end
end
end
end
# sends a form for adding a new comment
def newComment
@order = Order.find(params[:id])
render :update do |page|
page.replace_html 'newComment', :partial => 'shared/newComment', :object => @order
page["newComment"].show
end
end
# adds a Comment to the Order
def addComment
@order = Order.find(params[:id])
@comment = Comment.new(params[:comment])
@comment.user = @current_user
if @comment.title.length > 3 && @order.comments << @comment
flash[:notice] = _("Comment has been created.")
redirect_to :action => 'my_order_result', :id => @order
else
flash[:error] = _("The comment has not been saved. Check the title and try again.")
redirect_to :action => 'my_order_result', :id => @order
end
end
private
# Returns true if @current_user is member of an OrderGroup.
# Used as a :before_filter by OrderingController.
def ensureOrderGroupMember
!@current_user.find_ordergroup.nil?
end
end

View file

@ -0,0 +1,185 @@
# Controller for managing orders, i.e. all actions that require the "orders" role.
# Normal ordering actions of members of order groups is handled by the OrderingController.
class OrdersController < ApplicationController
# Security
before_filter :authenticate_orders
verify :method => :post, :only => [:finish, :create, :update, :destroy, :setAllBooked], :redirect_to => { :action => :index }
# Define layout exceptions for PDF actions:
layout "application", :except => [:faxPdf, :matrixPdf, :articlesPdf, :groupsPdf]
# List orders
def index
@current_orders = Order.find_current
@per_page = 15
if params['sort']
sort = case params['sort']
when "supplier" then "suppliers.name, ends DESC"
when "ends" then "ends DESC"
when "supplier_reverse" then "suppliers.name DESC, ends DESC"
when "ends_reverse" then "ends"
end
else
sort = "ends DESC"
end
@orders = Order.paginate :page => params[:page], :per_page => @per_page,
:order => sort, :conditions => ['ends < ? OR starts > ? OR finished = ?', Time.now, Time.now, true],
:include => :supplier
respond_to do |format|
format.html
format.js do
render :update do |page|
page.replace_html 'orders_table', :partial => "list"
end
end
end
end
# Gives a view for the results to a specific order
def show
@order= Order.find(params[:id])
unless @order.finished?
@order_articles= @order.get_articles
@group_orders= @order.group_orders
else
@finished= true
@order_articles= @order.order_article_results
@group_orders= @order.group_order_results
@comments= @order.comments
partial = case params[:view]
when 'normal' then "showResult"
when 'groups'then 'showResult_groups'
when 'articles'then 'showResult_articles'
end
render :partial => partial if partial
end
end
# Page to create a new order.
def new
@supplier = Supplier.find(params[:id])
@order = @supplier.orders.build :ends => 4.days.from_now
@template_orders = Order.find_all_by_supplier_id_and_finished(@supplier.id, true, :limit => 3, :order => 'starts DESC', :include => "order_article_results")
end
# Save a new order.
# order_articles will be saved in Order.article_ids=()
def create
@order = Order.new(params[:order])
if @order.save
flash[:notice] = _("The order has been created successfully.")
redirect_to :action => 'show', :id => @order
else
render :action => 'new'
end
end
# Page to edit an exsiting order.
# editing finished orders is done in FinanceController
def edit
@order = Order.find(params[:id], :include => :articles)
end
# Update an existing order.
def update
@order = Order.find params[:id]
if @order.update_attributes params[:order]
flash[:notice] = _("The order has been updated.")
redirect_to :action => 'show', :id => @order
else
render :action => 'edit'
end
@order.updateAllGroupOrders #important if ordered articles has been removed
end
# Delete an order.
def destroy
Order.find(params[:id]).destroy
redirect_to :action => 'index'
end
# Finish a current order.
def finish
order = Order.find(params[:id])
order.finish(@current_user)
flash[:notice] = _("The order has been finished successfully.")
redirect_to :action => 'show', :id => order
end
# Renders the groups-orderd PDF.
def groupsPdf
@order = Order.find(params[:id])
@options_for_rfpdf ||= {}
@options_for_rfpdf[:file_name] = "#{Date.today}_#{@order.name}_GruppenSortierung.pdf"
end
# Renders the articles-orderd PDF.
def articlesPdf
@order = Order.find(params[:id])
@options_for_rfpdf ||= {}
@options_for_rfpdf[:file_name] = "#{Date.today}_#{@order.name}_ArtikelSortierung.pdf"
end
# Renders the fax PDF.
def faxPdf
@order = Order.find(params[:id])
@options_for_rfpdf ||= {}
@options_for_rfpdf[:file_name] = "#{Date.today}_#{@order.name}_FAX.pdf"
end
# Renders the fax-text-file
# e.g. for easier use with online-fax-software, which don't accept pdf-files
def text_fax_template
order = Order.find(params[:id])
supplier = order.supplier
contact = FoodSoft.getFoodcoopContact
text = _("Order for") + " #{FoodSoft.getFoodcoopName}"
text += "\n" + _("Customer number") + ": #{supplier.customer_number}" unless supplier.customer_number.blank?
text += "\n" + _("Delivery date") + ": "
text += "\n\n#{supplier.name}\n#{supplier.address}\nFAX: #{supplier.fax}\n\n"
text += "****** " + _("Shipping address") + "\n\n"
text += "#{FoodSoft.getFoodcoopName}\n#{contact[:street]}\n#{contact[:zip_code]} #{contact[:city]}\n\n"
text += "****** " + _("Articles") + "\n\n"
text += _("Number") + " " + _("Quantity") + " " + _("Name") + "\n"
# now display all ordered articles
order.order_article_results.each do |article|
text += article.order_number.blank? ? " " : "#{article.order_number} "
quantity = article.units_to_order.to_i.to_s
quantity = " " + quantity if quantity.size < 2
text += "#{quantity} #{article.name}\n"
end
send_data text,
:type => 'text/plain; charset=utf-8; header=present',
:disposition => "attachment; filename=#{order.name}"
end
# Renders the matrix PDF.
def matrixPdf
@order = Order.find(params[:id])
@options_for_rfpdf ||= {}
@options_for_rfpdf[:file_name] = "#{Date.today}_#{@order.name}_Matrix.pdf"
end
# sends a form for adding a new comment
def newComment
@order = Order.find(params[:id])
render :update do |page|
page.replace_html 'newComment', :partial => 'shared/newComment', :object => @order
end
end
# adds a Comment to the Order
def addComment
@order = Order.find(params[:id])
@comment = Comment.new(params[:comment])
@comment.user = @current_user
if @comment.title.length > 3 && @order.comments << @comment
flash[:notice] = _("Comment has been created.")
redirect_to :action => 'show', :id => @order
else
flash[:error] = _("The comment has not been saved. Check the title and try again.")
redirect_to :action => 'show', :id => @order
end
end
end

View file

@ -0,0 +1,77 @@
class SuppliersController < ApplicationController
before_filter :authenticate_suppliers, :except => [:index, :list]
verify :method => :post, :only => [ :destroy, :create, :update ], :redirect_to => { :action => :list }
# messages
MSG_SUPPLIER_DESTOYED = "Lieferant wurde gelöscht"
MSG_SUPPLIER_UPDATED = 'Lieferant wurde aktualisiert'
MSG_SUPPLIER_CREATED = "Lieferant wurde erstellt"
def index
list
render :action => 'list'
end
def list
@supplier_column_names = ["Name", "Telefon", "Email", "Kundennummer"]
@supplier_columns = ["name", "phone", "email", "customer_number"]
@suppliers = Supplier.find :all
end
def show
@supplier = Supplier.find(params[:id])
@supplier_column_names = ["Name", "Telefon", "Telefon2", "FAX", "Email", "URL", "Kontakt", "Kundennummer", "Liefertage", "BestellHowTo", "Notiz", "Mindestbestellmenge"]
@supplier_columns = ["name", "phone", "phone2", "fax", "email", "url", "contact_person", "customer_number", "delivery_days", "order_howto", "note", "min_order_quantity"]
end
# new supplier
# if shared_supplier_id is given, the new supplier will filled whith its attributes
def new
if params[:shared_supplier_id]
shared_supplier = SharedSupplier.find(params[:shared_supplier_id])
@supplier = shared_supplier.build_supplier(shared_supplier.attributes)
else
@supplier = Supplier.new
end
end
def create
@supplier = Supplier.new(params[:supplier])
if @supplier.save
flash[:notice] = MSG_SUPPLIER_CREATED
redirect_to :action => 'list'
else
render :action => 'new'
end
end
def edit
@supplier = Supplier.find(params[:id])
end
def update
@supplier = Supplier.find(params[:id])
if @supplier.update_attributes(params[:supplier])
flash[:notice] = MSG_SUPPLIER_UPDATED
redirect_to :action => 'show', :id => @supplier
else
render :action => 'edit'
end
end
def destroy
Supplier.find(params[:id]).destroy
flash[:notice] = MSG_SUPPLIER_DESTOYED
redirect_to :action => 'list'
rescue => e
flash[:error] = _("An error has occurred: ") + e.message
redirect_to :action => 'show', :id => params[:id]
end
# gives a list with all available shared_suppliers
def shared_suppliers
@shared_suppliers = SharedSupplier.find(:all)
end
end

View file

@ -0,0 +1,128 @@
class TasksController < ApplicationController
#auto_complete_for :user, :nick
def index
@non_group_tasks = Task.find :all, :conditions => "group_id IS NULL AND done = 0", :order => "due_date ASC"
@groups = Group.find :all, :conditions => "type != 'OrderGroup'"
end
def myTasks
@unaccepted_tasks = @current_user.unaccepted_tasks
@accepted_tasks = @current_user.accepted_tasks
end
def new
if params[:id]
group = Group.find(params[:id])
@task = group.tasks.build :name => group.task_name,
:required_users => group.task_required_users,
:description => group.task_description,
:due_date => group.next_weekly_tasks[params[:task_from_now].to_i]
else
@task = Task.new
end
end
def create
@task = Task.new(params[:task])
if @task.errors.empty?
@task.save
flash[:notice] = "Aufgabe wurde erstellt"
if @task.group
redirect_to :action => "workgroup", :id => @task.group
else
redirect_to :action => "index"
end
else
render :template => "tasks/new"
end
end
def show
@task = Task.find(params[:id])
end
def edit
@task = Task.find(params[:id])
end
def update
@task = Task.find(params[:id])
@task.attributes=(params[:task])
if @task.errors.empty?
@task.save
flash[:notice] = "Aufgabe wurde aktualisiert"
if @task.group
redirect_to :action => "workgroup", :id => @task.group
else
redirect_to :action => "index"
end
else
render :template => "tasks/edit"
end
end
def destroy
Task.find(params[:id]).destroy
redirect_to :action => "index"
end
# Delete an given Assignment
# currently used in edit-view
def drop_assignment
ass = Assignment.find(params[:id])
task = ass.task
ass.destroy
redirect_to :action => "show", :id => task
end
# assign current_user to the task and set the assignment to "accepted"
# if there is already an assignment, only accepted will be set to true
def accept
task = Task.find(params[:id])
if ass = task.is_assigned?(current_user)
ass.update_attribute(:accepted, true)
else
task.assignments.create(:user => current_user, :accepted => true)
end
flash[:notice] = "Du hast die Aufgabe übernommen"
redirect_to :action => "myTasks"
end
# deletes assignment between current_user and given task
def reject
Task.find(params[:id]).users.delete(current_user)
redirect_to :action => "index"
end
def update_status
Task.find(params[:id]).update_attribute("done", params[:task][:done])
flash[:notice] = "Aufgabenstatus wurde aktualisiert"
redirect_to :action => "index"
end
# Shows all tasks, which are already done
def archive
@tasks = Task.find :all, :conditions => "done = 1", :order => "due_date DESC"
end
# shows workgroup (normal group) to edit weekly_tasks_template
def workgroup
@group = Group.find(params[:id])
if @group.is_a? OrderGroup
flash[:error] = "Keine Arbeitsgruppe gefunden"
redirect_to :action => "index"
end
end
# this method is uses for the auto_complete-function from script.aculo.us
def auto_complete_for_task_user_list
@users = User.find(
:all,
:conditions => [ 'LOWER(nick) LIKE ?', '%' + params[:task][:user_list].downcase + '%' ],
:order => 'nick ASC',
:limit => 8
)
render :partial => '/shared/auto_complete_users'
end
end