From c900cf4988be085fb21037b89c6602940a43410a Mon Sep 17 00:00:00 2001 From: wvengen Date: Tue, 26 Nov 2013 23:48:21 +0100 Subject: [PATCH] make product redistribution work! --- .../bootstrap_and_overrides.css.less | 5 ++ app/controllers/finance/receive_controller.rb | 29 ++++++++++-- app/models/order_article.rb | 46 ++++++++++++++++--- app/views/finance/receive/edit.html.haml | 17 ++++++- spec/models/order_article_spec.rb | 24 ++++++++-- 5 files changed, 103 insertions(+), 18 deletions(-) diff --git a/app/assets/stylesheets/bootstrap_and_overrides.css.less b/app/assets/stylesheets/bootstrap_and_overrides.css.less index 519614c0..24f6604c 100644 --- a/app/assets/stylesheets/bootstrap_and_overrides.css.less +++ b/app/assets/stylesheets/bootstrap_and_overrides.css.less @@ -253,6 +253,11 @@ tr.unavailable { height: inherit; } +// inline form elements +.inline { + display: inline; +} + // show package icon after amount of package numbers .package { background: url(package-bg.png) no-repeat right center; diff --git a/app/controllers/finance/receive_controller.rb b/app/controllers/finance/receive_controller.rb index 7bab88ca..9afffa85 100644 --- a/app/controllers/finance/receive_controller.rb +++ b/app/controllers/finance/receive_controller.rb @@ -6,19 +6,38 @@ class Finance::ReceiveController < Finance::BaseController end def update + # where to leave remainder during redistribution + rest_to = [] + rest_to << :tolerance if params[:rest_to_tolerance] + rest_to << :stock if params[:rest_to_stock] + rest_to << nil + # count what happens to the articles + counts = [0] * (rest_to.length+2) + cunits = [0] * (rest_to.length+2) OrderArticle.transaction do params[:order_articles].each do |oa_id, oa_params| unless oa_params.blank? oa = OrderArticle.find(oa_id) - # update attributes - oa.update_attributes!(oa_params) - # and process consequences - oa.redistribute(oa.units_received * oa.price.unit_quantity) unless oa.units_received.blank? + # update attributes; don't use update_attribute because it calls save + # which makes received_changed? not work anymore + oa.attributes = oa_params + counts[0] += 1 if oa.units_received_changed? + cunits[0] += oa.units_received * oa.article.unit_quantity + unless oa.units_received.blank? + oacounts = oa.redistribute oa.units_received * oa.price.unit_quantity, rest_to + oacounts.each_with_index {|c,i| cunits[i+1]+=c; counts[i+1]+=1 if c>0 } + end oa.save! end end - flash[:notice] = I18n.t('finance.receive.update.notice') + #flash[:notice] = I18n.t('finance.receive.update.notice') + notice = "Order received:" + notice += " #{counts.shift} articles (#{cunits.shift} units) updated" + notice += ", #{counts.shift} (#{cunits.shift}) using tolerance" if params[:rest_to_tolerance] + notice += ", #{counts.shift} (#{cunits.shift}) go to stock if foodsoft would support that" if params[:rest_to_stock] + notice += ", #{counts.shift} (#{cunits.shift}) left over" + flash[:notice] = notice redirect_to finance_order_index_path end end diff --git a/app/models/order_article.rb b/app/models/order_article.rb index 7e8c4a86..e9e31b2c 100644 --- a/app/models/order_article.rb +++ b/app/models/order_article.rb @@ -102,16 +102,50 @@ class OrderArticle < ActiveRecord::Base (units_to_order * price.unit_quantity) == group_orders_sum[:quantity] rescue false end - def redistribute(quantity) - # recompute - group_order_articles.each {|goa| goa.save_results! quantity } + # redistribute articles over ordergroups + # quantity Number of units to distribute + # surplus What to do when there are more articles than ordered quantity + # :tolerance fill member orders' tolerance + # :stock move to stock + # nil nothing; for catching the remaining count + # update_totals Whether to update group_order and ordergroup totals + # returns array with counts for each surplus method + def redistribute(quantity, surplus = [:tolerance], update_totals = true) + qty_left = quantity + counts = [0] * surplus.length + + if surplus.index(:tolerance).nil? + qty_for_members = [qty_left, self.quantity].min + else + qty_for_members = [qty_left, self.quantity+self.tolerance].min + counts[surplus.index(:tolerance)] = [0, qty_for_members-self.quantity].max + end + + # Recompute + group_order_articles.each {|goa| goa.save_results! qty_for_members } + qty_left -= qty_for_members + + # if there's anything left, move to stock if wanted + if qty_left > 0 and surplus.index(:stock) + counts[surplus.index(:stock)] = qty_left + # 1) find existing stock article with same name, unit, price + # 2) if not found, create new stock article + # avoiding duplicate stock article names + end + if qty_left > 0 and surplus.index(nil) + counts[surplus.index(nil)] = qty_left + end # Update GroupOrder prices & Ordergroup stats # TODO only affected group_orders, and once after redistributing all articles - order.group_orders.each(&:update_price!) - order.ordergroups.each(&:update_stats!) + if update_totals + update_ordergroup_prices + order.ordergroups.each(&:update_stats!) + end # TODO notifications + + counts end # Updates order_article and belongings during balancing process @@ -173,7 +207,7 @@ class OrderArticle < ActiveRecord::Base # updates prices of ALL ordergroups - these are actually too many # in case of performance issues, update only ordergroups, which ordered this article # CAUTION: in after_destroy callback related records (e.g. group_order_articles) are already non-existent - order.group_orders.each { |go| go.update_price! } + order.group_orders.each(&:update_price!) end end diff --git a/app/views/finance/receive/edit.html.haml b/app/views/finance/receive/edit.html.haml index 4be57743..cdeea92f 100644 --- a/app/views/finance/receive/edit.html.haml +++ b/app/views/finance/receive/edit.html.haml @@ -3,8 +3,21 @@ = form_tag(finance_receive_path(@order), :method => :put) do %section#results = render 'edit_articles' + .form-actions - = submit_tag t('.submit'), class: 'btn btn-primary' - = link_to t('ui.or_cancel'), finance_order_index_path + .pull-left + Surplus to + = label_tag :rest_to_tolerance, class: 'inline' do + = check_box_tag :rest_to_tolerance, 1, true + member tolerance, + %span{style: 'color: grey'} + and + = label_tag :rest_to_stock, class: 'inline' do + = check_box_tag :rest_to_stock, 1, false, disabled: true + stock + + .pull-right + = submit_tag t('.submit'), class: 'btn btn-primary' + = link_to t('ui.or_cancel'), finance_order_index_path %p= link_to_top diff --git a/spec/models/order_article_spec.rb b/spec/models/order_article_spec.rb index bc6dcc5c..c46d49ea 100644 --- a/spec/models/order_article_spec.rb +++ b/spec/models/order_article_spec.rb @@ -67,21 +67,21 @@ describe OrderArticle do it 'does nothing when nothing has changed' do set_quantities [3,2], [1,3], [1,0] - oa.redistribute 6 + expect(oa.redistribute 6, [:tolerance, nil]).to eq [1, 0] goa_reload expect([goa1, goa2, goa3].map(&:result).map(&:to_i)).to eq [4, 1, 1] end it 'works when there is nothing to distribute' do set_quantities [3,2], [1,3], [1,0] - oa.redistribute 0 + expect(oa.redistribute 0, [:tolerance, nil]).to eq [0, 0] goa_reload expect([goa1, goa2, goa3].map(&:result)).to eq [0, 0, 0] end it 'works when quantity needs to be reduced' do set_quantities [3,2], [1,3], [1,0] - oa.redistribute 4 + expect(oa.redistribute 4, [:tolerance, nil]).to eq [0, 0] goa_reload expect([goa1, goa2, goa3].map(&:result)).to eq [3, 1, 0] end @@ -89,18 +89,32 @@ describe OrderArticle do it 'works when quantity is increased within quantity' do set_quantities [3,0], [2,0], [2,0] expect([goa1, goa2, goa3].map(&:result)).to eq [3, 2, 1] - oa.redistribute 7 + expect(oa.redistribute 7, [:tolerance, nil]).to eq [0, 0] goa_reload expect([goa1, goa2, goa3].map(&:result).map(&:to_i)).to eq [3, 2, 2] end it 'works when there is just one for the first' do set_quantities [3,2], [1,3], [1,0] - oa.redistribute 1 + expect(oa.redistribute 1, [:tolerance, nil]).to eq [0, 0] goa_reload expect([goa1, goa2, goa3].map(&:result)).to eq [1, 0, 0] end + it 'works when there is tolerance and left-over' do + set_quantities [3,2], [1,1], [1,0] + expect(oa.redistribute 10, [:tolerance, nil]).to eq [3, 2] + goa_reload + expect([goa1, goa2, goa3].map(&:result)).to eq [5, 2, 1] + end + + it 'works when redistributing without tolerance' do + set_quantities [3,2], [1,3], [1,0] + expect(oa.redistribute 8, [nil]).to eq [3] + goa_reload + expect([goa1, goa2, goa3].map(&:result)).to eq [3, 1, 1] + end + end end