Initial commit of foodsoft 2
This commit is contained in:
commit
5b9a7e05df
|
@ -0,0 +1,6 @@
|
|||
log/*.log
|
||||
tmp/**/*
|
||||
config/*.yml
|
||||
db/*.sqlite3
|
||||
nbproject/
|
||||
|
|
@ -0,0 +1,296 @@
|
|||
FoodSoft - a webbased foodcoop management software
|
||||
Copyright (C) 2007 Benni and Lasse
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
|
@ -0,0 +1,20 @@
|
|||
== FoodSoft
|
||||
|
||||
Web-based software to manage a non-profit food coop (product catalog, ordering, accounting, job scheduling).
|
||||
|
||||
== License
|
||||
|
||||
FoodSoft - a webbased foodcoop management software
|
||||
Copyright (C) 2007 Benni and Lasse
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
(See file LICENSE for the full text of the GPL)
|
|
@ -0,0 +1,61 @@
|
|||
README for DEVELopment Project Setup
|
||||
====================================
|
||||
|
||||
Gratulations, you have successfully checked out the foodsoft project
|
||||
form the subversion repository. Now you are only a few steps away from
|
||||
trying it out and then jumping into development.
|
||||
|
||||
|
||||
Configure datebase
|
||||
------------------
|
||||
Create at least one local databases and then configure database access.
|
||||
|
||||
cp config/database.yml.SAMPLE config/database.yml
|
||||
|
||||
Edit database.yml to specify connection parameters.
|
||||
|
||||
|
||||
Configure Development Environment
|
||||
---------------------------------
|
||||
You need to create your own copy of the development environment configuration:
|
||||
|
||||
cp config/environments/development.rb.SAMPLE config/environments/development.rb
|
||||
|
||||
Edit development.rb to specify your settings (e.g. ActionMailer SMTP settings).
|
||||
|
||||
|
||||
Configure Foodsoft
|
||||
------------------
|
||||
You need to create your own copy of the foodsoft configuration settings:
|
||||
|
||||
cp config/foodsoft.yml.SAMPLE config/foodsoft.yml
|
||||
|
||||
Edit foodsoft.yml to suit your needs.
|
||||
|
||||
|
||||
Create log files
|
||||
----------------
|
||||
The WEBrick server will complain if these do not exist.
|
||||
|
||||
touch log/development.log
|
||||
touch log/test.log
|
||||
touch log/production.log
|
||||
touch log/server.log
|
||||
|
||||
|
||||
Create database schema
|
||||
----------------------
|
||||
rake db:schema:load
|
||||
|
||||
|
||||
Create user admin with password secret
|
||||
--------------------------------------
|
||||
rake foodsoft:create_admin
|
||||
|
||||
|
||||
Try it out
|
||||
----------
|
||||
Start the WEBrick server to try it out:
|
||||
|
||||
script/server
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||
|
||||
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
|
||||
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
require 'tasks/rails'
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 = |