mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Merge branch 'master' into 2-0-stable-jan-8th
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -41,3 +41,6 @@ libpeerconnection.log
|
||||
node_modules
|
||||
vendor/bundle/
|
||||
coverage
|
||||
/reports/
|
||||
!/reports/README.md
|
||||
bin/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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`.
|
||||
|
||||
4
Gemfile
4
Gemfile
@@ -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
|
||||
|
||||
27
Gemfile.lock
27
Gemfile.lock
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
10
app/assets/stylesheets/admin/components/input.css.scss
Normal file
10
app/assets/stylesheets/admin/components/input.css.scss
Normal file
@@ -0,0 +1,10 @@
|
||||
@import '../../darkswarm/branding';
|
||||
|
||||
.container {
|
||||
input {
|
||||
&[readonly] {
|
||||
background-color: $disabled-light;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
41
app/models/concerns/stock_settings_override_validation.rb
Normal file
41
app/models/concerns/stock_settings_override_validation.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"}}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 you’re not a producer, you’re 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}."
|
||||
|
||||
@@ -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...
|
||||
|
||||
@@ -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"
|
||||
|
||||
8
db/migrate/20181008201815_update_instagram_data.rb
Normal file
8
db/migrate/20181008201815_update_instagram_data.rb
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
47
lib/tasks/specs.rake
Normal 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
4
reports/README.md
Normal 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.
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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('')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user