Merge pull request #3293 from luisramos0/2-0-stable-jan-8th

[Spree Upgrade] Merging master into 2-0-stable (first run in Jan2019)
This commit is contained in:
Pau Pérez Fabregat
2019-01-09 16:11:18 +01:00
committed by GitHub
42 changed files with 978 additions and 145 deletions

3
.gitignore vendored
View File

@@ -41,3 +41,6 @@ libpeerconnection.log
node_modules
vendor/bundle/
coverage
/reports/
!/reports/README.md
bin/

View File

@@ -187,7 +187,7 @@ Metrics/AbcSize:
Max: 15
Metrics/BlockLength:
ExcludedMethods: ["context", "describe", "it"]
ExcludedMethods: ["collection", "context", "describe", "it", "member", "namespace", "resource", "resources"]
Metrics/BlockNesting:
Max: 3

View File

@@ -840,7 +840,6 @@ Layout/SpaceInsideHashLiteralBraces:
- 'spec/features/admin/reports_spec.rb'
- 'spec/features/consumer/shopping/checkout_spec.rb'
- 'spec/helpers/checkout_helper_spec.rb'
- 'spec/helpers/i18n_helper_spec.rb'
- 'spec/helpers/order_cycles_helper_spec.rb'
- 'spec/lib/open_food_network/enterprise_fee_calculator_spec.rb'
- 'spec/lib/open_food_network/feature_toggle_spec.rb'

View File

@@ -9,9 +9,6 @@ addons:
# Set the timezone for phantomjs with TZ
# Set the timezone for karma with TIMEZONE
#
# The test cases are roughly split according to their test times.
# It would be better to use https://github.com/ArturT/knapsack.
env:
global:
- TZ="Australia/Melbourne"
@@ -21,7 +18,7 @@ env:
- CI_NODE_INDEX=0
- CI_NODE_INDEX=1
- CI_NODE_INDEX=2
- CI_NODE_INDEX=3
- CI_NODE_INDEX=3 RSPEC_ENGINES="true"
- CI_NODE_INDEX=4 KARMA="true" GITHUB_DEPLOY="true"
before_script:
@@ -42,6 +39,7 @@ before_script:
script:
- 'if [ "$KARMA" = "true" ]; then bundle exec rake karma:run; else echo "Skipping karma run"; fi'
- 'if [ "$RSPEC_ENGINES" = "true" ]; then bundle exec rake openfoodnetwork:specs:engines:rspec; else echo "Skipping RSpec run in engines"; fi'
- "bundle exec rake 'knapsack:rspec[--format progress --tag ~performance]'"
after_success:

View File

@@ -69,10 +69,14 @@ Tests, both unit and integration, are based on RSpec. To run the test suite, fir
bundle exec rake db:test:prepare
Then the tests can be run with:
Then the main application tests can be run with:
bundle exec rspec spec
The tests of all custom engines can be run with:
bundle exec rake openfoodnetwork:specs:engines:rspec
Note: If your OS is not explicitly supported in the setup guides then not all tests may pass. However, you may still be able to develop. Get in touch with the [#dev][slack-dev] channel on Slack to troubleshoot issues and determine if they will preclude you from contributing to OFN.
Note: The time zone on your machine should match the one defined in `config/application.yml`.

View File

@@ -67,7 +67,7 @@ gem 'spinjs-rails'
gem 'rack-ssl', require: 'rack/ssl'
gem 'rack-rewrite'
gem 'custom_error_message', github: 'jeremydurham/custom-err-msg'
gem 'angularjs-file-upload-rails', '~> 1.1.6'
gem 'angularjs-file-upload-rails', '~> 2.4.1'
gem 'roadie-rails', '~> 1.1.1'
gem 'figaro'
gem 'blockenspiel'
@@ -149,6 +149,8 @@ group :development do
gem 'guard-rails'
gem 'guard-rspec', '~> 4.7.3'
gem 'rubocop', '>= 0.49.1'
gem 'spring', '=1.1.3'
gem 'spring-commands-rspec'
# 1.0.9 fixed openssl issues on macOS https://github.com/eventmachine/eventmachine/issues/602
# While we don't require this gem directly, no dependents forced the upgrade to a version

View File

@@ -172,7 +172,7 @@ GEM
railties (>= 3.1)
sprockets (~> 2.0)
tilt
angularjs-file-upload-rails (1.1.6)
angularjs-file-upload-rails (2.4.1)
angularjs-rails (1.5.5)
arel (3.0.3)
ast (2.4.0)
@@ -444,10 +444,10 @@ GEM
ruby-progressbar (~> 1.4)
geocoder (1.1.8)
gmaps4rails (1.5.6)
guard (2.14.1)
guard (2.15.0)
formatador (>= 0.2.4)
listen (>= 2.7, < 4.0)
lumberjack (~> 1.0)
lumberjack (>= 1.0.12, < 2.0)
nenv (~> 0.1)
notiffany (~> 0.0)
pry (>= 0.9.12)
@@ -507,17 +507,17 @@ GEM
listen (3.0.8)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
lumberjack (1.0.12)
lumberjack (1.0.13)
mail (2.5.5)
mime-types (~> 1.16)
treetop (~> 1.4.8)
method_source (0.9.0)
method_source (0.9.2)
mime-types (1.25.1)
mini_mime (1.0.1)
mini_portile2 (2.1.0)
mini_racer (0.1.15)
libv8 (~> 6.3)
momentjs-rails (2.5.1)
momentjs-rails (2.20.1)
railties (>= 3.1)
money (5.1.1)
i18n (~> 0.6.0)
@@ -566,7 +566,7 @@ GEM
activerecord (~> 3.0)
polyglot (0.3.5)
powerpack (0.1.1)
pry (0.11.2)
pry (0.12.2)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
pry-byebug (3.4.3)
@@ -605,7 +605,7 @@ GEM
thor (>= 0.14.6, < 2.0)
rainbow (3.0.0)
raindrops (0.19.0)
rake (12.3.1)
rake (12.3.2)
ransack (0.7.2)
actionpack (~> 3.0)
activerecord (~> 3.0)
@@ -691,6 +691,9 @@ GEM
rails (>= 3.1)
spreadsheet (1.1.7)
ruby-ole (>= 1.0)
spring (1.1.3)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
sprockets (2.2.3)
hike (~> 1.2)
multi_json (~> 1.0)
@@ -728,7 +731,7 @@ GEM
railties (>= 3.0)
warden (1.2.7)
rack (>= 1.0)
webmock (3.4.2)
webmock (3.5.1)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
@@ -739,7 +742,7 @@ GEM
activesupport (>= 2.3.4)
chronic (>= 0.6.3)
wicked_pdf (1.1.0)
wkhtmltopdf-binary (0.12.3.1)
wkhtmltopdf-binary (0.12.4)
xml-simple (1.1.5)
xpath (2.1.0)
nokogiri (~> 1.3)
@@ -753,7 +756,7 @@ DEPENDENCIES
acts-as-taggable-on (~> 3.4)
andand
angular-rails-templates (~> 0.3.0)
angularjs-file-upload-rails (~> 1.1.6)
angularjs-file-upload-rails (~> 2.4.1)
angularjs-rails (= 1.5.5)
atomic
awesome_print
@@ -834,6 +837,8 @@ DEPENDENCIES
spree_auth_devise!
spree_i18n!
spree_paypal_express!
spring (= 1.1.3)
spring-commands-rspec
stripe (~> 3.3.2)
timecop
truncate_html

View File

@@ -13,6 +13,11 @@ angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl",
$scope.RequestMonitor = RequestMonitor
$scope.selectView = Views.selectView
$scope.currentView = -> Views.currentView
$scope.onDemandOptions = [
{ description: t('js.variant_overrides.on_demand.use_producer_settings'), value: null },
{ description: t('js.variant_overrides.on_demand.yes'), value: true },
{ description: t('js.variant_overrides.on_demand.no'), value: false }
]
$scope.views = Views.setViews
inventory: { name: t('js.variant_overrides.inventory_products'), visible: true }
@@ -105,3 +110,35 @@ angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl",
StatusMessage.display 'success', t('js.variant_overrides.stock_reset')
.error (data, status) ->
$timeout -> StatusMessage.display 'failure', $scope.updateError(data, status)
# Variant override count_on_hand field placeholder logic:
# on_demand true -- Show "On Demand"
# on_demand false -- Show empty value to be set by the user
# on_demand nil -- Show producer on_hand value
$scope.countOnHandPlaceholder = (variant, hubId) ->
variantOverride = $scope.variantOverrides[hubId][variant.id]
if variantOverride.on_demand
t('js.variants.on_demand.yes')
else if variantOverride.on_demand == false
''
else
variant.on_hand
# This method should only be used when the variant override on_demand is changed.
#
# Change the count_on_hand value to a suggested value.
$scope.updateCountOnHand = (variant, hubId) ->
variantOverride = $scope.variantOverrides[hubId][variant.id]
suggested = $scope.countOnHandSuggestion(variant, hubId)
return if suggested == variantOverride.count_on_hand
variantOverride.count_on_hand = suggested
DirtyVariantOverrides.set hubId, variant.id, variantOverride.id, 'count_on_hand', suggested
# Suggest producer count_on_hand if variant has limited stock and variant override forces limited
# stock. Otherwise, clear whatever value is set.
$scope.countOnHandSuggestion = (variant, hubId) ->
variantOverride = $scope.variantOverrides[hubId][variant.id]
return null unless !variant.on_demand && variantOverride.on_demand == false
variant.on_hand

View File

@@ -0,0 +1,10 @@
@import '../../darkswarm/branding';
.container {
input {
&[readonly] {
background-color: $disabled-light;
cursor: default;
}
}
}

View File

@@ -5,25 +5,29 @@ class UserRegistrationsController < Spree::UserRegistrationsController
before_filter :set_checkout_redirect, only: :create
include I18nHelper
before_filter :set_locale
# POST /resource/sign_up
def create
@user = build_resource(params[:spree_user])
if resource.save
session[:spree_user_signup] = true
session[:confirmation_return_url] = params[:return_url]
associate_user
@user.locale = I18n.locale.to_s
unless resource.save
return render_error(@user.errors)
end
respond_to do |format|
format.html do
set_flash_message(:success, :signed_up_but_unconfirmed)
redirect_to after_sign_in_path_for(@user)
end
format.js do
render json: { email: @user.email }
end
session[:spree_user_signup] = true
session[:confirmation_return_url] = params[:return_url]
associate_user
respond_to do |format|
format.html do
set_flash_message(:success, :signed_up_but_unconfirmed)
redirect_to after_sign_in_path_for(@user)
end
format.js do
render json: { email: @user.email }
end
else
render_error(@user.errors)
end
rescue StandardError => error
OpenFoodNetwork::ErrorLogger.notify(error)

View File

@@ -1,7 +1,7 @@
module I18nHelper
def set_locale
# Save a given locale
if params[:locale] && Rails.application.config.i18n.available_locales.include?(params[:locale])
if params[:locale] && available_locale?(params[:locale])
spree_current_user.update_attributes!(locale: params[:locale]) if spree_current_user
cookies[:locale] = params[:locale]
end
@@ -13,4 +13,20 @@ module I18nHelper
I18n.locale = spree_current_user.andand.locale || cookies[:locale] || I18n.default_locale
end
def valid_locale(user)
if user.present? &&
user.locale.present? &&
available_locale?(user.locale)
user.locale
else
I18n.default_locale
end
end
private
def available_locale?(locale)
Rails.application.config.i18n.available_locales.include?(locale)
end
end

View File

@@ -1,15 +1,18 @@
require 'devise/mailers/helpers'
class EnterpriseMailer < Spree::BaseMailer
include Devise::Mailers::Helpers
include I18nHelper
def welcome(enterprise)
@enterprise = enterprise
subject = t('enterprise_mailer.welcome.subject',
enterprise: @enterprise.name,
sitename: Spree::Config[:site_name])
mail(:to => enterprise.contact.email,
:from => from_address,
:subject => subject)
I18n.with_locale valid_locale(@enterprise.owner) do
subject = t('enterprise_mailer.welcome.subject',
enterprise: @enterprise.name,
sitename: Spree::Config[:site_name])
mail(:to => enterprise.contact.email,
:from => from_address,
:subject => subject)
end
end
def manager_invitation(enterprise, user)
@@ -17,11 +20,12 @@ class EnterpriseMailer < Spree::BaseMailer
@instance = Spree::Config[:site_name]
@instance_email = from_address
subject = t('enterprise_mailer.invite_manager.subject', enterprise: @enterprise.name)
mail(to: user.email,
from: from_address,
subject: subject)
I18n.with_locale valid_locale(@enterprise.owner) do
subject = t('enterprise_mailer.invite_manager.subject', enterprise: @enterprise.name)
mail(to: user.email,
from: from_address,
subject: subject)
end
end
private

View File

@@ -1,4 +1,5 @@
class ProducerMailer < Spree::BaseMailer
include I18nHelper
def order_cycle_report(producer, order_cycle)
@producer = producer
@@ -10,9 +11,11 @@ class ProducerMailer < Spree::BaseMailer
@total = total_from_line_items(line_items)
@tax_total = tax_total_from_line_items(line_items)
subject = "[#{Spree::Config.site_name}] #{I18n.t('producer_mailer.order_cycle.subject', producer: producer.name)}"
I18n.with_locale valid_locale(@producer.owner) do
order_cycle_subject = I18n.t('producer_mailer.order_cycle.subject', producer: producer.name)
subject = "[#{Spree::Config.site_name}] #{order_cycle_subject}"
if has_orders?(order_cycle, producer)
return unless has_orders?(order_cycle, producer)
mail(
to: @producer.contact.email,
from: from_address,
@@ -23,7 +26,6 @@ class ProducerMailer < Spree::BaseMailer
end
end
private
def has_orders?(order_cycle, producer)

View File

@@ -2,32 +2,47 @@ Spree::OrderMailer.class_eval do
helper HtmlHelper
helper CheckoutHelper
helper SpreeCurrencyHelper
include I18nHelper
def cancel_email(order_or_order_id, resend = false)
@order = find_order(order_or_order_id)
I18n.with_locale valid_locale(@order.user) do
mail(to: @order.email,
from: from_address,
subject: mail_subject(t('order_mailer.cancel_email.subject'), resend))
end
end
def confirm_email_for_customer(order_or_order_id, resend = false)
@order = find_order(order_or_order_id)
subject = build_subject(t('order_mailer.confirm_email.subject'), resend)
mail(:to => @order.email,
:from => from_address,
:subject => subject,
:reply_to => @order.distributor.contact.email)
I18n.with_locale valid_locale(@order.user) do
subject = mail_subject(t('order_mailer.confirm_email.subject'), resend)
mail(:to => @order.email,
:from => from_address,
:subject => subject,
:reply_to => @order.distributor.contact.email)
end
end
def confirm_email_for_shop(order_or_order_id, resend = false)
@order = find_order(order_or_order_id)
subject = build_subject(t('order_mailer.confirm_email.subject'), resend)
mail(:to => @order.distributor.contact.email,
:from => from_address,
:subject => subject)
I18n.with_locale valid_locale(@order.user) do
subject = mail_subject(t('order_mailer.confirm_email.subject'), resend)
mail(:to => @order.distributor.contact.email,
:from => from_address,
:subject => subject)
end
end
def invoice_email(order_or_order_id, pdf)
@order = find_order(order_or_order_id)
subject = build_subject(t(:invoice))
attachments["invoice-#{@order.number}.pdf"] = pdf if pdf.present?
mail(:to => @order.email,
:from => from_address,
:subject => subject,
:reply_to => @order.distributor.contact.email)
attach_file("invoice-#{@order.number}.pdf", pdf)
I18n.with_locale valid_locale(@order.user) do
mail(to: @order.email,
from: from_address,
subject: mail_subject(t(:invoice), false),
reply_to: @order.distributor.contact.email)
end
end
private
@@ -37,8 +52,12 @@ Spree::OrderMailer.class_eval do
order_or_order_id.respond_to?(:id) ? order_or_order_id : Spree::Order.find(order_or_order_id)
end
def build_subject( subject_text, resend = false )
subject = (resend ? "[#{t(:resend).upcase}] " : "")
subject += "#{Spree::Config[:site_name]} #{subject_text} ##{@order.number}"
def mail_subject(base_subject, resend)
resend_prefix = (resend ? "[#{t(:resend).upcase}] " : '')
"#{resend_prefix}#{Spree::Config[:site_name]} #{base_subject} ##{@order.number}"
end
def attach_file(filename, file)
attachments[filename] = file if file.present?
end
end

View File

@@ -1,8 +1,12 @@
Spree::UserMailer.class_eval do
include I18nHelper
def signup_confirmation(user)
@user = user
mail(:to => user.email, :from => from_address,
:subject => t(:welcome_to) + Spree::Config[:site_name])
I18n.with_locale valid_locale(@user) do
mail(:to => user.email, :from => from_address,
:subject => t(:welcome_to) + Spree::Config[:site_name])
end
end
# Overriding `Spree::UserMailer.confirmation_instructions` which is
@@ -12,10 +16,12 @@ Spree::UserMailer.class_eval do
@instance = Spree::Config[:site_name]
@contact = ContentConfig.footer_email
subject = t('spree.user_mailer.confirmation_instructions.subject')
mail(to: confirmation_email_address,
from: from_address,
subject: subject)
I18n.with_locale valid_locale(@user) do
subject = t('spree.user_mailer.confirmation_instructions.subject')
mail(to: confirmation_email_address,
from: from_address,
subject: subject)
end
end
private

View File

@@ -1,6 +1,7 @@
class SubscriptionMailer < Spree::BaseMailer
helper CheckoutHelper
helper ShopMailHelper
include I18nHelper
def confirmation_email(order)
@type = 'confirmation'
@@ -46,10 +47,13 @@ class SubscriptionMailer < Spree::BaseMailer
private
def send_mail(order)
subject = "#{Spree::Config[:site_name]} #{t('order_mailer.confirm_email.subject')} ##{order.number}"
mail(to: order.email,
from: from_address,
subject: subject,
reply_to: order.distributor.contact.email)
I18n.with_locale valid_locale(order.user) do
confirm_email_subject = t('order_mailer.confirm_email.subject')
subject = "#{Spree::Config[:site_name]} #{confirm_email_subject} ##{order.number}"
mail(to: order.email,
from: from_address,
subject: subject,
reply_to: order.distributor.contact.email)
end
end
end

View File

@@ -0,0 +1,41 @@
module StockSettingsOverrideValidation
extend ActiveSupport::Concern
included do
before_validation :require_compatible_on_demand_and_count_on_hand
end
def require_compatible_on_demand_and_count_on_hand
disallow_count_on_hand_if_using_producer_stock_settings
disallow_count_on_hand_if_on_demand
require_count_on_hand_if_limited_stock
end
def disallow_count_on_hand_if_using_producer_stock_settings
return unless on_demand.nil? && count_on_hand.present?
error_message = I18n.t("count_on_hand.using_producer_stock_settings_but_count_on_hand_set",
scope: i18n_scope_for_stock_settings_override_validation_error)
errors.add(:count_on_hand, error_message)
end
def disallow_count_on_hand_if_on_demand
return unless on_demand? && count_on_hand.present?
error_message = I18n.t("count_on_hand.on_demand_but_count_on_hand_set",
scope: i18n_scope_for_stock_settings_override_validation_error)
errors.add(:count_on_hand, error_message)
end
def require_count_on_hand_if_limited_stock
return unless on_demand == false && count_on_hand.blank?
error_message = I18n.t("count_on_hand.limited_stock_but_no_count_on_hand",
scope: i18n_scope_for_stock_settings_override_validation_error)
errors.add(:count_on_hand, error_message)
end
def i18n_scope_for_stock_settings_override_validation_error
"activerecord.errors.models.#{self.class.name.underscore}"
end
end

View File

@@ -68,6 +68,18 @@ module ProductImport
private
def find_or_initialize_variant_override(entry, existing_variant)
existing_variant_override = VariantOverride.where(
variant_id: existing_variant.id,
hub_id: entry.enterprise_id
).first
existing_variant_override || VariantOverride.new(
variant_id: existing_variant.id,
hub_id: entry.enterprise_id
)
end
def enterprise_validation(entry)
return if name_presence_error entry
return if enterprise_not_found_error entry
@@ -310,21 +322,12 @@ module ProductImport
end
def create_inventory_item(entry, existing_variant)
existing_variant_override = VariantOverride.where(
variant_id: existing_variant.id,
hub_id: entry.enterprise_id
).first
find_or_initialize_variant_override(entry, existing_variant).tap do |variant_override|
check_variant_override_stock_settings(entry, variant_override)
variant_override = existing_variant_override || VariantOverride.new(
variant_id: existing_variant.id,
hub_id: entry.enterprise_id
)
variant_override.assign_attributes(count_on_hand: entry.on_hand, import_date: @import_time)
check_on_hand_nil(entry, variant_override)
variant_override.assign_attributes(entry.attributes.slice('price', 'on_demand'))
variant_override
variant_override.assign_attributes(import_date: @import_time)
variant_override.assign_attributes(entry.attributes.slice('price', 'on_demand'))
end
end
def mark_as_inventory_item(entry, variant_override)
@@ -355,5 +358,11 @@ module ProductImport
object.count_on_hand = 0 if object.respond_to?(:count_on_hand)
entry.on_hand_nil = true
end
def check_variant_override_stock_settings(entry, object)
object.count_on_hand = entry.on_hand.presence
object.on_demand = object.count_on_hand.blank? if entry.on_demand.blank?
entry.on_hand_nil = object.count_on_hand.blank?
end
end
end

View File

@@ -1,5 +1,6 @@
class VariantOverride < ActiveRecord::Base
extend Spree::LocalizedNumber
include StockSettingsOverrideValidation
acts_as_taggable
@@ -58,7 +59,7 @@ class VariantOverride < ActiveRecord::Base
def reset_stock!
if resettable
if default_stock?
self.attributes = { count_on_hand: default_stock }
self.attributes = { on_demand: false, count_on_hand: default_stock }
save
else
Bugsnag.notify RuntimeError.new "Attempting to reset stock level for a variant with no default stock level."

View File

@@ -2,17 +2,17 @@
.alpha.three.columns
= f.label :facebook, 'Facebook'
.omega.eight.columns
= f.text_field :facebook
= f.text_field :facebook, { placeholder: t('.facebook_placeholder') }
.row
.alpha.three.columns
= f.label :instagram, 'Instagram'
.omega.eight.columns
= f.text_field :instagram
= f.text_field :instagram, { placeholder: t('.instagram_placeholder') }
.row
.alpha.three.columns
= f.label :linkedin, 'LinkedIn'
.omega.eight.columns
= f.text_field :linkedin
= f.text_field :linkedin, { placeholder: t('.linkedin_placeholder') }
.row
.alpha.three.columns
= f.label :twitter

View File

@@ -8,9 +8,9 @@
%td.price{ ng: { show: 'columns.price.visible' } }
%input{name: 'variant-overrides-{{ variant.id }}-price', type: 'text', ng: {model: 'variantOverrides[hub_id][variant.id].price'}, placeholder: '{{ variant.price }}', 'ofn-track-variant-override' => 'price'}
%td.on_hand{ ng: { show: 'columns.on_hand.visible' } }
%input{name: 'variant-overrides-{{ variant.id }}-count_on_hand', type: 'text', ng: {model: 'variantOverrides[hub_id][variant.id].count_on_hand'}, placeholder: '{{ variant.on_hand }}', 'ofn-track-variant-override' => 'count_on_hand'}
%input{name: 'variant-overrides-{{ variant.id }}-count_on_hand', type: 'text', ng: { model: 'variantOverrides[hub_id][variant.id].count_on_hand', readonly: 'variantOverrides[hub_id][variant.id].on_demand != false' }, placeholder: '{{ countOnHandPlaceholder(variant, hub_id) }}', 'ofn-track-variant-override' => 'count_on_hand'}
%td.on_demand{ ng: { show: 'columns.on_demand.visible' } }
%input.field{ :type => 'checkbox', name: 'variant-overrides-{{ variant.id }}-on_demand', ng: { model: 'variantOverrides[hub_id][variant.id].on_demand' }, 'ofn-track-variant-override' => 'on_demand' }
%select{ name: 'variant-overrides-{{ variant.id }}-on_demand', ng: { model: 'variantOverrides[hub_id][variant.id].on_demand', change: 'updateCountOnHand(variant, hub_id)', options: 'option.value as option.description for option in onDemandOptions' }, 'ofn-track-variant-override' => 'on_demand' }
%td.reset{ ng: { show: 'columns.reset.visible' } }
%input{name: 'variant-overrides-{{ variant.id }}-resettable', type: 'checkbox', ng: {model: 'variantOverrides[hub_id][variant.id].resettable'}, placeholder: '{{ variant.resettable }}', 'ofn-track-variant-override' => 'resettable'}
%td.reset{ ng: { show: 'columns.reset.visible' } }
@@ -24,4 +24,4 @@
%button.icon-remove.fullwidth{ :type => 'button', ng: { click: "setVisibility(hub_id,variant.id,false)" } }
= t('admin.variant_overrides.index.hide')
%td.import_date{ ng: { show: 'columns.import_date.visible' } }
%span {{variantOverrides[hub_id][variant.id].import_date | date:"MMMM dd, yyyy HH:mm"}}
%span {{variantOverrides[hub_id][variant.id].import_date | date:"MMMM dd, yyyy HH:mm"}}

View File

@@ -88,6 +88,7 @@ module Openfoodnetwork
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(
#{config.root}/app/models/concerns
#{config.root}/app/presenters
#{config.root}/app/jobs
)

View File

@@ -47,6 +47,11 @@ en:
attributes:
orders_close_at:
after_orders_open_at: must be after open date
variant_override:
count_on_hand:
using_producer_stock_settings_but_count_on_hand_set: "must be blank because using producer stock settings"
on_demand_but_count_on_hand_set: "must be blank if on demand"
limited_stock_but_no_count_on_hand: "must be specified because forcing limited stock"
activemodel:
errors:
models:
@@ -772,6 +777,9 @@ en:
close_date: Close Date
social:
twitter_placeholder: eg. @the_prof
instagram_placeholder: eg. the_prof
facebook_placeholder: eg. www.facebook.com/PageNameHere
linkedin_placeholder: eg. www.linkedin.com/in/YourNameHere
stripe_connect:
connect_with_stripe: "Connect with Stripe"
stripe_connect_intro: "To accept payments using credit card, you will need to connect your stripe account to the Open Food Network. Use the button to the right to get started."
@@ -2601,7 +2609,14 @@ See the %{link} to find out more about %{sitename}'s features and to start using
in your cart have reduced. Here's what's changed:
now_out_of_stock: is now out of stock.
only_n_remainging: "now only has %{num} remaining."
variants:
on_demand:
"yes": "On demand"
variant_overrides:
on_demand:
use_producer_settings: "Use producer stock settings"
"yes": "Yes"
"no": "No"
inventory_products: "Inventory Products"
hidden_products: "Hidden Products"
new_products: "New Products"

View File

@@ -24,6 +24,11 @@ en_US:
attributes:
orders_close_at:
after_orders_open_at: must be after open date
variant_override:
count_on_hand:
using_producer_stock_settings_but_count_on_hand_set: "must be blank because using producer stock settings"
on_demand_but_count_on_hand_set: "must be blank if on demand"
limited_stock_but_no_count_on_hand: "must be specified because forcing limited stock"
activemodel:
errors:
models:
@@ -195,6 +200,7 @@ en_US:
admin_and_handling: Admin & Handling
profile: Profile
supplier_only: Supplier Only
has_shopfront: Has Shopfront
weight: Weight
volume: Volume
items: Items
@@ -219,6 +225,7 @@ en_US:
password_confirmation: Password Confirmation
reset_password_token: Reset password token
expired: has expired, please request a new one
back_to_payments_list: "Back to Payments List"
actions:
create_and_add_another: "Create and Add Another"
admin:
@@ -572,6 +579,7 @@ en_US:
tip: "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required."
shared: "Shared Resource?"
order_no: "Order No."
order_date: "Completed at"
max: "Max"
product_unit: "Product: Unit"
weight_volume: "Weight/Volume"
@@ -712,6 +720,9 @@ en_US:
close_date: Close Date
social:
twitter_placeholder: eg. @the_prof
instagram_placeholder: eg. the_prof
facebook_placeholder: eg. www.facebook.com/PageNameHere
linkedin_placeholder: eg. www.linkedin.com/in/YourNameHere
stripe_connect:
connect_with_stripe: "Connect with Stripe"
stripe_connect_intro: "To accept payments using credit card, you will need to connect your stripe account to the Open Food Network. Use the button to the right to get started."
@@ -1702,6 +1713,11 @@ en_US:
update_and_recalculate_fees: "Update And Recalculate Fees"
registration:
steps:
images:
continue: "Continue"
back: "Back"
headline: "Thanks!"
description: "Let's upload some pretty pictures so your profile looks great! :)"
type:
headline: "Last step to add %{enterprise}!"
question: "Are you a producer?"
@@ -1836,8 +1852,6 @@ en_US:
registration_type_error: "Please choose one. Are you are producer?"
registration_type_producer_help: "Producers make yummy things to eat and/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it."
registration_type_no_producer_help: "If youre not a producer, youre probably someone who sells and distributes food. You might be a hub, coop, buying group, retailer, wholesaler or other."
registration_images_headline: "Thanks!"
registration_images_description: "Let's upload some pretty pictures so your profile looks great! :)"
registration_detail_headline: "Let's get started"
registration_detail_enterprise: "Hooray! First we need to know a little bit about your enterprise:"
registration_detail_producer: "Oh Yeah! First we need to know a little bit about your farm:"
@@ -2234,6 +2248,7 @@ en_US:
validation_msg_relationship_already_established: "^That relationship is already established."
validation_msg_at_least_one_hub: "^At least one hub must be selected"
validation_msg_product_category_cant_be_blank: "^Product Category cant be blank"
validation_msg_tax: "^Tax Category is required"
validation_msg_tax_category_cant_be_blank: "^Tax Category can't be blank"
validation_msg_is_associated_with_an_exising_customer: "is associated with an existing customer"
content_configuration_pricing_table: "(TODO: Pricing table)"
@@ -2434,7 +2449,14 @@ en_US:
in your cart have reduced. Here's what's changed:
now_out_of_stock: is now out of stock.
only_n_remainging: "now only has %{num} remaining."
variants:
on_demand:
'yes': "On demand"
variant_overrides:
on_demand:
use_producer_settings: "Use producer stock settings"
'yes': "Yes"
'no': "No"
inventory_products: "Inventory Products"
hidden_products: "Hidden Products"
new_products: "New Products"
@@ -2538,6 +2560,12 @@ en_US:
other: "You have %{count} active order cycles."
manage_order_cycles: "MANAGE ORDER CYCLES"
payment_methods:
new:
new_payment_method: "New Payment Method"
back_to_payment_methods_list: "Back To Payment Methods List"
edit:
editing_payment_method: "Editing Payment Method"
back_to_payment_methods_list: "Back To Payment Methods List"
stripe_connect:
enterprise_select_placeholder: Choose...
loading_account_information_msg: Loading account information from stripe, please wait...
@@ -2598,14 +2626,21 @@ en_US:
bulk_coop_customer_payments: 'Bulk Co-op - Customer Payments'
users:
index:
listing_users: "Listing Users"
new_user: "New User"
user: "User"
enterprise_limit: "Enterprise Limit"
search: "Search"
email: "Email"
edit:
editing_user: "Editing User"
back_to_users_list: "Back To Users List"
general_settings: "General Settings"
form:
email: "Email"
roles: "Roles"
enterprise_limit: "Enterprise Limit"
confirm_password: "Confirm Password"
password: "Password"
email_confirmation:
confirmation_pending: "Email confirmation is pending. We've sent a confirmation email to %{address}."

View File

@@ -24,6 +24,11 @@ fr:
attributes:
orders_close_at:
after_orders_open_at: doit être postérieure à Date d'ouverture
variant_override:
count_on_hand:
using_producer_stock_settings_but_count_on_hand_set: "doit être vide car utilise les informations de stock du producteur"
on_demand_but_count_on_hand_set: "doit être vide si \"à volonté\""
limited_stock_but_no_count_on_hand: "doit être spécifié car pas \"à volonté\""
activemodel:
errors:
models:
@@ -117,7 +122,7 @@ fr:
explainer: Ces commandes ont été traitées mais pour certains produits, le stock était insuffisant
empty:
title: Pas de stock (%{count} commandes)
explainer: Ces commandes n'ont pas pu être traitées car les produits souhaités étaient en rupture de stok
explainer: Ces commandes n'ont pas pu être traitées car les produits souhaités étaient en rupture de stock
complete:
title: Déjà traité (%{count} commandes)
explainer: Ces commandes étaient déjà marquées comme passées, et n'ont donc pas été retouchées
@@ -716,6 +721,9 @@ fr:
close_date: Date de fermeture
social:
twitter_placeholder: ex. @OpenFoodNet_fr
instagram_placeholder: 'ex: OpenFoodNet_fr'
facebook_placeholder: 'ex: www.facebook.com/NomDeLaPage'
linkedin_placeholder: 'ex: www.linkedin.com/in/VotreNom'
stripe_connect:
connect_with_stripe: "Connecter avec Stripe"
stripe_connect_intro: "Pour accepter des paiements utilisant la carte bancaire, vous devez connecter votre compte Stripe à Open Food France. Cliquez sur le bouton à droite pour commencer."
@@ -1706,6 +1714,11 @@ fr:
update_and_recalculate_fees: "Mettre à jour et recalculer les frais"
registration:
steps:
images:
continue: "Suivant"
back: "Retour"
headline: "Merci!"
description: "Ajoutez maintenant de jolies photos pour que votre profil soit attractif! :)"
type:
headline: "Dernière étape pour ajouter %{enterprise} !"
question: "Etes-vous un producteur ?"
@@ -1840,8 +1853,6 @@ fr:
registration_type_error: "Veuillez faire un choix. Etes vous un producteur?"
registration_type_producer_help: "Un producteur fabrique de bonnes choses à boire et à manger. Vous êtes un producteur si vous les faites pousser, les élevez, les pétrissez, transformez, fermentez, les réduisez en grains, etc."
registration_type_no_producer_help: "Si vous n'êtes pas un producteur, vous êtes probablement un revendeur ou distributeur alimentaire: un \"hub\", une coopérative, un groupement d'achat, un revendeur, un grossiste, ou autre."
registration_images_headline: "Merci!"
registration_images_description: "Ajoutez maintenant de jolies photos pour que votre profil soit attractif! :)"
registration_detail_headline: "Commençons"
registration_detail_enterprise: "Woohoo! Dites-nous déjà quelques mots à propos de votre entreprise:"
registration_detail_producer: "Woohoo! Dites-nous déjà quelques mots à propos de votre ferme:"
@@ -2450,7 +2461,14 @@ fr:
à votre demande. Voilà les modifications opérées:
now_out_of_stock: est maintenant en rupture de stock.
only_n_remainging: "plus que %{num} en stock."
variants:
on_demand:
'yes': "A volonté"
variant_overrides:
on_demand:
use_producer_settings: "Utiliser les infos de stock producteur"
'yes': "Oui"
'no': "Non"
inventory_products: "Produits du Catalogue Boutique"
hidden_products: "Produits Masqués"
new_products: "Nouveaux Produits"
@@ -2554,6 +2572,12 @@ fr:
other: "Vous avez %{count} cycles de vente actifs."
manage_order_cycles: "GERER LES CYCLES DE VENTE"
payment_methods:
new:
new_payment_method: "Nouvelle méthode de paiement"
back_to_payment_methods_list: "Retour à la liste des méthodes de paiement"
edit:
editing_payment_method: "Modification de la méthode de paiement"
back_to_payment_methods_list: "Retour à la liste des méthodes de paiement"
stripe_connect:
enterprise_select_placeholder: Choisir...
loading_account_information_msg: Informations de compte en cours de chargement depuis Stripe, veuillez patienter...

View File

@@ -1144,7 +1144,7 @@ fr_BE:
ticket_column_unit_price: "Prix unitaire"
ticket_column_total_price: "Prix total"
menu_1_title: "Comptoir"
menu_1_url: "/Comptoir"
menu_1_url: "/shops"
menu_2_title: "Carte"
menu_2_url: "/map"
menu_3_title: "Producteurs"

View File

@@ -0,0 +1,8 @@
class UpdateInstagramData < ActiveRecord::Migration
def change
Enterprise.where("instagram like ?", "%instagram.com%").find_each do |e|
e.instagram = e.instagram.split('/').last
e.save
end
end
end

View File

@@ -0,0 +1,165 @@
# This simplifies variant overrides to have only the following combinations:
#
# on_demand | count_on_hand
# -----------+---------------
# true | nil
# false | set
# nil | nil
#
# Refer to the table {here}[https://github.com/openfoodfoundation/openfoodnetwork/issues/3067] for
# the effect of different variant and variant override stock configurations.
#
# Furthermore, this will allow all existing variant overrides to satisfy the newly added model
# validation rules.
class SimplifyVariantOverrideStockSettings < ActiveRecord::Migration
class VariantOverride < ActiveRecord::Base
belongs_to :variant
belongs_to :hub, class_name: "Enterprise"
scope :with_count_on_hand, -> { where("count_on_hand IS NOT NULL") }
scope :without_count_on_hand, -> { where(count_on_hand: nil) }
end
class Variant < ActiveRecord::Base
self.table_name = "spree_variants"
belongs_to :product
def name
namer = OpenFoodNetwork::OptionValueNamer.new(self)
namer.name
end
end
class Product < ActiveRecord::Base
self.table_name = "spree_products"
belongs_to :supplier, class_name: "Enterprise"
end
class Enterprise < ActiveRecord::Base; end
def up
ensure_reports_path_exists
CSV.open(csv_path, "w") do |csv|
csv << csv_header_row
update_use_producer_stock_settings_with_count_on_hand(csv)
update_on_demand_with_count_on_hand(csv)
update_limited_stock_without_count_on_hand(csv)
end
split_csv_by_distributor
end
def down
CSV.foreach(csv_path, headers: true) do |row|
VariantOverride.where(id: row["variant_override_id"])
.update_all(on_demand: row["previous_on_demand"],
count_on_hand: row["previous_count_on_hand"])
end
end
private
def reports_path
Rails.root.join("reports", "SimplifyVariantOverrideStockSettings")
end
def ensure_reports_path_exists
Dir.mkdir(reports_path) unless File.exist?(reports_path)
end
def csv_path
reports_path.join("changed_variant_overrides.csv")
end
def distributor_csv_path(name, id)
reports_path.join("changed_variant_overrides-#{name.parameterize('_')}-#{id}.csv")
end
# When on_demand is nil but count_on_hand is set, force limited stock.
def update_use_producer_stock_settings_with_count_on_hand(csv)
variant_overrides = VariantOverride.where(on_demand: nil).with_count_on_hand
update_variant_overrides_and_log(csv, variant_overrides) do |variant_override|
variant_override.update_attributes!(on_demand: false)
end
end
# Clear count_on_hand if forcing on demand.
def update_on_demand_with_count_on_hand(csv)
variant_overrides = VariantOverride.where(on_demand: true).with_count_on_hand
update_variant_overrides_and_log(csv, variant_overrides) do |variant_override|
variant_override.update_attributes!(count_on_hand: nil)
end
end
# When on_demand is false but count on hand is not specified, set this to use producer stock
# settings.
def update_limited_stock_without_count_on_hand(csv)
variant_overrides = VariantOverride.where(on_demand: false).without_count_on_hand
update_variant_overrides_and_log(csv, variant_overrides) do |variant_override|
variant_override.update_attributes!(on_demand: nil)
end
end
def update_variant_overrides_and_log(csv, variant_overrides)
variant_overrides.find_each do |variant_override|
csv << variant_override_log_row(variant_override) do
yield variant_override
end
end
end
def csv_header_row
%w(
variant_override_id
distributor_name distributor_id
producer_name producer_id
product_name product_id
variant_description variant_id
previous_on_demand previous_count_on_hand
updated_on_demand updated_count_on_hand
)
end
def variant_override_log_row(variant_override)
variant = variant_override.variant
distributor = variant_override.hub
product = variant.andand.product
supplier = product.andand.supplier
row = [
variant_override.id,
distributor.andand.name, distributor.andand.id,
supplier.andand.name, supplier.andand.id,
product.andand.name, product.andand.id,
variant.andand.name, variant.andand.id,
variant_override.on_demand, variant_override.count_on_hand
]
yield variant_override
row + [variant_override.on_demand, variant_override.count_on_hand]
end
def split_csv_by_distributor
table = CSV.read(csv_path)
distributor_ids = table[1..-1].map { |row| row[2] }.uniq # Don't use the header row.
distributor_ids.each do |distributor_id|
distributor_data_rows = filter_data_rows_for_distributor(table[1..-1], distributor_id)
distributor_name = distributor_data_rows.first[1]
CSV.open(distributor_csv_path(distributor_name, distributor_id), "w") do |csv|
csv << table[0] # Header row
distributor_data_rows.each { |row| csv << row }
end
end
end
def filter_data_rows_for_distributor(data_rows, distributor_id)
data_rows.select { |row| row[2] == distributor_id }
end
end

View File

@@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20181123012635) do
ActiveRecord::Schema.define(:version => 20181128054803) do
create_table "account_invoices", :force => true do |t|
t.integer "user_id", :null => false

View File

@@ -1,8 +1,3 @@
ENV["RAILS_ENV"] = "test"
require File.expand_path("dummy/config/environment.rb", __dir__)
require "rails/test_help"
Rails.backtrace_cleaner.remove_silencers!
require "../../spec/spec_helper.rb"
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }

47
lib/tasks/specs.rake Normal file
View File

@@ -0,0 +1,47 @@
namespace :openfoodnetwork do
namespace :specs do
namespace :engines do
def detect_engine_paths
Pathname("engines/").children.select(&:directory?)
end
def engine_name_for_engine(engine_path)
engine_path.basename.to_path
end
def execute_rspec_for_engine(engine_path)
system "cd #{engine_path.expand_path} && DISABLE_KNAPSACK=true bundle exec rspec"
end
engine_paths = detect_engine_paths
engine_paths.each do |engine_path|
engine_name = engine_name_for_engine(engine_path)
namespace engine_name do
desc "Run RSpec tests for engine \"#{engine_name}\""
task :rspec do
success = execute_rspec_for_engine(engine_path)
abort "Failure when running tests for engine \"#{engine_name}\"" unless success
end
end
end
namespace :all do
desc "Run RSpec tests for all engines"
task :rspec do
success = true
engine_paths.each do |engine_path|
success = !!execute_rspec_for_engine(engine_path) && success
end
abort "Failure encountered when running tests for engines" unless success
end
end
desc "Alias for openfoodnetwork:specs:engines:all:rspec"
task rspec: "all:rspec"
end
end
end

4
reports/README.md Normal file
View File

@@ -0,0 +1,4 @@
## `reports/` Directory
This directory may be used for reports generated for the OFN instance. For example, a database
migration may save in this directory a spreadsheet of changes it made during execution.

View File

@@ -48,6 +48,16 @@ describe UserRegistrationsController, type: :controller do
expect(json).to eq({"email" => "test@test.com"})
expect(controller.spree_current_user).to be_nil
end
it "sets user.locale from cookie on create" do
original_locale_cookie = cookies[:locale]
cookies[:locale] = "pt"
xhr :post, :create, spree_user: user_params, use_route: :spree
expect(assigns[:user].locale).to eq("pt")
cookies[:locale] = original_locale_cookie
end
end
context "when registration fails" do

View File

@@ -189,9 +189,20 @@ FactoryBot.define do
factory :variant_override, :class => VariantOverride do
price 77.77
on_demand false
count_on_hand 11111
default_stock 2000
resettable false
trait :on_demand do
on_demand true
count_on_hand nil
end
trait :use_producer_stock_settings do
on_demand nil
count_on_hand nil
end
end
factory :inventory_item, :class => InventoryItem do

View File

@@ -140,8 +140,8 @@ feature %q{
fill_in "variant-overrides-#{variant.id}-sku", with: 'NEWSKU'
fill_in "variant-overrides-#{variant.id}-price", with: '777.77'
select_on_demand variant, :no
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '123'
check "variant-overrides-#{variant.id}-on_demand"
page.should have_content "Changes to one override remain unsaved."
expect do
@@ -154,14 +154,15 @@ feature %q{
vo.hub_id.should == hub.id
vo.sku.should == "NEWSKU"
vo.price.should == 777.77
expect(vo.on_demand).to eq(false)
vo.count_on_hand.should == 123
vo.on_demand.should == true
end
describe "creating and then updating the new override" do
it "updates the same override instead of creating a duplicate" do
# When I create a new override
fill_in "variant-overrides-#{variant.id}-price", with: '777.77'
select_on_demand variant, :no
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '123'
page.should have_content "Changes to one override remain unsaved."
@@ -186,6 +187,7 @@ feature %q{
vo.variant_id.should == variant.id
vo.hub_id.should == hub.id
vo.price.should == 111.11
expect(vo.on_demand).to eq(false)
vo.count_on_hand.should == 111
end
end
@@ -218,7 +220,7 @@ feature %q{
end
context "with overrides" do
let!(:vo) { create(:variant_override, variant: variant, hub: hub, price: 77.77, count_on_hand: 11111, default_stock: 1000, resettable: true, tag_list: ["tag1","tag2","tag3"]) }
let!(:vo) { create(:variant_override, :on_demand, variant: variant, hub: hub, price: 77.77, default_stock: 1000, resettable: true, tag_list: ["tag1","tag2","tag3"]) }
let!(:vo_no_auth) { create(:variant_override, variant: variant, hub: hub2, price: 1, count_on_hand: 2) }
let!(:product2) { create(:simple_product, supplier: producer, variant_unit: 'weight', variant_unit_scale: 1) }
let!(:variant2) { create(:variant, product: product2, unit_value: 8, price: 1.00, on_hand: 12) }
@@ -235,11 +237,15 @@ feature %q{
it "product values are affected by overrides" do
page.should have_input "variant-overrides-#{variant.id}-price", with: '77.77', placeholder: '1.23'
page.should have_input "variant-overrides-#{variant.id}-count_on_hand", with: '11111', placeholder: '12'
expect(page).to have_input "variant-overrides-#{variant.id}-count_on_hand", with: "", placeholder: I18n.t("js.variants.on_demand.yes")
expect(page).to have_select "variant-overrides-#{variant.id}-on_demand", selected: I18n.t("js.variant_overrides.on_demand.yes")
expect(page).to have_input "variant-overrides-#{variant2.id}-count_on_hand", with: "40", placeholder: ""
end
it "updates existing overrides" do
fill_in "variant-overrides-#{variant.id}-price", with: '22.22'
select_on_demand variant, :no
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '8888'
page.should have_content "Changes to one override remain unsaved."
@@ -252,13 +258,36 @@ feature %q{
vo.variant_id.should == variant.id
vo.hub_id.should == hub.id
vo.price.should == 22.22
expect(vo.on_demand).to eq(false)
vo.count_on_hand.should == 8888
end
it "updates on_demand settings" do
select_on_demand variant, :no
click_button I18n.t("save_changes")
expect(page).to have_content I18n.t("js.changes_saved")
vo.reload
expect(vo.on_demand).to eq(false)
select_on_demand variant, :yes
click_button I18n.t("save_changes")
expect(page).to have_content I18n.t("js.changes_saved")
vo.reload
expect(vo.on_demand).to eq(true)
select_on_demand variant, :use_producer_settings
click_button I18n.t("save_changes")
expect(page).to have_content I18n.t("js.changes_saved")
vo.reload
expect(vo.on_demand).to be_nil
end
# Any new fields added to the VO model need to be added to this test
it "deletes overrides when values are cleared" do
first("div#columns-dropdown", :text => "COLUMNS").click
first("div#columns-dropdown div.menu div.menu_item", text: "On Demand").click
first("div#columns-dropdown div.menu div.menu_item", text: "Enable Stock Reset?").click
first("div#columns-dropdown div.menu div.menu_item", text: "Tags").click
first("div#columns-dropdown", :text => "COLUMNS").click
@@ -272,6 +301,7 @@ feature %q{
# Clearing values manually
fill_in "variant-overrides-#{variant.id}-price", with: ''
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: ''
select_on_demand variant, :use_producer_settings
fill_in "variant-overrides-#{variant.id}-default_stock", with: ''
within "tr#v_#{variant.id}" do
vo.tag_list.each do |tag|
@@ -297,7 +327,7 @@ feature %q{
first("div#bulk-actions-dropdown div.menu div.menu_item", text: "Reset Stock Levels To Defaults").click
page.should have_content 'Stocks reset to defaults.'
vo.reload
page.should have_input "variant-overrides-#{variant.id}-count_on_hand", with: '1000', placeholder: '12'
expect(page).to have_input "variant-overrides-#{variant.id}-count_on_hand", with: "1000", placeholder: ""
vo.count_on_hand.should == 1000
end
@@ -305,7 +335,7 @@ feature %q{
first("div#bulk-actions-dropdown").click
first("div#bulk-actions-dropdown div.menu div.menu_item", text: "Reset Stock Levels To Defaults").click
vo_no_reset.reload
page.should have_input "variant-overrides-#{variant2.id}-count_on_hand", with: '40', placeholder: '12'
expect(page).to have_input "variant-overrides-#{variant2.id}-count_on_hand", with: "40", placeholder: ""
vo_no_reset.count_on_hand.should == 40
end
@@ -315,9 +345,52 @@ feature %q{
first("div#bulk-actions-dropdown div.menu div.menu_item", text: "Reset Stock Levels To Defaults").click
page.should have_content "Save changes first"
end
describe "ensuring that on demand and count on hand settings are compatible" do
it "clears count on hand when not limited stock" do
# It clears count_on_hand when selecting true on_demand.
select_on_demand variant, :no
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: "200"
select_on_demand variant, :yes
expect(page).to have_input "variant-overrides-#{variant.id}-count_on_hand", with: ""
# It clears count_on_hand when selecting nil on_demand.
select_on_demand variant, :no
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: "200"
select_on_demand variant, :use_producer_settings
expect(page).to have_input "variant-overrides-#{variant.id}-count_on_hand", with: ""
# It saves the changes.
click_button I18n.t("save_changes")
expect(page).to have_content I18n.t("js.changes_saved")
vo.reload
expect(vo.count_on_hand).to be_nil
expect(vo.on_demand).to be_nil
end
it "provides explanation when attempting to save variant override with incompatible stock settings" do
# Successfully change stock settings.
select_on_demand variant, :no
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: "1111"
click_button I18n.t("save_changes")
expect(page).to have_content I18n.t("js.changes_saved")
# Make stock settings incompatible.
select_on_demand variant, :no
fill_in "variant-overrides-#{variant.id}-count_on_hand", with: ""
# It does not save the changes.
click_button I18n.t("save_changes")
expect(page).to have_content I18n.t("activerecord.errors.models.variant_override.count_on_hand.limited_stock_but_no_count_on_hand")
expect(page).to have_no_content I18n.t("js.changes_saved")
vo.reload
expect(vo.count_on_hand).to eq(1111)
expect(vo.on_demand).to eq(false)
end
end
end
end
end
describe "when manually placing an order" do
@@ -382,4 +455,9 @@ feature %q{
end
end
end
def select_on_demand(variant, value_sym)
option_label = I18n.t(value_sym, scope: "js.variant_overrides.on_demand")
select option_label, from: "variant-overrides-#{variant.id}-on_demand"
end
end

View File

@@ -22,7 +22,7 @@ feature "shopping with variant overrides defined", js: true, retry: 3 do
let(:product1_variant3) { create(:variant, product: product1, price: 44.44, unit_value: 4) }
let(:product3_variant1) { create(:variant, product: product3, price: 55.55, unit_value: 5, on_demand: true) }
let(:product3_variant2) { create(:variant, product: product3, price: 66.66, unit_value: 6, on_demand: true) }
let!(:product1_variant1_override) { create(:variant_override, hub: hub, variant: product1_variant1, price: 55.55, count_on_hand: nil, default_stock: nil, resettable: false) }
let!(:product1_variant1_override) { create(:variant_override, :use_producer_stock_settings, hub: hub, variant: product1_variant1, price: 55.55, count_on_hand: nil, default_stock: nil, resettable: false) }
let!(:product1_variant2_override) { create(:variant_override, hub: hub, variant: product1_variant2, count_on_hand: 0, default_stock: nil, resettable: false) }
let!(:product2_variant1_override) { create(:variant_override, hub: hub, variant: product2_variant1, count_on_hand: 0, default_stock: nil, resettable: false) }
let!(:product1_variant3_override) { create(:variant_override, hub: hub, variant: product1_variant3, count_on_hand: 3, default_stock: nil, resettable: false) }

View File

@@ -24,13 +24,13 @@ describe I18nHelper, type: :helper do
end
it "sets the chosen locale" do
allow(helper).to receive(:params) { {locale: "es"} }
allow(helper).to receive(:params) { { locale: "es" } }
helper.set_locale
expect(I18n.locale).to eq :es
end
it "remembers the chosen locale" do
allow(helper).to receive(:params) { {locale: "es"} }
allow(helper).to receive(:params) { { locale: "es" } }
helper.set_locale
allow(helper).to receive(:params) { {} }
@@ -39,16 +39,16 @@ describe I18nHelper, type: :helper do
end
it "ignores unavailable locales" do
allow(helper).to receive(:params) { {locale: "xx"} }
allow(helper).to receive(:params) { { locale: "xx" } }
helper.set_locale
expect(I18n.locale).to eq :en
end
it "remembers the last chosen locale" do
allow(helper).to receive(:params) { {locale: "en"} }
allow(helper).to receive(:params) { { locale: "en" } }
helper.set_locale
allow(helper).to receive(:params) { {locale: "es"} }
allow(helper).to receive(:params) { { locale: "es" } }
helper.set_locale
allow(helper).to receive(:params) { {} }
@@ -57,7 +57,7 @@ describe I18nHelper, type: :helper do
end
it "remembers the chosen locale after logging in" do
allow(helper).to receive(:params) { {locale: "es"} }
allow(helper).to receive(:params) { { locale: "es" } }
helper.set_locale
# log in
@@ -68,7 +68,7 @@ describe I18nHelper, type: :helper do
end
it "forgets the chosen locale without cookies" do
allow(helper).to receive(:params) { {locale: "es"} }
allow(helper).to receive(:params) { { locale: "es" } }
helper.set_locale
# clean up cookies
@@ -91,14 +91,14 @@ describe I18nHelper, type: :helper do
end
it "sets the chosen locale" do
allow(helper).to receive(:params) { {locale: "es"} }
allow(helper).to receive(:params) { { locale: "es" } }
helper.set_locale
expect(I18n.locale).to eq :es
expect(user.locale).to eq "es"
end
it "remembers the chosen locale" do
allow(helper).to receive(:params) { {locale: "es"} }
allow(helper).to receive(:params) { { locale: "es" } }
helper.set_locale
allow(helper).to receive(:params) { {} }
@@ -107,10 +107,10 @@ describe I18nHelper, type: :helper do
end
it "remembers the last chosen locale" do
allow(helper).to receive(:params) { {locale: "en"} }
allow(helper).to receive(:params) { { locale: "en" } }
helper.set_locale
allow(helper).to receive(:params) { {locale: "es"} }
allow(helper).to receive(:params) { { locale: "es" } }
helper.set_locale
allow(helper).to receive(:params) { {} }
@@ -119,7 +119,7 @@ describe I18nHelper, type: :helper do
end
it "remembers the chosen locale after logging out" do
allow(helper).to receive(:params) { {locale: "es"} }
allow(helper).to receive(:params) { { locale: "es" } }
helper.set_locale
# log out
@@ -130,7 +130,7 @@ describe I18nHelper, type: :helper do
end
it "remembers the chosen locale on another computer" do
allow(helper).to receive(:params) { {locale: "es"} }
allow(helper).to receive(:params) { { locale: "es" } }
helper.set_locale
expect(cookies[:locale]).to eq "es"
@@ -142,4 +142,36 @@ describe I18nHelper, type: :helper do
expect(I18n.locale).to eq :es
end
end
context "#valid_locale" do
around do |example|
original_default_locale = I18n.default_locale
original_available_locales = Rails.application.config.i18n.available_locales
I18n.default_locale = "es"
Rails.application.config.i18n.available_locales = ["es", "pt"]
example.run
I18n.default_locale = original_default_locale
Rails.application.config.i18n.available_locales = original_available_locales
end
let(:user) { build(:user) }
it "returns default locale if given user is nil" do
expect(helper.valid_locale(nil)).to eq I18n.default_locale
end
it "returns default locale if locale of given user is nil" do
expect(helper.valid_locale(user)).to eq I18n.default_locale
end
it "returns default locale if locale of given user is not available" do
user.locale = "cn"
expect(helper.valid_locale(user)).to eq I18n.default_locale
end
it "returns the locale of the given user if available" do
user.locale = "pt"
expect(helper.valid_locale(user)).to eq "pt"
end
end
end

View File

@@ -87,3 +87,115 @@ describe "VariantOverridesCtrl", ->
$httpBackend.flush()
expect(VariantOverrides.updateData).toHaveBeenCalledWith variant_overrides_mock
expect(StatusMessage.display).toHaveBeenCalledWith 'success', 'Stocks reset to defaults.'
describe "suggesting count_on_hand when on_demand is changed", ->
variant = null
beforeEach ->
scope.variantOverrides = {123: {}}
describe "when variant is on demand", ->
beforeEach ->
# Ideally, count_on_hand is blank when the variant is on demand. However, this rule is not
# enforced.
variant = {id: 2, on_demand: true, count_on_hand: 20, on_hand: "On demand"}
it "clears count_on_hand when variant override uses producer stock settings", ->
scope.variantOverrides[123][2] = {on_demand: null, count_on_hand: 1}
scope.updateCountOnHand(variant, 123)
expect(scope.variantOverrides[123][2].count_on_hand).toBeNull()
dirtyVariantOverride = DirtyVariantOverrides.dirtyVariantOverrides[123][2]
expect(dirtyVariantOverride.count_on_hand).toBeNull()
it "clears count_on_hand when variant override forces on demand", ->
scope.variantOverrides[123][2] = {on_demand: true, count_on_hand: 1}
scope.updateCountOnHand(variant, 123)
expect(scope.variantOverrides[123][2].count_on_hand).toBeNull()
dirtyVariantOverride = DirtyVariantOverrides.dirtyVariantOverrides[123][2]
expect(dirtyVariantOverride.count_on_hand).toBeNull()
it "clears count_on_hand when variant override forces limited stock", ->
scope.variantOverrides[123][2] = {on_demand: false, count_on_hand: 1}
scope.updateCountOnHand(variant, 123)
expect(scope.variantOverrides[123][2].count_on_hand).toBeNull()
dirtyVariantOverride = DirtyVariantOverrides.dirtyVariantOverrides[123][2]
expect(dirtyVariantOverride.count_on_hand).toBeNull()
describe "when variant has limited stock", ->
beforeEach ->
variant = {id: 2, on_demand: false, count_on_hand: 20, on_hand: 20}
it "clears count_on_hand when variant override uses producer stock settings", ->
scope.variantOverrides[123][2] = {on_demand: null, count_on_hand: 1}
scope.updateCountOnHand(variant, 123)
expect(scope.variantOverrides[123][2].count_on_hand).toBeNull()
dirtyVariantOverride = DirtyVariantOverrides.dirtyVariantOverrides[123][2]
expect(dirtyVariantOverride.count_on_hand).toBeNull()
it "clears count_on_hand when variant override forces on demand", ->
scope.variantOverrides[123][2] = {on_demand: true, count_on_hand: 1}
scope.updateCountOnHand(variant, 123)
expect(scope.variantOverrides[123][2].count_on_hand).toBeNull()
dirtyVariantOverride = DirtyVariantOverrides.dirtyVariantOverrides[123][2]
expect(dirtyVariantOverride.count_on_hand).toBeNull()
it "sets to producer count_on_hand when variant override forces limited stock", ->
scope.variantOverrides[123][2] = {on_demand: false, count_on_hand: 1}
scope.updateCountOnHand(variant, 123)
expect(scope.variantOverrides[123][2].count_on_hand).toBe(20)
dirtyVariantOverride = DirtyVariantOverrides.dirtyVariantOverrides[123][2]
expect(dirtyVariantOverride.count_on_hand).toBe(20)
describe "count on hand placeholder", ->
beforeEach ->
scope.variantOverrides = {123: {}}
describe "when variant is on demand", ->
variant = null
beforeEach ->
# Ideally, count_on_hand is blank when the variant is on demand. However, this rule is not
# enforced.
variant = {id: 2, on_demand: true, count_on_hand: 20, on_hand: t("on_demand")}
it "is 'On demand' when variant override uses producer stock settings", ->
scope.variantOverrides[123][2] = {on_demand: null, count_on_hand: 1}
placeholder = scope.countOnHandPlaceholder(variant, 123)
expect(placeholder).toBe(t("on_demand"))
it "is 'On demand' when variant override is on demand", ->
scope.variantOverrides[123][2] = {on_demand: true, count_on_hand: 1}
placeholder = scope.countOnHandPlaceholder(variant, 123)
expect(placeholder).toBe(t("js.variants.on_demand.yes"))
it "is blank when variant override is limited stock", ->
scope.variantOverrides[123][2] = {on_demand: false, count_on_hand: 1}
placeholder = scope.countOnHandPlaceholder(variant, 123)
expect(placeholder).toBe('')
describe "when variant is limited stock", ->
variant = null
beforeEach ->
variant = {id: 2, on_demand: false, count_on_hand: 20, on_hand: 20}
it "is variant count on hand when variant override uses producer stock settings", ->
scope.variantOverrides[123][2] = {on_demand: null, count_on_hand: 1}
placeholder = scope.countOnHandPlaceholder(variant, 123)
expect(placeholder).toBe(20)
it "is 'On demand' when variant override is on demand", ->
scope.variantOverrides[123][2] = {on_demand: true, count_on_hand: 1}
placeholder = scope.countOnHandPlaceholder(variant, 123)
expect(placeholder).toBe(t("js.variants.on_demand.yes"))
it "is blank when variant override is limited stock", ->
scope.variantOverrides[123][2] = {on_demand: false, count_on_hand: 1}
placeholder = scope.countOnHandPlaceholder(variant, 123)
expect(placeholder).toBe('')

View File

@@ -5,7 +5,7 @@ module OpenFoodNetwork
let(:hub) { create(:distributor_enterprise) }
let(:v) { create(:variant, price: 11.11, on_hand: 1, on_demand: true, sku: "VARIANTSKU") }
let(:vo) { create(:variant_override, hub: hub, variant: v, price: 22.22, count_on_hand: 2, on_demand: false, sku: "VOSKU") }
let(:vo_price_only) { create(:variant_override, hub: hub, variant: v, price: 22.22, count_on_hand: nil) }
let(:vo_price_only) { create(:variant_override, :use_producer_stock_settings, hub: hub, variant: v, price: 22.22) }
let(:scoper) { ScopeVariantToHub.new(hub) }
describe "overriding price" do

View File

@@ -17,9 +17,32 @@ describe Spree::UserMailer do
setup_email
end
it "sends an email when given a user" do
Spree::UserMailer.signup_confirmation(user).deliver
ActionMailer::Base.deliveries.count.should == 1
describe '#signup_confirmation' do
it "sends email when given a user" do
Spree::UserMailer.signup_confirmation(user).deliver
expect(ActionMailer::Base.deliveries.count).to eq(1)
end
describe "user locale" do
around do |example|
original_default_locale = I18n.default_locale
I18n.default_locale = 'pt'
example.run
I18n.default_locale = original_default_locale
end
it "sends email in user locale when user locale is defined" do
user.locale = 'es'
Spree::UserMailer.signup_confirmation(user).deliver
expect(ActionMailer::Base.deliveries.first.body).to include "Gracias por unirte"
end
it "sends email in default locale when user locale is not available" do
user.locale = 'cn'
Spree::UserMailer.signup_confirmation(user).deliver
expect(ActionMailer::Base.deliveries.first.body).to include "Obrigada por juntar-se"
end
end
end
# adapted from https://github.com/spree/spree_auth_devise/blob/70737af/spec/mailers/user_mailer_spec.rb

View File

@@ -35,6 +35,82 @@ describe VariantOverride do
end
end
describe "validation" do
describe "ensuring that on_demand and count_on_hand are compatible" do
let(:variant_override) { build(:variant_override, hub: hub, variant: variant,
on_demand: on_demand, count_on_hand: count_on_hand) }
context "when using producer stock settings" do
let(:on_demand) { nil }
context "when count_on_hand is blank" do
let(:count_on_hand) { nil }
it "is valid" do
expect(variant_override.save).to be_truthy
end
end
context "when count_on_hand is set" do
let(:count_on_hand) { 1 }
it "is invalid" do
expect(variant_override.save).to be_falsey
error_message = I18n.t("using_producer_stock_settings_but_count_on_hand_set",
scope: [i18n_scope_for_error, "count_on_hand"])
expect(variant_override.errors[:count_on_hand]).to eq([error_message])
end
end
end
context "when on demand" do
let(:on_demand) { true }
context "when count_on_hand is blank" do
let(:count_on_hand) { nil }
it "is valid" do
expect(variant_override.save).to be_truthy
end
end
context "when count_on_hand is set" do
let(:count_on_hand) { 1 }
it "is invalid" do
expect(variant_override.save).to be_falsey
error_message = I18n.t("on_demand_but_count_on_hand_set",
scope: [i18n_scope_for_error, "count_on_hand"])
expect(variant_override.errors[:count_on_hand]).to eq([error_message])
end
end
end
context "when limited stock" do
let(:on_demand) { false }
context "when count_on_hand is blank" do
let(:count_on_hand) { nil }
it "is invalid" do
expect(variant_override.save).to be_falsey
error_message = I18n.t("limited_stock_but_no_count_on_hand",
scope: [i18n_scope_for_error, "count_on_hand"])
expect(variant_override.errors[:count_on_hand]).to eq([error_message])
end
end
context "when count_on_hand is set" do
let(:count_on_hand) { 1 }
it "is valid" do
expect(variant_override.save).to be_truthy
end
end
end
end
end
describe "callbacks" do
let!(:vo) { create(:variant_override, hub: hub, variant: variant) }
@@ -59,7 +135,7 @@ describe VariantOverride do
end
describe "with nil count on hand" do
let(:variant_override) { create(:variant_override, variant: variant, hub: hub, count_on_hand: nil) }
let(:variant_override) { create(:variant_override, variant: variant, hub: hub, count_on_hand: nil, on_demand: true) }
describe "stock_overridden?" do
it "returns false" do
@@ -119,17 +195,42 @@ describe VariantOverride do
end
describe "resetting stock levels" do
it "resets the on hand level to the value in the default_stock field" do
vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock: 20, resettable: true)
vo.reset_stock!
expect(vo.reload.count_on_hand).to eq(20)
describe "forcing the on hand level to the value in the default_stock field" do
it "succeeds for variant override that forces limited stock" do
vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock: 20, resettable: true)
vo.reset_stock!
vo.reload
expect(vo.on_demand).to eq(false)
expect(vo.count_on_hand).to eq(20)
end
it "succeeds for variant override that forces unlimited stock" do
vo = create(:variant_override, :on_demand, variant: variant, hub: hub, default_stock: 20, resettable: true)
vo.reset_stock!
vo.reload
expect(vo.on_demand).to eq(false)
expect(vo.count_on_hand).to eq(20)
end
it "succeeds for variant override that uses producer stock settings" do
vo = create(:variant_override, :use_producer_stock_settings, variant: variant, hub: hub, default_stock: 20, resettable: true)
vo.reset_stock!
vo.reload
expect(vo.on_demand).to eq(false)
expect(vo.count_on_hand).to eq(20)
end
end
it "silently logs an error if the variant override doesn't have a default stock level" do
vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock: nil, resettable: true)
expect(Bugsnag).to receive(:notify)
vo.reset_stock!
expect(vo.reload.count_on_hand).to eq(12)
end
it "doesn't reset the level if the behaviour is disabled" do
vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock: 10, resettable: false)
vo.reset_stock!
@@ -140,4 +241,8 @@ describe VariantOverride do
context "extends LocalizedNumber" do
it_behaves_like "a model using the LocalizedNumber module", [:price]
end
def i18n_scope_for_error
"activerecord.errors.models.variant_override"
end
end

View File

@@ -6,9 +6,13 @@ require 'rubygems'
# Require pry when we're not inside Travis-CI
require 'pry' unless ENV['CI']
require 'knapsack'
Knapsack.tracker.config({enable_time_offset_warning: false}) unless ENV['CI']
Knapsack::Adapters::RSpecAdapter.bind
# This spec_helper.rb is being used by the custom engines in engines/. The engines are not set up to
# use Knapsack, and this provides the option to disable it when running the tests in CI services.
unless ENV['DISABLE_KNAPSACK']
require 'knapsack'
Knapsack.tracker.config({enable_time_offset_warning: false}) unless ENV['CI']
Knapsack::Adapters::RSpecAdapter.bind
end
ENV["RAILS_ENV"] ||= 'test'
require_relative "../config/environment"