diff --git a/.travis.yml b/.travis.yml index 383dac1c..003c0985 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ rvm: - 1.9.3 services: - redis-server +env: COVERALLS=1 before_install: - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..2d5fb822 --- /dev/null +++ b/CHANGELOG.md @@ -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) diff --git a/Gemfile b/Gemfile index ffb4ff0e..20469350 100644 --- a/Gemfile +++ b/Gemfile @@ -84,10 +84,12 @@ group :test do # webkit and poltergeist don't seem to work yet gem 'selenium-webdriver' gem 'database_cleaner' - gem 'simplecov', require: false # need to include rspec components before i18n-spec or rake fails in test environment gem 'rspec-core' gem 'rspec-expectations' gem 'rspec-rerun' gem 'i18n-spec' + # code coverage + gem 'simplecov', require: false + gem 'coveralls', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 66644fe2..ffb7da3e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -102,6 +102,12 @@ GEM execjs coffee-script-source (1.6.3) commonjs (0.2.7) + coveralls (0.7.0) + multi_json (~> 1.3) + rest-client + simplecov (>= 0.7) + term-ansicolor + thor daemons (1.1.9) database_cleaner (1.2.0) debug_inspector (0.0.2) @@ -248,6 +254,8 @@ GEM redis-namespace (~> 1.2) sinatra (>= 0.9.2) vegas (~> 0.1.2) + rest-client (1.6.7) + mime-types (>= 1.16) rspec (2.14.1) rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) @@ -306,6 +314,8 @@ GEM rack (~> 1.0) tilt (~> 1.1, != 1.3.0) sqlite3 (1.3.8) + term-ansicolor (1.2.2) + tins (~> 0.8) therubyracer (0.12.0) libv8 (~> 3.16.14.0) ref @@ -315,6 +325,7 @@ GEM rack (>= 1.0.0) thor (0.18.1) tilt (1.4.1) + tins (0.13.1) treetop (1.4.15) polyglot polyglot (>= 0.3.1) @@ -357,6 +368,7 @@ DEPENDENCIES client_side_validations client_side_validations-simple_form coffee-rails (~> 3.2.1) + coveralls daemons database_cleaner exception_notification diff --git a/VERSION b/VERSION index 94ff29cc..944880fa 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.1.1 +3.2.0 diff --git a/app/assets/stylesheets/bootstrap_and_overrides.css.less b/app/assets/stylesheets/bootstrap_and_overrides.css.less index 42fde3d5..305d0ccc 100644 --- a/app/assets/stylesheets/bootstrap_and_overrides.css.less +++ b/app/assets/stylesheets/bootstrap_and_overrides.css.less @@ -104,6 +104,10 @@ table { td.odd { background-color: @tableBackgroundAccent; } + + td.main_info { + font-weight: bold; + } tr.selected td { background-color: @successBackground; @@ -247,3 +251,40 @@ tr.unavailable { .modal form { 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; +} + diff --git a/app/controllers/deliveries_controller.rb b/app/controllers/deliveries_controller.rb index 86eedc14..3f18899f 100644 --- a/app/controllers/deliveries_controller.rb +++ b/app/controllers/deliveries_controller.rb @@ -50,57 +50,22 @@ class DeliveriesController < ApplicationController redirect_to supplier_deliveries_url(@supplier) 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 @stock_change = StockChange.new @stock_change.stock_article = StockArticle.find(params[:stock_article_id]) render :layout => false 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 diff --git a/app/controllers/stockit_controller.rb b/app/controllers/stockit_controller.rb index 475ac3a3..85c027ea 100644 --- a/app/controllers/stockit_controller.rb +++ b/app/controllers/stockit_controller.rb @@ -4,30 +4,62 @@ class StockitController < ApplicationController @stock_articles = StockArticle.undeleted.includes(:supplier, :article_category). order('suppliers.name, article_categories.name, articles.name') 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 - @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 def create @stock_article = StockArticle.new(params[:stock_article]) - if @stock_article.save - redirect_to stock_articles_path, :notice => I18n.t('stockit.stock_create.notice') + if @stock_article.valid? and @stock_article.save + render :layout => false else - render :action => 'new' + render :action => 'new', :layout => false end end def edit @stock_article = StockArticle.find(params[:id]) + + render :layout => false end def update @stock_article = StockArticle.find(params[:id]) if @stock_article.update_attributes(params[:stock_article]) - redirect_to stock_articles_path, :notice => I18n.t('stockit.stock_update.notice') + render :layout => false else - render :action => 'edit' + render :action => 'edit', :layout => false end end @@ -36,9 +68,15 @@ class StockitController < ApplicationController @stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC') 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 - @article = StockArticle.find(params[:id]) - @article.mark_as_deleted + @stock_article = StockArticle.find(params[:id]) + @stock_article.mark_as_deleted render :layout => false rescue => error render :partial => "destroy_fail", :layout => false, diff --git a/app/helpers/deliveries_helper.rb b/app/helpers/deliveries_helper.rb index b23107cc..2ef5d14b 100644 --- a/app/helpers/deliveries_helper.rb +++ b/app/helpers/deliveries_helper.rb @@ -25,13 +25,4 @@ module DeliveriesHelper return output.html_safe 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 diff --git a/app/helpers/stockit_helper.rb b/app/helpers/stockit_helper.rb index e75e1676..2bc5da01 100644 --- a/app/helpers/stockit_helper.rb +++ b/app/helpers/stockit_helper.rb @@ -14,4 +14,13 @@ module StockitHelper link_to StockTaking.model_name.human, stock_taking_path(stock_change.stock_taking) 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 diff --git a/app/models/group_order_article.rb b/app/models/group_order_article.rb index 3b37a990..9d4b8c82 100644 --- a/app/models/group_order_article.rb +++ b/app/models/group_order_article.rb @@ -101,49 +101,51 @@ class GroupOrderArticle < ActiveRecord::Base # See description of the ordering algorithm in the general application documentation for details. def calculate_result @calculate_result ||= begin - quantity = tolerance = 0 - stockit = order_article.article.is_a?(StockArticle) + quantity = tolerance = total_quantity = 0 # Get total - total = stockit ? order_article.article.quantity : order_article.units_to_order * order_article.price.unit_quantity - logger.debug("<#{order_article.article.name}>.unitsToOrder => items ordered: #{order_article.units_to_order} => #{total}") + if order_article.article.is_a?(StockArticle) + 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). # # Get all GroupOrderArticleQuantities for this OrderArticle... order_quantities = GroupOrderArticleQuantity.all( :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... - total_quantity = i = 0 - while (i < order_quantities.size && total_quantity < total) - q = (order_quantities[i].quantity <= total - total_quantity ? order_quantities[i].quantity : total - total_quantity) + order_quantities.each do |goaq| + q = [goaq.quantity, total - total_quantity].min total_quantity += q - if (order_quantities[i].group_order_article_id == self.id) - logger.debug("increasing quantity by #{q}") + if goaq.group_order_article_id == self.id + logger.debug "increasing quantity by #{q}" quantity += q end - i += 1 + break if total_quantity >= total end # Determine tolerance to be ordered... - if (total_quantity < total) - logger.debug("determining additional items to be ordered from tolerance") - i = 0 - while (i < order_quantities.size && total_quantity < total) - q = (order_quantities[i].tolerance <= total - total_quantity ? order_quantities[i].tolerance : total - total_quantity) + if total_quantity < total + logger.debug "determining additional items to be ordered from tolerance" + order_quantities.each do |goaq| + q = [goaq.tolerance, total - total_quantity].min total_quantity += q - if (order_quantities[i].group_order_article_id == self.id) - logger.debug("increasing tolerance by #{q}") + if goaq.group_order_article_id == self.id + logger.debug "increasing tolerance by #{q}" tolerance += q end - i += 1 + break if total_quantity >= total end end - logger.debug("determined quantity/tolerance/total: #{quantity} / #{tolerance} / #{quantity + tolerance}") + logger.debug "determined quantity/tolerance/total: #{quantity} / #{tolerance} / #{quantity + tolerance}" end {:quantity => quantity, :tolerance => tolerance, :total => quantity + tolerance} diff --git a/app/views/articles/_form.html.haml b/app/views/articles/_form.html.haml index 78dcdf7c..b63db423 100644 --- a/app/views/articles/_form.html.haml +++ b/app/views/articles/_form.html.haml @@ -7,20 +7,51 @@ .modal-body = f.input :availability = f.input :name - = f.input :origin - = f.input :manufacturer - = f.input :unit + .fold-line + = f.input :unit_quantity, label: Article.human_attribute_name(:unit), + input_html: {class: 'input-mini', title: Article.human_attribute_name(:unit_quantity)} + = f.input :unit, label: '×'.html_safe, + input_html: {class: 'input-mini', title: Article.human_attribute_name(:unit)} + = f.input :note = f.association :article_category + / TODO labels - = f.input :price - = f.input :unit_quantity + .fold-line + = 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 :tax, :wrapper => :append do - = f.input_field :tax - %span.add-on % - = f.input :deposit .modal-footer = link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'} = 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) : ' '); + }); diff --git a/app/views/deliveries/_form.html.haml b/app/views/deliveries/_form.html.haml index 3b500942..40337dde 100644 --- a/app/views/deliveries/_form.html.haml +++ b/app/views/deliveries/_form.html.haml @@ -42,9 +42,9 @@ } if('new' == selectedArticle.id) { $.ajax({ - url: '#{new_stock_article_supplier_deliveries_path(@supplier)}', + url: '#{new_stock_article_path}', type: 'get', - data: {stock_article: {name: selectedArticle.text}}, + data: {stock_article: {name: selectedArticle.text, supplier_id: #{@supplier.id}}}, contentType: 'application/json; charset=UTF-8' }); $('#new_stock_article').select2('data', null); @@ -52,7 +52,7 @@ } if('' != selectedArticle.id) { $.ajax({ - url: '#{derive_stock_article_supplier_deliveries_path(@supplier)}', + url: '#{derive_stock_articles_path}', type: 'get', data: {old_article_id: selectedArticle.id}, contentType: 'application/json; charset=UTF-8' @@ -61,6 +61,26 @@ 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) { @@ -96,7 +116,7 @@ %tr %th{:colspan => 5} - 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 %input#new_stock_article{:style => 'width: 500px;'} %tbody diff --git a/app/views/deliveries/_stock_article_for_adding.html.haml b/app/views/deliveries/_stock_article_for_adding.html.haml index b43df782..8575c121 100644 --- a/app/views/deliveries/_stock_article_for_adding.html.haml +++ b/app/views/deliveries/_stock_article_for_adding.html.haml @@ -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} %td= article.name %td{:data => {:toggle => :tooltip, :title => render(:partial => 'shared/article_price_info', :locals => {:article => article})}}= number_to_currency article.price %td= article.unit %td= article.article_category.name %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_other_price'), copy_stock_article_supplier_deliveries_path(@supplier, :old_stock_article_id => article.id), remote: true, class: 'btn btn-mini' - - deliver_button_disabled = ( @delivery and @delivery.includes_article? article ) ? ( 'disabled' ) : ( false ) + = link_to t('.action_edit'), edit_stock_article_path(article), remote: true, class: 'btn btn-mini' + = link_to t('.action_other_price'), stock_article_copy_path(article), remote: true, class: 'btn btn-mini' = 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 diff --git a/app/views/deliveries/_stock_article_form.html.haml b/app/views/deliveries/_stock_article_form.html.haml deleted file mode 100644 index 5fdce8de..00000000 --- a/app/views/deliveries/_stock_article_form.html.haml +++ /dev/null @@ -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') diff --git a/app/views/deliveries/copy_stock_article.js.erb b/app/views/deliveries/copy_stock_article.js.erb deleted file mode 100644 index de5d260e..00000000 --- a/app/views/deliveries/copy_stock_article.js.erb +++ /dev/null @@ -1,5 +0,0 @@ -$('#modalContainer').html( - '<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>' -); - -$('#modalContainer').modal(); diff --git a/app/views/deliveries/create_stock_article.js.erb b/app/views/deliveries/create_stock_article.js.erb deleted file mode 100644 index cb9d0159..00000000 --- a/app/views/deliveries/create_stock_article.js.erb +++ /dev/null @@ -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'); diff --git a/app/views/deliveries/derive_stock_article.js.erb b/app/views/deliveries/derive_stock_article.js.erb deleted file mode 100644 index de5d260e..00000000 --- a/app/views/deliveries/derive_stock_article.js.erb +++ /dev/null @@ -1,5 +0,0 @@ -$('#modalContainer').html( - '<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>' -); - -$('#modalContainer').modal(); diff --git a/app/views/deliveries/edit_stock_article.js.erb b/app/views/deliveries/edit_stock_article.js.erb deleted file mode 100644 index de5d260e..00000000 --- a/app/views/deliveries/edit_stock_article.js.erb +++ /dev/null @@ -1,5 +0,0 @@ -$('#modalContainer').html( - '<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>' -); - -$('#modalContainer').modal(); diff --git a/app/views/deliveries/form_on_stock_article_create.js.erb b/app/views/deliveries/form_on_stock_article_create.js.erb new file mode 100644 index 00000000..fd1b2d0f --- /dev/null +++ b/app/views/deliveries/form_on_stock_article_create.js.erb @@ -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); diff --git a/app/views/deliveries/form_on_stock_article_update.js.erb b/app/views/deliveries/form_on_stock_article_update.js.erb new file mode 100644 index 00000000..3d78eb0d --- /dev/null +++ b/app/views/deliveries/form_on_stock_article_update.js.erb @@ -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); diff --git a/app/views/deliveries/new_stock_article.js.erb b/app/views/deliveries/new_stock_article.js.erb deleted file mode 100644 index de5d260e..00000000 --- a/app/views/deliveries/new_stock_article.js.erb +++ /dev/null @@ -1,5 +0,0 @@ -$('#modalContainer').html( - '<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>' -); - -$('#modalContainer').modal(); diff --git a/app/views/deliveries/update_stock_article.js.erb b/app/views/deliveries/update_stock_article.js.erb deleted file mode 100644 index 7b2d56d6..00000000 --- a/app/views/deliveries/update_stock_article.js.erb +++ /dev/null @@ -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'); diff --git a/app/views/stockit/_form.html.haml b/app/views/stockit/_form.html.haml index 32e58623..1e56b2c2 100644 --- a/app/views/stockit/_form.html.haml +++ b/app/views/stockit/_form.html.haml @@ -1,18 +1,21 @@ -= simple_form_for stock_article, :validate => true do |f| - = f.association :supplier - = 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 => t('.price_hint') - = f.association :article_category - .form-actions - = f.submit class: 'btn' - = link_to t('ui.or_cancel'), stock_articles_path += simple_form_for stock_article, remote: true, :validate => true do |f| + .modal-header + = link_to t('ui.marks.close').html_safe, '#', class: 'close', data: {dismiss: 'modal'} + %h3= title + .modal-body + = f.association :supplier + = 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') diff --git a/app/views/stockit/_stock_article.html.haml b/app/views/stockit/_stock_article.html.haml new file mode 100644 index 00000000..72d4c510 --- /dev/null +++ b/app/views/stockit/_stock_article.html.haml @@ -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 diff --git a/app/views/stockit/_stock_article_details.html.haml b/app/views/stockit/_stock_article_details.html.haml new file mode 100644 index 00000000..617799e7 --- /dev/null +++ b/app/views/stockit/_stock_article_details.html.haml @@ -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' diff --git a/app/views/stockit/copy.js.erb b/app/views/stockit/copy.js.erb new file mode 100644 index 00000000..e3bc3321 --- /dev/null +++ b/app/views/stockit/copy.js.erb @@ -0,0 +1,9 @@ +$('#modalContainer').html('<%= j(render( + :partial => "form", + :locals => { + :title => t('.title'), + :stock_article => @stock_article + } +)) %>'); + +$('#modalContainer').modal(); diff --git a/app/views/stockit/create.js.erb b/app/views/stockit/create.js.erb new file mode 100644 index 00000000..aa75b2dc --- /dev/null +++ b/app/views/stockit/create.js.erb @@ -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'); diff --git a/app/views/stockit/derive.js.erb b/app/views/stockit/derive.js.erb new file mode 100644 index 00000000..e3bc3321 --- /dev/null +++ b/app/views/stockit/derive.js.erb @@ -0,0 +1,9 @@ +$('#modalContainer').html('<%= j(render( + :partial => "form", + :locals => { + :title => t('.title'), + :stock_article => @stock_article + } +)) %>'); + +$('#modalContainer').modal(); diff --git a/app/views/stockit/destroy.js.erb b/app/views/stockit/destroy.js.erb new file mode 100644 index 00000000..7bba9dbf --- /dev/null +++ b/app/views/stockit/destroy.js.erb @@ -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 %> +}); diff --git a/app/views/stockit/destroy.js.haml b/app/views/stockit/destroy.js.haml deleted file mode 100644 index 8bbde9ed..00000000 --- a/app/views/stockit/destroy.js.haml +++ /dev/null @@ -1,8 +0,0 @@ --# please polish the following line if you know how, same in partial _destroy_fail -var successDiv = $('
'); - -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. diff --git a/app/views/stockit/edit.html.haml b/app/views/stockit/edit.html.haml deleted file mode 100644 index bdf498c6..00000000 --- a/app/views/stockit/edit.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -- title t('.title') - -= render :partial => 'form', :locals => {:stock_article => @stock_article} diff --git a/app/views/stockit/edit.js.erb b/app/views/stockit/edit.js.erb new file mode 100644 index 00000000..e3bc3321 --- /dev/null +++ b/app/views/stockit/edit.js.erb @@ -0,0 +1,9 @@ +$('#modalContainer').html('<%= j(render( + :partial => "form", + :locals => { + :title => t('.title'), + :stock_article => @stock_article + } +)) %>'); + +$('#modalContainer').modal(); diff --git a/app/views/stockit/index.html.haml b/app/views/stockit/index.html.haml index ee65cea4..4ece59d5 100644 --- a/app/views/stockit/index.html.haml +++ b/app/views/stockit/index.html.haml @@ -3,6 +3,30 @@ :javascript $(function() { $('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 @@ -17,7 +41,7 @@ .btn-group = link_to_if @current_user.role_orders?, t('.order_online'), new_order_path(supplier_id: 0), 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('.show_stock_takings'), stock_takings_path, class: 'btn' @@ -42,22 +66,9 @@ %th= heading_helper StockArticle, :supplier %th= heading_helper StockArticle, :article_category %th - %tbody - - for article in @stock_articles - %tr{:class => stock_article_classes(article), :id => "stockArticle-#{article.id}"} - %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 + %tbody#articles-tbody + - for stock_article in @stock_articles + = render :partial => 'stock_article', :locals => {:stock_article => stock_article} %p = t '.stock_worth' = number_to_currency StockArticle.stock_value diff --git a/app/views/stockit/index_on_stock_article_create.js.erb b/app/views/stockit/index_on_stock_article_create.js.erb new file mode 100644 index 00000000..14a3bc90 --- /dev/null +++ b/app/views/stockit/index_on_stock_article_create.js.erb @@ -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); +})(); diff --git a/app/views/stockit/index_on_stock_article_update.js.erb b/app/views/stockit/index_on_stock_article_update.js.erb new file mode 100644 index 00000000..f2f644ba --- /dev/null +++ b/app/views/stockit/index_on_stock_article_update.js.erb @@ -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); +})(); diff --git a/app/views/stockit/new.html.haml b/app/views/stockit/new.html.haml deleted file mode 100644 index 05609a84..00000000 --- a/app/views/stockit/new.html.haml +++ /dev/null @@ -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} diff --git a/app/views/stockit/new.js.erb b/app/views/stockit/new.js.erb new file mode 100644 index 00000000..e3bc3321 --- /dev/null +++ b/app/views/stockit/new.js.erb @@ -0,0 +1,9 @@ +$('#modalContainer').html('<%= j(render( + :partial => "form", + :locals => { + :title => t('.title'), + :stock_article => @stock_article + } +)) %>'); + +$('#modalContainer').modal(); diff --git a/app/views/stockit/show.html.haml b/app/views/stockit/show.html.haml index 5f416bf9..077b6e3a 100644 --- a/app/views/stockit/show.html.haml +++ b/app/views/stockit/show.html.haml @@ -1,32 +1,22 @@ - 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 .span6 - %dl.dl-horizontal - %dt= StockArticle.human_attribute_name 'supplier' - %dd= link_to @stock_article.supplier.name, @stock_article.supplier - %dt= StockArticle.human_attribute_name 'name' - %dd= @stock_article.name - %dt= StockArticle.human_attribute_name 'unit' - %dd= @stock_article.unit - %dt= StockArticle.human_attribute_name 'price' - %dd= number_to_currency @stock_article.price - %dt= StockArticle.human_attribute_name 'tax' - %dd= number_to_percentage @stock_article.tax - %dt= StockArticle.human_attribute_name 'deposit' - %dd= number_to_currency @stock_article.deposit - %dt= StockArticle.human_attribute_name 'fc_price' - %dd= number_to_currency @stock_article.fc_price - %dt= StockArticle.human_attribute_name 'article_category' - %dd= @stock_article.article_category.name - %dt= StockArticle.human_attribute_name 'note' - %dd= @stock_article.note - %dt= StockArticle.human_attribute_name 'quantity' - %dd= @stock_article.quantity - %dt= StockArticle.human_attribute_name 'quantity_available' - %dd= @stock_article.quantity_available - .form-actions - = link_to t('ui.edit'), edit_stock_article_path(@stock_article), class: 'btn' + = render :partial => 'stock_article_details', :locals => {:stock_article => @stock_article} .span6 %h2= t('.stock_changes') diff --git a/app/views/stockit/show_on_stock_article_update.js.erb b/app/views/stockit/show_on_stock_article_update.js.erb new file mode 100644 index 00000000..ce057f89 --- /dev/null +++ b/app/views/stockit/show_on_stock_article_update.js.erb @@ -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) %>'); +})(); diff --git a/app/views/stockit/update.js.erb b/app/views/stockit/update.js.erb new file mode 100644 index 00000000..830d846c --- /dev/null +++ b/app/views/stockit/update.js.erb @@ -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'); diff --git a/config/locales/de.yml b/config/locales/de.yml index bef827b9..6209b266 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -36,7 +36,7 @@ de: created_on: Datum note: Notiz ordergroup: Bestellgruppe - user: + user: Eingetragen von group_order: price: Bestellsumme updated_by: Zuletzt bestellt @@ -71,10 +71,10 @@ de: name: Lieferant note: Notiz starts: Läuft vom - status: + status: Status order_article: missing_units: Fehlende Einheiten - missing_units_short: + missing_units_short: Fehlende units_to_order: Menge update_current_price: Globalen Preis aktualisieren order_comment: @@ -127,7 +127,7 @@ de: done: Erledigt? due_date: Wann erledigen? duration: Dauer - name: + name: Aufgabe required_users: Anzahl user_list: Verantwortlichen workgroup: Arbeitsgruppe @@ -269,7 +269,7 @@ de: application: controller: error_authn: Anmeldung erforderlich! - error_denied: + error_denied: Kein Zugriff! error_members_only: Diese Aktion ist nur für Mitglieder der Gruppe erlaubt! article_categories: create: @@ -385,13 +385,12 @@ de: how_many_units: Wie viele Einheiten (%{unit}) des Artikels »%{name}« liefern? create: notice: Lieferung wurde erstellt. Bitte nicht vergessen die Rechnung anzulegen! - create_stock_article: - notice: Neuer Lagerartikel »%{name}« gespeichert. destroy: notice: Lieferung wurde gelöscht. edit: title: Lieferung bearbeiten 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_stock_article: Lagerartikel anlegen title_fill_quantities: 2. Liefermenge angeben @@ -416,15 +415,11 @@ de: action_add_to_delivery: Liefern action_edit: Bearbeiten action_other_price: Kopieren - stock_article_form: - copy_stock_article: Lagerartikel kopieren stock_change_fields: remove_article: Artikel aus Lieferung entfernen suppliers_overview: Lieferantenübersicht update: notice: Lieferung wurde aktualisiert. - update_stock_article: - notice: Lagerartikel »%{name}« aktualisiert. documents: order_by_articles: filename: Bestellung %{name}-%{date} - Artikelsortierung @@ -708,7 +703,7 @@ de: ordered: Bestellt ordered_title: Menge + Toleranz show_hide: Zeige/Verstecke nicht bestellte Artikel - show_note: + show_note: Notiz zeigen title: Artikelübersicht unit_price: Einzelpreis comment: Kommentare lesen/schreiben @@ -1243,9 +1238,9 @@ de: shared: articles: ordered: Bestellt - ordered_desc: + ordered_desc: Anzahl der Artikel die durch das Mitglied bestellt wurden (Menge + Toleranz) received: Bekommen - received_desc: + received_desc: Anzahl der Artikel die ein Mitglied erhält articles_by_articles: ordergroup: Bestellgruppe 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! update_current_price: Ändert auch den Preis für aktuelle Bestellungen stock_article: - copy_stock_article: + copy: name: Bitte ändern edit_stock_article: price: