Merge remote-tracking branch 'foodcoops/master' into feature-i18n-cleanup

Conflicts:
	app/views/deliveries/_stock_article_form.html.haml
	config/locales/de.yml
	config/locales/en.yml
This commit is contained in:
wvengen 2013-12-17 15:58:47 +01:00
commit 94b2c8eac3
49 changed files with 656 additions and 358 deletions

View file

@ -3,6 +3,7 @@ rvm:
- 1.9.3 - 1.9.3
services: services:
- redis-server - redis-server
env: COVERALLS=1
before_install: before_install:
- "export DISPLAY=:99.0" - "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start" - "sh -e /etc/init.d/xvfb start"

17
CHANGELOG.md Normal file
View file

@ -0,0 +1,17 @@
# Foodsoft 3.2.0
(16 December 2013)
It's been a year since the previous release. Much has changed. Big changes have been:
* Translations to English, Dutch and French.
* Improved usability of delivery creation.
* The possibility to extend foodsoft with plugins (the wiki is now optional).
* Article search in the ordering screen.
* Foodcoops can choose to use full names and emails instead of nicknames.
* Foodcoops that don't use prepaid can set their minimum ordergroup balance below zero.
* Group and article PDFs now show articles ordered but not received in grey.
* Upgrade to Rails 3.
When you upgrade, be sure to review `config/app_config.yml.SAMPLE`. When you're running multiple foodcoops from a single installation, check your rake invocations as the syntax is now: `rake multicoops:run TASK=db:migrate`.
# Foodsoft 3.1.1
(20 July 2012)

View file

@ -84,10 +84,12 @@ group :test do
# webkit and poltergeist don't seem to work yet # webkit and poltergeist don't seem to work yet
gem 'selenium-webdriver' gem 'selenium-webdriver'
gem 'database_cleaner' gem 'database_cleaner'
gem 'simplecov', require: false
# need to include rspec components before i18n-spec or rake fails in test environment # need to include rspec components before i18n-spec or rake fails in test environment
gem 'rspec-core' gem 'rspec-core'
gem 'rspec-expectations' gem 'rspec-expectations'
gem 'rspec-rerun' gem 'rspec-rerun'
gem 'i18n-spec' gem 'i18n-spec'
# code coverage
gem 'simplecov', require: false
gem 'coveralls', require: false
end end

View file

@ -102,6 +102,12 @@ GEM
execjs execjs
coffee-script-source (1.6.3) coffee-script-source (1.6.3)
commonjs (0.2.7) commonjs (0.2.7)
coveralls (0.7.0)
multi_json (~> 1.3)
rest-client
simplecov (>= 0.7)
term-ansicolor
thor
daemons (1.1.9) daemons (1.1.9)
database_cleaner (1.2.0) database_cleaner (1.2.0)
debug_inspector (0.0.2) debug_inspector (0.0.2)
@ -248,6 +254,8 @@ GEM
redis-namespace (~> 1.2) redis-namespace (~> 1.2)
sinatra (>= 0.9.2) sinatra (>= 0.9.2)
vegas (~> 0.1.2) vegas (~> 0.1.2)
rest-client (1.6.7)
mime-types (>= 1.16)
rspec (2.14.1) rspec (2.14.1)
rspec-core (~> 2.14.0) rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0) rspec-expectations (~> 2.14.0)
@ -306,6 +314,8 @@ GEM
rack (~> 1.0) rack (~> 1.0)
tilt (~> 1.1, != 1.3.0) tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.8) sqlite3 (1.3.8)
term-ansicolor (1.2.2)
tins (~> 0.8)
therubyracer (0.12.0) therubyracer (0.12.0)
libv8 (~> 3.16.14.0) libv8 (~> 3.16.14.0)
ref ref
@ -315,6 +325,7 @@ GEM
rack (>= 1.0.0) rack (>= 1.0.0)
thor (0.18.1) thor (0.18.1)
tilt (1.4.1) tilt (1.4.1)
tins (0.13.1)
treetop (1.4.15) treetop (1.4.15)
polyglot polyglot
polyglot (>= 0.3.1) polyglot (>= 0.3.1)
@ -357,6 +368,7 @@ DEPENDENCIES
client_side_validations client_side_validations
client_side_validations-simple_form client_side_validations-simple_form
coffee-rails (~> 3.2.1) coffee-rails (~> 3.2.1)
coveralls
daemons daemons
database_cleaner database_cleaner
exception_notification exception_notification

View file

@ -1 +1 @@
3.1.1 3.2.0

View file

@ -104,6 +104,10 @@ table {
td.odd { td.odd {
background-color: @tableBackgroundAccent; background-color: @tableBackgroundAccent;
} }
td.main_info {
font-weight: bold;
}
tr.selected td { tr.selected td {
background-color: @successBackground; background-color: @successBackground;
@ -247,3 +251,40 @@ tr.unavailable {
.modal form { .modal form {
margin: 0; margin: 0;
} }
// multiple-column layout in forms (landscape tablet and wider only)
@media (min-width: 768px) {
.form-horizontal .fold-line {
.control-group {
float: left;
}
.control-group + .control-group {
.control-label {
width: auto;
margin: 0 10px;
}
.controls {
float: left;
margin-left: 0;
}
// fix margin somehow off
// XXX there must be a better way
margin-bottom: 0;
.help-block {
margin-top: 0;
margin-bottom: 20px;
}
}
.control-group:last-child {
float: none;
.controls {
float: none;
}
}
}
}
// allow to have indicator text instead of input with same markup
.control-text {
margin-top: 5px;
}

View file

@ -50,57 +50,22 @@ class DeliveriesController < ApplicationController
redirect_to supplier_deliveries_url(@supplier) redirect_to supplier_deliveries_url(@supplier)
end end
# three possibilites to fill a new_stock_article form
# (1) start from blank or use params
def new_stock_article
@stock_article = @supplier.stock_articles.build(params[:stock_article])
render :layout => false
end
# (2) StockArticle as template
def copy_stock_article
@stock_article = StockArticle.find(params[:old_stock_article_id]).dup
render :layout => false
end
# (3) non-stock Article as template
def derive_stock_article
@stock_article = Article.find(params[:old_article_id]).becomes(StockArticle).dup
render :layout => false
end
def create_stock_article
@stock_article = StockArticle.new(params[:stock_article])
if @stock_article.valid? and @stock_article.save
render :layout => false
else
render :action => 'new_stock_article', :layout => false
end
end
def edit_stock_article
@stock_article = StockArticle.find(params[:stock_article_id])
render :layout => false
end
def update_stock_article
@stock_article = StockArticle.find(params[:stock_article][:id])
if @stock_article.update_attributes(params[:stock_article])
render :layout => false
else
render :action => 'edit_stock_article', :layout => false
end
end
def add_stock_change def add_stock_change
@stock_change = StockChange.new @stock_change = StockChange.new
@stock_change.stock_article = StockArticle.find(params[:stock_article_id]) @stock_change.stock_article = StockArticle.find(params[:stock_article_id])
render :layout => false render :layout => false
end end
def form_on_stock_article_create # See publish/subscribe design pattern in /doc.
@stock_article = StockArticle.find(params[:id])
render :layout => false
end
def form_on_stock_article_update # See publish/subscribe design pattern in /doc.
@stock_article = StockArticle.find(params[:id])
render :layout => false
end
end end

View file

@ -4,30 +4,62 @@ class StockitController < ApplicationController
@stock_articles = StockArticle.undeleted.includes(:supplier, :article_category). @stock_articles = StockArticle.undeleted.includes(:supplier, :article_category).
order('suppliers.name, article_categories.name, articles.name') order('suppliers.name, article_categories.name, articles.name')
end end
def index_on_stock_article_create # See publish/subscribe design pattern in /doc.
@stock_article = StockArticle.find(params[:id])
render :layout => false
end
def index_on_stock_article_update # See publish/subscribe design pattern in /doc.
@stock_article = StockArticle.find(params[:id])
render :layout => false
end
# three possibilites to fill a new_stock_article form
# (1) start from blank or use params
def new def new
@stock_article = StockArticle.new @stock_article = StockArticle.new(params[:stock_article])
render :layout => false
end
# (2) StockArticle as template
def copy
@stock_article = StockArticle.find(params[:stock_article_id]).dup
render :layout => false
end
# (3) non-stock Article as template
def derive
@stock_article = Article.find(params[:old_article_id]).becomes(StockArticle).dup
render :layout => false
end end
def create def create
@stock_article = StockArticle.new(params[:stock_article]) @stock_article = StockArticle.new(params[:stock_article])
if @stock_article.save if @stock_article.valid? and @stock_article.save
redirect_to stock_articles_path, :notice => I18n.t('stockit.stock_create.notice') render :layout => false
else else
render :action => 'new' render :action => 'new', :layout => false
end end
end end
def edit def edit
@stock_article = StockArticle.find(params[:id]) @stock_article = StockArticle.find(params[:id])
render :layout => false
end end
def update def update
@stock_article = StockArticle.find(params[:id]) @stock_article = StockArticle.find(params[:id])
if @stock_article.update_attributes(params[:stock_article]) if @stock_article.update_attributes(params[:stock_article])
redirect_to stock_articles_path, :notice => I18n.t('stockit.stock_update.notice') render :layout => false
else else
render :action => 'edit' render :action => 'edit', :layout => false
end end
end end
@ -36,9 +68,15 @@ class StockitController < ApplicationController
@stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC') @stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC')
end end
def show_on_stock_article_update # See publish/subscribe design pattern in /doc.
@stock_article = StockArticle.find(params[:id])
render :layout => false
end
def destroy def destroy
@article = StockArticle.find(params[:id]) @stock_article = StockArticle.find(params[:id])
@article.mark_as_deleted @stock_article.mark_as_deleted
render :layout => false render :layout => false
rescue => error rescue => error
render :partial => "destroy_fail", :layout => false, render :partial => "destroy_fail", :layout => false,

View file

@ -25,13 +25,4 @@ module DeliveriesHelper
return output.html_safe return output.html_safe
end end
def stock_article_price_hint(stock_article)
t('simple_form.hints.stock_article.edit_stock_article.price',
:stock_article_copy_link => link_to(t('.copy_stock_article'),
copy_stock_article_supplier_deliveries_path(@supplier, :old_stock_article_id => stock_article.id),
:remote => true
)
)
end
end end

View file

@ -14,4 +14,13 @@ module StockitHelper
link_to StockTaking.model_name.human, stock_taking_path(stock_change.stock_taking) link_to StockTaking.model_name.human, stock_taking_path(stock_change.stock_taking)
end end
end end
def stock_article_price_hint(stock_article)
t('simple_form.hints.stock_article.edit_stock_article.price',
:stock_article_copy_link => link_to(t('.copy_stock_article'),
stock_article_copy_path(stock_article),
:remote => true
)
)
end
end end

View file

@ -101,49 +101,51 @@ 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 @calculate_result ||= begin
quantity = tolerance = 0 quantity = tolerance = total_quantity = 0
stockit = order_article.article.is_a?(StockArticle)
# Get total # Get total
total = stockit ? order_article.article.quantity : order_article.units_to_order * order_article.price.unit_quantity if order_article.article.is_a?(StockArticle)
logger.debug("<#{order_article.article.name}>.unitsToOrder => items ordered: #{order_article.units_to_order} => #{total}") total = order_article.article.quantity
logger.debug "<#{order_article.article.name}> (stock) => #{total}"
else
total = order_article.units_to_order * order_article.price.unit_quantity
logger.debug "<#{order_article.article.name}> units_to_order #{order_article.units_to_order} => #{total}"
end
if (total > 0) if total > 0
# In total there are enough units ordered. Now check the individual result for the ordergroup (group_order). # In total there are enough units ordered. Now check the individual result for the ordergroup (group_order).
# #
# Get all GroupOrderArticleQuantities for this OrderArticle... # Get all GroupOrderArticleQuantities for this OrderArticle...
order_quantities = GroupOrderArticleQuantity.all( order_quantities = GroupOrderArticleQuantity.all(
:conditions => ["group_order_article_id IN (?)", order_article.group_order_article_ids], :order => 'created_on') :conditions => ["group_order_article_id IN (?)", order_article.group_order_article_ids], :order => 'created_on')
logger.debug("GroupOrderArticleQuantity records found: #{order_quantities.size}") logger.debug "GroupOrderArticleQuantity records found: #{order_quantities.size}"
# Determine quantities to be ordered... # Determine quantities to be ordered...
total_quantity = i = 0 order_quantities.each do |goaq|
while (i < order_quantities.size && total_quantity < total) q = [goaq.quantity, total - total_quantity].min
q = (order_quantities[i].quantity <= total - total_quantity ? order_quantities[i].quantity : total - total_quantity)
total_quantity += q total_quantity += q
if (order_quantities[i].group_order_article_id == self.id) if goaq.group_order_article_id == self.id
logger.debug("increasing quantity by #{q}") logger.debug "increasing quantity by #{q}"
quantity += q quantity += q
end end
i += 1 break if total_quantity >= total
end end
# Determine tolerance to be ordered... # Determine tolerance to be ordered...
if (total_quantity < total) if total_quantity < total
logger.debug("determining additional items to be ordered from tolerance") logger.debug "determining additional items to be ordered from tolerance"
i = 0 order_quantities.each do |goaq|
while (i < order_quantities.size && total_quantity < total) q = [goaq.tolerance, total - total_quantity].min
q = (order_quantities[i].tolerance <= total - total_quantity ? order_quantities[i].tolerance : total - total_quantity)
total_quantity += q total_quantity += q
if (order_quantities[i].group_order_article_id == self.id) if goaq.group_order_article_id == self.id
logger.debug("increasing tolerance by #{q}") logger.debug "increasing tolerance by #{q}"
tolerance += q tolerance += q
end end
i += 1 break if total_quantity >= total
end end
end end
logger.debug("determined quantity/tolerance/total: #{quantity} / #{tolerance} / #{quantity + tolerance}") logger.debug "determined quantity/tolerance/total: #{quantity} / #{tolerance} / #{quantity + tolerance}"
end end
{:quantity => quantity, :tolerance => tolerance, :total => quantity + tolerance} {:quantity => quantity, :tolerance => tolerance, :total => quantity + tolerance}

View file

@ -7,20 +7,51 @@
.modal-body .modal-body
= f.input :availability = f.input :availability
= f.input :name = f.input :name
= f.input :origin .fold-line
= f.input :manufacturer = f.input :unit_quantity, label: Article.human_attribute_name(:unit),
= f.input :unit input_html: {class: 'input-mini', title: Article.human_attribute_name(:unit_quantity)}
= f.input :unit, label: '&times;'.html_safe,
input_html: {class: 'input-mini', title: Article.human_attribute_name(:unit)}
= f.input :note = f.input :note
= f.association :article_category = f.association :article_category
/ TODO labels
= f.input :price .fold-line
= f.input :unit_quantity = f.input :price do
.input-prepend
%span.add-on= t 'number.currency.format.unit'
= f.input_field :price, class: 'input-mini'
= f.input :tax do
.input-append
= f.input_field :tax, class: 'input-mini'
%span.add-on %
.fold-line
= f.input :deposit do
.input-prepend
%span.add-on= t 'number.currency.format.unit'
= f.input_field :deposit, class: 'input-mini'
.control-group
%label.control-label{for: 'article_fc_price'}
= Article.human_attribute_name(:fc_price)
.controls.control-text#article_fc_price
= number_to_currency(@article.fc_price) rescue nil
= f.input :origin
= f.input :manufacturer
= f.input :order_number = f.input :order_number
= f.input :tax, :wrapper => :append do
= f.input_field :tax
%span.add-on %
= f.input :deposit
.modal-footer .modal-footer
= link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'} = link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'}
= f.submit class: 'btn btn-primary' = f.submit class: 'btn btn-primary'
:javascript
var form = $('form.edit_article, form.new_article');
$('#article_price, #article_tax, #article_deposit', form).on('change keyup', function() {
var price = parseFloat($('#article_price', form).val());
var tax = parseFloat($('#article_tax', form).val());
var deposit = parseFloat($('#article_deposit', form).val());
// Article#gross_price and Article#fc_price
var gross_price = (price + deposit) * (tax / 100 + 1);
var fc_price = gross_price * (#{FoodsoftConfig[:price_markup].to_f} / 100 + 1);
$('#article_fc_price').html($.isNumeric(fc_price) ? I18n.l("currency", fc_price) : '&#133;');
});

View file

@ -42,9 +42,9 @@
} }
if('new' == selectedArticle.id) { if('new' == selectedArticle.id) {
$.ajax({ $.ajax({
url: '#{new_stock_article_supplier_deliveries_path(@supplier)}', url: '#{new_stock_article_path}',
type: 'get', type: 'get',
data: {stock_article: {name: selectedArticle.text}}, data: {stock_article: {name: selectedArticle.text, supplier_id: #{@supplier.id}}},
contentType: 'application/json; charset=UTF-8' contentType: 'application/json; charset=UTF-8'
}); });
$('#new_stock_article').select2('data', null); $('#new_stock_article').select2('data', null);
@ -52,7 +52,7 @@
} }
if('' != selectedArticle.id) { if('' != selectedArticle.id) {
$.ajax({ $.ajax({
url: '#{derive_stock_article_supplier_deliveries_path(@supplier)}', url: '#{derive_stock_articles_path}',
type: 'get', type: 'get',
data: {old_article_id: selectedArticle.id}, data: {old_article_id: selectedArticle.id},
contentType: 'application/json; charset=UTF-8' contentType: 'application/json; charset=UTF-8'
@ -61,6 +61,26 @@
return true; return true;
} }
}); });
// Subscribe to database changes.
// See publish/subscribe design pattern in /doc.
$(document).on('StockArticle#create', function(e) {
$.ajax({
url: '#{form_on_stock_article_create_supplier_deliveries_path(@supplier)}',
type: 'get',
data: {id: e.stock_article_id},
contentType: 'application/json; charset=UTF-8'
});
});
$(document).on('StockArticle#update', function(e) {
$.ajax({
url: '#{form_on_stock_article_update_supplier_deliveries_path(@supplier)}',
type: 'get',
data: {id: e.stock_article_id},
contentType: 'application/json; charset=UTF-8'
});
});
}); });
function mark_article_for_delivery(stock_article_id) { function mark_article_for_delivery(stock_article_id) {
@ -96,7 +116,7 @@
%tr %tr
%th{:colspan => 5} %th{:colspan => 5}
- if articles_for_select2(@supplier).empty? - if articles_for_select2(@supplier).empty?
= link_to t('.create_stock_article'), new_stock_article_supplier_deliveries_path(@supplier), :remote => true, :class => 'btn' = link_to t('.create_stock_article'), new_stock_article_path, :remote => true, :class => 'btn'
- else - else
%input#new_stock_article{:style => 'width: 500px;'} %input#new_stock_article{:style => 'width: 500px;'}
%tbody %tbody

View file

@ -1,11 +1,13 @@
- css_class = ( @delivery and @delivery.includes_article? article ) ? ( 'unavailable' ) : ( false ) - disable_delivery_action = ( @delivery and @delivery.includes_article? article )
- css_class = ( disable_delivery_action ) ? ( 'unavailable' ) : ( false )
- deliver_button_disabled = ( disable_delivery_action ) ? ( 'disabled' ) : ( false )
%tr{:id => "stock_article_#{article.id}", :class => css_class} %tr{:id => "stock_article_#{article.id}", :class => css_class}
%td= article.name %td= article.name
%td{:data => {:toggle => :tooltip, :title => render(:partial => 'shared/article_price_info', :locals => {:article => article})}}= number_to_currency article.price %td{:data => {:toggle => :tooltip, :title => render(:partial => 'shared/article_price_info', :locals => {:article => article})}}= number_to_currency article.price
%td= article.unit %td= article.unit
%td= article.article_category.name %td= article.article_category.name
%td %td
= link_to t('.action_edit'), edit_stock_article_supplier_deliveries_path(@supplier, :stock_article_id => article.id), remote: true, class: 'btn btn-mini' = link_to t('.action_edit'), edit_stock_article_path(article), remote: true, class: 'btn btn-mini'
= link_to t('.action_other_price'), copy_stock_article_supplier_deliveries_path(@supplier, :old_stock_article_id => article.id), remote: true, class: 'btn btn-mini' = link_to t('.action_other_price'), stock_article_copy_path(article), remote: true, class: 'btn btn-mini'
- deliver_button_disabled = ( @delivery and @delivery.includes_article? article ) ? ( 'disabled' ) : ( false )
= link_to t('.action_add_to_delivery'), add_stock_change_supplier_deliveries_path(@supplier, :stock_article_id => article.id), :method => :post, remote: true, class: 'button-add-stock-change btn btn-mini btn-primary', disabled: deliver_button_disabled = link_to t('.action_add_to_delivery'), add_stock_change_supplier_deliveries_path(@supplier, :stock_article_id => article.id), :method => :post, remote: true, class: 'button-add-stock-change btn btn-mini btn-primary', disabled: deliver_button_disabled

View file

@ -1,23 +0,0 @@
- url = ( stock_article.new_record? ) ? ( create_stock_article_supplier_deliveries_path(@supplier) ) : ( update_stock_article_supplier_deliveries_path(@supplier) )
= simple_form_for stock_article, url: url, remote: true, validate: true do |f|
= f.association :supplier, :as => :hidden
= f.hidden_field :id unless stock_article.new_record?
.modal-header
= link_to t('ui.marks.close').html_safe, '#', class: 'close', data: {dismiss: 'modal'}
%h3= t 'activerecord.models.stock_article'
.modal-body
= f.input :name
= f.input :unit
= f.input :note
- if stock_article.new_record?
= f.input :price
= f.input :tax, :wrapper => :append do
= f.input_field :tax
%span.add-on %
= f.input :deposit
- else
= f.input :price, :input_html => {:disabled => 'disabled'}, :hint => stock_article_price_hint(stock_article).html_safe
= f.association :article_category
.modal-footer
= link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'}
= f.submit :class => 'btn btn-primary', 'data-disable-with' => t('ui.please_wait')

View file

@ -1,5 +0,0 @@
$('#modalContainer').html(
'<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>'
);
$('#modalContainer').modal();

View file

@ -1,16 +0,0 @@
$('div.container-fluid').prepend(
'<%= j(render(:partial => 'shared/alert_success', :locals => {:alert_message => t('.notice', :name => @stock_article.name)})) %>'
);
(function() {
$('#stock_articles_for_adding tr').removeClass('success');
var stock_article_for_adding = $(
'<%= j(render(:partial => 'stock_article_for_adding', :locals => {:article => @stock_article})) %>'
).addClass('success');
$('#stock_articles_for_adding tbody').append(stock_article_for_adding);
updateSort('#stock_articles_for_adding');
})();
$('#modalContainer').modal('hide');

View file

@ -1,5 +0,0 @@
$('#modalContainer').html(
'<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>'
);
$('#modalContainer').modal();

View file

@ -1,5 +0,0 @@
$('#modalContainer').html(
'<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>'
);
$('#modalContainer').modal();

View file

@ -0,0 +1,31 @@
// Handle more advanced DOM update after AJAX database manipulation.
// See publish/subscribe design pattern in /doc.
(function(w) {
$('#stock_articles_for_adding tr').removeClass('success');
if(<%= @supplier.id != @stock_article.supplier.id %>) {
// the stock_article does _NOT_ belong to the current supplier
var try_again = w.confirm('<%= j(
t('deliveries.form.confirm_foreign_supplier_reedit', :name => @stock_article.name)
) %>');
if(try_again) {
$.ajax({
url: '<%= j edit_stock_article_path(@stock_article) %>',
type: 'get',
contentType: 'application/json; charset=UTF-8'
});
}
return false;
}
// the stock_article _DOES_ belong to the current supplier
var stock_article_for_adding = $(
'<%= j(render(:partial => 'stock_article_for_adding', :locals => {:article => @stock_article})) %>'
).addClass('success');
$('#stock_articles_for_adding tbody').append(stock_article_for_adding);
updateSort('#stock_articles_for_adding');
})(window);

View file

@ -0,0 +1,47 @@
// Handle more advanced DOM update after AJAX database manipulation.
// See publish/subscribe design pattern in /doc.
(function(w) {
// update entry in stock_article table
$('#stock_articles_for_adding tr').removeClass('success');
$('#stock_article_<%= @stock_article.id %>').remove();
if(<%= @supplier.id != @stock_article.supplier.id %>) {
// the stock_article does _NOT_ belong to the current supplier
var try_again = w.confirm('<%= j(
t('deliveries.form.confirm_foreign_supplier_reedit', :name => @stock_article.name)
) %>');
if(try_again) {
$.ajax({
url: '<%= j edit_stock_article_path(@stock_article) %>',
type: 'get',
contentType: 'application/json; charset=UTF-8'
});
}
}
else {
// the stock_article _DOES_ belong to the current supplier
var stock_article_for_adding = $(
'<%= j(render(:partial => 'stock_article_for_adding', :locals => {:article => @stock_article})) %>'
).addClass('success');
$('#stock_articles_for_adding tbody').append(stock_article_for_adding);
updateSort('#stock_articles_for_adding');
}
mark_article_for_delivery(<%= @stock_article.id %>);
// update entry in stock_changes table
$('#stock_changes tr').removeClass('success');
var stock_change_entry = $('#stock_change_stock_article_<%= @stock_article.id %>');
$('.stock_article_name', stock_change_entry).text('<%= j(@stock_article.name) %>');
$('.unit', stock_change_entry).text('<%= j(@stock_article.unit) %>');
stock_change_entry.addClass('success');
updateSort('#stock_changes');
})(window);

View file

@ -1,5 +0,0 @@
$('#modalContainer').html(
'<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>'
);
$('#modalContainer').modal();

View file

@ -1,32 +0,0 @@
$('div.container-fluid').prepend(
'<%= j(render(:partial => 'shared/alert_success', :locals => {:alert_message => t('.notice', :name => @stock_article.name)})) %>'
);
(function() {
// update entry in stock_article table
$('#stock_articles_for_adding tr').removeClass('success');
var stock_article_for_adding = $(
'<%= j(render(:partial => 'stock_article_for_adding', :locals => {:article => @stock_article, :delivery => @delivery})) %>'
).addClass('success');
$('#stock_article_<%= @stock_article.id %>').replaceWith(stock_article_for_adding);
updateSort('#stock_articles_for_adding');
mark_article_for_delivery(<%= @stock_article.id %>);
// update entry in stock_changes table
$('#stock_changes tr').removeClass('success');
var stock_change_entry = $('#stock_change_stock_article_<%= @stock_article.id %>');
$('.stock_article_name', stock_change_entry).text('<%= j(@stock_article.name) %>');
$('.unit', stock_change_entry).text('<%= j(@stock_article.unit) %>');
stock_change_entry.addClass('success');
updateSort('#stock_changes');
})();
$('#modalContainer').modal('hide');

View file

@ -1,18 +1,21 @@
= simple_form_for stock_article, :validate => true do |f| = simple_form_for stock_article, remote: true, :validate => true do |f|
= f.association :supplier .modal-header
= f.input :name = link_to t('ui.marks.close').html_safe, '#', class: 'close', data: {dismiss: 'modal'}
= f.input :unit %h3= title
= f.input :note .modal-body
= f.association :supplier
- if stock_article.new_record? = f.input :name
= f.input :price = f.input :unit
= f.input :tax, :wrapper => :append do = f.input :note
= f.input_field :tax - if stock_article.new_record?
%span.add-on % = f.input :price
= f.input :deposit = f.input :tax, :wrapper => :append do
- else = f.input_field :tax
= f.input :price, :input_html => {:disabled => 'disabled'}, :hint => t('.price_hint') %span.add-on %
= f.association :article_category = f.input :deposit
.form-actions - else
= f.submit class: 'btn' = f.input :price, :input_html => {:disabled => 'disabled'}, :hint => stock_article_price_hint(stock_article).html_safe
= link_to t('ui.or_cancel'), stock_articles_path = f.association :article_category
.modal-footer
= link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'}
= f.submit :class => 'btn btn-primary', 'data-disable-with' => t('ui.please_wait')

View file

@ -0,0 +1,14 @@
%tr{:class => stock_article_classes(stock_article), :id => "stockArticle-#{stock_article.id}"}
%td= link_to stock_article.name, stock_article
%td= stock_article.quantity
%td= stock_article.quantity_ordered
%td.main_info= stock_article.quantity_available
%td= stock_article.unit
%td= stock_article.price
%td= number_to_percentage stock_article.tax
%td= link_to stock_article.supplier.name, stock_article.supplier
%td= stock_article.article_category.name
%td
= link_to t('ui.edit'), edit_stock_article_path(stock_article), remote: true, class: 'btn btn-mini'
= link_to t('ui.delete'), stock_article, :method => :delete, :confirm => t('.confirm_delete', :name => stock_article.name),
class: 'btn btn-mini btn-danger', :remote => true

View file

@ -0,0 +1,26 @@
#stockArticleDetails
%dl.dl-horizontal
%dt= heading_helper(StockArticle, :supplier)
%dd= link_to stock_article.supplier.name, stock_article.supplier
%dt= heading_helper(StockArticle, :name)
%dd= stock_article.name
%dt= heading_helper(StockArticle, :unit)
%dd= stock_article.unit
%dt= heading_helper(StockArticle, :price)
%dd= number_to_currency stock_article.price
%dt= heading_helper(StockArticle, :tax)
%dd= number_to_percentage stock_article.tax
%dt= heading_helper(StockArticle, :deposit)
%dd= number_to_currency stock_article.deposit
%dt= heading_helper(StockArticle, :fc_price)
%dd= number_to_currency stock_article.fc_price
%dt= heading_helper(StockArticle, :article_category)
%dd= stock_article.article_category.name
%dt= heading_helper(StockArticle, :note)
%dd= stock_article.note
%dt= heading_helper(StockArticle, :quantity)
%dd= stock_article.quantity
%dt= heading_helper(StockArticle, :quantity_available)
%dd= stock_article.quantity_available
.form-actions
= link_to t('ui.edit'), edit_stock_article_path(stock_article), remote: true, class: 'btn'

View file

@ -0,0 +1,9 @@
$('#modalContainer').html('<%= j(render(
:partial => "form",
:locals => {
:title => t('.title'),
:stock_article => @stock_article
}
)) %>');
$('#modalContainer').modal();

View file

@ -0,0 +1,15 @@
$('div.container-fluid').prepend('<%= j(render(
:partial => 'shared/alert_success',
:locals => {
:alert_message => t('.notice', :name => @stock_article.name)
}
)) %>');
// Publish database changes.
// See publish/subscribe design pattern in /doc.
$(document).trigger({
type: 'StockArticle#create',
stock_article_id: <%= @stock_article.id %>
});
$('#modalContainer').modal('hide');

View file

@ -0,0 +1,9 @@
$('#modalContainer').html('<%= j(render(
:partial => "form",
:locals => {
:title => t('.title'),
:stock_article => @stock_article
}
)) %>');
$('#modalContainer').modal();

View file

@ -0,0 +1,13 @@
$('div.container-fluid').prepend('<%= j(render(
:partial => 'shared/alert_success',
:locals => {
:alert_message => t('.notice', :name => @stock_article.name)
}
)) %>');
// Publish database changes.
// See publish/subscribe design pattern in /doc.
$(document).trigger({
type: 'StockArticle#destroy',
stock_article_id: <%= @stock_article.id %>
});

View file

@ -1,8 +0,0 @@
-# please polish the following line if you know how, same in partial _destroy_fail
var successDiv = $('<div class="alert fade in alert-success"><a class="close" data-dismiss="alert" href="#">#{escape_javascript(t('ui.marks.close').html_safe)}</a></div>');
successDiv.append(document.createTextNode('#{escape_javascript(t('.notice', name: @article.name))}'));
$('div.container-fluid').prepend(successDiv);
$('#stockArticle-#{@article.id}').remove();
-# WARNING: Do not use a simple .fadeOut() above, because it conflicts with the show/hide function of unavailable articles.

View file

@ -1,3 +0,0 @@
- title t('.title')
= render :partial => 'form', :locals => {:stock_article => @stock_article}

View file

@ -0,0 +1,9 @@
$('#modalContainer').html('<%= j(render(
:partial => "form",
:locals => {
:title => t('.title'),
:stock_article => @stock_article
}
)) %>');
$('#modalContainer').modal();

View file

@ -3,6 +3,30 @@
:javascript :javascript
$(function() { $(function() {
$('tr.unavailable').hide(); $('tr.unavailable').hide();
// Subscribe to database changes.
// See publish/subscribe design pattern in /doc.
$(document).on('StockArticle#create', function(e) {
$.ajax({
url: '#{index_on_stock_article_create_stock_articles_path}',
type: 'get',
data: {id: e.stock_article_id},
contentType: 'application/json; charset=UTF-8'
});
});
$(document).on('StockArticle#destroy', function(e) {
$('#stockArticle-' + e.stock_article_id).remove();
});
$(document).on('StockArticle#update', function(e) {
$.ajax({
url: '#{index_on_stock_article_update_stock_articles_path}',
type: 'get',
data: {id: e.stock_article_id},
contentType: 'application/json; charset=UTF-8'
});
});
}) })
.well.well-small .well.well-small
@ -17,7 +41,7 @@
.btn-group .btn-group
= link_to_if @current_user.role_orders?, t('.order_online'), new_order_path(supplier_id: 0), = link_to_if @current_user.role_orders?, t('.order_online'), new_order_path(supplier_id: 0),
class: 'btn', class: 'btn btn-primary' class: 'btn', class: 'btn btn-primary'
= link_to t('.new_stock_article'), new_stock_article_path, class: 'btn' = link_to t('.new_stock_article'), new_stock_article_path, remote: true, class: 'btn'
= link_to t('.new_stock_taking'), new_stock_taking_path, class: 'btn' = link_to t('.new_stock_taking'), new_stock_taking_path, class: 'btn'
= link_to t('.show_stock_takings'), stock_takings_path, class: 'btn' = link_to t('.show_stock_takings'), stock_takings_path, class: 'btn'
@ -42,22 +66,9 @@
%th= heading_helper StockArticle, :supplier %th= heading_helper StockArticle, :supplier
%th= heading_helper StockArticle, :article_category %th= heading_helper StockArticle, :article_category
%th %th
%tbody %tbody#articles-tbody
- for article in @stock_articles - for stock_article in @stock_articles
%tr{:class => stock_article_classes(article), :id => "stockArticle-#{article.id}"} = render :partial => 'stock_article', :locals => {:stock_article => stock_article}
%td= link_to article.name, article
%td= article.quantity
%td= article.quantity_ordered
%th= article.quantity_available
%td= article.unit
%td= article.price
%td= number_to_percentage article.tax
%td= link_to article.supplier.name, article.supplier
%td= article.article_category.name
%td
= link_to t('ui.edit'), edit_stock_article_path(article), class: 'btn btn-mini'
= link_to t('ui.delete'), article, :method => :delete, :confirm => t('.confirm_delete'),
class: 'btn btn-mini btn-danger', :remote => true
%p %p
= t '.stock_worth' = t '.stock_worth'
= number_to_currency StockArticle.stock_value = number_to_currency StockArticle.stock_value

View file

@ -0,0 +1,14 @@
// Handle more advanced DOM update after AJAX database manipulation.
// See publish/subscribe design pattern in /doc.
(function() {
$('#articles-tbody tr').removeClass('success');
var stock_article_row = $('<%= j(render(
:partial => 'stock_article',
:locals => {
:stock_article => @stock_article
}
)) %>').addClass('success');
$('#articles-tbody').prepend(stock_article_row);
})();

View file

@ -0,0 +1,14 @@
// Handle more advanced DOM update after AJAX database manipulation.
// See publish/subscribe design pattern in /doc.
(function() {
$('#articles-tbody tr').removeClass('success');
var stock_article_row = $('<%= j(render(
:partial => 'stock_article',
:locals => {
:stock_article => @stock_article
}
)) %>').addClass('success');
$('#stockArticle-<%= @stock_article.id %>').replaceWith(stock_article_row);
})();

View file

@ -1,22 +0,0 @@
- title t('.title')
- content_for :head do
:javascript
$(function() {
$('#article_search').autocomplete({
source: '#{articles_search_stock_articles_path}',
select: function(e, ui) {
alert(ui.item.value);
//location.href = '#{nil}' + ui.item.value;
}
});
})
/
TODO: Fix this
%p
= t '.search_text'
= text_field_tag 'article_search'
#stock_article_form
= render :partial => 'form', :locals => {:stock_article => @stock_article}

View file

@ -0,0 +1,9 @@
$('#modalContainer').html('<%= j(render(
:partial => "form",
:locals => {
:title => t('.title'),
:stock_article => @stock_article
}
)) %>');
$('#modalContainer').modal();

View file

@ -1,32 +1,22 @@
- title @stock_article.name - title @stock_article.name
- content_for :javascript do
:javascript
$(function() {
// Subscribe to database changes.
// See publish/subscribe design pattern in /doc.
$(document).on('StockArticle#update', function(e) {
$.ajax({
url: '#{show_on_stock_article_update_stock_articles_path}',
type: 'get',
data: {id: e.stock_article_id},
contentType: 'application/json; charset=UTF-8'
});
});
});
.row-fluid .row-fluid
.span6 .span6
%dl.dl-horizontal = render :partial => 'stock_article_details', :locals => {:stock_article => @stock_article}
%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 .span6
%h2= t('.stock_changes') %h2= t('.stock_changes')

View file

@ -0,0 +1,13 @@
// Handle more advanced DOM update after AJAX database manipulation.
// See publish/subscribe design pattern in /doc.
(function() {
var stock_article_details = $('<%= j(render(
:partial => 'stock_article_details',
:locals => {
:stock_article => @stock_article
}
)) %>');
$('#stockArticleDetails').replaceWith(stock_article_details);
$('h1').first().text('<%= j(@stock_article.name) %>');
})();

View file

@ -0,0 +1,15 @@
$('div.container-fluid').prepend('<%= j(render(
:partial => 'shared/alert_success',
:locals => {
:alert_message => t('.notice', :name => @stock_article.name)
}
)) %>');
// Publish database changes.
// See publish/subscribe design pattern in /doc.
$(document).trigger({
type: 'StockArticle#update',
stock_article_id: <%= @stock_article.id %>
});
$('#modalContainer').modal('hide');

View file

@ -36,7 +36,7 @@ de:
created_on: Datum created_on: Datum
note: Notiz note: Notiz
ordergroup: Bestellgruppe ordergroup: Bestellgruppe
user: user: Eingetragen von
group_order: group_order:
price: Bestellsumme price: Bestellsumme
updated_by: Zuletzt bestellt updated_by: Zuletzt bestellt
@ -71,10 +71,10 @@ de:
name: Lieferant name: Lieferant
note: Notiz note: Notiz
starts: Läuft vom starts: Läuft vom
status: status: Status
order_article: order_article:
missing_units: Fehlende Einheiten missing_units: Fehlende Einheiten
missing_units_short: missing_units_short: Fehlende
units_to_order: Menge units_to_order: Menge
update_current_price: Globalen Preis aktualisieren update_current_price: Globalen Preis aktualisieren
order_comment: order_comment:
@ -127,7 +127,7 @@ de:
done: Erledigt? done: Erledigt?
due_date: Wann erledigen? due_date: Wann erledigen?
duration: Dauer duration: Dauer
name: name: Aufgabe
required_users: Anzahl required_users: Anzahl
user_list: Verantwortlichen user_list: Verantwortlichen
workgroup: Arbeitsgruppe workgroup: Arbeitsgruppe
@ -269,7 +269,7 @@ de:
application: application:
controller: controller:
error_authn: Anmeldung erforderlich! error_authn: Anmeldung erforderlich!
error_denied: error_denied: Kein Zugriff!
error_members_only: Diese Aktion ist nur für Mitglieder der Gruppe erlaubt! error_members_only: Diese Aktion ist nur für Mitglieder der Gruppe erlaubt!
article_categories: article_categories:
create: create:
@ -385,13 +385,12 @@ de:
how_many_units: Wie viele Einheiten (%{unit}) des Artikels »%{name}« liefern? how_many_units: Wie viele Einheiten (%{unit}) des Artikels »%{name}« liefern?
create: create:
notice: Lieferung wurde erstellt. Bitte nicht vergessen die Rechnung anzulegen! notice: Lieferung wurde erstellt. Bitte nicht vergessen die Rechnung anzulegen!
create_stock_article:
notice: Neuer Lagerartikel »%{name}« gespeichert.
destroy: destroy:
notice: Lieferung wurde gelöscht. notice: Lieferung wurde gelöscht.
edit: edit:
title: Lieferung bearbeiten title: Lieferung bearbeiten
form: form:
confirm_foreign_supplier_reedit: Der Lagerartikel »%{name}« wurde erfolgreich gespeichert. Er gehört jedoch nicht zu dem Lieferanten dieser Lieferung. Möchtest Du diesen Lagerartikel erneut bearbeiten?
create_from_blank: Ohne Vorlage anlegen create_from_blank: Ohne Vorlage anlegen
create_stock_article: Lagerartikel anlegen create_stock_article: Lagerartikel anlegen
title_fill_quantities: 2. Liefermenge angeben title_fill_quantities: 2. Liefermenge angeben
@ -416,15 +415,11 @@ de:
action_add_to_delivery: Liefern action_add_to_delivery: Liefern
action_edit: Bearbeiten action_edit: Bearbeiten
action_other_price: Kopieren action_other_price: Kopieren
stock_article_form:
copy_stock_article: Lagerartikel kopieren
stock_change_fields: stock_change_fields:
remove_article: Artikel aus Lieferung entfernen remove_article: Artikel aus Lieferung entfernen
suppliers_overview: Lieferantenübersicht suppliers_overview: Lieferantenübersicht
update: update:
notice: Lieferung wurde aktualisiert. notice: Lieferung wurde aktualisiert.
update_stock_article:
notice: Lagerartikel »%{name}« aktualisiert.
documents: documents:
order_by_articles: order_by_articles:
filename: Bestellung %{name}-%{date} - Artikelsortierung filename: Bestellung %{name}-%{date} - Artikelsortierung
@ -708,7 +703,7 @@ de:
ordered: Bestellt ordered: Bestellt
ordered_title: Menge + Toleranz ordered_title: Menge + Toleranz
show_hide: Zeige/Verstecke nicht bestellte Artikel show_hide: Zeige/Verstecke nicht bestellte Artikel
show_note: show_note: Notiz zeigen
title: Artikelübersicht title: Artikelübersicht
unit_price: Einzelpreis unit_price: Einzelpreis
comment: Kommentare lesen/schreiben comment: Kommentare lesen/schreiben
@ -1243,9 +1238,9 @@ de:
shared: shared:
articles: articles:
ordered: Bestellt ordered: Bestellt
ordered_desc: ordered_desc: Anzahl der Artikel die durch das Mitglied bestellt wurden (Menge + Toleranz)
received: Bekommen received: Bekommen
received_desc: received_desc: Anzahl der Artikel die ein Mitglied erhält
articles_by_articles: articles_by_articles:
ordergroup: Bestellgruppe ordergroup: Bestellgruppe
price: Gesamtpreis price: Gesamtpreis
@ -1287,7 +1282,7 @@ de:
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! 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:
name: Bitte ändern name: Bitte ändern
edit_stock_article: edit_stock_article:
price: <ul><li>Preisänderung gesperrt.</li><li>Bei Bedarf %{stock_article_copy_link}.</li></ul> price: <ul><li>Preisänderung gesperrt.</li><li>Bei Bedarf %{stock_article_copy_link}.</li></ul>
@ -1361,11 +1356,16 @@ de:
stockit: stockit:
check: check:
not_empty: ! '%{name} kann nicht gelöscht werden. Der Lagerbestand ist nicht null.' not_empty: ! '%{name} kann nicht gelöscht werden. Der Lagerbestand ist nicht null.'
copy:
title: Lagerartikel kopieren
create:
notice: Neuer Lagerartikel »%{name}« gespeichert.
destroy: destroy:
notice: Artikel %{name} gelöscht. notice: Artikel %{name} gelöscht.
edit: edit:
title: Lagerartikel bearbeiten title: Lagerartikel bearbeiten
form: form:
copy_stock_article: Lagerartikel kopieren
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.
index: index:
confirm_delete: Bist Du sicher? confirm_delete: Bist Du sicher?
@ -1388,10 +1388,8 @@ de:
new_quantity: Neuer Bestand new_quantity: Neuer Bestand
reason: Ereignis reason: Ereignis
stock_changes: Verlauf des Lagerbestands stock_changes: Verlauf des Lagerbestands
stock_create: update:
notice: Lagerartikel wurde gespeichert. notice: Lagerartikel »%{name}« aktualisiert.
stock_update:
notice: Lagerartikel wurde gespeichert.
suppliers: suppliers:
create: create:
notice: Lieferant wurde erstellt notice: Lieferant wurde erstellt

View file

@ -387,13 +387,12 @@ en:
how_many_units: ! 'How many units (%{unit}) to deliver? Stock article name: %{name}.' how_many_units: ! 'How many units (%{unit}) to deliver? Stock article name: %{name}.'
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:
notice: The new stock article "%{name}" was saved.
destroy: destroy:
notice: Delivery was deleted. notice: Delivery was deleted.
edit: edit:
title: Edit delivery title: Edit delivery
form: form:
confirm_foreign_supplier_reedit: The stock article %{name} was successfully saved. However, it belongs to a different supplier than this delivery. Would you like to edit the stock article again?
create_from_blank: Create new article create_from_blank: Create new article
create_stock_article: Create stock article create_stock_article: Create stock article
title_fill_quantities: 2. Set delivery quantities title_fill_quantities: 2. Set delivery quantities
@ -418,15 +417,11 @@ en:
action_add_to_delivery: Add to delivery action_add_to_delivery: Add to delivery
action_edit: Edit action_edit: Edit
action_other_price: Copy action_other_price: Copy
stock_article_form:
copy_stock_article: Copy stock article
stock_change_fields: stock_change_fields:
remove_article: Remove article from delivery remove_article: Remove article from delivery
suppliers_overview: Supplier overview suppliers_overview: Supplier overview
update: update:
notice: Delivery was updated. notice: Delivery was updated.
update_stock_article:
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
@ -1284,14 +1279,14 @@ en:
default_message: Errors were found. Please check the form. default_message: Errors were found. Please check the form.
hints: hints:
article: article:
unit: ! 'For example: KG or 1L or 500g' unit: e.g. KG or 1L or 500g
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: 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! 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:
name: Please modify name: Please modify
edit_stock_article: edit_stock_article:
price: <ul><li>Price changes are forbidden.</li><li>If necessary, %{stock_article_copy_link}.</li></ul> price: <ul><li>Price changes are forbidden.</li><li>If necessary, %{stock_article_copy_link}.</li></ul>
@ -1365,11 +1360,16 @@ en:
stockit: stockit:
check: check:
not_empty: ! '%{name} could not be deleted, the inventory is not zero.' not_empty: ! '%{name} could not be deleted, the inventory is not zero.'
copy:
title: Copy stock article
create:
notice: New stock article %{name} was created.
destroy: destroy:
notice: Article %{name} was deleted. notice: Article %{name} was deleted.
edit: edit:
title: Edit stock articles title: Edit stock articles
form: form:
copy_stock_article: copy stock article
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.
index: index:
confirm_delete: Are you sure you want to delete? confirm_delete: Are you sure you want to delete?
@ -1392,10 +1392,10 @@ en:
new_quantity: New quantity new_quantity: New quantity
reason: Reason reason: Reason
stock_changes: Stock quantity changes stock_changes: Stock quantity changes
stock_create: stock_article:
notice: Stock article was created. confirm_delete: Are you sure you want to delete the stock article %{name}?
stock_update: update:
notice: Stock article was saved. notice: Stock article %{name} was saved.
suppliers: suppliers:
create: create:
notice: Supplier was created notice: Supplier was created

View file

@ -391,8 +391,6 @@ fr:
how_many_units: Combien d unités (%{unit}) de l article %{name} doivent-elles être livrées? how_many_units: Combien d unités (%{unit}) de l article %{name} doivent-elles être livrées?
create: create:
notice: Le réapprovisionnement a bien a été défini. Attention à ne pas oublier de déposer la facture correspondante! notice: Le réapprovisionnement a bien a été défini. Attention à ne pas oublier de déposer la facture correspondante!
create_stock_article:
notice: L'article "%{name}" a été ajouté au stock.
destroy: destroy:
notice: Le réapprovisionnement a été annulé. notice: Le réapprovisionnement a été annulé.
edit: edit:
@ -422,15 +420,11 @@ fr:
action_add_to_delivery: Commander action_add_to_delivery: Commander
action_edit: Modifier action_edit: Modifier
action_other_price: Copier action_other_price: Copier
stock_article_form:
copy_stock_article: Copier l'article
stock_change_fields: stock_change_fields:
remove_article: Retirer l'article de cette commande remove_article: Retirer l'article de cette commande
suppliers_overview: Liste des fournisseusEs_rs suppliers_overview: Liste des fournisseusEs_rs
update: update:
notice: La commande a été actualisée notice: La commande a été actualisée
update_stock_article:
notice: Les données de l'article "%{name}" ont été mises à jour.
documents: documents:
order_by_articles: order_by_articles:
filename: Commande %{name}-%{date} - Trier par filename: Commande %{name}-%{date} - Trier par
@ -1294,7 +1288,7 @@ fr:
units_to_order: 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:
name: Merci de modifier name: Merci de modifier
edit_stock_article: edit_stock_article:
price: <ul><li>Modification du prix enregistrée. </li><li>Si nécessaire %{stock_article_copy_link}.</li></ul> price: <ul><li>Modification du prix enregistrée. </li><li>Si nécessaire %{stock_article_copy_link}.</li></ul>
@ -1370,11 +1364,16 @@ fr:
stockit: stockit:
check: check:
not_empty: ! '%{name} ne peut pas être supprimé, car il y en a encore en stock.' not_empty: ! '%{name} ne peut pas être supprimé, car il y en a encore en stock.'
copy:
title:
create:
notice: L'article "%{name}" a été ajouté au stock.
destroy: destroy:
notice: L'article %{name} a bien été supprimé du stock. notice: L'article %{name} a bien été supprimé du stock.
edit: edit:
title: Modifier l'article title: Modifier l'article
form: form:
copy_stock_article: Copier l'article
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.
index: index:
confirm_delete: T'es sûrE de ton coup? confirm_delete: T'es sûrE de ton coup?
@ -1397,10 +1396,8 @@ fr:
new_quantity: Nouveau stock new_quantity: Nouveau stock
reason: Raison reason: Raison
stock_changes: Afficher l'historique stock_changes: Afficher l'historique
stock_create: update:
notice: L'article a été sauvegardé. notice: Les données de l'article "%{name}" ont été mises à jour.
stock_update:
notice: L'article a été sauvegardé.
suppliers: suppliers:
create: create:
notice: FournisseusE_r misE à jour notice: FournisseusE_r misE à jour

View file

@ -387,8 +387,6 @@ nl:
how_many_units: ! 'Hoeveel eenheden (%{unit}) leveren? Voorraadartikel: %{name}.' how_many_units: ! 'Hoeveel eenheden (%{unit}) leveren? Voorraadartikel: %{name}.'
create: create:
notice: Levering is aangemaakt. Vergeet niet een factuur te maken! notice: Levering is aangemaakt. Vergeet niet een factuur te maken!
create_stock_article:
notice: Nieuw voorraadsartikel "%{name}" gemaakt.
destroy: destroy:
notice: Levering is verwijdered. notice: Levering is verwijdered.
edit: edit:
@ -418,15 +416,11 @@ nl:
action_add_to_delivery: Voeg toe aan levering action_add_to_delivery: Voeg toe aan levering
action_edit: Bewerk action_edit: Bewerk
action_other_price: Kopieer action_other_price: Kopieer
stock_article_form:
copy_stock_article: kopieer het voorraadartikel
stock_change_fields: stock_change_fields:
remove_article: Artikel uit levering halen remove_article: Artikel uit levering halen
suppliers_overview: Leverancieroverzicht suppliers_overview: Leverancieroverzicht
update: update:
notice: Levering is bijgewerkt. notice: Levering is bijgewerkt.
update_stock_article:
notice: Voorraadartikel "%{name}" is bijgewerkt.
documents: documents:
order_by_articles: order_by_articles:
filename: Bestelling %{name}-%{date} - Artikellijst filename: Bestelling %{name}-%{date} - Artikellijst
@ -1271,7 +1265,7 @@ nl:
units_to_order: Als je het aantal geleverde eenheden wijzigt, moet je daarna de hoeveelheden voor huishoudens aanpassen. Klik daarvoor op de artikelnaam. Als je dit vergeet, kunnen huishoudens belast worden voor artikelen die ze niet hebben gekregen! units_to_order: Als je het aantal geleverde eenheden wijzigt, moet je daarna de hoeveelheden voor huishoudens aanpassen. Klik daarvoor op de artikelnaam. Als je dit vergeet, kunnen huishoudens belast worden voor artikelen die ze niet hebben gekregen!
update_current_price: Ook prijs in huidige besteling aanpassen update_current_price: Ook prijs in huidige besteling aanpassen
stock_article: stock_article:
copy_stock_article: copy:
name: Wijzigen alsjeblieft name: Wijzigen alsjeblieft
edit_stock_article: edit_stock_article:
price: <ul><li>De prijs mag niet aangepast worden.</li><li>Indien nodig, %{stock_article_copy_link}.</li></ul> price: <ul><li>De prijs mag niet aangepast worden.</li><li>Indien nodig, %{stock_article_copy_link}.</li></ul>
@ -1345,11 +1339,16 @@ nl:
stockit: stockit:
check: check:
not_empty: ! '%{name} kon niet worden verwijderd, de inventaris is niet leeg.' not_empty: ! '%{name} kon niet worden verwijderd, de inventaris is niet leeg.'
copy:
title:
create:
notice: Nieuw voorraadsartikel "%{name}" gemaakt.
destroy: destroy:
notice: Artikel %{name} is verwijdered. notice: Artikel %{name} is verwijdered.
edit: edit:
title: Voorraadartikelen bewerken title: Voorraadartikelen bewerken
form: form:
copy_stock_article: kopieer het voorraadartikel
price_hint: Om chaos te voorkomen, kun je de prijs van bestaande voorraadartikelen niet aanpassen. price_hint: Om chaos te voorkomen, kun je de prijs van bestaande voorraadartikelen niet aanpassen.
index: index:
confirm_delete: Weet je zeker dat je dit wilt verwijderen? confirm_delete: Weet je zeker dat je dit wilt verwijderen?
@ -1372,10 +1371,8 @@ nl:
new_quantity: Nieuw aantal new_quantity: Nieuw aantal
reason: Reden reason: Reden
stock_changes: Verloop stock_changes: Verloop
stock_create: update:
notice: Voorraadsartikel is opgeslagen. notice: Voorraadartikel "%{name}" is bijgewerkt.
stock_update:
notice: Voorraadsartikel is bijgewerkt.
suppliers: suppliers:
create: create:
notice: Leverancier is aangemaakt. notice: Leverancier is aangemaakt.

View file

@ -86,9 +86,14 @@ Foodsoft::Application.routes.draw do
end end
resources :stock_articles, :to => 'stockit' do resources :stock_articles, :to => 'stockit' do
get :copy
collection do collection do
get :articles_search get :derive
get :fill_new_stock_article_form
get :index_on_stock_article_create
get :index_on_stock_article_update
get :show_on_stock_article_update
end end
end end
@ -96,15 +101,12 @@ Foodsoft::Application.routes.draw do
get :shared_suppliers, :on => :collection get :shared_suppliers, :on => :collection
resources :deliveries do resources :deliveries do
post :add_stock_change, :on => :collection collection do
post :add_stock_change
get :new_stock_article, :on => :collection
get :copy_stock_article, :on => :collection get :form_on_stock_article_create
get :derive_stock_article, :on => :collection get :form_on_stock_article_update
post :create_stock_article, :on => :collection end
get :edit_stock_article, :on => :collection
put :update_stock_article, :on => :collection
end end
resources :articles do resources :articles do

View file

@ -17,6 +17,9 @@ Getting started
``` ```
git clone https://github.com/foodcoops/foodsoft.git git clone https://github.com/foodcoops/foodsoft.git
``` ```
This brings up the bleeding-edge development version, which might contain some
unfinished parts. If you want to be safe, choose the last release:
`git checkout $(git tag -l | grep ^v | sort -rn | head -n1)`
1. Install RVM and Ruby 1.9.3 (if you have not done so before): 1. Install RVM and Ruby 1.9.3 (if you have not done so before):
``` ```

View file

@ -0,0 +1,40 @@
# Publish/subscribe pattern
## Handling DOM updates after AJAX database manipulation
As an example, let us consider the manipulation (create, update...) of `StockArticles`. This can be done in different views, e.g., `stock_articles/index`, `stock_articles/show` and `deliveries/_form` through modals using AJAX requests. As an advantage of the AJAX technique, the user does not need to reload the entire page. However, (after the update of the `StockArticle` in the database) it is generally required to update the DOM in the current view such that the page properly reacts to the asynchronous actions.
The process can be divided in two steps: **1.** AJAX database manipulation and **2.** DOM updates for the particular view. The crucial point is the coupling of the two steps since the controller for the first step offers the same functionality to all views and does not need to know anything about the current view.
### 1. AJAX database manipulation
**(i)** Example: current view `deliveries/_form` offers a link for the AJAX action `StockArticle#new`. This opens a modal filled with `stock_articles/_form`.
**(ii)** AJAX form post addresses the `StockArticle#create` action which handles the database manipulation.
**(iii)** The database manipulation is finished by the rendering of, e.g., `stock_articles/create.js.erb`. The key task there is to **publish** the database changes by calling `trigger`, i.e.,
$(document).trigger({
type: 'StockArticle#create',
stock_article_id: <%= @stock_article.id %>
});
### 2. DOM updates for the particular view
**(i)** Each view has the opportunity to **subscribe** to particular events of the previous step. A very simple example is the update of the `stock_articles/index` view after `StockArticle#destroy`:
$(document).on('StockArticle#destroy', function(e) {
$('#stockArticle-' + e.stock_article_id).remove();
});
However, in most of the situations you will like to use the full power of the MVC framework in order to read new data from the database and render some partial. Let us consider this slightly more advanced case in the following.
The view `stock_articles/index` could listen (amongst others) to `StockArticle#create` like this:
$(document).on('StockArticle#create', function(e) {
$.ajax({
url: '#{index_on_stock_article_create_stock_articles_path}',
type: 'get',
data: {id: e.stock_article_id},
contentType: 'application/json; charset=UTF-8'
});
});
**(ii)** The action `StockArticles#index_on_stock_article_create` is a special helper action to handle DOM updates of the `stock_articles/index` view after the creation of a new `StockArticle` with the given `id`.

View file

@ -1,14 +1,26 @@
# optional test coverage # optional test coverage
# needs to be loaded first, e.g. add a require at top of spec_helper # needs to be loaded first, e.g. add a require at top of spec_helper
if ENV['COVERAGE'] if ENV['COVERAGE'] or ENV['COVERALLS']
require 'simplecov' require 'simplecov'
# update coveralls.io on Travis CI
if ENV['COVERALLS']
require 'coveralls'
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
end
# slightly tweaked coverage reporting
def cov_no_plugins(source_file, path)
source_file.filename =~ /#{path}/ and not source_file.filename =~ /\/lib\/foodsoft_.*\//
end
SimpleCov.start do SimpleCov.start do
add_filter '/spec/' add_filter '/spec/'
add_filter '/test/' add_filter '/test/'
add_group 'Models', '/app/models/' add_group 'Models' do |s| cov_no_plugins s, '/app/models/' end
add_group 'Controllers', '/app/controllers/' add_group 'Controllers' do |s| cov_no_plugins s, '/app/controllers/' end
add_group 'Helpers', '/app/helpers/' add_group 'Helpers' do |s| cov_no_plugins s, '/app/helpers/' end
add_group 'Documents', '/app/documents/' add_group 'Documents' do |s| cov_no_plugins s, '/app/documents/' end
add_group 'Libraries', '/lib/' add_group 'Libraries' do |s| cov_no_plugins s, '/lib/' end
add_group 'Plugins' do |s| s.filename =~ /\/lib\/foodsoft_.*\// end
end end
end end