Merge branch 'master' into allow-no-nickname
Conflicts: app/views/home/ordergroup.html.haml app/views/login/new_password.html.haml app/views/shared/_auto_complete_users.rhtml app/views/shared/memberships/_current_members.rhtml app/views/shared/memberships/_non_members.rhtml
This commit is contained in:
commit
66ac3be81f
63 changed files with 428 additions and 438 deletions
11
Gemfile
11
Gemfile
|
@ -31,18 +31,20 @@ gem 'client_side_validations'
|
||||||
gem 'client_side_validations-simple_form'
|
gem 'client_side_validations-simple_form'
|
||||||
gem 'inherited_resources'
|
gem 'inherited_resources'
|
||||||
gem 'localize_input', git: "git://github.com/bennibu/localize_input.git"
|
gem 'localize_input', git: "git://github.com/bennibu/localize_input.git"
|
||||||
gem 'wikicloth'
|
|
||||||
gem 'daemons'
|
gem 'daemons'
|
||||||
gem 'twitter-bootstrap-rails'
|
gem 'twitter-bootstrap-rails'
|
||||||
gem 'simple-navigation'
|
gem 'simple-navigation'
|
||||||
gem 'simple-navigation-bootstrap'
|
gem 'simple-navigation-bootstrap'
|
||||||
gem 'meta_search'
|
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_tree'
|
||||||
gem "rails-settings-cached", "0.2.4"
|
gem "rails-settings-cached", "0.2.4"
|
||||||
gem 'resque'
|
gem 'resque'
|
||||||
gem 'whenever', require: false # For defining cronjobs, see config/schedule.rb
|
gem 'whenever', require: false # For defining cronjobs, see config/schedule.rb
|
||||||
|
|
||||||
|
# we use the git version of acts_as_versioned, and need to include it in this Gemfile
|
||||||
|
gem 'acts_as_versioned', git: 'git://github.com/technoweenie/acts_as_versioned.git'
|
||||||
|
gem 'foodsoft_wiki', path: 'lib/foodsoft_wiki'
|
||||||
|
|
||||||
group :production do
|
group :production do
|
||||||
gem 'exception_notification'
|
gem 'exception_notification'
|
||||||
end
|
end
|
||||||
|
@ -78,10 +80,9 @@ group :test do
|
||||||
gem 'rspec-rails'
|
gem 'rspec-rails'
|
||||||
gem 'factory_girl_rails', '~> 4.0'
|
gem 'factory_girl_rails', '~> 4.0'
|
||||||
gem 'faker'
|
gem 'faker'
|
||||||
# version requirements to avoid problem http://stackoverflow.com/questions/18114544
|
gem 'capybara'
|
||||||
gem 'capybara', '~> 2.1.0'
|
|
||||||
# webkit and poltergeist don't seem to work yet
|
# webkit and poltergeist don't seem to work yet
|
||||||
gem 'selenium-webdriver', '~> 2.35.1'
|
gem 'selenium-webdriver'
|
||||||
gem 'database_cleaner'
|
gem 'database_cleaner'
|
||||||
gem 'simplecov', require: false
|
gem 'simplecov', require: false
|
||||||
# need to include rspec components before i18n-spec or rake fails in test environment
|
# need to include rspec components before i18n-spec or rake fails in test environment
|
||||||
|
|
24
Gemfile.lock
24
Gemfile.lock
|
@ -18,6 +18,14 @@ GIT
|
||||||
acts_as_versioned (0.6.0)
|
acts_as_versioned (0.6.0)
|
||||||
activerecord (>= 3.0.9)
|
activerecord (>= 3.0.9)
|
||||||
|
|
||||||
|
PATH
|
||||||
|
remote: lib/foodsoft_wiki
|
||||||
|
specs:
|
||||||
|
foodsoft_wiki (0.0.1)
|
||||||
|
acts_as_versioned
|
||||||
|
rails (~> 3.2.15)
|
||||||
|
wikicloth
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
|
@ -251,7 +259,7 @@ GEM
|
||||||
rspec-mocks (~> 2.14.0)
|
rspec-mocks (~> 2.14.0)
|
||||||
ruby-prof (0.13.0)
|
ruby-prof (0.13.0)
|
||||||
ruby-rc4 (0.1.5)
|
ruby-rc4 (0.1.5)
|
||||||
rubyzip (0.9.9)
|
rubyzip (1.0.0)
|
||||||
sass (3.2.12)
|
sass (3.2.12)
|
||||||
sass-rails (3.2.6)
|
sass-rails (3.2.6)
|
||||||
railties (~> 3.2.0)
|
railties (~> 3.2.0)
|
||||||
|
@ -259,10 +267,10 @@ GEM
|
||||||
tilt (~> 1.3)
|
tilt (~> 1.3)
|
||||||
select2-rails (3.5.0)
|
select2-rails (3.5.0)
|
||||||
thor (~> 0.14)
|
thor (~> 0.14)
|
||||||
selenium-webdriver (2.35.1)
|
selenium-webdriver (2.37.0)
|
||||||
childprocess (>= 0.2.5)
|
childprocess (>= 0.2.5)
|
||||||
multi_json (~> 1.0)
|
multi_json (~> 1.0)
|
||||||
rubyzip (< 1.0.0)
|
rubyzip (~> 1.0.0)
|
||||||
websocket (~> 1.0.4)
|
websocket (~> 1.0.4)
|
||||||
simple-navigation (3.11.0)
|
simple-navigation (3.11.0)
|
||||||
activesupport (>= 2.3.2)
|
activesupport (>= 2.3.2)
|
||||||
|
@ -308,9 +316,9 @@ GEM
|
||||||
rails (>= 3.1)
|
rails (>= 3.1)
|
||||||
railties (>= 3.1)
|
railties (>= 3.1)
|
||||||
tzinfo (0.3.38)
|
tzinfo (0.3.38)
|
||||||
uglifier (2.2.1)
|
uglifier (2.3.0)
|
||||||
execjs (>= 0.3.0)
|
execjs (>= 0.3.0)
|
||||||
multi_json (~> 1.0, >= 1.0.2)
|
json (>= 1.8.0)
|
||||||
uniform_notifier (1.3.0)
|
uniform_notifier (1.3.0)
|
||||||
vegas (0.1.11)
|
vegas (0.1.11)
|
||||||
rack (>= 1.0.0)
|
rack (>= 1.0.0)
|
||||||
|
@ -336,7 +344,7 @@ DEPENDENCIES
|
||||||
bullet
|
bullet
|
||||||
capistrano (= 2.13.5)
|
capistrano (= 2.13.5)
|
||||||
capistrano-ext
|
capistrano-ext
|
||||||
capybara (~> 2.1.0)
|
capybara
|
||||||
client_side_validations
|
client_side_validations
|
||||||
client_side_validations-simple_form
|
client_side_validations-simple_form
|
||||||
coffee-rails (~> 3.2.1)
|
coffee-rails (~> 3.2.1)
|
||||||
|
@ -345,6 +353,7 @@ DEPENDENCIES
|
||||||
exception_notification
|
exception_notification
|
||||||
factory_girl_rails (~> 4.0)
|
factory_girl_rails (~> 4.0)
|
||||||
faker
|
faker
|
||||||
|
foodsoft_wiki!
|
||||||
haml-rails
|
haml-rails
|
||||||
i18n-js!
|
i18n-js!
|
||||||
i18n-spec
|
i18n-spec
|
||||||
|
@ -369,7 +378,7 @@ DEPENDENCIES
|
||||||
ruby-prof
|
ruby-prof
|
||||||
sass-rails (~> 3.2.3)
|
sass-rails (~> 3.2.3)
|
||||||
select2-rails
|
select2-rails
|
||||||
selenium-webdriver (~> 2.35.1)
|
selenium-webdriver
|
||||||
simple-navigation
|
simple-navigation
|
||||||
simple-navigation-bootstrap
|
simple-navigation-bootstrap
|
||||||
simple_form
|
simple_form
|
||||||
|
@ -380,4 +389,3 @@ DEPENDENCIES
|
||||||
twitter-bootstrap-rails
|
twitter-bootstrap-rails
|
||||||
uglifier (>= 1.0.3)
|
uglifier (>= 1.0.3)
|
||||||
whenever
|
whenever
|
||||||
wikicloth
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ FoodSoft
|
||||||
|
|
||||||
Web-based software to manage a non-profit food coop (product catalog, ordering, accounting, job scheduling).
|
Web-based software to manage a non-profit food coop (product catalog, ordering, accounting, job scheduling).
|
||||||
|
|
||||||
|
A food cooperative is a group of people that buy food from suppliers of their own choosing. A collective do-it-yourself supermarket. Members order their products online and collect them on a specified day. And all put in a bit of work to make that possible. Foodsoft facilitates the process.
|
||||||
|
|
||||||
If you're a food coop considering to use foodsoft, please have a look at the [wiki page for foodcoops](https://github.com/foodcoops/foodsoft/wiki/For-foodcoops). When you'd like to experiment with or develop foodsoft, you can read [how to set it up](https://github.com/foodcoops/foodsoft/blob/master/doc/SETUP_DEVELOPMENT.md) on your own computer.
|
If you're a food coop considering to use foodsoft, please have a look at the [wiki page for foodcoops](https://github.com/foodcoops/foodsoft/wiki/For-foodcoops). When you'd like to experiment with or develop foodsoft, you can read [how to set it up](https://github.com/foodcoops/foodsoft/blob/master/doc/SETUP_DEVELOPMENT.md) on your own computer.
|
||||||
|
|
||||||
More information about using this software and contributing can be found on the [wiki](https://github.com/foodcoops/foodsoft/wiki).
|
More information about using this software and contributing can be found on the [wiki](https://github.com/foodcoops/foodsoft/wiki).
|
||||||
|
|
|
@ -65,7 +65,7 @@ class GroupOrdersController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
# Returns true if @current_user is member of an Ordergroup.
|
# Returns true if @current_user is member of an Ordergroup.
|
||||||
# Used as a :before_filter by OrderingController.
|
# Used as a :before_filter by OrdersController.
|
||||||
def ensure_ordergroup_member
|
def ensure_ordergroup_member
|
||||||
@ordergroup = @current_user.ordergroup
|
@ordergroup = @current_user.ordergroup
|
||||||
if @ordergroup.nil?
|
if @ordergroup.nil?
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
class MembershipsController < ApplicationController
|
|
||||||
before_filter :authenticate_membership_or_admin
|
|
||||||
|
|
||||||
def add_member
|
|
||||||
user = User.find(params[:user_id])
|
|
||||||
Membership.create(:group => @group, :user => user)
|
|
||||||
redirect_to :action => 'reload', :id => @group
|
|
||||||
end
|
|
||||||
|
|
||||||
def drop_member
|
|
||||||
begin
|
|
||||||
Membership.find(params[:membership_id]).destroy
|
|
||||||
if User.find(@current_user.id).role_admin?
|
|
||||||
redirect_to :action => 'reload', :id => @group
|
|
||||||
else
|
|
||||||
# If the user drops himself from admin group
|
|
||||||
flash[:notice] = MESG_NO_ADMIN_ANYMORE
|
|
||||||
render(:update) {|page| page.redirect_to :controller => "index"}
|
|
||||||
end
|
|
||||||
rescue => error
|
|
||||||
flash[:error] = error.to_s
|
|
||||||
redirect_to :action => 'reload', :id => @group
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def reload
|
|
||||||
render :update do |page|
|
|
||||||
page.replace_html 'members', :partial => 'shared/memberships/current_members', :object => @group
|
|
||||||
page.replace_html 'non_members', :partial => 'shared/memberships/non_members', :object => @group
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
|
@ -48,6 +48,9 @@ class OrdersController < ApplicationController
|
||||||
end
|
end
|
||||||
send_data pdf.to_pdf, filename: pdf.filename, type: 'application/pdf'
|
send_data pdf.to_pdf, filename: pdf.filename, type: 'application/pdf'
|
||||||
end
|
end
|
||||||
|
format.text do
|
||||||
|
send_data text_fax_template, filename: @order.name+'.txt', type: 'text/plain'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -101,12 +104,14 @@ class OrdersController < ApplicationController
|
||||||
rescue => error
|
rescue => error
|
||||||
redirect_to orders_url, alert: I18n.t('errors.general_msg', :msg => error.message)
|
redirect_to orders_url, alert: I18n.t('errors.general_msg', :msg => error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
# Renders the fax-text-file
|
# Renders the fax-text-file
|
||||||
# e.g. for easier use with online-fax-software, which don't accept pdf-files
|
# e.g. for easier use with online-fax-software, which don't accept pdf-files
|
||||||
|
# TODO move to text template
|
||||||
def text_fax_template
|
def text_fax_template
|
||||||
order = Order.find(params[:id])
|
supplier = @order.supplier
|
||||||
supplier = order.supplier
|
|
||||||
contact = FoodsoftConfig[:contact].symbolize_keys
|
contact = FoodsoftConfig[:contact].symbolize_keys
|
||||||
text = I18n.t('orders.fax.heading', :name => FoodsoftConfig[:name])
|
text = I18n.t('orders.fax.heading', :name => FoodsoftConfig[:name])
|
||||||
text += "\n#{Supplier.human_attribute_name(:customer_number)}: #{supplier.customer_number}" unless supplier.customer_number.blank?
|
text += "\n#{Supplier.human_attribute_name(:customer_number)}: #{supplier.customer_number}" unless supplier.customer_number.blank?
|
||||||
|
@ -117,15 +122,13 @@ class OrdersController < ApplicationController
|
||||||
text += "****** " + I18n.t('orders.fax.articles') + "\n\n"
|
text += "****** " + I18n.t('orders.fax.articles') + "\n\n"
|
||||||
text += I18n.t('orders.fax.number') + " " + I18n.t('orders.fax.amount') + " " + I18n.t('orders.fax.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
|
||||||
(8 - number.size).times { number += " " }
|
(8 - number.size).times { number += " " }
|
||||||
quantity = oa.units_to_order.to_i.to_s
|
quantity = oa.units_to_order.to_i.to_s
|
||||||
quantity = " " + quantity if quantity.size < 2
|
quantity = " " + quantity if quantity.size < 2
|
||||||
text += "#{number} #{quantity} #{oa.article.name}\n"
|
text += "#{number} #{quantity} #{oa.article.name}\n"
|
||||||
end
|
end
|
||||||
send_data text,
|
text
|
||||||
:type => 'text/plain; charset=utf-8; header=present',
|
|
||||||
:disposition => "attachment; filename=#{order.name}"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -78,14 +78,14 @@ module ApplicationHelper
|
||||||
# When the 'short' option is true, abbreviations will be used:
|
# When the 'short' option is true, abbreviations will be used:
|
||||||
# When there is a non-empty model attribute 'foo', it looks for
|
# When there is a non-empty model attribute 'foo', it looks for
|
||||||
# the model attribute translation 'foo_short' and use that as
|
# the model attribute translation 'foo_short' and use that as
|
||||||
# heading, with an acronym title of 'foo'.
|
# heading, with an abbreviation title of 'foo'.
|
||||||
# Other options are passed through to I18n.
|
# Other options are passed through to I18n.
|
||||||
def heading_helper(model, attribute, options = {})
|
def heading_helper(model, attribute, options = {})
|
||||||
i18nopts = options.select {|a| !['short'].include?(a) }
|
i18nopts = options.select {|a| !['short'].include?(a) }
|
||||||
s = model.human_attribute_name(attribute, i18nopts)
|
s = model.human_attribute_name(attribute, i18nopts)
|
||||||
if options[:short]
|
if options[:short]
|
||||||
sshort = model.human_attribute_name("#{attribute}_short".to_sym, options.merge({defaults: ''}))
|
sshort = model.human_attribute_name("#{attribute}_short".to_sym, options.merge({defaults: ''}))
|
||||||
s = raw "<acronym title='#{s}'>#{sshort}</acronym>" unless sshort.empty?
|
s = raw "<abbr title='#{s}'>#{sshort}</abbr>" unless sshort.empty?
|
||||||
end
|
end
|
||||||
s
|
s
|
||||||
end
|
end
|
||||||
|
|
|
@ -106,7 +106,7 @@ class Article < ActiveRecord::Base
|
||||||
|
|
||||||
# to get the correspondent shared article
|
# to get the correspondent shared article
|
||||||
def shared_article
|
def shared_article
|
||||||
@shared_article ||= self.supplier.shared_supplier.shared_articles.find_by_number(self.order_number)
|
@shared_article ||= self.supplier.shared_supplier.shared_articles.find_by_number(self.order_number) rescue nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# convert units in foodcoop-size
|
# convert units in foodcoop-size
|
||||||
|
|
|
@ -90,7 +90,8 @@ class OrderArticle < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def ordered_quantities_equal_to_group_orders?
|
def ordered_quantities_equal_to_group_orders?
|
||||||
(units_to_order * price.unit_quantity) == group_orders_sum[:quantity]
|
# the rescue is a workaround for units_to_order not being defined in integration tests
|
||||||
|
(units_to_order * price.unit_quantity) == group_orders_sum[:quantity] rescue false
|
||||||
end
|
end
|
||||||
|
|
||||||
# Updates order_article and belongings during balancing process
|
# Updates order_article and belongings during balancing process
|
||||||
|
|
|
@ -17,7 +17,6 @@ class User < ActiveRecord::Base
|
||||||
has_many :assignments, :dependent => :destroy
|
has_many :assignments, :dependent => :destroy
|
||||||
has_many :tasks, :through => :assignments
|
has_many :tasks, :through => :assignments
|
||||||
has_many :send_messages, :class_name => "Message", :foreign_key => "sender_id"
|
has_many :send_messages, :class_name => "Message", :foreign_key => "sender_id"
|
||||||
has_many :pages, :foreign_key => 'updated_by'
|
|
||||||
has_many :created_orders, :class_name => 'Order', :foreign_key => 'created_by_user_id', :dependent => :nullify
|
has_many :created_orders, :class_name => 'Order', :foreign_key => 'created_by_user_id', :dependent => :nullify
|
||||||
|
|
||||||
attr_accessor :password, :settings_attributes
|
attr_accessor :password, :settings_attributes
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
%tr.edit_inline{:id=> "edit_"+@article.id.to_s}
|
%tr.edit_inline{:id=> "edit_"+@article.id.to_s}
|
||||||
%td{:colspan=>"10"}
|
%td{:colspan=>"10"}
|
||||||
= t('.note', article: h(@article.name), drop_link: link_to(t('.drop'), :controller => 'orders', :action => 'edit', :id => @order)).html_safe
|
= t('.note', article: h(@article.name), drop_link: link_to(t('.drop'), edit_order_path(@order))).html_safe
|
||||||
|
|
|
@ -9,7 +9,14 @@
|
||||||
// create List for search-feature (using list.js, http://listjs.com)
|
// create List for search-feature (using list.js, http://listjs.com)
|
||||||
var listjsResetPlugin = ['reset', {highlightClass: 'btn-primary'}];
|
var listjsResetPlugin = ['reset', {highlightClass: 'btn-primary'}];
|
||||||
var listjsDelayPlugin = ['delay', {delayedSearchTime: 500}];
|
var listjsDelayPlugin = ['delay', {delayedSearchTime: 500}];
|
||||||
new List(document.body, { valueNames: ['name'], engine: 'unlist', plugins: [listjsResetPlugin, listjsDelayPlugin] });
|
new List(document.body, {
|
||||||
|
valueNames: ['name'],
|
||||||
|
engine: 'unlist',
|
||||||
|
plugins: [listjsResetPlugin, listjsDelayPlugin],
|
||||||
|
// make large pages work too (as we don't have paging - articles may disappear!)
|
||||||
|
page: 10000,
|
||||||
|
indexAsync: true
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
- title t('.title'), false
|
- title t('.title'), false
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
%li.nav-header= t '.foodcoop'
|
%li.nav-header= t '.foodcoop'
|
||||||
%li= link_to t('.members'), foodcoop_users_path
|
%li= link_to t('.members'), foodcoop_users_path
|
||||||
%li= link_to t('.tasks'), user_tasks_path
|
%li= link_to t('.tasks'), user_tasks_path
|
||||||
%li= link_to t('.write_message'), :controller => "messages", :action => "new"
|
%li= link_to t('.write_message'), new_message_path
|
||||||
|
|
||||||
- has_ordergroup = !@current_user.ordergroup.nil?
|
- has_ordergroup = !@current_user.ordergroup.nil?
|
||||||
- has_orders_role = @current_user.role_orders?
|
- has_orders_role = @current_user.role_orders?
|
||||||
|
|
|
@ -4,16 +4,14 @@
|
||||||
.span4
|
.span4
|
||||||
%h2= @ordergroup.name
|
%h2= @ordergroup.name
|
||||||
.well
|
.well
|
||||||
|
- unless @ordergroup.description.blank?
|
||||||
|
%p= @ordergroup.description
|
||||||
%p
|
%p
|
||||||
%b= t '.description'
|
%b= Ordergroup.human_attribute_name(:available_funds) + ':'
|
||||||
= @ordergroup.description
|
|
||||||
%p
|
|
||||||
%b= t '.funds'
|
|
||||||
= number_to_currency(@ordergroup.get_available_funds())
|
= number_to_currency(@ordergroup.get_available_funds())
|
||||||
%h2= t '.people'
|
%p
|
||||||
%ul
|
%b= Ordergroup.human_attribute_name(:user_tokens) + ':'
|
||||||
- for membership in @ordergroup.memberships
|
= @ordergroup.memberships.map{|m| show_user m.user}.join(', ')
|
||||||
%li= show_user membership.user
|
|
||||||
= link_to t('.invite'), new_invite_path(:id => @ordergroup), :remote => true, class: 'btn btn-primary'
|
= link_to t('.invite'), new_invite_path(:id => @ordergroup), :remote => true, class: 'btn btn-primary'
|
||||||
.span8
|
.span8
|
||||||
%h2= t('.account_summary')
|
%h2= t('.account_summary')
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
%h3
|
%h3
|
||||||
= h(t('.user.title', user: show_user))
|
= h(t('.user.title', user: show_user))
|
||||||
%small= t '.user.since', when: distance_of_time_in_words(Time.now, @current_user.created_on)
|
%small= t '.user.since', when: distance_of_time_in_words(Time.now, @current_user.created_on)
|
||||||
= simple_form_for(@current_user, :url => { :action => 'update_profile'}) do |f|
|
= simple_form_for(@current_user, :url => update_profile_path) do |f|
|
||||||
= render :partial => 'shared/user_form_fields', :locals => {:f => f}
|
= render :partial => 'shared/user_form_fields', :locals => {:f => f}
|
||||||
.form-actions
|
.form-actions
|
||||||
= submit_tag t('ui.save'), class: 'btn'
|
= submit_tag t('ui.save'), class: 'btn'
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
!!!
|
|
||||||
%html
|
|
||||||
%head
|
|
||||||
%meta{"http-equiv" => "content-type", :content => "text/html;charset=UTF-8"}
|
|
||||||
%title= t '.title', title: (yield(:title) or controller.controller_name)
|
|
||||||
= stylesheet_link_tag 'application'
|
|
||||||
= stylesheet_link_tag "print", :media => "print"
|
|
||||||
<!--[if lte IE 7]>
|
|
||||||
= stylesheet_link_tag 'ie_hacks'
|
|
||||||
<![endif]-->
|
|
||||||
= javascript_include_tag 'application'
|
|
||||||
= csrf_meta_tags
|
|
||||||
= yield(:head)
|
|
||||||
%body
|
|
||||||
#logininfo= render :partial => 'shared/loginInfo'
|
|
||||||
|
|
||||||
#header
|
|
||||||
#logo
|
|
||||||
= link_to root_path do
|
|
||||||
= t('layouts.logo').html_safe
|
|
||||||
%span{:style => "color:white; font-size:45%; letter-spacing: -1px;"}= FoodsoftConfig[:name]
|
|
||||||
#nav= render :partial => 'layouts/main_tabnav'
|
|
||||||
|
|
||||||
#main
|
|
||||||
#content
|
|
||||||
- flash.each do |name, msg|
|
|
||||||
= content_tag :h3, msg, :id => "flash#{name.to_s.camelize}", :class => "flash #{name}"
|
|
||||||
#loader{:style => "display:none;"}= image_tag("loader.gif", :border => 0)
|
|
||||||
- if show_title?
|
|
||||||
%h1= yield(:title)
|
|
||||||
= yield
|
|
||||||
#ajax_box(style="display:none")
|
|
|
@ -1,6 +1,6 @@
|
||||||
- title t('.title')
|
- title t('.title')
|
||||||
= t('.body').html_safe
|
= t('.body').html_safe
|
||||||
= simple_form_for User.new, url: {action: 'reset_password'} do |form|
|
= simple_form_for User.new, url: reset_password_path do |form|
|
||||||
= form.input :email
|
= form.input :email
|
||||||
.form-actions
|
.form-actions
|
||||||
= form.submit t('.submit'), class: 'btn'
|
= form.submit t('.submit'), class: 'btn'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- title t('.title')
|
- title t('.title')
|
||||||
= raw t('.body', user: h(show_user(@user)))
|
= raw t('.body', user: h(show_user(@user)))
|
||||||
= simple_form_for @user, :url => {:action => 'update_password', :id => @user.id, :token => @user.reset_password_token} do |form|
|
= simple_form_for @user, :url => update_password_path(@user.id, :token => @user.reset_password_token) do |form|
|
||||||
= form.input :password
|
= form.input :password
|
||||||
= form.input :password_confirmation
|
= form.input :password_confirmation
|
||||||
.form-actions
|
.form-actions
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
%li= order_pdf(@order, :articles, t('.download.article_pdf'))
|
%li= order_pdf(@order, :articles, t('.download.article_pdf'))
|
||||||
%li= order_pdf(@order, :matrix, t('.download.matrix_pdf'))
|
%li= order_pdf(@order, :matrix, t('.download.matrix_pdf'))
|
||||||
%li= order_pdf(@order, :fax, t('.download.fax_pdf'))
|
%li= order_pdf(@order, :fax, t('.download.fax_pdf'))
|
||||||
%li= link_to t('.download.fax_txt'), {action: 'text_fax_template', id: @order }, {title: t('.download.download_file')}
|
%li= link_to t('.download.fax_txt'), order_path(@order, format: :txt), {title: t('.download.download_file')}
|
||||||
|
|
||||||
%section#articles_table
|
%section#articles_table
|
||||||
= render 'articles', order: @order
|
= render 'articles', order: @order
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
%ul.autocomplete
|
|
||||||
- for article in @articles
|
|
||||||
- supplier = @supplier ? "" : " - #{truncate(article.supplier.name)}"
|
|
||||||
%li{:id => article.id.to_s}= "#{article.name} (#{article.unit_quantity} * #{article.unit} | #{number_to_currency(article.price)}#{supplier})"
|
|
|
@ -1,5 +0,0 @@
|
||||||
<ul class="autocomplete">
|
|
||||||
<% for user in @users do -%>
|
|
||||||
<li><span class="nick"><%= show_user(user) %></span><span class="informal"> (<%= user.ordergroup.try(:name) %>)</span></li>
|
|
||||||
<% end -%>
|
|
||||||
</ul>
|
|
|
@ -1,9 +0,0 @@
|
||||||
%ul
|
|
||||||
%li
|
|
||||||
= image_tag 'b_user.png' , :size => '7x10', :border => 0, :alt => t('.profile')
|
|
||||||
= link_to h(@current_user.nick), my_profile_path, { :title => t('.edit_profile') }
|
|
||||||
- if FoodsoftConfig[:homepage]
|
|
||||||
%li= link_to FoodsoftConfig[:name], FoodsoftConfig[:homepage], { :title => t('.homepage_title') }
|
|
||||||
%li= link_to t('.help'), FoodsoftConfig[:help_url]
|
|
||||||
%li= link_to t('.feedback.title'), new_feedback_path, :title => t('.feedback.desc')
|
|
||||||
%li= link_to t('.logout'), logout_path
|
|
|
@ -1,21 +0,0 @@
|
||||||
<% if flash[:error] %>
|
|
||||||
<h3 class="error" id="flashError" ><%= flash[:error] %></h3>
|
|
||||||
<%= javascript_tag("new Effect.Highlight('flashError', {delay:0.8, duration:1});") -%>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<% memberships = @group.memberships
|
|
||||||
if memberships.size != 0 %>
|
|
||||||
<ul style="">
|
|
||||||
<% for membership in memberships %>
|
|
||||||
<li style="margin-left:-15px">
|
|
||||||
<%= show_user membership.user, full: true, markup: true %>
|
|
||||||
| <%= link_to_remote t('.drop'),
|
|
||||||
:url => { :controller => '/memberships', :action => 'drop_member', :id => @group, :membership_id => membership },
|
|
||||||
:before => "Element.show('loader')",
|
|
||||||
:success => "Element.hide('loader')" %>
|
|
||||||
</li>
|
|
||||||
<% end %>
|
|
||||||
</ul>
|
|
||||||
<% else %>
|
|
||||||
<p><i><%= t('.no_members', group: @group.name) %></i></p>
|
|
||||||
<% end %>
|
|
|
@ -1,23 +0,0 @@
|
||||||
<h1><%=h t('.title', group: @group.name) %></h1>
|
|
||||||
<p>
|
|
||||||
<i><%= t('.desc', link: remote_link_to(t('.invite'), :url => new_invite_path(:id => @group))).html_safe %></i>
|
|
||||||
</p>
|
|
||||||
<div class="left_column" style="width:48%">
|
|
||||||
<div class="box_title">
|
|
||||||
<h2><%= t('.already_members') %></h2>
|
|
||||||
</div>
|
|
||||||
<div class="column_content" id="members">
|
|
||||||
<%=render :partial => 'shared/memberships/current_members' %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right_column" style="width:48%">
|
|
||||||
<div class="box_title">
|
|
||||||
<h2><%= t('.no_members_yet') %></h2>
|
|
||||||
</div>
|
|
||||||
<div class="column_content" id="non_members">
|
|
||||||
<%= render :partial => 'shared/memberships/non_members' %>
|
|
||||||
<%= remote_link_to(t('.invite_someone'), :url => new_invite_path(:id => @group)) %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="edit_box" style="display:none"></div>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<ul>
|
|
||||||
<% for user in @group.non_members %>
|
|
||||||
<li>
|
|
||||||
<%= show_user user, full: true, markup: true %>
|
|
||||||
| <%= link_to_remote t('.add'),
|
|
||||||
:url => { :controller => '/memberships', :action => 'add_member', :id => @group, :user_id => user },
|
|
||||||
:before => "Element.show('loader')",
|
|
||||||
:success => "Element.hide('loader')" %>
|
|
||||||
</li>
|
|
||||||
<% end %>
|
|
||||||
</ul>
|
|
|
@ -13,5 +13,5 @@
|
||||||
- @tasks.each do |task|
|
- @tasks.each do |task|
|
||||||
%tr
|
%tr
|
||||||
%td= task.due_date unless task.due_date.nil?
|
%td= task.due_date unless task.due_date.nil?
|
||||||
%td= link_to t('.task_format', name: task.name, duration: task.duration), :controller => "tasks", :action => "show", :id => task
|
%td= link_to t('.task_format', name: task.name, duration: task.duration), task_path(task)
|
||||||
%td= task_assignments task
|
%td= task_assignments task
|
||||||
|
|
|
@ -853,10 +853,7 @@ de:
|
||||||
no_ordergroups: Leider bist Du kein Mitglied einer Bestellgruppe
|
no_ordergroups: Leider bist Du kein Mitglied einer Bestellgruppe
|
||||||
ordergroup:
|
ordergroup:
|
||||||
account_summary: Kontoauszug
|
account_summary: Kontoauszug
|
||||||
description: Beschreibung
|
|
||||||
funds: ! 'Verfügbares Guthaben:'
|
|
||||||
invite: Neue Person einladen
|
invite: Neue Person einladen
|
||||||
people: Personen
|
|
||||||
search: Suchen ...
|
search: Suchen ...
|
||||||
title: Meine Bestellgruppe
|
title: Meine Bestellgruppe
|
||||||
ordergroup_cancelled: Du bist jetzt kein Mitglied der Gruppe %{group} mehr.
|
ordergroup_cancelled: Du bist jetzt kein Mitglied der Gruppe %{group} mehr.
|
||||||
|
@ -906,8 +903,6 @@ de:
|
||||||
ordering:
|
ordering:
|
||||||
confirm_change: Änderungen an dieser Bestellung gehen verloren, wenn zu einer anderen Bestellung gewechselt wird. Möchtest Du trotzdem wechseln?
|
confirm_change: Änderungen an dieser Bestellung gehen verloren, wenn zu einer anderen Bestellung gewechselt wird. Möchtest Du trotzdem wechseln?
|
||||||
layouts:
|
layouts:
|
||||||
application1:
|
|
||||||
title: Foodsoft - %{title}
|
|
||||||
email:
|
email:
|
||||||
footer: ! '--
|
footer: ! '--
|
||||||
|
|
||||||
|
@ -1367,28 +1362,6 @@ de:
|
||||||
search_user: Nach Nutzerin suchen
|
search_user: Nach Nutzerin suchen
|
||||||
title: Wöchentliche Jobs
|
title: Wöchentliche Jobs
|
||||||
user_not_found: Keine Nutzerin gefunden
|
user_not_found: Keine Nutzerin gefunden
|
||||||
loginInfo:
|
|
||||||
edit_profile: Profil bearbeiten
|
|
||||||
feedback:
|
|
||||||
desc: Fehler gefunden? Vorschlag? Idee? Kritik?
|
|
||||||
title: Feedback
|
|
||||||
help: Hilfe
|
|
||||||
homepage_title: Foodcoop Homepage besuchen
|
|
||||||
logout: Abmelden
|
|
||||||
profile: Profil
|
|
||||||
memberships:
|
|
||||||
current_members:
|
|
||||||
drop: entfernen
|
|
||||||
no_members: ! '%{group} hat keine Mitglieder.'
|
|
||||||
members:
|
|
||||||
already_members: Sind schon Mitglieder
|
|
||||||
desc: Hier kannst Du Mitglieder der Gruppe verwalten oder ein neues Foodcoop-Mitglied in die Gruppe %{link}.
|
|
||||||
invite: einladen
|
|
||||||
invite_someone: Person einladen
|
|
||||||
no_members_yet: Sind noch keine Mitglieder
|
|
||||||
title: Mitglieder von %{group}
|
|
||||||
non_members:
|
|
||||||
add: hinzufügen
|
|
||||||
open_orders:
|
open_orders:
|
||||||
ending: Ende
|
ending: Ende
|
||||||
no_open_orders: Derzeit gibt es keine laufenden Bestellungen
|
no_open_orders: Derzeit gibt es keine laufenden Bestellungen
|
||||||
|
@ -1416,7 +1389,6 @@ de:
|
||||||
name: Bitte ändern
|
name: Bitte ändern
|
||||||
edit_stock_article:
|
edit_stock_article:
|
||||||
price: <ul><li>Preisänderung gesperrt.</li><li>Bei Bedarf %{stock_article_copy_link}.</li></ul>
|
price: <ul><li>Preisänderung gesperrt.</li><li>Bei Bedarf %{stock_article_copy_link}.</li></ul>
|
||||||
supplier:
|
|
||||||
supplier:
|
supplier:
|
||||||
min_order_quantity: Die Mindestbestellmenge wird während der Bestellung angezeigt und soll motivieren
|
min_order_quantity: Die Mindestbestellmenge wird während der Bestellung angezeigt und soll motivieren
|
||||||
task:
|
task:
|
||||||
|
|
|
@ -646,7 +646,7 @@ en:
|
||||||
title: Invoice %{number}
|
title: Invoice %{number}
|
||||||
order_articles:
|
order_articles:
|
||||||
edit:
|
edit:
|
||||||
stock_alert:
|
stock_alert: The price of stock articles cannot be changed!
|
||||||
title: Update article
|
title: Update article
|
||||||
new:
|
new:
|
||||||
title: Add delivered article to order
|
title: Add delivered article to order
|
||||||
|
@ -858,10 +858,7 @@ en:
|
||||||
no_ordergroups: You are unfortunately not a member of an ordergroup.
|
no_ordergroups: You are unfortunately not a member of an ordergroup.
|
||||||
ordergroup:
|
ordergroup:
|
||||||
account_summary: Account Statement
|
account_summary: Account Statement
|
||||||
description: description
|
|
||||||
funds: ! 'Available credit:'
|
|
||||||
invite: Invite a new Person
|
invite: Invite a new Person
|
||||||
people: People
|
|
||||||
search: Search ...
|
search: Search ...
|
||||||
title: My ordergroup
|
title: My ordergroup
|
||||||
ordergroup_cancelled: You cancelled membership of the group %{group}.
|
ordergroup_cancelled: You cancelled membership of the group %{group}.
|
||||||
|
@ -911,8 +908,6 @@ en:
|
||||||
ordering:
|
ordering:
|
||||||
confirm_change: Modifications to this order will be lost when you change the order. Do you want to lose the changes you made and continue?
|
confirm_change: Modifications to this order will be lost when you change the order. Do you want to lose the changes you made and continue?
|
||||||
layouts:
|
layouts:
|
||||||
application1:
|
|
||||||
title: Foodsoft - %{title}
|
|
||||||
email:
|
email:
|
||||||
footer: ! '--
|
footer: ! '--
|
||||||
|
|
||||||
|
@ -1372,28 +1367,6 @@ en:
|
||||||
search_user: Search user
|
search_user: Search user
|
||||||
title: Weekly jobs
|
title: Weekly jobs
|
||||||
user_not_found: No user found
|
user_not_found: No user found
|
||||||
loginInfo:
|
|
||||||
edit_profile: Edit profile
|
|
||||||
feedback:
|
|
||||||
desc: Found a bug? Suggestions? Review?
|
|
||||||
title: Feedback
|
|
||||||
help: Help
|
|
||||||
homepage_title: Visit Foodcoop Homepage
|
|
||||||
logout: Logout
|
|
||||||
profile: Profile
|
|
||||||
memberships:
|
|
||||||
current_members:
|
|
||||||
drop: remove
|
|
||||||
no_members: ! '%{group} has no members.'
|
|
||||||
members:
|
|
||||||
already_members: Are already members
|
|
||||||
desc: Here you can manage members of the group or invite a new Foodcoop-member to the group %{link}.
|
|
||||||
invite: invite
|
|
||||||
invite_someone: Invite someone
|
|
||||||
no_members_yet: Are not members yet
|
|
||||||
title: Members of %{group}
|
|
||||||
non_members:
|
|
||||||
add: add
|
|
||||||
open_orders:
|
open_orders:
|
||||||
ending: Ending
|
ending: Ending
|
||||||
no_open_orders: There are no current orders
|
no_open_orders: There are no current orders
|
||||||
|
@ -1421,7 +1394,6 @@ en:
|
||||||
name: Please modify
|
name: Please modify
|
||||||
edit_stock_article:
|
edit_stock_article:
|
||||||
price: <ul><li>Price changes are forbidden.</li><li>If necessary, %{stock_article_copy_link}.</li></ul>
|
price: <ul><li>Price changes are forbidden.</li><li>If necessary, %{stock_article_copy_link}.</li></ul>
|
||||||
supplier:
|
|
||||||
supplier:
|
supplier:
|
||||||
min_order_quantity: The minimum amount which has to be orderd will be shown during the order process and should motivate ordering
|
min_order_quantity: The minimum amount which has to be orderd will be shown during the order process and should motivate ordering
|
||||||
task:
|
task:
|
||||||
|
|
|
@ -879,10 +879,7 @@ fr:
|
||||||
no_ordergroups: Tu ne fais encore partie d'aucune cellule
|
no_ordergroups: Tu ne fais encore partie d'aucune cellule
|
||||||
ordergroup:
|
ordergroup:
|
||||||
account_summary: Relevé de compte
|
account_summary: Relevé de compte
|
||||||
description: Description
|
|
||||||
funds: ! 'Crédit disponible:'
|
|
||||||
invite: Engrainer une nouvelle personne
|
invite: Engrainer une nouvelle personne
|
||||||
people: Personnes
|
|
||||||
search: Rechercher ...
|
search: Rechercher ...
|
||||||
title: Ta cellule
|
title: Ta cellule
|
||||||
ordergroup_cancelled: Tu ne fais plus partie de la cellule %{group}.
|
ordergroup_cancelled: Tu ne fais plus partie de la cellule %{group}.
|
||||||
|
@ -932,8 +929,6 @@ fr:
|
||||||
ordering:
|
ordering:
|
||||||
confirm_change:
|
confirm_change:
|
||||||
layouts:
|
layouts:
|
||||||
application1:
|
|
||||||
title: Foodsoft - %{title}
|
|
||||||
email:
|
email:
|
||||||
footer: ! '--
|
footer: ! '--
|
||||||
|
|
||||||
|
@ -1374,28 +1369,6 @@ fr:
|
||||||
search_user: Rechercher par utilisatrice
|
search_user: Rechercher par utilisatrice
|
||||||
title: Boulots hebdomadaires
|
title: Boulots hebdomadaires
|
||||||
user_not_found: Aucune utilisatrice n'a été trouvée.
|
user_not_found: Aucune utilisatrice n'a été trouvée.
|
||||||
loginInfo:
|
|
||||||
edit_profile: Modifier le profil
|
|
||||||
feedback:
|
|
||||||
desc: Tu as détecté une erreur? Tu as des propositions, des idées, des critiques?
|
|
||||||
title: Retours
|
|
||||||
help: Aide
|
|
||||||
homepage_title: Vers la page d'accueil de la bouffecoop
|
|
||||||
logout: Te déconnecter
|
|
||||||
profile: Profil
|
|
||||||
memberships:
|
|
||||||
current_members:
|
|
||||||
drop: désinscrire
|
|
||||||
no_members: ! '%{group} n''a aucun membre pour le moment.'
|
|
||||||
members:
|
|
||||||
already_members: Sont déjà membre
|
|
||||||
desc: Sur cette page, tu peux gérer les membres de l'équipe, et aussi %{link} un nouveau membre.
|
|
||||||
invite: engrainer
|
|
||||||
invite_someone: Inviter quelqu'unE
|
|
||||||
no_members_yet: Ne sont pas encore membre
|
|
||||||
title: Membre de %{group}
|
|
||||||
non_members:
|
|
||||||
add: ajouter
|
|
||||||
open_orders:
|
open_orders:
|
||||||
ending: Clôture le
|
ending: Clôture le
|
||||||
no_open_orders: Il n'y a aucune commande en cours en ce moment
|
no_open_orders: Il n'y a aucune commande en cours en ce moment
|
||||||
|
@ -1423,7 +1396,6 @@ fr:
|
||||||
name: Merci de modifier
|
name: Merci de modifier
|
||||||
edit_stock_article:
|
edit_stock_article:
|
||||||
price: <ul><li>Modification du prix enregistrée. </li><li>Si nécessaire %{stock_article_copy_link}.</li></ul>
|
price: <ul><li>Modification du prix enregistrée. </li><li>Si nécessaire %{stock_article_copy_link}.</li></ul>
|
||||||
supplier:
|
|
||||||
supplier:
|
supplier:
|
||||||
min_order_quantity: La quantité minimum à commander est affichée pendant la commande et doit motiver
|
min_order_quantity: La quantité minimum à commander est affichée pendant la commande et doit motiver
|
||||||
task:
|
task:
|
||||||
|
|
|
@ -26,15 +26,15 @@ nl:
|
||||||
description: Omschrijving
|
description: Omschrijving
|
||||||
name: Naam
|
name: Naam
|
||||||
delivery:
|
delivery:
|
||||||
delivered_on:
|
delivered_on: Leverdatum
|
||||||
note:
|
note: Notitie
|
||||||
supplier:
|
supplier: Leverancier
|
||||||
financial_transaction:
|
financial_transaction:
|
||||||
amount: bedrag
|
amount: bedrag
|
||||||
note: notitie
|
note: notitie
|
||||||
group_order_article:
|
group_order_article:
|
||||||
ordergroup_id:
|
ordergroup_id: Huishouden
|
||||||
result:
|
result: Hoeveelheid
|
||||||
invoice:
|
invoice:
|
||||||
amount: Bedrag
|
amount: Bedrag
|
||||||
date: Factuurdatum
|
date: Factuurdatum
|
||||||
|
@ -47,12 +47,12 @@ nl:
|
||||||
paid_on: Betaald op
|
paid_on: Betaald op
|
||||||
supplier: Leverancier
|
supplier: Leverancier
|
||||||
message:
|
message:
|
||||||
body:
|
body: Bericht
|
||||||
group_id:
|
group_id: Groep
|
||||||
private:
|
private: Privé
|
||||||
recipient_tokens:
|
recipient_tokens: Geadresseerden
|
||||||
sent_to_all:
|
sent_to_all: Aan alle leden sturen
|
||||||
subject:
|
subject: Onderwerp
|
||||||
order:
|
order:
|
||||||
ends: Eindigt op
|
ends: Eindigt op
|
||||||
note: Notitie
|
note: Notitie
|
||||||
|
@ -71,22 +71,22 @@ nl:
|
||||||
name: Naam
|
name: Naam
|
||||||
user_tokens: Leden
|
user_tokens: Leden
|
||||||
page:
|
page:
|
||||||
body:
|
body: Bericht
|
||||||
parent_id:
|
parent_id:
|
||||||
title:
|
title: Titel
|
||||||
stock_article:
|
stock_article:
|
||||||
price: Prijs
|
price: Prijs
|
||||||
quantity: Aantal
|
quantity: Aantal
|
||||||
quantity_available: Beschikbaar
|
quantity_available: Beschikbaar
|
||||||
supplier: Leverancier
|
supplier: Leverancier
|
||||||
stock_taking:
|
stock_taking:
|
||||||
date:
|
date: Datum
|
||||||
note:
|
note: Notitie
|
||||||
supplier:
|
supplier:
|
||||||
address: Adres
|
address: Adres
|
||||||
contact_person: Contactpersoon
|
contact_person: Contactpersoon
|
||||||
customer_number: Klantnummer
|
customer_number: Klantnummer
|
||||||
customer_number_short:
|
customer_number_short: Klantnr.
|
||||||
delivery_days: Bezorgdagen
|
delivery_days: Bezorgdagen
|
||||||
email: Email
|
email: Email
|
||||||
fax: Fax
|
fax: Fax
|
||||||
|
@ -99,14 +99,14 @@ nl:
|
||||||
phone2: Telefoon 2
|
phone2: Telefoon 2
|
||||||
url: Homepage
|
url: Homepage
|
||||||
task:
|
task:
|
||||||
description:
|
description: Beschrijving
|
||||||
done:
|
done: Gedaan?
|
||||||
due_date:
|
due_date: Voor wanneer?
|
||||||
duration:
|
duration: Tijdsduur
|
||||||
name:
|
name: Naam
|
||||||
required_users:
|
required_users: Aantal
|
||||||
user_list:
|
user_list: Verantwoordelijken
|
||||||
workgroup:
|
workgroup: Werkgroep
|
||||||
user:
|
user:
|
||||||
email: Email
|
email: Email
|
||||||
first_name: Voornaam
|
first_name: Voornaam
|
||||||
|
@ -123,7 +123,7 @@ nl:
|
||||||
workgroup:
|
workgroup:
|
||||||
description: Omschrijving
|
description: Omschrijving
|
||||||
name: Naam
|
name: Naam
|
||||||
next_weekly_tasks_number:
|
next_weekly_tasks_number: Hoeveel weken vooruit moet de taak zichtbaar zijn?
|
||||||
role_admin: Beheer
|
role_admin: Beheer
|
||||||
role_article_meta: Artikelen
|
role_article_meta: Artikelen
|
||||||
role_finance: Financiën
|
role_finance: Financiën
|
||||||
|
@ -297,7 +297,7 @@ nl:
|
||||||
error_parse: ! '%{msg} ... in regel %{line}'
|
error_parse: ! '%{msg} ... in regel %{line}'
|
||||||
error_update: ! 'Er trad een fout op bij het bijwerken van artikel ''%{article}'': %{msg}'
|
error_update: ! 'Er trad een fout op bij het bijwerken van artikel ''%{article}'': %{msg}'
|
||||||
parse_upload:
|
parse_upload:
|
||||||
notice: ! '%{count} artikel zijn geanalyseerd'
|
notice: ! '%{count} artikelen zijn geanalyseerd'
|
||||||
sync:
|
sync:
|
||||||
notice: Catalogus is bijgewerkt
|
notice: Catalogus is bijgewerkt
|
||||||
shared_alert: ! '%{supplier} is niet aan een externe database gekoppeld'
|
shared_alert: ! '%{supplier} is niet aan een externe database gekoppeld'
|
||||||
|
@ -393,9 +393,9 @@ nl:
|
||||||
title:
|
title:
|
||||||
form:
|
form:
|
||||||
actions:
|
actions:
|
||||||
article:
|
article: Artikel
|
||||||
category:
|
category: Categorie
|
||||||
create_from_blank:
|
create_from_blank: Nieuwe artikel invoeren
|
||||||
create_stock_article:
|
create_stock_article:
|
||||||
price:
|
price:
|
||||||
quantity:
|
quantity:
|
||||||
|
@ -639,7 +639,7 @@ nl:
|
||||||
title: Factuur %{number}
|
title: Factuur %{number}
|
||||||
order_articles:
|
order_articles:
|
||||||
edit:
|
edit:
|
||||||
stock_alert:
|
stock_alert: De prijs van voorraadartikelen kan niet aangepast worden!
|
||||||
title: Artikel bijwerken
|
title: Artikel bijwerken
|
||||||
new:
|
new:
|
||||||
title: Geleverd artikel aan bestelling toevoegen
|
title: Geleverd artikel aan bestelling toevoegen
|
||||||
|
@ -850,10 +850,7 @@ nl:
|
||||||
no_ordergroups: Jammergenoeg ben je niet aangesloten bij een huishouden.
|
no_ordergroups: Jammergenoeg ben je niet aangesloten bij een huishouden.
|
||||||
ordergroup:
|
ordergroup:
|
||||||
account_summary: Rekeningafschrift
|
account_summary: Rekeningafschrift
|
||||||
description: Omschrijving
|
|
||||||
funds: ! 'Beschikbaar krediet:'
|
|
||||||
invite: Iemand uitnodigen
|
invite: Iemand uitnodigen
|
||||||
people: Personen
|
|
||||||
search: Zoeken ...
|
search: Zoeken ...
|
||||||
title: Mijn huishouden
|
title: Mijn huishouden
|
||||||
ordergroup_cancelled: Je bent geen lid meer van de groep %{group}.
|
ordergroup_cancelled: Je bent geen lid meer van de groep %{group}.
|
||||||
|
@ -903,8 +900,6 @@ nl:
|
||||||
ordering:
|
ordering:
|
||||||
confirm_change:
|
confirm_change:
|
||||||
layouts:
|
layouts:
|
||||||
application1:
|
|
||||||
title: Foodsoft - %{title}
|
|
||||||
email:
|
email:
|
||||||
footer: ! '--
|
footer: ! '--
|
||||||
|
|
||||||
|
@ -1279,7 +1274,7 @@ nl:
|
||||||
price: Totaalprijs
|
price: Totaalprijs
|
||||||
articles_by_groups:
|
articles_by_groups:
|
||||||
fc_price: FC-Prijs
|
fc_price: FC-Prijs
|
||||||
fc_price_desc: Prijs inclusief belasting, borg en foodcoop-toeslag
|
fc_price_desc: Prijs inclusief belasting, statiegeld en foodcoop marge
|
||||||
name: Naam
|
name: Naam
|
||||||
price: Totaalprijs
|
price: Totaalprijs
|
||||||
unit: Eenheid
|
unit: Eenheid
|
||||||
|
@ -1301,28 +1296,6 @@ nl:
|
||||||
search_user: Gebruiker zoeken
|
search_user: Gebruiker zoeken
|
||||||
title: Wekelijkse taken
|
title: Wekelijkse taken
|
||||||
user_not_found: Geen gebruiker gevonden
|
user_not_found: Geen gebruiker gevonden
|
||||||
loginInfo:
|
|
||||||
edit_profile: Profiel aanpassen
|
|
||||||
feedback:
|
|
||||||
desc: Fout gevonden? Opmerking? Idee?
|
|
||||||
title: Feedback
|
|
||||||
help: Help
|
|
||||||
homepage_title: Foodcoop startpagina bezoeken
|
|
||||||
logout: Uitloggen
|
|
||||||
profile: Profiel
|
|
||||||
memberships:
|
|
||||||
current_members:
|
|
||||||
drop: verwijderen
|
|
||||||
no_members: ! '%{group} heeft geen leden.'
|
|
||||||
members:
|
|
||||||
already_members: Zijn al lid
|
|
||||||
desc:
|
|
||||||
invite: uitnodiging
|
|
||||||
invite_someone: Iemand uitnodigen
|
|
||||||
no_members_yet: Zijn nog geen lid
|
|
||||||
title: Leden van %{group}
|
|
||||||
non_members:
|
|
||||||
add: toevoegen
|
|
||||||
open_orders:
|
open_orders:
|
||||||
ending: Einde
|
ending: Einde
|
||||||
no_open_orders: Er zijn momenteel geen lopende bestellingen.
|
no_open_orders: Er zijn momenteel geen lopende bestellingen.
|
||||||
|
@ -1350,7 +1323,6 @@ nl:
|
||||||
name:
|
name:
|
||||||
edit_stock_article:
|
edit_stock_article:
|
||||||
price:
|
price:
|
||||||
supplier:
|
|
||||||
supplier:
|
supplier:
|
||||||
min_order_quantity:
|
min_order_quantity:
|
||||||
task:
|
task:
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
|
|
||||||
SimpleNavigation::Configuration.run do |navigation|
|
SimpleNavigation::Configuration.run do |navigation|
|
||||||
|
|
||||||
|
# allow engines to add to the menu - https://gist.github.com/mjtko/4873ee0c112b6bd646f8
|
||||||
|
engines = Rails.application.railties.engines.select { |e| e.respond_to?(:navigation) }
|
||||||
|
# to include an engine but keep it from modifying the menu:
|
||||||
|
#engines.reject! { |e| e.instance_of? FoodsoftMyplugin::Engine }
|
||||||
|
|
||||||
navigation.items do |primary|
|
navigation.items do |primary|
|
||||||
primary.dom_class = 'nav'
|
primary.dom_class = 'nav'
|
||||||
|
|
||||||
|
@ -16,11 +21,6 @@ SimpleNavigation::Configuration.run do |navigation|
|
||||||
subnav.item :tasks, I18n.t('navigation.tasks'), tasks_path, id: nil
|
subnav.item :tasks, I18n.t('navigation.tasks'), tasks_path, id: nil
|
||||||
end
|
end
|
||||||
|
|
||||||
primary.item :wiki, I18n.t('navigation.wiki.title'), '#', id: nil do |subnav|
|
|
||||||
subnav.item :wiki_home, I18n.t('navigation.wiki.home'), wiki_path, id: nil
|
|
||||||
subnav.item :all_pages, I18n.t('navigation.wiki.all_pages'), all_pages_path, id: nil
|
|
||||||
end
|
|
||||||
|
|
||||||
primary.item :orders, I18n.t('navigation.orders.title'), '#', id: nil do |subnav|
|
primary.item :orders, I18n.t('navigation.orders.title'), '#', id: nil do |subnav|
|
||||||
subnav.item :ordering, I18n.t('navigation.orders.ordering'), group_orders_path, id: nil
|
subnav.item :ordering, I18n.t('navigation.orders.ordering'), group_orders_path, id: nil
|
||||||
subnav.item :ordering_archive, I18n.t('navigation.orders.archive'), archive_group_orders_path, id: nil
|
subnav.item :ordering_archive, I18n.t('navigation.orders.archive'), archive_group_orders_path, id: nil
|
||||||
|
@ -47,6 +47,8 @@ SimpleNavigation::Configuration.run do |navigation|
|
||||||
subnav.item :ordergroups, I18n.t('navigation.admin.ordergroups'), admin_ordergroups_path, id: nil
|
subnav.item :ordergroups, I18n.t('navigation.admin.ordergroups'), admin_ordergroups_path, id: nil
|
||||||
subnav.item :workgroups, I18n.t('navigation.admin.workgroups'), admin_workgroups_path, id: nil
|
subnav.item :workgroups, I18n.t('navigation.admin.workgroups'), admin_workgroups_path, id: nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
engines.each { |e| e.navigation(primary, self) }
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,27 +18,20 @@ Foodsoft::Application.routes.draw do
|
||||||
|
|
||||||
match '/login' => 'sessions#new', :as => 'login'
|
match '/login' => 'sessions#new', :as => 'login'
|
||||||
match '/logout' => 'sessions#destroy', :as => 'logout'
|
match '/logout' => 'sessions#destroy', :as => 'logout'
|
||||||
get '/login/forgot_password' => 'login#forgot_password', as: :forgot_password
|
get '/login/forgot_password' => 'login#forgot_password', as: :forgot_password
|
||||||
get '/login/new_password' => 'login#new_password', as: :new_password
|
post '/login/reset_password' => 'login#reset_password', as: :reset_password
|
||||||
|
get '/login/new_password' => 'login#new_password', as: :new_password
|
||||||
|
get '/login/update_password' => 'login#update_password', as: :update_password
|
||||||
match '/login/accept_invitation/:token' => 'login#accept_invitation', as: :accept_invitation
|
match '/login/accept_invitation/:token' => 'login#accept_invitation', as: :accept_invitation
|
||||||
resources :sessions, :only => [:new, :create, :destroy]
|
resources :sessions, :only => [:new, :create, :destroy]
|
||||||
|
|
||||||
########### User specific
|
########### User specific
|
||||||
|
|
||||||
match '/home/profile' => 'home#profile', :as => 'my_profile'
|
match '/home/profile' => 'home#profile', :as => 'my_profile'
|
||||||
|
put '/home/update_profile' => 'home#update_profile', :as => 'update_profile'
|
||||||
match '/home/ordergroup' => 'home#ordergroup', :as => 'my_ordergroup'
|
match '/home/ordergroup' => 'home#ordergroup', :as => 'my_ordergroup'
|
||||||
match '/home/cancel_membership' => 'home#cancel_membership', :as => 'cancel_membership'
|
match '/home/cancel_membership' => 'home#cancel_membership', :as => 'cancel_membership'
|
||||||
|
|
||||||
############ Wiki
|
|
||||||
|
|
||||||
resources :pages do
|
|
||||||
get :all, :on => :collection
|
|
||||||
get :version, :on => :member
|
|
||||||
get :revert, :on => :member
|
|
||||||
end
|
|
||||||
match '/wiki/:permalink' => 'pages#show', :as => 'wiki_page' # , :constraints => {:permalink => /[^\s]+/}
|
|
||||||
match '/wiki' => 'pages#show', :defaults => {:permalink => 'Home'}, :as => 'wiki'
|
|
||||||
|
|
||||||
############ Orders, ordering
|
############ Orders, ordering
|
||||||
|
|
||||||
resources :orders do
|
resources :orders do
|
||||||
|
@ -191,8 +184,5 @@ Foodsoft::Application.routes.draw do
|
||||||
|
|
||||||
resources :users, :only => [:index]
|
resources :users, :only => [:index]
|
||||||
|
|
||||||
# TODO: This is very error prone. Better deactivate this catch all route
|
|
||||||
match ':controller(/:action(/:id))(.:format)'
|
|
||||||
|
|
||||||
end # End of /:foodcoop scope
|
end # End of /:foodcoop scope
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# This migration comes from foodsoft_wiki_engine (originally 20090325175756)
|
||||||
|
class CreatePages < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table :pages do |t|
|
||||||
|
t.string :title
|
||||||
|
t.text :body
|
||||||
|
t.string :permalink
|
||||||
|
t.integer :lock_version, :default => 0
|
||||||
|
t.integer :updated_by
|
||||||
|
t.integer :redirect
|
||||||
|
t.integer :parent_id
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
add_index :pages, :title
|
||||||
|
add_index :pages, :permalink
|
||||||
|
Page.create_versioned_table # Automaticly creates pages_versions table
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table :pages
|
||||||
|
Page.drop_versioned_table
|
||||||
|
end
|
||||||
|
end
|
93
doc/BESTELLEN.md
Normal file
93
doc/BESTELLEN.md
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
|
||||||
|
# Bestellen
|
||||||
|
Das Bestellen ist der Hauptteil dieser Software und ein wenig kompliziert.
|
||||||
|
Hier starte ich den Versuch die Programmlogik in Text umzusetzen und
|
||||||
|
verweise auf die enstprechenden Controller bzw. Modelle.
|
||||||
|
Der relevante Controller ist `OrdersController`.
|
||||||
|
|
||||||
|
## Bestellung "in Netz stellen"
|
||||||
|
Darunter verstehen wir die Auswahl von Artikeln eines bestimmten Lieferanten fuer eine zeitlich begrenzte
|
||||||
|
Bestellung im Internet. Die relevanten Methoden sind `OrdersController#newOrder` und folgende.
|
||||||
|
Jede Bestellung wird durch die Klasse Order abgebildet.
|
||||||
|
|
||||||
|
Die zugehoerigen Artikel werden duch die Klasse `OrderArticle` mit den Artikeln verknuepft.
|
||||||
|
Dabei werden auch die Attribute `quantity`, `tolerance` und `quantity_to_order` gespeichert.
|
||||||
|
Diese Mengen repraesentieren die Gesamtbestellung, also alle Bestellgruppen.
|
||||||
|
|
||||||
|
## Eine Bestellgruppe bestellt...
|
||||||
|
Die Methode `OrdersController#order` schickt uns die Bestellenseite. Mit dieser
|
||||||
|
Oberflaeche koennen die Bestellgruppena die vorher ausgewaehlten Artikel
|
||||||
|
bestellen. Mittels den Buttons werden dabei live, also clientseitig, die
|
||||||
|
Preise ermittelt und der Gesamtpreis berechnet. Ist der Gesamtpreis groeßer als
|
||||||
|
der aktuelle Gruppenkontostand, so wird die Preisspalte rot unterlegt und die
|
||||||
|
Bestellung kann nicht gespeichert werden.
|
||||||
|
|
||||||
|
## (gruppen)-Bestellung wird gespeichert
|
||||||
|
|
||||||
|
Die Gruppenbestellung wird durch die Tabelle `group_oders` (`GroupOrder`)
|
||||||
|
abgebildet, bzw. die Bestellung und Bestellgruppe wird dort verknuepft.
|
||||||
|
|
||||||
|
Die bestellten Artikel der Bestellgruppe werden durch die Tabelle `group_order_articles`
|
||||||
|
(`GroupOrderArticle`) registriert. Dort werden nun die Modelle GroupOrder
|
||||||
|
und OrderArticle miteinander verbunden.
|
||||||
|
|
||||||
|
Bei jeder Bestellung wird außerdem die Summe der Menge, Toleranz in `GroupOrderArticle`
|
||||||
|
abgelegt. Allerdings muss jede Aenderung dieser Mengen mit protokolliert werden.
|
||||||
|
Dies ist wichtig, weil spaeter die Zuteilung der Einheiten pro bestellten Artikel
|
||||||
|
nach der chronologischen Reihenfolge erfolgt. (s.u.)
|
||||||
|
Das passiert dann in der Tabelle `group_order_article_quantities`
|
||||||
|
(`GroupOrderArticleQuantity`).
|
||||||
|
|
||||||
|
## Aenderunug einer Bestellung
|
||||||
|
|
||||||
|
Knifflig ist die Aenderung einer gruppenbestellung, weil die zeitliche
|
||||||
|
Reihenfolge dabei nicht durcheinander geraten darf.
|
||||||
|
Wir unterscheiden dehalb zwei Faelle:
|
||||||
|
|
||||||
|
### Erhoehe die Menge des Arikels.
|
||||||
|
Jetzt wird eine Zeile in `group_order_article_quantities` angelegt.
|
||||||
|
und zwar mit genau den Mengen, die zusatzlich bestellt wurden.
|
||||||
|
Quantity und Tolerance funktionieren analog.
|
||||||
|
|
||||||
|
Beispiel:
|
||||||
|
* Urspruenglich bestellt: 2(2) um 17uhr.
|
||||||
|
* Erhoehe Bestellung auf 4(2) um 18hur.
|
||||||
|
=> neue Zeile mit quantity = 2, tolerance = 0, und created_on = 18uhr
|
||||||
|
* Jetzt gibt es zwei zeilen die insgesamt 4(2) ergeben.
|
||||||
|
(die summen in `GroupOrderArticle` werden aktualisiert)
|
||||||
|
|
||||||
|
### Verringere die Mengen des Artikels.
|
||||||
|
Jetzt muss chronologisch zurueckgegangen werden und um die urspruenglich bestellten
|
||||||
|
Mengen zu verringern.
|
||||||
|
|
||||||
|
Beispiel von oben:
|
||||||
|
* Verringe Bestellung auf 2(1) um 19uhr.
|
||||||
|
=> Zeile mit created_on = 18uhr wird gelöscht und
|
||||||
|
in der Zeile mit created_on = 17uhr wird der Wert tolerance auf 1 gaendert.
|
||||||
|
|
||||||
|
## Wer bekommt wieviel?
|
||||||
|
|
||||||
|
Diese Frage wird wie schon erwaehnt mittels der `group_order_article_quantites`-Tabelle
|
||||||
|
geloest.
|
||||||
|
|
||||||
|
Beispiel.
|
||||||
|
|
||||||
|
* articel x mit unit_quantity = 5.
|
||||||
|
* 17uhr: gruppe a bestellt 2(3), weil sie auf jeden fall was von x bekommen will
|
||||||
|
* 18uhr: gruppe b bestellt 2(0)
|
||||||
|
* 19uhr: gruppe a faellt ein dass sie doch noch mehr braucht von x und aendert auf 4(1).
|
||||||
|
|
||||||
|
* jetzt gibt es drei zeilen in der tabelle, die so aussehen:
|
||||||
|
* (gruppe a), 2(1), 17uhr (wurde um 19uhr von 2(3) auf 2(1) geaendert)
|
||||||
|
* (gruppe b), 2(0), 18uhr
|
||||||
|
* (gruppe a), 2(0), 19uhr.
|
||||||
|
|
||||||
|
* die zuteilung wird dann wie folgt ermittelt:
|
||||||
|
* zeile 1: gruppe a bekommt 2
|
||||||
|
* zeile 2: gruppe b bekommt 2
|
||||||
|
* zeile 3: gruppe a bekommt 1, weil jetzt das gebinde schon voll ist.
|
||||||
|
|
||||||
|
* Endstand: insg. Bestellt wurden 6(1)
|
||||||
|
* Gruppe a bekommt 3 einheiten.
|
||||||
|
* gruppe b bekommt 2 einheiten.
|
||||||
|
* eine Einheit verfaellt.
|
|
@ -1,94 +0,0 @@
|
||||||
Use this README file to introduce your application and point to useful places in the API for learning more.
|
|
||||||
Run "rake doc:app" to generate API documentation for your models and controllers.
|
|
||||||
|
|
||||||
= The Foodsoft
|
|
||||||
|
|
||||||
is a Web-based software to manage a non-profit food coop (product catalog, ordering, accounting, job scheduling).
|
|
||||||
|
|
||||||
|
|
||||||
== Bestellen
|
|
||||||
Das Bestellen ist der Hauptteil dieser Software und ein wenig kompliziert.
|
|
||||||
Hier starte ich den Versuch die Programmlogik in Text umzusetzen und
|
|
||||||
verweise auf die enstprechenden Controller bzw. Modelle.
|
|
||||||
Der relevante Controller ist OrderingController.
|
|
||||||
|
|
||||||
=== Bestellung "in Netz stellen"
|
|
||||||
Darunter verstehen wir die Auswahl von Artikeln eines bestimmten Lieferanten fuer eine zeitlich begrenzte
|
|
||||||
Bestellung im Internet. Die relevanten Methoden sind OrdersController#newOrder und folgende.
|
|
||||||
Jede Bestellung wird durch die Klasse Order abgebildet.
|
|
||||||
|
|
||||||
Die zugehoerigen Artikel werden duch die Klasse OrderArticle mit den Artikeln verknuepft.
|
|
||||||
Dabei werden auch die Attribute quantity, tolerance und quantity_to_order gespeichert.
|
|
||||||
Diese Mengen repraesentieren die Gesamtbestellung, also alle Bestellgruppen.
|
|
||||||
|
|
||||||
=== Eine Bestellgruppe bestellt...
|
|
||||||
Die Methode OrdersController#order schickt uns die Bestellenseite. Mit dieser Oberflaeche
|
|
||||||
koennen die Bestellgruppena die vorher ausgewaehlten Artikel bestellen.
|
|
||||||
Mittels den Buttons werden dabei live, also clientseitig, die Preise ermittelt
|
|
||||||
und der Gesamtpreis berechnet. Ist der Gesamtpreis groeßer als der aktuelle
|
|
||||||
Gruppenkontostand, so wird die Preisspalte rot unterlegt und die Bestellung
|
|
||||||
kann nicht gespeichert werden.
|
|
||||||
|
|
||||||
=== (gruppen)-Bestellung wird gespeichert
|
|
||||||
|
|
||||||
Die Gruppenbestellung wird durch die Tabelle group_oders (GroupOrder)
|
|
||||||
abgebildet, bzw. die Bestellung und Bestellgruppe wird dort verknuepft.
|
|
||||||
|
|
||||||
Die bestellten Artikel der Bestellgruppe werden durch die Tablle group_order_articles
|
|
||||||
(GroupOrderArticle) registriert. Dort werden nun die Modelle GroupOrder
|
|
||||||
und OrderArticle miteinander verbunden.
|
|
||||||
|
|
||||||
Bei jeder Bestellung wird außerdem die Summe der Menge, Toleranz in GroupOrderArticle
|
|
||||||
abgelegt. Allerdings muss jede Aenderung dieser Mengen mit protokolliert werden.
|
|
||||||
Dies ist wichtig, weil spaeter die Zuteilung der Einheiten pro bestellten Artikel
|
|
||||||
nach der chronologischen Reihenfolge erfolgt. (s.u.)
|
|
||||||
Das passiert dann in der Tabelle group_order_article_quantities.
|
|
||||||
(GroupOrderArticleQuantity)
|
|
||||||
|
|
||||||
=== Aenderunug einer Bestellung
|
|
||||||
|
|
||||||
Knifflig ist die Aenderung einer gruppenbestellung, weil die zeitliche
|
|
||||||
Reihenfolge dabei nicht durcheinander geraten darf.
|
|
||||||
Wir unterscheiden dehalb zwei Faelle:
|
|
||||||
==== Erhoehe die Menge des Arikels.
|
|
||||||
Jetzt wird eine Zeile in group_order_article_quantities angelegt.
|
|
||||||
und zwar mit genau den Mengen, die zusatzlich bestellt wurden.
|
|
||||||
Quantity und Tolerance funktionieren analog.
|
|
||||||
Beispiel:
|
|
||||||
Urspruenglich bestellt: 2(2) um 17uhr.
|
|
||||||
Erhoehe Bestellung auf 4(2) um 18hur.
|
|
||||||
=> neue Zeile mit quantity= 2, tolerance = 0, und created_on = 18uhr
|
|
||||||
Jetzt gibt es zwei zeilen die insgesamt 4(2) ergeben.
|
|
||||||
(die summen in GroupOrderArticle werden aktualisiert)
|
|
||||||
==== Verringere die Mengen des Artikels.
|
|
||||||
Jetzt muss chronologisch zurueckgegangen werden und um die urspruenglich bestellten
|
|
||||||
Mengen zu verringern.
|
|
||||||
Beispiel von oben:
|
|
||||||
Verringe Bestellung auf 2(1) um 19uhr.
|
|
||||||
=> Zeile mit created_on = 18uhr wird gelöscht und
|
|
||||||
in der Zeile mit created_on = 17uhr wird der Wert tolerance auf 1 gaendert.
|
|
||||||
|
|
||||||
=== Wer bekommt wieviel?
|
|
||||||
|
|
||||||
Diese Frage wird wie schon erwaehnt mittels der group_order_article_quantites -Tabelle
|
|
||||||
geloest.
|
|
||||||
Beipspiel.
|
|
||||||
articel x mit unit_quantity = 5.
|
|
||||||
17uhr: gruppe a bestellt 2(3), weil sie auf jeden fall was von x bekommen will
|
|
||||||
18uhr: gruppe b bestellt 2(0)
|
|
||||||
19uhr: gruppe a faellt ein dass sie doch noch mehr braucht von x und aendert auf 4(1).
|
|
||||||
|
|
||||||
jetzt gibt es drei zeilen in der tabelle, die so aussehen:
|
|
||||||
(gruppe a), 2(1), 17uhr (wurde um 19uhr von 2(3) auf 2(1) geaendert)
|
|
||||||
(gruppe b), 2(0), 18uhr
|
|
||||||
(gruppe a), 2(0), 19uhr.
|
|
||||||
|
|
||||||
die zuteilung wird dann wie folgt ermittelt:
|
|
||||||
zeile 1: gruppe a bekommt 2
|
|
||||||
zeile 2: gruppe b bekommt 2
|
|
||||||
zeile 3: gruppe a bekommt 1, weil jetzt das gebinde schon voll ist.
|
|
||||||
|
|
||||||
Endstand: insg. Bestellt wurden 6(1)
|
|
||||||
Gruppe a bekommt 3 einheiten.
|
|
||||||
gruppe b bekommt 2 einheiten.
|
|
||||||
eine Einheit verfaellt.
|
|
|
@ -66,7 +66,7 @@ If you want to have more control, you can do these steps manually as
|
||||||
explained here.
|
explained here.
|
||||||
|
|
||||||
|
|
||||||
1. **Configure datebase**
|
1. **Configure database**
|
||||||
|
|
||||||
Create the database configuration from the default:
|
Create the database configuration from the default:
|
||||||
```sh
|
```sh
|
||||||
|
|
18
lib/foodsoft_wiki/README.md
Normal file
18
lib/foodsoft_wiki/README.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
FoodsoftWiki
|
||||||
|
============
|
||||||
|
|
||||||
|
This plugin adds wiki pages to foodsoft. A new 'Wiki' menu is added next to
|
||||||
|
the 'Foodcoops' menu in the navigation bar.
|
||||||
|
|
||||||
|
This plugin is enabled by default in foodsoft, so you don't need to do anything
|
||||||
|
to install it. If you still want to, for example when it has been disabled,
|
||||||
|
add the following to foodsoft's Gemfile:
|
||||||
|
|
||||||
|
```Gemfile
|
||||||
|
# we use the git version of acts_as_versioned, so this needs to be in foodsoft's Gemfile
|
||||||
|
gem 'acts_as_versioned', git: 'git://github.com/technoweenie/acts_as_versioned.git'
|
||||||
|
gem 'foodsoft_wiki', path: 'lib/foodsoft_wiki'
|
||||||
|
```
|
||||||
|
|
||||||
|
This plugin is part of the foodsoft package and uses the GPL-3 license (see
|
||||||
|
foodsoft's LICENSE for the full license text).
|
40
lib/foodsoft_wiki/Rakefile
Normal file
40
lib/foodsoft_wiki/Rakefile
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env rake
|
||||||
|
begin
|
||||||
|
require 'bundler/setup'
|
||||||
|
rescue LoadError
|
||||||
|
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
||||||
|
end
|
||||||
|
begin
|
||||||
|
require 'rdoc/task'
|
||||||
|
rescue LoadError
|
||||||
|
require 'rdoc/rdoc'
|
||||||
|
require 'rake/rdoctask'
|
||||||
|
RDoc::Task = Rake::RDocTask
|
||||||
|
end
|
||||||
|
|
||||||
|
RDoc::Task.new(:rdoc) do |rdoc|
|
||||||
|
rdoc.rdoc_dir = 'rdoc'
|
||||||
|
rdoc.title = 'FoodsoftWiki'
|
||||||
|
rdoc.options << '--line-numbers'
|
||||||
|
rdoc.rdoc_files.include('README.rdoc')
|
||||||
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||||
|
end
|
||||||
|
|
||||||
|
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
||||||
|
load 'rails/tasks/engine.rake'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Bundler::GemHelper.install_tasks
|
||||||
|
|
||||||
|
require 'rake/testtask'
|
||||||
|
|
||||||
|
Rake::TestTask.new(:test) do |t|
|
||||||
|
t.libs << 'lib'
|
||||||
|
t.libs << 'test'
|
||||||
|
t.pattern = 'test/**/*_test.rb'
|
||||||
|
t.verbose = false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
task :default => :test
|
15
lib/foodsoft_wiki/config/routes.rb
Normal file
15
lib/foodsoft_wiki/config/routes.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
Rails.application.routes.draw do
|
||||||
|
|
||||||
|
scope '/:foodcoop' do
|
||||||
|
|
||||||
|
resources :pages do
|
||||||
|
get :all, :on => :collection
|
||||||
|
get :version, :on => :member
|
||||||
|
get :revert, :on => :member
|
||||||
|
end
|
||||||
|
match '/wiki/:permalink' => 'pages#show', :as => 'wiki_page' # , :constraints => {:permalink => /[^\s]+/}
|
||||||
|
match '/wiki' => 'pages#show', :defaults => {:permalink => 'Home'}, :as => 'wiki'
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
24
lib/foodsoft_wiki/foodsoft_wiki.gemspec
Normal file
24
lib/foodsoft_wiki/foodsoft_wiki.gemspec
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
$:.push File.expand_path("../lib", __FILE__)
|
||||||
|
|
||||||
|
# Maintain your gem's version:
|
||||||
|
require "foodsoft_wiki/version"
|
||||||
|
|
||||||
|
# Describe your gem and declare its dependencies:
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = "foodsoft_wiki"
|
||||||
|
s.version = FoodsoftWiki::VERSION
|
||||||
|
s.authors = ["wvengen"]
|
||||||
|
s.email = ["dev-foodsoft@willem.engen.nl"]
|
||||||
|
s.homepage = "https://github.com/foodcoops/foodsoft"
|
||||||
|
s.summary = "Wiki plugin for foodsoft."
|
||||||
|
s.description = "Adds a wiki to foodsoft."
|
||||||
|
|
||||||
|
s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"]
|
||||||
|
s.test_files = Dir["test/**/*"]
|
||||||
|
|
||||||
|
s.add_dependency "rails", "~> 3.2.15"
|
||||||
|
s.add_dependency 'wikicloth'
|
||||||
|
s.add_dependency 'acts_as_versioned' # need git version, make sure that is included in foodsoft's Gemfile
|
||||||
|
|
||||||
|
s.add_development_dependency "sqlite3"
|
||||||
|
end
|
6
lib/foodsoft_wiki/lib/foodsoft_wiki.rb
Normal file
6
lib/foodsoft_wiki/lib/foodsoft_wiki.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
require 'wikicloth'
|
||||||
|
require 'acts_as_versioned'
|
||||||
|
require 'foodsoft_wiki/engine'
|
||||||
|
|
||||||
|
module FoodsoftWiki
|
||||||
|
end
|
14
lib/foodsoft_wiki/lib/foodsoft_wiki/engine.rb
Normal file
14
lib/foodsoft_wiki/lib/foodsoft_wiki/engine.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
module FoodsoftWiki
|
||||||
|
class Engine < ::Rails::Engine
|
||||||
|
def navigation(primary, ctx)
|
||||||
|
primary.item :wiki, I18n.t('navigation.wiki.title'), '#', id: nil do |subnav|
|
||||||
|
subnav.item :wiki_home, I18n.t('navigation.wiki.home'), ctx.wiki_path, id: nil
|
||||||
|
subnav.item :all_pages, I18n.t('navigation.wiki.all_pages'), ctx.all_pages_path, id: nil
|
||||||
|
end
|
||||||
|
# move this last added item to just after the foodcoop menu
|
||||||
|
if i = primary.items.index(primary[:foodcoop])
|
||||||
|
primary.items.insert(i+1, primary.items.delete_at(-1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
3
lib/foodsoft_wiki/lib/foodsoft_wiki/version.rb
Normal file
3
lib/foodsoft_wiki/lib/foodsoft_wiki/version.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module FoodsoftWiki
|
||||||
|
VERSION = "0.0.1"
|
||||||
|
end
|
|
@ -1,5 +1,6 @@
|
||||||
begin
|
begin
|
||||||
require 'rspec/core/rake_task'
|
require 'rspec/core/rake_task'
|
||||||
|
task(:spec).clear
|
||||||
RSpec::Core::RakeTask.new(:spec)
|
RSpec::Core::RakeTask.new(:spec)
|
||||||
task :default => :spec
|
task :default => :spec
|
||||||
rescue LoadError
|
rescue LoadError
|
||||||
|
|
|
@ -52,4 +52,58 @@ describe Article do
|
||||||
order = create :order, supplier: supplier, article_ids: [article.id]
|
order = create :order, supplier: supplier, article_ids: [article.id]
|
||||||
expect(article.in_open_order).to eq(order)
|
expect(article.in_open_order).to eq(order)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it 'has no shared article by default' do
|
||||||
|
expect(article.shared_article).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'connected to a shared database', :type => :feature do
|
||||||
|
let(:shared_supplier) { create(:supplier) }
|
||||||
|
let(:shared_article) { create :article, supplier: shared_supplier, order_number: Faker::Lorem.characters(rand(1..12)) }
|
||||||
|
let(:supplier) { create :supplier, shared_supplier_id: shared_supplier.id }
|
||||||
|
let(:article) { create :article, supplier: supplier, order_number: shared_article.order_number }
|
||||||
|
|
||||||
|
it 'can be found in the shared database' do
|
||||||
|
expect(article.shared_article).to_not be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can find updates' do
|
||||||
|
changed = article.shared_article_changed?
|
||||||
|
expect(changed).to_not be_false
|
||||||
|
expect(changed.length).to be > 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can be synchronised' do
|
||||||
|
# TODO move article sync from supplier to article
|
||||||
|
article # need to reference for it to exist when syncing
|
||||||
|
updated_article = supplier.sync_all[0].select{|s| s[0].id==article.id}.first[0]
|
||||||
|
article.update_attributes updated_article.attributes.reject{|k,v| k=='id' or k=='type'}
|
||||||
|
expect(article.name).to eq(shared_article.name)
|
||||||
|
# now synchronising shouldn't change anything anymore
|
||||||
|
expect(article.shared_article_changed?).to be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not need to synchronise an imported article' do
|
||||||
|
article = SharedArticle.find(shared_article.id).build_new_article(supplier)
|
||||||
|
expect(article.shared_article_changed?).to be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adapts to foodcoop units when synchronising' do
|
||||||
|
shared_article.unit = '1kg'
|
||||||
|
shared_article.unit_quantity = 1
|
||||||
|
shared_article.save!
|
||||||
|
article = SharedArticle.find(shared_article.id).build_new_article(supplier)
|
||||||
|
article.article_category = create :article_category
|
||||||
|
article.unit = '200g'
|
||||||
|
article.shared_updated_on -= 1 # to make update do something
|
||||||
|
article.save!
|
||||||
|
# TODO get sync functionality in article
|
||||||
|
updated_article = supplier.sync_all[0].select{|s| s[0].id==article.id}.first[0]
|
||||||
|
article.update_attributes! updated_article.attributes.reject{|k,v| k=='id' or k=='type'}
|
||||||
|
expect(article.unit).to eq '200g'
|
||||||
|
expect(article.unit_quantity).to eq 5
|
||||||
|
expect(article.price).to be_within(1e-3).of(shared_article.price/5)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,6 +29,8 @@ RSpec.configure do |config|
|
||||||
config.before(:each) do
|
config.before(:each) do
|
||||||
DatabaseCleaner.strategy = (example.metadata[:js] ? :truncation : :transaction)
|
DatabaseCleaner.strategy = (example.metadata[:js] ? :truncation : :transaction)
|
||||||
DatabaseCleaner.start
|
DatabaseCleaner.start
|
||||||
|
# maximise window so that buttons can be found on popups
|
||||||
|
example.metadata[:js] and page.driver.browser.manage.window.maximize
|
||||||
end
|
end
|
||||||
config.after(:each) do
|
config.after(:each) do
|
||||||
DatabaseCleaner.clean
|
DatabaseCleaner.clean
|
||||||
|
|
29
spec/support/shared_database.rb
Normal file
29
spec/support/shared_database.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# http://stackoverflow.com/questions/8774227
|
||||||
|
# http://blog.plataformatec.com.br/2011/12/three-tips-to-improve-the-performance-of-your-test-suite
|
||||||
|
class ActiveRecord::Base
|
||||||
|
mattr_accessor :shared_connection
|
||||||
|
@@shared_connection = nil
|
||||||
|
|
||||||
|
def self.connection
|
||||||
|
@@shared_connection || retrieve_connection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# Forces all threads to share the same connection. This works on
|
||||||
|
# Capybara because it starts the web server in a thread.
|
||||||
|
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
|
||||||
|
|
||||||
|
ActiveSupport.on_load(:after_initialize) do
|
||||||
|
# We simulate the shared database by pointing to our own database.
|
||||||
|
# This allows running tests without additional database setup.
|
||||||
|
# But take care when designing tests using the shared database.
|
||||||
|
SharedSupplier.establish_connection Rails.env
|
||||||
|
SharedArticle.establish_connection Rails.env
|
||||||
|
# hack for different structure of shared database
|
||||||
|
SharedArticle.class_eval do
|
||||||
|
alias_attribute :number, :order_number
|
||||||
|
alias_attribute :updated_on, :updated_at
|
||||||
|
def self.find_by_number(n)
|
||||||
|
find_by_order_number(n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
0
vendor/plugins/.gitkeep
vendored
0
vendor/plugins/.gitkeep
vendored
Loading…
Reference in a new issue