Merge branch 'rails3' into master
Conflicts: Gemfile Gemfile.lock Rakefile app/models/article_price.rb app/models/message.rb config/environment.rb vendor/plugins/acts_as_configurable/Rakefile vendor/plugins/acts_as_configurable/tasks/acts_as_configurable_tasks.rake vendor/plugins/acts_as_ordered/Rakefile vendor/plugins/acts_as_paranoid/Rakefile vendor/plugins/acts_as_tree/Rakefile vendor/plugins/acts_as_versioned/Rakefile vendor/plugins/auto_complete/Rakefile vendor/plugins/prawnto/Rakefile vendor/plugins/wikicloth/Rakefile vendor/plugins/will_paginate/Rakefile
11
.gitignore
vendored
|
@ -1,15 +1,18 @@
|
||||||
log/*.log
|
log/*.log
|
||||||
tmp/**/*
|
tmp/**/*
|
||||||
config/*.yml
|
config/*.yml
|
||||||
|
config/initializers/secret_token.rb
|
||||||
db/*.sqlite3
|
db/*.sqlite3
|
||||||
nbproject/
|
nbproject/
|
||||||
config/environments/development.rb
|
config/environments/development.rb
|
||||||
capfile
|
|
||||||
config/environments/fcschinke09.rb
|
|
||||||
*.swp
|
*.swp
|
||||||
*~
|
*~
|
||||||
public/**/*_cached.*
|
public/**/*_cached.*
|
||||||
config/initializers/session_store.rb
|
|
||||||
.idea
|
.idea
|
||||||
.rvmrc
|
|
||||||
.get-dump.yml
|
.get-dump.yml
|
||||||
|
.sass-cache/
|
||||||
|
doc/app/
|
||||||
|
# Deployment tools
|
||||||
|
Capfile
|
||||||
|
config/deploy.rb
|
||||||
|
config/deploy/*
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
1.8.7-p357
|
|
67
Gemfile
|
@ -1,18 +1,61 @@
|
||||||
# A sample Gemfile
|
# A sample Gemfile
|
||||||
source "http://rubygems.org"
|
source "https://rubygems.org"
|
||||||
#ruby "1.8.7"
|
ruby "1.9.3"
|
||||||
|
|
||||||
gem "rails", '2.3.17'
|
gem "rails", '~> 3.2.9'
|
||||||
|
|
||||||
gem 'mysql'
|
# Gems used only for assets and not required
|
||||||
gem "fastercsv"
|
# in production environments by default.
|
||||||
gem "prawn", '<=0.6.3'
|
group :assets do
|
||||||
gem 'haml', '>=2.0.6'
|
gem 'sass-rails', '~> 3.2.3'
|
||||||
gem 'routing-filter', '0.0.1', :require => 'routing_filter'
|
gem 'coffee-rails', '~> 3.2.1'
|
||||||
gem 'sqlite3-ruby'
|
|
||||||
gem 'rdoc', '>= 2.4.2'
|
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||||
|
gem 'therubyracer', :platforms => :ruby
|
||||||
|
|
||||||
|
gem 'uglifier', '>= 1.0.3'
|
||||||
|
end
|
||||||
|
|
||||||
|
gem 'jquery-rails'
|
||||||
|
|
||||||
|
gem 'mysql2'
|
||||||
|
gem 'prawn'
|
||||||
|
gem 'haml-rails'
|
||||||
|
gem 'kaminari'
|
||||||
|
gem 'client_side_validations'
|
||||||
|
gem 'simple_form'
|
||||||
|
gem 'inherited_resources'
|
||||||
|
gem 'localize_input', :git => "git://github.com/bennibu/localize_input.git"
|
||||||
|
gem 'wikicloth'
|
||||||
|
gem 'daemons'
|
||||||
|
gem 'twitter-bootstrap-rails'
|
||||||
|
gem 'simple-navigation'
|
||||||
|
gem 'simple-navigation-bootstrap'
|
||||||
|
gem 'meta_search'
|
||||||
|
gem 'acts_as_versioned', git: 'git://github.com/technoweenie/acts_as_versioned.git' # Use this instead of rubygem
|
||||||
|
gem 'acts_as_tree'
|
||||||
|
gem 'acts_as_configurable', git: 'git://github.com/bwalding/acts_as_configurable.git'
|
||||||
|
gem 'resque'
|
||||||
|
gem 'whenever', :require => false # For defining cronjobs, see config/schedule.rb
|
||||||
|
|
||||||
|
group :production do
|
||||||
|
gem 'exception_notification', :require => 'exception_notifier'
|
||||||
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem 'annotate'
|
gem 'sqlite3'
|
||||||
gem 'hirb'
|
|
||||||
|
# Better error output
|
||||||
|
gem 'better_errors'
|
||||||
|
gem 'binding_of_caller'
|
||||||
|
|
||||||
|
# Re-enable rails benchmarker/profiler
|
||||||
|
gem 'ruby-prof'
|
||||||
|
gem 'test-unit'
|
||||||
|
|
||||||
|
# Get infos when not using proper eager loading
|
||||||
|
gem 'bullet'
|
||||||
|
|
||||||
|
# Hide assets requests in log
|
||||||
|
gem 'quiet_assets'
|
||||||
end
|
end
|
||||||
|
|
297
Gemfile.lock
|
@ -1,57 +1,256 @@
|
||||||
GEM
|
GIT
|
||||||
remote: http://rubygems.org/
|
remote: git://github.com/bennibu/localize_input.git
|
||||||
|
revision: 5eb188d2525a073d09e142cf8b0b04e6ace6e7b0
|
||||||
specs:
|
specs:
|
||||||
actionmailer (2.3.17)
|
localize_input (0.1.0)
|
||||||
actionpack (= 2.3.17)
|
|
||||||
actionpack (2.3.17)
|
GIT
|
||||||
activesupport (= 2.3.17)
|
remote: git://github.com/bwalding/acts_as_configurable.git
|
||||||
rack (~> 1.1.0)
|
revision: cdf6f6f979019275b523d10684b748f08e2dd8e8
|
||||||
activerecord (2.3.17)
|
specs:
|
||||||
activesupport (= 2.3.17)
|
acts_as_configurable (0.0.1)
|
||||||
activeresource (2.3.17)
|
rake
|
||||||
activesupport (= 2.3.17)
|
|
||||||
activesupport (2.3.17)
|
GIT
|
||||||
annotate (2.4.0)
|
remote: git://github.com/technoweenie/acts_as_versioned.git
|
||||||
fastercsv (1.5.4)
|
revision: 63b1fc8529d028fae632fe80ec0cb25df56cd76b
|
||||||
haml (3.0.25)
|
specs:
|
||||||
hirb (0.3.4)
|
acts_as_versioned (0.6.0)
|
||||||
json (1.7.6)
|
activerecord (>= 3.0.9)
|
||||||
mysql (2.8.1)
|
|
||||||
prawn (0.6.3)
|
GEM
|
||||||
prawn-core (>= 0.6.3, < 0.7)
|
remote: https://rubygems.org/
|
||||||
prawn-format (>= 0.2.3, < 0.3)
|
specs:
|
||||||
prawn-layout (>= 0.3.2, < 0.4)
|
Ascii85 (1.0.2)
|
||||||
prawn-security (>= 0.1.1, < 0.2)
|
actionmailer (3.2.13)
|
||||||
prawn-core (0.6.3)
|
actionpack (= 3.2.13)
|
||||||
prawn-format (0.2.3)
|
mail (~> 2.5.3)
|
||||||
prawn-core
|
actionpack (3.2.13)
|
||||||
prawn-layout (0.3.2)
|
activemodel (= 3.2.13)
|
||||||
prawn-security (0.1.1)
|
activesupport (= 3.2.13)
|
||||||
rack (1.1.6)
|
builder (~> 3.0.0)
|
||||||
rails (2.3.17)
|
erubis (~> 2.7.0)
|
||||||
actionmailer (= 2.3.17)
|
journey (~> 1.0.4)
|
||||||
actionpack (= 2.3.17)
|
rack (~> 1.4.5)
|
||||||
activerecord (= 2.3.17)
|
rack-cache (~> 1.2)
|
||||||
activeresource (= 2.3.17)
|
rack-test (~> 0.6.1)
|
||||||
activesupport (= 2.3.17)
|
sprockets (~> 2.2.1)
|
||||||
rake (>= 0.8.3)
|
activemodel (3.2.13)
|
||||||
|
activesupport (= 3.2.13)
|
||||||
|
builder (~> 3.0.0)
|
||||||
|
activerecord (3.2.13)
|
||||||
|
activemodel (= 3.2.13)
|
||||||
|
activesupport (= 3.2.13)
|
||||||
|
arel (~> 3.0.2)
|
||||||
|
tzinfo (~> 0.3.29)
|
||||||
|
activeresource (3.2.13)
|
||||||
|
activemodel (= 3.2.13)
|
||||||
|
activesupport (= 3.2.13)
|
||||||
|
activesupport (3.2.13)
|
||||||
|
i18n (= 0.6.1)
|
||||||
|
multi_json (~> 1.0)
|
||||||
|
acts_as_tree (1.2.0)
|
||||||
|
activerecord (>= 3.0.0)
|
||||||
|
arel (3.0.2)
|
||||||
|
better_errors (0.2.0)
|
||||||
|
coderay (>= 1.0.0)
|
||||||
|
erubis (>= 2.7.0)
|
||||||
|
binding_of_caller (0.6.8)
|
||||||
|
builder (3.0.4)
|
||||||
|
bullet (4.3.0)
|
||||||
|
uniform_notifier
|
||||||
|
chronic (0.9.0)
|
||||||
|
client_side_validations (3.1.4)
|
||||||
|
coderay (1.0.8)
|
||||||
|
coffee-rails (3.2.2)
|
||||||
|
coffee-script (>= 2.2.0)
|
||||||
|
railties (~> 3.2.0)
|
||||||
|
coffee-script (2.2.0)
|
||||||
|
coffee-script-source
|
||||||
|
execjs
|
||||||
|
coffee-script-source (1.3.3)
|
||||||
|
commonjs (0.2.6)
|
||||||
|
daemons (1.1.9)
|
||||||
|
erubis (2.7.0)
|
||||||
|
exception_notification (2.6.1)
|
||||||
|
actionmailer (>= 3.0.4)
|
||||||
|
execjs (1.4.0)
|
||||||
|
multi_json (~> 1.0)
|
||||||
|
expression_parser (0.9.0)
|
||||||
|
haml (3.1.7)
|
||||||
|
haml-rails (0.3.5)
|
||||||
|
actionpack (>= 3.1, < 4.1)
|
||||||
|
activesupport (>= 3.1, < 4.1)
|
||||||
|
haml (~> 3.1)
|
||||||
|
railties (>= 3.1, < 4.1)
|
||||||
|
has_scope (0.5.1)
|
||||||
|
hashery (2.0.1)
|
||||||
|
hike (1.2.1)
|
||||||
|
i18n (0.6.1)
|
||||||
|
inherited_resources (1.3.1)
|
||||||
|
has_scope (~> 0.5.0)
|
||||||
|
responders (~> 0.6)
|
||||||
|
journey (1.0.4)
|
||||||
|
jquery-rails (2.1.3)
|
||||||
|
railties (>= 3.1.0, < 5.0)
|
||||||
|
thor (~> 0.14)
|
||||||
|
json (1.7.7)
|
||||||
|
kaminari (0.14.1)
|
||||||
|
actionpack (>= 3.0.0)
|
||||||
|
activesupport (>= 3.0.0)
|
||||||
|
less (2.2.2)
|
||||||
|
commonjs (~> 0.2.6)
|
||||||
|
less-rails (2.2.3)
|
||||||
|
actionpack (>= 3.1)
|
||||||
|
less (~> 2.2.0)
|
||||||
|
libv8 (3.3.10.4)
|
||||||
|
mail (2.5.3)
|
||||||
|
i18n (>= 0.4.0)
|
||||||
|
mime-types (~> 1.16)
|
||||||
|
treetop (~> 1.4.8)
|
||||||
|
meta_search (1.1.3)
|
||||||
|
actionpack (~> 3.1)
|
||||||
|
activerecord (~> 3.1)
|
||||||
|
activesupport (~> 3.1)
|
||||||
|
polyamorous (~> 0.5.0)
|
||||||
|
mime-types (1.21)
|
||||||
|
multi_json (1.7.1)
|
||||||
|
mysql2 (0.3.11)
|
||||||
|
pdf-reader (1.2.0)
|
||||||
|
Ascii85 (~> 1.0.0)
|
||||||
|
hashery (~> 2.0)
|
||||||
|
ruby-rc4
|
||||||
|
polyamorous (0.5.0)
|
||||||
|
activerecord (~> 3.0)
|
||||||
|
polyglot (0.3.3)
|
||||||
|
prawn (0.12.0)
|
||||||
|
pdf-reader (>= 0.9.0)
|
||||||
|
ttfunk (~> 1.0.2)
|
||||||
|
quiet_assets (1.0.2)
|
||||||
|
railties (>= 3.1, < 5.0)
|
||||||
|
rack (1.4.5)
|
||||||
|
rack-cache (1.2)
|
||||||
|
rack (>= 0.4)
|
||||||
|
rack-protection (1.3.2)
|
||||||
|
rack
|
||||||
|
rack-ssl (1.3.3)
|
||||||
|
rack
|
||||||
|
rack-test (0.6.2)
|
||||||
|
rack (>= 1.0)
|
||||||
|
rails (3.2.13)
|
||||||
|
actionmailer (= 3.2.13)
|
||||||
|
actionpack (= 3.2.13)
|
||||||
|
activerecord (= 3.2.13)
|
||||||
|
activeresource (= 3.2.13)
|
||||||
|
activesupport (= 3.2.13)
|
||||||
|
bundler (~> 1.0)
|
||||||
|
railties (= 3.2.13)
|
||||||
|
railties (3.2.13)
|
||||||
|
actionpack (= 3.2.13)
|
||||||
|
activesupport (= 3.2.13)
|
||||||
|
rack-ssl (~> 1.3.2)
|
||||||
|
rake (>= 0.8.7)
|
||||||
|
rdoc (~> 3.4)
|
||||||
|
thor (>= 0.14.6, < 2.0)
|
||||||
rake (10.0.3)
|
rake (10.0.3)
|
||||||
rdoc (3.12)
|
rdoc (3.12.2)
|
||||||
json (~> 1.4)
|
json (~> 1.4)
|
||||||
routing-filter (0.0.1)
|
redis (3.0.2)
|
||||||
sqlite3-ruby (1.2.4)
|
redis-namespace (1.2.1)
|
||||||
|
redis (~> 3.0.0)
|
||||||
|
responders (0.9.3)
|
||||||
|
railties (~> 3.1)
|
||||||
|
resque (1.23.0)
|
||||||
|
multi_json (~> 1.0)
|
||||||
|
redis-namespace (~> 1.0)
|
||||||
|
sinatra (>= 0.9.2)
|
||||||
|
vegas (~> 0.1.2)
|
||||||
|
ruby-prof (0.11.2)
|
||||||
|
ruby-rc4 (0.1.5)
|
||||||
|
sass (3.2.1)
|
||||||
|
sass-rails (3.2.5)
|
||||||
|
railties (~> 3.2.0)
|
||||||
|
sass (>= 3.1.10)
|
||||||
|
tilt (~> 1.3)
|
||||||
|
simple-navigation (3.9.0)
|
||||||
|
activesupport (>= 2.3.2)
|
||||||
|
simple-navigation-bootstrap (0.0.4)
|
||||||
|
simple-navigation (>= 3.7.0)
|
||||||
|
simple_form (2.0.3)
|
||||||
|
actionpack (~> 3.0)
|
||||||
|
activemodel (~> 3.0)
|
||||||
|
sinatra (1.3.3)
|
||||||
|
rack (~> 1.3, >= 1.3.6)
|
||||||
|
rack-protection (~> 1.2)
|
||||||
|
tilt (~> 1.3, >= 1.3.3)
|
||||||
|
sprockets (2.2.2)
|
||||||
|
hike (~> 1.2)
|
||||||
|
multi_json (~> 1.0)
|
||||||
|
rack (~> 1.0)
|
||||||
|
tilt (~> 1.1, != 1.3.0)
|
||||||
|
sqlite3 (1.3.6)
|
||||||
|
test-unit (2.5.3)
|
||||||
|
therubyracer (0.10.2)
|
||||||
|
libv8 (~> 3.3.10)
|
||||||
|
thor (0.17.0)
|
||||||
|
tilt (1.3.6)
|
||||||
|
treetop (1.4.12)
|
||||||
|
polyglot
|
||||||
|
polyglot (>= 0.3.1)
|
||||||
|
ttfunk (1.0.3)
|
||||||
|
twitter-bootstrap-rails (2.1.3)
|
||||||
|
actionpack (>= 3.1)
|
||||||
|
less-rails (~> 2.2.3)
|
||||||
|
railties (>= 3.1)
|
||||||
|
therubyracer (~> 0.10.2)
|
||||||
|
tzinfo (0.3.37)
|
||||||
|
uglifier (1.3.0)
|
||||||
|
execjs (>= 0.3.0)
|
||||||
|
multi_json (~> 1.0, >= 1.0.2)
|
||||||
|
uniform_notifier (1.1.1)
|
||||||
|
vegas (0.1.11)
|
||||||
|
rack (>= 1.0.0)
|
||||||
|
whenever (0.8.1)
|
||||||
|
activesupport (>= 2.3.4)
|
||||||
|
chronic (>= 0.6.3)
|
||||||
|
wikicloth (0.8.0)
|
||||||
|
builder
|
||||||
|
expression_parser
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
annotate
|
acts_as_configurable!
|
||||||
fastercsv
|
acts_as_tree
|
||||||
haml (>= 2.0.6)
|
acts_as_versioned!
|
||||||
hirb
|
better_errors
|
||||||
mysql
|
binding_of_caller
|
||||||
prawn (<= 0.6.3)
|
bullet
|
||||||
rails (= 2.3.17)
|
client_side_validations
|
||||||
rdoc (>= 2.4.2)
|
coffee-rails (~> 3.2.1)
|
||||||
routing-filter (= 0.0.1)
|
daemons
|
||||||
sqlite3-ruby
|
exception_notification
|
||||||
|
haml-rails
|
||||||
|
inherited_resources
|
||||||
|
jquery-rails
|
||||||
|
kaminari
|
||||||
|
localize_input!
|
||||||
|
meta_search
|
||||||
|
mysql2
|
||||||
|
prawn
|
||||||
|
quiet_assets
|
||||||
|
rails (~> 3.2.9)
|
||||||
|
resque
|
||||||
|
ruby-prof
|
||||||
|
sass-rails (~> 3.2.3)
|
||||||
|
simple-navigation
|
||||||
|
simple-navigation-bootstrap
|
||||||
|
simple_form
|
||||||
|
sqlite3
|
||||||
|
test-unit
|
||||||
|
therubyracer
|
||||||
|
twitter-bootstrap-rails
|
||||||
|
uglifier (>= 1.0.3)
|
||||||
|
whenever
|
||||||
|
wikicloth
|
||||||
|
|
|
@ -1 +1,4 @@
|
||||||
TODO..
|
MULTI_COOP_INSTALL
|
||||||
|
------------------
|
||||||
|
|
||||||
|
TODO ...
|
39
README_DEVEL
|
@ -38,16 +38,17 @@ Edit app_config.yml to suit your needs or just keep the defaults for now.
|
||||||
|
|
||||||
(4) Secret Token
|
(4) Secret Token
|
||||||
-------------------
|
-------------------
|
||||||
The user session is stored in a cookie. To avoid misusing the cookies and its sensitive information, rails will encrypt it with a token. So copy the config file
|
The user session are stored in cookies. Do avoid misusing the cookies and its sensitive information, rails
|
||||||
|
will encrypt it with a token. So copy the config file
|
||||||
|
|
||||||
cp config/initializers/session_store.rb.SAMPLE config/initializers/session_store.rb
|
cp config/initializers/secret_token.rb.SAMPLE config/initializers/secret_token.rb
|
||||||
|
|
||||||
and modify the token "config.action_controller.session"!
|
and modify the token!!
|
||||||
|
|
||||||
|
|
||||||
(5) Required ruby and gems
|
(5) Required ruby and gems
|
||||||
-------------------
|
-------------------
|
||||||
We reccomend the using of rvm (https://rvm.beginrescueend.com/). Install rvm and get the lates ruby (1.8.7).
|
We reccomend the using of rvm (https://rvm.beginrescueend.com/). Install rvm and get the latest ruby (>= 1.9.3).
|
||||||
If installed you only need to install the gem bundler:
|
If installed you only need to install the gem bundler:
|
||||||
|
|
||||||
gem install bundler
|
gem install bundler
|
||||||
|
@ -59,9 +60,7 @@ After that you get the other gems easily with (from project root):
|
||||||
|
|
||||||
(6) Create database (schema) and load defaults
|
(6) Create database (schema) and load defaults
|
||||||
--------------------------
|
--------------------------
|
||||||
rake db:create
|
rake db:setup
|
||||||
rake db:schema:load
|
|
||||||
rake db:seed
|
|
||||||
|
|
||||||
With this, you also get a ready to go user with username 'admin' and password 'secret'.
|
With this, you also get a ready to go user with username 'admin' and password 'secret'.
|
||||||
|
|
||||||
|
@ -70,4 +69,28 @@ With this, you also get a ready to go user with username 'admin' and password 's
|
||||||
---------------
|
---------------
|
||||||
Start the WEBrick server to try it out:
|
Start the WEBrick server to try it out:
|
||||||
|
|
||||||
script/server
|
bundle exec rails s
|
||||||
|
|
||||||
|
|
||||||
|
(8) (optional) Get background jobs done
|
||||||
|
---------------------------------------
|
||||||
|
We use for time intensive tasks a background job queue, at the moment resque with redis as key/value store.
|
||||||
|
Install redis (in ubuntu the package redis-server works out of the box) and start the resque worker with:
|
||||||
|
|
||||||
|
rake resque:work QUEUE=foodsoft_notifier
|
||||||
|
|
||||||
|
To have look on the current queue, failed jobs etc start the resque server with
|
||||||
|
|
||||||
|
resque-web
|
||||||
|
|
||||||
|
|
||||||
|
(9) (optional) View mails in browser instead in your logs
|
||||||
|
---------------------------------------------------------
|
||||||
|
We use mailcatcher in development mode to view all delivered mails in a browser interface.
|
||||||
|
Just install mailcatcher with gem install mailcatcher and start the service with
|
||||||
|
|
||||||
|
mailcatcher
|
||||||
|
|
||||||
|
From now on you have a smpt server listening on 1025. To see the emails go to
|
||||||
|
|
||||||
|
http://localhost:1080
|
||||||
|
|
8
Rakefile
|
@ -1,10 +1,8 @@
|
||||||
|
#!/usr/bin/env rake
|
||||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
# 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.
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||||
|
|
||||||
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
|
require File.expand_path('../config/application', __FILE__)
|
||||||
|
|
||||||
require 'rake'
|
require 'rake'
|
||||||
require 'rake/testtask'
|
|
||||||
#require 'rake/rdoctask'
|
|
||||||
|
|
||||||
require 'tasks/rails'
|
Foodsoft::Application.load_tasks
|
||||||
|
|
Before Width: | Height: | Size: 265 B After Width: | Height: | Size: 265 B |
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 311 B |
Before Width: | Height: | Size: 451 B After Width: | Height: | Size: 451 B |
Before Width: | Height: | Size: 256 B After Width: | Height: | Size: 256 B |
Before Width: | Height: | Size: 401 B After Width: | Height: | Size: 401 B |
Before Width: | Height: | Size: 514 B After Width: | Height: | Size: 514 B |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 378 B |
Before Width: | Height: | Size: 550 B After Width: | Height: | Size: 550 B |
Before Width: | Height: | Size: 621 B After Width: | Height: | Size: 621 B |
Before Width: | Height: | Size: 203 B After Width: | Height: | Size: 203 B |
Before Width: | Height: | Size: 677 B After Width: | Height: | Size: 677 B |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
BIN
app/assets/images/rails.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 549 B After Width: | Height: | Size: 549 B |
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 458 B |
118
app/assets/javascripts/application.js
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
//= require jquery
|
||||||
|
//= require jquery-ui
|
||||||
|
//= require jquery_ujs
|
||||||
|
//= require twitter/bootstrap
|
||||||
|
//= require jquery.tokeninput
|
||||||
|
//= require bootstrap-datepicker
|
||||||
|
//= require bootstrap-datepicker.de
|
||||||
|
//= require jquery.observe_field
|
||||||
|
//= require rails.validations
|
||||||
|
//= require_self
|
||||||
|
//= require ordering
|
||||||
|
|
||||||
|
// Load following statements, when DOM is ready
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
// Show/Hide a specific DOM element
|
||||||
|
$('a[data-toggle-this]').live('click', function() {
|
||||||
|
$($(this).data('toggle-this')).toggle();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove this item from DOM
|
||||||
|
$('a[data-remove-this]').live('click', function() {
|
||||||
|
$($(this).data('remove-this')).remove();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check/Uncheck a single checkbox
|
||||||
|
$('[data-check-this]').live('click', function() {
|
||||||
|
var checkbox = $($(this).data('check-this'));
|
||||||
|
checkbox.attr('checked', !checkbox.is(':checked'));
|
||||||
|
highlightRow(checkbox);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check/Uncheck all checkboxes for a specific form
|
||||||
|
$('input[data-check-all]').live('click', function() {
|
||||||
|
var status = $(this).is(':checked')
|
||||||
|
$($(this).data('check-all')).find('input[type="checkbox"]').each(function() {
|
||||||
|
$(this).attr('checked', status);
|
||||||
|
highlightRow($(this));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Submit form when changing a select menu.
|
||||||
|
$('form[data-submit-onchange] select').live('change', function() {
|
||||||
|
var confirmMessage = $(this).children(':selected').data('confirm');
|
||||||
|
if (confirmMessage) {
|
||||||
|
if (confirm(confirmMessage)) {
|
||||||
|
$(this).parents('form').submit();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$(this).parents('form').submit();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Submit form when changing text of an input field
|
||||||
|
// Use jquery observe_field plugin
|
||||||
|
$('form[data-submit-onchange] input[type=text]').each(function() {
|
||||||
|
$(this).observe_field(1, function() {
|
||||||
|
$(this).parents('form').submit();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Submit form when clicking on checkbox
|
||||||
|
$('form[data-submit-onchange] input[type=checkbox]:not(input[data-ignore-onchange])').click(function() {
|
||||||
|
$(this).parents('form').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('[data-redirect-to]').bind('change', function() {
|
||||||
|
var newLocation = $(this).children(':selected').val();
|
||||||
|
if (newLocation != "") {
|
||||||
|
document.location.href = newLocation;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remote paginations
|
||||||
|
$('div.pagination[data-remote] a').live('click', function() {
|
||||||
|
$.getScript($(this).attr('href'));
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show and hide loader on ajax callbacks
|
||||||
|
$('*[data-remote]').bind('ajax:beforeSend', function() {
|
||||||
|
$('#loader').show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('*[data-remote]').bind('ajax:complete', function() {
|
||||||
|
$('#loader').hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Disable submit button on ajax forms
|
||||||
|
$('form[data-remote]').bind('ajax:beforeSend', function() {
|
||||||
|
$(this).children('input[type="submit"]').attr('disabled', 'disabled');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use bootstrap datepicker for dateinput
|
||||||
|
$('.datepicker').datepicker({format: 'yyyy-mm-dd', weekStart: 1, language: 'de'});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// gives the row an yellow background
|
||||||
|
function highlightRow(checkbox) {
|
||||||
|
var row = checkbox.parents('tr');
|
||||||
|
if (checkbox.is(':checked')) {
|
||||||
|
row.addClass('selected');
|
||||||
|
} else {
|
||||||
|
row.removeClass('selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use with auto_complete to set a unique id,
|
||||||
|
// e.g. when the user selects a (may not unique) name
|
||||||
|
// There must be a hidden field with the id 'hidden_field'
|
||||||
|
function setHiddenId(text, li) {
|
||||||
|
$('hidden_id').value = li.id;
|
||||||
|
}
|
4
app/assets/javascripts/bootstrap.js.coffee
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
jQuery ->
|
||||||
|
$("a[rel=popover]").popover()
|
||||||
|
$(".tooltip").tooltip()
|
||||||
|
$("a[rel=tooltip]").tooltip()
|
187
app/assets/javascripts/ordering.js
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
// JavaScript that handles the dynamic ordering quantities on the ordering page.
|
||||||
|
//
|
||||||
|
// In a JavaScript block on the actual view, define the article data by calls to setData().
|
||||||
|
// You should also set the available group balance through setGroupBalance(amount).
|
||||||
|
//
|
||||||
|
// Call setDecimalSeparator(char) to overwrite the default character "." with a localized value.
|
||||||
|
|
||||||
|
var modified = false // indicates if anything has been clicked on this page
|
||||||
|
var groupBalance = 0; // available group money
|
||||||
|
var decimalSeparator = "."; // default decimal separator
|
||||||
|
var toleranceIsCostly = true; // default tolerance behaviour
|
||||||
|
var isStockit = false; // Wheter the order is from stock oder normal supplier
|
||||||
|
|
||||||
|
// Article data arrays:
|
||||||
|
var price = new Array();
|
||||||
|
var unit = new Array(); // items per order unit
|
||||||
|
var itemTotal = new Array(); // total item price
|
||||||
|
var quantityOthers = new Array();
|
||||||
|
var toleranceOthers = new Array();
|
||||||
|
var itemsAllocated = new Array(); // how many items the group has been allocated and should definitely get
|
||||||
|
var quantityAvailable = new Array(); // stock_order. how many items are currently in stock
|
||||||
|
|
||||||
|
function setDecimalSeparator(character) {
|
||||||
|
decimalSeparator = character;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setToleranceBehaviour(value) {
|
||||||
|
toleranceIsCostly = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStockit(value) {
|
||||||
|
isStockit = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGroupBalance(amount) {
|
||||||
|
groupBalance = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addData(orderArticleId, itemPrice, itemUnit, itemSubtotal, itemQuantityOthers, itemToleranceOthers, allocated, available) {
|
||||||
|
var i = orderArticleId;
|
||||||
|
price[i] = itemPrice;
|
||||||
|
unit[i] = itemUnit;
|
||||||
|
itemTotal[i] = itemSubtotal;
|
||||||
|
quantityOthers[i] = itemQuantityOthers;
|
||||||
|
toleranceOthers[i] = itemToleranceOthers;
|
||||||
|
itemsAllocated[i] = allocated;
|
||||||
|
quantityAvailable[i] = available;
|
||||||
|
}
|
||||||
|
|
||||||
|
function increaseQuantity(item) {
|
||||||
|
var value = Number($('#q_' + item).val()) + 1;
|
||||||
|
if (!isStockit || (value <= (quantityAvailable[item] + itemsAllocated[item]))) {
|
||||||
|
update(item, value, $('#t_' + item).val());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decreaseQuantity(item) {
|
||||||
|
var value = Number($('#q_' + item).val()) - 1;
|
||||||
|
if (value >= 0) {
|
||||||
|
update(item, value, $('#t_' + item).val());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function increaseTolerance(item) {
|
||||||
|
var value = Number($('#t_' + item).val()) + 1;
|
||||||
|
update(item, $('#q_' + item).val(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function decreaseTolerance(item) {
|
||||||
|
var value = Number($('#t_' + item).val()) - 1;
|
||||||
|
if (value >= 0) {
|
||||||
|
update(item, $('#q_' + item).val(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(item, quantity, tolerance) {
|
||||||
|
// set modification flag
|
||||||
|
modified = true
|
||||||
|
|
||||||
|
// update hidden input fields
|
||||||
|
$('#q_' + item).val(quantity);
|
||||||
|
$('#t_' + item).val(tolerance);
|
||||||
|
|
||||||
|
// calculate how many units would be ordered in total
|
||||||
|
var units = calcUnits(unit[item], quantityOthers[item] + Number(quantity), toleranceOthers[item] + Number(tolerance));
|
||||||
|
if (unitCompletedFromTolerance(unit[item], quantityOthers[item] + Number(quantity), toleranceOthers[item] + Number(tolerance))) {
|
||||||
|
$('#units_' + item).html('<span style=\"color:grey\">' + String(units) + '</span>');
|
||||||
|
} else {
|
||||||
|
$('#units_' + item).html(String(units));
|
||||||
|
}
|
||||||
|
|
||||||
|
// update used/unused quantity
|
||||||
|
var available = Math.max(0, units * unit[item] - quantityOthers[item]);
|
||||||
|
var q_used = Math.min(available, quantity);
|
||||||
|
// ensure that at least the amout of items this group has already been allocated is used
|
||||||
|
if (quantity >= itemsAllocated[item] && q_used < itemsAllocated[item]) {
|
||||||
|
q_used = itemsAllocated[item];
|
||||||
|
}
|
||||||
|
$('#q_used_' + item).html(String(q_used));
|
||||||
|
$('#q_unused_' + item).html(String(quantity - q_used));
|
||||||
|
$('#q_total_' + item).html(String(Number(quantity) + quantityOthers[item]));
|
||||||
|
|
||||||
|
// update used/unused tolerance
|
||||||
|
if (unit[item] > 1) {
|
||||||
|
available = Math.max(0, available - q_used - toleranceOthers[item]);
|
||||||
|
t_used = Math.min(available, tolerance);
|
||||||
|
$('#t_used_' + item).html(String(t_used));
|
||||||
|
$('#t_unused_' + item).html(String(tolerance - t_used));
|
||||||
|
$('#t_total_' + item).html(String(Number(tolerance) + toleranceOthers[item]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// update total price
|
||||||
|
if(toleranceIsCostly == true) {
|
||||||
|
itemTotal[item] = price[item] * (Number(quantity) + Number(tolerance));
|
||||||
|
} else {
|
||||||
|
itemTotal[item] = price[item] * (Number(quantity));
|
||||||
|
}
|
||||||
|
$('#price_' + item + '_display').html(asMoney(itemTotal[item]));
|
||||||
|
|
||||||
|
// update missing units
|
||||||
|
var missing_units = unit[item] - (((quantityOthers[item] + Number(quantity)) % unit[item]) + Number(tolerance) + toleranceOthers[item])
|
||||||
|
if (missing_units < 0) {
|
||||||
|
missing_units = 0;
|
||||||
|
}
|
||||||
|
$('#missing_units_' + item).html(String(missing_units));
|
||||||
|
|
||||||
|
// update balance
|
||||||
|
updateBalance();
|
||||||
|
}
|
||||||
|
|
||||||
|
function asMoney(amount) {
|
||||||
|
return String(amount.toFixed(2)).replace(/\./, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcUnits(unitSize, quantity, tolerance) {
|
||||||
|
var units = Math.floor(quantity / unitSize)
|
||||||
|
var remainder = quantity % unitSize
|
||||||
|
return units + ((remainder > 0) && (remainder + tolerance >= unitSize) ? 1 : 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function unitCompletedFromTolerance(unitSize, quantity, tolerance) {
|
||||||
|
var remainder = quantity % unitSize
|
||||||
|
return (remainder > 0 && (remainder + tolerance >= unitSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBalance() {
|
||||||
|
// update total price and order balance
|
||||||
|
var total = 0;
|
||||||
|
for (i in itemTotal) {
|
||||||
|
total += itemTotal[i];
|
||||||
|
}
|
||||||
|
$('#total_price').html(asMoney(total));
|
||||||
|
var balance = groupBalance - total;
|
||||||
|
$('#new_balance').html(asMoney(balance));
|
||||||
|
$('#total_balance').val(asMoney(balance));
|
||||||
|
// determine bgcolor and submit button state according to balance
|
||||||
|
var bgcolor = '';
|
||||||
|
if (balance < 0) {
|
||||||
|
bgcolor = '#FF0000';
|
||||||
|
$('#submit_button').attr('disabled', 'disabled')
|
||||||
|
} else {
|
||||||
|
$('#submit_button').removeAttr('disabled')
|
||||||
|
}
|
||||||
|
// update bgcolor
|
||||||
|
for (i in itemTotal) {
|
||||||
|
$('#td_price_' + i).css('background-color', bgcolor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$('input[data-increase_quantity]').click(function() {
|
||||||
|
increaseQuantity($(this).data('increase_quantity'));
|
||||||
|
});
|
||||||
|
$('input[data-decrease_quantity]').click(function() {
|
||||||
|
decreaseQuantity($(this).data('decrease_quantity'));
|
||||||
|
});
|
||||||
|
$('input[data-increase_tolerance]').click(function() {
|
||||||
|
increaseTolerance($(this).data('increase_tolerance'));
|
||||||
|
});
|
||||||
|
$('input[data-decrease_tolerance]').click(function() {
|
||||||
|
decreaseTolerance($(this).data('decrease_tolerance'));
|
||||||
|
});
|
||||||
|
|
||||||
|
$('a[data-confirm_switch_order]').click(function() {
|
||||||
|
return (!modified || confirm('Änderungen an dieser Bestellung gehen verloren, wenn zu einer anderen Bestellung gewechselt wird. Möchtest Du trotzdem wechseln?'));
|
||||||
|
});
|
||||||
|
});
|
4
app/assets/stylesheets/application.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/*
|
||||||
|
*= require bootstrap_and_overrides
|
||||||
|
*= require token-input-bootstrappy
|
||||||
|
*/
|
210
app/assets/stylesheets/bootstrap_and_overrides.css.less
vendored
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
@import "twitter/bootstrap/bootstrap";
|
||||||
|
body {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "twitter/bootstrap/responsive";
|
||||||
|
|
||||||
|
// Set the correct sprite paths
|
||||||
|
@iconSpritePath: asset-path('twitter/bootstrap/glyphicons-halflings.png');
|
||||||
|
@iconWhiteSpritePath: asset-path('twitter/bootstrap/glyphicons-halflings-white.png');
|
||||||
|
|
||||||
|
// Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
|
||||||
|
// Note: If you use asset_path() here, your compiled boostrap_and_overrides.css will not
|
||||||
|
// have the proper paths. So for now we use the absolute path.
|
||||||
|
@fontAwesomeEotPath: '/assets/fontawesome-webfont.eot';
|
||||||
|
@fontAwesomeWoffPath: '/assets/fontawesome-webfont.woff';
|
||||||
|
@fontAwesomeTtfPath: '/assets/fontawesome-webfont.ttf';
|
||||||
|
@fontAwesomeSvgPath: '/assets/fontawesome-webfont.svg';
|
||||||
|
|
||||||
|
// Font Awesome
|
||||||
|
@import "fontawesome";
|
||||||
|
|
||||||
|
// Your custom LESS stylesheets goes here
|
||||||
|
//
|
||||||
|
// Since bootstrap was imported above you have access to its mixins which
|
||||||
|
// you may use and inherit here
|
||||||
|
//
|
||||||
|
// If you'd like to override bootstrap's own variables, you can do so here as well
|
||||||
|
// See http://twitter.github.com/bootstrap/less.html for their names and documentation
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// @linkColor: #ff0000;
|
||||||
|
|
||||||
|
|
||||||
|
// Bootstrap datepicker
|
||||||
|
@import "datepicker";
|
||||||
|
|
||||||
|
// Custom styles
|
||||||
|
|
||||||
|
// Fix empty dd tags in horizontal dl, see https://github.com/twitter/bootstrap/issues/4062
|
||||||
|
.dl-horizontal {
|
||||||
|
dd { .clearfix(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@mainRedColor: #ED0606;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
margin: 10px 0 0 30px;
|
||||||
|
float: left;
|
||||||
|
font-size: 35px;
|
||||||
|
font-weight: bold;
|
||||||
|
display: block;
|
||||||
|
color: @mainRedColor;
|
||||||
|
|
||||||
|
span {
|
||||||
|
padding: 2px 4px;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: @mainRedColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
section {
|
||||||
|
padding-bottom: 30px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
border-bottom: 1px solid #d3d3d3;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: 50px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid lightGrey;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
|
||||||
|
a.sortdown:after {
|
||||||
|
content: ' \25BC';
|
||||||
|
}
|
||||||
|
a.sortup:after {
|
||||||
|
content: ' \25B2';
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.article-category {
|
||||||
|
background-color: #efefef;
|
||||||
|
td:first-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
th.numeric, td.numeric {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
td.odd {
|
||||||
|
background-color: @tableBackgroundAccent;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.selected td {
|
||||||
|
background-color: @successBackground;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tasks ..
|
||||||
|
.accepted {
|
||||||
|
color: #468847;
|
||||||
|
}
|
||||||
|
.unaccepted {
|
||||||
|
color: #B94A48;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wiki
|
||||||
|
#wikiContent {
|
||||||
|
.editsection {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.mw-headline a {
|
||||||
|
color: @textColor;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ordering
|
||||||
|
span.used {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
span.unused {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#order-footer, .article-info {
|
||||||
|
text-align: left;
|
||||||
|
z-index: 1;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #E4EED6;
|
||||||
|
border-top: 2px solid #78B74E;
|
||||||
|
|
||||||
|
#total-sum {
|
||||||
|
width: 22em;
|
||||||
|
margin: .5em 2em 0 0;
|
||||||
|
float: right;
|
||||||
|
#order-button {
|
||||||
|
margin: .5em 0;
|
||||||
|
|
||||||
|
input:disabled {
|
||||||
|
background-color: red; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#order-footer {
|
||||||
|
width: 100%;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-info {
|
||||||
|
z-index: 2;
|
||||||
|
width: 40em;
|
||||||
|
height: 8em;
|
||||||
|
border: none;
|
||||||
|
left: 30px;
|
||||||
|
|
||||||
|
.article-name {
|
||||||
|
text-align: center;
|
||||||
|
margin: 2px 0;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
width: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.pull-right {
|
||||||
|
width: 35%;
|
||||||
|
}
|
||||||
|
.pull-left {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.order-article .article-info {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.order-article:hover .article-info {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ********* Articles
|
||||||
|
|
||||||
|
tr.just-updated {
|
||||||
|
color: #468847;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.unavailable {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
// articles edit all
|
||||||
|
.field_with_errors {
|
||||||
|
input, select {
|
||||||
|
border-color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ********* Tweaks & fixes
|
||||||
|
|
||||||
|
// need more space for supplier&order information (in German, at least)
|
||||||
|
.dl-horizontal {
|
||||||
|
dt { width: 160px; }
|
||||||
|
dd { margin-left: 170px; }
|
||||||
|
}
|
147
app/assets/stylesheets/datepicker.less
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
/*!
|
||||||
|
* Datepicker for Bootstrap
|
||||||
|
*
|
||||||
|
* Copyright 2012 Stefan Petre
|
||||||
|
* Improvements by Andrew Rowls
|
||||||
|
* Licensed under the Apache License v2.0
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
.datepicker {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
padding: 4px;
|
||||||
|
margin-top: 1px;
|
||||||
|
.border-radius(4px);
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
border-left: 7px solid transparent;
|
||||||
|
border-right: 7px solid transparent;
|
||||||
|
border-bottom: 7px solid #ccc;
|
||||||
|
border-bottom-color: rgba(0,0,0,.2);
|
||||||
|
position: absolute;
|
||||||
|
top: -7px;
|
||||||
|
left: 6px;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
border-left: 6px solid transparent;
|
||||||
|
border-right: 6px solid transparent;
|
||||||
|
border-bottom: 6px solid @white;
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
left: 7px;
|
||||||
|
}
|
||||||
|
>div {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&.days div.datepicker-days {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
&.months div.datepicker-months {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
&.years div.datepicker-years {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
table{
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
td,
|
||||||
|
th{
|
||||||
|
text-align: center;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
.border-radius(4px);
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
&.day:hover {
|
||||||
|
background: @grayLighter;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
&.old,
|
||||||
|
&.new {
|
||||||
|
color: @grayLight;
|
||||||
|
}
|
||||||
|
&.disabled,
|
||||||
|
&.disabled:hover {
|
||||||
|
background: none;
|
||||||
|
color: @grayLight;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
&.today,
|
||||||
|
&.today:hover,
|
||||||
|
&.today.disabled,
|
||||||
|
&.today.disabled:hover {
|
||||||
|
@todayBackground: lighten(@orange, 30%);
|
||||||
|
.buttonBackground(@todayBackground, spin(@todayBackground, 20));
|
||||||
|
}
|
||||||
|
&.active,
|
||||||
|
&.active:hover,
|
||||||
|
&.active.disabled,
|
||||||
|
&.active.disabled:hover {
|
||||||
|
.buttonBackground(@btnPrimaryBackground, spin(@btnPrimaryBackground, 20));
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 -1px 0 rgba(0,0,0,.25);
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
width: 23%;
|
||||||
|
height: 54px;
|
||||||
|
line-height: 54px;
|
||||||
|
float: left;
|
||||||
|
margin: 1%;
|
||||||
|
cursor: pointer;
|
||||||
|
.border-radius(4px);
|
||||||
|
&:hover {
|
||||||
|
background: @grayLighter;
|
||||||
|
}
|
||||||
|
&.disabled,
|
||||||
|
&.disabled:hover {
|
||||||
|
background:none;
|
||||||
|
color: @grayLight;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
&.active,
|
||||||
|
&.active:hover,
|
||||||
|
&.active.disabled,
|
||||||
|
&.active.disabled:hover {
|
||||||
|
.buttonBackground(@btnPrimaryBackground, spin(@btnPrimaryBackground, 20));
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 -1px 0 rgba(0,0,0,.25);
|
||||||
|
}
|
||||||
|
&.old {
|
||||||
|
color: @grayLight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th.switch {
|
||||||
|
width: 145px;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead tr:first-child th,
|
||||||
|
tfoot tr:first-child th {
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover{
|
||||||
|
background: @grayLighter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*.dow {
|
||||||
|
border-top: 1px solid #ddd !important;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
.input-append,
|
||||||
|
.input-prepend {
|
||||||
|
&.date {
|
||||||
|
.add-on i {
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
// colors which are used in the foodsoft
|
// colors which are used in the foodsoft
|
||||||
!main_red = #ED0606
|
$main_red: #ED0606
|
||||||
!hover_yellow = #ffff72
|
$hover_yellow: #ffff72
|
||||||
!boxContent = #e4eed6
|
$boxContent: #e4eed6
|
||||||
!lightGrey = #efefef
|
$lightGrey: #efefef
|
||||||
!darkGreen = #78b74e
|
$darkGreen: #78b74e
|
||||||
!lightGreen = #e4eed6
|
$lightGreen: #e4eed6
|
||||||
|
|
||||||
/* General rules ...
|
// General rules ...
|
||||||
body
|
body
|
||||||
:background-color #fff
|
:background-color #fff
|
||||||
:color black
|
:color black
|
||||||
|
@ -26,17 +26,17 @@ body
|
||||||
:border
|
:border
|
||||||
:width 2px
|
:width 2px
|
||||||
:style solid
|
:style solid
|
||||||
:color = !main_red
|
color: $main_red
|
||||||
|
|
||||||
a, a:visited
|
a, a:visited
|
||||||
:text-decoration underline
|
:text-decoration underline
|
||||||
:color black
|
:color black
|
||||||
|
|
||||||
a:hover
|
a:hover
|
||||||
:color = !main_red
|
color: $main_red
|
||||||
|
|
||||||
h1, h2
|
h1, h2
|
||||||
:color = !main_red
|
color: $main_red
|
||||||
|
|
||||||
h1
|
h1
|
||||||
:font-size 2.2em
|
:font-size 2.2em
|
||||||
|
@ -46,7 +46,7 @@ h1
|
||||||
:border-bottom
|
:border-bottom
|
||||||
:width 1px
|
:width 1px
|
||||||
:style dotted
|
:style dotted
|
||||||
:color = !main_red
|
color: $main_red
|
||||||
|
|
||||||
h2
|
h2
|
||||||
:font-size 1.4em
|
:font-size 1.4em
|
||||||
|
@ -88,7 +88,7 @@ option
|
||||||
border-top: 1px solid #D7D7D7
|
border-top: 1px solid #D7D7D7
|
||||||
margin: .2em 0
|
margin: .2em 0
|
||||||
|
|
||||||
span.click-me
|
.click-me
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
.left
|
.left
|
||||||
float: left
|
float: left
|
||||||
|
@ -104,10 +104,15 @@ span.click-me
|
||||||
.hidden
|
.hidden
|
||||||
display: none
|
display: none
|
||||||
|
|
||||||
|
.warning
|
||||||
|
background: yellow
|
||||||
|
font-weight: bold
|
||||||
|
padding: 1px 10px
|
||||||
|
|
||||||
// ********************************* loginpage
|
// ********************************* loginpage
|
||||||
#login
|
#login
|
||||||
:margin auto
|
:margin auto
|
||||||
:width 27em
|
:width 35em
|
||||||
:font-size 1.2em
|
:font-size 1.2em
|
||||||
|
|
||||||
#login #meta
|
#login #meta
|
||||||
|
@ -124,7 +129,7 @@ span.click-me
|
||||||
|
|
||||||
|
|
||||||
#logo
|
#logo
|
||||||
:background = !main_red
|
background: $main_red
|
||||||
:height 1.1em
|
:height 1.1em
|
||||||
:width 8em
|
:width 8em
|
||||||
:padding 0 20px
|
:padding 0 20px
|
||||||
|
@ -136,17 +141,17 @@ span.click-me
|
||||||
:margin 0
|
:margin 0
|
||||||
a, a:hover
|
a, a:hover
|
||||||
:color white
|
:color white
|
||||||
:background-color = !main_red
|
background-color: $main_red
|
||||||
:text-decoration none
|
:text-decoration none
|
||||||
a span
|
a span
|
||||||
:color = !main_red
|
color: $main_red
|
||||||
:background #FFF
|
:background #FFF
|
||||||
:padding-right 0.1em
|
:padding-right 0.1em
|
||||||
:font-weight bold
|
:font-weight bold
|
||||||
:border-top
|
:border-top
|
||||||
:width 2px
|
:width 2px
|
||||||
:style dotted
|
:style dotted
|
||||||
:color = !main_red
|
color: $main_red
|
||||||
|
|
||||||
#logininfo
|
#logininfo
|
||||||
:position absolute
|
:position absolute
|
||||||
|
@ -162,7 +167,7 @@ span.click-me
|
||||||
:color #737272
|
:color #737272
|
||||||
:font-weight bold
|
:font-weight bold
|
||||||
a:hover
|
a:hover
|
||||||
:color = !main_red
|
color: $main_red
|
||||||
|
|
||||||
// ************************************* box structure
|
// ************************************* box structure
|
||||||
#main
|
#main
|
||||||
|
@ -268,8 +273,6 @@ table
|
||||||
:color #008000
|
:color #008000
|
||||||
tr.selected, tr.active
|
tr.selected, tr.active
|
||||||
:background-color #ffffc2
|
:background-color #ffffc2
|
||||||
tr.click-me
|
|
||||||
:cursor pointer
|
|
||||||
tr.ignored
|
tr.ignored
|
||||||
color: grey
|
color: grey
|
||||||
tr.success
|
tr.success
|
||||||
|
@ -290,7 +293,7 @@ table tfoot tr
|
||||||
:padding-top 0.8em
|
:padding-top 0.8em
|
||||||
|
|
||||||
tr.edit_inline
|
tr.edit_inline
|
||||||
:background-color = !hover_yellow
|
background-color: $hover_yellow
|
||||||
td, span
|
td, span
|
||||||
:padding 0.5em 0.2em
|
:padding 0.5em 0.2em
|
||||||
|
|
||||||
|
@ -318,15 +321,15 @@ td.currency, td.actions
|
||||||
:text-align right
|
:text-align right
|
||||||
:padding-right 0.5em
|
:padding-right 0.5em
|
||||||
td.closed
|
td.closed
|
||||||
background: url(/images/arrow_right_red.png) no-repeat center left
|
background: image-url('arrow_right_red.png') no-repeat center left
|
||||||
a
|
a
|
||||||
display: block
|
display: block
|
||||||
text-decoration: none
|
text-decoration: none
|
||||||
padding-left: 20px
|
padding-left: 20px
|
||||||
td.open
|
td.open
|
||||||
background: url(/images/arrow_down_red.png) no-repeat center left
|
background: image-url('arrow_down_red.png') no-repeat center left
|
||||||
|
|
||||||
// ************************************* for edit formulars */
|
// ************************************* for edit formulars
|
||||||
div.edit_form
|
div.edit_form
|
||||||
:border 2px solid #e3e3e3
|
:border 2px solid #e3e3e3
|
||||||
:background #f5f5f5
|
:background #f5f5f5
|
||||||
|
@ -345,7 +348,7 @@ div.edit_form
|
||||||
:border
|
:border
|
||||||
:width 3px
|
:width 3px
|
||||||
:style solid
|
:style solid
|
||||||
:color = !main_red
|
color: $main_red
|
||||||
|
|
||||||
// ***************************************** other boxes */
|
// ***************************************** other boxes */
|
||||||
|
|
||||||
|
@ -384,7 +387,7 @@ div.box_title
|
||||||
:font-size 1.3em
|
:font-size 1.3em
|
||||||
|
|
||||||
div.column_content
|
div.column_content
|
||||||
:background = !boxContent
|
background: $boxContent
|
||||||
:color black
|
:color black
|
||||||
:padding 10px
|
:padding 10px
|
||||||
margin-bottom: 2em
|
margin-bottom: 2em
|
||||||
|
@ -411,7 +414,7 @@ li.check div.spinner
|
||||||
:display block
|
:display block
|
||||||
:height 5px
|
:height 5px
|
||||||
:width 21px
|
:width 21px
|
||||||
:background-image url(/images/dots-white.gif)
|
:background-image image-url('dots-white.gif')
|
||||||
:line-height 16px
|
:line-height 16px
|
||||||
:float left
|
:float left
|
||||||
:margin-right 5px
|
:margin-right 5px
|
||||||
|
@ -439,7 +442,7 @@ table#order
|
||||||
-webkit-border-radius: 3px
|
-webkit-border-radius: 3px
|
||||||
padding: 0
|
padding: 0
|
||||||
th#col_required, th#col_tolerance
|
th#col_required, th#col_tolerance
|
||||||
:width 140px
|
:width 145px
|
||||||
th#col_packages, th#col_left_units
|
th#col_packages, th#col_left_units
|
||||||
:width 50px
|
:width 50px
|
||||||
td.quantity, td.tolerance
|
td.quantity, td.tolerance
|
||||||
|
@ -453,7 +456,7 @@ table#order
|
||||||
:padding-left 10px
|
:padding-left 10px
|
||||||
tfoot
|
tfoot
|
||||||
tr
|
tr
|
||||||
:background-color = !lightGreen
|
background-color: $lightGreen
|
||||||
td
|
td
|
||||||
:padding-right 10px
|
:padding-right 10px
|
||||||
#order-footer, .article-info
|
#order-footer, .article-info
|
||||||
|
@ -500,6 +503,7 @@ tr.order-article .article-info
|
||||||
display: none
|
display: none
|
||||||
tr.order-article:hover .article-info
|
tr.order-article:hover .article-info
|
||||||
display: block
|
display: block
|
||||||
|
|
||||||
// ********* Comments
|
// ********* Comments
|
||||||
#newComment
|
#newComment
|
||||||
:margin 1em
|
:margin 1em
|
||||||
|
@ -555,6 +559,7 @@ ul.autocomplete
|
||||||
background-color: #fff
|
background-color: #fff
|
||||||
text-align: center
|
text-align: center
|
||||||
margin: 0 10px 10px 0
|
margin: 0 10px 10px 0
|
||||||
|
|
||||||
// *** wiki
|
// *** wiki
|
||||||
#wiki_content
|
#wiki_content
|
||||||
border-style: none
|
border-style: none
|
||||||
|
@ -600,7 +605,8 @@ ul.autocomplete
|
||||||
margin: 0.3em 0 0 3.2em
|
margin: 0.3em 0 0 3.2em
|
||||||
padding: 0
|
padding: 0
|
||||||
list-style-image: none
|
list-style-image: none
|
||||||
li margin-bottom: 0.1em
|
li
|
||||||
|
margin-bottom: 0.1em
|
||||||
|
|
||||||
a.new_wiki_link
|
a.new_wiki_link
|
||||||
color: grey
|
color: grey
|
||||||
|
@ -612,7 +618,7 @@ a.new_wiki_link
|
||||||
margin-bottom: 2em
|
margin-bottom: 2em
|
||||||
width: 25em
|
width: 25em
|
||||||
border: 1px solid grey
|
border: 1px solid grey
|
||||||
:background-color= !lightGrey
|
background-color: $lightGrey
|
||||||
h2
|
h2
|
||||||
font-size: 1em
|
font-size: 1em
|
||||||
color: black
|
color: black
|
||||||
|
@ -626,14 +632,13 @@ a.new_wiki_link
|
||||||
height: 1em
|
height: 1em
|
||||||
color: #ED0606
|
color: #ED0606
|
||||||
a
|
a
|
||||||
:color = !main_red
|
color: $main_red
|
||||||
:text-decoration = none
|
text-decoration: none
|
||||||
a:hover
|
a:hover
|
||||||
:text-decoration = underline
|
text-decoration: underline
|
||||||
#sidebar
|
#sidebar
|
||||||
float: right
|
float: right
|
||||||
width: 290px
|
width: 290px
|
||||||
margin-top: -60px
|
|
||||||
#sidebar-links
|
#sidebar-links
|
||||||
margin-bottom: 18px
|
margin-bottom: 18px
|
||||||
text-align: right
|
text-align: right
|
|
@ -294,14 +294,14 @@ td.currency, td.actions {
|
||||||
padding-right: 0.5em; }
|
padding-right: 0.5em; }
|
||||||
|
|
||||||
td.closed {
|
td.closed {
|
||||||
background: url(/images/arrow_right_red.png) no-repeat center left; }
|
background: image-url('arrow_right_red.png') no-repeat center left; }
|
||||||
td.closed a {
|
td.closed a {
|
||||||
display: block;
|
display: block;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding-left: 20px; }
|
padding-left: 20px; }
|
||||||
|
|
||||||
td.open {
|
td.open {
|
||||||
background: url(/images/arrow_down_red.png) no-repeat center left; }
|
background: image-url('arrow_down_red.png') no-repeat center left; }
|
||||||
|
|
||||||
div.edit_form {
|
div.edit_form {
|
||||||
border: 2px solid #e3e3e3;
|
border: 2px solid #e3e3e3;
|
||||||
|
@ -371,7 +371,7 @@ li.check div.spinner {
|
||||||
display: block;
|
display: block;
|
||||||
height: 5px;
|
height: 5px;
|
||||||
width: 21px;
|
width: 21px;
|
||||||
background-image: url(/images/dots-white.gif);
|
background-image: image-url('dots-white.gif');
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
|
@ -516,25 +516,24 @@ ul.autocomplete .informal {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0 10px 10px 0; }
|
margin: 0 10px 10px 0; }
|
||||||
|
|
||||||
#wiki_content {
|
|
||||||
border-style: none;
|
|
||||||
color: black;
|
|
||||||
line-height: 1.5em; }
|
|
||||||
|
|
||||||
.wiki_show, .wiki_version, .wiki_new, .wiki_edit, .wiki_all {
|
.wiki_show, .wiki_version, .wiki_new, .wiki_edit, .wiki_all {
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
padding: 10px; }
|
padding: 10px; }
|
||||||
.wiki_show h1, .wiki_version h1, .wiki_new h1, .wiki_edit h1, .wiki_all h1 {
|
|
||||||
padding-left: 0;
|
|
||||||
padding-top: 10px;
|
|
||||||
border-bottom-style: solid; }
|
|
||||||
.wiki_show .column_content, .wiki_version .column_content, .wiki_new .column_content, .wiki_edit .column_content, .wiki_all .column_content {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
|
|
||||||
#wiki_content {
|
#wiki_content {
|
||||||
|
border: 1px solid grey;
|
||||||
|
margin-right: 300px;
|
||||||
|
padding: 10px;
|
||||||
|
color: black;
|
||||||
|
line-height: 1.5em;
|
||||||
min-height: 400px; }
|
min-height: 400px; }
|
||||||
#wiki_content span.editsection {
|
#wiki_content span.editsection {
|
||||||
display: none; }
|
display: none; }
|
||||||
|
#wiki_content h1 {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-top: 10px;
|
||||||
|
border: none;
|
||||||
|
margin-bottom: 10px; }
|
||||||
#wiki_content h2, #wiki_content h3, #wiki_content h4, #wiki_content h5, #wiki_content h6 {
|
#wiki_content h2, #wiki_content h3, #wiki_content h4, #wiki_content h5, #wiki_content h6 {
|
||||||
background: transparent none repeat scroll 0 0;
|
background: transparent none repeat scroll 0 0;
|
||||||
border-bottom: 1px solid #AAAAAA;
|
border-bottom: 1px solid #AAAAAA;
|
||||||
|
@ -593,8 +592,7 @@ a.new_wiki_link {
|
||||||
|
|
||||||
#sidebar {
|
#sidebar {
|
||||||
float: right;
|
float: right;
|
||||||
width: 290px;
|
width: 290px; }
|
||||||
margin-top: -60px; }
|
|
||||||
#sidebar #sidebar-links {
|
#sidebar #sidebar-links {
|
||||||
margin-bottom: 18px;
|
margin-bottom: 18px;
|
||||||
text-align: right; }
|
text-align: right; }
|
133
app/assets/stylesheets/token-input-bootstrappy.css
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
/* Example tokeninput style #2: Facebook style */
|
||||||
|
ul.token-input-list-facebook {
|
||||||
|
overflow: hidden;
|
||||||
|
height: auto !important;
|
||||||
|
height: 1%;
|
||||||
|
width: 400px;
|
||||||
|
border: 1px solid #BBB;
|
||||||
|
cursor: text;
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: Verdana, sans-serif;
|
||||||
|
min-height: 1px;
|
||||||
|
z-index: 999;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
list-style-type: none;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.token-input-list-facebook.token-input-focused-facebook {
|
||||||
|
border-color: rgba(82, 168, 236, 0.8);
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||||
|
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||||
|
outline: 0;
|
||||||
|
outline: thin dotted 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.token-input-list-facebook li input {
|
||||||
|
border: 0;
|
||||||
|
width: 100px;
|
||||||
|
padding: 3px 8px;
|
||||||
|
background-color: white;
|
||||||
|
margin: 2px 0;
|
||||||
|
-webkit-appearance: caret;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.token-input-token-facebook {
|
||||||
|
overflow: hidden;
|
||||||
|
height: auto !important;
|
||||||
|
height: 15px;
|
||||||
|
margin: 3px;
|
||||||
|
padding: 1px 3px;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
color: #555;
|
||||||
|
cursor: default;
|
||||||
|
border: 1px solid #BBB;
|
||||||
|
font-size: 11px;
|
||||||
|
border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
float: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.token-input-token-facebook p {
|
||||||
|
display: inline;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.token-input-token-facebook span {
|
||||||
|
color: #a6b3cf;
|
||||||
|
margin-left: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.token-input-selected-token-facebook {
|
||||||
|
background-color: #5670a6;
|
||||||
|
border: 1px solid #3b5998;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.token-input-input-token-facebook {
|
||||||
|
float: left;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.token-input-dropdown-facebook {
|
||||||
|
position: absolute;
|
||||||
|
width: 400px;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
overflow: hidden;
|
||||||
|
border-left: 1px solid #ccc;
|
||||||
|
border-right: 1px solid #ccc;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
cursor: default;
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: Verdana, sans-serif;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.token-input-dropdown-facebook p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.token-input-dropdown-facebook ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.token-input-dropdown-facebook ul li {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 3px;
|
||||||
|
margin: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.token-input-dropdown-facebook ul li.token-input-dropdown-item-facebook {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.token-input-dropdown-facebook ul li.token-input-dropdown-item2-facebook {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.token-input-dropdown-facebook ul li em {
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.token-input-dropdown-facebook ul li.token-input-selected-dropdown-item-facebook {
|
||||||
|
background-color: #08C;
|
||||||
|
color: #f5f5f5;
|
||||||
|
}
|
|
@ -1,69 +1,23 @@
|
||||||
|
# encoding: utf-8
|
||||||
class Admin::OrdergroupsController < Admin::BaseController
|
class Admin::OrdergroupsController < Admin::BaseController
|
||||||
|
inherit_resources
|
||||||
|
|
||||||
def index
|
def index
|
||||||
if (params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 100)
|
@ordergroups = Ordergroup.undeleted.order('name ASC')
|
||||||
@per_page = params[:per_page].to_i
|
|
||||||
else
|
# if somebody uses the search field:
|
||||||
@per_page = 20
|
unless params[:query].blank?
|
||||||
|
@ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:query]}%")
|
||||||
end
|
end
|
||||||
|
|
||||||
# if the search field is used
|
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
||||||
conditions = "name LIKE '%#{params[:query]}%'" unless params[:query].nil?
|
|
||||||
|
|
||||||
@total = Ordergroup.without_deleted.count(:conditions => conditions )
|
|
||||||
@ordergroups = Ordergroup.without_deleted.paginate(:conditions => conditions, :page => params[:page],
|
|
||||||
:per_page => @per_page, :order => 'name')
|
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.html # index.html.erb
|
|
||||||
format.js { render :partial => "ordergroups" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def show
|
|
||||||
@ordergroup = Ordergroup.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def new
|
|
||||||
@ordergroup = Ordergroup.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
@ordergroup = Ordergroup.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
@ordergroup = Ordergroup.new(params[:ordergroup])
|
|
||||||
@ordergroup.account_updated = Time.now
|
|
||||||
|
|
||||||
if @ordergroup.save
|
|
||||||
flash[:notice] = 'Ordergroup was successfully created.'
|
|
||||||
redirect_to([:admin, @ordergroup])
|
|
||||||
else
|
|
||||||
render :action => "new"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
@ordergroup = Ordergroup.find(params[:id])
|
|
||||||
|
|
||||||
if @ordergroup.update_attributes(params[:ordergroup])
|
|
||||||
flash[:notice] = 'Ordergroup was successfully updated.'
|
|
||||||
redirect_to([:admin, @ordergroup])
|
|
||||||
else
|
|
||||||
render :action => "edit"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@ordergroup = Ordergroup.find(params[:id])
|
@ordergroup = Ordergroup.find(params[:id])
|
||||||
@ordergroup.destroy
|
@ordergroup.mark_as_deleted
|
||||||
|
redirect_to admin_ordergroups_url, notice: t('admin.ordergroups.destroy.notice')
|
||||||
redirect_to(admin_ordergroups_url)
|
rescue => error
|
||||||
end
|
redirect_to admin_ordergroups_url, alert: t('admin.ordergroups.destroy.error')
|
||||||
|
|
||||||
def memberships
|
|
||||||
@group = Ordergroup.find(params[:id])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,69 +1,15 @@
|
||||||
class Admin::UsersController < Admin::BaseController
|
class Admin::UsersController < Admin::BaseController
|
||||||
|
inherit_resources
|
||||||
|
|
||||||
def index
|
def index
|
||||||
if (params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 100)
|
@users = User.order('nick ASC')
|
||||||
@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)
|
# if somebody uses the search field:
|
||||||
@users = User.paginate :page => params[:page], :conditions => conditions, :per_page => @per_page, :order => 'nick'
|
unless params[:user_name].blank?
|
||||||
|
@users = @users.where("first_name LIKE :user_name OR last_name LIKE :user_name OR nick LIKE :user_name",
|
||||||
respond_to do |format|
|
user_name: "%#{params[:user_name]}%")
|
||||||
format.html # listUsers.haml
|
|
||||||
format.js do
|
|
||||||
render :update do |page|
|
|
||||||
page.replace_html 'table', :partial => "users"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
@users = @users.page(params[:page]).per(@per_page)
|
||||||
@user = User.find(params[:id])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
|
||||||
@user = User.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
@user = User.new(params[:user])
|
|
||||||
if @user.save
|
|
||||||
flash[:notice] = 'Benutzerin wurde erfolgreich angelegt.'
|
|
||||||
redirect_to admin_users_path
|
|
||||||
else
|
|
||||||
render :action => 'new'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
@user = User.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
@user = User.find(params[:id])
|
|
||||||
if @user.update_attributes(params[:user])
|
|
||||||
flash[:notice] = 'Änderungen wurden gespeichert.'
|
|
||||||
redirect_to [:admin, @user]
|
|
||||||
else
|
|
||||||
render :action => 'edit'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
user = User.find(params[:id])
|
|
||||||
if user.nick == @current_user.nick
|
|
||||||
# deny destroying logged-in-user
|
|
||||||
flash[:error] = 'Du darfst Dich nicht selbst löschen.'
|
|
||||||
else
|
|
||||||
user.destroy
|
|
||||||
flash[:notice] = 'Benutzer_in wurde gelöscht.'
|
|
||||||
end
|
|
||||||
redirect_to admin_users_path
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,68 +1,20 @@
|
||||||
|
# encoding: utf-8
|
||||||
class Admin::WorkgroupsController < Admin::BaseController
|
class Admin::WorkgroupsController < Admin::BaseController
|
||||||
|
inherit_resources
|
||||||
|
|
||||||
def index
|
def index
|
||||||
if (params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 100)
|
@workgroups = Workgroup.order('name ASC')
|
||||||
@per_page = params[:per_page].to_i
|
# if somebody uses the search field:
|
||||||
else
|
@workgroups = @workgroups.where('name LIKE ?', "%#{params[:query]}%") unless params[:query].blank?
|
||||||
@per_page = 20
|
|
||||||
end
|
|
||||||
|
|
||||||
# if the search field is used
|
@workgroups = @workgroups.page(params[:page]).per(@per_page)
|
||||||
conditions = "name LIKE '%#{params[:query]}%'" unless params[:query].nil?
|
|
||||||
|
|
||||||
@total = Ordergroup.count(:conditions => conditions )
|
|
||||||
@workgroups = Workgroup.paginate(:conditions => conditions, :page => params[:page],
|
|
||||||
:per_page => @per_page, :order => 'name')
|
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.html # index.html.erb
|
|
||||||
format.js { render :partial => "workgroups" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def show
|
|
||||||
@workgroup = Workgroup.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def new
|
|
||||||
@workgroup = Workgroup.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
@workgroup = Workgroup.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
@workgroup = Workgroup.new(params[:workgroup])
|
|
||||||
|
|
||||||
if @workgroup.save
|
|
||||||
flash[:notice] = 'Workgroup was successfully created.'
|
|
||||||
redirect_to([:admin, @workgroup])
|
|
||||||
else
|
|
||||||
render :action => "new"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
@workgroup = Workgroup.find(params[:id])
|
|
||||||
|
|
||||||
if @workgroup.update_attributes(params[:workgroup])
|
|
||||||
flash[:notice] = 'Workgroup was successfully updated.'
|
|
||||||
redirect_to([:admin, @workgroup])
|
|
||||||
else
|
|
||||||
render :action => "edit"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@workgroup = Workgroup.find(params[:id])
|
@workgroup = Workgroup.find(params[:id])
|
||||||
@workgroup.destroy
|
@workgroup.destroy
|
||||||
|
redirect_to admin_workgroups_url, notice: t('admin.ordergroups.destroy.notice')
|
||||||
redirect_to(admin_workgroups_url)
|
rescue => error
|
||||||
end
|
redirect_to admin_workgroups_url, alert: t('admin.ordergroups.destroy.error')
|
||||||
|
|
||||||
def memberships
|
|
||||||
@group = Workgroup.find(params[:id])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,84 +1,54 @@
|
||||||
|
# encoding: utf-8
|
||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
|
|
||||||
filter_parameter_logging :password, :password_confirmation # do not log passwort parameters
|
protect_from_forgery
|
||||||
before_filter :select_foodcoop, :authenticate, :store_controller
|
before_filter :select_language, :select_foodcoop, :authenticate, :store_controller, :items_per_page, :set_redirect_to
|
||||||
after_filter :remove_controller
|
after_filter :remove_controller
|
||||||
|
|
||||||
# sends a mail, when an error occurs
|
|
||||||
# see plugins/exception_notification
|
|
||||||
include ExceptionNotifiable
|
|
||||||
|
|
||||||
# Returns the controller handling the current request.
|
# Returns the controller handling the current request.
|
||||||
def self.current
|
def self.current
|
||||||
Thread.current[:application_controller]
|
Thread.current[:application_controller]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Use this method to call a rake task,,
|
|
||||||
# e.g. to deliver mails after there are created.
|
|
||||||
def call_rake(task, options = {})
|
|
||||||
options[:rails_env] ||= Foodsoft.env
|
|
||||||
args = options.map { |n, v| "#{n.to_s.upcase}='#{v}'" }
|
|
||||||
system "/usr/bin/rake #{task} #{args.join(' ')} --trace 2>&1 >> #{Rails.root}/log/rake.log &"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def current_user
|
def current_user
|
||||||
begin
|
|
||||||
# check if there is a valid session and return the logged-in user (its object)
|
# check if there is a valid session and return the logged-in user (its object)
|
||||||
if session[:user] and session[:foodcoop]
|
if session[:user_id] and params[:foodcoop]
|
||||||
# for shared-host installations. check if the cookie-subdomain fits to request.
|
# for shared-host installations. check if the cookie-subdomain fits to request.
|
||||||
return User.current_user = User.find(session[:user]) if session[:foodcoop] == Foodsoft.env
|
@current_user ||= User.find_by_id(session[:user_id]) if session[:scope] == FoodsoftConfig.scope
|
||||||
end
|
|
||||||
rescue
|
|
||||||
reset_session
|
|
||||||
flash[:error]= _("An error has occurred. Please login again.")
|
|
||||||
redirect_to :controller => 'login'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
helper_method :current_user
|
||||||
def current_user=(user)
|
|
||||||
session[:user], session[:foodcoop] = user.id, Foodsoft.env
|
|
||||||
end
|
|
||||||
|
|
||||||
def return_to
|
|
||||||
session['return_to']
|
|
||||||
end
|
|
||||||
|
|
||||||
def return_to=(uri)
|
|
||||||
session['return_to'] = uri
|
|
||||||
end
|
|
||||||
|
|
||||||
def deny_access
|
def deny_access
|
||||||
self.return_to = request.request_uri
|
session[:return_to] = request.original_url
|
||||||
redirect_to :controller => '/login', :action => 'denied'
|
redirect_to login_url, :alert => 'Access denied!'
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def authenticate(role = 'any')
|
def authenticate(role = 'any')
|
||||||
# Attempt to retrieve authenticated user from controller instance or session...
|
# Attempt to retrieve authenticated user from controller instance or session...
|
||||||
if !(user = current_user)
|
if !current_user
|
||||||
# No user at all: redirect to login page.
|
# No user at all: redirect to login page.
|
||||||
self.return_to = request.request_uri
|
session[:user_id] = nil
|
||||||
redirect_to :controller => '/login'
|
session[:return_to] = request.original_url
|
||||||
return false
|
redirect_to login_url, :alert => 'Authentication required!'
|
||||||
else
|
else
|
||||||
# We have an authenticated user, now check role...
|
# We have an authenticated user, now check role...
|
||||||
# Roles gets the user through his memberships.
|
# Roles gets the user through his memberships.
|
||||||
hasRole = case role
|
hasRole = case role
|
||||||
when "admin" then user.role_admin?
|
when "admin" then current_user.role_admin?
|
||||||
when "finance" then user.role_finance?
|
when "finance" then current_user.role_finance?
|
||||||
when "article_meta" then user.role_article_meta?
|
when "article_meta" then current_user.role_article_meta?
|
||||||
when "suppliers" then user.role_suppliers?
|
when "suppliers" then current_user.role_suppliers?
|
||||||
when "orders" then user.role_orders?
|
when "orders" then current_user.role_orders?
|
||||||
when "any" then true # no role required
|
when "any" then true # no role required
|
||||||
else false # any unknown role will always fail
|
else false # any unknown role will always fail
|
||||||
end
|
end
|
||||||
if hasRole
|
if hasRole
|
||||||
@current_user = user
|
current_user
|
||||||
else
|
else
|
||||||
deny_access
|
deny_access
|
||||||
end
|
end
|
||||||
|
@ -110,12 +80,7 @@ class ApplicationController < ActionController::Base
|
||||||
def authenticate_membership_or_admin
|
def authenticate_membership_or_admin
|
||||||
@group = Group.find(params[:id])
|
@group = Group.find(params[:id])
|
||||||
unless @group.member?(@current_user) or @current_user.role_admin?
|
unless @group.member?(@current_user) or @current_user.role_admin?
|
||||||
flash[:error] = "Diese Aktion ist nur für Mitglieder der Gruppe erlaubt!"
|
redirect_to root_path, alert: "Diese Aktion ist nur für Mitglieder der Gruppe erlaubt!"
|
||||||
if request.xml_http_request?
|
|
||||||
render(:update) {|page| page.redirect_to root_path }
|
|
||||||
else
|
|
||||||
redirect_to root_path
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -138,23 +103,47 @@ class ApplicationController < ActionController::Base
|
||||||
# It uses the subdomain to select the appropriate section in the config files
|
# It uses the subdomain to select the appropriate section in the config files
|
||||||
# Use this method as a before filter (first filter!) in ApplicationController
|
# Use this method as a before filter (first filter!) in ApplicationController
|
||||||
def select_foodcoop
|
def select_foodcoop
|
||||||
if Foodsoft.config[:multi_coop_install]
|
if FoodsoftConfig[:multi_coop_install]
|
||||||
if !params[:foodcoop].blank?
|
if params[:foodcoop].present?
|
||||||
begin
|
begin
|
||||||
# Set Config
|
# Set Config and database connection
|
||||||
Foodsoft.env = params[:foodcoop]
|
FoodsoftConfig.select_foodcoop params[:foodcoop]
|
||||||
# Set database-connection
|
|
||||||
ActiveRecord::Base.establish_connection(Foodsoft.database)
|
|
||||||
rescue => error
|
rescue => error
|
||||||
flash[:error] = error.to_s
|
redirect_to root_url, alert: error.message
|
||||||
redirect_to root_path
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
redirect_to root_path
|
redirect_to root_url
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def items_per_page
|
||||||
|
if params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 100
|
||||||
|
@per_page = params[:per_page].to_i
|
||||||
else
|
else
|
||||||
# Deactivate routing filter
|
@per_page = 20
|
||||||
RoutingFilter::Foodcoop.active = false
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_redirect_to
|
||||||
|
session[:redirect_to] = params[:redirect_to] if params[:redirect_to]
|
||||||
|
end
|
||||||
|
|
||||||
|
def back_or_default_path(default = root_path)
|
||||||
|
if session[:redirect_to].present?
|
||||||
|
default = session[:redirect_to]
|
||||||
|
session[:redirect_to] = nil
|
||||||
|
end
|
||||||
|
default
|
||||||
|
end
|
||||||
|
|
||||||
|
# Always stay in foodcoop url scope
|
||||||
|
def default_url_options(options = {})
|
||||||
|
{foodcoop: FoodsoftConfig.scope}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Used to prevent accidently switching to :en in production mode.
|
||||||
|
def select_language
|
||||||
|
I18n.locale = :de
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,71 +1,27 @@
|
||||||
class ArticleCategoriesController < ApplicationController
|
class ArticleCategoriesController < ApplicationController
|
||||||
|
|
||||||
|
inherit_resources # Build default REST Actions via plugin
|
||||||
|
|
||||||
before_filter :authenticate_article_meta
|
before_filter :authenticate_article_meta
|
||||||
|
|
||||||
def index
|
|
||||||
@article_categories = ArticleCategory.all :order => 'name'
|
|
||||||
end
|
|
||||||
|
|
||||||
def new
|
|
||||||
@article_category = ArticleCategory.new
|
|
||||||
|
|
||||||
render :update do |page|
|
|
||||||
page['category_form'].replace_html :partial => 'article_categories/form'
|
|
||||||
page['category_form'].show
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
@article_category = ArticleCategory.find(params[:id])
|
|
||||||
|
|
||||||
render :update do |page|
|
|
||||||
page['category_form'].replace_html :partial => 'article_categories/form'
|
|
||||||
page['category_form'].show
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@article_category = ArticleCategory.new(params[:article_category])
|
create!(:notice => I18n.t('article_categories.create.notice')) { article_categories_path }
|
||||||
|
|
||||||
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/form'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@article_category = ArticleCategory.find(params[:id])
|
update!(:notice => I18n.t('article_categories.update.notice')) { article_categories_path }
|
||||||
|
|
||||||
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/form'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@article_category = ArticleCategory.find(params[:id])
|
destroy!
|
||||||
@article_category.destroy
|
rescue => error
|
||||||
|
redirect_to article_categories_path, alert: I18n.t('article_categories.destroy.error', message: error.message)
|
||||||
|
end
|
||||||
|
|
||||||
if @article_category.destroy
|
protected
|
||||||
render :update do |page|
|
|
||||||
page['category_'+@article_category.id.to_s].visual_effect :drop_out
|
def collection
|
||||||
end
|
@article_categories = ArticleCategory.order('name')
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
|
# encoding: utf-8
|
||||||
class ArticlesController < ApplicationController
|
class ArticlesController < ApplicationController
|
||||||
before_filter :authenticate_article_meta, :find_supplier
|
before_filter :authenticate_article_meta, :find_supplier
|
||||||
|
|
||||||
def index
|
def index
|
||||||
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']
|
if params['sort']
|
||||||
sort = case params['sort']
|
sort = case params['sort']
|
||||||
when "name" then "articles.name"
|
when "name" then "articles.name"
|
||||||
|
@ -25,68 +20,34 @@ class ArticlesController < ApplicationController
|
||||||
sort = "article_categories.name, articles.name"
|
sort = "article_categories.name, articles.name"
|
||||||
end
|
end
|
||||||
|
|
||||||
# if somebody uses the search field:
|
@articles = Article.undeleted.where(supplier_id: @supplier, :type => nil).includes(:article_category).order(sort)
|
||||||
conditions = ["articles.name LIKE ?", "%#{params[:query]}%"] unless params[:query].nil?
|
@articles = @articles.where('articles.name LIKE ?', "%#{params[:query]}%") unless params[:query].nil?
|
||||||
|
|
||||||
@total = @supplier.articles.without_deleted.count(:conditions => conditions)
|
@articles = @articles.page(params[:page]).per(@per_page)
|
||||||
@articles = @supplier.articles.without_deleted.paginate(
|
|
||||||
:order => sort,
|
|
||||||
:conditions => conditions,
|
|
||||||
:page => params[:page],
|
|
||||||
:per_page => @per_page,
|
|
||||||
:include => :article_category
|
|
||||||
)
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # list.haml
|
format.html
|
||||||
format.js do
|
format.js { render :layout => false }
|
||||||
render :update do |page|
|
|
||||||
page.replace_html 'table', :partial => "articles"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@article = @supplier.articles.build(:tax => 7.0)
|
@article = @supplier.articles.build(:tax => 7.0)
|
||||||
|
render :layout => false
|
||||||
render :update do |page|
|
|
||||||
page["edit_article"].replace_html :partial => 'new'
|
|
||||||
page["edit_article"].show
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@article = Article.new(params[:article])
|
@article = Article.new(params[:article])
|
||||||
if @article.valid? and @article.save
|
if @article.valid? and @article.save
|
||||||
render :update do |page|
|
render :layout => false
|
||||||
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
|
else
|
||||||
page[@article.id.to_s].addClassName 'just_updated'
|
render :action => 'new', :layout => false
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
render :update do |page|
|
|
||||||
page.replace_html 'edit_article', :partial => "new"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# edit an article and its price
|
|
||||||
def edit
|
def edit
|
||||||
@article = Article.find(params[:id])
|
@article = Article.find(params[:id])
|
||||||
|
render :action => 'new', :layout => false
|
||||||
render :update do |page|
|
|
||||||
page["edit_article"].replace_html :partial => 'edit'
|
|
||||||
page["edit_article"].show
|
|
||||||
end
|
|
||||||
#render :partial => "quick_edit", :layout => false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Updates one Article and highlights the line if succeded
|
# Updates one Article and highlights the line if succeded
|
||||||
|
@ -94,112 +55,79 @@ class ArticlesController < ApplicationController
|
||||||
@article = Article.find(params[:id])
|
@article = Article.find(params[:id])
|
||||||
|
|
||||||
if @article.update_attributes(params[:article])
|
if @article.update_attributes(params[:article])
|
||||||
render :update do |page|
|
render :layout => false
|
||||||
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
|
else
|
||||||
page[@article.id.to_s].removeClassName 'unavailable'
|
render :action => 'new', :layout => false
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
# Deletes article from database. send error msg, if article is used in a current order
|
# Deletes article from database. send error msg, if article is used in a current order
|
||||||
def destroy
|
def destroy
|
||||||
@article = Article.find(params[:id])
|
@article = Article.find(params[:id])
|
||||||
|
@article.mark_as_deleted unless @order = @article.in_open_order # If article is in an active Order, the Order will be returned
|
||||||
@order = @article.in_open_order # If article is in an active Order, the Order will be returned
|
render :layout => false
|
||||||
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
|
end
|
||||||
|
|
||||||
# Renders a form for editing all articles from a supplier
|
# Renders a form for editing all articles from a supplier
|
||||||
def edit_all
|
def edit_all
|
||||||
@articles = @supplier.articles.without_deleted
|
@articles = @supplier.articles.undeleted
|
||||||
end
|
end
|
||||||
|
|
||||||
# Updates all article of specific supplier
|
# Updates all article of specific supplier
|
||||||
# deletes all articles from params[outlisted_articles]
|
|
||||||
def update_all
|
def update_all
|
||||||
currentArticle = nil # used to find out which article caused a validation exception
|
invalid_articles = false
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Article.transaction do
|
Article.transaction do
|
||||||
unless params[:articles].blank?
|
unless params[:articles].blank?
|
||||||
# Update other article attributes...
|
# Update other article attributes...
|
||||||
for id, attributes in params[:articles]
|
@articles = Article.find(params[:articles].keys)
|
||||||
currentArticle = Article.find(id)
|
@articles.each do |article|
|
||||||
currentArticle.update_attributes!(attributes)
|
unless article.update_attributes(params[:articles][article.id.to_s])
|
||||||
|
invalid_articles = true unless invalid_articles # Remember that there are validation errors
|
||||||
end
|
end
|
||||||
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 supplier_articles_path(@supplier)
|
|
||||||
|
|
||||||
rescue => e
|
raise ActiveRecord::Rollback if invalid_articles # Rollback all changes
|
||||||
# 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(supplier_articles_path(@supplier)) : render(:action => 'edit_all')
|
|
||||||
else
|
|
||||||
flash[:error] = "Es trat ein Fehler beim Aktualisieren der Artikel auf: #{e.message}"
|
|
||||||
redirect_to supplier_articles_path(@supplier)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if invalid_articles
|
||||||
|
# An error has occurred, transaction has been rolled back.
|
||||||
|
flash.now.alert = I18n.t('articles.controller.error_invalid')
|
||||||
|
render :edit_all
|
||||||
|
else
|
||||||
|
# Successfully done.
|
||||||
|
redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.update_all.notice')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# makes different actions on selected articles
|
# makes different actions on selected articles
|
||||||
def update_selected
|
def update_selected
|
||||||
raise 'Du hast keine Artikel ausgewählt' if params[:selected_articles].nil?
|
raise I18n.t('articles.controller.error_nosel') if params[:selected_articles].nil?
|
||||||
articles = Article.find(params[:selected_articles])
|
articles = Article.find(params[:selected_articles])
|
||||||
|
Article.transaction do
|
||||||
case params[:selected_action]
|
case params[:selected_action]
|
||||||
when 'destroy'
|
when 'destroy'
|
||||||
articles.each {|a| a.destroy }
|
articles.each(&:mark_as_deleted)
|
||||||
flash[:notice] = 'Alle gewählten Artikel wurden gelöscht'
|
flash[:notice] = I18n.t('articles.controller.update_sel.notice_destroy')
|
||||||
when 'setNotAvailable'
|
when 'setNotAvailable'
|
||||||
articles.each {|a| a.update_attribute(:availability, false) }
|
articles.each {|a| a.update_attribute(:availability, false) }
|
||||||
flash[:notice] = 'Alle gewählten Artikel wurden auf "nicht verfügbar" gesetzt'
|
flash[:notice] = I18n.t('articles.controller.update_sel.notice_unavail')
|
||||||
when 'setAvailable'
|
when 'setAvailable'
|
||||||
articles.each {|a| a.update_attribute(:availability, true) }
|
articles.each {|a| a.update_attribute(:availability, true) }
|
||||||
flash[:notice] = 'Alle gewählten Artikel wurden auf "verfügbar" gesetzt'
|
flash[:notice] = I18n.t('articles.controller.update_sel.notice_avail')
|
||||||
else
|
else
|
||||||
flash[:error] = 'Keine Aktion ausgewählt!'
|
flash[:alert] = I18n.t('articles.controller.update_sel.notice_noaction')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
# action succeded
|
# action succeded
|
||||||
redirect_to supplier_articles_path(@supplier, :per_page => params[:per_page])
|
redirect_to supplier_articles_url(@supplier, :per_page => params[:per_page])
|
||||||
|
|
||||||
rescue => e
|
rescue => error
|
||||||
flash[:error] = 'Ein Fehler ist aufgetreten: ' + e
|
redirect_to supplier_articles_url(@supplier, :per_page => params[:per_page]),
|
||||||
redirect_to supplier_articles_path(@supplier, :per_page => params[:per_page])
|
:alert => I18n.t('errors.general_msg', :msg => error)
|
||||||
end
|
end
|
||||||
|
|
||||||
# lets start with parsing articles from uploaded file, yeah
|
# lets start with parsing articles from uploaded file, yeah
|
||||||
|
@ -232,14 +160,13 @@ class ArticlesController < ApplicationController
|
||||||
:tax => row[:tax])
|
:tax => row[:tax])
|
||||||
# stop parsing, when an article isn't valid
|
# stop parsing, when an article isn't valid
|
||||||
unless article.valid?
|
unless article.valid?
|
||||||
raise article.errors.full_messages.join(", ") + " ..in line " + (articles.index(row) + 2).to_s
|
raise I18n.t('articles.controller.error_parse', :msg => article.errors.full_messages.join(", "), :line => (articles.index(row) + 2).to_s)
|
||||||
end
|
end
|
||||||
@articles << article
|
@articles << article
|
||||||
end
|
end
|
||||||
flash.now[:notice] = @articles.size.to_s + " articles are parsed successfully."
|
flash.now[:notice] = I18n.t('articles.controller.parse_upload.notice', :count => @articles.size)
|
||||||
rescue => e
|
rescue => error
|
||||||
flash[:error] = "An error has occurred: " + e.message
|
redirect_to upload_supplier_articles_path(@supplier), :alert => I18n.t('errors.general_msg', :msg => error.message)
|
||||||
redirect_to upload_supplier_articles_path(@supplier)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -247,58 +174,40 @@ class ArticlesController < ApplicationController
|
||||||
def create_from_upload
|
def create_from_upload
|
||||||
begin
|
begin
|
||||||
Article.transaction do
|
Article.transaction do
|
||||||
for article_attributes in params[:articles]
|
invalid_articles = false
|
||||||
@supplier.articles.create!(article_attributes)
|
@articles = []
|
||||||
|
params[:articles].each do |_key, article_attributes|
|
||||||
|
@articles << (article = @supplier.articles.build(article_attributes))
|
||||||
|
invalid_articles = true unless article.save
|
||||||
end
|
end
|
||||||
|
|
||||||
|
raise I18n.t('articles.controller.error_invalid') if invalid_articles
|
||||||
end
|
end
|
||||||
# Successfully done.
|
# Successfully done.
|
||||||
flash[:notice] = "The articles are saved successfully"
|
redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.create_from_upload.notice', :count => @articles.size)
|
||||||
redirect_to supplier_articles_path(@supplier)
|
|
||||||
rescue => error
|
rescue => error
|
||||||
# An error has occurred, transaction has been rolled back.
|
# An error has occurred, transaction has been rolled back.
|
||||||
flash[:error] = "An error occured: #{error.message}"
|
flash.now[:error] = I18n.t('errors.general_msg', :msg => error.message)
|
||||||
redirect_to upload_supplier_articles_path(@supplier)
|
render :parse_upload
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# renders a view to import articles in local database
|
# renders a view to import articles in local database
|
||||||
#
|
#
|
||||||
def shared
|
def shared
|
||||||
conditions = []
|
# build array of keywords, required for meta search _all suffix
|
||||||
conditions << "supplier_id = #{@supplier.shared_supplier.id}"
|
params[:search][:name_contains_all] = params[:search][:name_contains_all].split(' ') if params[:search]
|
||||||
# check for keywords
|
# Build search with meta search plugin
|
||||||
conditions << params[:import_query].split(' ').collect { |keyword| "name LIKE '%#{keyword}%'" }.join(' AND ') unless params[:import_query].blank?
|
@search = @supplier.shared_supplier.shared_articles.search(params[:search])
|
||||||
# check for regional articles
|
@articles = @search.page(params[:page]).per(10)
|
||||||
conditions << "origin = 'REG'" if params[:regional]
|
render :layout => false
|
||||||
|
|
||||||
@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
|
end
|
||||||
|
|
||||||
# fills a form whith values of the selected shared_article
|
# fills a form whith values of the selected shared_article
|
||||||
def import
|
def import
|
||||||
shared_article = SharedArticle.find(params[:shared_article_id])
|
@article = SharedArticle.find(params[:shared_article_id]).build_new_article
|
||||||
@article = Article.new(
|
render :action => 'new', :layout => false
|
||||||
: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,
|
|
||||||
: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
|
end
|
||||||
|
|
||||||
# sync all articles with the external database
|
# sync all articles with the external database
|
||||||
|
@ -306,16 +215,43 @@ class ArticlesController < ApplicationController
|
||||||
def sync
|
def sync
|
||||||
# check if there is an shared_supplier
|
# check if there is an shared_supplier
|
||||||
unless @supplier.shared_supplier
|
unless @supplier.shared_supplier
|
||||||
flash[:error]= "#{@supplier.name} ist nicht mit einer externen Datenbank verknüpft."
|
redirect_to supplier_articles_url(@supplier), :alert => I18n.t('articles.controller.sync.shared_alert', :supplier => @supplier.name)
|
||||||
redirect_to supplier_articles_path(@supplier)
|
|
||||||
end
|
end
|
||||||
# sync articles against external database
|
# sync articles against external database
|
||||||
@updated_articles, @outlisted_articles = @supplier.sync_all
|
@updated_articles, @outlisted_articles = @supplier.sync_all
|
||||||
# convert to db-compatible-string
|
# convert to db-compatible-string
|
||||||
@updated_articles.each {|a, b| a.shared_updated_on = a.shared_updated_on.to_formatted_s(:db)}
|
@updated_articles.each {|a, b| a.shared_updated_on = a.shared_updated_on.to_formatted_s(:db)}
|
||||||
if @updated_articles.empty? && @outlisted_articles.empty?
|
if @updated_articles.empty? && @outlisted_articles.empty?
|
||||||
flash[:notice] = "Der Katalog ist aktuell."
|
redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.sync.notice')
|
||||||
redirect_to supplier_articles_path(@supplier)
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Updates, deletes articles when sync form is submitted
|
||||||
|
def update_synchronized
|
||||||
|
begin
|
||||||
|
Article.transaction do
|
||||||
|
# delete articles
|
||||||
|
if params[:outlisted_articles]
|
||||||
|
Article.find(params[:outlisted_articles].keys).each(&:mark_as_deleted)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update articles
|
||||||
|
params[:articles].each do |id, attrs|
|
||||||
|
Article.find(id).update_attributes! attrs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Successfully done.
|
||||||
|
redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.update_sync.notice')
|
||||||
|
|
||||||
|
rescue ActiveRecord::RecordInvalid => invalid
|
||||||
|
# An error has occurred, transaction has been rolled back.
|
||||||
|
redirect_to supplier_articles_path(@supplier),
|
||||||
|
alert: I18n.t('articles.controller.error_update', :article => invalid.record.name, :msg => invalid.record.errors.full_messages)
|
||||||
|
|
||||||
|
rescue => error
|
||||||
|
redirect_to supplier_articles_path(@supplier),
|
||||||
|
alert: I18n.t('errors.general_msg', :msg => error.message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,3 +1,4 @@
|
||||||
|
# encoding: utf-8
|
||||||
class DeliveriesController < ApplicationController
|
class DeliveriesController < ApplicationController
|
||||||
|
|
||||||
before_filter :find_supplier, :exclude => :fill_new_stock_article_form
|
before_filter :find_supplier, :exclude => :fill_new_stock_article_form
|
||||||
|
@ -34,7 +35,7 @@ class DeliveriesController < ApplicationController
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @delivery.save
|
if @delivery.save
|
||||||
flash[:notice] = 'Lieferung wurde erstellt. Bitte nicht vergessen die Rechnung anzulegen!'
|
flash[:notice] = I18n.t('deliveries.create.notice')
|
||||||
format.html { redirect_to([@supplier,@delivery]) }
|
format.html { redirect_to([@supplier,@delivery]) }
|
||||||
format.xml { render :xml => @delivery, :status => :created, :location => @delivery }
|
format.xml { render :xml => @delivery, :status => :created, :location => @delivery }
|
||||||
else
|
else
|
||||||
|
@ -53,7 +54,7 @@ class DeliveriesController < ApplicationController
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @delivery.update_attributes(params[:delivery])
|
if @delivery.update_attributes(params[:delivery])
|
||||||
flash[:notice] = 'Lieferung wurde aktualisiert.'
|
flash[:notice] = I18n.t('deliveries.update.notice')
|
||||||
format.html { redirect_to([@supplier,@delivery]) }
|
format.html { redirect_to([@supplier,@delivery]) }
|
||||||
format.xml { head :ok }
|
format.xml { head :ok }
|
||||||
else
|
else
|
||||||
|
@ -67,7 +68,7 @@ class DeliveriesController < ApplicationController
|
||||||
@delivery = Delivery.find(params[:id])
|
@delivery = Delivery.find(params[:id])
|
||||||
@delivery.destroy
|
@delivery.destroy
|
||||||
|
|
||||||
flash[:notice] = "Lieferung wurde gelöscht."
|
flash[:notice] = I18n.t('deliveries.destroy.notice')
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to(supplier_deliveries_url(@supplier)) }
|
format.html { redirect_to(supplier_deliveries_url(@supplier)) }
|
||||||
format.xml { head :ok }
|
format.xml { head :ok }
|
||||||
|
@ -92,11 +93,7 @@ class DeliveriesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_stock_change
|
def add_stock_change
|
||||||
|
render :layout => false
|
||||||
render :update do |page|
|
|
||||||
page.insert_html :bottom, 'stock_changes', :partial => 'stock_change',
|
|
||||||
:locals => {:stock_change => StockChange.new, :supplier => @supplier}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fill_new_stock_article_form
|
def fill_new_stock_article_form
|
||||||
|
|
|
@ -1,19 +1,14 @@
|
||||||
class FeedbackController < ApplicationController
|
class FeedbackController < ApplicationController
|
||||||
|
|
||||||
def new
|
def new
|
||||||
render :update do |page|
|
|
||||||
page.replace_html :ajax_box, :partial => "new"
|
|
||||||
page.show :ajax_box
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
unless params[:message].blank?
|
if params[:message].present?
|
||||||
Mailer.deliver_feedback(current_user, params[:message])
|
Mailer.feedback(current_user, params[:message]).deliver
|
||||||
end
|
redirect_to root_url, notice: t('feedback.create.notice')
|
||||||
|
else
|
||||||
render :update do |page|
|
render :action => 'new'
|
||||||
page.replace_html :ajax_box, :partial => "success"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,283 +1,50 @@
|
||||||
class Finance::BalancingController < ApplicationController
|
# encoding: utf-8
|
||||||
before_filter :authenticate_finance
|
class Finance::BalancingController < Finance::BaseController
|
||||||
verify :method => :post, :only => [:close, :close_direct]
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@financial_transactions = FinancialTransaction.find(:all, :order => "created_on DESC", :limit => 8)
|
@orders = Order.finished.page(params[:page]).per(@per_page).order('ends DESC')
|
||||||
@orders = Order.finished_not_closed
|
|
||||||
@unpaid_invoices = Invoice.unpaid
|
|
||||||
end
|
|
||||||
|
|
||||||
def list
|
|
||||||
@orders = Order.finished.paginate :page => params[:page], :per_page => 10, :order => 'ends DESC'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@order = Order.find(params[:id])
|
@order = Order.find(params[:order_id])
|
||||||
|
flash.now.alert = t('finance.balancing.new.alert') if @order.closed?
|
||||||
@comments = @order.comments
|
@comments = @order.comments
|
||||||
|
|
||||||
if params['sort']
|
@articles = @order.order_articles.ordered.includes(:article, :article_price,
|
||||||
sort = case params['sort']
|
group_order_articles: {group_order: :ordergroup})
|
||||||
when "name" then "articles.name"
|
|
||||||
when "order_number" then "articles.order_number"
|
sort_param = params['sort'] || 'name'
|
||||||
when "name_reverse" then "articles.name DESC"
|
@articles = case sort_param
|
||||||
when "order_number_reverse" then "articles.order_number DESC"
|
when 'name' then
|
||||||
end
|
OrderArticle.sort_by_name(@articles)
|
||||||
|
when 'name_reverse' then
|
||||||
|
OrderArticle.sort_by_name(@articles).reverse
|
||||||
|
when 'order_number' then
|
||||||
|
OrderArticle.sort_by_order_number(@articles)
|
||||||
|
when 'order_number_reverse' then
|
||||||
|
OrderArticle.sort_by_order_number(@articles).reverse
|
||||||
else
|
else
|
||||||
sort = "id"
|
@articles
|
||||||
end
|
end
|
||||||
|
|
||||||
@articles = @order.order_articles.ordered.find(
|
render layout: false if request.xhr?
|
||||||
:all,
|
|
||||||
:include => :article,
|
|
||||||
:order => sort
|
|
||||||
)
|
|
||||||
|
|
||||||
if params[:sort] == "order_number"
|
|
||||||
@articles = @articles.sort { |a,b| a.article.order_number.gsub(/[^[:digit:]]/, "").to_i <=> b.article.order_number.gsub(/[^[:digit:]]/, "").to_i }
|
|
||||||
elsif params[:sort] == "order_number_reverse"
|
|
||||||
@articles = @articles.sort { |a,b| b.article.order_number.gsub(/[^[:digit:]]/, "").to_i <=> a.article.order_number.gsub(/[^[:digit:]]/, "").to_i }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
view = params[:view]
|
def update_summary
|
||||||
params[:view] = nil
|
@order = Order.find(params[:id])
|
||||||
|
|
||||||
case view
|
|
||||||
when 'editResults'
|
|
||||||
render :partial => 'edit_results_by_articles' and return
|
|
||||||
when 'groupsOverview'
|
|
||||||
render :partial => 'shared/articles_by_groups', :locals => {:order => @order} and return
|
|
||||||
when 'articlesOverview'
|
|
||||||
render :partial => 'shared/articles_by_articles', :locals => {:order => @order} and return
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit_note
|
def edit_note
|
||||||
@order = Order.find(params[:id])
|
@order = Order.find(params[:id])
|
||||||
render :partial => 'edit_note'
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_note
|
def update_note
|
||||||
@order = Order.find(params[:id])
|
@order = Order.find(params[:id])
|
||||||
render :update do |page|
|
|
||||||
if @order.update_attributes(params[:order])
|
if @order.update_attributes(params[:order])
|
||||||
page["note"].replace_html simple_format(@order.note)
|
render :layout => false
|
||||||
page["edit_box"].hide
|
|
||||||
else
|
else
|
||||||
page["results"].replace_html :partial => "edit_note"
|
render :action => :edit_note, :layout => false
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def new_order_article
|
|
||||||
@order = Order.find(params[:id])
|
|
||||||
render :update do |page|
|
|
||||||
page["edit_box"].replace_html :partial => "new_order_article"
|
|
||||||
page["edit_box"].show
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def auto_complete_for_article_name
|
|
||||||
order = Order.find(params[:order_id])
|
|
||||||
find_params = {
|
|
||||||
:conditions => ["LOWER(articles.name) LIKE ?", "%#{params[:article][:name].downcase}%" ],
|
|
||||||
:order => 'articles.name ASC',
|
|
||||||
:limit => 8
|
|
||||||
}
|
|
||||||
@articles = if order.stockit?
|
|
||||||
StockArticle.all find_params
|
|
||||||
else
|
|
||||||
order.supplier.articles.all find_params
|
|
||||||
end
|
|
||||||
|
|
||||||
render :partial => 'shared/auto_complete_articles'
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_order_article
|
|
||||||
@order = Order.find(params[:order_id])
|
|
||||||
article = Article.find(params[:order_article][:article_id])
|
|
||||||
order_article = @order.order_articles.find_by_article_id(article.id)
|
|
||||||
|
|
||||||
unless order_article
|
|
||||||
# Article wasn't already assigned with this order, lets create a new one
|
|
||||||
order_article = @order.order_articles.build(params[:order_article])
|
|
||||||
order_article.article_price = order_article.article.article_prices.first
|
|
||||||
end
|
|
||||||
# Set units to order to 1, so the article is visible on page
|
|
||||||
order_article.units_to_order = 1
|
|
||||||
|
|
||||||
render :update do |page|
|
|
||||||
if order_article.save
|
|
||||||
page["edit_box"].hide
|
|
||||||
page.insert_html :top, "result_table", :partial => "order_article_result", :locals => {:order_article => order_article}
|
|
||||||
page["order_article_#{order_article.id}"].visual_effect :highlight, :duration => 2
|
|
||||||
page["group_order_articles_#{order_article.id}"].show
|
|
||||||
else
|
|
||||||
page["edit_box"].replace_html :partial => "new_order_article"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
rescue
|
|
||||||
render :update do |page|
|
|
||||||
page.replace_html "edit_box", :text => "<b>Keinen Artikel gefunden. Bitte erneut versuchen.</b>"
|
|
||||||
page.insert_html :bottom, "edit_box", :partial => "new_order_article"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit_order_article
|
|
||||||
@order_article = OrderArticle.find(params[:id])
|
|
||||||
render :update do |page|
|
|
||||||
page["edit_box"].replace_html :partial => 'edit_order_article'
|
|
||||||
page["edit_box"].show
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update this article and creates a new articleprice if neccessary
|
|
||||||
def update_order_article
|
|
||||||
@order_article = OrderArticle.find(params[:id])
|
|
||||||
begin
|
|
||||||
@order_article.update_article_and_price!(params[:article], params[:price], params[:order_article], params[:price_global])
|
|
||||||
render :update do |page|
|
|
||||||
page["edit_box"].hide
|
|
||||||
page["summary"].replace_html :partial => 'summary', :locals => {:order => @order_article.order}
|
|
||||||
page["summary"].visual_effect :highlight, :duration => 2
|
|
||||||
page["order_article_#{@order_article.id}"].replace_html :partial => 'order_article', :locals => {:order_article => @order_article}
|
|
||||||
page["order_article_#{@order_article.id}"].visual_effect :highlight, :delay => 0.5, :duration => 2
|
|
||||||
page["group_order_articles_#{@order_article.id}"].replace_html :partial => "group_order_articles", :locals => {:order_article => @order_article}
|
|
||||||
end
|
|
||||||
rescue => @error
|
|
||||||
render :update do |page|
|
|
||||||
page['edit_box'].replace_html :partial => 'edit_order_article'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy_order_article
|
|
||||||
order_article = OrderArticle.find(params[:id])
|
|
||||||
order_article.destroy
|
|
||||||
# Updates ordergroup values
|
|
||||||
order_article.group_order_articles.each { |goa| goa.group_order.update_price! }
|
|
||||||
|
|
||||||
render :update do |page|
|
|
||||||
page["order_article_#{order_article.id}"].remove
|
|
||||||
page["group_order_articles_#{order_article.id}"].remove
|
|
||||||
page["summary"].replace_html :partial => 'summary', :locals => {:order => order_article.order}
|
|
||||||
page["summary"].visual_effect :highlight, :duration => 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def new_group_order_article
|
|
||||||
goa = OrderArticle.find(params[:id]).group_order_articles.build
|
|
||||||
render :update do |page|
|
|
||||||
page["edit_box"].replace_html :partial => "new_group_order_article",
|
|
||||||
:locals => {:group_order_article => goa}
|
|
||||||
page["edit_box"].show
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a new GroupOrderArticle
|
|
||||||
# If the the chosen Ordergroup hasn't ordered yet, a GroupOrder will also be created
|
|
||||||
#FIXME: Clean up this messy code !
|
|
||||||
def create_group_order_article
|
|
||||||
goa = GroupOrderArticle.new(params[:group_order_article])
|
|
||||||
order_article = goa.order_article
|
|
||||||
order = order_article.order
|
|
||||||
|
|
||||||
# creates a new GroupOrder if necessary
|
|
||||||
group_order = GroupOrder.first :conditions => {:order_id => order.id, :ordergroup_id => goa.ordergroup_id}
|
|
||||||
unless group_order
|
|
||||||
goa.create_group_order(:order_id => order.id, :ordergroup_id => goa.ordergroup_id)
|
|
||||||
else
|
|
||||||
goa.group_order = group_order
|
|
||||||
end
|
|
||||||
|
|
||||||
# If there is an GroupOrderArticle already, only update result attribute.
|
|
||||||
if group_order_article = GroupOrderArticle.first(:conditions => {:group_order_id => goa.group_order, :order_article_id => goa.order_article})
|
|
||||||
goa = group_order_article
|
|
||||||
goa.result = params[:group_order_article]["result"]
|
|
||||||
end
|
|
||||||
|
|
||||||
render :update do |page|
|
|
||||||
if goa.save
|
|
||||||
goa.group_order.update_price! # Update the price attribute of new GroupOrder
|
|
||||||
order_article.update_results! if order_article.article.is_a?(StockArticle) # Update units_to_order of order_article
|
|
||||||
page["edit_box"].hide
|
|
||||||
page["order_article_#{order_article.id}"].replace_html :partial => 'order_article', :locals => {:order_article => order_article}
|
|
||||||
|
|
||||||
page["group_order_articles_#{order_article.id}"].replace_html :partial => 'group_order_articles',
|
|
||||||
:locals => {:order_article => order_article}
|
|
||||||
page["group_order_article_#{goa.id}"].visual_effect :highlight, :duration => 2
|
|
||||||
|
|
||||||
page["summary"].replace_html :partial => 'summary', :locals => {:order => order}
|
|
||||||
page["order_profit"].visual_effect :highlight, :duration => 2
|
|
||||||
else
|
|
||||||
page["edit_box"].replace_html :partial => "new_group_order_article",
|
|
||||||
:locals => {:group_order_article => goa}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit_group_order_article
|
|
||||||
group_order_article = GroupOrderArticle.find(params[:id])
|
|
||||||
render :partial => 'edit_group_order_article',
|
|
||||||
:locals => {:group_order_article => group_order_article}
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_group_order_article
|
|
||||||
goa = GroupOrderArticle.find(params[:id])
|
|
||||||
|
|
||||||
render :update do |page|
|
|
||||||
if goa.update_attributes(params[:group_order_article])
|
|
||||||
goa.group_order.update_price! # Update the price attribute of new GroupOrder
|
|
||||||
goa.order_article.update_results! if goa.order_article.article.is_a?(StockArticle) # Update units_to_order of order_article
|
|
||||||
|
|
||||||
page["edit_box"].hide
|
|
||||||
page["order_article_#{goa.order_article.id}"].replace_html :partial => 'order_article', :locals => {:order_article => goa.order_article}
|
|
||||||
page["group_order_articles_#{goa.order_article.id}"].replace_html :partial => 'group_order_articles',
|
|
||||||
:locals => {:order_article => goa.order_article}
|
|
||||||
page["summary"].replace_html :partial => 'summary', :locals => {:order => goa.order_article.order}
|
|
||||||
page["order_profit"].visual_effect :highlight, :duration => 2
|
|
||||||
else
|
|
||||||
page["edit_box"].replace_html :partial => 'edit_group_order_article'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_group_order_article_result
|
|
||||||
goa = GroupOrderArticle.find(params[:id])
|
|
||||||
|
|
||||||
if params[:modifier] == '-'
|
|
||||||
goa.update_attributes({:result => goa.result - 1})
|
|
||||||
elsif params[:modifier] == '+'
|
|
||||||
goa.update_attributes({:result => goa.result + 1})
|
|
||||||
end
|
|
||||||
|
|
||||||
render :update do |page|
|
|
||||||
goa.group_order.update_price! # Update the price attribute of new GroupOrder
|
|
||||||
goa.order_article.update_results! if goa.order_article.article.is_a?(StockArticle) # Update units_to_order of order_article
|
|
||||||
|
|
||||||
page["order_article_#{goa.order_article.id}"].replace_html :partial => 'order_article', :locals => {:order_article => goa.order_article}
|
|
||||||
page["group_order_articles_#{goa.order_article.id}"].replace_html :partial => 'group_order_articles',
|
|
||||||
:locals => {:order_article => goa.order_article}
|
|
||||||
page["summary"].replace_html :partial => 'summary', :locals => {:order => goa.order_article.order}
|
|
||||||
page["order_profit"].visual_effect :highlight, :duration => 2
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy_group_order_article
|
|
||||||
goa = GroupOrderArticle.find(params[:id])
|
|
||||||
goa.destroy
|
|
||||||
goa.group_order.update_price! # Updates the price attribute of new GroupOrder
|
|
||||||
goa.order_article.update_results! if goa.order_article.article.is_a?(StockArticle) # Update units_to_order of order_article
|
|
||||||
|
|
||||||
render :update do |page|
|
|
||||||
page["edit_box"].hide
|
|
||||||
page["order_article_#{goa.order_article.id}"].replace_html :partial => 'order_article', :locals => {:order_article => goa.order_article}
|
|
||||||
page["group_order_articles_#{goa.order_article.id}"].replace_html :partial => 'group_order_articles',
|
|
||||||
:locals => {:order_article => goa.order_article}
|
|
||||||
page["summary"].replace_html :partial => 'summary', :locals => {:order => goa.order_article.order}
|
|
||||||
page["order_profit"].visual_effect :highlight, :duration => 2
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -289,27 +56,20 @@ class Finance::BalancingController < ApplicationController
|
||||||
# Balances the Order, Update of the Ordergroup.account_balances
|
# Balances the Order, Update of the Ordergroup.account_balances
|
||||||
def close
|
def close
|
||||||
@order = Order.find(params[:id])
|
@order = Order.find(params[:id])
|
||||||
begin
|
|
||||||
@order.close!(@current_user)
|
@order.close!(@current_user)
|
||||||
flash[:notice] = "Bestellung wurde erfolgreich abgerechnet, die Kontostände aktualisiert."
|
redirect_to finance_order_index_url, notice: t('finance.balancing.close.notice')
|
||||||
redirect_to :action => "index"
|
|
||||||
rescue => e
|
rescue => error
|
||||||
flash[:error] = "Ein Fehler ist beim Abrechnen aufgetreten: " + e
|
redirect_to new_finance_order_url(order_id: @order.id), alert: t('finance.balancing.close.alert', message: error.message)
|
||||||
redirect_to :action => "new", :id => @order
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Close the order directly, without automaticly updating ordergroups account balances
|
# Close the order directly, without automaticly updating ordergroups account balances
|
||||||
def close_direct
|
def close_direct
|
||||||
@order = Order.find(params[:id])
|
@order = Order.find(params[:id])
|
||||||
if @order.finished?
|
@order.close_direct!(@current_user)
|
||||||
@order.update_attributes(:state => 'closed', :updated_by => @current_user)
|
redirect_to finance_order_index_url, notice: t('finance.balancing.close_direct.notice')
|
||||||
flash[:notice] = 'Die Bestellung wurde auf "gebucht" gesetzt.'
|
rescue => error
|
||||||
redirect_to :action => 'listOrders', :id => @order
|
redirect_to finance_order_index_url, alert: t('finance.balancing.close_direct.alert', message: error.message)
|
||||||
else
|
|
||||||
flash[:error] = 'Die Bestellung ist noch nicht beendet.'
|
|
||||||
redirect_to :action => 'listOrders', :id => @order
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
11
app/controllers/finance/base_controller.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class Finance::BaseController < ApplicationController
|
||||||
|
before_filter :authenticate_finance
|
||||||
|
|
||||||
|
def index
|
||||||
|
@financial_transactions = FinancialTransaction.order('created_on DESC').limit(8)
|
||||||
|
@orders = Order.finished_not_closed
|
||||||
|
@unpaid_invoices = Invoice.unpaid
|
||||||
|
|
||||||
|
render template: 'finance/index'
|
||||||
|
end
|
||||||
|
end
|
65
app/controllers/finance/financial_transactions_controller.rb
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
class Finance::FinancialTransactionsController < ApplicationController
|
||||||
|
before_filter :authenticate_finance
|
||||||
|
before_filter :find_ordergroup, :except => [:new_collection, :create_collection]
|
||||||
|
inherit_resources
|
||||||
|
# belongs_to :ordergroup
|
||||||
|
|
||||||
|
def index
|
||||||
|
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
|
||||||
|
|
||||||
|
@financial_transactions = @ordergroup.financial_transactions.includes(:user).order(sort).
|
||||||
|
page(params[:page]).per(@per_page)
|
||||||
|
if params[:query].present?
|
||||||
|
@financial_transactions = @financial_transactions.where('note LIKE ?', "%#{params[:query]}%")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@financial_transaction = @ordergroup.financial_transactions.build
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@financial_transaction = FinancialTransaction.new(params[:financial_transaction])
|
||||||
|
@financial_transaction.user = current_user
|
||||||
|
@financial_transaction.add_transaction!
|
||||||
|
redirect_to finance_ordergroup_transactions_url(@ordergroup), notice: t('finance.financial_transactions.create.notice')
|
||||||
|
rescue ActiveRecord::RecordInvalid => error
|
||||||
|
flash.now[:alert] = error.message
|
||||||
|
render :action => :new
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_collection
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_collection
|
||||||
|
raise "Notiz wird benötigt!" if params[:note].blank?
|
||||||
|
params[:financial_transactions].each do |trans|
|
||||||
|
# ignore empty amount fields ...
|
||||||
|
unless trans[:amount].blank?
|
||||||
|
Ordergroup.find(trans[:ordergroup_id]).add_financial_transaction!(trans[:amount], params[:note], @current_user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
redirect_to finance_ordergroups_url, notice: t('finance.create_collection.create.notice')
|
||||||
|
rescue => error
|
||||||
|
redirect_to finance_new_transaction_collection_url, alert: t('finance.create_collection.create.alert', error: error.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def find_ordergroup
|
||||||
|
@ordergroup = Ordergroup.find(params[:ordergroup_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
83
app/controllers/finance/group_order_articles_controller.rb
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
class Finance::GroupOrderArticlesController < ApplicationController
|
||||||
|
|
||||||
|
before_filter :authenticate_finance
|
||||||
|
|
||||||
|
layout false # We only use this controller to server js snippets, no need for layout rendering
|
||||||
|
|
||||||
|
def new
|
||||||
|
@order_article = OrderArticle.find(params[:order_article_id])
|
||||||
|
@group_order_article = GroupOrderArticle.new(order_article: @order_article)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@group_order_article = GroupOrderArticle.new(params[:group_order_article])
|
||||||
|
@order_article = @group_order_article.order_article
|
||||||
|
|
||||||
|
# As we hide group_order_articles with a result of 0, we should not complain, when an existing group_order_article is found
|
||||||
|
goa = GroupOrderArticle.where(group_order_id: @group_order_article.group_order_id,
|
||||||
|
order_article_id: @order_article.id).first
|
||||||
|
|
||||||
|
if goa and goa.update_attributes(params[:group_order_article])
|
||||||
|
@group_order_article = goa
|
||||||
|
|
||||||
|
update_summaries(@group_order_article)
|
||||||
|
render :update
|
||||||
|
|
||||||
|
elsif @group_order_article.save
|
||||||
|
update_summaries(@group_order_article)
|
||||||
|
render :update
|
||||||
|
|
||||||
|
else # Validation failed, show form
|
||||||
|
render :new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@group_order_article = GroupOrderArticle.find(params[:id])
|
||||||
|
@order_article = @group_order_article.order_article
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@group_order_article = GroupOrderArticle.find(params[:id])
|
||||||
|
@order_article = @group_order_article.order_article
|
||||||
|
|
||||||
|
if @group_order_article.update_attributes(params[:group_order_article])
|
||||||
|
update_summaries(@group_order_article)
|
||||||
|
else
|
||||||
|
render :edit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_result
|
||||||
|
group_order_article = GroupOrderArticle.find(params[:id])
|
||||||
|
@order_article = group_order_article.order_article
|
||||||
|
|
||||||
|
if params[:modifier] == '-'
|
||||||
|
group_order_article.update_attribute :result, group_order_article.result - 1
|
||||||
|
elsif params[:modifier] == '+'
|
||||||
|
group_order_article.update_attribute :result, group_order_article.result + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
update_summaries(group_order_article)
|
||||||
|
|
||||||
|
render :update
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
group_order_article = GroupOrderArticle.find(params[:id])
|
||||||
|
group_order_article.destroy
|
||||||
|
update_summaries(group_order_article)
|
||||||
|
@order_article = group_order_article.order_article
|
||||||
|
|
||||||
|
render :update
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def update_summaries(group_order_article)
|
||||||
|
# Update the price attribute of new GroupOrder
|
||||||
|
group_order_article.group_order.update_price!
|
||||||
|
# Update units_to_order of order_article
|
||||||
|
group_order_article.order_article.update_results! if group_order_article.order_article.article.is_a?(StockArticle)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,47 +1,31 @@
|
||||||
class Finance::InvoicesController < ApplicationController
|
class Finance::InvoicesController < ApplicationController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@invoices = Invoice.find(:all, :order => "date DESC")
|
@invoices = Invoice.includes(:supplier, :delivery, :order).order('date DESC').page(params[:page]).per(@per_page)
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.html # index.html.erb
|
|
||||||
format.xml { render :xml => @invoices }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@invoice = Invoice.find(params[:id])
|
@invoice = Invoice.find(params[:id])
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.html # show.html.erb
|
|
||||||
format.xml { render :xml => @invoice }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@invoice = Invoice.new :supplier_id => params[:supplier_id],
|
@invoice = Invoice.new :supplier_id => params[:supplier_id],
|
||||||
:delivery_id => params[:delivery_id], :order_id => params[:order_id]
|
:delivery_id => params[:delivery_id],
|
||||||
|
:order_id => params[:order_id]
|
||||||
respond_to do |format|
|
|
||||||
format.html # new.html.erb
|
|
||||||
format.xml { render :xml => @invoice }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
@invoice = Invoice.find(params[:id])
|
@invoice = Invoice.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
# POST /invoices
|
|
||||||
# POST /invoices.xml
|
|
||||||
def create
|
def create
|
||||||
@invoice = Invoice.new(params[:invoice])
|
@invoice = Invoice.new(params[:invoice])
|
||||||
|
|
||||||
if @invoice.save
|
if @invoice.save
|
||||||
flash[:notice] = "Rechnung wurde erstellt."
|
flash[:notice] = I18n.t('finance.create.notice')
|
||||||
if @invoice.order
|
if @invoice.order
|
||||||
# Redirect to balancing page
|
# Redirect to balancing page
|
||||||
redirect_to :controller => 'balancing', :action => 'new', :id => @invoice.order
|
redirect_to new_finance_order_url(order_id: @invoice.order.id)
|
||||||
else
|
else
|
||||||
redirect_to [:finance, @invoice]
|
redirect_to [:finance, @invoice]
|
||||||
end
|
end
|
||||||
|
@ -50,32 +34,20 @@ class Finance::InvoicesController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# PUT /invoices/1
|
|
||||||
# PUT /invoices/1.xml
|
|
||||||
def update
|
def update
|
||||||
@invoice = Invoice.find(params[:id])
|
@invoice = Invoice.find(params[:id])
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
if @invoice.update_attributes(params[:invoice])
|
if @invoice.update_attributes(params[:invoice])
|
||||||
flash[:notice] = 'Invoice was successfully updated.'
|
redirect_to [:finance, @invoice], notice: I18n.t('finance.update.notice')
|
||||||
format.html { redirect_to([:finance, @invoice]) }
|
|
||||||
format.xml { head :ok }
|
|
||||||
else
|
else
|
||||||
format.html { render :action => "edit" }
|
render :edit
|
||||||
format.xml { render :xml => @invoice.errors, :status => :unprocessable_entity }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# DELETE /invoices/1
|
|
||||||
# DELETE /invoices/1.xml
|
|
||||||
def destroy
|
def destroy
|
||||||
@invoice = Invoice.find(params[:id])
|
@invoice = Invoice.find(params[:id])
|
||||||
@invoice.destroy
|
@invoice.destroy
|
||||||
|
|
||||||
respond_to do |format|
|
redirect_to finance_invoices_url
|
||||||
format.html { redirect_to(finance_invoices_path) }
|
|
||||||
format.xml { head :ok }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
39
app/controllers/finance/order_articles_controller.rb
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
class Finance::OrderArticlesController < ApplicationController
|
||||||
|
|
||||||
|
before_filter :authenticate_finance
|
||||||
|
|
||||||
|
layout false # We only use this controller to serve js snippets, no need for layout rendering
|
||||||
|
|
||||||
|
def new
|
||||||
|
@order = Order.find(params[:order_id])
|
||||||
|
@order_article = @order.order_articles.build
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@order = Order.find(params[:order_id])
|
||||||
|
@order_article = @order.order_articles.build(params[:order_article])
|
||||||
|
unless @order_article.save
|
||||||
|
render action: :new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@order = Order.find(params[:order_id])
|
||||||
|
@order_article = OrderArticle.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@order = Order.find(params[:order_id])
|
||||||
|
@order_article = OrderArticle.find(params[:id])
|
||||||
|
begin
|
||||||
|
@order_article.update_article_and_price!(params[:order_article], params[:article], params[:article_price])
|
||||||
|
rescue
|
||||||
|
render action: :edit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@order_article = OrderArticle.find(params[:id])
|
||||||
|
@order_article.destroy
|
||||||
|
end
|
||||||
|
end
|
20
app/controllers/finance/ordergroups_controller.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
class Finance::OrdergroupsController < Finance::BaseController
|
||||||
|
|
||||||
|
def index
|
||||||
|
if params["sort"]
|
||||||
|
sort = case params["sort"]
|
||||||
|
when "name" then "name"
|
||||||
|
when "account_balance" then "account_balance"
|
||||||
|
when "name_reverse" then "name DESC"
|
||||||
|
when "account_balance_reverse" then "account_balance DESC"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
sort = "name"
|
||||||
|
end
|
||||||
|
|
||||||
|
@ordergroups = Ordergroup.undeleted.order(sort)
|
||||||
|
@ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:query]}%") unless params[:query].nil?
|
||||||
|
|
||||||
|
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,103 +0,0 @@
|
||||||
class Finance::TransactionsController < ApplicationController
|
|
||||||
before_filter :authenticate_finance
|
|
||||||
|
|
||||||
def index
|
|
||||||
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 "account_balance" then "account_balance"
|
|
||||||
when "name_reverse" then "name 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.without_deleted.count(:conditions => conditions)
|
|
||||||
@groups = Ordergroup.without_deleted.paginate :conditions => conditions,
|
|
||||||
:page => params[:page], :per_page => @per_page, :order => sort
|
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.html
|
|
||||||
format.js { render :partial => "ordergroups" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def list
|
|
||||||
@group = Ordergroup.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
|
|
||||||
format.js { render :partial => "list" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def new
|
|
||||||
@group = Ordergroup.find(params[:id])
|
|
||||||
@financial_transaction = @group.financial_transactions.build
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
@group = Ordergroup.find(params[:financial_transaction][:ordergroup_id])
|
|
||||||
amount = params[:financial_transaction][:amount]
|
|
||||||
note = params[:financial_transaction][:note]
|
|
||||||
begin
|
|
||||||
@group.add_financial_transaction(amount, note, @current_user)
|
|
||||||
flash[:notice] = 'Transaktion erfolgreich angelegt.'
|
|
||||||
redirect_to :action => 'list', :id => @group
|
|
||||||
rescue => e
|
|
||||||
@financial_transaction = FinancialTransaction.new(params[:financial_transaction])
|
|
||||||
flash.now[:error] = 'Transaktion konnte nicht angelegt werden!' + ' (' + e.message + ')'
|
|
||||||
render :action => 'new'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def new_collection
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_collection
|
|
||||||
note = params[:note]
|
|
||||||
raise "Notiz wird benötigt!" if note.blank?
|
|
||||||
params[:financial_transactions].each do |trans|
|
|
||||||
# ignore empty amount fields ...
|
|
||||||
unless trans[:amount].blank?
|
|
||||||
Ordergroup.find(trans[:ordergroup_id]).add_financial_transaction trans[:amount], note, @current_user
|
|
||||||
end
|
|
||||||
end
|
|
||||||
flash[:notice] = "Alle Transaktionen wurden gespeichert."
|
|
||||||
redirect_to :action => 'index'
|
|
||||||
rescue => error
|
|
||||||
flash[:error] = "Ein Fehler ist aufgetreten: " + error.to_s
|
|
||||||
redirect_to :action => 'new_collection'
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
|
@ -1,29 +1,21 @@
|
||||||
class Foodcoop::OrdergroupsController < ApplicationController
|
class Foodcoop::OrdergroupsController < ApplicationController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
if (params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 100)
|
@ordergroups = Ordergroup.undeleted.order('name DESC')
|
||||||
@per_page = params[:per_page].to_i
|
|
||||||
else
|
unless params[:name].blank? # Search by name
|
||||||
@per_page = 20
|
@ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:name]}%")
|
||||||
end
|
end
|
||||||
|
|
||||||
if (params[:only_active].to_i == 1)
|
if params[:only_active] # Select only active groups
|
||||||
if (! params[:query].blank?)
|
@ordergroups = @ordergroups.joins(:orders).where("orders.starts >= ?", Time.now.months_ago(3)).uniq
|
||||||
conditions = ["orders.starts >= ? AND name LIKE ?", Time.now.months_ago(3), "%#{params[:query]}%"]
|
|
||||||
else
|
|
||||||
conditions = ["orders.starts >= ?", Time.now.months_ago(3)]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
# if somebody uses the search field:
|
|
||||||
conditions = ["name LIKE ?", "%#{params[:query]}%"] unless params[:query].blank?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@total = Ordergroup.count(:conditions => conditions, :include => "orders")
|
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
||||||
@ordergroups = Ordergroup.paginate(:page => params[:page], :per_page => @per_page, :conditions => conditions, :order => "name", :include => "orders")
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # index.html.erb
|
format.html # index.html.erb
|
||||||
format.js { render :partial => "ordergroups" }
|
format.js { render :layout => false }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,36 +1,23 @@
|
||||||
class Foodcoop::UsersController < ApplicationController
|
class Foodcoop::UsersController < ApplicationController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
# sort by ordergroups
|
@users = User.order('nick ASC')
|
||||||
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:
|
# if somebody uses the search field:
|
||||||
unless params[:query].blank?
|
unless params[:user_name].blank?
|
||||||
conditions = ["first_name LIKE ? OR last_name LIKE ? OR nick LIKE ?",
|
@users = @users.where("first_name LIKE :user_name OR last_name LIKE :user_name OR nick LIKE :user_name",
|
||||||
"%#{params[:query]}%", "%#{params[:query]}%", "%#{params[:query]}%"]
|
user_name: "%#{params[:user_name]}%")
|
||||||
end
|
end
|
||||||
|
|
||||||
@total = User.count(:conditions => conditions)
|
if params[:ordergroup_name]
|
||||||
@users = User.paginate(:page => params[:page], :per_page => @per_page, :conditions => conditions, :order => "nick", :include => :groups)
|
@users = @users.joins(:groups).where("groups.type = 'Ordergroup' AND groups.name LIKE ?", "%#{params[:ordergroup_name]}%")
|
||||||
|
end
|
||||||
|
|
||||||
|
@users = @users.page(params[:page]).per(@per_page).order('users.nick ASC')
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # index.html.erb
|
format.html # index.html.haml
|
||||||
format.js { render :partial => "users" }
|
format.js { render :layout => false } # index.js.erb
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -14,13 +14,9 @@ class Foodcoop::WorkgroupsController < ApplicationController
|
||||||
def update
|
def update
|
||||||
@workgroup = Workgroup.find(params[:id])
|
@workgroup = Workgroup.find(params[:id])
|
||||||
if @workgroup.update_attributes(params[:workgroup])
|
if @workgroup.update_attributes(params[:workgroup])
|
||||||
flash[:notice] = "Arbeitsgruppe wurde aktualisiert"
|
redirect_to foodcoop_workgroups_url, :notice => I18n.t('workgroups.update.notice')
|
||||||
redirect_to foodcoop_workgroups_url
|
|
||||||
else
|
else
|
||||||
render :action => 'edit'
|
render :action => 'edit'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def memberships
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
99
app/controllers/group_orders_controller.rb
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
# 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 GroupOrdersController < ApplicationController
|
||||||
|
# Security
|
||||||
|
before_filter :ensure_ordergroup_member
|
||||||
|
before_filter :ensure_open_order, :only => [:new, :create, :edit, :update, :order, :stock_order, :saveOrder]
|
||||||
|
before_filter :ensure_my_group_order, only: [:show, :edit, :update]
|
||||||
|
before_filter :enough_apples?, only: [:new, :create]
|
||||||
|
|
||||||
|
# Index page.
|
||||||
|
def index
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@group_order = @order.group_orders.build(:ordergroup => @ordergroup, :updated_by => current_user)
|
||||||
|
@ordering_data = @group_order.load_data
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@group_order = GroupOrder.new(params[:group_order])
|
||||||
|
begin
|
||||||
|
@group_order.save_ordering!
|
||||||
|
redirect_to group_order_url(@group_order), :notice => I18n.t('group_orders.create.notice')
|
||||||
|
rescue ActiveRecord::StaleObjectError
|
||||||
|
redirect_to group_orders_url, :alert => I18n.t('group_orders.create.error_stale')
|
||||||
|
rescue => exception
|
||||||
|
logger.error('Failed to update order: ' + exception.message)
|
||||||
|
redirect_to group_orders_url, :alert => I18n.t('group_orders.create.error_general')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@order= @group_order.order
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@ordering_data = @group_order.load_data
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@group_order.attributes = params[:group_order]
|
||||||
|
begin
|
||||||
|
@group_order.save_ordering!
|
||||||
|
redirect_to group_order_url(@group_order), :notice => I18n.t('group_orders.update.notice')
|
||||||
|
rescue ActiveRecord::StaleObjectError
|
||||||
|
redirect_to group_orders_url, :alert => I18n.t('group_orders.update.error_stale')
|
||||||
|
rescue => exception
|
||||||
|
logger.error('Failed to update order: ' + exception.message)
|
||||||
|
redirect_to group_orders_url, :alert => I18n.t('group_orders.update.error_general')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Shows all Orders of the Ordergroup
|
||||||
|
# if selected, it shows all orders of the foodcoop
|
||||||
|
def archive
|
||||||
|
# get only orders belonging to the ordergroup
|
||||||
|
@closed_orders = Order.closed.page(params[:page]).per(10)
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # archive.html.haml
|
||||||
|
format.js # archive.js.erb
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Returns true if @current_user is member of an Ordergroup.
|
||||||
|
# Used as a :before_filter by OrderingController.
|
||||||
|
def ensure_ordergroup_member
|
||||||
|
@ordergroup = @current_user.ordergroup
|
||||||
|
if @ordergroup.nil?
|
||||||
|
redirect_to root_url, :alert => I18n.t('group_orders.errors.no_member')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_open_order
|
||||||
|
@order = Order.find((params[:order_id] || params[:group_order][:order_id]),
|
||||||
|
:include => [:supplier, :order_articles])
|
||||||
|
unless @order.open?
|
||||||
|
flash[:notice] = I18n.t('group_orders.error_closed')
|
||||||
|
redirect_to :action => 'index'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_my_group_order
|
||||||
|
@group_order = @ordergroup.group_orders.find(params[:id])
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
redirect_to group_orders_url, alert: I18n.t('group_orders.errors.notfound')
|
||||||
|
end
|
||||||
|
|
||||||
|
def enough_apples?
|
||||||
|
if @ordergroup.not_enough_apples?
|
||||||
|
redirect_to group_orders_url,
|
||||||
|
alert: t('not_enough_apples', scope: 'group_orders.messages', apples: @ordergroup.apples,
|
||||||
|
stop_ordering_under: FoodsoftConfig[:stop_ordering_under])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,34 +1,24 @@
|
||||||
|
# encoding: utf-8
|
||||||
class HomeController < ApplicationController
|
class HomeController < ApplicationController
|
||||||
helper :messages
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@currentOrders = Order.open
|
|
||||||
@ordergroup = @current_user.ordergroup
|
|
||||||
# unaccepted tasks
|
# unaccepted tasks
|
||||||
@unaccepted_tasks = @current_user.unaccepted_tasks
|
@unaccepted_tasks = Task.unaccepted_tasks_for(current_user)
|
||||||
# task in next week
|
# task in next week
|
||||||
@next_tasks = @current_user.next_tasks
|
@next_tasks = Task.next_assigned_tasks_for(current_user)
|
||||||
@messages = Message.public.all :order => 'created_at DESC', :limit => 5
|
|
||||||
# count tasks with no responsible person
|
# count tasks with no responsible person
|
||||||
# tasks for groups the current user is not a member are ignored
|
# tasks for groups the current user is not a member are ignored
|
||||||
tasks = Task.find(:all, :conditions => ["assigned = ? and done = ?", false, false])
|
@unassigned_tasks = Task.unassigned_tasks_for(current_user)
|
||||||
@unassigned_tasks_number = 0
|
|
||||||
for task in tasks
|
|
||||||
(@unassigned_tasks_number += 1) unless task.workgroup && !current_user.member_of?(task.workgroup)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def profile
|
def profile
|
||||||
@user = @current_user
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_profile
|
def update_profile
|
||||||
@user = @current_user
|
if @current_user.update_attributes(params[:user])
|
||||||
if @user.update_attributes(params[:user])
|
redirect_to my_profile_url, notice: I18n.t('home.changes_saved')
|
||||||
flash[:notice] = 'Änderungen wurden gespeichert.'
|
|
||||||
redirect_to :action => 'profile'
|
|
||||||
else
|
else
|
||||||
render :action => 'profile'
|
render :profile
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -37,10 +27,6 @@ class HomeController < ApplicationController
|
||||||
@ordergroup = @user.ordergroup
|
@ordergroup = @user.ordergroup
|
||||||
|
|
||||||
unless @ordergroup.nil?
|
unless @ordergroup.nil?
|
||||||
@ordergroup_column_names = ["Description", "Actual Size", "Balance", "Updated"]
|
|
||||||
@ordergroup_columns = ["description", "account_balance", "account_updated"]
|
|
||||||
|
|
||||||
#listing the financial transactions with ajax...
|
|
||||||
|
|
||||||
if params['sort']
|
if params['sort']
|
||||||
sort = case params['sort']
|
sort = case params['sort']
|
||||||
|
@ -55,21 +41,11 @@ class HomeController < ApplicationController
|
||||||
sort = "created_on DESC"
|
sort = "created_on DESC"
|
||||||
end
|
end
|
||||||
|
|
||||||
# or if somebody uses the search field:
|
@financial_transactions = @ordergroup.financial_transactions.page(params[:page]).per(@per_page).order(sort)
|
||||||
conditions = ["note LIKE ?", "%#{params[:query]}%"] unless params[:query].nil?
|
@financial_transactions = @financial_transactions.where("note LIKE ?", "%#{params[:query]}%") if params[:query].present?
|
||||||
|
|
||||||
@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 { render :partial => "finance/transactions/list" }
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
flash[:error] = "Leider bist Du kein Mitglied einer Bestellgruppe"
|
redirect_to root_path, :alert => I18n.t('home.no_ordergroups')
|
||||||
redirect_to root_path
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -78,9 +54,9 @@ class HomeController < ApplicationController
|
||||||
membership = Membership.find(params[:membership_id])
|
membership = Membership.find(params[:membership_id])
|
||||||
if membership.user == current_user
|
if membership.user == current_user
|
||||||
membership.destroy
|
membership.destroy
|
||||||
flash[:notice] = "Du bist jetzt kein Mitglied der Gruppe #{membership.group.name} mehr."
|
flash[:notice] = I18n.t('home.ordergroup_cancelled', :group => membership.group.name)
|
||||||
else
|
else
|
||||||
flash[:error] = "Ein Problem ist aufgetreten."
|
flash[:error] = I18n.t('errors.general')
|
||||||
end
|
end
|
||||||
redirect_to my_profile_path
|
redirect_to my_profile_path
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
class InvitesController < ApplicationController
|
class InvitesController < ApplicationController
|
||||||
|
|
||||||
before_filter :authenticate_membership_or_admin, :only => [:new]
|
before_filter :authenticate_membership_or_admin, :only => [:new]
|
||||||
#TODO: auhtorize also for create action.
|
#TODO: authorize also for create action.
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@invite = Invite.new(:user => @current_user, :group => @group)
|
@invite = Invite.new(:user => @current_user, :group => @group)
|
||||||
|
|
||||||
render :update do |page|
|
|
||||||
page.replace_html :edit_box, :partial => "new"
|
|
||||||
page.show :edit_box
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@invite = Invite.new(params[:invite])
|
@invite = Invite.new(params[:invite])
|
||||||
|
|
||||||
render :update do |page|
|
|
||||||
if @invite.save
|
if @invite.save
|
||||||
page.replace_html :edit_box, :partial => "success"
|
Mailer.invite(@invite).deliver
|
||||||
else
|
|
||||||
page.replace_html :edit_box, :partial => "new"
|
respond_to do |format|
|
||||||
|
format.html do
|
||||||
|
redirect_to back_or_default_path, notice: I18n.t('invites.success')
|
||||||
end
|
end
|
||||||
|
format.js { render layout: false }
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
render action: :new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,69 +1,33 @@
|
||||||
|
# encoding: utf-8
|
||||||
class LoginController < ApplicationController
|
class LoginController < ApplicationController
|
||||||
skip_before_filter :authenticate # no authentication since this is the login page
|
skip_before_filter :authenticate # no authentication since this is the login page
|
||||||
before_filter :validate_token, :only => [:password, :update_password]
|
before_filter :validate_token, :only => [:new_password, :update_password]
|
||||||
|
|
||||||
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] = "Abgemeldet"
|
|
||||||
render :action => 'login'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Displays a "denied due to insufficient privileges" message and provides the login form.
|
|
||||||
def denied
|
|
||||||
flash[:error] = "Du bist nicht berechtigt diese Seite zu besuchen. Bitte als berechtige Benutzerin anmelden oder zurück gehen."
|
|
||||||
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 root_path
|
|
||||||
end
|
|
||||||
else
|
|
||||||
current_user = nil
|
|
||||||
flash[:error] = "Tschuldige, die Anmeldung war nicht erfolgreich. Bitte erneut versuchen."
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Display the form to enter an email address requesting a token to set a new password.
|
# Display the form to enter an email address requesting a token to set a new password.
|
||||||
def forgot_password
|
def forgot_password
|
||||||
|
@user = User.new
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sends an email to a user with the token that allows setting a new password through action "password".
|
# Sends an email to a user with the token that allows setting a new password through action "password".
|
||||||
def reset_password
|
def reset_password
|
||||||
if (user = User.find_by_email(params[:login][:email]))
|
if request.get? || params[:user].nil? # Catch for get request and give better error message.
|
||||||
|
redirect_to forgot_password_url, alert: 'Ein Problem ist aufgetreten. Bitte erneut versuchen' and return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (user = User.find_by_email(params[:user][:email]))
|
||||||
user.reset_password_token = user.new_random_password(16)
|
user.reset_password_token = user.new_random_password(16)
|
||||||
user.reset_password_expires = Time.now.advance(:days => 2)
|
user.reset_password_expires = Time.now.advance(:days => 2)
|
||||||
if user.save
|
if user.save
|
||||||
email = Mailer.deliver_reset_password(user)
|
Mailer.reset_password(user).deliver
|
||||||
logger.debug("Sent password reset email to #{user.email}.")
|
logger.debug("Sent password reset email to #{user.email}.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
flash[:notice] = "Wenn Deine E-Mail hier registiert ist bekommst Du jetzt eine Nachricht mit einem Passwort-Zurücksetzen-Link."
|
redirect_to login_url, :notice => I18n.t('login.controller.reset_password.notice')
|
||||||
render :action => 'login'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set a new password with a token from the password reminder email.
|
# 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.
|
# Called with params :id => User.id and :token => User.reset_password_token to specify a new password.
|
||||||
def password
|
def new_password
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets a new password.
|
# Sets a new password.
|
||||||
|
@ -74,38 +38,32 @@ class LoginController < ApplicationController
|
||||||
@user.reset_password_token = nil
|
@user.reset_password_token = nil
|
||||||
@user.reset_password_expires = nil
|
@user.reset_password_expires = nil
|
||||||
@user.save
|
@user.save
|
||||||
flash[:notice] = "Dein Passwort wurde aktualisiert. Du kannst Dich jetzt anmelden."
|
redirect_to login_url, :notice => I18n.t('login.controller.update_password.notice')
|
||||||
render :action => 'login'
|
|
||||||
else
|
else
|
||||||
render :action => 'password'
|
render :new_password
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Invited users.
|
# For invited users.
|
||||||
def invite
|
def accept_invitation
|
||||||
@invite = Invite.find_by_token(params[:id])
|
@invite = Invite.find_by_token(params[:token])
|
||||||
if (@invite.nil? || @invite.expires_at < Time.now)
|
if @invite.nil? || @invite.expires_at < Time.now
|
||||||
flash[:error] = "Deine Einladung ist nicht (mehr) gültig."
|
redirect_to login_url, alert: I18n.t('login.controller.error_invite_invalid')
|
||||||
render :action => 'login'
|
|
||||||
elsif @invite.group.nil?
|
elsif @invite.group.nil?
|
||||||
flash[:error] = "Die Gruppe, in die Du eingeladen wurdest, existiert leider nicht mehr."
|
redirect_to login_url, alert: I18n.t('login.controller.error_group_invalid')
|
||||||
render :action => 'login'
|
elsif request.post?
|
||||||
elsif (request.post?)
|
|
||||||
User.transaction do
|
User.transaction do
|
||||||
@user = User.new(params[:user])
|
@user = User.new(params[:user])
|
||||||
@user.email = @invite.email
|
@user.email = @invite.email
|
||||||
if @user.save
|
if @user.save
|
||||||
Membership.new(:user => @user, :group => @invite.group).save!
|
Membership.new(:user => @user, :group => @invite.group).save!
|
||||||
@invite.destroy
|
@invite.destroy
|
||||||
flash[:notice] = "Herzlichen Glückwunsch, Dein Account wurde erstellt. Du kannst Dich nun einloggen."
|
redirect_to login_url, notice: I18n.t('login.controller.accept_invitation.notice')
|
||||||
render :action => 'login'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@user = User.new(:email => @invite.email)
|
@user = User.new(:email => @invite.email)
|
||||||
end
|
end
|
||||||
rescue
|
|
||||||
flash[:error] = "Ein Fehler ist aufgetreten. Bitte erneut versuchen."
|
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
@ -113,8 +71,7 @@ class LoginController < ApplicationController
|
||||||
def validate_token
|
def validate_token
|
||||||
@user = User.find_by_id_and_reset_password_token(params[:id], params[:token])
|
@user = User.find_by_id_and_reset_password_token(params[:id], params[:token])
|
||||||
if (@user.nil? || @user.reset_password_expires < Time.now)
|
if (@user.nil? || @user.reset_password_expires < Time.now)
|
||||||
flash[:error] = "Ungültiger oder abgelaufener Token. Bitte versuch es erneut."
|
redirect_to forgot_password_url, alert: I18n.t('login.controller.error_token_invalid')
|
||||||
render :action => 'forgot_password'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,22 +2,23 @@ class MessagesController < ApplicationController
|
||||||
|
|
||||||
# Renders the "inbox" action.
|
# Renders the "inbox" action.
|
||||||
def index
|
def index
|
||||||
@messages = Message.public.paginate :page => params[:page], :per_page => 20, :order => 'created_at DESC'
|
@messages = Message.public.page(params[:page]).per(@per_page).order('created_at DESC').includes(:sender)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates a new message object.
|
# Creates a new message object.
|
||||||
def new
|
def new
|
||||||
@message = Message.new
|
@message = Message.new(params[:message])
|
||||||
|
if @message.reply_to and not @message.reply_to.is_readable_for?(current_user)
|
||||||
|
redirect_to new_message_url, alert: 'Nachricht ist privat!'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates a new message.
|
# Creates a new message.
|
||||||
def create
|
def create
|
||||||
@message = @current_user.send_messages.new(params[:message])
|
@message = @current_user.send_messages.new(params[:message])
|
||||||
if @message.save
|
if @message.save
|
||||||
#FIXME: Send Mails wit ID instead of using message.state ...
|
Resque.enqueue(UserNotifier, FoodsoftConfig.scope, 'message_deliver', @message.id)
|
||||||
call_rake :send_emails
|
redirect_to messages_url, :notice => I18n.t('messages.create.notice')
|
||||||
flash[:notice] = "Nachricht ist gespeichert und wird versendet."
|
|
||||||
redirect_to messages_path
|
|
||||||
else
|
else
|
||||||
render :action => 'new'
|
render :action => 'new'
|
||||||
end
|
end
|
||||||
|
@ -26,51 +27,8 @@ class MessagesController < ApplicationController
|
||||||
# Shows a single message.
|
# Shows a single message.
|
||||||
def show
|
def show
|
||||||
@message = Message.find(params[:id])
|
@message = Message.find(params[:id])
|
||||||
|
unless @message.is_readable_for?(current_user)
|
||||||
|
redirect_to messages_url, alert: 'Nachricht ist privat!'
|
||||||
end
|
end
|
||||||
|
|
||||||
# Replys to the message specified through :id.
|
|
||||||
def reply
|
|
||||||
message = Message.find(params[:id])
|
|
||||||
@message = Message.new(:recipient => message.sender, :subject => "Re: #{message.subject}")
|
|
||||||
@message.body = "#{message.sender.nick} schrieb am #{I18n.l(message.created_at.to_date)} um #{I18n.l(message.created_at, :format => :time)}:\n"
|
|
||||||
message.body.each_line{|l| @message.body += "> #{l}"}
|
|
||||||
render :action => 'new'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Shows new-message form with the recipient user specified through :id.
|
|
||||||
def user
|
|
||||||
if (recipient = User.find(params[:id]))
|
|
||||||
@message = Message.new(:recipient => recipient)
|
|
||||||
render :action => 'new'
|
|
||||||
else
|
|
||||||
flash[:error] = 'Unbekannte_r EmpfängerIn.'
|
|
||||||
redirect_to :action=> 'index'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Shows new-message form with the recipient user specified through :id.
|
|
||||||
def group
|
|
||||||
group = Group.find(params[:id], :include => :memberships)
|
|
||||||
if (group && !group.memberships.empty?)
|
|
||||||
@message = Message.new(:group_id => group.id)
|
|
||||||
render :action => 'new'
|
|
||||||
else
|
|
||||||
flash[:error] = 'Empfängergruppe ist unbekannt oder hat keine Mitglieder.'
|
|
||||||
redirect_to :action=> 'index'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Auto-complete for recipient user list.
|
|
||||||
def auto_complete_for_message_recipients_nicks
|
|
||||||
@users = User.find(:all,
|
|
||||||
:conditions => ['LOWER(nick) LIKE ?', '%' + params[:message][:recipients_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
|
||||||
end
|
end
|
||||||
|
|
16
app/controllers/order_comments_controller.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
class OrderCommentsController < ApplicationController
|
||||||
|
|
||||||
|
def new
|
||||||
|
@order = Order.find(params[:order_id])
|
||||||
|
@order_comment = @order.comments.build(:user => current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@order_comment = OrderComment.new(params[:order_comment])
|
||||||
|
if @order_comment.save
|
||||||
|
render :layout => false
|
||||||
|
else
|
||||||
|
render :action => :new, :layout => false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,209 +0,0 @@
|
||||||
# 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 :ensure_ordergroup_member
|
|
||||||
before_filter :ensure_open_order, :only => [:order, :stock_order, :saveOrder]
|
|
||||||
|
|
||||||
verify :method => :post, :only => [:saveOrder], :redirect_to => {:action => :index}
|
|
||||||
|
|
||||||
# Index page.
|
|
||||||
def index
|
|
||||||
end
|
|
||||||
|
|
||||||
# Edit a current order.
|
|
||||||
def order
|
|
||||||
redirect_to :action => 'stock_order', :id => @order if @order.stockit?
|
|
||||||
|
|
||||||
# Load order article data...
|
|
||||||
@articles_grouped_by_category = @order.articles_grouped_by_category
|
|
||||||
# save results of earlier orders in array
|
|
||||||
ordered_articles = Array.new
|
|
||||||
@group_order = @order.group_orders.find(:first,
|
|
||||||
:conditions => "ordergroup_id = #{@ordergroup.id}", :include => :group_order_articles)
|
|
||||||
|
|
||||||
if @group_order
|
|
||||||
# Group has already ordered, so get the results...
|
|
||||||
for goa in @group_order.group_order_articles
|
|
||||||
ordered_articles[goa.order_article_id] = {:quantity => goa.quantity,
|
|
||||||
:tolerance => goa.tolerance,
|
|
||||||
:quantity_result => goa.result(:quantity),
|
|
||||||
:tolerance_result => goa.result(:tolerance)}
|
|
||||||
end
|
|
||||||
@version = @group_order.lock_version
|
|
||||||
@availableFunds = @ordergroup.get_available_funds(@group_order)
|
|
||||||
else
|
|
||||||
@version = 0
|
|
||||||
@availableFunds = @ordergroup.get_available_funds
|
|
||||||
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_grouped_by_category.each do |category_name, order_articles|
|
|
||||||
for order_article in order_articles
|
|
||||||
# price/unit size
|
|
||||||
@price[i] = order_article.article.fc_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
|
|
||||||
|
|
||||||
def stock_order
|
|
||||||
# Load order article data...
|
|
||||||
@articles_grouped_by_category = @order.articles_grouped_by_category
|
|
||||||
# save results of earlier orders in array
|
|
||||||
ordered_articles = Array.new
|
|
||||||
@group_order = @order.group_orders.find(:first,
|
|
||||||
:conditions => "ordergroup_id = #{@ordergroup.id}", :include => :group_order_articles)
|
|
||||||
|
|
||||||
if @group_order
|
|
||||||
# Group has already ordered, so get the results...
|
|
||||||
for goa in @group_order.group_order_articles
|
|
||||||
ordered_articles[goa.order_article_id] = {:quantity => goa.quantity,
|
|
||||||
:tolerance => goa.tolerance,
|
|
||||||
:quantity_result => goa.result(:quantity),
|
|
||||||
:tolerance_result => goa.result(:tolerance)}
|
|
||||||
end
|
|
||||||
@version = @group_order.lock_version
|
|
||||||
@availableFunds = @ordergroup.get_available_funds(@group_order)
|
|
||||||
else
|
|
||||||
@version = 0
|
|
||||||
@availableFunds = @ordergroup.get_available_funds
|
|
||||||
end
|
|
||||||
|
|
||||||
# load prices ....
|
|
||||||
@price = Array.new; @quantity_available = Array.new
|
|
||||||
@others_quantity = Array.new; @quantity = Array.new; @quantity_result = Array.new; @used_quantity = Array.new; @unused_quantity = Array.new
|
|
||||||
i = 0;
|
|
||||||
@articles_grouped_by_category.each do |category_name, order_articles|
|
|
||||||
for order_article in order_articles
|
|
||||||
# price/unit size
|
|
||||||
@price[i] = order_article.article.fc_price
|
|
||||||
@quantity_available[i] = order_article.article.quantity_available(@order)
|
|
||||||
# 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)
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update changes to a current order.
|
|
||||||
def saveOrder
|
|
||||||
if (params[:total_balance].to_i < 0) #TODO: Better use a real test on sufficiant funds
|
|
||||||
flash[:error] = 'Der Bestellwert übersteigt das verfügbare Guthaben.'
|
|
||||||
redirect_to :action => 'order'
|
|
||||||
elsif (ordered = params[:ordered])
|
|
||||||
begin
|
|
||||||
Order.transaction do
|
|
||||||
# Try to find group_order
|
|
||||||
group_order = @order.group_orders.first :conditions => "ordergroup_id = #{@ordergroup.id}",
|
|
||||||
:include => [:group_order_articles]
|
|
||||||
# Create group order if necessary...
|
|
||||||
unless group_order.nil?
|
|
||||||
# check for conflicts well ahead
|
|
||||||
if (params[:version].to_i != group_order.lock_version)
|
|
||||||
raise ActiveRecord::StaleObjectError
|
|
||||||
end
|
|
||||||
else
|
|
||||||
group_order = @ordergroup.group_orders.create!(:order => @order, :updated_by => @current_user, :price => 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create/update group_order_articles...
|
|
||||||
for order_article in @order.order_articles
|
|
||||||
|
|
||||||
# Find the group_order_article, create a new one if necessary...
|
|
||||||
group_order_article = group_order.group_order_articles.detect { |v| v.order_article_id == order_article.id }
|
|
||||||
if group_order_article.nil?
|
|
||||||
group_order_article = group_order.group_order_articles.create(:order_article_id => order_article.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get ordered quantities and update group_order_articles/_quantities...
|
|
||||||
quantities = ordered.fetch(order_article.id.to_s, {:quantity => 0, :tolerance => 0})
|
|
||||||
group_order_article.update_quantities(quantities[:quantity].to_i, quantities[:tolerance].to_i)
|
|
||||||
|
|
||||||
# Also update results for the order_article
|
|
||||||
order_article.update_results!
|
|
||||||
end
|
|
||||||
|
|
||||||
group_order.update_price!
|
|
||||||
group_order.update_attribute(:updated_by, @current_user)
|
|
||||||
end
|
|
||||||
flash[:notice] = 'Die Bestellung wurde gespeichert.'
|
|
||||||
rescue ActiveRecord::StaleObjectError
|
|
||||||
flash[:error] = 'In der Zwischenzeit hat jemand anderes auch bestellt, daher konnte die Bestellung nicht aktualisiert werden.'
|
|
||||||
rescue => exception
|
|
||||||
logger.error('Failed to update order: ' + exception.message)
|
|
||||||
flash[:error] = 'Die Bestellung konnte nicht aktualisiert werden, da ein Fehler auftrat.'
|
|
||||||
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])
|
|
||||||
@group_order = @order.group_order(@ordergroup)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Shows all Orders of the Ordergroup
|
|
||||||
# if selected, it shows all orders of the foodcoop
|
|
||||||
def myOrders
|
|
||||||
# get only orders belonging to the ordergroup
|
|
||||||
@closed_orders = Order.paginate :page => params[:page], :per_page => 10,
|
|
||||||
:conditions => { :state => 'closed' }, :order => "orders.ends DESC"
|
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.html # myOrders.haml
|
|
||||||
format.js { render :partial => "orders", :locals => {:orders => @closed_orders, :pagination => true} }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# adds a Comment to the Order
|
|
||||||
def add_comment
|
|
||||||
order = Order.find(params[:id])
|
|
||||||
comment = order.comments.build(params[:comment])
|
|
||||||
comment.user = @current_user
|
|
||||||
if !comment.text.blank? and comment.save
|
|
||||||
flash[:notice] = "Kommentar wurde erstellt."
|
|
||||||
else
|
|
||||||
flash[:error] = "Kommentar konnte nicht erstellt werden. Leerer Kommentar?"
|
|
||||||
end
|
|
||||||
redirect_to :action => 'my_order_result', :id => order
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Returns true if @current_user is member of an Ordergroup.
|
|
||||||
# Used as a :before_filter by OrderingController.
|
|
||||||
def ensure_ordergroup_member
|
|
||||||
@ordergroup = @current_user.ordergroup
|
|
||||||
if @ordergroup.nil?
|
|
||||||
flash[:notice] = 'Sie gehören keiner Bestellgruppe an.'
|
|
||||||
redirect_to :controller => root_path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def ensure_open_order
|
|
||||||
@order = Order.find(params[:id], :include => [:supplier, :order_articles])
|
|
||||||
unless @order.open?
|
|
||||||
flash[:notice] = 'Diese Bestellung ist bereits abgeschlossen.'
|
|
||||||
redirect_to :action => 'index'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
|
@ -1,13 +1,11 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
#
|
||||||
# Controller for managing orders, i.e. all actions that require the "orders" role.
|
# 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.
|
# Normal ordering actions of members of order groups is handled by the OrderingController.
|
||||||
class OrdersController < ApplicationController
|
class OrdersController < ApplicationController
|
||||||
|
|
||||||
before_filter :authenticate_orders
|
before_filter :authenticate_orders
|
||||||
|
|
||||||
# Define layout exceptions for PDF actions:
|
|
||||||
layout "application", :except => [:faxPdf, :matrixPdf, :articlesPdf, :groupsPdf]
|
|
||||||
prawnto :prawn => { :page_size => 'A4' }
|
|
||||||
|
|
||||||
# List orders
|
# List orders
|
||||||
def index
|
def index
|
||||||
@open_orders = Order.open
|
@open_orders = Order.open
|
||||||
|
@ -22,18 +20,7 @@ class OrdersController < ApplicationController
|
||||||
else
|
else
|
||||||
sort = "ends DESC"
|
sort = "ends DESC"
|
||||||
end
|
end
|
||||||
@orders = Order.paginate :page => params[:page], :per_page => @per_page,
|
@orders = Order.page(params[:page]).per(@per_page).order(sort).where("state != 'open'").includes(:supplier)
|
||||||
:order => sort, :conditions => "state != 'open'",
|
|
||||||
:include => :supplier
|
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.html
|
|
||||||
format.js do
|
|
||||||
render :update do |page|
|
|
||||||
page.replace_html 'orders_table', :partial => "orders"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Gives a view for the results to a specific order
|
# Gives a view for the results to a specific order
|
||||||
|
@ -41,13 +28,26 @@ class OrdersController < ApplicationController
|
||||||
def show
|
def show
|
||||||
@order= Order.find(params[:id])
|
@order= Order.find(params[:id])
|
||||||
|
|
||||||
if params[:view] # Articles-list will be replaced
|
respond_to do |format|
|
||||||
partial = case params[:view]
|
format.html
|
||||||
when 'normal' then "articles"
|
format.js do
|
||||||
|
@partial = case params[:view]
|
||||||
|
when 'default' then "articles"
|
||||||
when 'groups'then 'shared/articles_by_groups'
|
when 'groups'then 'shared/articles_by_groups'
|
||||||
when 'articles'then 'shared/articles_by_articles'
|
when 'articles'then 'shared/articles_by_articles'
|
||||||
|
else 'articles'
|
||||||
|
end
|
||||||
|
render :layout => false
|
||||||
|
end
|
||||||
|
format.pdf do
|
||||||
|
pdf = case params[:document]
|
||||||
|
when 'groups' then OrderByGroups.new(@order)
|
||||||
|
when 'articles' then OrderByArticles.new(@order)
|
||||||
|
when 'fax' then OrderFax.new(@order)
|
||||||
|
when 'matrix' then OrderMatrix.new(@order)
|
||||||
|
end
|
||||||
|
send_data pdf.to_pdf, filename: pdf.filename, type: 'application/pdf'
|
||||||
end
|
end
|
||||||
render :partial => partial, :locals => {:order => @order} if partial
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -60,10 +60,12 @@ class OrdersController < ApplicationController
|
||||||
# order_articles will be saved in Order.article_ids=()
|
# order_articles will be saved in Order.article_ids=()
|
||||||
def create
|
def create
|
||||||
@order = Order.new(params[:order])
|
@order = Order.new(params[:order])
|
||||||
|
@order.created_by = current_user
|
||||||
if @order.save
|
if @order.save
|
||||||
flash[:notice] = "Die Bestellung wurde erstellt."
|
flash[:notice] = I18n.t('orders.create.notice')
|
||||||
redirect_to @order
|
redirect_to @order
|
||||||
else
|
else
|
||||||
|
logger.debug "[debug] order errors: #{@order.errors.messages}"
|
||||||
render :action => 'new'
|
render :action => 'new'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -78,7 +80,7 @@ class OrdersController < ApplicationController
|
||||||
def update
|
def update
|
||||||
@order = Order.find params[:id]
|
@order = Order.find params[:id]
|
||||||
if @order.update_attributes params[:order]
|
if @order.update_attributes params[:order]
|
||||||
flash[:notice] = "Die Bestellung wurde aktualisiert."
|
flash[:notice] = I18n.t('orders.update.notice')
|
||||||
redirect_to :action => 'show', :id => @order
|
redirect_to :action => 'show', :id => @order
|
||||||
else
|
else
|
||||||
render :action => 'edit'
|
render :action => 'edit'
|
||||||
|
@ -95,31 +97,7 @@ class OrdersController < ApplicationController
|
||||||
def finish
|
def finish
|
||||||
order = Order.find(params[:id])
|
order = Order.find(params[:id])
|
||||||
order.finish!(@current_user)
|
order.finish!(@current_user)
|
||||||
call_rake "foodsoft:finished_order_tasks", :order_id => order.id
|
redirect_to order, notice: I18n.t('orders.finish.notice')
|
||||||
flash[:notice] = "Die Bestellung wurde beendet."
|
|
||||||
redirect_to order
|
|
||||||
end
|
|
||||||
|
|
||||||
# Renders the groups-orderd PDF.
|
|
||||||
def groupsPdf
|
|
||||||
@order = Order.find(params[:id])
|
|
||||||
prawnto :filename => "#{Date.today}_#{@order.name}_GruppenSortierung.pdf"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Renders the articles-orderd PDF.
|
|
||||||
def articlesPdf
|
|
||||||
@order = Order.find(params[:id])
|
|
||||||
prawnto :filename => "#{Date.today}_#{@order.name}_ArtikelSortierung.pdf",
|
|
||||||
:prawn => { :left_margin => 48,
|
|
||||||
:right_margin => 48,
|
|
||||||
:top_margin => 48,
|
|
||||||
:bottom_margin => 48 }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Renders the fax PDF.
|
|
||||||
def faxPdf
|
|
||||||
@order = Order.find(params[:id])
|
|
||||||
prawnto :filename => "#{Date.today}_#{@order.name}_FAX.pdf"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Renders the fax-text-file
|
# Renders the fax-text-file
|
||||||
|
@ -127,15 +105,15 @@ class OrdersController < ApplicationController
|
||||||
def text_fax_template
|
def text_fax_template
|
||||||
order = Order.find(params[:id])
|
order = Order.find(params[:id])
|
||||||
supplier = order.supplier
|
supplier = order.supplier
|
||||||
contact = Foodsoft.config[:contact].symbolize_keys
|
contact = FoodsoftConfig[:contact].symbolize_keys
|
||||||
text = "Bestellung für" + " #{Foodsoft.config[:name]}"
|
text = I18n.t('orders.fax.heading', :name => FoodsoftConfig[:name])
|
||||||
text += "\n" + "Kundennummer" + ": #{supplier.customer_number}" unless supplier.customer_number.blank?
|
text += "\n" + I18n.t('orders.fax.customer_number') + ': #{supplier.customer_number}' unless supplier.customer_number.blank?
|
||||||
text += "\n" + "Liefertag" + ": "
|
text += "\n" + I18n.t('orders.fax.delivery_day')
|
||||||
text += "\n\n#{supplier.name}\n#{supplier.address}\nFAX: #{supplier.fax}\n\n"
|
text += "\n\n#{supplier.name}\n#{supplier.address}\n" + I18n.t('simple_form.labels.supplier.fax') + ": #{supplier.fax}\n\n"
|
||||||
text += "****** " + "Versandadresse" + "\n\n"
|
text += "****** " + I18n.t('orders.fax.to_address') + "\n\n"
|
||||||
text += "#{Foodsoft.config[:name]}\n#{contact[:street]}\n#{contact[:zip_code]} #{contact[:city]}\n\n"
|
text += "#{FoodsoftConfig[:name]}\n#{contact[:street]}\n#{contact[:zip_code]} #{contact[:city]}\n\n"
|
||||||
text += "****** " + "Artikel" + "\n\n"
|
text += "****** " + I18n.t('orders.fax.articles') + "\n\n"
|
||||||
text += "Nummer" + " " + "Menge" + " " + "Name" + "\n"
|
text += I18n.t('orders.fax.number') + " " + I18n.t('orders.fax.amount') + " " + I18n.t('orders.fax.name') + "\n"
|
||||||
# now display all ordered articles
|
# now display all ordered articles
|
||||||
order.order_articles.ordered.all(:include => [:article, :article_price]).each do |oa|
|
order.order_articles.ordered.all(:include => [:article, :article_price]).each do |oa|
|
||||||
number = oa.article.order_number
|
number = oa.article.order_number
|
||||||
|
@ -148,28 +126,4 @@ class OrdersController < ApplicationController
|
||||||
:type => 'text/plain; charset=utf-8; header=present',
|
:type => 'text/plain; charset=utf-8; header=present',
|
||||||
:disposition => "attachment; filename=#{order.name}"
|
:disposition => "attachment; filename=#{order.name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Renders the matrix PDF.
|
|
||||||
def matrixPdf
|
|
||||||
@order = Order.find(params[:id])
|
|
||||||
unless @order.order_articles.ordered.empty?
|
|
||||||
prawnto :filename => "#{Date.today}_#{@order.name}_Matrix.pdf"
|
|
||||||
else
|
|
||||||
flash[:error] = "Es sind keine Artikel bestellt worden."
|
|
||||||
redirect_to @order
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# adds a Comment to the Order
|
|
||||||
def add_comment
|
|
||||||
order = Order.find(params[:id])
|
|
||||||
comment = order.comments.build(params[:comment])
|
|
||||||
comment.user = @current_user
|
|
||||||
if !comment.text.empty? and comment.save
|
|
||||||
flash[:notice] = "Kommentar wurde erstellt."
|
|
||||||
else
|
|
||||||
flash[:error] = "Kommentar konnte nicht erstellt werden. Leerer Kommentar?"
|
|
||||||
end
|
|
||||||
redirect_to order
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# encoding: utf-8
|
||||||
class PagesController < ApplicationController
|
class PagesController < ApplicationController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@ -16,7 +17,7 @@ class PagesController < ApplicationController
|
||||||
elsif params[:id]
|
elsif params[:id]
|
||||||
page = Page.find_by_id(params[:id])
|
page = Page.find_by_id(params[:id])
|
||||||
if page.nil?
|
if page.nil?
|
||||||
flash[:error] = "Seite existiert nicht!"
|
flash[:error] = I18n.t('pages.cshow.error_noexist')
|
||||||
redirect_to all_pages_path and return
|
redirect_to all_pages_path and return
|
||||||
else
|
else
|
||||||
redirect_to wiki_page_path(page.permalink) and return
|
redirect_to wiki_page_path(page.permalink) and return
|
||||||
|
@ -28,7 +29,7 @@ class PagesController < ApplicationController
|
||||||
elsif @page.redirect?
|
elsif @page.redirect?
|
||||||
page = Page.find_by_id(@page.redirect)
|
page = Page.find_by_id(@page.redirect)
|
||||||
unless page.nil?
|
unless page.nil?
|
||||||
flash[:notice] = "Weitergeleitet von #{@page.title} ..."
|
flash[:notice] = I18n.t('pages.cshow.redirect_notice', :page => @page.title)
|
||||||
redirect_to wiki_page_path(page.permalink)
|
redirect_to wiki_page_path(page.permalink)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -56,7 +57,7 @@ class PagesController < ApplicationController
|
||||||
render :action => 'new'
|
render :action => 'new'
|
||||||
else
|
else
|
||||||
if @page.save
|
if @page.save
|
||||||
flash[:notice] = 'Seite wurde angelegt.'
|
flash[:notice] = I18n.t('pages.create.notice')
|
||||||
redirect_to(wiki_page_path(@page.permalink))
|
redirect_to(wiki_page_path(@page.permalink))
|
||||||
else
|
else
|
||||||
render :action => "new"
|
render :action => "new"
|
||||||
|
@ -75,7 +76,7 @@ class PagesController < ApplicationController
|
||||||
if @page.save
|
if @page.save
|
||||||
@page.parent_id = parent_id if (!params[:parent_id].blank? \
|
@page.parent_id = parent_id if (!params[:parent_id].blank? \
|
||||||
and params[:parent_id] != @page_id)
|
and params[:parent_id] != @page_id)
|
||||||
flash[:notice] = 'Seite wurde aktualisiert.'
|
flash[:notice] = I18n.t('pages.update.notice')
|
||||||
redirect_to wiki_page_path(@page.permalink)
|
redirect_to wiki_page_path(@page.permalink)
|
||||||
else
|
else
|
||||||
render :action => "edit"
|
render :action => "edit"
|
||||||
|
@ -83,7 +84,7 @@ class PagesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
rescue ActiveRecord::StaleObjectError
|
rescue ActiveRecord::StaleObjectError
|
||||||
flash[:error] = "Achtung, die Seite wurde gerade von jemand anderes bearbeitet. Bitte versuche es erneut."
|
flash[:error] = I18n.t('pages.error_stale_object')
|
||||||
redirect_to wiki_page_path(@page.permalink)
|
redirect_to wiki_page_path(@page.permalink)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -91,25 +92,27 @@ class PagesController < ApplicationController
|
||||||
@page = Page.find(params[:id])
|
@page = Page.find(params[:id])
|
||||||
@page.destroy
|
@page.destroy
|
||||||
|
|
||||||
flash[:notice] = "Die Seite '#{@page.title}' und alle Unterseiten wurden erfolgreich gelöscht."
|
flash[:notice] = I18n.t('pages.destroy.notice', :page => @page.title)
|
||||||
redirect_to wiki_path
|
redirect_to wiki_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def all
|
def all
|
||||||
@recent_pages = Page.non_redirected.all :order => 'updated_at DESC'
|
@pages = Page.non_redirected
|
||||||
@pages = Page.non_redirected.all :order => 'title'
|
@partial = params[:view] || 'recent_changes'
|
||||||
@top_pages = Page.no_parent.non_redirected.all :order => 'created_at'
|
|
||||||
|
|
||||||
view = params[:view]
|
if params[:name]
|
||||||
params[:view] = nil
|
@pages = @pages.where("title LIKE ?", "%#{params[:name]}%").limit(20).order('updated_at DESC')
|
||||||
|
@partial = 'title_list'
|
||||||
case view
|
else
|
||||||
when 'recentChanges'
|
order = case @partial
|
||||||
render :partial => 'recent_changes' and return
|
when 'recent_changes' then
|
||||||
when 'siteMap'
|
'updated_at DESC'
|
||||||
render :partial => 'site_map' and return
|
when 'site_map' then
|
||||||
when 'titleList'
|
'created_at DESC'
|
||||||
render :partial => 'title_list' and return
|
when 'title_list' then
|
||||||
|
'title DESC'
|
||||||
|
end
|
||||||
|
@pages.order(order)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
31
app/controllers/sessions_controller.rb
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
class SessionsController < ApplicationController
|
||||||
|
|
||||||
|
skip_before_filter :authenticate
|
||||||
|
layout 'login'
|
||||||
|
|
||||||
|
def new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
user = User.authenticate(params[:nick], params[:password])
|
||||||
|
if user
|
||||||
|
session[:user_id] = user.id
|
||||||
|
session[:scope] = FoodsoftConfig.scope # Save scope in session to not allow switching between foodcoops with one account
|
||||||
|
if session[:return_to].present?
|
||||||
|
redirect_to_url = session[:return_to]
|
||||||
|
session[:return_to] = nil
|
||||||
|
else
|
||||||
|
redirect_to_url = root_url
|
||||||
|
end
|
||||||
|
redirect_to redirect_to_url, :notice => I18n.t('sessions.logged_in')
|
||||||
|
else
|
||||||
|
flash.now.alert = I18n.t('sessions.login_invalid')
|
||||||
|
render "new"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
session[:user_id] = nil
|
||||||
|
redirect_to login_url, :notice => I18n.t('sessions.logged_out')
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,76 +1,21 @@
|
||||||
class StockTakingsController < ApplicationController
|
class StockTakingsController < ApplicationController
|
||||||
|
inherit_resources
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@stock_takings = StockTaking.find(:all)
|
@stock_takings = StockTaking.order('date DESC').page(params[:page]).per(@per_page)
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.html # index.html.erb
|
|
||||||
format.xml { render :xml => @stock_takings }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
|
||||||
@stock_taking = StockTaking.find(params[:id])
|
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.html # show.html.erb
|
|
||||||
format.xml { render :xml => @stock_taking }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@stock_taking = StockTaking.new
|
@stock_taking = StockTaking.new
|
||||||
StockArticle.without_deleted.each { |a| @stock_taking.stock_changes.build(:stock_article => a) }
|
StockArticle.undeleted.each { |a| @stock_taking.stock_changes.build(:stock_article => a) }
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.html # new.html.erb
|
|
||||||
format.xml { render :xml => @stock_taking }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def edit
|
|
||||||
@stock_taking = StockTaking.find(params[:id])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@stock_taking = StockTaking.new(params[:stock_taking])
|
create!(:notice => I18n.t('stock_takings.create.notice'))
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
if @stock_taking.save
|
|
||||||
flash[:notice] = 'StockTaking was successfully created.'
|
|
||||||
format.html { redirect_to(@stock_taking) }
|
|
||||||
format.xml { render :xml => @stock_taking, :status => :created, :location => @stock_taking }
|
|
||||||
else
|
|
||||||
format.html { render :action => "new" }
|
|
||||||
format.xml { render :xml => @stock_taking.errors, :status => :unprocessable_entity }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@stock_taking = StockTaking.find(params[:id])
|
update!(:notice => I18n.t('stock_takings.update.notice'))
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
if @stock_taking.update_attributes(params[:stock_taking])
|
|
||||||
flash[:notice] = 'StockTaking was successfully updated.'
|
|
||||||
format.html { redirect_to(@stock_taking) }
|
|
||||||
format.xml { head :ok }
|
|
||||||
else
|
|
||||||
format.html { render :action => "edit" }
|
|
||||||
format.xml { render :xml => @stock_taking.errors, :status => :unprocessable_entity }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
@stock_taking = StockTaking.find(params[:id])
|
|
||||||
@stock_taking.destroy
|
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.html { redirect_to(stock_takings_url) }
|
|
||||||
format.xml { head :ok }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fill_new_stock_article_form
|
def fill_new_stock_article_form
|
||||||
|
@ -107,6 +52,4 @@ class StockTakingsController < ApplicationController
|
||||||
page.visual_effect :DropOut, "stock_change_#{stock_change.id}"
|
page.visual_effect :DropOut, "stock_change_#{stock_change.id}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
class StockitController < ApplicationController
|
class StockitController < ApplicationController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@stock_articles = StockArticle.without_deleted.all(
|
@stock_articles = StockArticle.undeleted.includes(:supplier, :article_category).
|
||||||
:include => [:supplier, :article_category],
|
order('suppliers.name, article_categories.name, articles.name')
|
||||||
:order => 'suppliers.name, article_categories.name, articles.name'
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
|
@ -14,8 +12,7 @@ class StockitController < ApplicationController
|
||||||
def create
|
def create
|
||||||
@stock_article = StockArticle.new(params[:stock_article])
|
@stock_article = StockArticle.new(params[:stock_article])
|
||||||
if @stock_article.save
|
if @stock_article.save
|
||||||
flash[:notice] = "Lagerartikel wurde gespeichert."
|
redirect_to stock_articles_path, :notice => I18n.t('stockit.stock_create.notice')
|
||||||
redirect_to stock_articles_path
|
|
||||||
else
|
else
|
||||||
render :action => 'new'
|
render :action => 'new'
|
||||||
end
|
end
|
||||||
|
@ -28,31 +25,25 @@ class StockitController < ApplicationController
|
||||||
def update
|
def update
|
||||||
@stock_article = StockArticle.find(params[:id])
|
@stock_article = StockArticle.find(params[:id])
|
||||||
if @stock_article.update_attributes(params[:stock_article])
|
if @stock_article.update_attributes(params[:stock_article])
|
||||||
flash[:notice] = "Lagerartikel wurde gespeichert."
|
redirect_to stock_articles_path, :notice => I18n.t('stockit.stock_update.notice')
|
||||||
redirect_to stock_articles_path
|
|
||||||
else
|
else
|
||||||
render :action => 'edit'
|
render :action => 'edit'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
StockArticle.find(params[:id]).destroy
|
@article = StockArticle.find(params[:id])
|
||||||
redirect_to stock_articles_path
|
@article.mark_as_deleted
|
||||||
|
render :layout => false
|
||||||
rescue => error
|
rescue => error
|
||||||
flash[:error] = "Ein Fehler ist aufgetreten: " + error.message
|
render :partial => "destroy_fail", :layout => false,
|
||||||
redirect_to stock_articles_path
|
:locals => { :fail_msg => I18n.t('errors.general_msg', :msg => error.message) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def auto_complete_for_article_name
|
#TODO: Fix this!!
|
||||||
conditions = [ "LOWER(articles.name) LIKE ?", '%' + params[:article][:name].downcase + '%' ]
|
def articles_search
|
||||||
|
@articles = Article.not_in_stock.limit(8).where('name LIKE ?', "%#{params[:term]}%")
|
||||||
if params[:supplier_id]
|
render :json => @articles.map(&:name)
|
||||||
@supplier = Supplier.find(params[:supplier_id])
|
|
||||||
@articles = @supplier.articles.without_deleted.all(:conditions => conditions, :limit => 8)
|
|
||||||
else
|
|
||||||
@articles = Article.without_deleted.not_in_stock.all(:conditions => conditions, :limit => 8)
|
|
||||||
end
|
|
||||||
render :partial => 'shared/auto_complete_articles'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fill_new_stock_article_form
|
def fill_new_stock_article_form
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
# encoding: utf-8
|
||||||
class SuppliersController < ApplicationController
|
class SuppliersController < ApplicationController
|
||||||
before_filter :authenticate_suppliers, :except => [:index, :list]
|
before_filter :authenticate_suppliers, :except => [:index, :list]
|
||||||
helper :deliveries
|
helper :deliveries
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@suppliers = Supplier.without_deleted :order => 'name'
|
@suppliers = Supplier.undeleted.order(:name)
|
||||||
@deliveries = Delivery.recent
|
@deliveries = Delivery.recent
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ class SuppliersController < ApplicationController
|
||||||
def create
|
def create
|
||||||
@supplier = Supplier.new(params[:supplier])
|
@supplier = Supplier.new(params[:supplier])
|
||||||
if @supplier.save
|
if @supplier.save
|
||||||
flash[:notice] = "Lieferant wurde erstellt"
|
flash[:notice] = I18n.t('suppliers.create.notice')
|
||||||
redirect_to suppliers_path
|
redirect_to suppliers_path
|
||||||
else
|
else
|
||||||
render :action => 'new'
|
render :action => 'new'
|
||||||
|
@ -40,7 +41,7 @@ class SuppliersController < ApplicationController
|
||||||
def update
|
def update
|
||||||
@supplier = Supplier.find(params[:id])
|
@supplier = Supplier.find(params[:id])
|
||||||
if @supplier.update_attributes(params[:supplier])
|
if @supplier.update_attributes(params[:supplier])
|
||||||
flash[:notice] = 'Lieferant wurde aktualisiert'
|
flash[:notice] = I18n.t('suppliers.update.notice')
|
||||||
redirect_to @supplier
|
redirect_to @supplier
|
||||||
else
|
else
|
||||||
render :action => 'edit'
|
render :action => 'edit'
|
||||||
|
@ -49,11 +50,11 @@ class SuppliersController < ApplicationController
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@supplier = Supplier.find(params[:id])
|
@supplier = Supplier.find(params[:id])
|
||||||
@supplier.destroy
|
@supplier.mark_as_deleted
|
||||||
flash[:notice] = "Lieferant wurde gelöscht"
|
flash[:notice] = I18n.t('suppliers.destroy.notice')
|
||||||
redirect_to suppliers_path
|
redirect_to suppliers_path
|
||||||
rescue => e
|
rescue => e
|
||||||
flash[:error] = "Ein Fehler ist aufgetreten: " + e.message
|
flash[:error] = I18n.t('errors.general_msg', :msg => e.message)
|
||||||
redirect_to @supplier
|
redirect_to @supplier
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,25 @@
|
||||||
|
# encoding: utf-8
|
||||||
class TasksController < ApplicationController
|
class TasksController < ApplicationController
|
||||||
#auto_complete_for :user, :nick
|
#auto_complete_for :user, :nick
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@non_group_tasks = Task.non_group
|
@non_group_tasks = Task.non_group.includes(assignments: :user)
|
||||||
@groups = Workgroup.all
|
@groups = Workgroup.includes(open_tasks: {assignments: :user})
|
||||||
end
|
end
|
||||||
|
|
||||||
def user
|
def user
|
||||||
@unaccepted_tasks = @current_user.unaccepted_tasks
|
@unaccepted_tasks = Task.unaccepted_tasks_for(current_user)
|
||||||
@accepted_tasks = @current_user.accepted_tasks
|
@accepted_tasks = Task.accepted_tasks_for(current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@task = Task.new
|
@task = Task.new(current_user_id: current_user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@task = Task.new(params[:task])
|
@task = Task.new(params[:task])
|
||||||
if @task.errors.empty? && @task.save
|
if @task.save
|
||||||
flash[:notice] = "Aufgabe wurde erstellt"
|
redirect_to tasks_url, :notice => I18n.t('tasks.create.notice')
|
||||||
if @task.workgroup
|
|
||||||
redirect_to :action => "workgroup", :id => @task.workgroup
|
|
||||||
else
|
|
||||||
redirect_to :action => "index"
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
render :template => "tasks/new"
|
render :template => "tasks/new"
|
||||||
end
|
end
|
||||||
|
@ -35,17 +31,18 @@ class TasksController < ApplicationController
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
@task = Task.find(params[:id])
|
@task = Task.find(params[:id])
|
||||||
|
@task.current_user_id = current_user.id
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@task = Task.find(params[:id])
|
@task = Task.find(params[:id])
|
||||||
@task.attributes=(params[:task])
|
@task.attributes=(params[:task])
|
||||||
if @task.errors.empty? && @task.save
|
if @task.errors.empty? && @task.save
|
||||||
flash[:notice] = "Aufgabe wurde aktualisiert"
|
flash[:notice] = I18n.t('tasks.update.notice')
|
||||||
if @task.workgroup
|
if @task.workgroup
|
||||||
redirect_to :action => "workgroup", :id => @task.workgroup
|
redirect_to workgroup_tasks_url(workgroup_id: @task.workgroup_id)
|
||||||
else
|
else
|
||||||
redirect_to :action => "index"
|
redirect_to tasks_url
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
render :template => "tasks/edit"
|
render :template => "tasks/edit"
|
||||||
|
@ -53,18 +50,13 @@ class TasksController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
Task.find(params[:id]).destroy
|
task = Task.find(params[:id])
|
||||||
flash[:notice] = "Aufgabe wurde gelöscht"
|
# Save user_ids to update apple statistics after destroy
|
||||||
redirect_to :action => "index"
|
user_ids = task.user_ids
|
||||||
end
|
task.destroy
|
||||||
|
task.update_ordergroup_stats(user_ids)
|
||||||
|
|
||||||
# Delete an given Assignment
|
redirect_to tasks_url, :notice => I18n.t('tasks.destroy.notice')
|
||||||
# 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
|
end
|
||||||
|
|
||||||
# assign current_user to the task and set the assignment to "accepted"
|
# assign current_user to the task and set the assignment to "accepted"
|
||||||
|
@ -76,8 +68,7 @@ class TasksController < ApplicationController
|
||||||
else
|
else
|
||||||
task.assignments.create(:user => current_user, :accepted => true)
|
task.assignments.create(:user => current_user, :accepted => true)
|
||||||
end
|
end
|
||||||
flash[:notice] = "Du hast die Aufgabe übernommen"
|
redirect_to user_tasks_path, :notice => I18n.t('tasks.accept.notice')
|
||||||
redirect_to user_tasks_path
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# deletes assignment between current_user and given task
|
# deletes assignment between current_user and given task
|
||||||
|
@ -86,34 +77,21 @@ class TasksController < ApplicationController
|
||||||
redirect_to :action => "index"
|
redirect_to :action => "index"
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_status
|
def set_done
|
||||||
Task.find(params[:id]).update_attribute("done", params[:task][:done])
|
Task.find(params[:id]).update_attribute :done, true
|
||||||
flash[:notice] = "Aufgabenstatus wurde aktualisiert"
|
redirect_to tasks_url, :notice => I18n.t('tasks.set_done.notice')
|
||||||
redirect_to :action => "index"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Shows all tasks, which are already done
|
# Shows all tasks, which are already done
|
||||||
def archive
|
def archive
|
||||||
@tasks = Task.done.paginate :page => params[:page], :per_page => 30
|
@tasks = Task.done.page(params[:page]).per(@per_page).order('tasks.updated_on DESC').includes(assignments: :user)
|
||||||
end
|
end
|
||||||
|
|
||||||
# shows workgroup (normal group) to edit weekly_tasks_template
|
# shows workgroup (normal group) to edit weekly_tasks_template
|
||||||
def workgroup
|
def workgroup
|
||||||
@group = Group.find(params[:id])
|
@group = Group.find(params[:workgroup_id])
|
||||||
if @group.is_a? Ordergroup
|
if @group.is_a? Ordergroup
|
||||||
flash[:error] = "Keine Arbeitsgruppe gefunden"
|
redirect_to tasks_url, :alert => I18n.t('tasks.error_not_found')
|
||||||
redirect_to :action => "index"
|
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
11
app/controllers/users_controller.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class UsersController < ApplicationController
|
||||||
|
|
||||||
|
# Currently used to display users nick and ids for autocomplete
|
||||||
|
def index
|
||||||
|
@users = User.where("nick LIKE ?", "%#{params[:q]}%")
|
||||||
|
respond_to do |format|
|
||||||
|
format.json { render :json => @users.map { |u| u.token_attributes } }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
34
app/documents/order_by_articles.rb
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
class OrderByArticles < OrderPdf
|
||||||
|
|
||||||
|
def filename
|
||||||
|
I18n.t('documents.order_by_articles.filename', :name => @order.name, :date => @order.ends.to_date) + '.pdf'
|
||||||
|
end
|
||||||
|
|
||||||
|
def title
|
||||||
|
I18n.t('documents.order_by_articles.title', :name => @order.name,
|
||||||
|
:date => @order.ends.strftime(I18n.t('date.formats.default')))
|
||||||
|
end
|
||||||
|
|
||||||
|
def body
|
||||||
|
@order.order_articles.ordered.each do |order_article|
|
||||||
|
text "#{order_article.article.name} (#{order_article.article.unit} | #{order_article.price.unit_quantity.to_s} | #{number_with_precision(order_article.price.fc_price, precision: 2)})",
|
||||||
|
style: :bold, size: 10
|
||||||
|
rows = []
|
||||||
|
rows << I18n.t('documents.order_by_articles.rows')
|
||||||
|
for goa in order_article.group_order_articles
|
||||||
|
rows << [goa.group_order.ordergroup.name,
|
||||||
|
goa.result,
|
||||||
|
number_with_precision(order_article.price.fc_price * goa.result, precision: 2)]
|
||||||
|
end
|
||||||
|
|
||||||
|
table rows, column_widths: [200,40,40], cell_style: {size: 8, overflow: :shrink_to_fit} do |table|
|
||||||
|
table.columns(1..2).align = :right
|
||||||
|
table.cells.border_width = 1
|
||||||
|
table.cells.border_color = '666666'
|
||||||
|
end
|
||||||
|
move_down 10
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
52
app/documents/order_by_groups.rb
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
class OrderByGroups < OrderPdf
|
||||||
|
|
||||||
|
def filename
|
||||||
|
I18n.t('documents.order_by_groups.filename', :name => @order.name, :date => @order.ends.to_date) + '.pdf'
|
||||||
|
end
|
||||||
|
|
||||||
|
def title
|
||||||
|
I18n.t('documents.order_by_groups.title', :name => @order.name,
|
||||||
|
:date => @order.ends.strftime(I18n.t('date.formats.default')))
|
||||||
|
end
|
||||||
|
|
||||||
|
def body
|
||||||
|
# Start rendering
|
||||||
|
@order.group_orders.each do |group_order|
|
||||||
|
text group_order.ordergroup.name, size: 9, style: :bold
|
||||||
|
|
||||||
|
total = 0
|
||||||
|
rows = []
|
||||||
|
rows << I18n.t('documents.order_by_groups.rows') # Table Header
|
||||||
|
|
||||||
|
group_order_articles = group_order.group_order_articles.ordered
|
||||||
|
group_order_articles.each do |goa|
|
||||||
|
price = goa.order_article.price.fc_price
|
||||||
|
sub_total = price * goa.result
|
||||||
|
total += sub_total
|
||||||
|
rows << [goa.order_article.article.name,
|
||||||
|
goa.result,
|
||||||
|
number_with_precision(price, precision: 2),
|
||||||
|
goa.order_article.price.unit_quantity,
|
||||||
|
goa.order_article.article.unit,
|
||||||
|
number_with_precision(sub_total, precision: 2)]
|
||||||
|
end
|
||||||
|
rows << [ I18n.t('documents.order_by_groups.sum'), nil, nil, nil, nil, number_with_precision(total, precision: 2)]
|
||||||
|
|
||||||
|
table rows, column_widths: [250,50,50,50,50,50], cell_style: {size: 8, overflow: :shrink_to_fit} do |table|
|
||||||
|
# borders
|
||||||
|
table.cells.borders = []
|
||||||
|
table.row(0).borders = [:bottom]
|
||||||
|
table.row(group_order_articles.size).borders = [:bottom]
|
||||||
|
table.cells.border_width = 1
|
||||||
|
table.cells.border_color = '666666'
|
||||||
|
|
||||||
|
table.columns(1..3).align = :right
|
||||||
|
table.columns(5).align = :right
|
||||||
|
end
|
||||||
|
|
||||||
|
move_down 15
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
72
app/documents/order_fax.rb
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
class OrderFax < OrderPdf
|
||||||
|
|
||||||
|
def filename
|
||||||
|
I18n.t('documents.order_fax.filename', :name => @order.name, :date => @order.ends.to_date) + '.pdf'
|
||||||
|
end
|
||||||
|
|
||||||
|
def title
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def body
|
||||||
|
contact = FoodsoftConfig[:contact].symbolize_keys
|
||||||
|
|
||||||
|
# From paragraph
|
||||||
|
bounding_box [margin_box.right-200,margin_box.top], width: 200 do
|
||||||
|
text FoodsoftConfig[:name], size: 9, align: :right
|
||||||
|
move_down 5
|
||||||
|
text contact[:street], size: 9, align: :right
|
||||||
|
move_down 5
|
||||||
|
text "#{contact[:zip_code]} #{contact[:city]}", size: 9, align: :right
|
||||||
|
move_down 5
|
||||||
|
text "#{I18n.t('simple_form.labels.supplier.customer_number')}: #{@order.supplier.try(:customer_number)}", size: 9, align: :right
|
||||||
|
move_down 5
|
||||||
|
text "#{I18n.t('simple_form.labels.supplier.phone')}: #{contact[:phone]}", size: 9, align: :right
|
||||||
|
move_down 5
|
||||||
|
text "#{I18n.t('simple_form.labels.supplier.email')}: #{contact[:email]}", size: 9, align: :right
|
||||||
|
end
|
||||||
|
|
||||||
|
# Recipient
|
||||||
|
bounding_box [margin_box.left,margin_box.top-60], width: 200 do
|
||||||
|
text @order.name
|
||||||
|
move_down 5
|
||||||
|
text @order.supplier.try(:address).to_s
|
||||||
|
move_down 5
|
||||||
|
text "#{I18n.t('simple_form.labels.supplier.fax')}: #{@order.supplier.try(:fax)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
move_down 5
|
||||||
|
text Date.today.strftime(I18n.t('date.formats.default')), align: :right
|
||||||
|
|
||||||
|
move_down 10
|
||||||
|
text "#{I18n.t('simple_form.labels.delivery.delivered_on')}:"
|
||||||
|
move_down 10
|
||||||
|
text "#{I18n.t('simple_form.labels.supplier.contact_person')}: #{@order.supplier.try(:contact_person)}"
|
||||||
|
move_down 10
|
||||||
|
|
||||||
|
# Articles
|
||||||
|
data = [I18n.t('documents.order_fax.rows')]
|
||||||
|
data = @order.order_articles.ordered.all(include: :article).collect do |a|
|
||||||
|
[a.article.order_number,
|
||||||
|
a.units_to_order,
|
||||||
|
a.article.name,
|
||||||
|
a.price.unit_quantity,
|
||||||
|
a.article.unit,
|
||||||
|
a.price.price]
|
||||||
|
end
|
||||||
|
table data, cell_style: {size: 8, overflow: :shrink_to_fit} do |table|
|
||||||
|
table.cells.border_width = 1
|
||||||
|
table.cells.border_color = '666666'
|
||||||
|
|
||||||
|
table.columns(1).align = :right
|
||||||
|
table.columns(3..5).align = :right
|
||||||
|
end
|
||||||
|
#font_size: 8,
|
||||||
|
#vertical_padding: 3,
|
||||||
|
#border_style: :grid,
|
||||||
|
#headers: ["BestellNr.", "Menge","Name", "Gebinde", "Einheit","Preis/Einheit"],
|
||||||
|
#align: {0 => :left}
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
87
app/documents/order_matrix.rb
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
class OrderMatrix < OrderPdf
|
||||||
|
|
||||||
|
MAX_ARTICLES_PER_PAGE = 16 # How many order_articles shoud written on a page
|
||||||
|
|
||||||
|
def filename
|
||||||
|
I18n.t('documents.order_matrix.filename', :name => @order.name, :date => @order.ends.to_date) + '.pdf'
|
||||||
|
end
|
||||||
|
|
||||||
|
def title
|
||||||
|
I18n.t('documents.order_matrix.title', :name => @order.name,
|
||||||
|
:date => @order.ends.strftime(I18n.t('date.formats.default')))
|
||||||
|
end
|
||||||
|
|
||||||
|
def body
|
||||||
|
order_articles = @order.order_articles.ordered
|
||||||
|
|
||||||
|
text I18n.t('documents.order_matrix.heading'), style: :bold
|
||||||
|
move_down 5
|
||||||
|
text I18n.t('documents.order_matrix.total', :count => order_articles.size), size: 8
|
||||||
|
move_down 10
|
||||||
|
|
||||||
|
order_articles_data = [I18n.t('documents.order_matrix.rows')]
|
||||||
|
|
||||||
|
order_articles.each do |a|
|
||||||
|
order_articles_data << [a.article.name,
|
||||||
|
a.article.unit,
|
||||||
|
a.price.unit_quantity,
|
||||||
|
number_with_precision(a.price.fc_price, precision: 2),
|
||||||
|
a.units_to_order]
|
||||||
|
end
|
||||||
|
|
||||||
|
table order_articles_data, cell_style: {size: 8, overflow: :shrink_to_fit} do |table|
|
||||||
|
table.cells.border_width = 1
|
||||||
|
table.cells.border_color = '666666'
|
||||||
|
end
|
||||||
|
|
||||||
|
page_number = 0
|
||||||
|
total_num_order_articles = order_articles.size
|
||||||
|
|
||||||
|
while page_number * MAX_ARTICLES_PER_PAGE < total_num_order_articles do # Start page generating
|
||||||
|
|
||||||
|
page_number += 1
|
||||||
|
start_new_page(layout: :landscape)
|
||||||
|
|
||||||
|
# Collect order_articles for this page
|
||||||
|
current_order_articles = order_articles.select do |a|
|
||||||
|
order_articles.index(a) >= (page_number-1) * MAX_ARTICLES_PER_PAGE and
|
||||||
|
order_articles.index(a) < page_number * MAX_ARTICLES_PER_PAGE
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make order_articles header
|
||||||
|
header = [""]
|
||||||
|
for header_article in current_order_articles
|
||||||
|
name = header_article.article.name.gsub(/[-\/]/, " ").gsub(".", ". ")
|
||||||
|
name = name.split.collect { |w| w.truncate(8) }.join(" ")
|
||||||
|
header << name.truncate(30)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Collect group results
|
||||||
|
groups_data = [header]
|
||||||
|
|
||||||
|
@order.group_orders.includes(:ordergroup).all.each do |group_order|
|
||||||
|
|
||||||
|
group_result = [group_order.ordergroup.name.truncate(20)]
|
||||||
|
|
||||||
|
for order_article in current_order_articles
|
||||||
|
# get the Ordergroup result for this order_article
|
||||||
|
goa = order_article.group_order_articles.first conditions: { group_order_id: group_order.id }
|
||||||
|
group_result << ((goa.nil? || goa.result == 0) ? "" : goa.result.to_i)
|
||||||
|
end
|
||||||
|
groups_data << group_result
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make table
|
||||||
|
column_widths = [85]
|
||||||
|
(MAX_ARTICLES_PER_PAGE + 1).times { |i| column_widths << 41 unless i == 0 }
|
||||||
|
table groups_data, column_widths: column_widths, cell_style: {size: 8, overflow: :shrink_to_fit} do |table|
|
||||||
|
table.cells.border_width = 1
|
||||||
|
table.cells.border_color = '666666'
|
||||||
|
table.row_colors = ['ffffff','ececec']
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,3 +1,5 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
#
|
||||||
# Methods added to this helper will be available to all templates in the application.
|
# Methods added to this helper will be available to all templates in the application.
|
||||||
module ApplicationHelper
|
module ApplicationHelper
|
||||||
|
|
||||||
|
@ -18,97 +20,76 @@ module ApplicationHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates ajax-controlled-links for pagination
|
# Creates ajax-controlled-links for pagination
|
||||||
# see also the plugin "will_paginate"
|
|
||||||
def pagination_links_remote(collection, options = {})
|
def pagination_links_remote(collection, options = {})
|
||||||
per_page = options[:per_page] || @per_page
|
per_page = options[:per_page] || @per_page
|
||||||
params = options[:params] || {}
|
params = options[:params] || {}
|
||||||
update = options[:update] || nil
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
previous_label = '« ' + "Vorherige"
|
|
||||||
next_label = "Nächste" + ' »'
|
|
||||||
# Merge other url-options for will_paginate
|
|
||||||
params = params.merge({:per_page => per_page})
|
params = params.merge({:per_page => per_page})
|
||||||
will_paginate collection, { :params => params, :remote => true, :update => update,
|
paginate collection, :params => params, :remote => true
|
||||||
:previous_label => previous_label, :next_label => next_label, }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Link-collection for per_page-options when using the pagination-plugin
|
# Link-collection for per_page-options when using the pagination-plugin
|
||||||
def items_per_page(options = {})
|
def items_per_page(options = {})
|
||||||
per_page_options = options[:per_page_options] || [20, 50, 100]
|
per_page_options = options[:per_page_options] || [20, 50, 100]
|
||||||
current = options[:current] || @per_page
|
current = options[:current] || @per_page
|
||||||
action = options[:action] || controller.action_name
|
params = params || {}
|
||||||
update = options[:update] || nil
|
|
||||||
|
|
||||||
links = []
|
links = per_page_options.map do |per_page|
|
||||||
per_page_options.each do |per_page|
|
params.merge!({:per_page => per_page})
|
||||||
unless per_page == current
|
link_class = 'btn'
|
||||||
links << link_to_remote(
|
link_class << ' disabled' if per_page == current
|
||||||
per_page,
|
link_to(per_page, params, :remote => true, class: link_class)
|
||||||
{ :url => { :action => action, :params => {:per_page => per_page}},
|
end
|
||||||
:before => "Element.show('loader')",
|
|
||||||
:success => "Element.hide('loader')",
|
content_tag :div, class: 'btn-group pull-right' do
|
||||||
:method => :get, :update => update } )
|
links.join.html_safe
|
||||||
else
|
|
||||||
links << per_page
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return "Pro Seite: " + links.join(" ")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def sort_td_class_helper(param)
|
|
||||||
result = 'class="sortup"' if params[:sort] == param
|
|
||||||
result = 'class="sortdown"' if params[:sort] == param + "_reverse"
|
|
||||||
return result
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def sort_link_helper(text, key, options = {})
|
def sort_link_helper(text, key, options = {})
|
||||||
per_page = options[:per_page] || 10
|
# Hmtl options
|
||||||
action = options[:action] || "list"
|
|
||||||
remote = options[:remote].nil? ? true : options[:remote]
|
remote = options[:remote].nil? ? true : options[:remote]
|
||||||
key += "_reverse" if params[:sort] == key
|
class_name = case params[:sort]
|
||||||
|
when key then
|
||||||
link_options = {
|
'sortup'
|
||||||
:url => url_for(:params => params.merge({:sort => key, :page => nil, :per_page => per_page})),
|
when key + '_reverse' then
|
||||||
:before => "Element.show('loader')",
|
'sortdown'
|
||||||
:success => "Element.hide('loader')",
|
|
||||||
:method => :get
|
|
||||||
}
|
|
||||||
html_options = {
|
|
||||||
:title => _("Nach #{text} sortieren"),
|
|
||||||
:href => url_for(:action => action, :params => params.merge({:sort => key, :page => nil, :per_page => per_page}))
|
|
||||||
}
|
|
||||||
|
|
||||||
if remote
|
|
||||||
link_to_remote(text, link_options, html_options)
|
|
||||||
else
|
else
|
||||||
link_to(text, link_options[:url], html_options)
|
nil
|
||||||
end
|
end
|
||||||
|
html_options = {
|
||||||
|
:title => I18n.t('helpers.application.sort_by', text: text),
|
||||||
|
:remote => remote,
|
||||||
|
:class => class_name
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Url options
|
||||||
|
key += "_reverse" if params[:sort] == key
|
||||||
|
per_page = options[:per_page] || @per_page
|
||||||
|
url_options = params.merge(per_page: per_page, sort: key)
|
||||||
|
url_options.merge!({page: params[:page]}) if params[:page]
|
||||||
|
url_options.merge!({query: params[:query]}) if params[:query]
|
||||||
|
|
||||||
|
link_to(text, url_for(url_options), html_options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Generates a link to the top of the website
|
# Generates a link to the top of the website
|
||||||
def link_to_top
|
def link_to_top
|
||||||
link_to image_tag("arrow_up_red.png", :size => "16x16", :border => "0", :alt => "Nach oben"), "#"
|
link_to '#' do
|
||||||
|
content_tag :i, nil, class: 'icon-arrow-up icon-large'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the weekday. 0 is sunday, 1 is monday and so on
|
# Returns the weekday. 0 is sunday, 1 is monday and so on
|
||||||
def weekday(dayNumber)
|
def weekday(dayNumber)
|
||||||
weekdays = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"]
|
weekdays = I18n.t('date.day_names')
|
||||||
return weekdays[dayNumber]
|
return weekdays[dayNumber]
|
||||||
end
|
end
|
||||||
|
|
||||||
# highlights a phrase in given text
|
|
||||||
# based on the rails text-helper 'highlight'
|
|
||||||
def highlight_phrase(text, phrase, highlighter = '<strong class="highlight">\1</strong>')
|
|
||||||
unless phrase.blank? || text.nil?
|
|
||||||
phrase.split(' ').each {|keyword| text.gsub!(/(#{Regexp.escape(keyword)})/i, highlighter)}
|
|
||||||
end
|
|
||||||
return text
|
|
||||||
end
|
|
||||||
|
|
||||||
# to set a title for both the h1-tag and the title in the header
|
# to set a title for both the h1-tag and the title in the header
|
||||||
def title(page_title, show_title = true)
|
def title(page_title, show_title = true)
|
||||||
@content_for_title = page_title.to_s
|
content_for(:title) { page_title.to_s }
|
||||||
@show_title = show_title
|
@show_title = show_title
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -121,8 +102,11 @@ module ApplicationHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def icon(name, options={})
|
def icon(name, options={})
|
||||||
icons = { :delete => { :file => 'b_drop.png', :alt => 'Löschen'},
|
icons = {
|
||||||
:edit => { :file => 'b_edit.png', :alt => 'Bearbeiten'}}
|
:delete => { :file => 'b_drop.png', :alt => I18n.t('ui.delete')},
|
||||||
|
:edit => { :file => 'b_edit.png', :alt => I18n.t('ui.edit')},
|
||||||
|
:members => { :file => 'b_users.png', :alt => I18n.t('helpers.application.edit_user')}
|
||||||
|
}
|
||||||
options[:alt] ||= icons[name][:alt]
|
options[:alt] ||= icons[name][:alt]
|
||||||
options[:title] ||= icons[name][:title]
|
options[:title] ||= icons[name][:title]
|
||||||
options.merge!({:size => '16x16',:border => "0"})
|
options.merge!({:size => '16x16',:border => "0"})
|
||||||
|
@ -137,28 +121,42 @@ module ApplicationHelper
|
||||||
:success => "Element.hide('loader')",
|
:success => "Element.hide('loader')",
|
||||||
:method => :get
|
:method => :get
|
||||||
}
|
}
|
||||||
link_to_remote(text, remote_options.merge(options))
|
link_to(text, options[:url], remote_options.merge(options))
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_roles(record)
|
def format_roles(record)
|
||||||
roles = []
|
roles = []
|
||||||
roles << 'Admin' if record.role_admin?
|
roles << I18n.t('helpers.application.role_admin') if record.role_admin?
|
||||||
roles << 'Finanzen' if record.role_finance?
|
roles << I18n.t('helpers.application.role_finance') if record.role_finance?
|
||||||
roles << 'Lieferanten' if record.role_suppliers?
|
roles << I18n.t('helpers.application.role_suppliers') if record.role_suppliers?
|
||||||
roles << 'Artikel' if record.role_article_meta?
|
roles << I18n.t('helpers.application.role_article_meta') if record.role_article_meta?
|
||||||
roles << 'Bestellung' if record.role_orders?
|
roles << I18n.t('helpers.application.role_orders') if record.role_orders?
|
||||||
roles.join(', ')
|
roles.join(', ')
|
||||||
end
|
end
|
||||||
|
|
||||||
def link_to_gmaps(address)
|
def link_to_gmaps(address)
|
||||||
link_to h(address), "http://maps.google.de/?q=#{h(address)}", :title => "Show it on google maps",
|
link_to h(address), "http://maps.google.com/?q=#{h(address)}", :title => I18n.t('helpers.application.show_google_maps'),
|
||||||
:target => "_blank"
|
:target => "_blank"
|
||||||
end
|
end
|
||||||
|
|
||||||
# offers a link for writing message to user
|
# offers a link for writing message to user
|
||||||
# checks for nil (useful for relations)
|
# checks for nil (useful for relations)
|
||||||
def link_to_user_message_if_valid(user)
|
def link_to_user_message_if_valid(user)
|
||||||
user.nil? ? '??' : ( link_to user.nick, user_message_path(user), :title => _('Nachricht schreiben') )
|
user.nil? ? '??' : link_to(user.nick, new_message_path('message[mail_to]' => user.id),
|
||||||
|
:title => I18n.t('helpers.application.write_message'))
|
||||||
|
end
|
||||||
|
|
||||||
|
def bootstrap_flash
|
||||||
|
flash_messages = []
|
||||||
|
flash.each do |type, message|
|
||||||
|
type = :success if type == :notice
|
||||||
|
type = :error if type == :alert
|
||||||
|
text = content_tag(:div,
|
||||||
|
content_tag(:button, I18n.t('ui.marks.close').html_safe, :class => "close", "data-dismiss" => "alert") +
|
||||||
|
message, :class => "alert fade in alert-#{type}")
|
||||||
|
flash_messages << text if message
|
||||||
|
end
|
||||||
|
flash_messages.join("\n").html_safe
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,9 +6,14 @@ module ArticlesHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def row_classes(article)
|
def row_classes(article)
|
||||||
classes = " click-me"
|
classes = []
|
||||||
classes += " unavailable" if !article.availability
|
classes << "unavailable" if !article.availability
|
||||||
classes += " just_updated" if @article.recently_updated && @article.availability
|
classes << "just-updated" if article.recently_updated && article.availability
|
||||||
classes
|
classes.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Flatten search params, used in import from external database
|
||||||
|
def search_params
|
||||||
|
Hash[params[:search].map { |k,v| [k, (v.is_a?(Array) ? v.join(" ") : v)] }]
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -3,14 +3,15 @@ module DeliveriesHelper
|
||||||
def link_to_invoice(delivery)
|
def link_to_invoice(delivery)
|
||||||
if delivery.invoice
|
if delivery.invoice
|
||||||
link_to number_to_currency(delivery.invoice.amount), [:finance, delivery.invoice],
|
link_to number_to_currency(delivery.invoice.amount), [:finance, delivery.invoice],
|
||||||
:title => "Rechnung anzeigen"
|
title: I18n.t('helpers.deliveries.show_invoice')
|
||||||
else
|
else
|
||||||
link_to "Rechnung anlegen", new_finance_invoice_path(:supplier_id => delivery.supplier.id, :delivery_id => delivery.id)
|
link_to I18n.t('helpers.deliveries.new_invoice'), new_finance_invoice_path(supplier_id: delivery.supplier.id, delivery_id: delivery.id),
|
||||||
|
class: 'btn btn-mini'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def stock_articles_for_select(supplier)
|
def stock_articles_for_select(supplier)
|
||||||
supplier.stock_articles.without_deleted.collect {|a| ["#{a.name} (#{number_to_currency a.price}/#{a.unit})", a.id] }
|
supplier.stock_articles.undeleted.map {|a| ["#{a.name} (#{number_to_currency a.price}/#{a.unit})", a.id] }
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
13
app/helpers/finance/balancing_helper.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module Finance::BalancingHelper
|
||||||
|
def balancing_view_partial
|
||||||
|
view = params[:view] || 'edit_results'
|
||||||
|
case view
|
||||||
|
when 'edit_results' then
|
||||||
|
'edit_results_by_articles'
|
||||||
|
when 'groups_overview' then
|
||||||
|
'shared/articles_by_groups'
|
||||||
|
when 'articles_overview' then
|
||||||
|
'shared/articles_by_articles'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
10
app/helpers/finance/order_articles_helper.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module Finance::OrderArticlesHelper
|
||||||
|
|
||||||
|
def new_order_articles_collection
|
||||||
|
if @order.stockit?
|
||||||
|
StockArticle.order('articles.name')
|
||||||
|
else
|
||||||
|
@order.supplier.articles.order('articles.name')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
2
app/helpers/finance/ordergroups_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module Finance::OrdergroupsHelper
|
||||||
|
end
|
39
app/helpers/group_orders_helper.rb
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
module GroupOrdersHelper
|
||||||
|
def data_to_js(ordering_data)
|
||||||
|
ordering_data[:order_articles].map { |id, data|
|
||||||
|
[id, data[:price], data[:unit], data[:total_price], data[:others_quantity], data[:others_tolerance], data[:used_quantity], data[:quantity_available]]
|
||||||
|
}.map { |row|
|
||||||
|
"addData(#{row.join(', ')});"
|
||||||
|
}.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_to_ordering(order, options = {})
|
||||||
|
path = if group_order = order.group_order(current_user.ordergroup)
|
||||||
|
edit_group_order_path(group_order, :order_id => order.id)
|
||||||
|
else
|
||||||
|
new_group_order_path(:order_id => order.id)
|
||||||
|
end
|
||||||
|
link_to order.name, path, options
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return css class names for order result table
|
||||||
|
|
||||||
|
def order_article_class_name(quantity, tolerance, result)
|
||||||
|
if (quantity + tolerance > 0)
|
||||||
|
result > 0 ? 'success' : 'failed'
|
||||||
|
else
|
||||||
|
'ignored'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_order_results(order_article, group_order_id)
|
||||||
|
goa = order_article.group_order_articles.detect { |goa| goa.group_order_id == group_order_id }
|
||||||
|
quantity, tolerance, result, sub_total = if goa.present?
|
||||||
|
[goa.quantity, goa.tolerance, goa.result, goa.total_price(order_article)]
|
||||||
|
else
|
||||||
|
[0, 0, 0, 0]
|
||||||
|
end
|
||||||
|
|
||||||
|
{group_order_article: goa, quantity: quantity, tolerance: tolerance, result: result, sub_total: sub_total}
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,16 +1,4 @@
|
||||||
module MessagesHelper
|
module MessagesHelper
|
||||||
def groups_for_select
|
|
||||||
groups = [[" -- Arbeitsgruppen -- ", ""]]
|
|
||||||
groups += Workgroup.find(:all, :order => 'name', :include => :memberships).reject{ |g| g.memberships.empty? }.collect do |g|
|
|
||||||
[g.name, g.id]
|
|
||||||
end
|
|
||||||
groups += [[" -- Bestellgruppen -- ", ""]]
|
|
||||||
groups += Ordergroup.without_deleted(:order => 'name', :include => :memberships).reject{ |g| g.memberships.empty? }.collect do |g|
|
|
||||||
[g.name, g.id]
|
|
||||||
end
|
|
||||||
groups
|
|
||||||
end
|
|
||||||
|
|
||||||
def format_subject(message, length)
|
def format_subject(message, length)
|
||||||
if message.subject.length > length
|
if message.subject.length > length
|
||||||
subject = truncate(message.subject, :length => length)
|
subject = truncate(message.subject, :length => length)
|
||||||
|
@ -19,6 +7,14 @@ module MessagesHelper
|
||||||
subject = message.subject
|
subject = message.subject
|
||||||
body = truncate(message.body, :length => length - subject.length)
|
body = truncate(message.body, :length => length - subject.length)
|
||||||
end
|
end
|
||||||
"<b>#{link_to(h(subject), message)}</b> <span style='color:grey'>#{h(body)}</span>"
|
"<b>#{link_to(h(subject), message)}</b> <span style='color:grey'>#{h(body)}</span>".html_safe
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_to_new_message(options = {})
|
||||||
|
messages_params = options[:message_params] || nil
|
||||||
|
link_text = content_tag :id, nil, class: 'icon-envelope'
|
||||||
|
link_text << " #{options[:text]}" if options[:text].present?
|
||||||
|
link_to(link_text.html_safe, new_message_path(message: messages_params), class: 'btn',
|
||||||
|
title: I18n.t('helpers.submit.message.create'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
2
app/helpers/order_comments_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module OrderCommentsHelper
|
||||||
|
end
|
|
@ -1,19 +1,18 @@
|
||||||
|
# encoding: utf-8
|
||||||
module OrdersHelper
|
module OrdersHelper
|
||||||
|
|
||||||
def update_articles_link(order, text, view)
|
def update_articles_link(order, text, view)
|
||||||
link_to_remote text, :url => order_path(order, :view => view),
|
link_to text, order_path(order, view: view), remote: true
|
||||||
:update => 'articles', :before => "Element.show('loader')", :success => "Element.hide('loader')",
|
|
||||||
:method => :get
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def link_to_pdf(order, action)
|
def order_pdf(order, document, text)
|
||||||
link_to image_tag("save_pdf.png", :size => "16x16", :border => "0", :alt => "PDF erstellen"),
|
link_to text, order_path(order, document: document, format: :pdf), title: I18n.t('helpers.orders.order_pdf')
|
||||||
{ :action => action, :id => order, :format => :pdf }, { :title => "PDF erstellen" }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def options_for_suppliers_to_select
|
def options_for_suppliers_to_select
|
||||||
suppliers = Supplier.without_deleted.collect {|s| [ s.name, url_for(:action => "new", :supplier_id => s)] }
|
options = [[I18n.t('helpers.orders.option_choose')]]
|
||||||
stockit = [["Lager", url_for(:action => 'new', :supplier_id => 0)]]
|
options += Supplier.all.map {|s| [ s.name, url_for(action: "new", supplier_id: s)] }
|
||||||
options_for_select(stockit + suppliers)
|
options += [[I18n.t('helpers.orders.option_stock'), url_for(action: 'new', supplier_id: 0)]]
|
||||||
|
options_for_select(options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,14 +2,14 @@ module PagesHelper
|
||||||
include WikiCloth
|
include WikiCloth
|
||||||
|
|
||||||
def wikified_body(body, title = nil)
|
def wikified_body(body, title = nil)
|
||||||
WikiCloth.new({:data => body+"\n", :link_handler => Wikilink.new, :params => {:referer => title}}).to_html
|
WikiCloth.new({:data => body+"\n", :link_handler => Wikilink.new, :params => {:referer => title}}).to_html.html_safe
|
||||||
end
|
end
|
||||||
|
|
||||||
def link_to_wikipage(page, text = nil)
|
def link_to_wikipage(page, text = nil)
|
||||||
if text == nil
|
if text == nil
|
||||||
link_to page.title, wiki_page_path(page.permalink)
|
link_to page.title, wiki_page_path(:permalink => page.permalink)
|
||||||
else
|
else
|
||||||
link_to text, wiki_page_path(page.permalink)
|
link_to text, wiki_page_path(:permalink => page.permalink)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -41,11 +41,11 @@ module PagesHelper
|
||||||
unless toc.blank?
|
unless toc.blank?
|
||||||
toc = WikiCloth.new({:data => toc, :link_handler => Wikilink.new}).to_html
|
toc = WikiCloth.new({:data => toc, :link_handler => Wikilink.new}).to_html
|
||||||
|
|
||||||
section_count = 0
|
|
||||||
toc.gsub(/<li>([^<>\n]*)/) do
|
toc.gsub(/<li>([^<>\n]*)/) do
|
||||||
section_count += 1
|
name = $1
|
||||||
"<li><a href='#section-#{section_count}'>#{$1}</a>"
|
anchor = name.gsub(/\s/, '_').gsub(/[^a-zA-Z_]/, '')
|
||||||
end
|
"<li><a href='##{anchor}'>#{name.truncate(20)}</a>"
|
||||||
|
end.html_safe
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
2
app/helpers/sessions_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module SessionsHelper
|
||||||
|
end
|
|
@ -1,2 +1,7 @@
|
||||||
module StockitHelper
|
module StockitHelper
|
||||||
|
def stock_article_classes(article)
|
||||||
|
class_names = []
|
||||||
|
class_names << "unavailable" if article.quantity_available <= 0
|
||||||
|
class_names.join(" ")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
module TasksHelper
|
module TasksHelper
|
||||||
|
|
||||||
|
def task_assignments(task)
|
||||||
|
task.assignments.map do |ass|
|
||||||
|
content_tag :span, ass.user.nick, :class => (ass.accepted? ? 'accepted' : 'unaccepted')
|
||||||
|
end.join(", ").html_safe
|
||||||
|
end
|
||||||
|
|
||||||
# generate colored number of still required users
|
# generate colored number of still required users
|
||||||
def highlighted_required_users(task)
|
def highlighted_required_users(task)
|
||||||
unless task.enough_users_assigned?
|
unless task.enough_users_assigned?
|
||||||
still_required = task.required_users - task.assignments.select { |ass| ass.accepted }.size
|
content_tag :span, task.still_required_users, class: 'badge badge-important',
|
||||||
"<small style='color:red;font-weight:bold'>(#{still_required})</small>"
|
title: I18n.t('helpers.tasks.required_users', :count => task.still_required_users)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
2
app/helpers/users_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module UsersHelper
|
||||||
|
end
|
5
app/inputs/date_picker_input.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
class DatePickerInput < SimpleForm::Inputs::StringInput
|
||||||
|
def input
|
||||||
|
@builder.text_field(attribute_name, input_html_options.merge({class: 'datepicker'}))
|
||||||
|
end
|
||||||
|
end
|
101
app/mailers/mailer.rb
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
# ActionMailer class that handles all emails for the FoodSoft.
|
||||||
|
class Mailer < ActionMailer::Base
|
||||||
|
|
||||||
|
layout 'email' # Use views/layouts/email.txt.erb
|
||||||
|
|
||||||
|
default from: "FoodSoft <#{FoodsoftConfig[:email_sender]}>",
|
||||||
|
sender: FoodsoftConfig[:email_sender],
|
||||||
|
errors_to: FoodsoftConfig[:email_sender]
|
||||||
|
|
||||||
|
# Sends an email copy of the given internal foodsoft message.
|
||||||
|
def foodsoft_message(message, recipient)
|
||||||
|
set_foodcoop_scope
|
||||||
|
@message = message
|
||||||
|
|
||||||
|
mail subject: "[#{FoodsoftConfig[:name]}] " + message.subject,
|
||||||
|
to: recipient.email,
|
||||||
|
from: "#{message.sender.nick} <#{message.sender.email}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sends an email with instructions on how to reset the password.
|
||||||
|
# Assumes user.setResetPasswordToken has been successfully called already.
|
||||||
|
def reset_password(user)
|
||||||
|
set_foodcoop_scope
|
||||||
|
@user = user
|
||||||
|
@link = new_password_url(id: @user.id, token: @user.reset_password_token)
|
||||||
|
|
||||||
|
mail :to => @user.email,
|
||||||
|
:subject => "[#{FoodsoftConfig[:name]}] " + I18n.t('mailer.reset_password.subject', :username => @user.nick)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sends an invite email.
|
||||||
|
def invite(invite)
|
||||||
|
set_foodcoop_scope
|
||||||
|
@invite = invite
|
||||||
|
@link = accept_invitation_url(token: @invite.token)
|
||||||
|
|
||||||
|
mail :to => @invite.email,
|
||||||
|
:subject => I18n.t('mailer.invite.subject')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Notify user of upcoming task.
|
||||||
|
def upcoming_tasks(user, task)
|
||||||
|
set_foodcoop_scope
|
||||||
|
@user = user
|
||||||
|
@task = task
|
||||||
|
|
||||||
|
mail :to => user.email,
|
||||||
|
:subject => "[#{FoodsoftConfig[:name]}] " + I18n.t('mailer.upcoming_tasks.subject')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sends order result for specific Ordergroup
|
||||||
|
def order_result(user, group_order)
|
||||||
|
set_foodcoop_scope
|
||||||
|
@order = group_order.order
|
||||||
|
@group_order = group_order
|
||||||
|
|
||||||
|
mail :to => user.email,
|
||||||
|
:subject => "[#{FoodsoftConfig[:name]}] " + I18n.t('mailer.order_result.subject', :name => group_order.order.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Notify user if account balance is less than zero
|
||||||
|
def negative_balance(user,transaction)
|
||||||
|
set_foodcoop_scope
|
||||||
|
@group = user.ordergroup
|
||||||
|
@transaction = transaction
|
||||||
|
|
||||||
|
mail :to => user.email,
|
||||||
|
:subject => "[#{FoodsoftConfig[:name]}] " + I18n.t('mailer.negative_balance')
|
||||||
|
end
|
||||||
|
|
||||||
|
def feedback(user, feedback)
|
||||||
|
set_foodcoop_scope
|
||||||
|
@user = user
|
||||||
|
@feedback = feedback
|
||||||
|
|
||||||
|
mail :to => FoodsoftConfig[:notification]["error_recipients"],
|
||||||
|
:from => "#{user.nick} <#{user.email}>",
|
||||||
|
:sender => FoodsoftConfig[:notification]["sender_address"],
|
||||||
|
:errors_to => FoodsoftConfig[:notification]["sender_address"],
|
||||||
|
:subject => "[#{FoodsoftConfig[:name]}] " + I18n.t('mailer.feedback.subject', :email => user.email)
|
||||||
|
end
|
||||||
|
|
||||||
|
def not_enough_users_assigned(task, user)
|
||||||
|
set_foodcoop_scope
|
||||||
|
@task = task
|
||||||
|
@user = user
|
||||||
|
|
||||||
|
mail :to => user.email,
|
||||||
|
:subject => "[#{FoodsoftConfig[:name]}] " + I18n.t('mailer.not_enough_users_assigned.subject', :task => task.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_foodcoop_scope(foodcoop = FoodsoftConfig.scope)
|
||||||
|
ActionMailer::Base.default_url_options[:protocol] = FoodsoftConfig[:protocol]
|
||||||
|
ActionMailer::Base.default_url_options[:host] = FoodsoftConfig[:host]
|
||||||
|
ActionMailer::Base.default_url_options[:foodcoop] = foodcoop
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,46 +1,25 @@
|
||||||
# == Schema Information
|
# encoding: utf-8
|
||||||
#
|
|
||||||
# Table name: articles
|
|
||||||
#
|
|
||||||
# id :integer not null, primary key
|
|
||||||
# name :string(255) default(""), not null
|
|
||||||
# supplier_id :integer default(0), not null
|
|
||||||
# article_category_id :integer default(0), not null
|
|
||||||
# unit :string(255) default(""), not null
|
|
||||||
# note :string(255)
|
|
||||||
# availability :boolean default(TRUE), not null
|
|
||||||
# manufacturer :string(255)
|
|
||||||
# origin :string(255)
|
|
||||||
# shared_updated_on :datetime
|
|
||||||
# price :decimal(, )
|
|
||||||
# tax :float
|
|
||||||
# deposit :decimal(, ) default(0.0)
|
|
||||||
# unit_quantity :integer default(1), not null
|
|
||||||
# order_number :string(255)
|
|
||||||
# created_at :datetime
|
|
||||||
# updated_at :datetime
|
|
||||||
# quantity :integer default(0)
|
|
||||||
# deleted_at :datetime
|
|
||||||
# type :string(255)
|
|
||||||
#
|
|
||||||
|
|
||||||
class Article < ActiveRecord::Base
|
class Article < ActiveRecord::Base
|
||||||
acts_as_paranoid # Avoid deleting the article for consistency of order-results
|
|
||||||
extend ActiveSupport::Memoizable # Ability to cache method results. Use memoize :expensive_method
|
extend ActiveSupport::Memoizable # Ability to cache method results. Use memoize :expensive_method
|
||||||
|
|
||||||
|
# Replace numeric seperator with database format
|
||||||
|
localize_input_of :price, :tax, :deposit
|
||||||
|
|
||||||
# Associations
|
# Associations
|
||||||
belongs_to :supplier
|
belongs_to :supplier
|
||||||
belongs_to :article_category
|
belongs_to :article_category
|
||||||
has_many :article_prices, :order => "created_at DESC"
|
has_many :article_prices, :order => "created_at DESC"
|
||||||
|
|
||||||
named_scope :available, :conditions => {:availability => true}
|
scope :undeleted, -> { where(deleted_at: nil) }
|
||||||
named_scope :not_in_stock, :conditions => {:type => nil}
|
scope :available, -> { undeleted.where(availability: true) }
|
||||||
|
scope :not_in_stock, :conditions => {:type => nil}
|
||||||
|
|
||||||
# Validations
|
# Validations
|
||||||
validates_presence_of :name, :unit, :price, :tax, :deposit, :unit_quantity, :supplier_id, :article_category_id
|
validates_presence_of :name, :unit, :price, :tax, :deposit, :unit_quantity, :supplier_id, :article_category
|
||||||
validates_length_of :name, :in => 4..60
|
validates_length_of :name, :in => 4..60
|
||||||
validates_length_of :unit, :in => 2..15
|
validates_length_of :unit, :in => 2..15
|
||||||
validates_numericality_of :price, :unit_quantity, :greater_than => 0
|
validates_numericality_of :price, :greater_than_or_equal_to => 0
|
||||||
|
validates_numericality_of :unit_quantity, :greater_than => 0
|
||||||
validates_numericality_of :deposit, :tax
|
validates_numericality_of :deposit, :tax
|
||||||
validates_uniqueness_of :name, :scope => [:supplier_id, :deleted_at, :type]
|
validates_uniqueness_of :name, :scope => [:supplier_id, :deleted_at, :type]
|
||||||
|
|
||||||
|
@ -48,21 +27,6 @@ class Article < ActiveRecord::Base
|
||||||
before_save :update_price_history
|
before_save :update_price_history
|
||||||
before_destroy :check_article_in_use
|
before_destroy :check_article_in_use
|
||||||
|
|
||||||
# Custom attribute setter that accepts decimal numbers using localized decimal separator.
|
|
||||||
def price=(price)
|
|
||||||
self[:price] = String.delocalized_decimal(price)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Custom attribute setter that accepts decimal numbers using localized decimal separator.
|
|
||||||
def tax=(tax)
|
|
||||||
self[:tax] = String.delocalized_decimal(tax)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Custom attribute setter that accepts decimal numbers using localized decimal separator.
|
|
||||||
def deposit=(deposit)
|
|
||||||
self[:deposit] = String.delocalized_decimal(deposit)
|
|
||||||
end
|
|
||||||
|
|
||||||
# The financial gross, net plus tax and deposti
|
# The financial gross, net plus tax and deposti
|
||||||
def gross_price
|
def gross_price
|
||||||
((price + deposit) * (tax / 100 + 1)).round(2)
|
((price + deposit) * (tax / 100 + 1)).round(2)
|
||||||
|
@ -70,7 +34,7 @@ class Article < ActiveRecord::Base
|
||||||
|
|
||||||
# The price for the foodcoop-member.
|
# The price for the foodcoop-member.
|
||||||
def fc_price
|
def fc_price
|
||||||
(gross_price * (Foodsoft.config[:price_markup] / 100 + 1)).round(2)
|
(gross_price * (FoodsoftConfig[:price_markup] / 100 + 1)).round(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns true if article has been updated at least 2 days ago
|
# Returns true if article has been updated at least 2 days ago
|
||||||
|
@ -86,6 +50,11 @@ class Article < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
memoize :in_open_order
|
memoize :in_open_order
|
||||||
|
|
||||||
|
# Returns true if the article has been ordered in the given order at least once
|
||||||
|
def ordered_in_order?(order)
|
||||||
|
order.order_articles.where(article_id: id).where('quantity > 0').one?
|
||||||
|
end
|
||||||
|
|
||||||
# this method checks, if the shared_article has been changed
|
# this method checks, if the shared_article has been changed
|
||||||
# unequal attributes will returned in array
|
# unequal attributes will returned in array
|
||||||
# if only the timestamps differ and the attributes are equal,
|
# if only the timestamps differ and the attributes are equal,
|
||||||
|
@ -157,8 +126,8 @@ class Article < ActiveRecord::Base
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
else # get factors for fc and supplier
|
else # get factors for fc and supplier
|
||||||
fc_unit_factor = Foodsoft.config[:units][self.unit]
|
fc_unit_factor = FoodsoftConfig[:units][self.unit]
|
||||||
supplier_unit_factor = Foodsoft.config[:units][self.shared_article.unit]
|
supplier_unit_factor = FoodsoftConfig[:units][self.shared_article.unit]
|
||||||
if fc_unit_factor and supplier_unit_factor
|
if fc_unit_factor and supplier_unit_factor
|
||||||
convertion_factor = fc_unit_factor / supplier_unit_factor
|
convertion_factor = fc_unit_factor / supplier_unit_factor
|
||||||
new_price = BigDecimal((convertion_factor * shared_article.price).to_s).round(2)
|
new_price = BigDecimal((convertion_factor * shared_article.price).to_s).round(2)
|
||||||
|
@ -173,11 +142,20 @@ class Article < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def deleted?
|
||||||
|
deleted_at.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def mark_as_deleted
|
||||||
|
check_article_in_use
|
||||||
|
update_column :deleted_at, Time.now
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
# Checks if the article is in use before it will deleted
|
# Checks if the article is in use before it will deleted
|
||||||
def check_article_in_use
|
def check_article_in_use
|
||||||
raise self.name.to_s + " kann nicht gelöscht werden. Der Artikel befindet sich in einer laufenden Bestellung!" if self.in_open_order
|
raise I18n.t('articles.model.error_in_use', :article => self.name.to_s) if self.in_open_order
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create an ArticlePrice, when the price-attr are changed.
|
# Create an ArticlePrice, when the price-attr are changed.
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
class ArticleCategory < ActiveRecord::Base
|
class ArticleCategory < ActiveRecord::Base
|
||||||
has_many :articles
|
has_many :articles
|
||||||
|
|
||||||
validates_length_of :name, :in => 2..20
|
validates :name, :presence => true, :uniqueness => true, :length => { :in => 2..20 }
|
||||||
validates_uniqueness_of :name
|
|
||||||
|
before_destroy :check_for_associated_articles
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def check_for_associated_articles
|
||||||
|
raise I18n.t('activerecord.errors.has_many_left', collection: Article.model_name.human) if articles.undeleted.exists?
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# == Schema Information
|
|
||||||
#
|
|
||||||
# Table name: article_categories
|
|
||||||
#
|
|
||||||
# id :integer(4) not null, primary key
|
|
||||||
# name :string(255) default(""), not null
|
|
||||||
# description :string(255)
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
|
@ -4,22 +4,11 @@ class ArticlePrice < ActiveRecord::Base
|
||||||
has_many :order_articles
|
has_many :order_articles
|
||||||
|
|
||||||
validates_presence_of :price, :tax, :deposit, :unit_quantity
|
validates_presence_of :price, :tax, :deposit, :unit_quantity
|
||||||
validates_numericality_of :price, :unit_quantity, :greater_than => 0
|
validates_numericality_of :price, :greater_than_or_equal_to => 0
|
||||||
|
validates_numericality_of :unit_quantity, :greater_than => 0
|
||||||
|
validates_numericality_of :deposit, :tax
|
||||||
|
|
||||||
# Custom attribute setter that accepts decimal numbers using localized decimal separator.
|
localize_input_of :price, :tax, :deposit
|
||||||
def price=(price)
|
|
||||||
self[:price] = String.delocalized_decimal(price)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Custom attribute setter that accepts decimal numbers using localized decimal separator.
|
|
||||||
def tax=(tax)
|
|
||||||
self[:tax] = String.delocalized_decimal(tax)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Custom attribute setter that accepts decimal numbers using localized decimal separator.
|
|
||||||
def deposit=(deposit)
|
|
||||||
self[:deposit] = String.delocalized_decimal(deposit)
|
|
||||||
end
|
|
||||||
|
|
||||||
# The financial gross, net plus tax and deposit.
|
# The financial gross, net plus tax and deposit.
|
||||||
def gross_price
|
def gross_price
|
||||||
|
@ -28,20 +17,7 @@ class ArticlePrice < ActiveRecord::Base
|
||||||
|
|
||||||
# The price for the foodcoop-member.
|
# The price for the foodcoop-member.
|
||||||
def fc_price
|
def fc_price
|
||||||
(gross_price * (Foodsoft.config[:price_markup] / 100 + 1)).round(2)
|
(gross_price * (FoodsoftConfig[:price_markup] / 100 + 1)).round(2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# == Schema Information
|
|
||||||
#
|
|
||||||
# Table name: article_prices
|
|
||||||
#
|
|
||||||
# id :integer(4) not null, primary key
|
|
||||||
# article_id :integer(4)
|
|
||||||
# price :decimal(8, 2) default(0.0), not null
|
|
||||||
# tax :decimal(8, 2) default(0.0), not null
|
|
||||||
# deposit :decimal(8, 2) default(0.0), not null
|
|
||||||
# unit_quantity :integer(4)
|
|
||||||
# created_at :datetime
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
|
@ -2,26 +2,6 @@ class Assignment < ActiveRecord::Base
|
||||||
|
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :task
|
belongs_to :task
|
||||||
|
|
||||||
# after user is assigned mark task as assigned
|
|
||||||
def after_create
|
|
||||||
self.task.update_attribute(:assigned, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# update assigned-attribute
|
|
||||||
def after_destroy
|
|
||||||
self.task.update_attribute(:assigned, false) if self.task.assignments.empty?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# == Schema Information
|
|
||||||
#
|
|
||||||
# Table name: assignments
|
|
||||||
#
|
|
||||||
# id :integer(4) not null, primary key
|
|
||||||
# user_id :integer(4) default(0), not null
|
|
||||||
# task_id :integer(4) default(0), not null
|
|
||||||
# accepted :boolean(1) default(FALSE)
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ class Delivery < ActiveRecord::Base
|
||||||
has_one :invoice
|
has_one :invoice
|
||||||
has_many :stock_changes, :dependent => :destroy
|
has_many :stock_changes, :dependent => :destroy
|
||||||
|
|
||||||
named_scope :recent, :order => 'created_at DESC', :limit => 10
|
scope :recent, :order => 'created_at DESC', :limit => 10
|
||||||
|
|
||||||
validates_presence_of :supplier_id
|
validates_presence_of :supplier_id
|
||||||
|
|
||||||
|
@ -19,14 +19,3 @@ class Delivery < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# == Schema Information
|
|
||||||
#
|
|
||||||
# Table name: deliveries
|
|
||||||
#
|
|
||||||
# id :integer(4) not null, primary key
|
|
||||||
# supplier_id :integer(4)
|
|
||||||
# delivered_on :date
|
|
||||||
# created_at :datetime
|
|
||||||
# note :text
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
|
@ -4,25 +4,14 @@ class FinancialTransaction < ActiveRecord::Base
|
||||||
belongs_to :ordergroup
|
belongs_to :ordergroup
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
|
||||||
validates_presence_of :note, :user_id, :ordergroup_id
|
validates_presence_of :amount, :note, :user_id, :ordergroup_id
|
||||||
validates_numericality_of :amount
|
validates_numericality_of :amount
|
||||||
|
|
||||||
# Custom attribute setter that accepts decimal numbers using localized decimal separator.
|
localize_input_of :amount
|
||||||
def amount=(amount)
|
|
||||||
self[:amount] = String.delocalized_decimal(amount)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
# Use this save method instead of simple save and after callback
|
||||||
|
def add_transaction!
|
||||||
|
ordergroup.add_financial_transaction! amount, note, user
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# == Schema Information
|
|
||||||
#
|
|
||||||
# Table name: financial_transactions
|
|
||||||
#
|
|
||||||
# id :integer(4) not null, primary key
|
|
||||||
# ordergroup_id :integer(4) default(0), not null
|
|
||||||
# amount :decimal(8, 2) default(0.0), not null
|
|
||||||
# note :text default(""), not null
|
|
||||||
# user_id :integer(4) default(0), not null
|
|
||||||
# created_on :datetime not null
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
# Groups organize the User.
|
# Groups organize the User.
|
||||||
# A Member gets the roles from the Group
|
# A Member gets the roles from the Group
|
||||||
class Group < ActiveRecord::Base
|
class Group < ActiveRecord::Base
|
||||||
has_many :memberships, :dependent => :destroy
|
has_many :memberships
|
||||||
has_many :users, :through => :memberships
|
has_many :users, :through => :memberships
|
||||||
|
|
||||||
validates_length_of :name, :in => 1..25
|
validates :name, :presence => true, :length => {:in => 1..25}
|
||||||
validates_uniqueness_of :name
|
|
||||||
|
attr_reader :user_tokens
|
||||||
|
|
||||||
|
scope :undeleted, -> { where(deleted_at: nil) }
|
||||||
|
|
||||||
# Returns true if the given user if is an member of this group.
|
# Returns true if the given user if is an member of this group.
|
||||||
def member?(user)
|
def member?(user)
|
||||||
|
@ -17,57 +20,22 @@ class Group < ActiveRecord::Base
|
||||||
User.all(:order => 'nick').reject { |u| users.include?(u) }
|
User.all(:order => 'nick').reject { |u| users.include?(u) }
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check before destroy a group, if this is the last group with admin role
|
def user_tokens=(ids)
|
||||||
def before_destroy
|
self.user_ids = ids.split(",")
|
||||||
if self.role_admin == true && Group.find_all_by_role_admin(true).size == 1
|
|
||||||
raise "Die letzte Gruppe mit Admin-Rechten darf nicht gelöscht werden"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
def deleted?
|
||||||
|
deleted_at.present?
|
||||||
# validates uniqueness of the Group.name. Checks groups and ordergroups
|
|
||||||
def validate
|
|
||||||
errors.add(:name, "ist schon vergeben") if (group = Group.find_by_name(name) || group = Ordergroup.find_by_name(name)) && self != group
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# add validation check on update
|
def mark_as_deleted
|
||||||
def validate_on_update
|
# TODO: Checks for participating in not closed orders
|
||||||
# error if this is the last group with admin role and role_admin should set to false
|
transaction do
|
||||||
if self.role_admin == false && Group.find_all_by_role_admin(true).size == 1 && self == Group.find(:first, :conditions => "role_admin = 1")
|
memberships.destroy_all
|
||||||
errors.add(:role_admin, "Der letzten Gruppe mit Admin-Rechten darf die Admin-Rolle nicht entzogen werden")
|
# TODO: What should happen to users?
|
||||||
|
update_column :deleted_at, Time.now
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# == Schema Information
|
|
||||||
#
|
|
||||||
# Table name: groups
|
|
||||||
#
|
|
||||||
# id :integer(4) not null, primary key
|
|
||||||
# type :string(255) default(""), not null
|
|
||||||
# name :string(255) default(""), not null
|
|
||||||
# description :string(255)
|
|
||||||
# account_balance :decimal(8, 2) default(0.0), not null
|
|
||||||
# account_updated :datetime
|
|
||||||
# created_on :datetime not null
|
|
||||||
# role_admin :boolean(1) default(FALSE), not null
|
|
||||||
# role_suppliers :boolean(1) default(FALSE), not null
|
|
||||||
# role_article_meta :boolean(1) default(FALSE), not null
|
|
||||||
# role_finance :boolean(1) default(FALSE), not null
|
|
||||||
# role_orders :boolean(1) default(FALSE), not null
|
|
||||||
# weekly_task :boolean(1) default(FALSE)
|
|
||||||
# weekday :integer(4)
|
|
||||||
# task_name :string(255)
|
|
||||||
# task_description :string(255)
|
|
||||||
# task_required_users :integer(4) default(1)
|
|
||||||
# deleted_at :datetime
|
|
||||||
# contact_person :string(255)
|
|
||||||
# contact_phone :string(255)
|
|
||||||
# contact_address :string(255)
|
|
||||||
# stats :text
|
|
||||||
# task_duration :integer(4) default(1)
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# A GroupOrder represents an Order placed by an Ordergroup.
|
# A GroupOrder represents an Order placed by an Ordergroup.
|
||||||
class GroupOrder < ActiveRecord::Base
|
class GroupOrder < ActiveRecord::Base
|
||||||
|
|
||||||
|
attr_accessor :group_order_articles_attributes
|
||||||
|
|
||||||
belongs_to :order
|
belongs_to :order
|
||||||
belongs_to :ordergroup
|
belongs_to :ordergroup
|
||||||
has_many :group_order_articles, :dependent => :destroy
|
has_many :group_order_articles, :dependent => :destroy
|
||||||
|
@ -12,41 +14,74 @@ class GroupOrder < ActiveRecord::Base
|
||||||
validates_numericality_of :price
|
validates_numericality_of :price
|
||||||
validates_uniqueness_of :ordergroup_id, :scope => :order_id # order groups can only order once per order
|
validates_uniqueness_of :ordergroup_id, :scope => :order_id # order groups can only order once per order
|
||||||
|
|
||||||
named_scope :open, lambda { {:conditions => ["order_id IN (?)", Order.open.collect(&:id)]} }
|
scope :in_open_orders, joins(:order).merge(Order.open)
|
||||||
named_scope :finished, lambda { {:conditions => ["order_id IN (?)", Order.finished_not_closed.collect(&:id)]} }
|
scope :in_finished_orders, joins(:order).merge(Order.finished_not_closed)
|
||||||
|
|
||||||
|
# Generate some data for the javascript methods in ordering view
|
||||||
|
def load_data
|
||||||
|
data = {}
|
||||||
|
data[:available_funds] = ordergroup.get_available_funds(self)
|
||||||
|
|
||||||
|
# load prices and other stuff....
|
||||||
|
data[:order_articles] = {}
|
||||||
|
order.articles_grouped_by_category.each do |article_category, order_articles|
|
||||||
|
order_articles.each do |order_article|
|
||||||
|
|
||||||
|
# Get the result of last time ordering, if possible
|
||||||
|
goa = group_order_articles.detect { |goa| goa.order_article_id == order_article.id }
|
||||||
|
|
||||||
|
# Build hash with relevant data
|
||||||
|
data[:order_articles][order_article.id] = {
|
||||||
|
:price => order_article.article.fc_price,
|
||||||
|
:unit => order_article.article.unit_quantity,
|
||||||
|
:quantity => (goa ? goa.quantity : 0),
|
||||||
|
:others_quantity => order_article.quantity - (goa ? goa.quantity : 0),
|
||||||
|
:used_quantity => (goa ? goa.result(:quantity) : 0),
|
||||||
|
:tolerance => (goa ? goa.result(:tolerance) : 0),
|
||||||
|
:others_tolerance => order_article.tolerance - (goa ? goa.result(:tolerance) : 0),
|
||||||
|
:used_tolerance => (goa ? goa.result(:tolerance) : 0),
|
||||||
|
:total_price => (goa ? goa.total_price : 0),
|
||||||
|
:missing_units => order_article.missing_units,
|
||||||
|
:quantity_available => (order.stockit? ? order_article.article.quantity_available : 0)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
def save_group_order_articles
|
||||||
|
for order_article in order.order_articles
|
||||||
|
# Find the group_order_article, create a new one if necessary...
|
||||||
|
group_order_article = group_order_articles.find_or_create_by_order_article_id(order_article.id)
|
||||||
|
|
||||||
|
# Get ordered quantities and update group_order_articles/_quantities...
|
||||||
|
quantities = group_order_articles_attributes.fetch(order_article.id.to_s, {:quantity => 0, :tolerance => 0})
|
||||||
|
group_order_article.update_quantities(quantities[:quantity].to_i, quantities[:tolerance].to_i)
|
||||||
|
|
||||||
|
# Also update results for the order_article
|
||||||
|
logger.debug "[save_group_order_articles] update order_article.results!"
|
||||||
|
order_article.update_results!
|
||||||
|
end
|
||||||
|
|
||||||
|
# set attributes to nil to avoid and infinite loop of
|
||||||
|
end
|
||||||
|
|
||||||
# Updates the "price" attribute.
|
# Updates the "price" attribute.
|
||||||
# Until the order is finished this will be the maximum price or
|
|
||||||
# the minimum price depending on configuration. When the order is finished it
|
|
||||||
# will be the value depending of the article results.
|
|
||||||
def update_price!
|
def update_price!
|
||||||
total = 0
|
total = group_order_articles.includes(:order_article => [:article, :article_price]).to_a.sum(&:total_price)
|
||||||
for article in group_order_articles.find(:all, :include => :order_article)
|
|
||||||
unless order.finished?
|
|
||||||
if Foodsoft.config[:tolerance_is_costly]
|
|
||||||
total += article.order_article.article.fc_price * (article.quantity + article.tolerance)
|
|
||||||
else
|
|
||||||
total += article.order_article.article.fc_price * article.quantity
|
|
||||||
end
|
|
||||||
else
|
|
||||||
total += article.order_article.price.fc_price * article.result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
update_attribute(:price, total)
|
update_attribute(:price, total)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Save GroupOrder and updates group_order_articles/quantities accordingly
|
||||||
|
def save_ordering!
|
||||||
|
transaction do
|
||||||
|
save!
|
||||||
|
save_group_order_articles
|
||||||
|
update_price!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# == Schema Information
|
|
||||||
#
|
|
||||||
# Table name: group_orders
|
|
||||||
#
|
|
||||||
# id :integer(4) not null, primary key
|
|
||||||
# ordergroup_id :integer(4) default(0), not null
|
|
||||||
# order_id :integer(4) default(0), not null
|
|
||||||
# price :decimal(8, 2) default(0.0), not null
|
|
||||||
# lock_version :integer(4) default(0), not null
|
|
||||||
# updated_on :datetime not null
|
|
||||||
# updated_by_user_id :integer(4)
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
|
@ -8,19 +8,24 @@ class GroupOrderArticle < ActiveRecord::Base
|
||||||
belongs_to :order_article
|
belongs_to :order_article
|
||||||
has_many :group_order_article_quantities, :dependent => :destroy
|
has_many :group_order_article_quantities, :dependent => :destroy
|
||||||
|
|
||||||
validates_presence_of :group_order_id, :order_article_id
|
validates_presence_of :group_order, :order_article
|
||||||
validates_inclusion_of :quantity, :in => 0..99
|
validates_inclusion_of :quantity, :in => 0..99
|
||||||
validates_inclusion_of :result, :in => 0..99, :allow_nil => true
|
validates_inclusion_of :result, :in => 0..99, :allow_nil => true
|
||||||
validates_inclusion_of :tolerance, :in => 0..99
|
validates_inclusion_of :tolerance, :in => 0..99
|
||||||
validates_uniqueness_of :order_article_id, :scope => :group_order_id # just once an article per group order
|
validates_uniqueness_of :order_article_id, :scope => :group_order_id # just once an article per group order
|
||||||
|
|
||||||
attr_accessor :ordergroup_id # To create an new GroupOrder if neccessary
|
scope :ordered, :conditions => 'result > 0'
|
||||||
|
|
||||||
named_scope :ordered, :conditions => 'result > 0'
|
localize_input_of :result
|
||||||
|
|
||||||
# Custom attribute setter that accepts decimal numbers using localized decimal separator.
|
# Setter used in group_order_article#new
|
||||||
def result=(result)
|
# We have to create an group_order, if the ordergroup wasn't involved in the order yet
|
||||||
self[:result] = String.delocalized_decimal(result)
|
def ordergroup_id=(id)
|
||||||
|
self.group_order = GroupOrder.find_or_initialize_by_order_id_and_ordergroup_id(order_article.order_id, id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def ordergroup_id
|
||||||
|
group_order.try(:ordergroup_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Updates the quantity/tolerance for this GroupOrderArticle by updating both GroupOrderArticle properties
|
# Updates the quantity/tolerance for this GroupOrderArticle by updating both GroupOrderArticle properties
|
||||||
|
@ -157,19 +162,22 @@ class GroupOrderArticle < ActiveRecord::Base
|
||||||
self.update_attribute(:result, calculate_result[:total])
|
self.update_attribute(:result, calculate_result[:total])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns total price for this individual article
|
||||||
|
# Until the order is finished this will be the maximum price or
|
||||||
|
# the minimum price depending on configuration. When the order is finished it
|
||||||
|
# will be the value depending of the article results.
|
||||||
|
def total_price(order_article = self.order_article)
|
||||||
|
unless order_article.order.finished?
|
||||||
|
if FoodsoftConfig[:tolerance_is_costly]
|
||||||
|
order_article.article.fc_price * (quantity + tolerance)
|
||||||
|
else
|
||||||
|
order_article.article.fc_price * quantity
|
||||||
|
end
|
||||||
|
else
|
||||||
|
order_article.price.fc_price * result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# == Schema Information
|
|
||||||
#
|
|
||||||
# Table name: group_order_articles
|
|
||||||
#
|
|
||||||
# id :integer(4) not null, primary key
|
|
||||||
# group_order_id :integer(4) default(0), not null
|
|
||||||
# order_article_id :integer(4) default(0), not null
|
|
||||||
# quantity :integer(4) default(0), not null
|
|
||||||
# tolerance :integer(4) default(0), not null
|
|
||||||
# updated_on :datetime not null
|
|
||||||
# result :decimal(8, 3)
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
|
@ -11,14 +11,3 @@ class GroupOrderArticleQuantity < ActiveRecord::Base
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# == Schema Information
|
|
||||||
#
|
|
||||||
# Table name: group_order_article_quantities
|
|
||||||
#
|
|
||||||
# id :integer(4) not null, primary key
|
|
||||||
# group_order_article_id :integer(4) default(0), not null
|
|
||||||
# quantity :integer(4) default(0)
|
|
||||||
# tolerance :integer(4) default(0)
|
|
||||||
# created_on :datetime not null
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|