make product redistribution work!
This commit is contained in:
parent
2d99141229
commit
c900cf4988
5 changed files with 103 additions and 18 deletions
|
@ -253,6 +253,11 @@ tr.unavailable {
|
||||||
height: inherit;
|
height: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inline form elements
|
||||||
|
.inline {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
// show package icon after amount of package numbers
|
// show package icon after amount of package numbers
|
||||||
.package {
|
.package {
|
||||||
background: url(package-bg.png) no-repeat right center;
|
background: url(package-bg.png) no-repeat right center;
|
||||||
|
|
|
@ -6,19 +6,38 @@ class Finance::ReceiveController < Finance::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
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
|
OrderArticle.transaction do
|
||||||
params[:order_articles].each do |oa_id, oa_params|
|
params[:order_articles].each do |oa_id, oa_params|
|
||||||
unless oa_params.blank?
|
unless oa_params.blank?
|
||||||
oa = OrderArticle.find(oa_id)
|
oa = OrderArticle.find(oa_id)
|
||||||
# update attributes
|
# update attributes; don't use update_attribute because it calls save
|
||||||
oa.update_attributes!(oa_params)
|
# which makes received_changed? not work anymore
|
||||||
# and process consequences
|
oa.attributes = oa_params
|
||||||
oa.redistribute(oa.units_received * oa.price.unit_quantity) unless oa.units_received.blank?
|
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!
|
oa.save!
|
||||||
end
|
end
|
||||||
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
|
redirect_to finance_order_index_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -102,16 +102,50 @@ class OrderArticle < ActiveRecord::Base
|
||||||
(units_to_order * price.unit_quantity) == group_orders_sum[:quantity] rescue false
|
(units_to_order * price.unit_quantity) == group_orders_sum[:quantity] rescue false
|
||||||
end
|
end
|
||||||
|
|
||||||
def redistribute(quantity)
|
# redistribute articles over ordergroups
|
||||||
# recompute
|
# quantity Number of units to distribute
|
||||||
group_order_articles.each {|goa| goa.save_results! quantity }
|
# 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
|
# Update GroupOrder prices & Ordergroup stats
|
||||||
# TODO only affected group_orders, and once after redistributing all articles
|
# TODO only affected group_orders, and once after redistributing all articles
|
||||||
order.group_orders.each(&:update_price!)
|
if update_totals
|
||||||
order.ordergroups.each(&:update_stats!)
|
update_ordergroup_prices
|
||||||
|
order.ordergroups.each(&:update_stats!)
|
||||||
|
end
|
||||||
|
|
||||||
# TODO notifications
|
# TODO notifications
|
||||||
|
|
||||||
|
counts
|
||||||
end
|
end
|
||||||
|
|
||||||
# Updates order_article and belongings during balancing process
|
# 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
|
# updates prices of ALL ordergroups - these are actually too many
|
||||||
# in case of performance issues, update only ordergroups, which ordered this article
|
# 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
|
# 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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,8 +3,21 @@
|
||||||
= form_tag(finance_receive_path(@order), :method => :put) do
|
= form_tag(finance_receive_path(@order), :method => :put) do
|
||||||
%section#results
|
%section#results
|
||||||
= render 'edit_articles'
|
= render 'edit_articles'
|
||||||
|
|
||||||
.form-actions
|
.form-actions
|
||||||
= submit_tag t('.submit'), class: 'btn btn-primary'
|
.pull-left
|
||||||
= link_to t('ui.or_cancel'), finance_order_index_path
|
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
|
%p= link_to_top
|
||||||
|
|
|
@ -67,21 +67,21 @@ describe OrderArticle do
|
||||||
|
|
||||||
it 'does nothing when nothing has changed' do
|
it 'does nothing when nothing has changed' do
|
||||||
set_quantities [3,2], [1,3], [1,0]
|
set_quantities [3,2], [1,3], [1,0]
|
||||||
oa.redistribute 6
|
expect(oa.redistribute 6, [:tolerance, nil]).to eq [1, 0]
|
||||||
goa_reload
|
goa_reload
|
||||||
expect([goa1, goa2, goa3].map(&:result).map(&:to_i)).to eq [4, 1, 1]
|
expect([goa1, goa2, goa3].map(&:result).map(&:to_i)).to eq [4, 1, 1]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'works when there is nothing to distribute' do
|
it 'works when there is nothing to distribute' do
|
||||||
set_quantities [3,2], [1,3], [1,0]
|
set_quantities [3,2], [1,3], [1,0]
|
||||||
oa.redistribute 0
|
expect(oa.redistribute 0, [:tolerance, nil]).to eq [0, 0]
|
||||||
goa_reload
|
goa_reload
|
||||||
expect([goa1, goa2, goa3].map(&:result)).to eq [0, 0, 0]
|
expect([goa1, goa2, goa3].map(&:result)).to eq [0, 0, 0]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'works when quantity needs to be reduced' do
|
it 'works when quantity needs to be reduced' do
|
||||||
set_quantities [3,2], [1,3], [1,0]
|
set_quantities [3,2], [1,3], [1,0]
|
||||||
oa.redistribute 4
|
expect(oa.redistribute 4, [:tolerance, nil]).to eq [0, 0]
|
||||||
goa_reload
|
goa_reload
|
||||||
expect([goa1, goa2, goa3].map(&:result)).to eq [3, 1, 0]
|
expect([goa1, goa2, goa3].map(&:result)).to eq [3, 1, 0]
|
||||||
end
|
end
|
||||||
|
@ -89,18 +89,32 @@ describe OrderArticle do
|
||||||
it 'works when quantity is increased within quantity' do
|
it 'works when quantity is increased within quantity' do
|
||||||
set_quantities [3,0], [2,0], [2,0]
|
set_quantities [3,0], [2,0], [2,0]
|
||||||
expect([goa1, goa2, goa3].map(&:result)).to eq [3, 2, 1]
|
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
|
goa_reload
|
||||||
expect([goa1, goa2, goa3].map(&:result).map(&:to_i)).to eq [3, 2, 2]
|
expect([goa1, goa2, goa3].map(&:result).map(&:to_i)).to eq [3, 2, 2]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'works when there is just one for the first' do
|
it 'works when there is just one for the first' do
|
||||||
set_quantities [3,2], [1,3], [1,0]
|
set_quantities [3,2], [1,3], [1,0]
|
||||||
oa.redistribute 1
|
expect(oa.redistribute 1, [:tolerance, nil]).to eq [0, 0]
|
||||||
goa_reload
|
goa_reload
|
||||||
expect([goa1, goa2, goa3].map(&:result)).to eq [1, 0, 0]
|
expect([goa1, goa2, goa3].map(&:result)).to eq [1, 0, 0]
|
||||||
end
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue