Merge branch 'master' into updated-gems

Conflicts:
	Gemfile
	Gemfile.lock
This commit is contained in:
wvengen 2013-10-09 10:37:02 +02:00
commit bdce1b5872
64 changed files with 1039 additions and 472 deletions

View file

@ -18,6 +18,7 @@ end
gem 'jquery-rails' gem 'jquery-rails'
gem 'select2-rails' gem 'select2-rails'
gem 'bootstrap-datepicker-rails' gem 'bootstrap-datepicker-rails'
gem 'rails-assets-listjs', '0.2.0.beta.4' # remember to maintain list.*.js plugins and template engines on update
gem 'mysql2' gem 'mysql2'
gem 'prawn' gem 'prawn'

View file

@ -202,6 +202,8 @@ GEM
activesupport (= 3.2.14) activesupport (= 3.2.14)
bundler (~> 1.0) bundler (~> 1.0)
railties (= 3.2.14) railties (= 3.2.14)
rails-assets-listjs (0.2.0.beta.4)
railties (>= 3.1)
rails-settings-cached (0.2.4) rails-settings-cached (0.2.4)
rails (>= 3.0.0) rails (>= 3.0.0)
railties (3.2.14) railties (3.2.14)
@ -346,6 +348,7 @@ DEPENDENCIES
prawn prawn
quiet_assets quiet_assets
rails (~> 3.2.9) rails (~> 3.2.9)
rails-assets-listjs (= 0.2.0.beta.4)
rails-settings-cached (= 0.2.4) rails-settings-cached (= 0.2.4)
resque resque
rspec-core rspec-core

View file

@ -1,8 +1,9 @@
FoodSoft FoodSoft
========= =========
[![Build Status](https://travis-ci.org/foodcoops/foodsoft.png?branch=tests-rspec)](https://travis-ci.org/foodcoops/foodsoft) [![Build Status](https://travis-ci.org/foodcoops/foodsoft.png)](https://travis-ci.org/foodcoops/foodsoft)
[![Code Climate](https://codeclimate.com/github/foodcoops/foodsoft.png)](https://codeclimate.com/github/foodcoops/foodsoft) [![Code Climate](https://codeclimate.com/github/foodcoops/foodsoft.png)](https://codeclimate.com/github/foodcoops/foodsoft)
[![Dependency Status](https://gemnasium.com/foodcoops/foodsoft.png)](https://gemnasium.com/foodcoops/foodsoft) [![Dependency Status](https://gemnasium.com/foodcoops/foodsoft.png)](https://gemnasium.com/foodcoops/foodsoft)
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/foodcoops/foodsoft/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
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).

View file

@ -7,6 +7,10 @@
//= require bootstrap-datepicker/locales/bootstrap-datepicker.de //= require bootstrap-datepicker/locales/bootstrap-datepicker.de
//= require bootstrap-datepicker/locales/bootstrap-datepicker.nl //= require bootstrap-datepicker/locales/bootstrap-datepicker.nl
//= require jquery.observe_field //= require jquery.observe_field
//= require list
//= require list.unlist
//= require list.delay
//= require list.reset
//= require rails.validations //= require rails.validations
//= require rails.validations.simple_form //= require rails.validations.simple_form
//= require_self //= require_self

View file

@ -0,0 +1,50 @@
// for use with listjs 0.2.0
// https://github.com/javve/list.js
(function(window, undefined) {
window.List.prototype.plugins.delay = function(locals, options) {
var list = this;
this.searchTimeout = undefined;
var init = {
start: function(options) {
this.defaults(options);
this.callbacks(options);
this.onload(options);
},
defaults: function(options) {
options.delayedSearchClass = options.delayedSearchClass || 'delayed-search';
options.delayedSearchTime = options.delayedSearchTime || 500;
},
callbacks: function(options) {
$('.' + options.delayedSearchClass, list.listContainer).keyup(list.searchDelayStart);
},
onload: function(options) {
var initialSearchTerm = $('.' + options.delayedSearchClass, list.listContainer).val();
if('' != initialSearchTerm) {
list.search(initialSearchTerm);
}
}
};
this.searchDelayStart = function(searchString, columns) {
// TODO: if keycode corresponds to 'ENTER' ? skip delay
clearTimeout(list.searchTimeout);
list.searchTimeout = window.setTimeout(
function() {list.searchDelayEnd(searchString, columns)},
options.delayedSearchTime
);
$(list.listContainer).trigger('updateComing');
};
this.searchDelayEnd = function(searchString, columns) {
list.search(searchString, columns);
};
init.start(options);
}
})(window);

View file

@ -0,0 +1,42 @@
// for use with listjs 0.2.0
// https://github.com/javve/list.js
(function(window, undefined) {
window.List.prototype.plugins.reset = function(locals, options) {
var list = this;
var init = {
start: function(options) {
this.defaults(options);
this.callbacks(options);
},
defaults: function(options) {
options.highlightClass = options.highlightClass || 'btn-primary';
options.resetSearchClass = options.resetSearchClass || 'reset-search';
options.resettableClass = options.resettableClass || 'resettable';
},
callbacks: function(options) {
$('.' + options.resetSearchClass, list.listContainer).click(list.resetSearch);
list.on('updated', list.highlightResetButton);
$(list.listContainer).on('updateComing', function() {
list.highlightResetButton(false);
});
}
};
this.highlightResetButton = function(highlightEnabled) {
highlightEnabled = (undefined === highlightEnabled) ? (list.searched) : (highlightEnabled);
$('.' + options.resetSearchClass, list.listContainer).toggleClass(options.highlightClass, highlightEnabled);
};
this.resetSearch = function() {
$('.' + options.resettableClass, list.listContainer).val('');
list.search('');
};
init.start(options);
}
})(window);

View file

@ -0,0 +1,124 @@
// for use with listjs 0.2.0
// https://github.com/javve/list.js
/*******************************************************************************
********************************************************************************
The following code is a modification of list.js. It was created by copy-pasting
the original code with the copyright notice below.
********************************************************************************
*******************************************************************************/
/*******************************************************************************
Begin copyright notice of the original code
*******************************************************************************/
/*
ListJS Beta 0.2.0
By Jonny Strömberg (www.jonnystromberg.com, www.listjs.com)
OBS. The API is not frozen. It MAY change!
License (MIT)
Copyright (c) 2011 Jonny Strömberg http://jonnystromberg.com
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
/*******************************************************************************
End copyright notice of the original code
*******************************************************************************/
(function(w, undefined) {
/*******************************************************************************
Begin copy-pasted and modified code
*******************************************************************************/
// * template engine which adds class 'unlisted' instead of removing from DOM
// * especially useful in case of formulars
// * uses jQuery's $
w.List.prototype.templateEngines.unlist = function(list, settings) {
var h = w.ListJsHelpers;
// start with standard engine, override specific methods afterwards
this.superClass = w.List.prototype.templateEngines.standard;
this.superClass(list, settings);
// todo refer to listjs code instead of copy-pasting
var listSource = h.getByClass(settings.listClass, list.listContainer, true);
var templater = this;
var ensure = {
created: function(item) {
if(item.elm === undefined) {
templater.create(item);
}
}
};
var init = {
start: function(options) {
this.defaults(options);
this.callbacks(options);
},
defaults: function(options) {
options.listHeadingsClass = options.listHeadingsClass || 'list-heading';
},
callbacks: function(options) {
list.on('updated', templater.updateListHeadings);
}
};
this.show = function(item) {
ensure.created(item);
listSource.appendChild(item.elm); // append item (or move it to the end)
$(item.elm).removeClass('unlisted');
};
this.hide = function(item) {
ensure.created(item);
$(item.elm).addClass('unlisted');
listSource.appendChild(item.elm);
};
this.clear = function() {
$(listSource.childNodes).addClass('unlisted');
};
this.updateListHeadings = function() {
var headSel = '.' + settings.listHeadingsClass;
$(headSel, listSource).each(function() {
var listedCount = $(this).nextUntil(headSel, ':not(.unlisted)').length;
$(this).toggleClass('unlisted', 0==listedCount);
});
};
init.start(settings);
};
/*******************************************************************************
End copy-pasted and modified code
*******************************************************************************/
})(window);

View file

@ -3,4 +3,5 @@
*= require select2 *= require select2
*= require token-input-bootstrappy *= require token-input-bootstrappy
*= require bootstrap-datepicker *= require bootstrap-datepicker
*= require list.unlist
*/ */

View file

@ -237,3 +237,8 @@ tr.unavailable {
margin-bottom: 15px margin-bottom: 15px
} }
} }
// allow buttons as input add-on (with proper height)
.input-append button.add-on {
height: inherit;
}

View file

@ -0,0 +1,3 @@
.list .unlisted:not(.no-unlist) {
display: none;
}

View file

@ -26,7 +26,7 @@ class ApplicationController < ActionController::Base
def deny_access def deny_access
session[:return_to] = request.original_url session[:return_to] = request.original_url
redirect_to login_url, :alert => 'Access denied!' redirect_to login_url, :alert => I18n.t('application.controller.error_denied')
end end
private private
@ -37,7 +37,7 @@ class ApplicationController < ActionController::Base
# No user at all: redirect to login page. # No user at all: redirect to login page.
session[:user_id] = nil session[:user_id] = nil
session[:return_to] = request.original_url session[:return_to] = request.original_url
redirect_to login_url, :alert => 'Authentication required!' redirect_to login_url, :alert => I18n.t('application.controller.error_authn')
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.
@ -83,7 +83,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?
redirect_to root_path, alert: "Diese Aktion ist nur für Mitglieder der Gruppe erlaubt!" redirect_to root_path, alert: I18n.t('application.controller.error_members_only')
end end
end end

View file

@ -10,7 +10,7 @@ class Finance::BalancingController < Finance::BaseController
flash.now.alert = t('finance.balancing.new.alert') if @order.closed? flash.now.alert = t('finance.balancing.new.alert') if @order.closed?
@comments = @order.comments @comments = @order.comments
@articles = @order.order_articles.ordered.includes(:article, :article_price, @articles = @order.order_articles.ordered_or_member.includes(:article, :article_price,
group_order_articles: {group_order: :ordergroup}) group_order_articles: {group_order: :ordergroup})
sort_param = params['sort'] || 'name' sort_param = params['sort'] || 'name'

View file

@ -34,7 +34,7 @@ class Finance::FinancialTransactionsController < ApplicationController
@financial_transaction = FinancialTransaction.new(params[:financial_transaction]) @financial_transaction = FinancialTransaction.new(params[:financial_transaction])
@financial_transaction.user = current_user @financial_transaction.user = current_user
@financial_transaction.add_transaction! @financial_transaction.add_transaction!
redirect_to finance_ordergroup_transactions_url(@ordergroup), notice: t('finance.financial_transactions.create.notice') redirect_to finance_ordergroup_transactions_url(@ordergroup), notice: I18n.t('finance.financial_transactions.controller.create.notice')
rescue ActiveRecord::RecordInvalid => error rescue ActiveRecord::RecordInvalid => error
flash.now[:alert] = error.message flash.now[:alert] = error.message
render :action => :new render :action => :new
@ -44,16 +44,16 @@ class Finance::FinancialTransactionsController < ApplicationController
end end
def create_collection def create_collection
raise "Notiz wird benötigt!" if params[:note].blank? raise I18n.t('finance.financial_transactions.controller.create_collection.error_note_required') if params[:note].blank?
params[:financial_transactions].each do |trans| params[:financial_transactions].each do |trans|
# ignore empty amount fields ... # ignore empty amount fields ...
unless trans[:amount].blank? unless trans[:amount].blank?
Ordergroup.find(trans[:ordergroup_id]).add_financial_transaction!(trans[:amount], params[:note], @current_user) Ordergroup.find(trans[:ordergroup_id]).add_financial_transaction!(trans[:amount], params[:note], @current_user)
end end
end end
redirect_to finance_ordergroups_url, notice: t('finance.create_collection.create.notice') redirect_to finance_ordergroups_url, notice: I18n.t('finance.financial_transactions.controller.create_collection.notice')
rescue => error rescue => error
redirect_to finance_new_transaction_collection_url, alert: t('finance.create_collection.create.alert', error: error.to_s) redirect_to finance_new_transaction_collection_url, alert: I18n.t('finance.financial_transactions.controller.create_collection.alert', error: error.to_s)
end end
protected protected

View file

@ -65,7 +65,13 @@ class Finance::GroupOrderArticlesController < ApplicationController
def destroy def destroy
group_order_article = GroupOrderArticle.find(params[:id]) group_order_article = GroupOrderArticle.find(params[:id])
# only destroy if quantity and tolerance was zero already, so that we don't
# lose what the user ordered, if any
if group_order_article.quantity > 0 or group_order_article.tolerance >0
group_order_article.update_attribute(:result, 0)
else
group_order_article.destroy group_order_article.destroy
end
update_summaries(group_order_article) update_summaries(group_order_article)
@order_article = group_order_article.order_article @order_article = group_order_article.order_article

View file

@ -42,6 +42,14 @@ class Finance::OrderArticlesController < ApplicationController
def destroy def destroy
@order_article = OrderArticle.find(params[:id]) @order_article = OrderArticle.find(params[:id])
# only destroy if there are no associated GroupOrders; if we would, the requested
# quantity and tolerance would be gone. Instead of destroying, we set all result
# quantities to zero.
if @order_article.group_order_articles.count == 0
@order_article.destroy @order_article.destroy
else
@order_article.group_order_articles.each { |goa| goa.update_attribute(:result, 0) }
@order_article.update_results!
end
end end
end end

View file

@ -31,6 +31,11 @@ class StockitController < ApplicationController
end end
end end
def show
@stock_article = StockArticle.find(params[:id])
@stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC')
end
def destroy def destroy
@article = StockArticle.find(params[:id]) @article = StockArticle.find(params[:id])
@article.mark_as_deleted @article.mark_as_deleted
@ -55,9 +60,4 @@ class StockitController < ApplicationController
render :partial => 'form', :locals => {:stock_article => stock_article} render :partial => 'form', :locals => {:stock_article => stock_article}
end end
def history
@stock_article = StockArticle.undeleted.find(params[:stock_article_id])
@stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC').each {|s| s.readonly!}
end
end end

View file

@ -12,20 +12,29 @@ class OrderByArticles < OrderPdf
def body def body
@order.order_articles.ordered.each do |order_article| @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 = []
rows << I18n.t('documents.order_by_articles.rows') dimrows = []
for goa in order_article.group_order_articles for goa in order_article.group_order_articles.ordered
rows << [goa.group_order.ordergroup.name, rows << [goa.group_order.ordergroup.name,
"#{goa.quantity} + #{goa.tolerance}",
goa.result, goa.result,
number_with_precision(order_article.price.fc_price * goa.result, precision: 2)] number_with_precision(order_article.price.fc_price * goa.result, precision: 2)]
dimrows << rows.length if goa.result == 0
end end
next if rows.length == 0
rows.unshift I18n.t('documents.order_by_articles.rows') # table header
table rows, column_widths: [200,40,40], cell_style: {size: 8, overflow: :shrink_to_fit} do |table| 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)})",
table.columns(1..2).align = :right style: :bold, size: 10
table rows, cell_style: {size: 8, overflow: :shrink_to_fit} do |table|
table.column(0).width = 200
table.columns(1..3).align = :right
table.column(2).font_style = :bold
table.cells.border_width = 1 table.cells.border_width = 1
table.cells.border_color = '666666' table.cells.border_color = '666666'
table.rows(0).border_bottom_width = 2
# dim rows which were ordered but not received
dimrows.each { |ri| table.row(ri).text_color = '999999' }
end end
move_down 10 move_down 10
end end

View file

@ -12,12 +12,10 @@ class OrderByGroups < OrderPdf
def body def body
# Start rendering # Start rendering
@order.group_orders.each do |group_order| @order.group_orders.ordered.each do |group_order|
text group_order.ordergroup.name, size: 9, style: :bold
total = 0 total = 0
rows = [] rows = []
rows << I18n.t('documents.order_by_groups.rows') # Table Header dimrows = []
group_order_articles = group_order.group_order_articles.ordered group_order_articles = group_order.group_order_articles.ordered
group_order_articles.each do |goa| group_order_articles.each do |goa|
@ -25,15 +23,20 @@ class OrderByGroups < OrderPdf
sub_total = price * goa.result sub_total = price * goa.result
total += sub_total total += sub_total
rows << [goa.order_article.article.name, rows << [goa.order_article.article.name,
"#{goa.quantity} + #{goa.tolerance}",
goa.result, goa.result,
number_with_precision(price, precision: 2), number_with_precision(price, precision: 2),
goa.order_article.price.unit_quantity, goa.order_article.price.unit_quantity,
goa.order_article.article.unit, goa.order_article.article.unit,
number_with_precision(sub_total, precision: 2)] number_with_precision(sub_total, precision: 2)]
dimrows << rows.length if goa.result == 0
end end
rows << [ I18n.t('documents.order_by_groups.sum'), nil, nil, nil, nil, number_with_precision(total, precision: 2)] next if rows.length == 0
rows << [ I18n.t('documents.order_by_groups.sum'), nil, nil, nil, nil, nil, number_with_precision(total, precision: 2)]
rows.unshift I18n.t('documents.order_by_groups.rows') # Table Header
table rows, column_widths: [250,50,50,50,50,50], cell_style: {size: 8, overflow: :shrink_to_fit} do |table| text group_order.ordergroup.name, size: 9, style: :bold
table rows, width: 500, cell_style: {size: 8, overflow: :shrink_to_fit} do |table|
# borders # borders
table.cells.borders = [] table.cells.borders = []
table.row(0).borders = [:bottom] table.row(0).borders = [:bottom]
@ -41,8 +44,14 @@ class OrderByGroups < OrderPdf
table.cells.border_width = 1 table.cells.border_width = 1
table.cells.border_color = '666666' table.cells.border_color = '666666'
table.columns(1..3).align = :right table.column(0).width = 240
table.columns(5).align = :right table.column(2).font_style = :bold
table.columns(1..4).align = :right
table.column(6).align = :right
table.column(6).font_style = :bold
# dim rows which were ordered but not received
dimrows.each { |ri| table.row(ri).text_color = '999999' }
end end
move_down 15 move_down 15

View file

@ -1,6 +1,5 @@
# encoding: utf-8 # encoding: utf-8
class Article < ActiveRecord::Base class Article < ActiveRecord::Base
extend ActiveSupport::Memoizable # Ability to cache method results. Use memoize :expensive_method
# Replace numeric seperator with database format # Replace numeric seperator with database format
localize_input_of :price, :tax, :deposit localize_input_of :price, :tax, :deposit
@ -44,11 +43,12 @@ class Article < ActiveRecord::Base
# If the article is used in an open Order, the Order will be returned. # If the article is used in an open Order, the Order will be returned.
def in_open_order def in_open_order
@in_open_order ||= begin
order_articles = OrderArticle.all(:conditions => ['order_id IN (?)', Order.open.collect(&:id)]) order_articles = OrderArticle.all(:conditions => ['order_id IN (?)', Order.open.collect(&:id)])
order_article = order_articles.detect {|oa| oa.article_id == id } order_article = order_articles.detect {|oa| oa.article_id == id }
order_article ? order_article.order : nil order_article ? order_article.order : nil
end end
memoize :in_open_order end
# Returns true if the article has been ordered in the given order at least once # Returns true if the article has been ordered in the given order at least once
def ordered_in_order?(order) def ordered_in_order?(order)

View file

@ -17,6 +17,8 @@ class GroupOrder < ActiveRecord::Base
scope :in_open_orders, joins(:order).merge(Order.open) scope :in_open_orders, joins(:order).merge(Order.open)
scope :in_finished_orders, joins(:order).merge(Order.finished_not_closed) scope :in_finished_orders, joins(:order).merge(Order.finished_not_closed)
scope :ordered, :include => :ordergroup, :order => 'groups.name'
# Generate some data for the javascript methods in ordering view # Generate some data for the javascript methods in ordering view
def load_data def load_data
data = {} data = {}

View file

@ -2,7 +2,6 @@
# The chronologically order of the Ordergroup - activity are stored in GroupOrderArticleQuantity # The chronologically order of the Ordergroup - activity are stored in GroupOrderArticleQuantity
# #
class GroupOrderArticle < ActiveRecord::Base class GroupOrderArticle < ActiveRecord::Base
extend ActiveSupport::Memoizable # Ability to cache method results. Use memoize :expensive_method
belongs_to :group_order belongs_to :group_order
belongs_to :order_article belongs_to :order_article
@ -14,7 +13,7 @@ class GroupOrderArticle < ActiveRecord::Base
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
scope :ordered, :conditions => 'result > 0' scope :ordered, :conditions => 'group_order_articles.result > 0 OR group_order_articles.quantity > 0 OR group_order_articles.tolerance > 0', :include => {:group_order => :ordergroup}, :order => 'groups.name'
localize_input_of :result localize_input_of :result
@ -101,6 +100,7 @@ class GroupOrderArticle < ActiveRecord::Base
# #
# See description of the ordering algorithm in the general application documentation for details. # See description of the ordering algorithm in the general application documentation for details.
def calculate_result def calculate_result
@calculate_result ||= begin
quantity = tolerance = 0 quantity = tolerance = 0
stockit = order_article.article.is_a?(StockArticle) stockit = order_article.article.is_a?(StockArticle)
@ -148,7 +148,7 @@ class GroupOrderArticle < ActiveRecord::Base
{:quantity => quantity, :tolerance => tolerance, :total => quantity + tolerance} {:quantity => quantity, :tolerance => tolerance, :total => quantity + tolerance}
end end
memoize :calculate_result end
# Returns order result, # Returns order result,
# either calcualted on the fly or fetched from result attribute # either calcualted on the fly or fetched from result attribute
@ -167,7 +167,7 @@ class GroupOrderArticle < ActiveRecord::Base
# the minimum price depending on configuration. When the order is finished it # the minimum price depending on configuration. When the order is finished it
# will be the value depending of the article results. # will be the value depending of the article results.
def total_price(order_article = self.order_article) def total_price(order_article = self.order_article)
unless order_article.order.finished? if order_article.order.open?
if FoodsoftConfig[:tolerance_is_costly] if FoodsoftConfig[:tolerance_is_costly]
order_article.article.fc_price * (quantity + tolerance) order_article.article.fc_price * (quantity + tolerance)
else else

View file

@ -2,6 +2,8 @@
# #
class Order < ActiveRecord::Base class Order < ActiveRecord::Base
attr_accessor :ignore_warnings
# Associations # Associations
has_many :order_articles, :dependent => :destroy has_many :order_articles, :dependent => :destroy
has_many :articles, :through => :order_articles has_many :articles, :through => :order_articles
@ -17,6 +19,7 @@ class Order < ActiveRecord::Base
# Validations # Validations
validates_presence_of :starts validates_presence_of :starts
validate :starts_before_ends, :include_articles validate :starts_before_ends, :include_articles
validate :keep_ordered_articles
# Callbacks # Callbacks
after_save :save_order_articles, :update_price_of_group_orders after_save :save_order_articles, :update_price_of_group_orders
@ -55,7 +58,12 @@ class Order < ActiveRecord::Base
end end
def article_ids def article_ids
@article_ids ||= order_articles.map(&:article_id) @article_ids ||= order_articles.map { |a| a.article_id.to_s }
end
# Returns an array of article ids that lead to a validation error.
def erroneous_article_ids
@erroneous_article_ids ||= []
end end
def open? def open?
@ -209,24 +217,24 @@ class Order < ActiveRecord::Base
protected protected
def starts_before_ends def starts_before_ends
errors.add(:ends, I18n.t('articles.model.error_starts_before_ends')) if (ends && starts && ends <= starts) errors.add(:ends, I18n.t('orders.model.error_starts_before_ends')) if (ends && starts && ends <= starts)
end end
def include_articles def include_articles
errors.add(:articles, I18n.t('articles.model.error_nosel')) if article_ids.empty? errors.add(:articles, I18n.t('orders.model.error_nosel')) if article_ids.empty?
end
def keep_ordered_articles
chosen_order_articles = order_articles.find_all_by_article_id(article_ids)
to_be_removed = order_articles - chosen_order_articles
to_be_removed_but_ordered = to_be_removed.select { |a| a.quantity > 0 or a.tolerance > 0 }
unless to_be_removed_but_ordered.empty? or ignore_warnings
errors.add(:articles, I18n.t(stockit? ? 'orders.model.warning_ordered_stock' : 'orders.model.warning_ordered'))
@erroneous_article_ids = to_be_removed_but_ordered.map { |a| a.article_id }
end
end end
def save_order_articles def save_order_articles
#self.articles = Article.find(article_ids) # This doesn't deletes the group_order_articles, belonging to order_articles,
# # see http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
#
## Ensure to delete also the group_order_articles, belonging to order_articles
## This case is relevant, when removing articles from a running order
#goa_ids = GroupOrderArticle.where(group_order_id: group_order_ids).includes(:order_article).
# select { |goa| goa.order_article.nil? }.map(&:id)
#GroupOrderArticle.delete_all(id: goa_ids) unless goa_ids.empty?
# fetch selected articles # fetch selected articles
articles_list = Article.find(article_ids) articles_list = Article.find(article_ids)
# create new order_articles # create new order_articles

View file

@ -12,7 +12,8 @@ class OrderArticle < ActiveRecord::Base
validate :article_and_price_exist validate :article_and_price_exist
validates_uniqueness_of :article_id, scope: :order_id validates_uniqueness_of :article_id, scope: :order_id
scope :ordered, :conditions => "units_to_order >= 1" scope :ordered, :conditions => "units_to_order > 0"
scope :ordered_or_member, -> { includes(:group_order_articles).where("units_to_order > 0 OR group_order_articles.result > 0") }
before_create :init_from_balancing before_create :init_from_balancing
after_destroy :update_ordergroup_prices after_destroy :update_ordergroup_prices

View file

@ -1,4 +1,4 @@
- title 'Artikel mit externer Datenbank synchronisieren' - title t('.title')
= form_tag update_synchronized_supplier_articles_path(@supplier) do = form_tag update_synchronized_supplier_articles_path(@supplier) do
%h2= t '.outlist.title' %h2= t '.outlist.title'
@ -19,9 +19,8 @@
%h2= t '.update.title' %h2= t '.update.title'
%p %p
%i %i
%b= @updated_articles.size = t '.update.update_msg', count: @updated_articles.size
= t '.update.update_msg' = t '.update.body'
= t('.update.body').html_safe
%table.table %table.table
%thead %thead
%tr %tr

View file

@ -5,21 +5,19 @@
$('#stock_changes tr').removeClass('success'); $('#stock_changes tr').removeClass('success');
var quantity = w.prompt('<%= j(t('.how_many_units', :unit => @stock_change.stock_article.unit, :name => @stock_change.stock_article.name)) %>');
if(null === quantity) {
return false;
}
var stock_change = $( var stock_change = $(
'<%= j(render(:partial => 'stock_change', :locals => {:stock_change => @stock_change})) %>' '<%= j(render(:partial => 'stock_change', :locals => {:stock_change => @stock_change})) %>'
).addClass('success'); ).addClass('success');
enablePriceTooltips(stock_change); enablePriceTooltips(stock_change);
$('input.stock-change-quantity', stock_change).val(quantity);
$('#stock_changes').append(stock_change); $('#stock_changes').append(stock_change);
mark_article_for_delivery(<%= @stock_change.stock_article.id %>); mark_article_for_delivery(<%= @stock_change.stock_article.id %>);
updateSort('#stock_changes'); updateSort('#stock_changes');
var quantity = w.prompt('<%= j(t('.how_many_units', :unit => @stock_change.stock_article.unit, :name => @stock_change.stock_article.name)) %>'); <%# how to properly escape here? %>
if(null === quantity) {
stock_change.remove();
mark_article_for_delivery(<%= @stock_change.stock_article.id %>);
return false;
}
$('input.stock-change-quantity', stock_change).val(quantity);
})(window); })(window);

View file

@ -7,12 +7,17 @@
setMinimumBalance(#{FoodsoftConfig[:minimum_balance] or 0}); setMinimumBalance(#{FoodsoftConfig[:minimum_balance] or 0});
setToleranceBehaviour(#{FoodsoftConfig[:tolerance_is_costly]}); setToleranceBehaviour(#{FoodsoftConfig[:tolerance_is_costly]});
setStockit(#{@order.stockit?}); setStockit(#{@order.stockit?});
// create List for search-feature (using list.js, http://listjs.com)
var listjsResetPlugin = ['reset', {highlightClass: 'btn-primary'}];
var listjsDelayPlugin = ['delay', {delayedSearchTime: 500}];
new List(document.body, { valueNames: ['name'], engine: 'unlist', plugins: [listjsResetPlugin, listjsDelayPlugin] });
}); });
- title t('.title'), false - title t('.title'), false
.row-fluid .row-fluid
.well.pull-left .well.pull-left
%button{type: "button", class: "close", data: {dismiss: 'alert'}}= '&times;'.html_safe
%h2= @order.name %h2= @order.name
%dl.dl-horizontal %dl.dl-horizontal
- unless @order.note.blank? - unless @order.note.blank?
@ -35,8 +40,17 @@
%dd= number_to_currency(@ordering_data[:available_funds]) %dd= number_to_currency(@ordering_data[:available_funds])
.well.pull-right .well.pull-right
%button{type: "button", class: "close", data: {dismiss: 'alert'}}= '&times;'.html_safe
= render 'switch_order', current_order: @order = render 'switch_order', current_order: @order
.row-fluid
.well.clear
.form-search
.input-append
= text_field_tag :article, params[:article], placeholder: t('.search_article'), class: 'search-query delayed-search resettable'
%button.add-on.btn.reset-search{:type => :button, :title => t('.reset_article_search')}
%i.icon.icon-remove
= form_for @group_order do |f| = form_for @group_order do |f|
= f.hidden_field :lock_version = f.hidden_field :lock_version
= f.hidden_field :order_id = f.hidden_field :order_id
@ -59,9 +73,9 @@
%th(style="width:20px")= t '.available' %th(style="width:20px")= t '.available'
%th#col_required= t '.amount' %th#col_required= t '.amount'
%th{style: "width:15px;"}= t '.sum' %th{style: "width:15px;"}= t '.sum'
%tbody %tbody.list
- @order.articles_grouped_by_category.each do |category, order_articles| - @order.articles_grouped_by_category.each do |category, order_articles|
%tr.article-category %tr.list-heading.article-category
%td %td
= category = category
%i.icon-tag %i.icon-tag

