Add optional boxfill phase to orders
This commit is contained in:
parent
c1413ff817
commit
a03789e048
13 changed files with 201 additions and 50 deletions
|
@ -40,7 +40,7 @@ class Admin::ConfigsController < Admin::BaseController
|
||||||
# turn recurring rules into something palatable
|
# turn recurring rules into something palatable
|
||||||
def parse_recurring_selects!(config)
|
def parse_recurring_selects!(config)
|
||||||
if config
|
if config
|
||||||
for k in [:pickup, :ends] do
|
for k in [:pickup, :boxfill, :ends] do
|
||||||
if config[k]
|
if config[k]
|
||||||
# allow clearing it using dummy value '{}' ('' would break recurring_select)
|
# allow clearing it using dummy value '{}' ('' would break recurring_select)
|
||||||
if config[k][:recurr].present? && config[k][:recurr] != '{}'
|
if config[k][:recurr].present? && config[k][:recurr] != '{}'
|
||||||
|
|
|
@ -54,7 +54,8 @@ module Admin::ConfigsHelper
|
||||||
checked_value = options.delete(:checked_value) || 'true'
|
checked_value = options.delete(:checked_value) || 'true'
|
||||||
unchecked_value = options.delete(:unchecked_value) || 'false'
|
unchecked_value = options.delete(:unchecked_value) || 'false'
|
||||||
options[:checked] = 'checked' if v=options.delete(:value) && v!='false'
|
options[:checked] = 'checked' if v=options.delete(:value) && v!='false'
|
||||||
form.hidden_field(key, value: unchecked_value, as: :hidden) + form.check_box(key, options, checked_value, false)
|
# different key for hidden field so that allow clocking on label focuses the control
|
||||||
|
form.hidden_field(key, id: "#{key}_", value: unchecked_value, as: :hidden) + form.check_box(key, options, checked_value, false)
|
||||||
elsif options[:as] == :select_recurring
|
elsif options[:as] == :select_recurring
|
||||||
options[:value] = FoodsoftDateUtil.rule_from(options[:value])
|
options[:value] = FoodsoftDateUtil.rule_from(options[:value])
|
||||||
options[:rules] ||= []
|
options[:rules] ||= []
|
||||||
|
@ -111,6 +112,13 @@ module Admin::ConfigsHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @return [String] Tooltip element (span)
|
||||||
|
# @param form [ActionView::Helpers::FormBuilder] Form object.
|
||||||
|
# @param key [Symbol, String] Configuration key of a boolean (e.g. +use_messages+).
|
||||||
|
def config_tooltip(form, key, options={}, &block)
|
||||||
|
content_tag :span, config_input_tooltip_options(form, key, options), &block
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def config_input_tooltip_options(form, key, options)
|
def config_input_tooltip_options(form, key, options)
|
||||||
|
|
|
@ -58,8 +58,10 @@ class GroupOrder < ActiveRecord::Base
|
||||||
group_order_article = group_order_articles.where(order_article_id: order_article.id).first_or_create
|
group_order_article = group_order_articles.where(order_article_id: order_article.id).first_or_create
|
||||||
|
|
||||||
# Get ordered quantities and update group_order_articles/_quantities...
|
# Get ordered quantities and update group_order_articles/_quantities...
|
||||||
quantities = group_order_articles_attributes.fetch(order_article.id.to_s, {:quantity => 0, :tolerance => 0})
|
if group_order_articles_attributes
|
||||||
group_order_article.update_quantities(quantities[:quantity].to_i, quantities[:tolerance].to_i)
|
quantities = group_order_articles_attributes.fetch(order_article.id.to_s, {:quantity => 0, :tolerance => 0})
|
||||||
|
group_order_article.update_quantities(quantities[:quantity].to_i, quantities[:tolerance].to_i)
|
||||||
|
end
|
||||||
|
|
||||||
# Also update results for the order_article
|
# Also update results for the order_article
|
||||||
logger.debug "[save_group_order_articles] update order_article.results!"
|
logger.debug "[save_group_order_articles] update order_article.results!"
|
||||||
|
@ -86,4 +88,3 @@ class GroupOrder < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -199,5 +199,3 @@ class GroupOrderArticle < ActiveRecord::Base
|
||||||
result != result_computed unless result.nil?
|
result != result_computed unless result.nil?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ class Order < ActiveRecord::Base
|
||||||
# Allow separate inputs for date and time
|
# Allow separate inputs for date and time
|
||||||
# with workaround for https://github.com/einzige/date_time_attribute/issues/14
|
# with workaround for https://github.com/einzige/date_time_attribute/issues/14
|
||||||
include DateTimeAttributeValidate
|
include DateTimeAttributeValidate
|
||||||
date_time_attribute :starts, :ends
|
date_time_attribute :starts, :boxfill, :ends
|
||||||
|
|
||||||
def stockit?
|
def stockit?
|
||||||
supplier_id == 0
|
supplier_id == 0
|
||||||
|
@ -92,8 +92,16 @@ class Order < ActiveRecord::Base
|
||||||
state == "closed"
|
state == "closed"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def boxfill?
|
||||||
|
FoodsoftConfig[:use_boxfill] && open? && boxfill.present? && boxfill < Time.now
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_boxfill_useful?
|
||||||
|
FoodsoftConfig[:use_boxfill] && supplier.try(:has_tolerance?)
|
||||||
|
end
|
||||||
|
|
||||||
def expired?
|
def expired?
|
||||||
!ends.nil? && ends < Time.now
|
ends.present? && ends < Time.now
|
||||||
end
|
end
|
||||||
|
|
||||||
# sets up first guess of dates when initializing a new object
|
# sets up first guess of dates when initializing a new object
|
||||||
|
@ -105,7 +113,8 @@ class Order < ActiveRecord::Base
|
||||||
last = (DateTime.parse(FoodsoftConfig[:order_schedule][:initial]) rescue nil)
|
last = (DateTime.parse(FoodsoftConfig[:order_schedule][:initial]) rescue nil)
|
||||||
last ||= Order.finished.reorder(:starts).first.try(:starts)
|
last ||= Order.finished.reorder(:starts).first.try(:starts)
|
||||||
last ||= self.starts
|
last ||= self.starts
|
||||||
# adjust end date
|
# adjust boxfill and end date
|
||||||
|
self.boxfill ||= FoodsoftDateUtil.next_occurrence last, self.starts, FoodsoftConfig[:order_schedule][:boxfill] if is_boxfill_useful?
|
||||||
self.ends ||= FoodsoftDateUtil.next_occurrence last, self.starts, FoodsoftConfig[:order_schedule][:ends]
|
self.ends ||= FoodsoftDateUtil.next_occurrence last, self.starts, FoodsoftConfig[:order_schedule][:ends]
|
||||||
end
|
end
|
||||||
self
|
self
|
||||||
|
@ -251,7 +260,9 @@ class Order < ActiveRecord::Base
|
||||||
|
|
||||||
def starts_before_ends
|
def starts_before_ends
|
||||||
delta = Rails.env.test? ? 1 : 0 # since Rails 4.2 tests appear to have time differences, with this validation failing
|
delta = Rails.env.test? ? 1 : 0 # since Rails 4.2 tests appear to have time differences, with this validation failing
|
||||||
errors.add(:ends, I18n.t('orders.model.error_starts_before_ends')) if (ends && starts && ends <= (starts-delta))
|
errors.add(:ends, I18n.t('orders.model.error_starts_before_ends')) if ends && starts && ends <= (starts-delta)
|
||||||
|
errors.add(:ends, I18n.t('orders.model.error_boxfill_before_ends')) if ends && boxfill && ends <= (boxfill-delta)
|
||||||
|
errors.add(:boxfill, I18n.t('orders.model.error_starts_before_boxfill')) if boxfill && starts && boxfill <= (starts-delta)
|
||||||
end
|
end
|
||||||
|
|
||||||
def include_articles
|
def include_articles
|
||||||
|
@ -288,4 +299,3 @@ class Order < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -42,10 +42,11 @@ class OrderArticle < ActiveRecord::Base
|
||||||
# Update quantity/tolerance/units_to_order from group_order_articles
|
# Update quantity/tolerance/units_to_order from group_order_articles
|
||||||
def update_results!
|
def update_results!
|
||||||
if order.open?
|
if order.open?
|
||||||
quantity = group_order_articles.collect(&:quantity).sum
|
self.quantity = group_order_articles.collect(&:quantity).sum
|
||||||
tolerance = group_order_articles.collect(&:tolerance).sum
|
self.tolerance = group_order_articles.collect(&:tolerance).sum
|
||||||
update_attributes(:quantity => quantity, :tolerance => tolerance,
|
self.units_to_order = calculate_units_to_order(quantity, tolerance)
|
||||||
:units_to_order => calculate_units_to_order(quantity, tolerance))
|
enforce_boxfill if order.boxfill?
|
||||||
|
save!
|
||||||
elsif order.finished?
|
elsif order.finished?
|
||||||
update_attribute(:units_to_order, group_order_articles.collect(&:result).sum)
|
update_attribute(:units_to_order, group_order_articles.collect(&:result).sum)
|
||||||
end
|
end
|
||||||
|
@ -186,10 +187,11 @@ class OrderArticle < ActiveRecord::Base
|
||||||
|
|
||||||
# @return [Number] Units missing for the last +unit_quantity+ of the article.
|
# @return [Number] Units missing for the last +unit_quantity+ of the article.
|
||||||
def missing_units
|
def missing_units
|
||||||
units = price.unit_quantity - ((quantity % price.unit_quantity) + tolerance)
|
_missing_units(price.unit_quantity, quantity, tolerance)
|
||||||
units = 0 if units < 0
|
end
|
||||||
units = 0 if units == price.unit_quantity
|
|
||||||
units
|
def missing_units_was
|
||||||
|
_missing_units(price.unit_quantity, quantity_was, tolerance_was)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check if the result of any associated GroupOrderArticle was overridden manually
|
# Check if the result of any associated GroupOrderArticle was overridden manually
|
||||||
|
@ -219,5 +221,26 @@ class OrderArticle < ActiveRecord::Base
|
||||||
order.group_orders.each(&:update_price!)
|
order.group_orders.each(&:update_price!)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
# Throws an exception when the changed article decreases the amount of filled boxes.
|
||||||
|
def enforce_boxfill
|
||||||
|
# Either nothing changes, or
|
||||||
|
# missing_units becomes less and the amount doesn't decrease, or
|
||||||
|
# tolerance was moved to quantity. Only then are changes allowed in the boxfill phase.
|
||||||
|
delta_q = quantity - quantity_was
|
||||||
|
delta_t = tolerance - tolerance_was
|
||||||
|
delta_mis = missing_units - missing_units_was
|
||||||
|
delta_box = units_to_order - units_to_order_was
|
||||||
|
unless (delta_q == 0 && delta_t == 0) ||
|
||||||
|
(delta_mis < 0 && delta_box >= 0 && delta_t >= 0) ||
|
||||||
|
(delta_q > 0 && delta_q == -delta_t)
|
||||||
|
raise ActiveRecord::RecordNotSaved.new("Change not acceptable in boxfill phase, sorry.", self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def _missing_units(unit_quantity, quantity, tolerance)
|
||||||
|
units = unit_quantity - ((quantity % unit_quantity) + tolerance)
|
||||||
|
units = 0 if units < 0
|
||||||
|
units = 0 if units == unit_quantity
|
||||||
|
units
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -116,6 +116,11 @@ class Supplier < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @return [Boolean] Whether there are articles that would use tolerance (unit_quantity > 1)
|
||||||
|
def has_tolerance?
|
||||||
|
articles.where('articles.unit_quantity > 1').any?
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
# make sure the shared_sync_method is allowed for the shared supplier
|
# make sure the shared_sync_method is allowed for the shared supplier
|
||||||
|
|
|
@ -11,8 +11,18 @@
|
||||||
%span.add-on= t 'number.currency.format.unit'
|
%span.add-on= t 'number.currency.format.unit'
|
||||||
= config_input_field form, :minimum_balance, as: :decimal, class: 'input-small'
|
= config_input_field form, :minimum_balance, as: :decimal, class: 'input-small'
|
||||||
|
|
||||||
|
%h4= t '.schedule_title'
|
||||||
= form.simple_fields_for :order_schedule do |fields|
|
= form.simple_fields_for :order_schedule do |fields|
|
||||||
|
#boxfill-schedule.collapse{class: ('in' if FoodsoftConfig[:use_boxfill])}
|
||||||
|
= fields.simple_fields_for 'boxfill' do |fields|
|
||||||
|
.fold-line
|
||||||
|
= config_input fields, 'recurr', as: :select_recurring, input_html: {class: 'input-xlarge'}
|
||||||
|
= config_input fields, 'time', input_html: {class: 'input-mini'}
|
||||||
= fields.simple_fields_for 'ends' do |fields|
|
= fields.simple_fields_for 'ends' do |fields|
|
||||||
.fold-line
|
.fold-line
|
||||||
= config_input fields, 'recurr', as: :select_recurring, input_html: {class: 'input-xlarge'}, allow_blank: true
|
= config_input fields, 'recurr', as: :select_recurring, input_html: {class: 'input-xlarge'}, allow_blank: true
|
||||||
= config_input fields, 'time', input_html: {class: 'input-mini'}
|
= config_input fields, 'time', input_html: {class: 'input-mini'}
|
||||||
|
-# can't use collapse and tooltip on same element :/
|
||||||
|
= config_input form, :use_boxfill, as: :boolean do
|
||||||
|
= config_tooltip form, :use_boxfill do
|
||||||
|
= config_input_field form, :use_boxfill, as: :boolean, title: '', data: {toggle: 'collapse', target: '#boxfill-schedule'}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
= f.hidden_field :supplier_id
|
= f.hidden_field :supplier_id
|
||||||
.fold-line
|
.fold-line
|
||||||
= f.input :starts, as: :date_picker_time
|
= f.input :starts, as: :date_picker_time
|
||||||
|
= f.input :boxfill, as: :date_picker_time if @order.is_boxfill_useful?
|
||||||
= f.input :ends, as: :date_picker_time
|
= f.input :ends, as: :date_picker_time
|
||||||
= f.input :note, input_html: {rows: 2, class: 'input-xxlarge'}
|
= f.input :note, input_html: {rows: 2, class: 'input-xxlarge'}
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,7 @@ en:
|
||||||
sent_to_all: Send to all members
|
sent_to_all: Send to all members
|
||||||
subject: Subject
|
subject: Subject
|
||||||
order:
|
order:
|
||||||
|
boxfill: Fill boxes after
|
||||||
closed_by: Settled by
|
closed_by: Settled by
|
||||||
created_by: Created by
|
created_by: Created by
|
||||||
ends: Ends at
|
ends: Ends at
|
||||||
|
@ -237,6 +238,8 @@ en:
|
||||||
pdf_title: PDF documents
|
pdf_title: PDF documents
|
||||||
tab_messages:
|
tab_messages:
|
||||||
emails_title: Sending email
|
emails_title: Sending email
|
||||||
|
tab_payment:
|
||||||
|
schedule_title: Ordering schedule
|
||||||
tab_tasks:
|
tab_tasks:
|
||||||
periodic_title: Periodic tasks
|
periodic_title: Periodic tasks
|
||||||
tabs:
|
tabs:
|
||||||
|
@ -478,6 +481,9 @@ en:
|
||||||
ends:
|
ends:
|
||||||
recurr: Schedule for default order closing date.
|
recurr: Schedule for default order closing date.
|
||||||
time: Default time when orders are closed.
|
time: Default time when orders are closed.
|
||||||
|
boxfill:
|
||||||
|
recurr: Schedule for when the box-fill phase starts by default.
|
||||||
|
time: Default time when the box-fill phase of the ordering starts.
|
||||||
initial: Schedule starts at this date.
|
initial: Schedule starts at this date.
|
||||||
page_footer: Shown on each page at the bottom. Enter "blank" to disable the footer completely.
|
page_footer: Shown on each page at the bottom. Enter "blank" to disable the footer completely.
|
||||||
pdf_add_page_breaks:
|
pdf_add_page_breaks:
|
||||||
|
@ -492,6 +498,7 @@ en:
|
||||||
tax_default: Default VAT percentage for new articles.
|
tax_default: Default VAT percentage for new articles.
|
||||||
tolerance_is_costly: Order as much of the member tolerance as possible (compared to only as much needed to fill the last box). Enabling this also includes the tolerance in the total price of the open member order.
|
tolerance_is_costly: Order as much of the member tolerance as possible (compared to only as much needed to fill the last box). Enabling this also includes the tolerance in the total price of the open member order.
|
||||||
use_apple_points: When the apple point system is enabled, members are required to do some tasks to be able to keep ordering.
|
use_apple_points: When the apple point system is enabled, members are required to do some tasks to be able to keep ordering.
|
||||||
|
use_boxfill: When enabled, near end of an order, members are only able to change their order when increases the total amount ordered. This helps to fill any remaining boxes. You still need to set a box-fill date for the orders.
|
||||||
use_messages: Allow members to communicate with each other within Foodsoft.
|
use_messages: Allow members to communicate with each other within Foodsoft.
|
||||||
use_nick: Show and use nicknames instead of real names. When enabling this, please check that each user has a nickname.
|
use_nick: Show and use nicknames instead of real names. When enabling this, please check that each user has a nickname.
|
||||||
use_wiki: Enable editable wiki pages.
|
use_wiki: Enable editable wiki pages.
|
||||||
|
@ -523,6 +530,9 @@ en:
|
||||||
ends:
|
ends:
|
||||||
recurr: Order ends
|
recurr: Order ends
|
||||||
time: time
|
time: time
|
||||||
|
boxfill:
|
||||||
|
recurr: Box fill after
|
||||||
|
time: time
|
||||||
initial: Schedule start
|
initial: Schedule start
|
||||||
page_footer: Page footer
|
page_footer: Page footer
|
||||||
pdf_add_page_breaks: Page breaks
|
pdf_add_page_breaks: Page breaks
|
||||||
|
@ -536,6 +546,7 @@ en:
|
||||||
time_zone: Time zone
|
time_zone: Time zone
|
||||||
tolerance_is_costly: Tolerance is costly
|
tolerance_is_costly: Tolerance is costly
|
||||||
use_apple_points: Apple points
|
use_apple_points: Apple points
|
||||||
|
use_boxfill: Box-fill phase
|
||||||
use_messages: Messages
|
use_messages: Messages
|
||||||
use_nick: Use nicknames
|
use_nick: Use nicknames
|
||||||
use_wiki: Enable wiki
|
use_wiki: Enable wiki
|
||||||
|
@ -1279,6 +1290,8 @@ en:
|
||||||
close_direct_message: Order settled without charging member accounts.
|
close_direct_message: Order settled without charging member accounts.
|
||||||
error_closed: Order was already settled
|
error_closed: Order was already settled
|
||||||
error_nosel: At least one article must be selected. You may want to delete the order instead?
|
error_nosel: At least one article must be selected. You may want to delete the order instead?
|
||||||
|
error_boxfill_before_ends: must be after the box-fill date (or remain empty)
|
||||||
|
error_starts_before_boxfill: must be after the start date (or remain empty)
|
||||||
error_starts_before_ends: must be after the start date (or remain empty)
|
error_starts_before_ends: must be after the start date (or remain empty)
|
||||||
notice_close: 'Order: %{name}, until %{ends}'
|
notice_close: 'Order: %{name}, until %{ends}'
|
||||||
stock: Stock
|
stock: Stock
|
||||||
|
|
5
db/migrate/20150923190747_add_boxfill_to_order.rb
Normal file
5
db/migrate/20150923190747_add_boxfill_to_order.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
class AddBoxfillToOrder < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :orders, :boxfill, :datetime
|
||||||
|
end
|
||||||
|
end
|
23
db/schema.rb
23
db/schema.rb
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20150301000000) do
|
ActiveRecord::Schema.define(version: 20150923190747) do
|
||||||
|
|
||||||
create_table "article_categories", force: :cascade do |t|
|
create_table "article_categories", force: :cascade do |t|
|
||||||
t.string "name", limit: 255, default: "", null: false
|
t.string "name", limit: 255, default: "", null: false
|
||||||
|
@ -37,7 +37,7 @@ ActiveRecord::Schema.define(version: 20150301000000) do
|
||||||
t.integer "article_category_id", limit: 4, default: 0, null: false
|
t.integer "article_category_id", limit: 4, default: 0, null: false
|
||||||
t.string "unit", limit: 255, default: "", null: false
|
t.string "unit", limit: 255, default: "", null: false
|
||||||
t.string "note", limit: 255
|
t.string "note", limit: 255
|
||||||
t.boolean "availability", limit: 1, default: true, null: false
|
t.boolean "availability", default: true, null: false
|
||||||
t.string "manufacturer", limit: 255
|
t.string "manufacturer", limit: 255
|
||||||
t.string "origin", limit: 255
|
t.string "origin", limit: 255
|
||||||
t.datetime "shared_updated_on"
|
t.datetime "shared_updated_on"
|
||||||
|
@ -61,7 +61,7 @@ ActiveRecord::Schema.define(version: 20150301000000) do
|
||||||
create_table "assignments", force: :cascade do |t|
|
create_table "assignments", force: :cascade do |t|
|
||||||
t.integer "user_id", limit: 4, default: 0, null: false
|
t.integer "user_id", limit: 4, default: 0, null: false
|
||||||
t.integer "task_id", limit: 4, default: 0, null: false
|
t.integer "task_id", limit: 4, default: 0, null: false
|
||||||
t.boolean "accepted", limit: 1, default: false
|
t.boolean "accepted", default: false
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "assignments", ["user_id", "task_id"], name: "index_assignments_on_user_id_and_task_id", unique: true, using: :btree
|
add_index "assignments", ["user_id", "task_id"], name: "index_assignments_on_user_id_and_task_id", unique: true, using: :btree
|
||||||
|
@ -127,18 +127,18 @@ ActiveRecord::Schema.define(version: 20150301000000) do
|
||||||
t.string "description", limit: 255
|
t.string "description", limit: 255
|
||||||
t.decimal "account_balance", precision: 12, scale: 2, default: 0, null: false
|
t.decimal "account_balance", precision: 12, scale: 2, default: 0, null: false
|
||||||
t.datetime "created_on", null: false
|
t.datetime "created_on", null: false
|
||||||
t.boolean "role_admin", limit: 1, default: false, null: false
|
t.boolean "role_admin", default: false, null: false
|
||||||
t.boolean "role_suppliers", limit: 1, default: false, null: false
|
t.boolean "role_suppliers", default: false, null: false
|
||||||
t.boolean "role_article_meta", limit: 1, default: false, null: false
|
t.boolean "role_article_meta", default: false, null: false
|
||||||
t.boolean "role_finance", limit: 1, default: false, null: false
|
t.boolean "role_finance", default: false, null: false
|
||||||
t.boolean "role_orders", limit: 1, default: false, null: false
|
t.boolean "role_orders", default: false, null: false
|
||||||
t.datetime "deleted_at"
|
t.datetime "deleted_at"
|
||||||
t.string "contact_person", limit: 255
|
t.string "contact_person", limit: 255
|
||||||
t.string "contact_phone", limit: 255
|
t.string "contact_phone", limit: 255
|
||||||
t.string "contact_address", limit: 255
|
t.string "contact_address", limit: 255
|
||||||
t.text "stats", limit: 65535
|
t.text "stats", limit: 65535
|
||||||
t.integer "next_weekly_tasks_number", limit: 4, default: 8
|
t.integer "next_weekly_tasks_number", limit: 4, default: 8
|
||||||
t.boolean "ignore_apple_restriction", limit: 1, default: false
|
t.boolean "ignore_apple_restriction", default: false
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "groups", ["name"], name: "index_groups_on_name", unique: true, using: :btree
|
add_index "groups", ["name"], name: "index_groups_on_name", unique: true, using: :btree
|
||||||
|
@ -184,7 +184,7 @@ ActiveRecord::Schema.define(version: 20150301000000) do
|
||||||
t.string "subject", limit: 255, null: false
|
t.string "subject", limit: 255, null: false
|
||||||
t.text "body", limit: 65535
|
t.text "body", limit: 65535
|
||||||
t.integer "email_state", limit: 4, default: 0, null: false
|
t.integer "email_state", limit: 4, default: 0, null: false
|
||||||
t.boolean "private", limit: 1, default: false
|
t.boolean "private", default: false
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.integer "reply_to", limit: 4
|
t.integer "reply_to", limit: 4
|
||||||
t.integer "group_id", limit: 4
|
t.integer "group_id", limit: 4
|
||||||
|
@ -224,6 +224,7 @@ ActiveRecord::Schema.define(version: 20150301000000) do
|
||||||
t.integer "updated_by_user_id", limit: 4
|
t.integer "updated_by_user_id", limit: 4
|
||||||
t.decimal "foodcoop_result", precision: 8, scale: 2
|
t.decimal "foodcoop_result", precision: 8, scale: 2
|
||||||
t.integer "created_by_user_id", limit: 4
|
t.integer "created_by_user_id", limit: 4
|
||||||
|
t.datetime "boxfill"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "orders", ["state"], name: "index_orders_on_state", using: :btree
|
add_index "orders", ["state"], name: "index_orders_on_state", using: :btree
|
||||||
|
@ -316,7 +317,7 @@ ActiveRecord::Schema.define(version: 20150301000000) do
|
||||||
t.string "name", limit: 255, default: "", null: false
|
t.string "name", limit: 255, default: "", null: false
|
||||||
t.string "description", limit: 255
|
t.string "description", limit: 255
|
||||||
t.date "due_date"
|
t.date "due_date"
|
||||||
t.boolean "done", limit: 1, default: false
|
t.boolean "done", default: false
|
||||||
t.integer "workgroup_id", limit: 4
|
t.integer "workgroup_id", limit: 4
|
||||||
t.datetime "created_on", null: false
|
t.datetime "created_on", null: false
|
||||||
t.datetime "updated_on", null: false
|
t.datetime "updated_on", null: false
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe OrderArticle do
|
describe OrderArticle do
|
||||||
let(:order) { FactoryGirl.create :order, article_count: 1 }
|
let(:order) { create :order, article_count: 1 }
|
||||||
let(:oa) { order.order_articles.first }
|
let(:oa) { order.order_articles.first }
|
||||||
|
|
||||||
it 'is not ordered by default' do
|
it 'is not ordered by default' do
|
||||||
|
@ -34,15 +34,15 @@ describe OrderArticle do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'redistribution' do
|
describe 'redistribution' do
|
||||||
let(:admin) { FactoryGirl.create :user, groups:[FactoryGirl.create(:workgroup, role_finance: true)] }
|
let(:admin) { create :user, groups:[create(:workgroup, role_finance: true)] }
|
||||||
let(:article) { FactoryGirl.create :article, unit_quantity: 3 }
|
let(:article) { create :article, unit_quantity: 3 }
|
||||||
let(:order) { FactoryGirl.create :order, article_ids: [article.id] }
|
let(:order) { create :order, article_ids: [article.id] }
|
||||||
let(:go1) { FactoryGirl.create :group_order, order: order }
|
let(:go1) { create :group_order, order: order }
|
||||||
let(:go2) { FactoryGirl.create :group_order, order: order }
|
let(:go2) { create :group_order, order: order }
|
||||||
let(:go3) { FactoryGirl.create :group_order, order: order }
|
let(:go3) { create :group_order, order: order }
|
||||||
let(:goa1) { FactoryGirl.create :group_order_article, group_order: go1, order_article: oa }
|
let(:goa1) { create :group_order_article, group_order: go1, order_article: oa }
|
||||||
let(:goa2) { FactoryGirl.create :group_order_article, group_order: go2, order_article: oa }
|
let(:goa2) { create :group_order_article, group_order: go2, order_article: oa }
|
||||||
let(:goa3) { FactoryGirl.create :group_order_article, group_order: go3, order_article: oa }
|
let(:goa3) { create :group_order_article, group_order: go3, order_article: oa }
|
||||||
|
|
||||||
# set quantities of group_order_articles
|
# set quantities of group_order_articles
|
||||||
def set_quantities(q1, q2, q3)
|
def set_quantities(q1, q2, q3)
|
||||||
|
@ -117,4 +117,80 @@ describe OrderArticle do
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'boxfill' do
|
||||||
|
before { FoodsoftConfig[:use_boxfill] = true }
|
||||||
|
let(:article) { create :article, unit_quantity: 6 }
|
||||||
|
let(:order) { create :order, article_ids: [article.id], starts: 1.week.ago }
|
||||||
|
let(:oa) { order.order_articles.first }
|
||||||
|
let(:go) { create :group_order, order: order }
|
||||||
|
let(:goa) { create :group_order_article, group_order: go, order_article: oa }
|
||||||
|
|
||||||
|
shared_examples "boxfill" do |success, q|
|
||||||
|
# initial situation
|
||||||
|
before do
|
||||||
|
goa.update_quantities *q.keys[0]
|
||||||
|
oa.update_results!; oa.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
# check starting condition
|
||||||
|
it '(before)' do
|
||||||
|
expect([oa.quantity, oa.tolerance, oa.missing_units]).to eq q.keys[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
# actual test
|
||||||
|
it (success ? 'succeeds' : 'fails') do
|
||||||
|
order.update_attributes(boxfill: boxfill_from)
|
||||||
|
|
||||||
|
r = proc {
|
||||||
|
goa.update_quantities *q.values[0]
|
||||||
|
oa.update_results!
|
||||||
|
}
|
||||||
|
if success
|
||||||
|
r.call
|
||||||
|
else
|
||||||
|
expect(r).to raise_error(ActiveRecord::RecordNotSaved)
|
||||||
|
end
|
||||||
|
|
||||||
|
oa.reload
|
||||||
|
expect([oa.quantity, oa.tolerance, oa.missing_units]).to eq q.values[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'before the date' do
|
||||||
|
let(:boxfill_from) { 1.hour.from_now }
|
||||||
|
context 'decreasing the missing units' do
|
||||||
|
include_examples "boxfill", true, [6,0]=>[5,0], [6,0,0]=>[5,0,1]
|
||||||
|
end
|
||||||
|
context 'decreasing the tolerance' do
|
||||||
|
include_examples "boxfill", true, [1,2]=>[1,1], [1,2,3]=>[1,1,4]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'after the date' do
|
||||||
|
let(:boxfill_from) { 1.second.ago }
|
||||||
|
context 'changing nothing in particular' do
|
||||||
|
include_examples "boxfill", true, [4,1]=>[4,1], [4,1,1]=>[4,1,1]
|
||||||
|
end
|
||||||
|
context 'increasing missing units' do
|
||||||
|
include_examples "boxfill", false, [3,0]=>[2,0], [3,0,3]=>[3,0,3]
|
||||||
|
end
|
||||||
|
context 'increasing tolerance' do
|
||||||
|
include_examples "boxfill", true, [2,1]=>[2,2], [2,1,3]=>[2,2,2]
|
||||||
|
end
|
||||||
|
context 'decreasing quantity to fix missing units' do
|
||||||
|
include_examples "boxfill", true, [7,0]=>[6,0], [7,0,5]=>[6,0,0]
|
||||||
|
end
|
||||||
|
context 'decreasing quantity keeping missing units equal' do
|
||||||
|
include_examples "boxfill", false, [7,0]=>[1,0], [7,0,5]=>[7,0,5]
|
||||||
|
end
|
||||||
|
context 'moving tolerance to quantity' do
|
||||||
|
include_examples "boxfill", true, [4,2]=>[6,0], [4,2,0]=>[6,0,0]
|
||||||
|
end
|
||||||
|
# @todo enable test when tolerance doesn't count in missing_units
|
||||||
|
#context 'decreasing tolerance' do
|
||||||
|
# include_examples "boxfill", false, [0,2]=>[0,0], [0,2,0]=>[0,2,0]
|
||||||
|
#end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue