Merge pull request #255 from wvengen/feature-orders_group_edit
Edit order_group_article result in orders screen
This commit is contained in:
commit
0128f72103
38 changed files with 421 additions and 225 deletions
1
Gemfile
1
Gemfile
|
@ -86,6 +86,7 @@ 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 'connection_pool'
|
||||||
# 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'
|
||||||
|
|
|
@ -114,6 +114,7 @@ GEM
|
||||||
execjs
|
execjs
|
||||||
coffee-script-source (1.6.3)
|
coffee-script-source (1.6.3)
|
||||||
commonjs (0.2.7)
|
commonjs (0.2.7)
|
||||||
|
connection_pool (1.2.0)
|
||||||
content_for_in_controllers (0.0.2)
|
content_for_in_controllers (0.0.2)
|
||||||
coveralls (0.7.0)
|
coveralls (0.7.0)
|
||||||
multi_json (~> 1.3)
|
multi_json (~> 1.3)
|
||||||
|
@ -387,6 +388,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)
|
||||||
|
connection_pool
|
||||||
coveralls
|
coveralls
|
||||||
daemons
|
daemons
|
||||||
database_cleaner
|
database_cleaner
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
//= 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 bootstrap-datepicker/locales/bootstrap-datepicker.fr
|
//= require bootstrap-datepicker/locales/bootstrap-datepicker.fr
|
||||||
//= require jquery.observe_field
|
|
||||||
//= require list
|
//= require list
|
||||||
//= require list.unlist
|
//= require list.unlist
|
||||||
//= require list.delay
|
//= require list.delay
|
||||||
|
@ -20,6 +19,7 @@
|
||||||
//= require ordering
|
//= require ordering
|
||||||
//= require stupidtable
|
//= require stupidtable
|
||||||
//= require touchclick
|
//= require touchclick
|
||||||
|
//= require delta_input
|
||||||
|
|
||||||
// Load following statements, when DOM is ready
|
// Load following statements, when DOM is ready
|
||||||
$(function() {
|
$(function() {
|
||||||
|
@ -69,17 +69,32 @@ $(function() {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Submit form when changing text of an input field
|
// Submit form when clicking on checkbox
|
||||||
// Use jquery observe_field plugin
|
$(document).on('click', 'form[data-submit-onchange] input[type=checkbox]:not(input[data-ignore-onchange])', function() {
|
||||||
$('form[data-submit-onchange] input[type=text]').each(function() {
|
$(this).parents('form').submit();
|
||||||
$(this).observe_field(1, function() {
|
|
||||||
$(this).parents('form').submit();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Submit form when clicking on checkbox
|
// Submit form when changing text of an input field.
|
||||||
$('form[data-submit-onchange] input[type=checkbox]:not(input[data-ignore-onchange])').click(function() {
|
// Wubmission will be done after 500ms of not typed, unless data-submit-onchange=changed,
|
||||||
$(this).parents('form').submit();
|
// in which case it happens when the input box loses its focus ('changed' event).
|
||||||
|
$(document).on('changed keyup focusin', 'form[data-submit-onchange] input[type=text]', function(e) {
|
||||||
|
var input = $(this);
|
||||||
|
// when form has data-submit-onchange=changed, don't do updates while typing
|
||||||
|
if (e.type!='changed' && input.parents('form[data-submit-onchange=changed]')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// remember old value when it's getting the focus
|
||||||
|
if (e.type=='focusin') {
|
||||||
|
input.data('old-value', input.val());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// trigger timeout to submit form when value was changed
|
||||||
|
clearTimeout(input.data('submit-timeout-id'));
|
||||||
|
input.data('submit-timeout-id', setTimeout(function() {
|
||||||
|
if (input.val() != input.data('old-value')) input.parents('form').submit();
|
||||||
|
input.removeData('submit-timeout-id');
|
||||||
|
input.removeData('old-value');
|
||||||
|
}, 500));
|
||||||
});
|
});
|
||||||
|
|
||||||
$('[data-redirect-to]').bind('change', function() {
|
$('[data-redirect-to]').bind('change', function() {
|
||||||
|
|
49
app/assets/javascripts/delta_input.js
Normal file
49
app/assets/javascripts/delta_input.js
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$(document).on('click', 'button[data-increment]', function() {
|
||||||
|
data_delta_update($('#'+$(this).data('increment')), +1);
|
||||||
|
});
|
||||||
|
$(document).on('click', 'button[data-decrement]', function() {
|
||||||
|
data_delta_update($('#'+$(this).data('decrement')), -1);
|
||||||
|
});
|
||||||
|
$(document).on('change keyup', 'input[type="text"][data-delta]', function() {
|
||||||
|
data_delta_update(this, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function data_delta_update(el, direction) {
|
||||||
|
var id = $(el).attr('id');
|
||||||
|
|
||||||
|
var min = $(el).data('min');
|
||||||
|
var max = $(el).data('max');
|
||||||
|
var delta = $(el).data('delta');
|
||||||
|
var granularity = $(el).data('granularity');
|
||||||
|
|
||||||
|
var val = $(el).val();
|
||||||
|
var oldval = $.isNumeric(val) ? Number(val) : 0;
|
||||||
|
var newval = oldval + delta*direction;
|
||||||
|
|
||||||
|
if (newval < min) newval = min;
|
||||||
|
if (newval > max) newval = max;
|
||||||
|
|
||||||
|
// disable buttons when min/max reached
|
||||||
|
$('button[data-decrement='+id+']').attr('disabled', newval<=min ? 'disabled' : null);
|
||||||
|
$('button[data-increment='+id+']').attr('disabled', newval>=max ? 'disabled' : null);
|
||||||
|
|
||||||
|
// warn when what was entered is not a number
|
||||||
|
$(el).toggleClass('error', val!='' && val!='.' && (!$.isNumeric(val) || val < 0));
|
||||||
|
|
||||||
|
// update field, unless the user is typing
|
||||||
|
if (!$(el).is(':focus')) {
|
||||||
|
$(el).val(round_float(newval, granularity));
|
||||||
|
$(el).trigger('changed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// truncate numbers because of tiny floating point deviations
|
||||||
|
// if we don't do this, 1.0 might be shown as 0.99999999
|
||||||
|
function round_float(s, granularity) {
|
||||||
|
var e = granularity ? 1/granularity : 1000;
|
||||||
|
return Math.round(Number(s)*e) / e;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
@import "twitter/bootstrap/bootstrap";
|
@import "twitter/bootstrap/bootstrap";
|
||||||
@import "twitter/bootstrap/responsive";
|
@import "twitter/bootstrap/responsive";
|
||||||
|
@import "delta_input";
|
||||||
|
|
||||||
body {
|
body {
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
|
@ -104,7 +106,7 @@ table {
|
||||||
content: ' \25B2';
|
content: ' \25B2';
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.article-category {
|
tr.list-heading {
|
||||||
background-color: #efefef;
|
background-color: #efefef;
|
||||||
td:first-child {
|
td:first-child {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
@ -126,6 +128,10 @@ table {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.center, td.center, th.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
// Tasks ..
|
// Tasks ..
|
||||||
.accepted {
|
.accepted {
|
||||||
color: #468847;
|
color: #468847;
|
||||||
|
@ -238,6 +244,27 @@ tr.unavailable {
|
||||||
min-width: 3.5em;
|
min-width: 3.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// small cells with just a 'x' or '='
|
||||||
|
td.symbol, th.symbol {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.symbol { color: tint(@textColor, @nonessentialDim); }
|
||||||
|
.used .symbol { color: tint(@articleUsedColor, @nonessentialDim); }
|
||||||
|
.unused .symbol { color: tint(@articleUnusedColor, @nonessentialDim); }
|
||||||
|
.unavailable .symbol { color: @articleUnavailColor; }
|
||||||
|
|
||||||
|
// hide symbols completely on small screens to save space
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.symbol {
|
||||||
|
font-size: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ********* Tweaks & fixes
|
// ********* Tweaks & fixes
|
||||||
|
|
||||||
// Fix bootstrap dropdown menu on mobile
|
// Fix bootstrap dropdown menu on mobile
|
||||||
|
|
33
app/assets/stylesheets/delta_input.less
Normal file
33
app/assets/stylesheets/delta_input.less
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// needs @import "twitter/bootstrap/bootstrap";
|
||||||
|
|
||||||
|
form.delta-input, .delta-input form {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delta-input {
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 0;
|
||||||
|
width: 24px;
|
||||||
|
height: 28px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
input[data-delta] {
|
||||||
|
text-align: center;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle error class outside of bootstrap controls
|
||||||
|
input[data-delta].error {
|
||||||
|
// relevant bootstrap portion of: .formFieldState(@errorText, @errorText, @errorBackground);
|
||||||
|
border-color: @errorText;
|
||||||
|
.box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
|
||||||
|
&:focus {
|
||||||
|
border-color: darken(@errorText, 10%);
|
||||||
|
@shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@errorText, 20%);
|
||||||
|
.box-shadow(@shadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
class Finance::GroupOrderArticlesController < ApplicationController
|
class GroupOrderArticlesController < ApplicationController
|
||||||
|
|
||||||
before_filter :authenticate_finance
|
before_filter :authenticate_finance
|
||||||
|
before_filter :find_group_order_article, except: [:new, :create]
|
||||||
|
|
||||||
layout false # We only use this controller to server js snippets, no need for layout rendering
|
layout false # We only use this controller to server js snippets, no need for layout rendering
|
||||||
|
|
||||||
|
@ -10,6 +11,8 @@ class Finance::GroupOrderArticlesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
# XXX when ordergroup_id appears before order_article_id in the parameters, you
|
||||||
|
# can get `NoMethodError - undefined method 'order_id' for nil:NilClass`
|
||||||
@group_order_article = GroupOrderArticle.new(params[:group_order_article])
|
@group_order_article = GroupOrderArticle.new(params[:group_order_article])
|
||||||
@order_article = @group_order_article.order_article
|
@order_article = @group_order_article.order_article
|
||||||
|
|
||||||
|
@ -21,59 +24,38 @@ class Finance::GroupOrderArticlesController < ApplicationController
|
||||||
@group_order_article = goa
|
@group_order_article = goa
|
||||||
|
|
||||||
update_summaries(@group_order_article)
|
update_summaries(@group_order_article)
|
||||||
render :update
|
render :create
|
||||||
|
|
||||||
elsif @group_order_article.save
|
elsif @group_order_article.save
|
||||||
update_summaries(@group_order_article)
|
update_summaries(@group_order_article)
|
||||||
render :update
|
render :create
|
||||||
|
|
||||||
else # Validation failed, show form
|
else # Validation failed, show form
|
||||||
render :new
|
render :new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
|
||||||
@group_order_article = GroupOrderArticle.find(params[:id])
|
|
||||||
@order_article = @group_order_article.order_article
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@group_order_article = GroupOrderArticle.find(params[:id])
|
if params[:delta]
|
||||||
@order_article = @group_order_article.order_article
|
@group_order_article.update_attribute :result, [@group_order_article.result + params[:delta].to_f, 0].max
|
||||||
|
|
||||||
if @group_order_article.update_attributes(params[:group_order_article])
|
|
||||||
update_summaries(@group_order_article)
|
|
||||||
else
|
else
|
||||||
render :edit
|
@group_order_article.update_attributes(params[:group_order_article])
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_result
|
|
||||||
group_order_article = GroupOrderArticle.find(params[:id])
|
|
||||||
@order_article = group_order_article.order_article
|
|
||||||
|
|
||||||
if params[:modifier] == '-'
|
|
||||||
group_order_article.update_attribute :result, group_order_article.result - 1
|
|
||||||
elsif params[:modifier] == '+'
|
|
||||||
group_order_article.update_attribute :result, group_order_article.result + 1
|
|
||||||
end
|
end
|
||||||
|
|
||||||
update_summaries(group_order_article)
|
update_summaries(@group_order_article)
|
||||||
|
|
||||||
render :update
|
render :update
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
group_order_article = GroupOrderArticle.find(params[:id])
|
|
||||||
# only destroy if quantity and tolerance was zero already, so that we don't
|
# only destroy if quantity and tolerance was zero already, so that we don't
|
||||||
# lose what the user ordered, if any
|
# lose what the user ordered, if any
|
||||||
if group_order_article.quantity > 0 or group_order_article.tolerance >0
|
if @group_order_article.quantity > 0 or @group_order_article.tolerance >0
|
||||||
group_order_article.update_attribute(:result, 0)
|
@group_order_article.update_attribute(:result, 0)
|
||||||
else
|
else
|
||||||
group_order_article.destroy
|
@group_order_article.destroy
|
||||||
end
|
end
|
||||||
update_summaries(group_order_article)
|
update_summaries(@group_order_article)
|
||||||
@order_article = group_order_article.order_article
|
|
||||||
|
|
||||||
render :update
|
render :update
|
||||||
end
|
end
|
||||||
|
@ -86,4 +68,8 @@ class Finance::GroupOrderArticlesController < ApplicationController
|
||||||
# Update units_to_order of order_article
|
# Update units_to_order of order_article
|
||||||
group_order_article.order_article.update_results! if group_order_article.order_article.article.is_a?(StockArticle)
|
group_order_article.order_article.update_results! if group_order_article.order_article.article.is_a?(StockArticle)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def find_group_order_article
|
||||||
|
@group_order_article = GroupOrderArticle.find(params[:id])
|
||||||
|
end
|
||||||
end
|
end
|
|
@ -13,10 +13,10 @@ class OrdersController < ApplicationController
|
||||||
@per_page = 15
|
@per_page = 15
|
||||||
if params['sort']
|
if params['sort']
|
||||||
sort = case params['sort']
|
sort = case params['sort']
|
||||||
when "supplier" then "suppliers.name, ends DESC"
|
when "supplier" then "suppliers.name, ends DESC"
|
||||||
when "ends" then "ends DESC"
|
when "ends" then "ends DESC"
|
||||||
when "supplier_reverse" then "suppliers.name DESC"
|
when "supplier_reverse" then "suppliers.name DESC"
|
||||||
when "ends_reverse" then "ends"
|
when "ends_reverse" then "ends"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
sort = "ends DESC"
|
sort = "ends DESC"
|
||||||
|
@ -30,9 +30,9 @@ class OrdersController < ApplicationController
|
||||||
@order= Order.find(params[:id])
|
@order= Order.find(params[:id])
|
||||||
@view = (params[:view] or 'default').gsub(/[^-_a-zA-Z0-9]/, '')
|
@view = (params[:view] or 'default').gsub(/[^-_a-zA-Z0-9]/, '')
|
||||||
@partial = case @view
|
@partial = case @view
|
||||||
when 'default' then 'articles'
|
when 'default' then 'articles'
|
||||||
when 'groups'then 'shared/articles_by_groups'
|
when 'groups' then 'shared/articles_by/groups'
|
||||||
when 'articles'then 'shared/articles_by_articles'
|
when 'articles' then 'shared/articles_by/articles'
|
||||||
else 'articles'
|
else 'articles'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,10 +43,10 @@ class OrdersController < ApplicationController
|
||||||
end
|
end
|
||||||
format.pdf do
|
format.pdf do
|
||||||
pdf = case params[:document]
|
pdf = case params[:document]
|
||||||
when 'groups' then OrderByGroups.new(@order)
|
when 'groups' then OrderByGroups.new(@order)
|
||||||
when 'articles' then OrderByArticles.new(@order)
|
when 'articles' then OrderByArticles.new(@order)
|
||||||
when 'fax' then OrderFax.new(@order)
|
when 'fax' then OrderFax.new(@order)
|
||||||
when 'matrix' then OrderMatrix.new(@order)
|
when 'matrix' then OrderMatrix.new(@order)
|
||||||
end
|
end
|
||||||
send_data pdf.to_pdf, filename: pdf.filename, type: 'application/pdf'
|
send_data pdf.to_pdf, filename: pdf.filename, type: 'application/pdf'
|
||||||
end
|
end
|
||||||
|
@ -120,13 +120,11 @@ class OrdersController < ApplicationController
|
||||||
|
|
||||||
def receive_on_order_article_create # See publish/subscribe design pattern in /doc.
|
def receive_on_order_article_create # See publish/subscribe design pattern in /doc.
|
||||||
@order_article = OrderArticle.find(params[:order_article_id])
|
@order_article = OrderArticle.find(params[:order_article_id])
|
||||||
|
|
||||||
render :layout => false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def receive_on_order_article_update # See publish/subscribe design pattern in /doc.
|
def receive_on_order_article_update # See publish/subscribe design pattern in /doc.
|
||||||
@order_article = OrderArticle.find(params[:order_article_id])
|
@order_article = OrderArticle.find(params[:order_article_id])
|
||||||
|
|
||||||
render :layout => false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -78,14 +78,19 @@ module ApplicationHelper
|
||||||
# When the 'short' option is true, abbreviations will be used:
|
# When the 'short' option is true, abbreviations will be used:
|
||||||
# When there is a non-empty model attribute 'foo', it looks for
|
# When there is a non-empty model attribute 'foo', it looks for
|
||||||
# the model attribute translation 'foo_short' and use that as
|
# the model attribute translation 'foo_short' and use that as
|
||||||
# heading, with an abbreviation title of 'foo'.
|
# heading, with an abbreviation title of 'foo'. If a translation
|
||||||
|
# 'foo_desc' is present, that is used instead, but that can be
|
||||||
|
# be overridden by the option 'desc'.
|
||||||
# Other options are passed through to I18n.
|
# Other options are passed through to I18n.
|
||||||
def heading_helper(model, attribute, options = {})
|
def heading_helper(model, attribute, options = {})
|
||||||
i18nopts = options.select {|a| !['short'].include?(a) }.merge({count: 2})
|
i18nopts = {count: 2}.merge(options.select {|a| !['short', 'desc'].include?(a) })
|
||||||
s = model.human_attribute_name(attribute, i18nopts)
|
s = model.human_attribute_name(attribute, i18nopts)
|
||||||
if options[:short]
|
if options[:short]
|
||||||
|
desc = options[:desc]
|
||||||
|
desc ||= model.human_attribute_name("#{attribute}_desc".to_sym, options.merge({fallback: true, default: '', count: 2}))
|
||||||
|
desc.blank? and desc = s
|
||||||
sshort = model.human_attribute_name("#{attribute}_short".to_sym, options.merge({fallback: true, default: '', count: 2}))
|
sshort = model.human_attribute_name("#{attribute}_short".to_sym, options.merge({fallback: true, default: '', count: 2}))
|
||||||
s = raw "<abbr title='#{s}'>#{sshort}</abbr>" unless sshort.blank?
|
s = raw "<abbr title='#{desc}'>#{sshort}</abbr>" unless sshort.blank?
|
||||||
end
|
end
|
||||||
s
|
s
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,9 +5,9 @@ module Finance::BalancingHelper
|
||||||
when 'edit_results' then
|
when 'edit_results' then
|
||||||
'edit_results_by_articles'
|
'edit_results_by_articles'
|
||||||
when 'groups_overview' then
|
when 'groups_overview' then
|
||||||
'shared/articles_by_groups'
|
'shared/articles_by/groups'
|
||||||
when 'articles_overview' then
|
when 'articles_overview' then
|
||||||
'shared/articles_by_articles'
|
'shared/articles_by/articles'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
14
app/helpers/group_order_articles_helper.rb
Normal file
14
app/helpers/group_order_articles_helper.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
module GroupOrderArticlesHelper
|
||||||
|
|
||||||
|
# return an edit field for a GroupOrderArticle result
|
||||||
|
def group_order_article_edit_result(goa)
|
||||||
|
unless goa.group_order.order.finished? and current_user.role_finance?
|
||||||
|
goa.result
|
||||||
|
else
|
||||||
|
simple_form_for goa, remote: true, html: {'data-submit-onchange' => 'changed', class: 'delta-input'} do |f|
|
||||||
|
f.input_field :result, as: :delta, class: 'input-nano', data: {min: 0}, id: "r_#{goa.id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -18,7 +18,7 @@ module OrdersHelper
|
||||||
options_for_select(options)
|
options_for_select(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# "1 ordered units, 2 billed, 2 received"
|
# "1×2 ordered, 2×2 billed, 2×2 received"
|
||||||
def units_history_line(order_article, options={})
|
def units_history_line(order_article, options={})
|
||||||
if order_article.order.open?
|
if order_article.order.open?
|
||||||
nil
|
nil
|
||||||
|
@ -26,10 +26,9 @@ module OrdersHelper
|
||||||
units_info = []
|
units_info = []
|
||||||
[:units_to_order, :units_billed, :units_received].map do |unit|
|
[:units_to_order, :units_billed, :units_received].map do |unit|
|
||||||
if n = order_article.send(unit)
|
if n = order_article.send(unit)
|
||||||
i18nkey = if units_info.empty? and options[:plain] then unit else "#{unit}_short" end
|
|
||||||
line = n.to_s + ' '
|
line = n.to_s + ' '
|
||||||
line += pkg_helper(order_article.price) + ' ' unless options[:plain] or n == 0
|
line += pkg_helper(order_article.price, options) + ' ' unless n == 0
|
||||||
line += OrderArticle.human_attribute_name(i18nkey, count: n)
|
line += OrderArticle.human_attribute_name("#{unit}_short", count: n)
|
||||||
units_info << line
|
units_info << line
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -39,13 +38,16 @@ module OrdersHelper
|
||||||
|
|
||||||
# can be article or article_price
|
# can be article or article_price
|
||||||
# icon: `false` to not show the icon
|
# icon: `false` to not show the icon
|
||||||
|
# plain: `true` to not use html (implies icon: false)
|
||||||
# soft_uq: `true` to hide unit quantity specifier on small screens
|
# soft_uq: `true` to hide unit quantity specifier on small screens
|
||||||
# sensible in tables with multiple columns calling `pkg_helper`
|
# sensible in tables with multiple columns calling `pkg_helper`
|
||||||
def pkg_helper(article, options={})
|
def pkg_helper(article, options={})
|
||||||
return '' if not article or article.unit_quantity == 1
|
return '' if not article or article.unit_quantity == 1
|
||||||
uq_text = "× #{article.unit_quantity}".html_safe
|
uq_text = "× #{article.unit_quantity}"
|
||||||
uq_text = content_tag(:span, uq_text, class: 'hidden-phone') if options[:soft_uq]
|
uq_text = content_tag(:span, uq_text, class: 'hidden-phone') if options[:soft_uq]
|
||||||
if options[:icon].nil? or options[:icon]
|
if options[:plain]
|
||||||
|
uq_text
|
||||||
|
elsif options[:icon].nil? or options[:icon]
|
||||||
pkg_helper_icon(uq_text)
|
pkg_helper_icon(uq_text)
|
||||||
else
|
else
|
||||||
pkg_helper_icon(uq_text, tag: :span)
|
pkg_helper_icon(uq_text, tag: :span)
|
||||||
|
|
24
app/inputs/delta_input.rb
Normal file
24
app/inputs/delta_input.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
class DeltaInput < SimpleForm::Inputs::StringInput
|
||||||
|
# for now, need to pass id or it won't work
|
||||||
|
def input
|
||||||
|
@input_html_options[:data] ||= {}
|
||||||
|
@input_html_options[:data][:delta] ||= 1
|
||||||
|
@input_html_options[:autocomplete] ||= 'off'
|
||||||
|
# TODO get generated id, don't know how yet - `add_default_name_and_id_for_value` might be an option
|
||||||
|
|
||||||
|
template.content_tag :div, class: 'delta-input input-prepend input-append' do
|
||||||
|
delta_button('−', -1) + super + delta_button('+', 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
#template.button_tag('−', type: :submit, data: {decrement: @input_html_options[:id]}, tabindex: -1, class: 'btn') +
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def delta_button(title, direction)
|
||||||
|
data = { (direction>0 ? 'increment' : 'decrement') => @input_html_options[:id] }
|
||||||
|
delta = direction * @input_html_options[:data][:delta]
|
||||||
|
template.button_tag(title, type: :button, name: 'delta', value: delta, data: data, tabindex: -1, class: 'btn')
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,38 +4,31 @@
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
%td{:style => "width:8em"}= Ordergroup.model_name.human
|
%td{:style => "width:8em"}= Ordergroup.model_name.human
|
||||||
%td= t('.units')
|
-#%td.center= t('.units')
|
||||||
|
%td.center
|
||||||
|
%acronym{:title => t('shared.articles.received_desc')}= t 'shared.articles.received'
|
||||||
%td= t('.total')
|
%td= t('.total')
|
||||||
%td{:colspan => "3",:style => "width:14em"}
|
%td{:colspan => "3",:style => "width:14em"}
|
||||||
= link_to t('.add_group'), new_finance_group_order_article_path(order_article_id: order_article.id),
|
= link_to t('.add_group'), new_group_order_article_path(order_article_id: order_article.id),
|
||||||
remote: true, class: 'btn btn-mini'
|
remote: true, class: 'btn btn-mini'
|
||||||
%tbody
|
%tbody
|
||||||
|
- totals = {result: 0}
|
||||||
- for group_order_article in order_article.group_order_articles.select { |goa| goa.result > 0 }
|
- for group_order_article in order_article.group_order_articles.select { |goa| goa.result > 0 }
|
||||||
%tr[group_order_article]
|
%tr[group_order_article]
|
||||||
%td
|
%td
|
||||||
%td{:style=>"width:50%"}
|
%td{:style=>"width:50%"}
|
||||||
= group_order_article.group_order.ordergroup.name
|
= group_order_article.group_order.ordergroup.name
|
||||||
%td{:id => "group_order_article_#{group_order_article.id}_quantity", :style => "white-space:nowrap"}
|
%td.center= group_order_article_edit_result(group_order_article)
|
||||||
= group_order_article.result
|
%td.numeric= number_to_currency(group_order_article.order_article.price.fc_price * group_order_article.result)
|
||||||
= link_to "+", update_result_finance_group_order_article_path(group_order_article, modifier: '+'),
|
|
||||||
method: :put, remote: true, class: 'btn btn-mini'
|
|
||||||
= link_to "-", update_result_finance_group_order_article_path(group_order_article, modifier: '-'),
|
|
||||||
method: :put, remote: true, class: 'btn btn-mini'
|
|
||||||
%td.numeric
|
|
||||||
= number_to_currency(group_order_article.order_article.price.fc_price * group_order_article.result)
|
|
||||||
%td.actions{:style=>"width:1em"}
|
%td.actions{:style=>"width:1em"}
|
||||||
= link_to t('ui.edit'), edit_finance_group_order_article_path(group_order_article), remote: true,
|
= link_to t('ui.delete'), group_order_article_path(group_order_article), method: :delete,
|
||||||
class: 'btn btn-mini'
|
|
||||||
%td.actions{:style=>"width:1em"}
|
|
||||||
= link_to t('ui.delete'), finance_group_order_article_path(group_order_article), method: :delete,
|
|
||||||
remote: true, class: 'btn btn-mini btn-danger'
|
remote: true, class: 'btn btn-mini btn-danger'
|
||||||
%td
|
%td
|
||||||
|
- totals[:result] += group_order_article.result
|
||||||
%tfoot
|
%tfoot
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
%td{:style => "width:8em"}= t('.total_fc')
|
%td{:style => "width:8em"}= t('.total_fc')
|
||||||
%td{:id => "group_orders_sum_quantity_#{order_article.id}"}
|
%td.center= totals[:result]
|
||||||
= order_article.group_orders_sum[:quantity]
|
%td.numeric= number_to_currency(order_article.group_orders_sum[:price])
|
||||||
%td.numeric{:id => "group_orders_sum_price_#{order_article.id}"}
|
|
||||||
= number_to_currency(order_article.group_orders_sum[:price])
|
|
||||||
%td{:colspan => "3"}
|
%td{:colspan => "3"}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
$(function() {
|
$(function() {
|
||||||
// Subscribe to database changes.
|
// Subscribe to database changes.
|
||||||
// See publish/subscribe design pattern in /doc.
|
// See publish/subscribe design pattern in /doc.
|
||||||
$(document).on('OrderArticle#update', function(e) {
|
$(document).on('OrderArticle#update GroupOrderArticle#create GroupOrderArticle#update', function(e) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '#{new_on_order_article_update_finance_order_path(@order)}',
|
url: '#{new_on_order_article_update_finance_order_path(@order)}',
|
||||||
type: 'get',
|
type: 'get',
|
||||||
|
@ -21,6 +21,7 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
= render 'shared/articles_by/common', order: @order
|
||||||
|
|
||||||
- title t('.title', name: @order.name)
|
- title t('.title', name: @order.name)
|
||||||
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
$('#modalContainer').html('#{j(render("form"))}');
|
|
||||||
$('#modalContainer').modal();
|
|
|
@ -1,2 +0,0 @@
|
||||||
$('#modalContainer').html('#{j(render("form"))}');
|
|
||||||
$('#modalContainer').modal();
|
|
|
@ -1,4 +0,0 @@
|
||||||
$('#modalContainer').modal('hide');
|
|
||||||
$('#order_article_#{@order_article.id}').html('#{j(render('finance/balancing/order_article', order_article: @order_article))}');
|
|
||||||
$('#group_order_articles_#{@order_article.id}').html('#{j(render('finance/balancing/group_order_articles', order_article: @order_article))}');
|
|
||||||
$('#summaryChangedWarning').show();
|
|
|
@ -1,11 +1,11 @@
|
||||||
= simple_form_for [:finance, @group_order_article], remote: true do |form|
|
= simple_form_for @group_order_article, remote: true do |form|
|
||||||
= form.hidden_field :order_article_id
|
= form.hidden_field :order_article_id
|
||||||
.modal-header
|
.modal-header
|
||||||
= link_to t('ui.marks.close').html_safe, '#', class: 'close', data: {dismiss: 'modal'}
|
= link_to t('ui.marks.close').html_safe, '#', class: 'close', data: {dismiss: 'modal'}
|
||||||
%h3= t('.amount_change_for', article: @order_article.article.name)
|
%h3= t('.amount_change_for', article: @order_article.article.name)
|
||||||
.modal-body
|
.modal-body
|
||||||
= form.input :ordergroup_id, as: :select, collection: Ordergroup.all.map { |g| [g.name, g.id] }
|
= form.input :ordergroup_id, as: :select, collection: Ordergroup.all.map { |g| [g.name, g.id] }
|
||||||
= form.input :result, hint: I18n.t('finance.group_order_articles.form.result_hint', unit: @order_article.article.unit) # Why do we need the full prefix?
|
= form.input :result, hint: I18n.t('group_order_articles.form.result_hint', unit: @order_article.article.unit) # Why do we need the full prefix?
|
||||||
.modal-footer
|
.modal-footer
|
||||||
= link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'}
|
= link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'}
|
||||||
= form.submit t('ui.save'), class: 'btn btn-primary'
|
= form.submit t('ui.save'), class: 'btn btn-primary'
|
10
app/views/group_order_articles/create.js.erb
Normal file
10
app/views/group_order_articles/create.js.erb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
$('#modalContainer').modal('hide');
|
||||||
|
|
||||||
|
// trigger hooks for views
|
||||||
|
$(document).trigger({
|
||||||
|
type: 'GroupOrderArticle#create',
|
||||||
|
order_article_id: <%= @group_order_article.order_article_id %>,
|
||||||
|
group_order_id: <%= @group_order_article.group_order_id %>,
|
||||||
|
group_order_article_id: <%= @group_order_article.id %>,
|
||||||
|
group_order_article_price: <%= @group_order_article.total_price %>
|
||||||
|
});
|
2
app/views/group_order_articles/new.js.erb
Normal file
2
app/views/group_order_articles/new.js.erb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
$('#modalContainer').html('<%= j render("form") %>');
|
||||||
|
$('#modalContainer').modal();
|
8
app/views/group_order_articles/update.js.erb
Normal file
8
app/views/group_order_articles/update.js.erb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// and trigger hooks for views including this
|
||||||
|
$(document).trigger({
|
||||||
|
type: 'GroupOrderArticle#update',
|
||||||
|
order_article_id: <%= @group_order_article.order_article_id %>,
|
||||||
|
group_order_id: <%= @group_order_article.group_order_id %>,
|
||||||
|
group_order_article_id: <%= @group_order_article.id %>,
|
||||||
|
group_order_article_price: <%= @group_order_article.total_price %>
|
||||||
|
});
|
|
@ -98,3 +98,5 @@
|
||||||
$(function() {
|
$(function() {
|
||||||
activate_search('#{j @view}', '#{j t(".search_placeholder.#{@view}")}');
|
activate_search('#{j @view}', '#{j t(".search_placeholder.#{@view}")}');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
= render 'shared/articles_by/common', order: @order
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
%table.table.table-hover.list
|
|
||||||
%thead.list-heading
|
|
||||||
%tr
|
|
||||||
%th{:style => 'width:70%'}= t '.ordergroup'
|
|
||||||
%th
|
|
||||||
%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'
|
|
||||||
|
|
||||||
- for order_article in order.order_articles.ordered.all(:include => [:article, :article_price])
|
|
||||||
%tbody
|
|
||||||
%tr
|
|
||||||
%th.name{:colspan => "4"}
|
|
||||||
= order_article.article.name
|
|
||||||
= "(#{order_article.article.unit} | #{order_article.price.unit_quantity} | #{number_to_currency(order_article.price.gross_price)})"
|
|
||||||
- for goa in order_article.group_order_articles.ordered
|
|
||||||
%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= "#{goa.quantity} + #{goa.tolerance}"
|
|
||||||
%td
|
|
||||||
%b= goa.result
|
|
||||||
%td= number_to_currency(order_article.price.fc_price * goa.result)
|
|
||||||
%tr
|
|
||||||
%td(colspan="4" )
|
|
||||||
- reset_cycle('groups')
|
|
|
@ -1,40 +0,0 @@
|
||||||
%table.table.table-hover.list
|
|
||||||
%thead.list-heading
|
|
||||||
%tr
|
|
||||||
%th{:style => "width:40%"}= heading_helper Article, :name
|
|
||||||
%th
|
|
||||||
%acronym{:title => t('shared.articles.ordered_desc')}= t 'shared.articles.ordered'
|
|
||||||
%th
|
|
||||||
%acronym{:title => t('shared.articles.received_desc')}= t 'shared.articles.received'
|
|
||||||
%th
|
|
||||||
%acronym{:title => t('.fc_price_desc')}= t '.fc_price'
|
|
||||||
%th
|
|
||||||
%acronym{:title => t('.unit_quantity_desc')}= t '.unit_quantity'
|
|
||||||
%th= heading_helper Article, :unit
|
|
||||||
%th= t '.price'
|
|
||||||
|
|
||||||
- for group_order in order.group_orders.ordered
|
|
||||||
%tbody
|
|
||||||
%tr
|
|
||||||
%th{:colspan => "7"}
|
|
||||||
%h4.name= group_order.ordergroup.name
|
|
||||||
- total = 0
|
|
||||||
- for goa in group_order.group_order_articles.ordered.all(:include => :order_article)
|
|
||||||
- fc_price = goa.order_article.price.fc_price
|
|
||||||
- subTotal = fc_price * goa.result
|
|
||||||
- total += subTotal
|
|
||||||
%tr{:class => [cycle('even', 'odd', :name => 'articles'), if goa.result == 0 then 'unavailable' end]}
|
|
||||||
%td.name{:style => "width:40%"}=h goa.order_article.article.name
|
|
||||||
%td= "#{goa.quantity} + #{goa.tolerance}"
|
|
||||||
%td
|
|
||||||
%b= goa.result
|
|
||||||
%td= number_to_currency(fc_price)
|
|
||||||
%td= goa.order_article.price.unit_quantity
|
|
||||||
%td= goa.order_article.article.unit
|
|
||||||
%td= number_to_currency(subTotal)
|
|
||||||
%tr{:class => cycle('even', 'odd', :name => 'articles')}
|
|
||||||
%th{:colspan => "6"} Summe
|
|
||||||
%th= number_to_currency(total)
|
|
||||||
%tr
|
|
||||||
%th(colspan="7")
|
|
||||||
- reset_cycle("articles")
|
|
11
app/views/shared/articles_by/_article_single.html.haml
Normal file
11
app/views/shared/articles_by/_article_single.html.haml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
%tbody{id: "oa_#{order_article.id}"}
|
||||||
|
- if not defined?(heading) or heading
|
||||||
|
%tr.list-heading
|
||||||
|
%th.name{:colspan => "4"}>
|
||||||
|
= order_article.article.name + ' '
|
||||||
|
= "(#{order_article.article.unit}, #{number_to_currency order_article.price.fc_price}"
|
||||||
|
- pkg_info = pkg_helper(order_article.price)
|
||||||
|
= ", #{pkg_info}".html_safe unless pkg_info.blank?
|
||||||
|
)
|
||||||
|
- for goa in order_article.group_order_articles.ordered
|
||||||
|
= render 'shared/articles_by/article_single_goa', goa: goa, edit: (edit rescue nil)
|
|
@ -0,0 +1,5 @@
|
||||||
|
%tr{class: if goa.result == 0 then 'unavailable' end, id: "goa_#{goa.id}"}
|
||||||
|
%td{:style => "width:70%"}= goa.group_order.ordergroup.name
|
||||||
|
%td.center= "#{goa.quantity} + #{goa.tolerance}"
|
||||||
|
%td.center.input-delta= (edit or true rescue true) ? group_order_article_edit_result(goa) : goa.result
|
||||||
|
%td.price{data: {value: goa.total_price}}= number_to_currency(goa.total_price)
|
14
app/views/shared/articles_by/_articles.html.haml
Normal file
14
app/views/shared/articles_by/_articles.html.haml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
%table.table.table-hover.list#articles_by_articles
|
||||||
|
%thead.list-heading
|
||||||
|
%tr
|
||||||
|
%th{:style => 'width:70%'}= Ordergroup.model_name.human
|
||||||
|
%th.center
|
||||||
|
%acronym{:title => t('shared.articles.ordered_desc')}= t 'shared.articles.ordered'
|
||||||
|
%th.center
|
||||||
|
%acronym{:title => t('shared.articles.received_desc')}= t 'shared.articles.received'
|
||||||
|
%th= t 'shared.articles_by.price'
|
||||||
|
|
||||||
|
- for order_article in order.order_articles.ordered.all(:include => [:article, :article_price])
|
||||||
|
= render 'shared/articles_by/article_single', order_article: order_article, edit: (edit rescue nil)
|
||||||
|
%tr
|
||||||
|
%td{colspan: 4}
|
30
app/views/shared/articles_by/_common.html.haml
Normal file
30
app/views/shared/articles_by/_common.html.haml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
-# common javascript for updating articles_by views
|
||||||
|
-# include this in all pages that use articles_by views (directly or via ajax)
|
||||||
|
= content_for :javascript do
|
||||||
|
:javascript
|
||||||
|
$(document).on('GroupOrderArticle#update', function(e) {
|
||||||
|
|
||||||
|
var el_goa = $('#goa_'+e.group_order_article_id);
|
||||||
|
|
||||||
|
// update total price of group_order_article
|
||||||
|
// show localised value, store raw number in data attribute
|
||||||
|
var el_price = $('.price', el_goa);
|
||||||
|
var old_price = el_price.data('value');
|
||||||
|
if (el_price.length) {
|
||||||
|
el_price.text(I18n.l('currency', e.group_order_article_price));
|
||||||
|
el_price.data('value', e.group_order_article_price);
|
||||||
|
}
|
||||||
|
|
||||||
|
// group_order_article is greyed when result==0
|
||||||
|
el_goa.toggleClass('unavailable', $('input#r_'+e.group_order_article_id, el_goa).val()==0);
|
||||||
|
|
||||||
|
// update total price of group_order, order_article and/or ordergroup, when present
|
||||||
|
var el_sum = $('#group_order_'+e.group_order_id+', #single_ordergroup_total, #single_order_article_total');
|
||||||
|
var el_price_sum = $('.price_sum', el_sum);
|
||||||
|
if (el_price_sum.length) {
|
||||||
|
var old_price_sum = el_price_sum.data('value');
|
||||||
|
var new_price_sum = old_price_sum - old_price + e.group_order_article_price;
|
||||||
|
el_price_sum.text(I18n.l('currency', new_price_sum));
|
||||||
|
el_price_sum.data('value', new_price_sum);
|
||||||
|
}
|
||||||
|
});
|
15
app/views/shared/articles_by/_group_single.html.haml
Normal file
15
app/views/shared/articles_by/_group_single.html.haml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
%tbody{id: "group_order_#{group_order.id}"}
|
||||||
|
- if not defined?(heading) or heading
|
||||||
|
%tr.list-heading
|
||||||
|
%th{colspan: 9}
|
||||||
|
%h4.name= group_order.ordergroup.name
|
||||||
|
- total = 0
|
||||||
|
- for goa in group_order.group_order_articles.ordered.all(:include => :order_article)
|
||||||
|
- total += goa.total_price
|
||||||
|
= render 'shared/articles_by/group_single_goa', goa: goa, edit: (edit rescue nil)
|
||||||
|
%tr{class: cycle('even', 'odd', :name => 'articles')}
|
||||||
|
%th{colspan: 7}= t 'shared.articles_by.price_sum'
|
||||||
|
%th.price_sum{colspan: 2, data: {value: total}}= number_to_currency(total)
|
||||||
|
%tr
|
||||||
|
%th{colspan: 9}
|
||||||
|
- reset_cycle("articles")
|
11
app/views/shared/articles_by/_group_single_goa.html.haml
Normal file
11
app/views/shared/articles_by/_group_single_goa.html.haml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
%tr{class: [cycle('even', 'odd', :name => 'articles'), if goa.result == 0 then 'unavailable' end], id: "goa_#{goa.id}"}
|
||||||
|
%td.name= goa.order_article.article.name
|
||||||
|
%td= goa.order_article.article.unit
|
||||||
|
%td.center= "#{goa.quantity} + #{goa.tolerance}"
|
||||||
|
%td.center.input-delta= (edit or true rescue true) ? group_order_article_edit_result(goa) : goa.result
|
||||||
|
%td.symbol ×
|
||||||
|
%td= number_to_currency(goa.order_article.price.fc_price)
|
||||||
|
%td.symbol =
|
||||||
|
%td.price{data: {value: goa.total_price}}= number_to_currency(goa.total_price)
|
||||||
|
%td= pkg_helper goa.order_article.price
|
||||||
|
|
17
app/views/shared/articles_by/_groups.html.haml
Normal file
17
app/views/shared/articles_by/_groups.html.haml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
%table.table.table-hover.list#articles_by_groups
|
||||||
|
%thead.list-heading
|
||||||
|
%tr
|
||||||
|
%th{:style => "width:40%"}= heading_helper Article, :name
|
||||||
|
%th= heading_helper Article, :unit
|
||||||
|
%th.center
|
||||||
|
%acronym{:title => t('shared.articles.ordered_desc')}= t 'shared.articles.ordered'
|
||||||
|
%th.center
|
||||||
|
%acronym{:title => t('shared.articles.received_desc')}= t 'shared.articles.received'
|
||||||
|
%th.symbol
|
||||||
|
%th= heading_helper Article, :fc_price, short: true
|
||||||
|
%th.symbol
|
||||||
|
%th= t 'shared.articles_by.price'
|
||||||
|
%th= #heading_helper Article, :unit_quantity, short: true
|
||||||
|
|
||||||
|
- for group_order in order.group_orders.ordered
|
||||||
|
= render 'shared/articles_by/group_single', group_order: group_order, edit: (edit rescue nil)
|
|
@ -7,6 +7,7 @@ de:
|
||||||
availability_short: verf.
|
availability_short: verf.
|
||||||
deposit: Pfand
|
deposit: Pfand
|
||||||
fc_price: Endpreis
|
fc_price: Endpreis
|
||||||
|
fc_price_desc: Preis incl. MwSt, Pfand und Foodcoop-Aufschlag
|
||||||
fc_price_short: FC-Preis
|
fc_price_short: FC-Preis
|
||||||
fc_share: FoodCoop-Aufschlag
|
fc_share: FoodCoop-Aufschlag
|
||||||
fc_share_short: FC-Aufschlag
|
fc_share_short: FC-Aufschlag
|
||||||
|
@ -1285,9 +1286,8 @@ de:
|
||||||
ordergroup: Bestellgruppe
|
ordergroup: Bestellgruppe
|
||||||
price: Gesamtpreis
|
price: Gesamtpreis
|
||||||
articles_by_groups:
|
articles_by_groups:
|
||||||
fc_price: FC-Preis
|
|
||||||
fc_price_desc: Preis incl. MwSt, Pfand und Foodcoop-Aufschlag
|
|
||||||
price: Gesamtpreis
|
price: Gesamtpreis
|
||||||
|
price_sum: Summe
|
||||||
unit_quantity: GebGr
|
unit_quantity: GebGr
|
||||||
unit_quantity_desc: Gebindegröße
|
unit_quantity_desc: Gebindegröße
|
||||||
group:
|
group:
|
||||||
|
|
|
@ -7,6 +7,7 @@ en:
|
||||||
availability_short: avail.
|
availability_short: avail.
|
||||||
deposit: Deposit
|
deposit: Deposit
|
||||||
fc_price: FoodCoop price
|
fc_price: FoodCoop price
|
||||||
|
fc_price_desc: Price including taxes, deposit and Foodcoop-charge
|
||||||
fc_price_short: FC price
|
fc_price_short: FC price
|
||||||
fc_share: FoodCoop margin
|
fc_share: FoodCoop margin
|
||||||
fc_share_short: FC margin
|
fc_share_short: FC margin
|
||||||
|
@ -599,10 +600,6 @@ en:
|
||||||
ordergroup:
|
ordergroup:
|
||||||
remove: Remove
|
remove: Remove
|
||||||
remove_group: Remove group
|
remove_group: Remove group
|
||||||
group_order_articles:
|
|
||||||
form:
|
|
||||||
amount_change_for: Change amount for %{article}
|
|
||||||
result_hint: ! 'Unit: %{unit}'
|
|
||||||
index:
|
index:
|
||||||
amount_fc: Amount(FC)
|
amount_fc: Amount(FC)
|
||||||
end: End
|
end: End
|
||||||
|
@ -736,6 +733,10 @@ en:
|
||||||
error_general: The order couldn’t be updated due to a bug.
|
error_general: The order couldn’t be updated due to a bug.
|
||||||
error_stale: Someone else has ordered in the meantime, couldn't update the order.
|
error_stale: Someone else has ordered in the meantime, couldn't update the order.
|
||||||
notice: The order was saved.
|
notice: The order was saved.
|
||||||
|
group_order_articles:
|
||||||
|
form:
|
||||||
|
amount_change_for: Change amount for %{article}
|
||||||
|
result_hint: ! 'Unit: %{unit}'
|
||||||
helpers:
|
helpers:
|
||||||
application:
|
application:
|
||||||
edit_user: Edit user
|
edit_user: Edit user
|
||||||
|
@ -1293,15 +1294,9 @@ en:
|
||||||
ordered_desc: Number of articles as ordered by member (amount + tolerance)
|
ordered_desc: Number of articles as ordered by member (amount + tolerance)
|
||||||
received: Received
|
received: Received
|
||||||
received_desc: Number of articles that (will be) received by member
|
received_desc: Number of articles that (will be) received by member
|
||||||
articles_by_articles:
|
articles_by:
|
||||||
ordergroup: Ordergroup
|
|
||||||
price: Total price
|
price: Total price
|
||||||
articles_by_groups:
|
price_sum: Sum
|
||||||
fc_price: FC-Price
|
|
||||||
fc_price_desc: Price including taxes, deposit and Foodcoop-charge
|
|
||||||
price: Total price
|
|
||||||
unit_quantity: Lot quantity
|
|
||||||
unit_quantity_desc: How many units per lot.
|
|
||||||
group:
|
group:
|
||||||
access: Access to
|
access: Access to
|
||||||
activated: activated
|
activated: activated
|
||||||
|
|
|
@ -53,6 +53,8 @@ Foodsoft::Application.routes.draw do
|
||||||
get :archive, :on => :collection
|
get :archive, :on => :collection
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources :group_order_articles
|
||||||
|
|
||||||
resources :order_comments, :only => [:new, :create]
|
resources :order_comments, :only => [:new, :create]
|
||||||
|
|
||||||
############ Foodcoop orga
|
############ Foodcoop orga
|
||||||
|
@ -155,12 +157,6 @@ Foodsoft::Application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :group_order_articles do
|
|
||||||
member do
|
|
||||||
put :update_result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
resources :invoices
|
resources :invoices
|
||||||
|
|
||||||
resources :ordergroups, :only => [:index] do
|
resources :ordergroups, :only => [:index] do
|
||||||
|
|
|
@ -2,6 +2,7 @@ require_relative '../spec_helper'
|
||||||
|
|
||||||
describe 'settling an order', :type => :feature do
|
describe 'settling an order', :type => :feature do
|
||||||
let(:admin) { create :user, groups:[create(:workgroup, role_finance: true)] }
|
let(:admin) { create :user, groups:[create(:workgroup, role_finance: true)] }
|
||||||
|
let(:user) { create :user, groups:[create(:ordergroup)] }
|
||||||
let(:supplier) { create :supplier }
|
let(:supplier) { create :supplier }
|
||||||
let(:article) { create :article, supplier: supplier, unit_quantity: 1 }
|
let(:article) { create :article, supplier: supplier, unit_quantity: 1 }
|
||||||
let(:order) { 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
|
||||||
|
@ -43,10 +44,10 @@ describe 'settling an order', :type => :feature do
|
||||||
expect(page).to have_content(go1.ordergroup.name)
|
expect(page).to have_content(go1.ordergroup.name)
|
||||||
expect(page).to have_content(go2.ordergroup.name)
|
expect(page).to have_content(go2.ordergroup.name)
|
||||||
# and that their order results match what we expect
|
# and that their order results match what we expect
|
||||||
expect(page).to have_selector("#group_order_article_#{goa1.id}_quantity")
|
expect(page).to have_selector("#r_#{goa1.id}")
|
||||||
expect(find("#group_order_article_#{goa1.id}_quantity").text.to_f).to eq(3)
|
expect(find("#r_#{goa1.id}").value.to_f).to eq(3)
|
||||||
expect(page).to have_selector("#group_order_article_#{goa2.id}_quantity")
|
expect(page).to have_selector("#r_#{goa2.id}")
|
||||||
expect(find("#group_order_article_#{goa2.id}_quantity").text.to_f).to eq(1)
|
expect(find("#r_#{goa2.id}").value.to_f).to eq(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -120,6 +121,48 @@ describe 'settling an order', :type => :feature do
|
||||||
expect(oa.units_to_order).to eq(0)
|
expect(oa.units_to_order).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'can add an ordergroup to an order article' do
|
||||||
|
user # need to reference user before "new article" dialog is loaded
|
||||||
|
click_link article.name
|
||||||
|
within("#group_order_articles_#{oa.id}") do
|
||||||
|
click_link I18n.t('finance.balancing.group_order_articles.add_group')
|
||||||
|
end
|
||||||
|
expect(page).to have_selector('form#new_group_order_article')
|
||||||
|
within('#new_group_order_article') do
|
||||||
|
select user.ordergroup.name, :from => 'group_order_article_ordergroup_id'
|
||||||
|
fill_in 'group_order_article_result', :with => 8
|
||||||
|
find('input[type="submit"]').click
|
||||||
|
end
|
||||||
|
expect(page).to have_content(user.ordergroup.name)
|
||||||
|
goa = GroupOrderArticle.last
|
||||||
|
expect(goa).to_not be_nil
|
||||||
|
expect(goa.result).to eq 8
|
||||||
|
expect(page).to have_selector("#group_order_article_#{goa.id}")
|
||||||
|
expect(find("#r_#{goa.id}").value.to_f).to eq 8
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can modify an ordergroup result' do
|
||||||
|
click_link article.name
|
||||||
|
within("#group_order_articles_#{oa.id}") do
|
||||||
|
fill_in "r_#{goa1.id}", :with => 5
|
||||||
|
# leave input box and wait a bit so that update is sent using ajax
|
||||||
|
find("#r_#{goa1.id}").native.send_keys :tab
|
||||||
|
sleep 1
|
||||||
|
end
|
||||||
|
expect(goa1.reload.result).to eq 5
|
||||||
|
expect(find("#group_order_articles_#{oa.id} tfoot td:nth-child(3)").text.to_f).to eq 6
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can modify an ordergroup result using the + button' do
|
||||||
|
click_link article.name
|
||||||
|
within("#group_order_article_#{goa1.id}") do
|
||||||
|
4.times { find('button[data-increment]').click }
|
||||||
|
sleep 1
|
||||||
|
end
|
||||||
|
expect(goa1.reload.result).to eq 7
|
||||||
|
expect(find("#group_order_articles_#{oa.id} tfoot td:nth-child(3)").text.to_f).to eq 8
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,7 @@ class ActiveRecord::Base
|
||||||
@@shared_connection = nil
|
@@shared_connection = nil
|
||||||
|
|
||||||
def self.connection
|
def self.connection
|
||||||
@@shared_connection || retrieve_connection
|
@@shared_connection || ConnectionPool::Wrapper.new(:size => 1) { retrieve_connection }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Forces all threads to share the same connection. This works on
|
# Forces all threads to share the same connection. This works on
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
// jquery.observe_field.js
|
|
||||||
|
|
||||||
|
|
||||||
(function( $ ){
|
|
||||||
|
|
||||||
jQuery.fn.observe_field = function(frequency, callback) {
|
|
||||||
|
|
||||||
frequency = frequency * 1000; // translate to milliseconds
|
|
||||||
|
|
||||||
return this.each(function(){
|
|
||||||
var $this = $(this);
|
|
||||||
var prev = $this.val();
|
|
||||||
|
|
||||||
var check = function() {
|
|
||||||
var val = $this.val();
|
|
||||||
if(prev != val){
|
|
||||||
prev = val;
|
|
||||||
$this.map(callback); // invokes the callback on $this
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var reset = function() {
|
|
||||||
if(ti){
|
|
||||||
clearInterval(ti);
|
|
||||||
ti = setInterval(check, frequency);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
check();
|
|
||||||
var ti = setInterval(check, frequency); // invoke check periodically
|
|
||||||
|
|
||||||
// reset counter after user interaction
|
|
||||||
$this.bind('keyup click mousemove', reset); //mousemove is for selects
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
})( jQuery );
|
|
||||||
|
|
Loading…
Reference in a new issue