View file

@ -4,4 +4,4 @@
= form.hidden_field :group_id = form.hidden_field :group_id
= form.input :email = form.input :email
= form.submit t('.action') = form.submit t('.action')
= link_to t('.back'), :back = link_to t('ui.or_cancel'), :back

View file

@ -27,10 +27,14 @@
= category_name = category_name
%i.icon-tag %i.icon-tag
- for article in articles - for article in articles
/ check if the article is selected / check if the article is selected or has an error
- included = @order.article_ids.include?(article.id) - included = @order.article_ids.include?(article.id.to_s)
- included_class = included ? ' selected' : '' - row_class = ''
%tr{:class => included_class, :id => article.id.to_s } - if included
- row_class = 'selected'
- elsif @order.erroneous_article_ids.include?(article.id)
- row_class = 'error'
%tr{class: row_class, id: article.id}
%td= check_box_tag "order[article_ids][]", article.id, included, :id => "checkbox_#{article.id}" %td= check_box_tag "order[article_ids][]", article.id, included, :id => "checkbox_#{article.id}"
%td.click-me{'data-check-this' => "#checkbox_#{article.id}"}= article.name %td.click-me{'data-check-this' => "#checkbox_#{article.id}"}= article.name
%td=h truncate article.note, :length => 25 %td=h truncate article.note, :length => 25
@ -52,3 +56,7 @@
.form-actions .form-actions
= f.submit class: 'btn' = f.submit class: 'btn'
= link_to t('ui.or_cancel'), orders_path = link_to t('ui.or_cancel'), orders_path
- unless @order.erroneous_article_ids.empty?
&nbsp;
= check_box_tag 'order[ignore_warnings]'
= t '.ignore_warnings'

View file

@ -2,8 +2,10 @@
%thead %thead
%tr %tr
%th{:style => 'width:70%'}= t '.ordergroup' %th{:style => 'width:70%'}= t '.ordergroup'
%th= t '.ordered' %th
%th= t '.received' %acronym{:title => t('shared.articles.ordered_desc')}= t 'shared.articles.ordered'
%th
%acronym{:title => t('shared.articles.received_desc')}= t 'shared.articles.received'
%th= t '.price' %th= t '.price'
- for order_article in order.order_articles.ordered.all(:include => [:article, :article_price]) - for order_article in order.order_articles.ordered.all(:include => [:article, :article_price])
@ -13,8 +15,8 @@
= order_article.article.name = order_article.article.name
= "(#{order_article.article.unit} | #{order_article.price.unit_quantity} | #{number_to_currency(order_article.price.gross_price)})" = "(#{order_article.article.unit} | #{order_article.price.unit_quantity} | #{number_to_currency(order_article.price.gross_price)})"
%tbody %tbody
- for goa in order_article.group_order_articles - for goa in order_article.group_order_articles.ordered
%tr{:class => cycle('even', 'odd', :name => 'groups')} %tr{:class => [cycle('even', 'odd', :name => 'groups'), if goa.result == 0 then 'unavailable' end]}
%td{:style => "width:70%"}=h goa.group_order.ordergroup.name %td{:style => "width:70%"}=h goa.group_order.ordergroup.name
%td= "#{goa.quantity} + #{goa.tolerance}" %td= "#{goa.quantity} + #{goa.tolerance}"
%td %td

View file

@ -3,7 +3,9 @@
%tr %tr
%th{:style => "width:40%"}= t '.name' %th{:style => "width:40%"}= t '.name'
%th %th
%acronym{:title => t('.units_desc')}= t '.units' %acronym{:title => t('shared.articles.ordered_desc')}= t 'shared.articles.ordered'
%th
%acronym{:title => t('shared.articles.received_desc')}= t 'shared.articles.received'
%th %th
%acronym{:title => t('.fc_price_desc')}= t '.fc_price' %acronym{:title => t('.fc_price_desc')}= t '.fc_price'
%th %th
@ -11,10 +13,10 @@
%th= t '.unit' %th= t '.unit'
%th= t '.price' %th= t '.price'
- for group_order in order.group_orders.all - for group_order in order.group_orders.ordered
%thead %thead
%tr %tr
%th{:colspan => "6"} %th{:colspan => "7"}
%h4= group_order.ordergroup.name %h4= group_order.ordergroup.name
%tbody %tbody
- total = 0 - total = 0
@ -22,17 +24,19 @@
- fc_price = goa.order_article.price.fc_price - fc_price = goa.order_article.price.fc_price
- subTotal = fc_price * goa.result - subTotal = fc_price * goa.result
- total += subTotal - total += subTotal
%tr{:class => cycle('even', 'odd', :name => 'articles')} %tr{:class => [cycle('even', 'odd', :name => 'articles'), if goa.result == 0 then 'unavailable' end]}
%td{:style => "width:40%"}=h goa.order_article.article.name %td{:style => "width:40%"}=h goa.order_article.article.name
%td= goa.result %td= "#{goa.quantity} + #{goa.tolerance}"
%td
%b= goa.result
%td= number_to_currency(fc_price) %td= number_to_currency(fc_price)
%td= goa.order_article.price.unit_quantity %td= goa.order_article.price.unit_quantity
%td= goa.order_article.article.unit %td= goa.order_article.article.unit
%td= number_to_currency(subTotal) %td= number_to_currency(subTotal)
%tr{:class => cycle('even', 'odd', :name => 'articles')} %tr{:class => cycle('even', 'odd', :name => 'articles')}
%th{:colspan => "5"} Summe %th{:colspan => "6"} Summe
%th= number_to_currency(total) %th= number_to_currency(total)
%tr %tr
%th(colspan="6") %th(colspan="7")
- reset_cycle("articles") - reset_cycle("articles")

View file

@ -3,5 +3,6 @@
= form.hidden_field :stock_article_id = form.hidden_field :stock_article_id
= "Menge (#{stock_change.stock_article.quantity_available})" = "Menge (#{stock_change.stock_article.quantity_available})"
= form.text_field :quantity, :size => 5, :autocomplete => 'off' = form.text_field :quantity, :size => 5, :autocomplete => 'off'
%span{:data => {:toggle => :tooltip, :title => render(:partial => 'shared/article_price_info', :locals => {:article => stock_change.stock_article})}}
%b= stock_change.stock_article.name %b= stock_change.stock_article.name
= "(#{number_to_currency(stock_change.stock_article.price)} / #{stock_change.stock_article.unit})" = "(#{number_to_currency(stock_change.stock_article.price)} / #{stock_change.stock_article.unit})"

View file

@ -1,5 +1,20 @@
- title t('.title') - title t('.title')
- content_for :javascript do
:javascript
$(function() {
enablePriceTooltips();
});
function enablePriceTooltips(context) {
$('[data-toggle~="tooltip"]', context).tooltip({
animation: false,
html: true,
placement: 'left',
container: 'body'
});
}
- content_for :sidebar do - content_for :sidebar do
%p %p
%i= t('.text_deviations', inv_link: link_to(t('.temp_inventory'), stock_articles_path)).html_safe %i= t('.text_deviations', inv_link: link_to(t('.temp_inventory'), stock_articles_path)).html_safe

View file

@ -1,17 +0,0 @@
- title t('.stock_changes', :article_name => @stock_article.name)
%table.table.table-hover#stock_changes
%thead
%tr
%th= t '.datetime'
%th= t '.reason'
%th= t '.change_quantity'
%th= t '.new_quantity'
%tbody
- reversed_history = @stock_article.quantity_history.reverse
- @stock_changes.each_with_index do |stock_change, index|
%tr
%td= l stock_change.created_at
%td= link_to_stock_change_reason(stock_change)
%td= stock_change.quantity
%td= reversed_history[index]

View file

@ -45,7 +45,7 @@
%tbody %tbody
- for article in @stock_articles - for article in @stock_articles
%tr{:class => stock_article_classes(article), :id => "stockArticle-#{article.id}"} %tr{:class => stock_article_classes(article), :id => "stockArticle-#{article.id}"}
%td=h article.name %td= link_to article.name, article
%td= article.quantity %td= article.quantity
%td= article.quantity - article.quantity_available %td= article.quantity - article.quantity_available
%th= article.quantity_available %th= article.quantity_available
@ -56,7 +56,6 @@
%td= article.article_category.name %td= article.article_category.name
%td %td
= link_to t('ui.edit'), edit_stock_article_path(article), class: 'btn btn-mini' = link_to t('ui.edit'), edit_stock_article_path(article), class: 'btn btn-mini'
= link_to t('ui.history'), stock_article_history_path(article), class: 'btn btn-mini'
= link_to t('ui.delete'), article, :method => :delete, :confirm => t('.confirm_delete'), = link_to t('ui.delete'), article, :method => :delete, :confirm => t('.confirm_delete'),
class: 'btn btn-mini btn-danger', :remote => true class: 'btn btn-mini btn-danger', :remote => true
%p %p

View file

@ -0,0 +1,47 @@
- title @stock_article.name
.row-fluid
.span6
%dl.dl-horizontal
%dt= StockArticle.human_attribute_name 'supplier'
%dd= link_to @stock_article.supplier.name, @stock_article.supplier
%dt= StockArticle.human_attribute_name 'name'
%dd= @stock_article.name
%dt= StockArticle.human_attribute_name 'unit'
%dd= @stock_article.unit
%dt= StockArticle.human_attribute_name 'price'
%dd= number_to_currency @stock_article.price
%dt= StockArticle.human_attribute_name 'tax'
%dd= number_to_percentage @stock_article.tax
%dt= StockArticle.human_attribute_name 'deposit'
%dd= number_to_currency @stock_article.deposit
%dt= StockArticle.human_attribute_name 'fc_price'
%dd= number_to_currency @stock_article.fc_price
%dt= StockArticle.human_attribute_name 'article_category'
%dd= @stock_article.article_category.name
%dt= StockArticle.human_attribute_name 'note'
%dd= @stock_article.note
%dt= StockArticle.human_attribute_name 'quantity'
%dd= @stock_article.quantity
%dt= StockArticle.human_attribute_name 'quantity_available'
%dd= @stock_article.quantity_available
.form-actions
= link_to t('ui.edit'), edit_stock_article_path(@stock_article), class: 'btn'
.span6
%h2= t('.stock_changes')
%table.table.table-hover#stock_changes
%thead
%tr
%th= t '.datetime'
%th= t '.reason'
%th= t '.change_quantity'
%th= t '.new_quantity'
%tbody
- reversed_history = @stock_article.quantity_history.reverse
- @stock_changes.each_with_index do |stock_change, index|
%tr
%td= l stock_change.created_at
%td= link_to_stock_change_reason(stock_change)
%td= stock_change.quantity
%td= reversed_history[index]

View file

@ -0,0 +1 @@
Resque.inline = Rails.env.test?

View file

@ -42,7 +42,10 @@ de:
fc_price: Endpreis fc_price: Endpreis
fc_share: FC-Aufschlag fc_share: FC-Aufschlag
gross_price: Bruttopreis gross_price: Bruttopreis
name: Name
note: Notiz
price: Nettopreis price: Nettopreis
supplier: Lieferantin
tax: MwSt tax: MwSt
unit: Einheit unit: Einheit
unit_quantity: Gebindegröße unit_quantity: Gebindegröße
@ -51,6 +54,8 @@ de:
note: Notiz note: Notiz
stock_article: stock_article:
price: Nettopreis price: Nettopreis
quantity: Lagerbestand
quantity_available: Verfügbarer Bestand
user: user:
first_name: Vorname first_name: Vorname
password: Passwort password: Passwort
@ -212,6 +217,11 @@ de:
workgroups: workgroups:
members: Mitglieder members: Mitglieder
name: Name name: Name
application:
controller:
error_authn:
error_denied:
error_members_only: Diese Aktion ist nur für Mitglieder der Gruppe erlaubt!
article_categories: article_categories:
create: create:
notice: Die Kategorie wurde gespeichert notice: Die Kategorie wurde gespeichert
@ -305,18 +315,20 @@ de:
error_nosel: Du hast keine Artikel ausgewählt error_nosel: Du hast keine Artikel ausgewählt
parse_upload: parse_upload:
body: <p><i>Bitte überprufe die engelesenen Artikel.</i></p> <p><i>Achtung, momentan gibt es keine Überprüfung auf doppelte Artikel.</i></p> body: <p><i>Bitte überprufe die engelesenen Artikel.</i></p> <p><i>Achtung, momentan gibt es keine Überprüfung auf doppelte Artikel.</i></p>
title:
sync:
outlist: outlist:
body: ! 'Folgende Artikel wurden ausgelistet und werden <b>gelöscht</b>:' body: ! 'Folgende Artikel wurden ausgelistet und werden <b>gelöscht</b>:'
body_skip: Es müssen keine Artikel gelöscht werden. body_skip: Es müssen keine Artikel gelöscht werden.
title: Auslisten ... title: Auslisten ...
price_short: Preis price_short: Preis
submit: Alle löschen/aktualisieren submit: Alle synchronisieren
title: Artikel mit externer Datenbank synchronisieren title: Artikel mit externer Datenbank synchronisieren
unit_quantity_short: GebGr unit_quantity_short: GebGr
update: update:
body: <p><i>Jeder Artikel wird doppelt angezeigt. Die alten Werte sind grau und die Textfelder sind mit den aktuellen Werten vorausgefüllt.</i></p> <p><i>Abweichungen zu den alten Artikeln sind gelb markiert.</i></p> body: ! 'Jeder Artikel wird doppelt angezeigt: die alten Werte sind grau und die Textfelder sind mit den aktuellen Werten vorausgefüllt. Abweichungen zu den alten Artikeln sind gelb markiert.'
title: Aktualisieren ... title: Aktualisieren ...
update_msg: ! 'Artikel müssen aktualisiert werden:' update_msg: ! '%{count} Artikel müssen aktualisiert werden.'
upload: upload:
body: <p>Die Datei muss eine Textdatei mit der Endung '.csv' sein. Die erste Zeile wird beim Einlesen ignoriert.</p> <p>Die Felder müssen mit einem Semikolon (';') getrennt und der Text mit doppelten Anführungszeichen ("Text...") umklammert werden.</p> <p>Als Zeichensatz wird UTF-8 erwartet. Korrekte Reihenfolge der Spalten:</p> body: <p>Die Datei muss eine Textdatei mit der Endung '.csv' sein. Die erste Zeile wird beim Einlesen ignoriert.</p> <p>Die Felder müssen mit einem Semikolon (';') getrennt und der Text mit doppelten Anführungszeichen ("Text...") umklammert werden.</p> <p>Als Zeichensatz wird UTF-8 erwartet. Korrekte Reihenfolge der Spalten:</p>
fields: fields:
@ -482,14 +494,16 @@ de:
filename: Bestellung %{name}-%{date} - Artikelsortierung filename: Bestellung %{name}-%{date} - Artikelsortierung
rows: rows:
- Bestellgruppe - Bestellgruppe
- Menge - Bestellt
- Bekommen
- Preis - Preis
title: ! 'Artikelsortierung der Bestellung: %{name}, beendet am %{date}' title: ! 'Artikelsortierung der Bestellung: %{name}, beendet am %{date}'
order_by_groups: order_by_groups:
filename: Bestellung %{name}-%{date} - Gruppensortierung filename: Bestellung %{name}-%{date} - Gruppensortierung
rows: rows:
- Artikel - Artikel
- Menge - Bestellt
- Bekommen
- Preis - Preis
- GebGr - GebGr
- Einheit - Einheit
@ -505,6 +519,7 @@ de:
- Gebinde - Gebinde
- Einheit - Einheit
- Preis/Einheit - Preis/Einheit
- Summe
total: Gesamtpreis total: Gesamtpreis
order_matrix: order_matrix:
filename: Bestellung %{name}-%{date} - Sortiermatrix filename: Bestellung %{name}-%{date} - Sortiermatrix
@ -641,10 +656,12 @@ de:
create: create:
notice: Rechnung wurde erstellt. notice: Rechnung wurde erstellt.
financial_transactions: financial_transactions:
controller:
create: create:
notice: Die Transaktion wurde gespeichert. notice: Die Transaktion wurde gespeichert.
create_collection: create_collection:
alert: ! 'Ein Fehler ist aufgetreten: %{error}' alert: ! 'Ein Fehler ist aufgetreten: %{error}'
error_note_required: Notiz wird benötigt!
notice: Alle Transaktionen wurden gespeichert. notice: Alle Transaktionen wurden gespeichert.
index: index:
balance: ! 'Kontostand: %{balance}' balance: ! 'Kontostand: %{balance}'
@ -784,6 +801,8 @@ de:
new_funds: Neuer Kontostand new_funds: Neuer Kontostand
note: Notiz note: Notiz
price: Preis price: Preis
reset_article_search: Suche zurücksetzen
search_article: Artikel suchen...
sum: Summe sum: Summe
sum_amount: ! 'Gesamtbestellmenge bisher:' sum_amount: ! 'Gesamtbestellmenge bisher:'
supplier: Lieferant supplier: Lieferant
@ -968,7 +987,6 @@ de:
title: Person einladen title: Person einladen
new: new:
action: Einlading abschicken action: Einlading abschicken
back: oder zurück
body: <p>Hier kannst du eine Person in die Gruppe <b>%{group}</b> einladen, die noch nicht Mitglied der Foodcoop ist.</p> body: <p>Hier kannst du eine Person in die Gruppe <b>%{group}</b> einladen, die noch nicht Mitglied der Foodcoop ist.</p>
success: Benutzerin wurde erfolgreich eingeladen. success: Benutzerin wurde erfolgreich eingeladen.
layouts: layouts:
@ -1299,6 +1317,7 @@ de:
finish: finish:
notice: Die Bestellung wurde beendet. notice: Die Bestellung wurde beendet.
form: form:
ignore_warnings: Warnungen ignorieren
name: Name name: Name
note: Notiz note: Notiz
origin: Herkunft origin: Herkunft
@ -1326,6 +1345,8 @@ de:
error_starts_before_ends: muss nach dem Bestellstart liegen (oder leer bleiben) error_starts_before_ends: muss nach dem Bestellstart liegen (oder leer bleiben)
notice_close: ! 'Bestellung: %{name}, bis %{ends}' notice_close: ! 'Bestellung: %{name}, bis %{ends}'
stock: Lager stock: Lager
warning_ordered: ! 'Warnung: Die rot markierten Artikel wurden in der laufenden Bestellung bereits bestellt. Wenn Du sie hier abwählst, werden alle bestehenden Bestellungen dieses Artikels gelöscht.'
warning_ordered_stock: ! 'Warnung: Die rot markierten Artikel wurden in der laufenden Lagerbestellung bereits bestellt bzw. gekauft. Wenn Du sie hier abwählst, werden alle bestehenden Bestellungen bzw. Käufe dieses Artikels gelöscht und nicht abgerechnet!'
new: new:
title: Neue Bestellung anlegen title: Neue Bestellung anlegen
orders: orders:
@ -1452,11 +1473,14 @@ de:
title: Foodsoft anmelden title: Foodsoft anmelden
user: Benutzerin user: Benutzerin
shared: shared:
articles:
ordered: Bestellt
ordered_desc:
received: Bekommen
received_desc:
articles_by_articles: articles_by_articles:
ordered: Bestellt (Menge + Toleranz)
ordergroup: Bestellgruppe ordergroup: Bestellgruppe
price: Gesamtpreis price: Gesamtpreis
received: Bekommen
articles_by_groups: articles_by_groups:
fc_price: FC-Preis fc_price: FC-Preis
fc_price_desc: Preis incl. MwSt, Pfand und Foodcoop-Aufschlag fc_price_desc: Preis incl. MwSt, Pfand und Foodcoop-Aufschlag
@ -1465,8 +1489,6 @@ de:
unit: Einheit unit: Einheit
unit_quantity: GebGr unit_quantity: GebGr
unit_quantity_desc: Gebindegröße unit_quantity_desc: Gebindegröße
units: Menge
units_desc: Zugeteilte Einheiten
group: group:
access: Zugriff auf access: Zugriff auf
activated: aktiviert activated: aktiviert
@ -1525,7 +1547,7 @@ de:
message: message:
private: Nachricht erscheint nicht im Foodsoft Posteingang private: Nachricht erscheint nicht im Foodsoft Posteingang
order_article: order_article:
units_to_order: Anzahl gelieferter Gebinde units_to_order: Wenn Du die Gesamtanzahl gelieferter Gebinde änderst, musst Du auch die individuelle Anzahl der einzelnen Bestellgruppen anpassen, indem Du auf den Artikelnamen klickst. Sie werden nicht automatisch neuberechnet und andernfalls werden den Bestellgruppen Artikel in Rechnung gestellt, die nicht geliefert wurden!
update_current_price: Ändert auch den Preis für aktuelle Bestellungen update_current_price: Ändert auch den Preis für aktuelle Bestellungen
stock_article: stock_article:
copy_stock_article: copy_stock_article:
@ -1718,15 +1740,6 @@ de:
title: Lagerartikel bearbeiten title: Lagerartikel bearbeiten
form: form:
price_hint: Um Chaos zu vermeiden können bis auf weiteres die Preise von angelegten Lagerartikeln nicht mehr verändert werden. price_hint: Um Chaos zu vermeiden können bis auf weiteres die Preise von angelegten Lagerartikeln nicht mehr verändert werden.
history:
change_quantity: Veränderung
datetime: Zeitpunkt
delivery: Lieferung
new_quantity: Neuer Bestand
order: Bestellung
reason: Ereignis
stock_changes: Verlauf anzeigen für »%{article_name}«
stock_taking: Inventur
index: index:
article: article:
article: Artikel article: Artikel
@ -1752,6 +1765,15 @@ de:
new: new:
search_text: ! 'Suche nache Artikeln aus allen Katalogen:' search_text: ! 'Suche nache Artikeln aus allen Katalogen:'
title: Neuen Lagerartikel anlegen title: Neuen Lagerartikel anlegen
show:
change_quantity: Veränderung
datetime: Zeitpunkt
delivery: Lieferung
new_quantity: Neuer Bestand
order: Bestellung
reason: Ereignis
stock_changes: Verlauf des Lagerbestands
stock_taking: Inventur
stock_create: stock_create:
notice: Lagerartikel wurde gespeichert. notice: Lagerartikel wurde gespeichert.
stock_update: stock_update:
@ -1876,7 +1898,6 @@ de:
close: Schließen close: Schließen
delete: Löschen delete: Löschen
edit: Bearbeiten edit: Bearbeiten
history: Verlauf anzeigen
marks: marks:
close: ! '&times;' close: ! '&times;'
success: <i class="icon icon-ok"></i> success: <i class="icon icon-ok"></i>

View file

@ -42,7 +42,10 @@ en:
fc_price: FC price fc_price: FC price
fc_share: FC share fc_share: FC share
gross_price: gross price gross_price: gross price
name: name
note: note
price: price price: price
supplier: supplier
tax: VAT tax: VAT
unit: unit unit: unit
unit_quantity: unit quantity unit_quantity: unit quantity
@ -50,7 +53,9 @@ en:
amount: amount amount: amount
note: note note: note
stock_article: stock_article:
price: Price price: price
quantity: quantity
quantity_available: available quantity
user: user:
first_name: First name first_name: First name
password: Password password: Password
@ -212,6 +217,11 @@ en:
workgroups: workgroups:
members: members members: members
name: name name: name
application:
controller:
error_authn: Authentication required!
error_denied: Access denied!
error_members_only: This action is only available to members of the group!
article_categories: article_categories:
create: create:
notice: Category was stored notice: Category was stored
@ -305,20 +315,22 @@ en:
error_nosel: You have selected no articles error_nosel: You have selected no articles
parse_upload: parse_upload:
body: <p><i>Please verify the articles.</i></p> <p><i>Warning, at the moment there is no check for duplicate articles.</i></p> body: <p><i>Please verify the articles.</i></p> <p><i>Warning, at the moment there is no check for duplicate articles.</i></p>
title: Upload articles
sync:
outlist: outlist:
body: ! 'The following articles were outlisted and <b>deleted</b>:' body: ! 'The following articles were removed from the list and will be <b>deleted</b>:'
body_skip: No articles to delete. body_skip: No articles to delete.
title: Outlist ... title: Remove from list ...
price_short: Price price_short: Price
submit: Delete/update all submit: Synchronize all
title: Synchronize articles with external database title: Synchronize articles with external database
unit_quantity_short: unit quantity unit_quantity_short: Unit quantity
update: update:
body: ! '<p><i>Every article is shown twice. The old values are gray and contain the current values.</i></p> body: ! 'Every article is shown twice: old values are gray, while the text fields contain updated values. Differences with the old articles are marked yellow.'
<p><i>Differences with the old articles are marked yellow.</i></p>'
title: Update ... title: Update ...
update_msg: ! 'Articles must be updated:' update_msg:
one: One article needs to be updated.
other: ! '%{count} articles need to be updated.'
upload: upload:
body: <p>The file has to be a text file with the ending '.csv' The first line will be ignored when imported</p> <p>The fields have to be separated with semicolons (';') and the text enclosed by double quotation marks ("text...").</p> <p>As character set UTF-8 is demanded. Correct order of the column:</p> body: <p>The file has to be a text file with the ending '.csv' The first line will be ignored when imported</p> <p>The fields have to be separated with semicolons (';') and the text enclosed by double quotation marks ("text...").</p> <p>As character set UTF-8 is demanded. Correct order of the column:</p>
fields: fields:
@ -430,7 +442,7 @@ en:
create: create:
notice: Delivery was created. Please dont forget to create invoice! notice: Delivery was created. Please dont forget to create invoice!
create_stock_article: create_stock_article:
notice: The new stock article »%{name}« was saved. notice: The new stock article "%{name}" was saved.
destroy: destroy:
notice: Delivery was deleted. notice: Delivery was deleted.
edit: edit:
@ -478,20 +490,22 @@ en:
update: update:
notice: Delivery was updated. notice: Delivery was updated.
update_stock_article: update_stock_article:
notice: The stock article »%{name}« was updated. notice: The stock article "%{name}" was updated.
documents: documents:
order_by_articles: order_by_articles:
filename: Order %{name}-%{date} - by articles filename: Order %{name}-%{date} - by articles
rows: rows:
- Order group - Order group
- Amount - Ordered
- Received
- Price - Price
title: ! 'Order sorted by articles: %{name}, closed at %{date}' title: ! 'Order sorted by articles: %{name}, closed at %{date}'
order_by_groups: order_by_groups:
filename: Order %{name}-%{date} - by group filename: Order %{name}-%{date} - by group
rows: rows:
- Article - Article
- Amount - Ordered
- Received
- Price - Price
- Unit quantity - Unit quantity
- Unit - Unit
@ -646,10 +660,12 @@ en:
create: create:
notice: Invoice was created notice: Invoice was created
financial_transactions: financial_transactions:
controller:
create: create:
notice: The transaction was saved. notice: The transaction was saved.
create_collection: create_collection:
alert: ! 'An error occured: %{error}' alert: ! 'An error occured: %{error}'
error_note_required: Note is required!
notice: All transactions were saved. notice: All transactions were saved.
index: index:
balance: ! 'Balance of account: %{balance}' balance: ! 'Balance of account: %{balance}'
@ -789,6 +805,8 @@ en:
new_funds: New account balance new_funds: New account balance
note: Note note: Note
price: Price price: Price
reset_article_search: Reset search
search_article: Search for article...
sum: Sum sum: Sum
sum_amount: Current amount sum_amount: Current amount
supplier: Supplier supplier: Supplier
@ -896,7 +914,7 @@ en:
warning: Warning, if you have less then %{threshold} of apple points, you are not allowed to place an order! warning: Warning, if you have less then %{threshold} of apple points, you are not allowed to place an order!
changes_saved: Changes saved. changes_saved: Changes saved.
index: index:
due_date_format: ! '%A %d %b' due_date_format: ! '%A %d %B'
messages: messages:
title: Newest Messages title: Newest Messages
view_all: See all messages view_all: See all messages
@ -973,7 +991,6 @@ en:
title: Invite person title: Invite person
new: new:
action: Send invite action: Send invite
back: or go back
body: <p>Here you can add a person to the group <b>%{group}</b>, who is not yet a member of the foodcoop.</p> body: <p>Here you can add a person to the group <b>%{group}</b>, who is not yet a member of the foodcoop.</p>
success: User was invited successfully. success: User was invited successfully.
layouts: layouts:
@ -1304,13 +1321,14 @@ en:
finish: finish:
notice: The order has been closed. notice: The order has been closed.
form: form:
ignore_warnings: Ignore warnings
name: Name name: Name
note: Note note: Note
origin: Origin origin: Origin
prices: Prices (net/FC) prices: Prices (net/FC)
select_all: Select all select_all: Select all
stockit: In stock stockit: In stock
supplier: Supplier supplier: Producer
title: Article title: Article
unit_quantity: Unit quantity unit_quantity: Unit quantity
index: index:
@ -1331,6 +1349,8 @@ en:
error_starts_before_ends: must be after the start date (or remain empty) error_starts_before_ends: must be after the start date (or remain empty)
notice_close: ! 'Order: %{name}, until %{ends}' notice_close: ! 'Order: %{name}, until %{ends}'
stock: Stock stock: Stock
warning_ordered: ! 'Warning: Articles marked red have already been ordered within this open order. If you uncheck them here, all existing orders of these articles will be deleted.'
warning_ordered_stock: ! 'Warning: Articles marked red have already been ordered/ purchased within this open stock order. If you uncheck them here, all existing orders/ purchases of these articles will be deleted and it will not be accounted for them.'
new: new:
title: Create new order title: Create new order
orders: orders:
@ -1390,7 +1410,7 @@ en:
notice: Page was created notice: Page was created
cshow: cshow:
error_noexist: Page doesnt exist! error_noexist: Page doesnt exist!
redirect_notice: Redirected from %{page} .. redirect_notice: Redirected from %{page} ...
destroy: destroy:
notice: The page '%{page}' and all subpages have been deleted successfully. notice: The page '%{page}' and all subpages have been deleted successfully.
edit: edit:
@ -1457,11 +1477,14 @@ en:
title: Foodsoft login title: Foodsoft login
user: User user: User
shared: shared:
articles:
ordered: Ordered
ordered_desc: Number of articles as ordered by member (amount + tolerance)
received: Received
received_desc: Number of articles that (will be) received by member
articles_by_articles: articles_by_articles:
ordered: Ordered (Amount + Tolerance)
ordergroup: Ordergroup ordergroup: Ordergroup
price: Total price price: Total price
received: Received
articles_by_groups: articles_by_groups:
fc_price: FC-Price fc_price: FC-Price
fc_price_desc: Price including taxes, deposit and Foodcoop-charge fc_price_desc: Price including taxes, deposit and Foodcoop-charge
@ -1470,8 +1493,6 @@ en:
unit: Unit unit: Unit
unit_quantity: Lot quantity unit_quantity: Lot quantity
unit_quantity_desc: How many units per lot. unit_quantity_desc: How many units per lot.
units: Amount
units_desc: Assigned units
group: group:
access: Access to access: Access to
activated: activated activated: activated
@ -1530,7 +1551,7 @@ en:
message: message:
private: Message doesnt show in Foodsoft mail inbox private: Message doesnt show in Foodsoft mail inbox
order_article: order_article:
units_to_order: Amount of delivered units units_to_order: If you change the total amount of delivered units, you also have to change individual group amounts by clicking on the article name. They will not be automatically recalculated and so ordergroups may be accounted for articles that were not delivered!
update_current_price: Also update the price of the current order update_current_price: Also update the price of the current order
stock_article: stock_article:
copy_stock_article: copy_stock_article:
@ -1723,15 +1744,6 @@ en:
title: Edit stock articles title: Edit stock articles
form: form:
price_hint: To avoid choas, it is not possible to edit the prices of already added stock articles until further notice. price_hint: To avoid choas, it is not possible to edit the prices of already added stock articles until further notice.
history:
change_quantity: Change
datetime: Time
delivery: Delivery
new_quantity: New quantity
order: Order
reason: Reason
stock_changes: Stock quantity changes of %{article_name}
stock_taking: Inventory
index: index:
article: article:
article: Article article: Article
@ -1757,6 +1769,15 @@ en:
new: new:
search_text: ! 'Search for articles in all catalogues:' search_text: ! 'Search for articles in all catalogues:'
title: Add new stock article title: Add new stock article
show:
change_quantity: Change
datetime: Time
delivery: Delivery
new_quantity: New quantity
order: Order
reason: Reason
stock_changes: Stock quantity changes
stock_taking: Inventory
stock_create: stock_create:
notice: Stock article was created. notice: Stock article was created.
stock_update: stock_update:
@ -1860,7 +1881,7 @@ en:
title: Show task title: Show task
update: update:
notice: Task has been updated notice: Task has been updated
notice_converted: Task has been updated and was converted to a regular task notice_converted: Task has been updated and was converted to a non-repeating task.
user: user:
more: Nothing to do? %{tasks_link} are tasks for sure. more: Nothing to do? %{tasks_link} are tasks for sure.
tasks_link: Here tasks_link: Here
@ -1881,7 +1902,6 @@ en:
close: Close close: Close
delete: Delete delete: Delete
edit: Edit edit: Edit
history: Show history
marks: marks:
close: ! '&times;' close: ! '&times;'
success: <i class="icon icon-ok"></i> success: <i class="icon icon-ok"></i>

View file

@ -42,7 +42,10 @@ fr:
fc_price: prix final fc_price: prix final
fc_share: supplément boufcoop fc_share: supplément boufcoop
gross_price: prix brut gross_price: prix brut
name:
note:
price: prix net price: prix net
supplier:
tax: TVA tax: TVA
unit: unité unit: unité
unit_quantity: unités par lot unit_quantity: unités par lot
@ -51,6 +54,8 @@ fr:
note: note note: note
stock_article: stock_article:
price: Prix net price: Prix net
quantity:
quantity_available:
user: user:
first_name: Prénom first_name: Prénom
password: Mot de passe password: Mot de passe
@ -212,6 +217,11 @@ fr:
workgroups: workgroups:
members: membres members: membres
name: nom name: nom
application:
controller:
error_authn:
error_denied:
error_members_only:
article_categories: article_categories:
create: create:
notice: La catégorie a bien été définie. notice: La catégorie a bien été définie.
@ -307,6 +317,8 @@ fr:
body: ! '<p><i>Merci de vérifier les articles importés.</i></p> body: ! '<p><i>Merci de vérifier les articles importés.</i></p>
<p><i>Attention, les doublons ne sont pas automatiquement détectés.</p></i>.' <p><i>Attention, les doublons ne sont pas automatiquement détectés.</p></i>.'
title:
sync:
outlist: outlist:
body: ! 'Les articles suivants ne sont plus dans la liste et seront donc <b>supprimés</b>:' body: ! 'Les articles suivants ne sont plus dans la liste et seront donc <b>supprimés</b>:'
body_skip: Aucun article à supprimer. body_skip: Aucun article à supprimer.
@ -316,9 +328,9 @@ fr:
title: Synchroniser les articles avec la base de données extérieure title: Synchroniser les articles avec la base de données extérieure
unit_quantity_short: U/L unit_quantity_short: U/L
update: update:
body: <p><i>Chaque article apparaît deux fois. Les anciennes données sont rappelées en gris, et les champs du formulaire ont été préremplis avec les nouvelles valeurs. </i></p><p><i>Les changements sont marqués en jaune.</p></i> body: ! 'Chaque article apparaît deux fois: les anciennes données sont rappelées en gris, et les champs du formulaire ont été préremplis avec les nouvelles valeurs. Les changements sont marqués en jaune.'
title: Mettre à jour... title: Mettre à jour...
update_msg: ! 'Ces articles doivent être mis à jour:' update_msg:
upload: upload:
body: ! '<p>Le fichier doit être au format texte et son nom doit se terminer par l''extension ".csv". La première ligne sera ignorée lors de l''importation.</p> body: ! '<p>Le fichier doit être au format texte et son nom doit se terminer par l''extension ".csv". La première ligne sera ignorée lors de l''importation.</p>
@ -655,10 +667,12 @@ fr:
create: create:
notice: La facture a bien été définie. notice: La facture a bien été définie.
financial_transactions: financial_transactions:
controller:
create: create:
notice: La transaction a été sauvegardée. notice: La transaction a été sauvegardée.
create_collection: create_collection:
alert: ! 'Une erreur s''est produite: %{error}' alert: ! 'Une erreur s''est produite: %{error}'
error_note_required:
notice: Les transactions ont été sauvegardées. notice: Les transactions ont été sauvegardées.
index: index:
balance: ! 'Solde: %{balance}' balance: ! 'Solde: %{balance}'
@ -802,6 +816,8 @@ fr:
new_funds: Nouveau solde new_funds: Nouveau solde
note: Note note: Note
price: Prix price: Prix
reset_article_search:
search_article:
sum: Prix total sum: Prix total
sum_amount: ! 'Quantité déjà commandée:' sum_amount: ! 'Quantité déjà commandée:'
supplier: Fourni par supplier: Fourni par
@ -994,7 +1010,6 @@ fr:
title: Engrainer une personne title: Engrainer une personne
new: new:
action: Engrainer! action: Engrainer!
back: ou revenir en arrière
body: <p>Sur cette page, tu peux engrainer une personne qui ne fait pas encore partie de la Boufcoop à rejoindre la cellule <b>%{group}</b> body: <p>Sur cette page, tu peux engrainer une personne qui ne fait pas encore partie de la Boufcoop à rejoindre la cellule <b>%{group}</b>
success: La_le membre a été engrainéE avec succès! success: La_le membre a été engrainéE avec succès!
layouts: layouts:
@ -1288,7 +1303,7 @@ fr:
article_count: ! 'Articles commandés:' article_count: ! 'Articles commandés:'
name: Nom name: Nom
prices: Prix brut/net prices: Prix brut/net
prices_sum: Totaux (des prix bruts/nets) prices_sum: ! 'Totaux (des prix bruts/nets):'
unit_quantity: Unités par lots x Lots unit_quantity: Unités par lots x Lots
units_full: Lots complet units_full: Lots complet
units_ordered: Unités commandées units_ordered: Unités commandées
@ -1308,6 +1323,7 @@ fr:
finish: finish:
notice: La commande a été close. notice: La commande a été close.
form: form:
ignore_warnings:
name: Nom name: Nom
note: Note note: Note
origin: Origine origin: Origine
@ -1335,6 +1351,8 @@ fr:
error_starts_before_ends: doit être postérieur à la date de début de la commande (ou bien être laissé vierge) error_starts_before_ends: doit être postérieur à la date de début de la commande (ou bien être laissé vierge)
notice_close: ! 'Commande: %{name}, jusqu''au %{ends}' notice_close: ! 'Commande: %{name}, jusqu''au %{ends}'
stock: Stock stock: Stock
warning_ordered:
warning_ordered_stock:
new: new:
title: Définir une nouvelle commande title: Définir une nouvelle commande
orders: orders:
@ -1459,11 +1477,14 @@ fr:
title: Te connecter à Foodsoft title: Te connecter à Foodsoft
user: Identifiant user: Identifiant
shared: shared:
articles:
ordered: Commandé
ordered_desc:
received: Reçu
received_desc:
articles_by_articles: articles_by_articles:
ordered: Commandé (Quantité + Tolérance)
ordergroup: Cellul ordergroup: Cellul
price: Prix total price: Prix total
received: Reçu
articles_by_groups: articles_by_groups:
fc_price: Prix coop fc_price: Prix coop
fc_price_desc: Prix avec TVA, consigne et part de la coop inclus. fc_price_desc: Prix avec TVA, consigne et part de la coop inclus.
@ -1472,8 +1493,6 @@ fr:
unit: Unité unit: Unité
unit_quantity: U/L unit_quantity: U/L
unit_quantity_desc: Unités par lot unit_quantity_desc: Unités par lot
units: Quantité
units_desc: Unités assignées
group: group:
access: Accès à access: Accès à
activated: activé activated: activé
@ -1532,7 +1551,7 @@ fr:
message: message:
private: Le message n'apparaîtra pas dans la boîte de réception du Foodsoft private: Le message n'apparaîtra pas dans la boîte de réception du Foodsoft
order_article: order_article:
units_to_order: Nombre de lots livrés units_to_order:
update_current_price: Modifie aussi le prix des commandes en cours update_current_price: Modifie aussi le prix des commandes en cours
stock_article: stock_article:
copy_stock_article: copy_stock_article:
@ -1727,15 +1746,6 @@ fr:
title: Modifier l'article title: Modifier l'article
form: form:
price_hint: Pour éviter que ça soit le bazar, les prix des articles en stock ne peuvent plus être modifiés. price_hint: Pour éviter que ça soit le bazar, les prix des articles en stock ne peuvent plus être modifiés.
history:
change_quantity: Modification
datetime: Temps
delivery: Réapprovisionnement
new_quantity: Nouveau stock
order: Commande
reason: Raison
stock_changes: Afficher l'historique pour "%{article_name}"
stock_taking: Inventaire
index: index:
article: article:
article: Article article: Article
@ -1761,6 +1771,15 @@ fr:
new: new:
search_text: ! 'Rechercher des articles dans tous les catalogues:' search_text: ! 'Rechercher des articles dans tous les catalogues:'
title: Ajouter un article au stock title: Ajouter un article au stock
show:
change_quantity: Modification
datetime: Temps
delivery: Réapprovisionnement
new_quantity: Nouveau stock
order: Commande
reason: Raison
stock_changes: Afficher l'historique
stock_taking: Inventaire
stock_create: stock_create:
notice: L'article a été sauvegardé. notice: L'article a été sauvegardé.
stock_update: stock_update:
@ -1893,7 +1912,6 @@ fr:
close: Fermer close: Fermer
delete: Supprimer delete: Supprimer
edit: Modifier edit: Modifier
history: Afficher l'historique
marks: marks:
close: ! '&times;' close: ! '&times;'
success: <i class="icon icon-ok"></i> success: <i class="icon icon-ok"></i>

View file

@ -39,10 +39,13 @@ nl:
article_category: categorie article_category: categorie
availability: Artikel leverbaar? availability: Artikel leverbaar?
deposit: statiegeld deposit: statiegeld
fc_price: fc_price: prijs foodcoop
fc_share: fc_share: marge foodcoop
gross_price: bruto prijs gross_price: bruto prijs
name:
note:
price: netto prijs price: netto prijs
supplier:
tax: BTW tax: BTW
unit: eenheid unit: eenheid
unit_quantity: groothandelseenheid unit_quantity: groothandelseenheid
@ -51,6 +54,8 @@ nl:
note: notitie note: notitie
stock_article: stock_article:
price: prijs price: prijs
quantity:
quantity_available:
user: user:
first_name: Voornaam first_name: Voornaam
password: Wachtwoord password: Wachtwoord
@ -87,7 +92,7 @@ nl:
task: task:
attributes: attributes:
done: done:
exclusion: exclusion: gedane taken kunnen niet herhaald worden
template: template:
body: ! 'Controleer de volgende velden:' body: ! 'Controleer de volgende velden:'
header: header:
@ -145,7 +150,7 @@ nl:
first_paragraph: Hier kun je %{url} toevoegen, bewerken en verwijderen. first_paragraph: Hier kun je %{url} toevoegen, bewerken en verwijderen.
new_ordergroup: Nieuw huishouden toevoegen new_ordergroup: Nieuw huishouden toevoegen
new_ordergroups: nieuwe huishoudens new_ordergroups: nieuwe huishoudens
second_paragraph: second_paragraph: ! 'Bedenk het <em>onderscheid tussen werkgroep en huishouden</em>: een huishouden heeft een rekening en kan bestellen. in een <em>%{url}</em> (bijv. sorteergroep) werken leden samen om taken te vervullen. Leden kunnen slechts lid zijn van éen huishouden, maar van meerdere werkgroepen.'
title: Huishoudens title: Huishoudens
workgroup: werkgroep workgroup: werkgroep
new: new:
@ -212,6 +217,11 @@ nl:
workgroups: workgroups:
members: leden members: leden
name: naam name: naam
application:
controller:
error_authn: Inloggen vereist.
error_denied: Geen toegang.
error_members_only: Deze actie is alleen beschikbaar voor leden van de groep!
article_categories: article_categories:
create: create:
notice: Categorie is opgeslagen notice: Categorie is opgeslagen
@ -263,7 +273,7 @@ nl:
notice: Alle artikelen en prijzen zijn bijgewerkt. notice: Alle artikelen en prijzen zijn bijgewerkt.
destroy_active_article: destroy_active_article:
drop: verwijderen drop: verwijderen
note: note: ! '%{article} is deel van een lopende bestelling en kan niet verwijderd worden. Het artikel graag eerst uit de bestelling(en) %{drop_link}.'
edit_all: edit_all:
note: ! 'Verplichte velden zijn: Naam, eenheid, (netto) prijs en bestellingsnummer.' note: ! 'Verplichte velden zijn: Naam, eenheid, (netto) prijs en bestellingsnummer.'
submit: Alle artikelen bijwerken submit: Alle artikelen bijwerken
@ -305,18 +315,22 @@ nl:
error_nosel: Je hebt geen artikelen geselecteerd error_nosel: Je hebt geen artikelen geselecteerd
parse_upload: parse_upload:
body: <p><i>Ingelezen artikelen graag controleren.</i></p> <p><i>Let op, momenteel vind er geen controle op dubbele artikelen plaats.</i></p> body: <p><i>Ingelezen artikelen graag controleren.</i></p> <p><i>Let op, momenteel vind er geen controle op dubbele artikelen plaats.</i></p>
title:
sync:
outlist: outlist:
body: ! 'De volgende artikelen werden uit de lijst gehaald en worden <b>gewist</b>:' body: ! 'De volgende artikelen zijn uit de lijst gehaald en worden <b>verwijderd</b>:'
body_skip: Er zijn geen artikelen om te wissen. body_skip: Er zijn geen artikelen om te verwijderen.
title: Uit de lijst halen ... title: Uit de lijst halen ...
price_short: prijs price_short: prijs
submit: Alle wissen/bijwerken submit: Alles synchroniseren
title: Artikelen met externe database synchroniseren title: Artikelen met externe database synchroniseren
unit_quantity_short: unit_quantity_short: Gr.Eenh.
update: update:
body: body: ! 'Ieder artikel wordt tweemaal getoond: oude waarden zijn grijs, en de tekstvelden bevatten de nieuwe waarden. Verschillen met de oude artikelen zijn geel gemarkeerd.'
title: title: Bijwerken ...
update_msg: update_msg:
one: Er moet éen artikel bijgewerkt worden.
other: Er moeten %{count} artikelen bijgewerkt worden.
upload: upload:
body: body:
fields: fields:
@ -426,11 +440,11 @@ nl:
add_stock_change: add_stock_change:
how_many_units: how_many_units:
create: create:
notice: notice: Levering is aangemaakt. Vergeet niet een factuur te maken!
create_stock_article: create_stock_article:
notice: notice: Nieuw voorraadsartikel "%{name}" gemaakt.
destroy: destroy:
notice: notice: Levering is verwijdered.
edit: edit:
title: title:
form: form:
@ -474,7 +488,7 @@ nl:
remove_article: remove_article:
suppliers_overview: suppliers_overview:
update: update:
notice: notice: Levering is bijgewerkt.
update_stock_article: update_stock_article:
notice: notice:
documents: documents:
@ -498,33 +512,38 @@ nl:
title: ! 'Huishoudenslijst van bestelling: %{name}, gesloten op %{date}' title: ! 'Huishoudenslijst van bestelling: %{name}, gesloten op %{date}'
order_fax: order_fax:
filename: Bestelling %{name}-%{date} - Fax filename: Bestelling %{name}-%{date} - Fax
rows: ! '[]' rows:
total: Totaal total: Totaal
order_matrix: order_matrix:
filename: Bestelling %{name}-%{date} - Sorteermatrix filename: Bestelling %{name}-%{date} - Sorteermatrix
heading: Artikeloverzicht heading: Artikeloverzicht
rows: rows:
- Artikel
- Eenheid
- Gr.Eenh.
- Foodcoop-prijs
- Aantal
title: ! 'Sorteermatrix van bestelling: %{name}, gesloten op %{date}' title: ! 'Sorteermatrix van bestelling: %{name}, gesloten op %{date}'
total: total:
one: In totaal éen artikel one: In totaal éen artikel
other: In totaal %{count} artikelen other: In totaal %{count} artikelen
errors: errors:
format: format: ! '%{attribute} %{message}'
general: Er is een probleem opgetreden. general: Er is een probleem opgetreden.
general_again: general_again: Er is een fout opgetreden. Probeer het opnieuw.
general_msg: ! 'Er is een probleem opgetreden: %{msg}' general_msg: ! 'Er is een probleem opgetreden: %{msg}'
messages: messages:
accepted: moet geaccepteerd worden accepted: moet geaccepteerd worden
blank: moet ingevuld worden blank: moet ingevuld worden
confirmation: confirmation: komt niet overeen met de bevestiging
empty: moet ingevuld worden empty: moet ingevuld worden
equal_to: moet precies %{count} zijn equal_to: moet precies %{count} zijn
even: even: moet even zijn
exclusion: moet even zijn exclusion: moet even zijn
greater_than: moet groter dan %{count} zijn greater_than: moet groter dan %{count} zijn
greater_than_or_equal_to: greater_than_or_equal_to: moet groter dan of gelijk zijn aan %{count}
inclusion: inclusion: geen geldige waarde
invalid: invalid: is ongeldig
less_than: moet kleiner dan %{count} zijn less_than: moet kleiner dan %{count} zijn
less_than_or_equal_to: moet groter of gelijk aan %{count} zijn less_than_or_equal_to: moet groter of gelijk aan %{count} zijn
not_a_number: is geen getal not_a_number: is geen getal
@ -534,16 +553,24 @@ nl:
taken: is al in gebruik taken: is al in gebruik
taken_with_deleted: is al in gebruik (verwijderde groep) taken_with_deleted: is al in gebruik (verwijderde groep)
too_long: too_long:
one: is te lang (niet meer dan éen teken)
other: is te lang (niet meer dan %{count} tekens)
too_short: too_short:
one: is te kort (niet minder dan éen teken)
other: is te kort (niet minder dan %{count} tekens)
wrong_length: wrong_length:
one: heeft de verkeerde lengte (moet precies éen zijn)
other: heeft de verkeerde lengte (moet precies %{count} tekens hebben)
template: template:
body: body: ! 'Controleer alsjeblieft de volgende velden:'
header: header:
one: ! 'Kon %{model} niet opslaan: éen fout gevonden.'
other: ! 'Kon %{model} niet opslaan: %{count} fouten gevonden.'
feedback: feedback:
create: create:
notice: notice: Bericht verstuurd. Vriendelijk bedankt!
new: new:
first_paragraph: first_paragraph: Probleem gevonden? Voorstel? Idee? Verbeterpunt? We horen graag je feedback.
second_paragraph: second_paragraph:
send: send:
title: title:
@ -630,10 +657,12 @@ nl:
create: create:
notice: Rekening is gemaakt notice: Rekening is gemaakt
financial_transactions: financial_transactions:
controller:
create: create:
notice: De transactie is opgeslagen. notice: De transactie is opgeslagen.
create_collection: create_collection:
alert: alert:
error_note_required: Notitie ontbreekt.
notice: Alle transacties zijn opgeslagen. notice: Alle transacties zijn opgeslagen.
index: index:
balance: ! 'Tegoed: %{balance}' balance: ! 'Tegoed: %{balance}'
@ -653,8 +682,8 @@ nl:
sidebar: sidebar:
title: title:
ordergroup: ordergroup:
remove: remove: Verwijderen
remove_group: remove_group: Huishouden verwijderen
transactions: transactions:
amount: Bedrag amount: Bedrag
date: Datum date: Datum
@ -708,7 +737,7 @@ nl:
title: Tegoeden beheren title: Tegoeden beheren
ordergroups: ordergroups:
account_balance: Tegoed account_balance: Tegoed
account_statement: account_statement: Rekeningafschrift
contact: contact:
name: Naam name: Naam
new_transaction: Nieuwe transactie new_transaction: Nieuwe transactie
@ -717,31 +746,31 @@ nl:
foodcoop: foodcoop:
ordergroups: ordergroups:
index: index:
name: name: Naam ...
only_active: only_active: Alleen actieve
only_active_desc: only_active_desc: (minstens eenmaal in de laatste 3 maanden besteld)
title: title: Huishoudens
ordergroups: ordergroups:
last_ordered: last_ordered: laatste besteld
name: name: Naam
user: user: Leden
users: users:
index: index:
body: body: <p>Hier kun je leden van deze foodcoop een bericht sturen.</p> <p>Om je eigen contactgegevens te laten zien, moet je die vrijgeven op %{profile_link}.</p>
ph_name: ph_name: Naam ...
ph_ordergroup: ph_ordergroup: Huishouden ...
profile_link: profile_link: Instellingen
title: title: Leden
workgroups: workgroups:
edit: edit:
invite_link: invite_link: hier
invite_new: invite_new: Nieuwe leden kun je %{invite_link} uitnodigen.
title: title: Groep bewerken
index: index:
body: body: <p>De groep kan alleen aangepast worden door leden ervan.<br/> Als je lid wilt worden van een groep, stuur dan een bericht aan een van de leden.</p>
title: title: Werkgroepen
workgroup: workgroup:
edit: edit: Groep bewerken
show_tasks: show_tasks:
group_orders: group_orders:
archive: archive:
@ -773,6 +802,8 @@ nl:
new_funds: Nieuw tegoed new_funds: Nieuw tegoed
note: Notitie note: Notitie
price: Prijs price: Prijs
reset_article_search:
search_article:
sum: Som sum: Som
sum_amount: ! 'Huidig totaalbedrag:' sum_amount: ! 'Huidig totaalbedrag:'
supplier: Leverancier supplier: Leverancier
@ -791,9 +822,9 @@ nl:
title: Afgerekende bestellingen title: Afgerekende bestellingen
finished_orders: finished_orders:
title: Niet afgerekende bestellingen title: Niet afgerekende bestellingen
total_sum: total_sum: Totaal
funds: funds:
account_balance: account_balance: Tegoed
available_funds: available_funds:
finished_orders: niet afgerekende bestellingen finished_orders: niet afgerekende bestellingen
open_orders: Lopende bestellingen open_orders: Lopende bestellingen
@ -841,7 +872,7 @@ nl:
title: Lopende bestellingen title: Lopende bestellingen
update: update:
error_general: error_general:
error_stale: error_stale: In de tussentijd heeft iemand anders ook bestelt, daarom kon je bestelling niet opgeslagen worden. Sorry!
notice: De bestelling is opgeslagen. notice: De bestelling is opgeslagen.
helpers: helpers:
application: application:
@ -875,36 +906,36 @@ nl:
home: home:
apple_bar: apple_bar:
desc: desc:
more_info: more_info: Meer informatie
points: points:
warning: warning:
changes_saved: Wijzigingen opgeslagen. changes_saved: Wijzigingen opgeslagen.
index: index:
due_date_format: due_date_format: ! '%A %d %B'
messages: messages:
title: title:
view_all: view_all:
my_ordergroup: my_ordergroup:
funds: funds: ! '| Beschikbaar tegoed:'
last_update: last_update: Laatst gewijzigd %{when} geleden
title: title: Mijn huishouden
transactions: transactions:
amount: amount: Bedrag
note: note: Notitie
title: title: Laatste transacties
view: view: Rekeningafschrift tonen
when: when: Wanneer
where: where: Wie
ordergroup: ordergroup:
title: title:
tasks_move: tasks_move:
action: action: Taken op je nemen/annuleren
desc: desc: Je bent voor de volgende taken verantwoordelijk.
title: title: Taken op je nemen
tasks_open: tasks_open:
action: action: open taken
desc: desc: Er zijn %{size}
title: title: open taken
title: Beginpagina title: Beginpagina
your_tasks: Jouw taken your_tasks: Jouw taken
no_ordergroups: Jammergenoeg ben je niet aangesloten bij een huishouden. no_ordergroups: Jammergenoeg ben je niet aangesloten bij een huishouden.
@ -930,8 +961,8 @@ nl:
start_nav: start_nav:
admin: admin:
finances: finances:
accounts: accounts: Tegoeden bijwerken
settle: settle: Bestelling afrekenen
title: Financiën title: Financiën
foodcoop: Foodcoop foodcoop: Foodcoop
members: Leden members: Leden
@ -957,7 +988,6 @@ nl:
title: title:
new: new:
action: action:
back:
body: body:
success: success:
layouts: layouts:
@ -1037,16 +1067,34 @@ nl:
Vriendelijke groet van %{foodcoop}.' Vriendelijke groet van %{foodcoop}.'
reset_password: reset_password:
subject: subject: Nieuw wachtwoord voor %{username}
text: text: ! 'Beste %{user},
Jij (of iemand anders) heeft een nieuw wachtwoord aangevraagd voor het foodcoop ordersysteem.
Ga naar de volgende pagina om een nieuw wachtwoord in te voeren: %{link}
Dit kan slechts éenmaal gedaan worden, en op zijn laatst op %{expires}.
Wanneer je je wachtwoord niet wilt veranderen, hoef je niets te doen; dan blijft je huidige wachtwoord geldig.
Groeten van je foodcoop!'
upcoming_tasks: upcoming_tasks:
nextweek: nextweek: ! 'Taken voor komende week:'
subject: subject: Er is een taak te doen!
text0: text0: ! 'Beste %{user},
text1:
Je bent opgegeven voor "%{task}". Deze taak is morgen te vervullen (%{when})!'
text1: ! 'Mijn taken: %{user_tasks_url}
Groeten van %{foodcoop}.'
messages: messages:
create: create:
notice: notice: Bericht is opgeslagen en wordt verzonden.
index: index:
new: new:
title: title:
@ -1063,9 +1111,9 @@ nl:
subscribe: subscribe:
subscribe_msg: subscribe_msg:
wiki: wiki:
no_user_found: no_user_found: Geen gebruiker gevonden
search: search: Zoeken ...
search_user: search_user: Gebruiker zoeken
title: title:
show: show:
all_messages: all_messages:
@ -1076,15 +1124,15 @@ nl:
title: title:
model: model:
delivery: delivery:
each_stock_article_must_be_unique: each_stock_article_must_be_unique: In een levering mag ieder voorraadsartikel maar een keer voorkomen.
membership: membership:
no_admin_delete: no_admin_delete: Lidmaatschap kan niet beeindigd worden. Je bent de laatste administrator.
order_article: order_article:
error_price: error_price: moet ingevuld worden en een huidige prijs hebben
page: page:
redirect: redirect: Doorverwijzing naar [[%{title}]]...
user: user:
no_ordergroup: no_ordergroup: geen huishouden
navigation: navigation:
admin: admin:
home: Overzicht home: Overzicht
@ -1172,45 +1220,46 @@ nl:
delimiter: delimiter:
ordergroups: ordergroups:
edit: edit:
title: title: Huidhouden bewerken
index: index:
title: title: Huishoudens
model: model:
error_single_group: error_single_group: ! '%{user} behoort al tot een ander huishouden'
invalid_balance: invalid_balance: is geen geldig nummer
orders: orders:
articles: articles:
article_count: article_count: ! 'Bestelde artikelen:'
name: name: Naam
prices: prices: Netto/bruto prijs
prices_sum: prices_sum: ! 'Totaal (netto/bruto prijs):'
unit_quantity: unit_quantity: Groothandelseenheid
units_full: units_full: Volle eenheden
units_ordered: units_ordered: Bestelde eenheden
create: create:
notice: notice: De bestelling is aangemaakt.
edit: edit:
title: title: Bestelling aanpassen
fax: fax:
amount: amount: Aantal
articles: articles: Artikelen
customer_number: customer_number: Klantnummer
delivery_day: delivery_day: Bezorgdag
heading: heading: Bestelling voor %{name}
name: name: Naam
number: number: Nummer
to_address: to_address: Verzendadres
finish: finish:
notice: De bestelling is gesloten. notice: De bestelling is gesloten.
form: form:
name: ignore_warnings: Waarschuwingen negeren
note: name: Naam
origin: note: Notitie
prices: origin: Herkomst
select_all: prices: Prijs (netto/FC)
stockit: select_all: Alles selecteren
supplier: stockit: Beschikbaar
title: supplier: Producent
title: Artikel
unit_quantity: unit_quantity:
index: index:
action_end: action_end:
@ -1228,8 +1277,10 @@ nl:
error_closed: Bestelling was al afgerekend error_closed: Bestelling was al afgerekend
error_nosel: error_nosel:
error_starts_before_ends: error_starts_before_ends:
notice_close: notice_close: ! 'Bestelling: %{name}, tot %{ends}'
stock: Voorraad stock: Voorraad
warning_ordered:
warning_ordered_stock:
new: new:
title: title:
orders: orders:
@ -1270,7 +1321,7 @@ nl:
finished: gesloten finished: gesloten
open: lopend open: lopend
update: update:
notice: notice: De bestelling is bijgewerkt.
pages: pages:
all: all:
new_page: new_page:
@ -1284,12 +1335,12 @@ nl:
body: body:
title_toc: title_toc:
create: create:
notice: notice: Pagina is gemaakt.
cshow: cshow:
error_noexist: error_noexist:
redirect_notice: redirect_notice: Doorverwezen van %{page} ...
destroy: destroy:
notice: notice: De pagina '%{page}' en alle subpagina's zijn verwijderd.
edit: edit:
title: title:
error_stale_object: error_stale_object:
@ -1333,7 +1384,7 @@ nl:
versions: versions:
title: title:
update: update:
notice: notice: Pagina is bijgewerkt.
version: version:
author: author:
date_format: date_format:
@ -1354,11 +1405,14 @@ nl:
title: Foodsoft aanmelden title: Foodsoft aanmelden
user: Gebruiker user: Gebruiker
shared: shared:
articles:
ordered: Besteld
ordered_desc: Hoeveel artikelen het lid besteld heeft (aantal + tolerantie)
received: Ontvangen
received_desc: Hoeveel artikelen het lid is toegewezen danwel heeft gekregen
articles_by_articles: articles_by_articles:
ordered: Besteld (Hoeveelheid + Tolerantie)
ordergroup: Huishouden ordergroup: Huishouden
price: Totaalprijs price: Totaalprijs
received: Ontvangen
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, borg en foodcoop-toeslag
@ -1367,19 +1421,17 @@ nl:
unit: Eenheid unit: Eenheid
unit_quantity: Gr.Eenh. unit_quantity: Gr.Eenh.
unit_quantity_desc: Hoeveel eenheden per groothandelsverpakking unit_quantity_desc: Hoeveel eenheden per groothandelsverpakking
units: Aantal
units_desc: Toegewezen eenheden
group: group:
access: Toegang tot access: Toegang tot
activated: actief activated: actief
address: Adres address: Adres
apple_limit: apple_limit:
contact: Contact contact: Contact
deactivated: deactivated: inactief
description: description: Beschrijving
members: Leden members: Leden
no_weekly_job: no_weekly_job: geen wekelijkse taak ingesteld
weekly_job: weekly_job: wekelijkse taak
group_form_fields: group_form_fields:
search: Zoeken ... search: Zoeken ...
search_user: Gebruiker zoeken search_user: Gebruiker zoeken
@ -1388,8 +1440,8 @@ nl:
loginInfo: loginInfo:
edit_profile: Profiel aanpassen edit_profile: Profiel aanpassen
feedback: feedback:
desc: desc: Fout gevonden? Opmerking? Idee?
title: title: Feedback
help: Help help: Help
homepage_title: Foodcoop startpagina bezoeken homepage_title: Foodcoop startpagina bezoeken
logout: Uitloggen logout: Uitloggen
@ -1417,7 +1469,7 @@ nl:
total_sum: Totaalsom total_sum: Totaalsom
who_ordered: Wie heeft besteld? who_ordered: Wie heeft besteld?
workgroup_members: workgroup_members:
title: title: Groepsleden
simple_form: simple_form:
error_notification: error_notification:
default_message: default_message:
@ -1531,7 +1583,7 @@ nl:
address: address:
contact_person: contact_person:
customer_number: customer_number:
delivery_days: delivery_days: Bezorgdagen
email: email:
fax: fax:
is_subscribed: is_subscribed:
@ -1582,7 +1634,7 @@ nl:
'yes': Ja 'yes': Ja
stock_takings: stock_takings:
create: create:
notice: notice: Inventarisatie is aangelegd.
edit: edit:
title: title:
index: index:
@ -1610,25 +1662,16 @@ nl:
date: date:
note: note:
update: update:
notice: notice: Inventarisatie is bijgewerkt.
stockit: stockit:
check: check:
not_empty: not_empty:
destroy: destroy:
notice: notice: Artikel %{name} is verwijdered.
edit: edit:
title: title:
form: form:
price_hint: price_hint:
history:
change_quantity:
datetime:
delivery:
new_quantity:
order:
reason:
stock_changes:
stock_taking:
index: index:
article: article:
article: article:
@ -1654,15 +1697,24 @@ nl:
new: new:
search_text: search_text:
title: title:
show:
change_quantity:
datetime:
delivery:
new_quantity:
order:
reason:
stock_changes:
stock_taking:
stock_create: stock_create:
notice: notice: Voorraadsartikel is opgeslagen.
stock_update: stock_update:
notice: notice: Voorraadsartikel is bijgewerkt.
suppliers: suppliers:
create: create:
notice: notice: Leverancier is aangemaakt.
destroy: destroy:
notice: notice: Leverancier is verwijderd
edit: edit:
title: title:
index: index:
@ -1688,7 +1740,7 @@ nl:
new_delivery: new_delivery:
show_deliveries: show_deliveries:
update: update:
notice: notice: Leverancier is bijgewerkt
support: support:
array: array:
last_word_connector: last_word_connector:
@ -1696,7 +1748,7 @@ nl:
words_connector: words_connector:
tasks: tasks:
accept: accept:
notice: notice: Je hebt de taak geaccepteerd
archive: archive:
title: title:
archive_tasks: archive_tasks:
@ -1705,9 +1757,9 @@ nl:
task_format: task_format:
who: who:
create: create:
notice: notice: Taak is aangemaakt
destroy: destroy:
notice: notice: Taak is verwijderd
edit: edit:
title: title:
warning_periodic: warning_periodic:
@ -1745,7 +1797,7 @@ nl:
title: title:
repeated: repeated:
set_done: set_done:
notice: notice: De status van de taak is aangepast
show: show:
accept_task: accept_task:
confirm_delete_group: confirm_delete_group:
@ -1756,8 +1808,8 @@ nl:
reject_task: reject_task:
title: title:
update: update:
notice: notice: Taak is bijgewerkt
notice_converted: notice_converted: Taak is bijgewerkt en omgezet naar een eenmalige taak.
user: user:
more: more:
tasks_link: tasks_link:
@ -1778,7 +1830,6 @@ nl:
close: Sluiten close: Sluiten
delete: Verwijder delete: Verwijder
edit: Bewerk edit: Bewerk
history:
marks: marks:
close: ! '&times;' close: ! '&times;'
success: <i class="icon icon-ok"></i> success: <i class="icon icon-ok"></i>

View file

@ -97,8 +97,6 @@ Foodsoft::Application.routes.draw do
get :articles_search get :articles_search
get :fill_new_stock_article_form get :fill_new_stock_article_form
end end
get :history
end end
resources :suppliers do resources :suppliers do

View file

@ -3,13 +3,11 @@
# Upcoming tasks notifier # Upcoming tasks notifier
every :day, :at => '7:20 am' do every :day, :at => '7:20 am' do
rake "multicoops:run foodsoft:notify_upcoming_tasks" rake "multicoops:run TASK=foodsoft:notify_upcoming_tasks"
end end
# Weekly taks # Weekly taks
every :sunday, :at => '7:14 am' do every :sunday, :at => '7:14 am' do
rake "multicoops:run foodsoft:create_upcoming_weekly_tasks" rake "multicoops:run TASK=foodsoft:create_upcoming_periodic_tasks"
rake "multicoops:run foodsoft:notify_users_of_weekly_task" rake "multicoops:run TASK=foodsoft:notify_users_of_weekly_task"
end end

View file

@ -0,0 +1,18 @@
class UpdateGroupOrderTotals < ActiveRecord::Migration
def self.up
say "If you have ever modified an order after it was settled, the group_order's " +
"price may be calculated incorrectly. This can take a lot of time on a " +
"large database."
say "If you do want to update the ordergroup totals, open the rails console " +
"(by running `rails c`), and enter:"
say "GroupOrder.all.each { |go| go.order.closed? and go.update_price! }", subitem: true
say "You may want to check first that no undesired accounting issues are introduced. " +
"It may be wise to discuss this with those responsible for the ordering finances."
end
def self.down
end
end

View file

@ -5,12 +5,12 @@ namespace :foodsoft do
task :notify_upcoming_tasks => :environment do task :notify_upcoming_tasks => :environment do
tasks = Task.where(done: false, due_date: 1.day.from_now.to_date) tasks = Task.where(done: false, due_date: 1.day.from_now.to_date)
for task in tasks for task in tasks
puts "Send notifications for #{task.name} to .." rake_say "Send notifications for #{task.name} to .."
for user in task.users for user in task.users
begin begin
Mailer.upcoming_tasks(user, task).deliver if user.settings.notify['upcoming_tasks'] == 1 Mailer.upcoming_tasks(user, task).deliver if user.settings.notify['upcoming_tasks'] == 1
rescue rescue
puts "deliver aborted for #{user.email}.." rake_say "deliver aborted for #{user.email}.."
end end
end end
end end
@ -27,7 +27,7 @@ namespace :foodsoft do
begin begin
Mailer.not_enough_users_assigned(task, user).deliver Mailer.not_enough_users_assigned(task, user).deliver
rescue rescue
puts "deliver aborted for #{user.email}" rake_say "deliver aborted for #{user.email}"
end end
end end
end end
@ -47,3 +47,8 @@ namespace :foodsoft do
end end
end end
end end
# Helper
def rake_say(message)
puts message unless Rake.application.options.silent
end

View file

@ -8,7 +8,7 @@ namespace :multicoops do
task :run => :environment do task :run => :environment do
task_to_run = ENV['TASK'] task_to_run = ENV['TASK']
FoodsoftConfig.each_coop do |coop| FoodsoftConfig.each_coop do |coop|
puts "Run '#{task_to_run}' for #{coop}" rake_say "Run '#{task_to_run}' for #{coop}"
Rake::Task[task_to_run].execute Rake::Task[task_to_run].execute
end end
end end
@ -17,8 +17,14 @@ namespace :multicoops do
task :run_single => :environment do task :run_single => :environment do
task_to_run = ENV['TASK'] task_to_run = ENV['TASK']
FoodsoftConfig.select_foodcoop ENV['FOODCOOP'] FoodsoftConfig.select_foodcoop ENV['FOODCOOP']
puts "Run '#{task_to_run}' for #{ENV['FOODCOOP']}" rake_say "Run '#{task_to_run}' for #{ENV['FOODCOOP']}"
Rake::Task[task_to_run].execute Rake::Task[task_to_run].execute
end end
end end
# Helper
def rake_say(message)
puts message unless Rake.application.options.silent
end

View file

@ -1,3 +1,6 @@
require 'rspec/core/rake_task' begin
RSpec::Core::RakeTask.new(:spec) require 'rspec/core/rake_task'
task :default => :spec RSpec::Core::RakeTask.new(:spec)
task :default => :spec
rescue LoadError
end

View file

@ -92,7 +92,7 @@ require 'localeapp/rails'
Localeapp.configure do |config| Localeapp.configure do |config|
config.api_key = '$LOCALEAPP_KEY' config.api_key = '$LOCALEAPP_KEY'
config.sending_environments = ['$RAILS_ENV'] config.sending_environments = []
config.polling_environments = ['$RAILS_ENV'] config.polling_environments = ['$RAILS_ENV']
end end
EOF EOF

View file

@ -10,7 +10,7 @@ FactoryGirl.define do
deposit { rand(10) < 8 ? 0 : [0.0, 0.80, 1.20, 12.00].sample } deposit { rand(10) < 8 ? 0 : [0.0, 0.80, 1.20, 12.00].sample }
unit_quantity { rand(5) < 3 ? 1 : rand(1..20) } unit_quantity { rand(5) < 3 ? 1 : rand(1..20) }
#supplier_id #supplier_id
article_category { FactoryGirl.create :article_category } article_category { create :article_category }
end end
factory :article_category do factory :article_category do

View file

@ -4,7 +4,7 @@ FactoryGirl.define do
# requires order # requires order
factory :group_order do factory :group_order do
ordergroup { FactoryGirl.create(:user, groups: [FactoryGirl.create(:ordergroup)]).ordergroup } ordergroup { create(:user, groups: [FactoryGirl.create(:ordergroup)]).ordergroup }
end end
end end

View file

@ -4,7 +4,7 @@ FactoryGirl.define do
factory :order do factory :order do
starts { Time.now } starts { Time.now }
supplier { FactoryGirl.create :supplier, article_count: (article_count.nil? ? true : article_count) } supplier { create :supplier, article_count: (article_count.nil? ? true : article_count) }
article_ids { supplier.articles.map(&:id) unless supplier.nil? } article_ids { supplier.articles.map(&:id) unless supplier.nil? }
ignore do ignore do

View file

@ -14,7 +14,7 @@ FactoryGirl.define do
after :create do |supplier, evaluator| after :create do |supplier, evaluator|
article_count = evaluator.article_count article_count = evaluator.article_count
article_count = rand(1..99) if article_count == true article_count = rand(1..99) if article_count == true
FactoryGirl.create_list :article, article_count, supplier: supplier create_list :article, article_count, supplier: supplier
end end
end end

View file

@ -12,7 +12,7 @@ FactoryGirl.define do
sequence(:nick) { |n| "admin#{n}" } sequence(:nick) { |n| "admin#{n}" }
first_name 'Administrator' first_name 'Administrator'
after :create do |user, evaluator| after :create do |user, evaluator|
FactoryGirl.create :workgroup, role_admin: true, user_ids: [user.id] create :workgroup, role_admin: true, user_ids: [user.id]
end end
end end
end end

View file

@ -1,15 +1,15 @@
require 'spec_helper' require_relative '../spec_helper'
describe 'settling an order', :type => :feature do describe 'settling an order', :type => :feature do
let(:admin) { FactoryGirl.create :user, groups:[FactoryGirl.create(:workgroup, role_finance: true)] } let(:admin) { create :user, groups:[create(:workgroup, role_finance: true)] }
let(:supplier) { FactoryGirl.create :supplier } let(:supplier) { create :supplier }
let(:article) { FactoryGirl.create :article, supplier: supplier, unit_quantity: 1 } let(:article) { create :article, supplier: supplier, unit_quantity: 1 }
let(:order) { FactoryGirl.create :order, supplier: supplier, article_ids: [article.id] } # need to ref article let(:order) { create :order, supplier: supplier, article_ids: [article.id] } # need to ref article
let(:go1) { FactoryGirl.create :group_order, order: order } let(:go1) { create :group_order, order: order }
let(:go2) { FactoryGirl.create :group_order, order: order } let(:go2) { create :group_order, order: order }
let(:oa) { order.order_articles.find_by_article_id(article.id) } let(:oa) { order.order_articles.find_by_article_id(article.id) }
let(:goa1) { FactoryGirl.create :group_order_article, group_order: go1, order_article: oa } let(:goa1) { create :group_order_article, group_order: go1, order_article: oa }
let(:goa2) { FactoryGirl.create :group_order_article, group_order: go2, order_article: oa } let(:goa2) { create :group_order_article, group_order: go2, order_article: oa }
before do before do
goa1.update_quantities(3, 0) goa1.update_quantities(3, 0)
goa2.update_quantities(1, 0) goa2.update_quantities(1, 0)
@ -50,6 +50,76 @@ describe 'settling an order', :type => :feature do
end end
end end
it 'keeps ordered quantities when article is deleted from resulting order' do
within("#order_article_#{oa.id}") do
click_link I18n.t('ui.delete')
page.driver.browser.switch_to.alert.accept
end
expect(page).to_not have_selector("#order_article_#{oa.id}")
expect(OrderArticle.exists?(oa.id)).to be_true
oa.reload
expect(oa.quantity).to eq(4)
expect(oa.tolerance).to eq(0)
expect(oa.units_to_order).to eq(0)
expect(goa1.reload.result).to eq(0)
expect(goa2.reload.result).to eq(0)
end
it 'deletes an OrderArticle with no GroupOrderArticles' do
goa1.destroy
goa2.destroy
within("#order_article_#{oa.id}") do
click_link I18n.t('ui.delete')
page.driver.browser.switch_to.alert.accept
end
expect(page).to_not have_selector("#order_article_#{oa.id}")
expect(OrderArticle.exists?(oa.id)).to be_false
end
it 'keeps ordered quantities when GroupOrderArticle is deleted from resulting order' do
click_link article.name
expect(page).to have_selector("#group_order_article_#{goa1.id}")
within("#group_order_article_#{goa1.id}") do
click_link I18n.t('ui.delete')
end
expect(page).to_not have_selector("#group_order_article_#{goa1.id}")
expect(OrderArticle.exists?(oa.id)).to be_true
expect(GroupOrderArticle.exists?(goa1.id)).to be_true
goa1.reload
expect(goa1.result).to eq(0)
expect(goa1.quantity).to eq(3)
expect(goa1.tolerance).to eq(0)
end
it 'deletes a GroupOrderArticle with no ordered amounts' do
goa1.update_attributes({:quantity => 0, :tolerance => 0})
click_link article.name
expect(page).to have_selector("#group_order_article_#{goa1.id}")
within("#group_order_article_#{goa1.id}") do
click_link I18n.t('ui.delete')
end
expect(page).to_not have_selector("#group_order_article_#{goa1.id}")
expect(OrderArticle.exists?(oa.id)).to be_true
expect(GroupOrderArticle.exists?(goa1.id)).to be_false
end
it 'keeps product when amount is set to zero' do
within("#order_article_#{oa.id}") do
click_link I18n.t('ui.edit')
end
within("#edit_order_article_#{oa.id}") do
fill_in :order_article_units_to_order, :with => 0
find('input[type="submit"]').click
end
expect(page).to have_selector("#order_article_#{oa.id}")
# make sure it still works after reloading
visit new_finance_order_path(order_id: order.id)
expect(page).to have_selector("#order_article_#{oa.id}")
expect(OrderArticle.exists?(oa.id)).to be_true
oa.reload
expect(oa.units_to_order).to eq(0)
end
end end
end end

View file

@ -1,12 +1,12 @@
require 'spec_helper' require_relative '../spec_helper'
describe 'product distribution', :type => :feature do describe 'product distribution', :type => :feature do
let(:admin) { FactoryGirl.create :admin } let(:admin) { create :admin }
let(:user_a) { FactoryGirl.create :user, groups: [FactoryGirl.create(:ordergroup)] } let(:user_a) { create :user, groups: [create(:ordergroup)] }
let(:user_b) { FactoryGirl.create :user, groups: [FactoryGirl.create(:ordergroup)] } let(:user_b) { create :user, groups: [create(:ordergroup)] }
let(:supplier) { FactoryGirl.create :supplier } let(:supplier) { create :supplier }
let(:article) { FactoryGirl.create :article, supplier: supplier, unit_quantity: 5 } let(:article) { create :article, supplier: supplier, unit_quantity: 5 }
let(:order) { FactoryGirl.create(:order, supplier: supplier, article_ids: [article.id]) } let(:order) { create(:order, supplier: supplier, article_ids: [article.id]) }
let(:oa) { order.order_articles.first } let(:oa) { order.order_articles.first }
describe :type => :feature do describe :type => :feature do

View file

@ -1,7 +1,7 @@
require 'spec_helper' require_relative '../spec_helper'
describe 'the session', :type => :feature do describe 'the session', :type => :feature do
let(:user) { FactoryGirl.create :user } let(:user) { create :user }
describe 'login page', :type => :feature do describe 'login page', :type => :feature do
it 'is accesible' do it 'is accesible' do

View file

@ -1,16 +1,16 @@
require 'spec_helper' require_relative '../spec_helper'
describe 'supplier', :type => :feature do describe 'supplier', :type => :feature do
let(:supplier) { FactoryGirl.create :supplier } let(:supplier) { create :supplier }
describe :type => :feature, :js => true do describe :type => :feature, :js => true do
let(:user) { FactoryGirl.create :user, groups:[FactoryGirl.create(:workgroup, role_suppliers: true)] } let(:user) { create :user, groups:[create(:workgroup, role_suppliers: true)] }
before { login user } before { login user }
it 'can be created' do it 'can be created' do
visit suppliers_path visit suppliers_path
click_on I18n.t('suppliers.index.action_new') click_on I18n.t('suppliers.index.action_new')
supplier = FactoryGirl.build :supplier supplier = build :supplier
within('#new_supplier') do within('#new_supplier') do
fill_in 'supplier_name', :with => supplier.name fill_in 'supplier_name', :with => supplier.name
fill_in 'supplier_address', :with => supplier.address fill_in 'supplier_address', :with => supplier.address
@ -28,8 +28,8 @@ describe 'supplier', :type => :feature do
end end
describe :type => :feature, :js => true do describe :type => :feature, :js => true do
let(:article_category) { FactoryGirl.create :article_category } let(:article_category) { create :article_category }
let(:user) { FactoryGirl.create :user, groups:[FactoryGirl.create(:workgroup, role_article_meta: true)] } let(:user) { create :user, groups:[create(:workgroup, role_article_meta: true)] }
before { login user } before { login user }
it 'can visit supplier articles path' do it 'can visit supplier articles path' do

View file

@ -1,8 +1,8 @@
require 'spec_helper' require_relative '../spec_helper'
describe Article do describe Article do
let(:supplier) { FactoryGirl.create :supplier } let(:supplier) { create :supplier }
let(:article) { FactoryGirl.create :article, supplier: supplier } let(:article) { create :article, supplier: supplier }
it 'has a unique name' do it 'has a unique name' do
article2 = FactoryGirl.build :article, supplier: supplier, name: article.name article2 = FactoryGirl.build :article, supplier: supplier, name: article.name
@ -44,4 +44,12 @@ describe Article do
expect(article.article_prices.all.map(&:price)).to eq([article.price, oldprice]) expect(article.article_prices.all.map(&:price)).to eq([article.price, oldprice])
end end
it 'is not in an open order by default' do
expect(article.in_open_order).to be_nil
end
it 'is knows its open order' do
order = create :order, supplier: supplier, article_ids: [article.id]
expect(article.in_open_order).to eq(order)
end
end end

View file

@ -1,10 +1,10 @@
require 'spec_helper' require_relative '../spec_helper'
describe GroupOrderArticle do describe GroupOrderArticle do
let(:user) { FactoryGirl.create :user, groups: [FactoryGirl.create(:ordergroup)] } let(:user) { create :user, groups: [create(:ordergroup)] }
let(:order) { FactoryGirl.create(:order).reload } let(:order) { create(:order) }
let(:go) { FactoryGirl.create :group_order, order: order, ordergroup: user.ordergroup } let(:go) { create :group_order, order: order, ordergroup: user.ordergroup }
let(:goa) { FactoryGirl.create :group_order_article, group_order: go, order_article: order.order_articles.first } let(:goa) { create :group_order_article, group_order: go, order_article: order.order_articles.first }
it 'has zero quantity by default' do expect(goa.quantity).to eq(0) end it 'has zero quantity by default' do expect(goa.quantity).to eq(0) end
it 'has zero tolerance by default' do expect(goa.tolerance).to eq(0) end it 'has zero tolerance by default' do expect(goa.tolerance).to eq(0) end
@ -13,9 +13,9 @@ describe GroupOrderArticle do
it 'has zero total price by default' do expect(goa.total_price).to eq(0) end it 'has zero total price by default' do expect(goa.total_price).to eq(0) end
describe do describe do
let(:article) { FactoryGirl.create :article, supplier: order.supplier, unit_quantity: 1 } let(:article) { create :article, supplier: order.supplier, unit_quantity: 1 }
let(:oa) { order.order_articles.create(:article => article) } let(:oa) { order.order_articles.create(:article => article) }
let(:goa) { FactoryGirl.create :group_order_article, group_order: go, order_article: oa } let(:goa) { create :group_order_article, group_order: go, order_article: oa }
it 'can be ordered by piece' do it 'can be ordered by piece' do
goa.update_quantities(1, 0) goa.update_quantities(1, 0)
@ -42,7 +42,6 @@ describe GroupOrderArticle do
expect(goa.quantity).to eq(0) expect(goa.quantity).to eq(0)
expect(goa.tolerance).to eq(0) expect(goa.tolerance).to eq(0)
end end
end end
end end

View file

@ -1,8 +1,8 @@
require 'spec_helper' require_relative '../spec_helper'
describe GroupOrder do describe GroupOrder do
let(:user) { FactoryGirl.create :user, groups: [FactoryGirl.create(:ordergroup)] } let(:user) { create :user, groups: [create(:ordergroup)] }
let(:order) { FactoryGirl.create :order } let(:order) { create :order }
# the following two tests are currently disabled - https://github.com/foodcoops/foodsoft/issues/158 # the following two tests are currently disabled - https://github.com/foodcoops/foodsoft/issues/158
@ -15,7 +15,7 @@ describe GroupOrder do
#end #end
describe do describe do
let(:go) { FactoryGirl.create :group_order, order: order, ordergroup: user.ordergroup } let(:go) { create :group_order, order: order, ordergroup: user.ordergroup }
it 'has zero price initially' do it 'has zero price initially' do
expect(go.price).to eq(0) expect(go.price).to eq(0)

View file

@ -1,22 +1,22 @@
require 'spec_helper' require_relative '../spec_helper'
describe Order do describe Order do
it 'needs a supplier' do it 'needs a supplier' do
expect(FactoryGirl.build(:order, supplier: nil)).to be_invalid expect(build(:order, supplier: nil)).to be_invalid
end end
it 'needs order articles' do it 'needs order articles' do
supplier = FactoryGirl.create :supplier, article_count: 0 supplier = create :supplier, article_count: 0
expect(FactoryGirl.build(:order, supplier: supplier)).to be_invalid expect(build(:order, supplier: supplier)).to be_invalid
end end
it 'can be created' do it 'can be created' do
expect(FactoryGirl.build(:order, article_count: 1)).to be_valid expect(build(:order, article_count: 1)).to be_valid
end end
describe 'with articles' do describe 'with articles' do
let(:order) { FactoryGirl.create :order } let(:order) { create :order }
it 'is open by default' do expect(order).to be_open end it 'is open by default' do expect(order).to be_open end
it 'is not finished by default' do expect(order).to_not be_finished end it 'is not finished by default' do expect(order).to_not be_finished end

View file

@ -1,15 +1,15 @@
require 'spec_helper' require_relative '../spec_helper'
describe Supplier do describe Supplier do
let(:supplier) { FactoryGirl.create :supplier } let(:supplier) { create :supplier }
it 'has a unique name' do it 'has a unique name' do
supplier2 = FactoryGirl.build :supplier, name: supplier.name supplier2 = build :supplier, name: supplier.name
expect(supplier2).to be_invalid expect(supplier2).to be_invalid
end end
it 'has valid articles' do it 'has valid articles' do
supplier = FactoryGirl.create :supplier, article_count: true supplier = create :supplier, article_count: true
supplier.articles.all.each {|a| expect(a).to be_valid } supplier.articles.all.each {|a| expect(a).to be_valid }
end end

View file

@ -1,9 +1,9 @@
require 'spec_helper' require_relative '../spec_helper'
describe User do describe User do
it 'is correctly created' do it 'is correctly created' do
user = FactoryGirl.create :user, user = create :user,
nick: 'johnnydoe', first_name: 'Johnny', last_name: 'DoeBar', nick: 'johnnydoe', first_name: 'Johnny', last_name: 'DoeBar',
email: 'johnnydoe@foodcoop.test', phone: '+1234567890' email: 'johnnydoe@foodcoop.test', phone: '+1234567890'
expect(user.nick).to eq('johnnydoe') expect(user.nick).to eq('johnnydoe')
@ -15,7 +15,7 @@ describe User do
end end
describe 'does not have the role' do describe 'does not have the role' do
let(:user) { FactoryGirl.create :user } let(:user) { create :user }
it 'admin' do expect(user.role_admin?).to be_false end it 'admin' do expect(user.role_admin?).to be_false end
it 'finance' do expect(user.role_finance?).to be_false end it 'finance' do expect(user.role_finance?).to be_false end
it 'article_meta' do expect(user.role_article_meta?).to be_false end it 'article_meta' do expect(user.role_article_meta?).to be_false end
@ -24,7 +24,7 @@ describe User do
end end
describe do describe do
let(:user) { FactoryGirl.create :user, password: 'blahblah' } let(:user) { create :user, password: 'blahblah' }
it 'can authenticate with correct password' do it 'can authenticate with correct password' do
expect(User.authenticate(user.nick, 'blahblah')).to be_true expect(User.authenticate(user.nick, 'blahblah')).to be_true
@ -44,15 +44,15 @@ describe User do
end end
it 'has a unique nick' do it 'has a unique nick' do
expect(FactoryGirl.build(:user, nick: user.nick, email: "x-#{user.email}")).to be_invalid expect(build(:user, nick: user.nick, email: "x-#{user.email}")).to be_invalid
end end
it 'has a unique email' do it 'has a unique email' do
expect(FactoryGirl.build(:user, email: "#{user.email}")).to be_invalid expect(build(:user, email: "#{user.email}")).to be_invalid
end end
end end
describe 'admin' do describe 'admin' do
let(:user) { FactoryGirl.create :admin } let(:user) { create :admin }
it 'default admin role' do expect(user.role_admin?).to be_true end it 'default admin role' do expect(user.role_admin?).to be_true end
end end

View file

@ -1,6 +1,6 @@
# This file is copied to spec/ when you run 'rails generate rspec:install' # This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test' ENV["RAILS_ENV"] ||= 'test'
require 'support/coverage' # needs to be first require_relative 'support/coverage' # needs to be first
require File.expand_path("../../config/environment", __FILE__) require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails' require 'rspec/rails'
require 'rspec/autorun' require 'rspec/autorun'

View file

@ -0,0 +1,4 @@
RSpec.configure do |config|
# load FactoryGirl shortcuts create(), etc.
config.include FactoryGirl::Syntax::Methods
end