Compare commits

..

2 Commits

Author SHA1 Message Date
Jean-Baptiste Bellet
e4ad6d736b Canceling an order: clicking on cancel btn or cancel action in dropdown 2023-04-20 13:53:37 +02:00
basilawwad
9f30471373 Use a shared_examples for the order cancellation tests 2023-04-20 12:28:27 +02:00
578 changed files with 7209 additions and 20687 deletions

View File

@@ -5,8 +5,6 @@
#
# cp .env.development .env.local
VERBOSE_QUERY_LOGS=true
SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
OFN_REDIS_URL="redis://localhost:6379/1"

View File

@@ -18,7 +18,7 @@ permissions:
jobs:
knapsack_rspec_controllers:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
@@ -86,7 +86,7 @@ jobs:
bundle exec rake knapsack_pro:rspec
knapsack_rspec_models:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
@@ -155,7 +155,7 @@ jobs:
bundle exec rake knapsack_pro:rspec
knapsack_rspec_system_admin:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
@@ -233,7 +233,7 @@ jobs:
if-no-files-found: ignore
knapsack_rspec_system_consumer:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
@@ -311,7 +311,7 @@ jobs:
if-no-files-found: ignore
knapsack_rspec_engines:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
@@ -389,7 +389,7 @@ jobs:
if-no-files-found: ignore
knapsack_rspec_test_the_rest:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10
@@ -459,7 +459,7 @@ jobs:
bundle exec rake knapsack_pro:rspec
non_knapsack_jest_karma:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:10

View File

@@ -1,5 +1,8 @@
# Basically, ignore everythings expect app/webpacker/controllers/*.js and app/webpacker/packs/*.js
*.css
*.scss
# Except v2
!/app/webpacker/css/admin/v2/**/*.scss
*.md
*.yml
*.yaml
@@ -9,10 +12,6 @@
babel.config.js
postcss.config.js
/app/webpacker/css/darkswarm/
/app/webpacker/css/mail/
/app/webpacker/css/shared/
/app/assets/
/config/
/coverage/

View File

@@ -1,3 +1 @@
{
"printWidth": 100
}
{}

View File

@@ -40,7 +40,6 @@ Metrics/BlockLength:
"resources",
"scenario",
"shared_examples",
"shared_examples_for",
"xdescribe",
]

View File

@@ -1,11 +1,41 @@
# This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 1400 --no-auto-gen-timestamp`
# using RuboCop version 1.51.0.
# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 1400`
# on 2023-04-14 06:27:24 UTC using RuboCop version 1.50.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 4
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
# Include: **/*.gemfile, **/Gemfile, **/gems.rb
Bundler/OrderedGems:
Exclude:
- 'Gemfile'
# Offense count: 4
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: Severity, Include.
# Include: **/*.gemspec
Gemspec/DeprecatedAttributeAssignment:
Exclude:
- 'engines/catalog/catalog.gemspec'
- 'engines/dfc_provider/dfc_provider.gemspec'
- 'engines/order_management/order_management.gemspec'
- 'engines/web/web.gemspec'
# Offense count: 4
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: Severity, Include.
# Include: **/*.gemspec
Gemspec/RequireMFA:
Exclude:
- 'engines/catalog/catalog.gemspec'
- 'engines/dfc_provider/dfc_provider.gemspec'
- 'engines/order_management/order_management.gemspec'
- 'engines/web/web.gemspec'
# Offense count: 4
# Configuration parameters: Severity, Include.
# Include: **/*.gemspec
@@ -84,6 +114,13 @@ Layout/EmptyLinesAroundBlockBody:
- 'spec/requests/checkout/concurrency_spec.rb'
- 'spec/system/admin/order_cycles/list_spec.rb'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
Layout/ExtraSpacing:
Exclude:
- 'spec/spec_helper.rb'
# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, IndentationWidth.
@@ -100,7 +137,7 @@ Layout/FirstHashElementIndentation:
Exclude:
- 'spec/services/products_renderer_spec.rb'
# Offense count: 10
# Offense count: 11
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
# SupportedHashRocketStyles: key, separator, table
@@ -109,6 +146,7 @@ Layout/FirstHashElementIndentation:
Layout/HashAlignment:
Exclude:
- 'app/controllers/spree/users_controller.rb'
- 'app/models/spree/image.rb'
- 'spec/migrations/migrate_customer_names_spec.rb'
- 'spec/models/enterprise_spec.rb'
- 'spec/system/admin/customers_spec.rb'
@@ -141,7 +179,7 @@ Layout/LeadingCommentSpace:
Exclude:
- 'spec/system/admin/enterprises_spec.rb'
# Offense count: 115
# Offense count: 114
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: space, no_space
@@ -198,13 +236,12 @@ Layout/LineEndStringConcatenationIndentation:
- 'spec/system/consumer/cookies_spec.rb'
- 'spec/system/consumer/shopping/cart_spec.rb'
# Offense count: 643
# Offense count: 605
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
# URISchemes: http, https
Layout/LineLength:
Exclude:
- 'app/components/confirm_modal_component.rb'
- 'app/controllers/admin/bulk_line_items_controller.rb'
- 'app/controllers/admin/enterprise_fees_controller.rb'
- 'app/controllers/admin/enterprise_relationships_controller.rb'
@@ -367,8 +404,6 @@ Layout/LineLength:
- 'spec/system/admin/adjustments_spec.rb'
- 'spec/system/admin/bulk_order_management_spec.rb'
- 'spec/system/admin/bulk_product_update_spec.rb'
- 'spec/system/admin/order_spec.rb'
- 'spec/system/admin/product_import_spec.rb'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
@@ -414,7 +449,7 @@ Layout/TrailingEmptyLines:
Exclude:
- 'Rakefile'
# Offense count: 73
# Offense count: 67
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowInHeredoc.
Layout/TrailingWhitespace:
@@ -422,6 +457,7 @@ Layout/TrailingWhitespace:
- 'app/controllers/spree/users_controller.rb'
- 'app/controllers/user_confirmations_controller.rb'
- 'app/models/enterprise.rb'
- 'app/models/spree/image.rb'
- 'spec/controllers/spree/credit_cards_controller_spec.rb'
- 'spec/controllers/user_confirmations_controller_spec.rb'
- 'spec/factories/order_factory.rb'
@@ -439,6 +475,7 @@ Layout/TrailingWhitespace:
- 'spec/system/admin/order_spec.rb'
- 'spec/system/admin/product_import_spec.rb'
- 'spec/system/admin/shipping_methods_spec.rb'
- 'spec/system/consumer/split_checkout_spec.rb'
# Offense count: 7
# This cop supports safe autocorrection (--autocorrect).
@@ -468,7 +505,7 @@ Lint/ConstantDefinitionInBlock:
- 'spec/validators/date_time_string_validator_spec.rb'
- 'spec/validators/integer_array_validator_spec.rb'
# Offense count: 6
# Offense count: 8
# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches.
Lint/DuplicateBranch:
Exclude:
@@ -581,7 +618,6 @@ Lint/UselessMethodDefinition:
- 'app/models/spree/gateway.rb'
# Offense count: 3
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: CheckForMethodsWithNoSideEffects.
Lint/Void:
Exclude:
@@ -652,12 +688,11 @@ Metrics/BlockNesting:
Exclude:
- 'app/models/spree/payment/processing.rb'
# Offense count: 47
# Offense count: 46
# Configuration parameters: CountComments, Max, CountAsOne.
Metrics/ClassLength:
Exclude:
- 'app/components/products_table_component.rb'
- 'app/controllers/admin/enterprise_fees_controller.rb'
- 'app/controllers/admin/enterprises_controller.rb'
- 'app/controllers/admin/order_cycles_controller.rb'
- 'app/controllers/admin/resource_controller.rb'
@@ -704,7 +739,7 @@ Metrics/ClassLength:
- 'lib/reporting/reports/enterprise_fee_summary/scope.rb'
- 'lib/reporting/reports/xero_invoices/base.rb'
# Offense count: 36
# Offense count: 35
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
Metrics/CyclomaticComplexity:
Exclude:
@@ -759,7 +794,7 @@ Metrics/MethodLength:
- 'lib/reporting/reports/xero_invoices/base.rb'
- 'lib/tasks/sample_data/product_factory.rb'
# Offense count: 49
# Offense count: 50
# Configuration parameters: CountComments, Max, CountAsOne.
Metrics/ModuleLength:
Exclude:
@@ -808,16 +843,16 @@ Metrics/ModuleLength:
- 'spec/models/spree/product_spec.rb'
- 'spec/models/spree/shipping_method_spec.rb'
- 'spec/models/spree/tax_rate_spec.rb'
- 'spec/models/spree/variant_spec.rb'
- 'spec/services/permissions/order_spec.rb'
- 'spec/services/variant_units/option_value_namer_spec.rb'
- 'spec/support/request/shop_workflow.rb'
- 'spec/support/request/stripe_stubs.rb'
# Offense count: 8
# Offense count: 7
# Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters.
Metrics/ParameterLists:
Exclude:
- 'app/components/confirm_modal_component.rb'
- 'app/helpers/angular_form_builder.rb'
- 'app/models/product_import/entry_processor.rb'
- 'lib/reporting/reports/xero_invoices/base.rb'
@@ -852,7 +887,6 @@ Naming/HeredocDelimiterNaming:
- 'app/models/content_configuration.rb'
# Offense count: 5
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyleForLeadingUnderscores.
# SupportedStylesForLeadingUnderscores: disallowed, required, optional
Naming/MemoizedInstanceVariableName:
@@ -931,6 +965,12 @@ Rails/ApplicationController:
Exclude:
- 'engines/dfc_provider/app/controllers/dfc_provider/base_controller.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/ApplicationJob:
Exclude:
- 'app/jobs/report_job.rb'
# Offense count: 5
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: NilOrEmpty, NotPresent, UnlessPresent.
@@ -1078,6 +1118,7 @@ Rails/InverseOf:
Exclude:
- 'app/models/enterprise.rb'
- 'app/models/order_cycle.rb'
- 'app/models/spree/adjustment.rb'
- 'app/models/spree/country.rb'
- 'app/models/spree/inventory_unit.rb'
- 'app/models/spree/line_item.rb'
@@ -1116,7 +1157,7 @@ Rails/LexicallyScopedActionFilter:
- 'app/controllers/spree/admin/zones_controller.rb'
- 'app/controllers/spree/users_controller.rb'
# Offense count: 8
# Offense count: 9
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/NegateInclude:
Exclude:
@@ -1125,10 +1166,11 @@ Rails/NegateInclude:
- 'app/models/product_import/spreadsheet_entry.rb'
- 'app/models/spree/order/checkout.rb'
- 'app/services/order_cart_reset.rb'
- 'engines/order_management/app/services/order_management/stock/estimator.rb'
- 'lib/spree/localized_number.rb'
- 'spec/support/matchers/table_matchers.rb'
# Offense count: 17
# Offense count: 18
Rails/OutputSafety:
Exclude:
- 'app/helpers/angular_form_helper.rb'
@@ -1173,7 +1215,7 @@ Rails/PluckInWhere:
Exclude:
- 'app/models/spree/variant.rb'
# Offense count: 30
# Offense count: 28
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/RedundantPresenceValidationOnBelongsTo:
Exclude:
@@ -1683,7 +1725,7 @@ Style/RedundantStringEscape:
- 'spec/controllers/spree/admin/shipping_methods_controller_spec.rb'
- 'spec/system/admin/enterprise_fees_spec.rb'
# Offense count: 208
# Offense count: 206
Style/Send:
Exclude:
- 'app/controllers/split_checkout_controller.rb'
@@ -1733,7 +1775,7 @@ Style/SlicingWithRange:
- 'engines/order_management/app/services/order_management/subscriptions/validator.rb'
- 'lib/discourse/single_sign_on.rb'
# Offense count: 28
# Offense count: 29
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Mode.
Style/StringConcatenation:
@@ -1754,6 +1796,7 @@ Style/StringConcatenation:
- 'lib/spree/core/environment_extension.rb'
- 'spec/models/spree/line_item_spec.rb'
- 'spec/models/spree/product_spec.rb'
- 'spec/models/spree/variant_spec.rb'
- 'spec/services/embedded_page_service_spec.rb'
- 'spec/support/api_helper.rb'
- 'spec/support/features/datepicker_helper.rb'

View File

@@ -71,5 +71,5 @@ From here, your pull request will progress through the [Review, Test, Merge & De
[slack-dev]: https://openfoodnetwork.slack.com/messages/C2GQ45KNU
[ofn-transifex]: https://www.transifex.com/open-food-foundation/open-food-network/
[i18n]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Internationalisation-%28i18n%29
[welcome-dev]: https://github.com/orgs/openfoodfoundation/projects/5
[welcome-dev]: https://github.com/orgs/openfoodfoundation/projects/2
[ci]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Continuous-Integration

10
Gemfile
View File

@@ -14,11 +14,11 @@ gem "aws-sdk-s3", require: false
gem "image_processing"
gem 'activemerchant', '>= 1.78.0'
gem 'rexml'
gem 'angular-rails-templates', '>= 0.3.0'
gem 'awesome_nested_set'
gem 'ransack', '~> 2.6.0'
gem 'responders'
gem 'rexml'
gem 'webpacker', '~> 5'
gem 'i18n'
@@ -63,7 +63,6 @@ gem 'devise-token_authenticatable'
gem 'jwt', '~> 2.3'
gem 'oauth2', '~> 1.4.7' # Used for Stripe Connect
gem 'datafoodconsortium-connector'
gem 'jsonapi-serializer'
gem 'pagy', '~> 5.1'
@@ -71,8 +70,8 @@ gem 'rswag-api'
gem 'rswag-ui'
gem 'omniauth_openid_connect'
gem 'omniauth-rails_csrf_protection'
gem 'openid_connect', '~> 1.3'
gem 'omniauth-rails_csrf_protection'
gem 'angularjs-rails', '1.8.0'
gem 'bugsnag'
@@ -151,7 +150,6 @@ group :test, :development do
gem 'capybara'
gem 'cuprite'
gem 'database_cleaner', require: false
gem 'debug', '>= 1.0.0'
gem "factory_bot_rails", '6.2.0', require: false
gem 'fuubar', '~> 2.5.1'
gem 'json_spec', '~> 1.1.4'
@@ -162,6 +160,7 @@ group :test, :development do
gem 'rswag-specs'
gem 'shoulda-matchers'
gem 'timecop'
gem 'debug', '>= 1.0.0'
end
group :test do
@@ -176,11 +175,10 @@ end
group :development do
gem 'debugger-linecache'
gem 'rails-erd'
gem 'foreman'
gem 'listen'
gem 'pry', '~> 0.13.0'
gem 'query_count'
gem 'rails-erd'
gem 'rubocop'
gem 'rubocop-rails'
gem 'spring'

View File

@@ -137,7 +137,7 @@ GEM
activerecord (>= 6.0, < 7.1)
acts_as_list (1.0.4)
activerecord (>= 4.2)
addressable (2.8.4)
addressable (2.8.2)
public_suffix (>= 2.0.2, < 6.0)
aes_key_wrap (1.1.0)
afm (0.2.2)
@@ -157,16 +157,16 @@ GEM
awesome_nested_set (3.5.0)
activerecord (>= 4.0.0, < 7.1)
aws-eventstream (1.2.0)
aws-partitions (1.760.0)
aws-sdk-core (3.171.1)
aws-partitions (1.742.0)
aws-sdk-core (3.171.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.64.0)
aws-sdk-kms (1.63.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.122.0)
aws-sdk-s3 (1.120.1)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
@@ -191,7 +191,7 @@ GEM
railties (>= 5.2)
thread-local (>= 1.1.0)
cancancan (1.15.0)
capybara (3.39.1)
capybara (3.39.0)
addressable
matrix
mini_mime (>= 0.1.3)
@@ -219,7 +219,7 @@ GEM
matrix
ruby-rc4 (>= 0.1.5)
concurrent-ruby (1.2.2)
connection_pool (2.4.1)
connection_pool (2.4.0)
crack (0.4.5)
rexml
crass (1.0.6)
@@ -234,16 +234,14 @@ GEM
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
datafoodconsortium-connector (1.0.0.pre.alpha.6)
virtual_assembly-semantizer (~> 1.0, >= 1.0.4)
date (3.3.3)
ddtrace (1.11.1)
ddtrace (1.10.1)
debase-ruby_core_source (>= 0.10.16, <= 3.2.0)
libdatadog (~> 2.0.0.1.0)
libddwaf (~> 1.8.2.0.0)
libddwaf (~> 1.6.2.0.0)
msgpack
debase-ruby_core_source (3.2.0)
debug (1.8.0)
debug (1.7.2)
irb (>= 1.5.0)
reline (>= 0.3.1)
debugger-linecache (1.2.0)
@@ -276,7 +274,7 @@ GEM
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faraday (2.7.5)
faraday (2.7.4)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-follow_redirects (0.3.0)
@@ -340,7 +338,7 @@ GEM
hiredis (0.6.3)
htmlentities (4.3.4)
httpclient (2.8.3)
i18n (1.13.0)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
i18n-js (3.9.2)
i18n (>= 0.6.6)
@@ -351,7 +349,7 @@ GEM
activerecord (>= 3.0)
io-console (0.6.0)
ipaddress (0.8.3)
irb (1.6.4)
irb (1.6.3)
reline (>= 0.3.0)
jmespath (1.6.2)
jquery-rails (4.4.0)
@@ -361,20 +359,12 @@ GEM
jquery-ui-rails (4.2.1)
railties (>= 3.2.16)
json (2.6.3)
json-canonicalization (0.3.1)
json-jwt (1.16.3)
activesupport (>= 4.2)
aes_key_wrap
bindata
faraday (~> 2.0)
faraday-follow_redirects
json-ld (3.2.3)
htmlentities (~> 4.3)
json-canonicalization (~> 0.3)
link_header (~> 0.0, >= 0.0.8)
multi_json (~> 1.15)
rack (~> 2.2)
rdf (~> 3.2, >= 3.2.9)
json-schema (3.0.0)
addressable (>= 2.8)
json_spec (1.1.5)
@@ -383,23 +373,22 @@ GEM
jsonapi-serializer (2.2.0)
activesupport (>= 4.2)
jwt (2.7.0)
knapsack_pro (4.1.0)
knapsack_pro (3.9.0)
rake
launchy (2.5.0)
addressable (~> 2.7)
letter_opener (1.8.1)
launchy (>= 2.2, < 3)
libdatadog (2.0.0.1.0)
libddwaf (1.8.2.0.0)
libddwaf (1.6.2.0.0)
ffi (~> 1.0)
libv8-node (16.10.0.0)
link_header (0.0.8)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
loofah (2.21.3)
loofah (2.20.0)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
nokogiri (>= 1.5.9)
mail (2.8.1)
mini_mime (>= 0.1.1)
net-imap
@@ -416,7 +405,7 @@ GEM
rake
mini_magick (4.11.0)
mini_mime (1.1.2)
mini_portile2 (2.8.2)
mini_portile2 (2.8.1)
mini_racer (0.6.3)
libv8-node (~> 16.10.0.0)
minitest (5.18.0)
@@ -424,7 +413,7 @@ GEM
money (~> 6.12)
money (6.16.0)
i18n (>= 0.6.4, <= 2)
msgpack (1.7.0)
msgpack (1.6.1)
multi_json (1.15.0)
multi_xml (0.6.0)
net-imap (0.3.4)
@@ -437,8 +426,8 @@ GEM
net-smtp (0.3.3)
net-protocol
nio4r (2.5.9)
nokogiri (1.15.2)
mini_portile2 (~> 2.8.2)
nokogiri (1.14.3)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
oauth2 (1.4.11)
faraday (>= 0.17.3, < 3.0)
@@ -473,10 +462,10 @@ GEM
paper_trail (12.3.0)
activerecord (>= 5.2)
request_store (~> 1.1)
parallel (1.23.0)
parallel (1.22.1)
paranoia (2.6.1)
activerecord (>= 5.1, < 7.1)
parser (3.2.2.1)
parser (3.2.2.0)
ast (~> 2.4.1)
paypal-sdk-core (0.3.4)
multi_json (~> 1.0)
@@ -490,20 +479,17 @@ GEM
ruby-rc4
ttfunk
pg (1.2.3)
power_assert (2.0.3)
power_assert (2.0.2)
private_address_check (0.5.0)
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (5.0.1)
puma (6.3.0)
puma (6.2.2)
nio4r (~> 2.0)
query_count (1.1.1)
activerecord (>= 4.2)
railties (>= 4.2)
raabro (1.4.0)
racc (1.6.2)
rack (2.2.7)
rack (2.2.6.4)
rack-mini-profiler (2.3.4)
rack (>= 1.2.0)
rack-oauth2 (1.21.3)
@@ -546,10 +532,9 @@ GEM
activesupport (>= 4.2)
choice (~> 0.2.0)
ruby-graphviz (~> 1.2)
rails-html-sanitizer (1.6.0)
loofah (~> 2.21)
nokogiri (~> 1.14)
rails-i18n (7.0.7)
rails-html-sanitizer (1.5.0)
loofah (~> 2.19, >= 2.19.1)
rails-i18n (7.0.6)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8)
rails_safe_tasks (1.0.0)
@@ -569,8 +554,6 @@ GEM
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rdf (3.2.9)
link_header (~> 0.0, >= 0.0.8)
redcarpet (3.6.0)
redis (4.8.1)
redis-client (0.14.1)
@@ -600,36 +583,36 @@ GEM
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
rspec-mocks (~> 3.12.0)
rspec-core (3.12.2)
rspec-core (3.12.1)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.3)
rspec-expectations (3.12.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.5)
rspec-mocks (3.12.3)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-rails (6.0.3)
rspec-rails (6.0.1)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
rspec-core (~> 3.12)
rspec-expectations (~> 3.12)
rspec-mocks (~> 3.12)
rspec-support (~> 3.12)
rspec-core (~> 3.11)
rspec-expectations (~> 3.11)
rspec-mocks (~> 3.11)
rspec-support (~> 3.11)
rspec-retry (0.6.2)
rspec-core (> 3.3)
rspec-support (3.12.0)
rswag-api (2.9.0)
rswag-api (2.8.0)
railties (>= 3.1, < 7.1)
rswag-specs (2.9.0)
rswag-specs (2.8.0)
activesupport (>= 3.1, < 7.1)
json-schema (>= 2.2, < 4.0)
railties (>= 3.1, < 7.1)
rspec-core (>= 2.14)
rswag-ui (2.9.0)
rswag-ui (2.8.0)
actionpack (>= 3.1, < 7.1)
railties (>= 3.1, < 7.1)
rubocop (1.51.0)
rubocop (1.50.2)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 3.2.0.0)
@@ -639,7 +622,7 @@ GEM
rubocop-ast (>= 1.28.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.28.1)
rubocop-ast (1.28.0)
parser (>= 3.2.1.0)
rubocop-rails (2.19.1)
activesupport (>= 4.2.0)
@@ -669,12 +652,12 @@ GEM
semantic_range (3.0.0)
shoulda-matchers (5.3.0)
activesupport (>= 5.2.0)
sidekiq (7.1.1)
sidekiq (7.0.8)
concurrent-ruby (< 2)
connection_pool (>= 2.3.0)
rack (>= 2.2.4)
redis-client (>= 0.14.0)
sidekiq-scheduler (5.0.3)
redis-client (>= 0.11.0)
sidekiq-scheduler (5.0.2)
rufus-scheduler (~> 3.2)
sidekiq (>= 6, < 8)
tilt (>= 1.4.0)
@@ -714,16 +697,16 @@ GEM
rack (>= 2, < 4)
railties (>= 5.2, < 8)
redis (>= 4.0, < 6.0)
stringex (2.8.6)
stringex (2.8.5)
stripe (8.5.0)
swd (1.3.0)
activesupport (>= 3)
attr_required (>= 0.0.5)
httpclient (>= 2.4)
temple (0.8.2)
test-unit (3.5.9)
test-unit (3.5.7)
power_assert
thor (1.2.2)
thor (1.2.1)
thread-local (1.1.0)
tilt (2.1.0)
timecop (0.9.6)
@@ -743,7 +726,7 @@ GEM
activemodel (>= 3.0.0)
public_suffix
vcr (6.1.0)
view_component (3.0.0)
view_component (2.82.0)
activesupport (>= 5.2.0, < 8.0)
concurrent-ruby (~> 1.0)
method_source (~> 1.0)
@@ -751,8 +734,6 @@ GEM
rails (>= 5.2, < 8.0)
stimulus_reflex (>= 3.5.0.pre2)
view_component (>= 2.28.0)
virtual_assembly-semantizer (1.0.4)
json-ld (~> 3.2, >= 3.2.3)
warden (1.2.9)
rack (>= 2.0.9)
web-console (4.2.0)
@@ -784,7 +765,7 @@ GEM
xml-simple (1.1.8)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.6.8)
zeitwerk (2.6.7)
PLATFORMS
ruby
@@ -818,7 +799,6 @@ DEPENDENCIES
combine_pdf
cuprite
database_cleaner
datafoodconsortium-connector
db2fog!
ddtrace
debug (>= 1.0.0)
@@ -877,7 +857,6 @@ DEPENDENCIES
private_address_check
pry (~> 0.13.0)
puma
query_count
rack-mini-profiler (< 3.0.0)
rack-rewrite
rack-timeout

View File

@@ -12,6 +12,5 @@ angular.module("ofn.admin", [
"admin.orders"
]).config ($httpProvider, $locationProvider, $qProvider) ->
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
# for the next line, you should also probably check file: app/assets/javascripts/admin/utils/utils.js.coffee
$locationProvider.hashPrefix('')
$qProvider.errorOnUnhandledRejections(false)

View File

@@ -7,6 +7,7 @@
// jquery and angular
//= require jquery2
//= require jquery_ujs
//= require jquery.ui.all
//= require jquery.powertip
//= require jquery.cookie

View File

@@ -22,7 +22,6 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
"order_bill_address_phone",
"order_bill_address_firstname",
"order_bill_address_lastname",
"order_bill_address_full_name",
"variant_product_supplier_name",
"order_email",
"order_number",
@@ -122,16 +121,16 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
else
StatusMessage.display 'failure', t "unsaved_changes_error"
$scope.cancelOrder = (order, sendEmailCancellation, restock_items) ->
$scope.cancelOrder = (order, sendEmailCancellation) ->
return $http(
method: 'GET'
url: "/admin/orders/#{order.number}/fire?e=cancel&send_cancellation_email=#{sendEmailCancellation}&restock_items=#{restock_items}")
url: "/admin/orders/#{order.number}/fire?e=cancel&send_cancellation_email=#{sendEmailCancellation}")
$scope.deleteLineItem = (lineItem) ->
if lineItem.order.item_count == 1
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) ->
ofnCancelOrderAlert((confirm, sendEmailCancellation) ->
if confirm
$scope.cancelOrder(lineItem.order, sendEmailCancellation, restock_items).then(->
$scope.cancelOrder(lineItem.order, sendEmailCancellation).then(->
$scope.refreshData()
)
else
@@ -153,11 +152,11 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
willCancelOrders = true if (order.item_count == itemsPerOrder.get(order).length)
if willCancelOrders
ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) ->
ofnCancelOrderAlert((confirm, sendEmailCancellation) ->
if confirm
itemsPerOrder.forEach (items, order) =>
if order.item_count == items.length
$scope.cancelOrder(order, sendEmailCancellation, restock_items).then(-> $scope.refreshData())
$scope.cancelOrder(order, sendEmailCancellation).then(-> $scope.refreshData())
else
Promise.all(LineItems.delete(item) for item in items).then(-> $scope.refreshData())
, "js.admin.deleting_item_will_cancel_order")

View File

@@ -0,0 +1,32 @@
angular.module("admin.orders").controller "bulkInvoiceCtrl", ($scope, $http, $timeout) ->
$scope.createBulkInvoice = ->
$scope.invoice_id = null
$scope.poll = 1
$scope.loading = true
$scope.message = null
$scope.error = null
$scope.poll_wait = 5 # 5 Seconds between each check
$scope.poll_retries = 80 # Maximum checks before stopping
$http.post('/admin/orders/invoices', {order_ids: $scope.selected_orders}).then (response) ->
$scope.invoice_id = response.data
$scope.pollBulkInvoice()
$scope.pollBulkInvoice = ->
$timeout($scope.nextPoll, $scope.poll_wait * 1000)
$scope.nextPoll = ->
$http.get('/admin/orders/invoices/'+$scope.invoice_id+'/poll').then (response) ->
$scope.loading = false
$scope.message = t('js.admin.orders.index.bulk_invoice_created')
.catch (response) ->
$scope.poll++
if $scope.poll > $scope.poll_retries
$scope.loading = false
$scope.error = t('js.admin.orders.index.bulk_invoice_failed')
return
$scope.pollBulkInvoice()

View File

@@ -0,0 +1,117 @@
angular.module("admin.orders").controller "ordersCtrl", ($scope, $timeout, RequestMonitor, Orders, SortOptions, $window, $filter, $location, KeyValueMapStore) ->
$scope.RequestMonitor = RequestMonitor
$scope.pagination = Orders.pagination
$scope.orders = Orders.all
$scope.sortOptions = SortOptions
$scope.per_page_options = [
{id: 15, name: t('js.admin.orders.index.per_page', results: 15)},
{id: 50, name: t('js.admin.orders.index.per_page', results: 50)},
{id: 100, name: t('js.admin.orders.index.per_page', results: 100)}
]
$scope.selected_orders = []
$scope.checkboxes = {}
$scope.selected = false
$scope.select_all = false
$scope.poll = 0
$scope.rowStatus = {}
KeyValueMapStore.localStorageKey = 'ordersFilters'
KeyValueMapStore.storableKeys = ["q", "sorting", "page", "per_page"]
$scope.initialise = ->
unless KeyValueMapStore.restoreValues($scope)
$scope.setDefaults()
$scope.fetchResults()
$scope.setDefaults = ->
$scope.per_page = 15
$scope.q = {
completed_at_not_null: true
}
e = new CustomEvent("flatpickr_clear");
window.dispatchEvent(e)
$scope.clearFilters = () ->
KeyValueMapStore.clearKeyValueMap()
$scope.setDefaults()
$scope.fetchResults()
$scope.fetchResults = (page=1) ->
startDateWithTime = $scope.appendStringIfNotEmpty($scope.q?.completed_at_gteq, ' 00:00:00')
endDateWithTime = $scope.appendStringIfNotEmpty($scope.q?.completed_at_lteq, ' 23:59:59')
$scope.resetSelected()
params = {
'q[completed_at_gteq]': startDateWithTime,
'q[completed_at_lteq]': endDateWithTime,
'q[state_eq]': $scope.q?.state_eq,
'q[number_cont]': $scope.q?.number_cont,
'q[email_cont]': $scope.q?.email_cont,
'q[bill_address_firstname_start]': $scope.q?.bill_address_firstname_start,
'q[bill_address_lastname_start]': $scope.q?.bill_address_lastname_start,
# Set default checkbox values to null. See: https://github.com/openfoodfoundation/openfoodnetwork/pull/3076#issuecomment-440010498
'q[completed_at_not_null]': $scope.q?.completed_at_not_null || null,
'q[distributor_id_in][]': $scope.q?.distributor_id_in,
'q[order_cycle_id_in][]': $scope.q?.order_cycle_id_in,
'q[s]': $scope.sorting || 'completed_at desc',
shipping_method_id: $scope.q?.shipping_method_id,
per_page: $scope.per_page,
page: page
}
KeyValueMapStore.setStoredValues($scope)
RequestMonitor.load(Orders.index(params).$promise)
$scope.appendStringIfNotEmpty = (baseString, stringToAppend) ->
return baseString unless baseString
return baseString if baseString.endsWith(stringToAppend)
baseString + stringToAppend
$scope.resetSelected = ->
$scope.selected_orders.length = 0
$scope.selected = false
$scope.select_all = false
$scope.checkboxes = {}
$scope.toggleSelection = (id) ->
index = $scope.selected_orders.indexOf(id)
if index == -1
$scope.selected_orders.push(id)
else
$scope.selected_orders.splice(index, 1)
$scope.toggleAll = ->
$scope.selected_orders.length = 0
$scope.orders.forEach (order) ->
$scope.checkboxes[order.id] = $scope.select_all
$scope.selected_orders.push order.id if $scope.select_all
$scope.$watch 'sortOptions', (sort) ->
return unless sort && sort.predicate != ""
$scope.sorting = sort.getSortingExpr()
$scope.fetchResults()
, true
$scope.capturePayment = (order) ->
$scope.rowAction('capture', order)
$scope.shipOrder = (order) ->
$scope.rowAction('ship', order)
$scope.rowAction = (action, order) ->
$scope.rowStatus[order.id] = "loading"
Orders[action](order).$promise.then (data) ->
$scope.rowStatus[order.id] = "success"
$timeout(->
$scope.rowStatus[order.id] = null
, 1500)
, (error) ->
$scope.rowStatus[order.id] = "error"
$scope.changePage = (newPage) ->
$scope.page = newPage
$scope.fetchResults(newPage)

View File

@@ -0,0 +1,5 @@
angular.module("admin.orders").directive "invoicesModal", ($modal) ->
restrict: 'C'
link: (scope, elem, attrs, ctrl) ->
elem.on "click", (ev) =>
scope.uploadModal = $modal.open(templateUrl: 'admin/modals/bulk_invoice.html', controller: ctrl, scope: scope, windowClass: 'simple-modal')

View File

@@ -15,7 +15,7 @@ $(document).ready(function() {
console.log(msg);
});
}
$('.admin-order-edit-form a.ship').click(handle_ship_click);
$('[data-hook=admin_order_edit_form] a.ship').click(handle_ship_click);
//handle shipping method edit click
$('a.edit-method').click(toggleMethodEdit);
@@ -37,7 +37,7 @@ $(document).ready(function() {
console.log(msg);
});
}
$('.admin-order-edit-form a.save-method').click(handle_shipping_method_save);
$('[data-hook=admin_order_edit_form] a.save-method').click(handle_shipping_method_save);
//handle tracking info edit/delete
@@ -64,8 +64,8 @@ $(document).ready(function() {
return Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipmentNumber + ".json");
}
$('.admin-order-edit-form a.save-tracking').click(saveTrackingInfo);
$('.admin-order-edit-form a.delete-tracking').click(deleteTrackingInfo);
$('[data-hook=admin_order_edit_form] a.save-tracking').click(saveTrackingInfo);
$('[data-hook=admin_order_edit_form] a.delete-tracking').click(deleteTrackingInfo);
// handle note edit/delete
@@ -96,8 +96,8 @@ $(document).ready(function() {
});
}
$('.admin-order-edit-form a.save-note').click(saveNote);
$('.admin-order-edit-form a.delete-note').click(deleteNote);
$('[data-hook=admin_order_edit_form] a.save-note').click(saveNote);
$('[data-hook=admin_order_edit_form] a.delete-note').click(deleteNote);
// Makes API call for notes/tracking info
makeApiCall = function(url, params) {

View File

@@ -100,7 +100,6 @@ adjustItems = function(shipment_number, variant_id, quantity, restock_item){
doAdjustItems(shipment_number, variant_id, quantity, inventory_units, restock_item, () => {
var redirectTo = new URL(Spree.routes.cancel_order.toString());
redirectTo.searchParams.append("send_cancellation_email", sendEmailCancellation);
redirectTo.searchParams.append("restock_item", restock_item);
window.location.href = redirectTo.toString();
});
}

View File

@@ -5,8 +5,8 @@ angular.module("admin.utils").factory "StatusMessage", ->
alert: {style: {color: 'grey'}}
notice: {style: {color: 'grey'}}
success: {style: {color: '#9fc820'}}
failure: {style: {color: '#C85136'}}
error: {style: {color: '#C85136'}}
failure: {style: {color: '#da5354'}}
error: {style: {color: '#da5354'}}
statusMessage:
text: ""

View File

@@ -1,4 +1,3 @@
angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider, $locationProvider) ->
# for the next line, you should also probably check file: app/assets/javascripts/admin/admin_ofn.js.coffee
$locationProvider.hashPrefix('')
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"

View File

@@ -1,34 +0,0 @@
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require jquery2
//= require admin/spree/spree-select2
//= require admin/spree/handlebar_extensions
//= require i18n/translations
//= require darkswarm/i18n.translate.js
//= require moment/min/moment.min.js
//= require moment/locale/ar.js
//= require moment/locale/ca.js
//= require moment/locale/de.js
//= require moment/locale/en-gb.js
//= require moment/locale/es.js
//= require moment/locale/fil.js
//= require moment/locale/fr.js
//= require moment/locale/it.js
//= require moment/locale/nb.js
//= require moment/locale/nl-be.js
//= require moment/locale/pt-br.js
//= require moment/locale/pt.js
//= require moment/locale/ru.js
//= require moment/locale/sv.js
//= require moment/locale/tr.js
//= require moment/locale/pl.js
//= require js-big-decimal/dist/web/js-big-decimal.min.js
window.angular = { module: function(noop){ return { value: function(){} } } }

View File

@@ -67,3 +67,11 @@ document.addEventListener "turbo:before-render", ->
rootscope = null
window.injector = null
true
document.addEventListener "ajax:beforeSend", (event) =>
window.Turbo.navigator.adapter.progressBar.setValue(0)
window.Turbo.navigator.adapter.progressBar.show()
document.addEventListener "ajax:complete", (event) =>
window.Turbo.navigator.adapter.progressBar.setValue(100)
window.Turbo.navigator.adapter.progressBar.hide()

View File

@@ -25,8 +25,6 @@ angular.module('Darkswarm').controller "OrderCycleChangeCtrl", ($scope, $rootSco
Cart.reloadFinalisedLineItems()
ChangeableOrdersAlert.reload()
$rootScope.$broadcast 'orderCycleSelected'
event = new CustomEvent('orderCycleSelected')
window.dispatchEvent(event)
$scope.closesInLessThan3Months = () ->
moment().diff(moment(OrderCycle.orders_close_at(), "YYYY-MM-DD HH:mm:SS Z"), 'days') > -75

View File

@@ -0,0 +1,22 @@
angular.module('Darkswarm').controller "PageSelectionCtrl", ($scope, $rootScope, $location) ->
$scope.selectedPage = ->
# The path looks like `/contact` for the URL `https://ofn.org/shop#/contact`.
# We remove the slash at the beginning.
page = $location.path()[1..]
return $scope.whitelist[0] unless page
# If the path points to an unrelated path like `/login`, stay where we were.
return $scope.lastPage unless page in $scope.whitelist
$scope.lastPage = page
page
$scope.whitelistPages = (pages) ->
$scope.whitelist = pages
$scope.lastPage = pages[0]
# when an order cycle is changed, ensure the shop tab is active to save a click
$rootScope.$on "orderCycleSelected", ->
if $scope.selectedPage() != "shop"
$location.path("shop")

View File

@@ -4,16 +4,7 @@
{{ 'admin.actions' | t }}
%i{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
%div.menu{ 'ng-show' => "expanded" }
%div{ 'ng-repeat' => "link in links" }
%a.menu_item{ 'ng-if': "link.method", href: '{{link.url}}', target: "{{link.target || '_self'}}", data: { method: "{{ link.method }}", "ujs-navigate": "false", confirm: "{{link.confirm}}" } }
%span
%i{ ng: { class: "link.icon" } }
%span {{ link.name }}
%a.menu_item{ 'ng-if': "link.confirm && !link.method", href: '{{link.url}}', target: "{{link.target || '_self'}}", "data-confirm": "{{link.confirm}}" }
%span
%i{ ng: { class: "link.icon" } }
%span {{ link.name }}
%a.menu_item{ 'ng-if': "!link.confirm && !link.method", href: '{{link.url}}', target: "{{link.target || '_self'}}" }
%span
%i{ ng: { class: "link.icon" } }
%span {{ link.name }}
%a.menu_item{ 'ng-repeat' => "link in links", href: '{{link.url}}', target: "{{link.target || '_self'}}", data: { method: "{{ link.method || 'get' }}", confirm: "{{link.confirm}}" } }
%span
%i{ ng: { class: "link.icon" } }
%span {{ link.name }}

View File

@@ -3,7 +3,7 @@
.seven.columns.alpha
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage == ''", style: 'StatusMessage.statusMessage.style' } }
{{ StatusMessage.statusMessage.text || "&nbsp;" }}
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage !== ''" }, style: 'color: #C85136' }
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage !== ''" }, style: 'color: #da5354' }
{{ StatusMessage.invalidMessage || "&nbsp;" }}
.nine.columns.omega.text-right{ ng: { transclude: true } }

View File

@@ -1,13 +0,0 @@
# frozen_string_literal: true
class SessionChannel < ApplicationCable::Channel
def self.for_request(request)
"SessionChannel:#{request.session.id}"
end
def subscribed
return reject if current_user.nil?
stream_from "SessionChannel:#{session_id}"
end
end

View File

@@ -1,12 +1,10 @@
# frozen_string_literal: true
class ConfirmModalComponent < ModalComponent
def initialize(id:, confirm_actions: nil, reflex: nil, controller: nil, message: nil, confirm_reflexes: nil)
def initialize(id:, confirm_actions: nil, controllers: nil, message: nil)
super(id: id, close_button: true)
@confirm_actions = confirm_actions
@reflex = reflex
@confirm_reflexes = confirm_reflexes
@controller = controller
@controllers = controllers
@message = message
end

View File

@@ -1,4 +1,4 @@
%div{ id: @id, "data-controller": "modal #{@controller}", "data-action": "keyup@document->modal#closeIfEscapeKey", "data-#{@controller}-reflex-value": @reflex }
%div{ id: @id, "data-controller": "modal #{@controllers}", "data-action": "keyup@document->modal#closeIfEscapeKey" }
.reveal-modal-bg.fade{ "data-modal-target": "background", "data-action": "click->modal#close" }
.reveal-modal.fade.tiny.help-modal{ "data-modal-target": "modal" }
= content
@@ -7,4 +7,4 @@
.modal-actions
%input{ class: "button icon-plus #{close_button_class}", type: 'button', value: t('js.admin.modals.cancel'), "data-action": "click->modal#close" }
%input{ class: "button icon-plus primary", type: 'button', value: t('js.admin.modals.confirm'), "data-action": @confirm_actions, "data-reflex": @confirm_reflexes }
%input{ class: "button icon-plus primary", type: 'button', value: t('js.admin.modals.confirm'), "data-action": @confirm_actions }

View File

@@ -1,6 +1,6 @@
%tr
- @columns.each do |column|
%td.products_column{class: column[:id]}
- if column[:id] == "name" && @image&.attachment.present?
- if column[:id] == "name" && @image
= image_tag @image.url(:mini)
= column[:value]

View File

@@ -173,7 +173,8 @@ class ProductsTableComponent < ViewComponentReflex::Component
:default_price,
:stock_locations,
:stock_items,
:variant_overrides
:variant_overrides,
{ option_values: :option_type }
]
]
end

View File

@@ -11,7 +11,7 @@ module Admin
@line_items = order_permissions.
editable_line_items.where(order_id: orders).
includes(:variant).
includes(variant: { option_values: :option_type }).
ransack(params[:q]).result.
reorder('spree_line_items.order_id ASC, spree_line_items.id ASC')

View File

@@ -18,8 +18,6 @@ module Admin
end
end
ContentConfig.updated_at = Time.zone.now
flash[:success] =
t(:successfully_updated, resource: I18n.t('admin.contents.edit.your_content'))

View File

@@ -7,7 +7,6 @@ module Admin
before_action :load_enterprise_fee_set, only: :index
before_action :load_data
before_action :check_enterprise_fee_input, only: [:bulk_update]
before_action :check_calculators_compatibility_with_taxes, only: [:bulk_update]
def index
@include_calculators = params[:include_calculators].present?
@@ -120,17 +119,5 @@ module Admin
end
end
end
def check_calculators_compatibility_with_taxes
enterprise_fee_bulk_params['collection_attributes'].each do |_, enterprise_fee|
next unless enterprise_fee['inherits_tax_category'] == "true"
next unless EnterpriseFee::PER_ORDER_CALCULATORS.include?(enterprise_fee['calculator_type'])
flash[:error] = I18n.t(
'activerecord.errors.models.enterprise_fee.inherit_tax_requires_per_item_calculator'
)
return redirect_to redirect_path
end
end
end
end

View File

@@ -29,6 +29,7 @@ module Admin
after_action :geocode_address_if_use_geocoder, only: [:create, :update]
helper 'spree/products'
include OrderCyclesHelper
def index
@@ -46,7 +47,6 @@ module Admin
def edit
@object = Enterprise.where(permalink: params[:id]).
includes(users: [:ship_address, :bill_address]).first
@object.build_custom_tab if @object.custom_tab.nil?
if params[:stimulus]
@enterprise.is_primary_producer = params[:is_primary_producer]
@enterprise.sells = params[:enterprise_sells]
@@ -64,8 +64,6 @@ module Admin
update_tag_rules(tag_rules_attributes) if tag_rules_attributes.present?
update_enterprise_notifications
delete_custom_tab if params[:custom_tab] == 'false'
if @object.update(enterprise_params)
flash[:success] = flash_message_for(@object, :successfully_updated)
respond_with(@object) do |format|
@@ -138,11 +136,6 @@ module Admin
protected
def delete_custom_tab
@object.custom_tab.destroy if @object.custom_tab.present?
enterprise_params.delete(:custom_tab_attributes)
end
def build_resource
enterprise = super
enterprise.address ||= Spree::Address.new
@@ -186,7 +179,7 @@ module Admin
if enterprises.present?
enterprises.includes(
supplied_products:
[:supplier, :variants, { master: [:images] }]
[:supplier, { master: [:images], variants: { option_values: :option_type } }]
)
end
when :index

View File

@@ -37,7 +37,7 @@ module Admin
end
def reset_absent_products
@importer = ProductImport::ProductImporter.new(File.new(file_path),
@importer = ProductImport::ProductImporter.new(File.new(params[:filepath]),
spree_current_user, import_into: params[:import_into], enterprises_to_reset: params[:enterprises_to_reset], updated_ids: params[:updated_ids], settings: params[:settings])
if params.key?(:enterprises_to_reset) && params.key?(:updated_ids)
@@ -56,7 +56,7 @@ module Admin
end
def process_data(method)
@importer = ProductImport::ProductImporter.new(File.new(file_path),
@importer = ProductImport::ProductImporter.new(File.new(params[:filepath]),
spree_current_user, start: params[:start], end: params[:end], settings: params[:settings])
begin
@@ -112,25 +112,5 @@ module Admin
def model_class
ProductImport::ProductImporter
end
def file_path
@file_path ||= validate_file_path(sanitize_file_path(params[:filepath]))
end
def sanitize_file_path(file_path)
FilePathSanitizer.new.sanitize(file_path, on_error: method(:raise_invalid_file_path))
end
def validate_file_path(file_path)
return file_path if file_path.to_s.match?(TEMP_FILE_PATH_REGEX)
raise_invalid_file_path
end
def raise_invalid_file_path
redirect_to '/admin/product_import', notice: I18n.t(:product_import_no_data_in_spreadsheet_notice)
raise 'Invalid File Path'
end
TEMP_FILE_PATH_REGEX = %r{^/tmp/product_import[A-Za-z0-9-]*/import\.csv$}
end
end

View File

@@ -77,7 +77,7 @@ module Admin
assign_view_data
if @blob
@error = ".report_taking_longer_html"
@error_url = @blob.expiring_service_url
@error_url = @blob.url
else
@error = ".report_taking_longer"
@error_url = ""

View File

@@ -26,14 +26,11 @@ module Admin
private
def load_enterprise
@enterprise = OpenFoodNetwork::Permissions
.new(spree_current_user)
.editable_enterprises
.find_by(permalink: params[:enterprise_id])
@enterprise = Enterprise.find_by permalink: params[:enterprise_id]
end
def permitted_resource_params
params.require(:voucher).permit(:code, :amount)
params.require(:voucher).permit(:code)
end
end
end

View File

@@ -47,15 +47,17 @@ module Api
def capture
authorize! :admin, order
payment_capture = OrderCaptureService.new(order)
pending_payment = order.pending_payments.first
if payment_capture.call
return payment_capture_failed unless order.payment_required? && pending_payment
if pending_payment.capture!
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
elsif payment_capture.gateway_error.present?
error_during_processing(payment_capture.gateway_error)
else
payment_capture_failed
end
rescue Spree::Core::GatewayError => e
error_during_processing(e)
end
private

View File

@@ -116,8 +116,9 @@ module Api
def product_query_includes
[
master: { images: { attachment_attachment: :blob } },
variants: [:default_price, :stock_locations, :stock_items, :variant_overrides]
master: [:images],
variants: [:default_price, :stock_locations, :stock_items, :variant_overrides,
{ option_values: :option_type }]
]
end

View File

@@ -9,12 +9,12 @@ module Api
before_action :product
def index
@variants = scope.ransack(params[:q]).result
@variants = scope.includes(option_values: :option_type).ransack(params[:q]).result
render json: @variants, each_serializer: Api::VariantSerializer
end
def show
@variant = scope.find(params[:id])
@variant = scope.includes(option_values: :option_type).find(params[:id])
render json: @variant, serializer: Api::VariantSerializer
end

View File

@@ -9,7 +9,6 @@ require 'spree/core/controller_helpers/common'
require 'open_food_network/referer_parser'
class ApplicationController < ActionController::Base
include CablecarResponses
include Pagy::Backend
include RequestTimeouts

View File

@@ -30,8 +30,6 @@ module CheckoutCallbacks
@order.manual_shipping_selection = true
@order.checkout_processing = true
@voucher_adjustment = @order.voucher_adjustments.first
redirect_to(main_app.shop_path) && return if redirect_to_shop?
redirect_to_cart_path && return unless valid_order_line_items?
end

View File

@@ -5,15 +5,10 @@ module WhiteLabel
include EnterprisesHelper
def hide_ofn_navigation(distributor = current_distributor)
return false unless OpenFoodNetwork::FeatureToggle.enabled?(:white_label)
# if the distributor has the hide_ofn_navigation preference set to true
# then we should hide the OFN navigation
@hide_ofn_navigation = distributor.hide_ofn_navigation
# if the distributor has the hide_ofn_navigation preference
# set to false, there is no need to check the white_label_logo preference
return unless @hide_ofn_navigation
@white_label_logo = distributor.white_label_logo
@white_label_distributor = distributor
end
end

View File

@@ -4,6 +4,7 @@ require 'open_food_network/enterprise_injection_data'
class EnterprisesController < BaseController
layout "darkswarm"
helper Spree::ProductsHelper
include OrderCyclesHelper
include SerializerHelper
include WhiteLabel

View File

@@ -30,8 +30,6 @@ class SplitCheckoutController < ::BaseController
end
def update
return process_voucher if params[:apply_voucher].present?
if confirm_order || update_order
return if performed?
@@ -61,27 +59,6 @@ class SplitCheckoutController < ::BaseController
replace("#flashes", partial("shared/flashes", locals: { flashes: flash }))
end
def render_voucher_section_or_redirect
respond_to do |format|
format.cable_ready { render_voucher_section }
format.html { redirect_to checkout_step_path(:payment) }
end
end
# Using the power of cable_car we replace only the #voucher_section instead of reloading the page
def render_voucher_section
render(
status: :ok,
cable_ready: cable_car.replace(
"#voucher-section",
partial(
"split_checkout/voucher_section",
locals: { order: @order, voucher_adjustment: @order.voucher_adjustments.first }
)
)
)
end
def order_error_messages
# Remove ship_address.* errors if no shipping method is not selected
remove_ship_address_errors if no_ship_address_needed?
@@ -202,47 +179,10 @@ class SplitCheckoutController < ::BaseController
selected_shipping_method.first.require_ship_address == false
end
def process_voucher
if add_voucher
render_voucher_section_or_redirect
elsif @order.errors.present?
render_error
end
end
def add_voucher
if params.dig(:order, :voucher_code).blank?
@order.errors.add(:voucher, I18n.t('split_checkout.errors.voucher_not_found'))
return false
end
# Fetch Voucher
voucher = Voucher.find_by(code: params[:order][:voucher_code], enterprise: @order.distributor)
if voucher.nil?
@order.errors.add(:voucher, I18n.t('split_checkout.errors.voucher_not_found'))
return false
end
adjustment = voucher.create_adjustment(voucher.code, @order)
if !adjustment.valid?
@order.errors.add(:voucher, I18n.t('split_checkout.errors.add_voucher_error'))
adjustment.errors.each { |error| @order.errors.import(error) }
return false
end
true
end
def summary_step?
params[:step] == "summary"
end
def payment_step?
params[:step] == "payment"
end
def advance_order_state
return if @order.complete?
@@ -312,14 +252,5 @@ class SplitCheckoutController < ::BaseController
def recalculate_tax
@order.create_tax_charge!
@order.update_order!
apply_voucher if @order.voucher_adjustments.present?
end
def apply_voucher
VoucherAdjustmentsService.calculate(@order)
# update order to take into account the voucher we applied
@order.update_order!
end
end

View File

@@ -65,7 +65,6 @@ module Spree
params.require(:order).permit(
:email,
:use_billing,
:customer_id,
bill_address_attributes: ::PermittedAttributes::Address.attributes,
ship_address_attributes: ::PermittedAttributes::Address.attributes
)

View File

@@ -9,37 +9,20 @@ module Spree
helper CheckoutHelper
before_action :load_order, only: [:edit, :update, :fire, :resend,
:invoice, :print, :distribution]
before_action :load_distribution_choices, only: [:new, :edit, :update, :distribution]
:invoice, :print]
before_action :load_distribution_choices, only: [:new, :edit, :update]
# Ensure that the distributor is set for an order when
before_action :ensure_distribution, only: :new
before_action :require_distributor_abn, only: :invoice
before_action :restore_saved_query!, only: :index
respond_to :html, :json
def index
orders = SearchOrders.new(search_params, spree_current_user).orders
@pagy, @orders = pagy(orders, items: params[:per_page] || 15)
update_search_results if searching?
end
def new
@order = Order.create
@order.created_by = spree_current_user
@order.generate_order_number
@order.save
redirect_to spree.distribution_admin_order_path(@order)
end
def distribution
return if order_params.blank?
on_update
@order.assign_attributes(order_params)
return unless @order.save(context: :set_distribution_step)
redirect_to spree.admin_order_customer_path(@order)
redirect_to spree.edit_admin_order_url(@order)
end
def edit
@@ -50,7 +33,12 @@ module Spree
end
def update
on_update
@order.recreate_all_fees!
unless @order.cart?
@order.create_tax_charge!
@order.update_order!
end
if params[:set_distribution_step] && @order.update(order_params)
return redirect_to spree.admin_order_customer_path(@order)
@@ -117,45 +105,6 @@ module Spree
private
def update_search_results
session[:admin_orders_search] = search_params
render cable_ready: cable_car.inner_html(
"#orders-index",
partial("spree/admin/orders/table", locals: { pagy: @pagy, orders: @orders })
)
end
def searching?
params[:q].present? && request.format.symbol == :cable_ready
end
def search_params
default_filters.deep_merge(
params.permit(:page, :per_page, :shipping_method_id, q: {})
).to_h.with_indifferent_access
end
def default_filters
{ q: { completed_at_not_null: 1, s: "completed_at desc" } }
end
def restore_saved_query!
return unless request.format.html?
@_params = ActionController::Parameters.new(session[:admin_orders_search] || {})
@stored_query = search_params.to_query
end
def on_update
@order.recreate_all_fees!
return if @order.cart?
@order.create_tax_charge!
@order.update_order!
end
def order_params
return params[:order] if params[:order].blank?
@@ -193,6 +142,17 @@ module Spree
ocs.closed +
ocs.undated
end
def ensure_distribution
unless @order
@order = Spree::Order.new
@order.generate_order_number
@order.save!
end
return if @order.distribution_set?
render 'set_distribution', locals: { order: @order }
end
end
end
end

View File

@@ -7,6 +7,7 @@ module Spree
before_action :load_data
before_action :validate_payment_method_provider, only: [:create]
before_action :load_hubs, only: [:new, :edit, :update]
before_action :validate_calculator_preferred_value, only: [:update]
respond_to :html
@@ -16,10 +17,6 @@ module Spree
@payment_method = payment_method_class.constantize.new(base_params)
@object = @payment_method
if !base_params["calculator_type"] && gateway_params["calculator_type"]
@payment_method.calculator_type = gateway_params["calculator_type"]
end
load_hubs
if @payment_method.save
@@ -47,7 +44,6 @@ module Spree
redirect_to spree.edit_admin_payment_method_path(@payment_method)
else
respond_with(@payment_method)
clear_preference_cache
end
end
@@ -186,23 +182,33 @@ module Spree
end
end
# Ensure the calculator to be updated is the correct type
if params_for_update["calculator_type"] && params_for_update["calculator_attributes"]
add_type_to_calculator_attributes(params_for_update)
end
params_for_update
end
end
def clear_preference_cache
@payment_method.calculator.preferences.each_key do |key|
Rails.cache.delete(@payment_method.calculator.preference_cache_key(key))
def validate_calculator_preferred_value
return if calculator_preferred_values.all? do |value|
preferred_value_from_params = gateway_params.dig(:calculator_attributes, value)
preferred_value_from_params.nil? || Float(preferred_value_from_params,
exception: false)
end
flash[:error] = I18n.t(:calculator_preferred_value_error)
redirect_to spree.edit_admin_payment_method_path(@payment_method)
end
def add_type_to_calculator_attributes(hash)
hash["calculator_attributes"]["type"] = hash["calculator_type"]
def calculator_preferred_values
[
:preferred_amount,
:preferred_flat_percent,
:preferred_flat_percent,
:preferred_first_item,
:preferred_additional_item,
:preferred_max_items,
:preferred_normal_amount,
:preferred_discount_amount,
:preferred_minimal_amount
]
end
end
end

View File

@@ -7,6 +7,7 @@ require 'open_food_network/permissions'
module Spree
module Admin
class ProductsController < ::Admin::ResourceController
helper 'spree/products'
include OpenFoodNetwork::SpreeApiKeyLoader
include OrderCyclesHelper
include EnterprisesHelper
@@ -22,6 +23,10 @@ module Spree
def create
delete_stock_params_and_set_after do
if params[:product][:prototype_id].present?
@prototype = Spree::Prototype.find(params[:product][:prototype_id])
end
@object.attributes = permitted_resource_params
if @object.save
flash[:success] = flash_message_for(@object, :successfully_created)
@@ -112,6 +117,7 @@ module Spree
def load_data
@taxons = Taxon.order(:name)
@option_types = OptionType.order(:name)
@tax_categories = TaxCategory.order(:name)
@shipping_categories = ShippingCategory.order(:name)
end
@@ -121,7 +127,7 @@ module Spree
end
def product_includes
[{ variants: [:images] },
[{ variants: [:images, { option_values: :option_type }] },
{ master: [:images, :default_price] }]
end

View File

@@ -5,6 +5,7 @@ require 'open_food_network/scope_variants_for_search'
module Spree
module Admin
class VariantsController < ::Admin::ResourceController
helper 'spree/products'
belongs_to 'spree/product', find_by: :permalink
before_action :assign_default_attributes, only: :new
@@ -80,6 +81,8 @@ module Spree
end
def create_before
option_values = params[:new_variant]
option_values&.each_value { |id| @object.option_values << OptionValue.find(id) }
@object.save
end

View File

@@ -10,7 +10,7 @@ module Spree
layout 'darkswarm'
rescue_from ActiveRecord::RecordNotFound, with: :render_404
helper 'spree/orders'
helper 'spree/products', 'spree/orders'
respond_to :html, :json

View File

@@ -1,32 +0,0 @@
# frozen_string_literal: true
class VoucherAdjustmentsController < BaseController
include CablecarResponses
def destroy
@order = current_order
@order.voucher_adjustments.find_by(id: params[:id])&.destroy
respond_to do |format|
format.cable_ready { render_voucher_section }
format.html { redirect_to checkout_step_path(:payment) }
end
end
private
# Using the power of cable_car we replace only the #voucher_section instead of reloading the page
def render_voucher_section
render(
status: :ok,
cable_ready: cable_car.replace(
"#voucher-section",
partial(
"split_checkout/voucher_section",
locals: { order: @order, voucher_adjustment: @order.voucher_adjustments.first }
)
)
)
end
end

View File

@@ -49,8 +49,13 @@ module Admin
{ name: 'tag_rules', icon_class: "icon-random", show: is_shop },
{ name: 'shop_preferences', icon_class: "icon-shopping-cart", show: is_shop },
{ name: 'users', icon_class: "icon-user", show: true },
{ name: 'white_label', icon_class: "icon-leaf", show: true },
]
] + [add_white_label_if_feature_activated].compact
end
def add_white_label_if_feature_activated
return nil unless OpenFoodNetwork::FeatureToggle.enabled?(:white_label)
{ name: 'white_label', icon_class: "icon-leaf", show: true }
end
end
end

View File

@@ -68,14 +68,4 @@ module ApplicationHelper
wicked_pdf_stylesheet_pack_tag(source)
end
end
def cache_with_locale(key = nil, options = {}, &block)
cache(cache_key_with_locale(key, I18n.locale), options) do
yield(block)
end
end
def cache_key_with_locale(key, locale)
Array.wrap(key) + [locale.to_s, I18nDigests.for_locale(locale)]
end
end

View File

@@ -87,10 +87,4 @@ module EnterprisesHelper
main_app.producers_url
end
end
def main_logo_link(enterprise)
return enterprise.white_label_logo_link if enterprise&.white_label_logo_link.present?
main_app.root_path
end
end

View File

@@ -19,27 +19,15 @@ module ShopHelper
def shop_tabs
[
{ name: 'home', title: t(:shopping_tabs_home), show: show_home_tab?,
default: show_home_tab? },
{ name: 'shop', title: t(:shopping_tabs_shop), show: !require_customer?,
default: !show_home_tab? },
{ name: 'home', title: t(:shopping_tabs_home), show: show_home_tab? },
{ name: 'shop', title: t(:shopping_tabs_shop), show: !require_customer? },
{ name: 'about', title: t(:shopping_tabs_about), show: true },
{ name: 'producers', title: t(:shopping_tabs_producers), show: true },
{ name: 'contact', title: t(:shopping_tabs_contact), show: true },
{ name: 'groups', title: t(:shopping_tabs_groups), show: show_groups_tabs? },
custom_tab,
{ name: 'groups', title: t(:shopping_tabs_groups), show: current_distributor.groups.any? },
].select{ |tab| tab[:show] }
end
def custom_tab
{
name: "custom_#{current_distributor.custom_tab&.title&.parameterize}",
title: current_distributor.custom_tab&.title,
show: current_distributor.custom_tab.present?,
custom: true,
}
end
def shop_tab_names
shop_tabs.map { |tab| tab[:name] }
end
@@ -64,10 +52,4 @@ module ShopHelper
true
end
private
def show_groups_tabs?
!current_distributor.hide_groups_tab? && current_distributor.groups.any?
end
end

View File

@@ -29,8 +29,8 @@ module Spree
def preference_field_tag(name, value, options)
case options[:type]
when :integer, :decimal
number_field_tag(name, value, preference_field_options(options))
when :integer
text_field_tag(name, value, preference_field_options(options))
when :boolean
hidden_field_tag(name, 0) +
check_box_tag(name, 1, value, preference_field_options(options))
@@ -49,8 +49,8 @@ module Spree
def preference_field_for(form, field, options, object)
case options[:type]
when :integer, :decimal
form.number_field(field, preference_field_options(options))
when :integer
form.text_field(field, preference_field_options(options))
when :boolean
form.check_box(field, preference_field_options(options))
when :string
@@ -80,24 +80,26 @@ module Spree
end
def preference_field_options(options)
field_options =
case options[:type]
when :integer
{ size: 10, class: 'input_integer', step: 1 }
when :decimal
# Allow any number of decimal places
{ size: 10, class: 'input_integer', step: :any }
when :boolean
{}
when :string
{ size: 10, class: 'input_string fullwidth' }
when :password
{ size: 10, class: 'password_string fullwidth' }
when :text
{ rows: 15, cols: 85, class: 'fullwidth' }
else
{ size: 10, class: 'input_string fullwidth' }
end
field_options = case options[:type]
when :integer
{ size: 10,
class: 'input_integer' }
when :boolean
{}
when :string
{ size: 10,
class: 'input_string fullwidth' }
when :password
{ size: 10,
class: 'password_string fullwidth' }
when :text
{ rows: 15,
cols: 85,
class: 'fullwidth' }
else
{ size: 10,
class: 'input_string fullwidth' }
end
field_options.merge!(
readonly: options[:readonly],

View File

@@ -34,6 +34,7 @@ module Spree
link = link_to_with_icon(options[:icon], titleized_label, destination_url)
css_classes << 'tab-with-icon'
else
titleized_label = raw("<span class='text'>#{titleized_label}</span>")
link = link_to(titleized_label, destination_url)
end

View File

@@ -62,6 +62,7 @@ module Spree
{ name: t(:resend_confirmation),
url: spree.resend_admin_order_path(@order),
icon: 'icon-email',
method: 'post',
confirm: t(:confirm_resend_order_confirmation) }
end
@@ -111,7 +112,7 @@ module Spree
event_label = I18n.t("cancel", scope: "actions")
button_link_to(event_label,
fire_admin_order_url(@order, e: "cancel"),
method: :put, icon: "icon-cancel", form_id: "cancel_order_form")
method: :put, icon: "icon-remove", form_id: "cancel_order_form")
end
def resume_event_link

View File

@@ -0,0 +1,13 @@
# frozen_string_literal: true
module Spree
module ProductsHelper
def product_has_variant_unit_option_type?(product)
product.option_types.any? { |option_type| variant_unit_option_type? option_type }
end
def variant_unit_option_type?(option_type)
Spree::Product.all_variant_unit_option_types.include? option_type
end
end
end

View File

@@ -1,10 +1,7 @@
# frozen_string_literal: true
class BulkInvoiceJob < ApplicationJob
include CableReady::Broadcaster
delegate :render, to: ActionController::Base
def perform(order_ids, filepath, options = {})
def perform(order_ids, filepath)
pdf = CombinePDF.new
sorted_orders(order_ids).each do |order|
@@ -14,8 +11,6 @@ class BulkInvoiceJob < ApplicationJob
end
pdf.save filepath
broadcast(filepath, options[:channel]) if options[:channel]
end
private
@@ -23,22 +18,10 @@ class BulkInvoiceJob < ApplicationJob
# Ensures the records are returned in the same order the ids were originally given in
def sorted_orders(order_ids)
orders_by_id = Spree::Order.where(id: order_ids).to_a.index_by(&:id)
order_ids.map { |id| orders_by_id[id.to_i] }
order_ids.map { |id| orders_by_id[id] }
end
def renderer
@renderer ||= InvoiceRenderer.new
end
def broadcast(filepath, channel)
file_id = filepath.split("/").last.split(".").first
cable_ready[channel].
inner_html(
selector: "#bulk_invoices_modal .modal-content",
html: render(partial: "spree/admin/orders/bulk/invoice_link",
locals: { invoice_url: "/admin/orders/invoices/#{file_id}" })
).
broadcast
end
end

View File

@@ -2,24 +2,9 @@
# Renders a report and stores it in a given blob.
class ReportJob < ApplicationJob
NOTIFICATION_TIME = 5.seconds
def perform(report_class, user, params, format, blob)
start_time = Time.zone.now
report = report_class.new(user, params, render: true)
result = report.render_as(format)
blob.store(result)
execution_time = Time.zone.now - start_time
email_result(user, blob) if execution_time > NOTIFICATION_TIME
end
def email_result(user, blob)
ReportMailer.with(
to: user.email,
blob: blob,
).report_ready.deliver_later
end
end

View File

@@ -60,7 +60,7 @@ class ProducerMailer < ApplicationMailer
def line_items_from(order_cycle, producer)
@line_items ||= Spree::LineItem.
includes(variant: [:product]).
includes(:option_values, variant: [:product, { option_values: :option_type }]).
from_order_cycle(order_cycle).
sorted_by_name_and_unit_value.
merge(Spree::Product.with_deleted.in_supplier(producer)).

View File

@@ -1,12 +0,0 @@
# frozen_string_literal: true
class ReportMailer < ApplicationMailer
def report_ready
# When we are in a background job then we don't have an HTTP request object
# and we need to tell ActiveStorage the hostname to generate URLs.
ActiveStorage::Current.url_options ||= url_options
@blob = params[:blob]
mail(params)
end
end

View File

@@ -28,7 +28,6 @@ module Spree
def confirm_email_for_customer(order_or_order_id, resend = false)
@order = find_order(order_or_order_id)
@hide_ofn_navigation = @order.distributor.hide_ofn_navigation
I18n.with_locale valid_locale(@order.user) do
subject = mail_subject(t('spree.order_mailer.confirm_email.subject'), resend)
mail(to: @order.email,

View File

@@ -11,7 +11,6 @@ class SubscriptionMailer < ApplicationMailer
def confirmation_email(order)
@type = 'confirmation'
@order = order
@hide_ofn_navigation = @order.distributor.hide_ofn_navigation
send_mail(order)
end

View File

@@ -1,11 +1,14 @@
# frozen_string_literal: false
require 'spree/localized_number'
module Calculator
class FlatPercentItemTotal < Spree::Calculator
extend Spree::LocalizedNumber
preference :flat_percent, :decimal, default: 0
validates :preferred_flat_percent,
numericality: true
localize_number :preferred_flat_percent
def self.description
Spree.t(:flat_percent)

View File

@@ -1,15 +1,18 @@
# frozen_string_literal: true
require 'spree/localized_number'
class Calculator::FlatPercentPerItem < Spree::Calculator
# Spree's FlatPercentItemTotal calculator sums all amounts, and then calculates a percentage
# on them.
# In the cart, we display line item individual amounts rounded, so to have consistent
# calculations we do the same internally. Here, we round adjustments at the individual
# item level first, then multiply by the item quantity.
extend Spree::LocalizedNumber
preference :flat_percent, :decimal, default: 0
validates :preferred_flat_percent,
numericality: true
localize_number :preferred_flat_percent
def self.description
I18n.t(:flat_percent_per_item)

View File

@@ -1,11 +1,14 @@
# frozen_string_literal: false
require 'spree/localized_number'
module Calculator
class FlatRate < Spree::Calculator
extend Spree::LocalizedNumber
preference :amount, :decimal, default: 0
validates :preferred_amount,
numericality: true
localize_number :preferred_amount
def self.description
I18n.t(:flat_rate_per_order)

View File

@@ -1,14 +1,17 @@
# frozen_string_literal: false
require 'spree/localized_number'
module Calculator
class FlexiRate < Spree::Calculator
extend Spree::LocalizedNumber
preference :first_item, :decimal, default: 0.0
preference :additional_item, :decimal, default: 0.0
preference :max_items, :integer, default: 0
validates :preferred_first_item,
:preferred_additional_item,
numericality: true
localize_number :preferred_first_item,
:preferred_additional_item
def self.description
I18n.t(:flexible_rate)

View File

@@ -1,11 +1,14 @@
# frozen_string_literal: false
require 'spree/localized_number'
module Calculator
class PerItem < Spree::Calculator
extend Spree::LocalizedNumber
preference :amount, :decimal, default: 0
validates :preferred_amount,
numericality: true
localize_number :preferred_amount
def self.description
I18n.t(:flat_rate_per_item)

View File

@@ -1,15 +1,18 @@
# frozen_string_literal: false
require 'spree/localized_number'
module Calculator
class PriceSack < Spree::Calculator
extend Spree::LocalizedNumber
preference :minimal_amount, :decimal, default: 0
preference :normal_amount, :decimal, default: 0
preference :discount_amount, :decimal, default: 0
validates :preferred_minimal_amount,
:preferred_normal_amount,
:preferred_discount_amount,
numericality: true
localize_number :preferred_minimal_amount,
:preferred_normal_amount,
:preferred_discount_amount
def self.description
I18n.t(:price_sack)

View File

@@ -1,10 +1,15 @@
# frozen_string_literal: true
require 'spree/localized_number'
module Calculator
class Weight < Spree::Calculator
extend Spree::LocalizedNumber
preference :unit_from_list, :string, default: "kg"
preference :per_unit, :decimal, default: 0.0
localize_number :preferred_per_unit
def self.description
I18n.t('spree.weight')
end

View File

@@ -83,19 +83,4 @@ class ContentConfiguration < Spree::Preferences::Configuration
# User Guide
preference :user_guide_link, :string, default: 'https://guide.openfoodnetwork.org/'
# ContentConfig Caching
preference :updated_at_timestamp, :integer, default: Time.zone.today.to_time.to_i
def updated_at
Time.zone.at updated_at_timestamp
end
def updated_at=(time)
self.updated_at_timestamp = time.to_i
end
def cache_key
"ContentConfig:#{updated_at_timestamp}"
end
end

View File

@@ -1,5 +0,0 @@
# frozen_string_literal: false
class CustomTab < ApplicationRecord
belongs_to :enterprise, optional: false
end

View File

@@ -15,10 +15,6 @@ class Enterprise < ApplicationRecord
medium: { resize_to_fill: [720, 156] },
large: { resize_to_fill: [1200, 260] },
}.freeze
WHITE_LABEL_LOGO_SIZES = {
default: { gravity: "Center", resize_to_fill: [217, 44] },
mobile: { gravity: "Center", resize_to_fill: [75, 26] },
}.freeze
VALID_INSTAGRAM_REGEX = %r{\A[a-zA-Z0-9._]{1,30}([^/-]*)\z}
searchable_attributes :sells, :is_primary_producer, :name
@@ -70,7 +66,6 @@ class Enterprise < ApplicationRecord
has_many :tag_rules
has_one :stripe_account, dependent: :destroy
has_many :vouchers
has_one :custom_tab, dependent: :destroy
delegate :latitude, :longitude, :city, :state_name, to: :address
@@ -85,12 +80,10 @@ class Enterprise < ApplicationRecord
reject_if: lambda { |tag_rule|
tag_rule[:preferred_customer_tags].blank?
}
accepts_nested_attributes_for :custom_tab
has_one_attached :logo
has_one_attached :promo_image
has_one_attached :terms_and_conditions
has_one_attached :white_label_logo
validates :logo,
processable_image: true,
@@ -309,14 +302,6 @@ class Enterprise < ApplicationRecord
)
end
def white_label_logo_url(name = :default)
return unless white_label_logo.variable?
Rails.application.routes.url_helpers.url_for(
white_label_logo.variant(WHITE_LABEL_LOGO_SIZES[name])
)
end
def website
strip_url self[:website]
end

View File

@@ -58,12 +58,6 @@ class EnterpriseFee < ApplicationRecord
elsif inherits_tax_category_changed?
self.tax_category_id = nil if inherits_tax_category?
end
if inherits_tax_category? && PER_ORDER_CALCULATORS.include?(calculator_type)
errors.add(:base, :inherit_tax_requires_per_item_calculator)
throw :abort
end
true
end
end

View File

@@ -16,7 +16,7 @@ class Exchange < ApplicationRecord
belongs_to :sender, class_name: 'Enterprise'
belongs_to :receiver, class_name: 'Enterprise'
has_many :exchange_variants, dependent: :delete_all
has_many :exchange_variants, dependent: :destroy
has_many :variants, through: :exchange_variants
has_many :exchange_fees, dependent: :destroy
@@ -25,8 +25,6 @@ class Exchange < ApplicationRecord
validates :order_cycle, :sender, :receiver, presence: true
validates :sender_id, uniqueness: { scope: [:order_cycle_id, :receiver_id, :incoming] }
before_destroy :delete_related_exchange_variants, prepend: true
after_save :touch_receiver
accepts_nested_attributes_for :variants
@@ -88,9 +86,9 @@ class Exchange < ApplicationRecord
exchange = dup
exchange.order_cycle = new_order_cycle
exchange.enterprise_fee_ids = enterprise_fee_ids
exchange.variant_ids = variant_ids
exchange.tag_ids = tag_ids
exchange.save!
clone_all_exchange_variants(exchange.id)
exchange
end
@@ -107,26 +105,4 @@ class Exchange < ApplicationRecord
receiver.touch_later
end
private
# An Order Cycle can have thousands of ExchangeVariants.
# It's a simple association without any callbacks on creation. So we can
# insert in bulk and improve the performance tenfold for large order cycles.
def clone_all_exchange_variants(exchange_id)
return unless variant_ids.any?
ExchangeVariant.insert_all( # rubocop:disable Rails/SkipsModelValidations
variant_ids.map{ |variant_id| { variant_id: variant_id, exchange_id: exchange_id } }
)
end
def delete_related_exchange_variants
return unless incoming?
ExchangeVariant.where(variant_id: variant_ids).
joins(:exchange).
where(exchanges: { order_cycle: order_cycle, incoming: false }).
delete_all
end
end

View File

@@ -3,15 +3,9 @@
class ExchangeVariant < ApplicationRecord
belongs_to :exchange
belongs_to :variant, class_name: 'Spree::Variant'
after_destroy :destroy_related_outgoing_variants
after_destroy :delete_related_outgoing_variants
def delete_related_outgoing_variants
return unless exchange.incoming?
ExchangeVariant.where(variant_id: variant_id).
joins(:exchange).
where(exchanges: { order_cycle: exchange.order_cycle, incoming: false }).
delete_all
def destroy_related_outgoing_variants
VariantDeleter.new.destroy_related_outgoing_variants(variant_id, exchange.order_cycle)
end
end

View File

@@ -2,9 +2,6 @@
# Stores a generated report.
class ReportBlob < ActiveStorage::Blob
# AWS S3 limits URL expiry to one week.
LIFETIME = 1.week
def self.create_for_upload_later!(filename)
# ActiveStorage discourages modifying a blob later but we need a blob
# before we know anything about the report file. It enables us to use the
@@ -15,7 +12,7 @@ class ReportBlob < ActiveStorage::Blob
checksum: "0",
content_type: content_type(filename),
).tap do |blob|
ActiveStorage::PurgeJob.set(wait: LIFETIME).perform_later(blob)
ActiveStorage::PurgeJob.set(wait: 1.month).perform_later(blob)
end
end
@@ -34,10 +31,6 @@ class ReportBlob < ActiveStorage::Blob
end
def result
@result ||= download.force_encoding(Encoding::UTF_8)
end
def expiring_service_url
url(expires_in: LIFETIME)
@result ||= download
end
end

View File

@@ -22,6 +22,8 @@ module Spree
can :manage, :all
else
can [:index, :read], Country
can [:index, :read], OptionType
can [:index, :read], OptionValue
can :create, Order
can :read, Order do |order, token|
order.user == user || order.token && token == order.token
@@ -272,7 +274,7 @@ module Spree
# Enterprise User can access orders that are placed inside a OC they coordinate
order.order_cycle&.coordinated_by?(user)
end
can [:admin, :bulk_management, :managed, :distribution], Spree::Order do
can [:admin, :bulk_management, :managed], Spree::Order do
user.admin? || user.enterprises.any?(&:is_distributor)
end
can [:admin, :create, :show, :poll], :invoice

View File

@@ -4,7 +4,7 @@ module Spree
class Address < ApplicationRecord
include AddressDisplay
searchable_attributes :firstname, :lastname, :phone, :full_name
searchable_attributes :firstname, :lastname, :phone
searchable_associations :country, :state
belongs_to :country, class_name: "Spree::Country"
@@ -28,12 +28,6 @@ module Spree
alias_attribute :last_name, :lastname
delegate :name, to: :state, prefix: true, allow_nil: true
ransacker :full_name, formatter: proc { |value| value.to_s } do |parent|
Arel::Nodes::SqlLiteral.new(
"CONCAT(#{parent.table_name}.firstname, ' ', #{parent.table_name}.lastname)"
)
end
def self.default
new(country: DefaultCountry.country)
end

View File

@@ -42,6 +42,9 @@ module Spree
belongs_to :order, class_name: "Spree::Order"
belongs_to :tax_category, class_name: 'Spree::TaxCategory'
belongs_to :tax_rate, -> { where spree_adjustments: { originator_type: 'Spree::TaxRate' } },
foreign_key: 'originator_id'
validates :label, presence: true
validates :amount, numericality: true

View File

@@ -88,6 +88,7 @@ module Spree
# Default mail headers settings
preference :mails_from, :string, default: 'ofn@example.com'
preference :mail_bcc, :string, default: 'ofn@example.com'
preference :intercept_email, :string, default: nil
# Default smtp settings
preference :mail_host, :string, default: 'localhost'

View File

@@ -9,7 +9,7 @@ module Spree
include LineItemStockChanges
searchable_attributes :price, :quantity, :order_id, :variant_id, :tax_category_id
searchable_associations :order, :order_cycle, :variant, :product, :supplier, :tax_category
searchable_associations :order, :order_cycle, :variant, :product, :supplier, :tax_category, :option_values
searchable_scopes :with_tax, :without_tax
belongs_to :order, class_name: "Spree::Order", inverse_of: :line_items
@@ -22,6 +22,9 @@ module Spree
has_many :adjustments, as: :adjustable, dependent: :destroy
has_and_belongs_to_many :option_values, join_table: 'spree_option_values_line_items',
class_name: 'Spree::OptionValue'
before_validation :adjust_quantity
before_validation :copy_price
before_validation :copy_tax_category
@@ -39,16 +42,12 @@ module Spree
before_save :update_inventory
before_save :calculate_final_weight_volume, if: :quantity_changed?,
unless: :final_weight_volume_changed?
before_save :assign_units, if: ->(line_item) {
line_item.new_record? || line_item.final_weight_volume_changed?
}
before_destroy :update_inventory_before_destroy
after_destroy :update_order
after_save :update_order
after_save :update_units
before_destroy :update_inventory_before_destroy
after_destroy :update_order
delegate :product, :variant_unit, :unit_description, :display_name, :display_as, to: :variant
delegate :product, :unit_description, :display_name, to: :variant
attr_accessor :skip_stock_check, :target_shipment # Allows manual skipping of Stock::AvailabilityValidator

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
module Spree
class OptionType < ApplicationRecord
has_many :option_values, -> { order(:position) }, dependent: :destroy
has_many :product_option_types, dependent: :destroy
has_many :products, through: :product_option_types
validates :name, :presentation, presence: true
default_scope -> { order("#{table_name}.position") }
accepts_nested_attributes_for :option_values,
reject_if: lambda { |ov|
ov[:name].blank? || ov[:presentation].blank?
},
allow_destroy: true
end
end

View File

@@ -0,0 +1,12 @@
# frozen_string_literal: true
module Spree
class OptionValue < ApplicationRecord
belongs_to :option_type
acts_as_list scope: :option_type
has_and_belongs_to_many :variants, join_table: 'spree_option_values_variants',
class_name: "Spree::Variant"
validates :name, :presentation, presence: true
end
end

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
module Spree
class OptionValuesLineItem < ApplicationRecord
belongs_to :line_item, class_name: 'Spree::LineItem'
belongs_to :option_value, class_name: 'Spree::OptionValue'
end
end

View File

@@ -62,13 +62,6 @@ module Spree
has_many :line_item_adjustments, through: :line_items, source: :adjustments
has_many :shipment_adjustments, through: :shipments, source: :adjustments
has_many :all_adjustments, class_name: 'Spree::Adjustment', dependent: :destroy
has_many :voucher_adjustments,
-> {
where(originator_type: 'Voucher')
.order("#{Spree::Adjustment.table_name}.created_at ASC")
},
class_name: 'Spree::Adjustment',
dependent: :destroy
belongs_to :order_cycle
belongs_to :distributor, class_name: 'Enterprise'
@@ -104,8 +97,6 @@ module Spree
validates :email, presence: true,
format: /\A([\w.%+\-']+)@([\w\-]+\.)+(\w{2,})\z/i,
if: :require_email
validates :order_cycle, presence: true, on: :set_distribution_step
validates :distributor, presence: true, on: :set_distribution_step
make_permalink field: :number

View File

@@ -63,7 +63,7 @@ module Spree
# first unshipped that's leaving from a stock_location that stocks this variant
def determine_target_shipment(variant)
target_shipment = order.shipments.detect do |shipment|
(shipment.ready? || shipment.pending?) && shipment.contains?(variant)
(shipment.ready? || shipment.pending?) && shipment.include?(variant)
end
target_shipment || order.shipments.detect do |shipment|

View File

@@ -17,7 +17,6 @@ module Spree
validates :name, presence: true
validate :distributor_validation
validates_associated :calculator
after_initialize :init
@@ -41,8 +40,9 @@ module Spree
where(id: non_unique_matches.map(&:id))
}
scope :for_distributor, ->(distributor) {
joins(:distributors).where('enterprises.id = ?', distributor)
scope :for_distributor, lambda { |distributor|
joins(:distributors).
where('enterprises.id = ?', distributor)
}
scope :for_subscriptions, -> { where(type: Subscription::ALLOWED_PAYMENT_METHOD_TYPES) }

View File

@@ -22,8 +22,7 @@ module Spree
when :password
self[:value].to_s
when :decimal
BigDecimal(self[:value].to_s, exception: false)&.round(2, BigDecimal::ROUND_HALF_UP) ||
self[:value]
BigDecimal(self[:value].to_s).round(2, BigDecimal::ROUND_HALF_UP)
when :integer
self[:value].to_i
when :boolean

View File

@@ -117,7 +117,7 @@ module Spree
value.to_s
when :decimal
value = 0 if value.blank?
BigDecimal(value.to_s, exception: false)&.round(2, BigDecimal::ROUND_HALF_UP) || value
BigDecimal(value.to_s).round(2, BigDecimal::ROUND_HALF_UP)
when :integer
value.to_i
when :boolean

View File

@@ -11,6 +11,7 @@ require 'concerns/product_stock'
#
# MASTER VARIANT
# Every product has one master variant, which stores master price and sku, size and weight, etc.
# The master variant does not have option values associated with it.
# Price, SKU, size, weight, etc. are all delegated to the master variant.
# Contains on_hand inventory levels only when there are no variants for the product.
#
@@ -32,6 +33,11 @@ module Spree
searchable_associations :supplier, :properties, :primary_taxon, :variants, :master
searchable_scopes :active, :with_properties
has_many :product_option_types, dependent: :destroy
# We have an after_destroy callback on Spree::ProductOptionType. However, if we
# don't specify dependent => destroy on this association, it is not called. See:
# https://github.com/rails/rails/issues/7618
has_many :option_types, through: :product_option_types, dependent: :destroy
has_many :product_properties, dependent: :destroy
has_many :properties, through: :product_properties
@@ -81,6 +87,7 @@ module Spree
delegate :images_attributes=, :display_as=, to: :master
after_create :set_master_variant_defaults
after_create :build_variants_from_option_values_hash, if: :option_values_hash
after_save :save_master
delegate :images, to: :master, prefix: true
@@ -109,12 +116,16 @@ module Spree
presence: { if: ->(p) { p.variant_unit == 'items' } }
validate :validate_image_for_master
attr_accessor :option_values_hash
accepts_nested_attributes_for :product_properties,
allow_destroy: true,
reject_if: lambda { |pp| pp[:property_name].blank? }
make_permalink order: :name
alias :options :product_option_types
after_initialize :ensure_master
after_initialize :set_available_on_to_now, if: :new_record?
@@ -249,12 +260,32 @@ module Spree
end
end
# Ensures option_types and product_option_types exist for keys in option_values_hash
def ensure_option_types_exist_for_values_hash
return if option_values_hash.nil?
option_values_hash.keys.map(&:to_i).each do |id|
option_type_ids << id unless option_type_ids.include?(id)
unless product_option_types.pluck(:option_type_id).include?(id)
product_option_types.create(option_type_id: id)
end
end
end
# for adding products which are closely related to existing ones
def duplicate
duplicator = Spree::Core::ProductDuplicator.new(self)
duplicator.duplicate
end
# split variants list into hash which shows mapping of opt value onto matching variants
# eg categorise_variants_from_option(color) => {"red" -> [...], "blue" -> [...]}
def categorise_variants_from_option(opt_type)
return {} unless option_types.include?(opt_type)
variants.active.group_by { |v| v.option_values.detect { |o| o.option_type == opt_type } }
end
def self.like_any(fields, values)
where fields.map { |field|
values.map { |value|
@@ -320,6 +351,21 @@ module Spree
variants.map(&:import_date).compact.max
end
def variant_unit_option_type
return if variant_unit.blank?
option_type_name = "unit_#{variant_unit}"
option_type_presentation = variant_unit.capitalize
Spree::OptionType.find_by(name: option_type_name) ||
Spree::OptionType.create!(name: option_type_name,
presentation: option_type_presentation)
end
def self.all_variant_unit_option_types
Spree::OptionType.where('name LIKE ?', 'unit_%%')
end
def destroy
transaction do
touch_distributors
@@ -334,6 +380,21 @@ module Spree
private
# Builds variants from a hash of option types & values
def build_variants_from_option_values_hash
ensure_option_types_exist_for_values_hash
values = option_values_hash.values
values = values.inject(values.shift) { |memo, value| memo.product(value).map(&:flatten) }
values.each do |ids|
variants.create(
option_value_ids: ids,
price: master.price
)
end
save
end
# ensures the master variant is flagged as such
def set_master_variant_defaults
master.is_master = true
@@ -379,6 +440,8 @@ module Spree
def update_units
return unless saved_change_to_variant_unit? || saved_change_to_variant_unit_name?
option_types.delete self.class.all_variant_unit_option_types
option_types << variant_unit_option_type if variant_unit.present?
variants_including_master.each(&:update_units)
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
module Spree
class ProductOptionType < ApplicationRecord
after_destroy :remove_option_values
belongs_to :product, class_name: 'Spree::Product'
belongs_to :option_type, class_name: 'Spree::OptionType'
acts_as_list scope: :product
def remove_option_values
product.variants_including_master.each do |variant|
option_values = variant.option_values.where(option_type_id: option_type)
variant.option_values.destroy(*option_values)
end
end
end
end

View File

@@ -246,7 +246,7 @@ module Spree
@tracking_url ||= shipping_method.build_tracking_url(tracking)
end
def contains?(variant)
def include?(variant)
inventory_units_for(variant).present?
end

View File

@@ -61,7 +61,7 @@ module Spree
# Here we allow checkout with shipping methods without zones (see issue #3928 for details)
# and also checkout with addresses outside of the zones of the selected shipping method
# This method could be used, like in Spree, to validate shipping method zones on checkout.
def delivers_to?(address)
def include?(address)
address.present?
end

View File

@@ -69,8 +69,7 @@ module Spree
return 0 unless category
address ||= Address.new(country_id: DefaultCountry.id)
rate = category.tax_rates
.detect { |tax_rate| tax_rate.zone.contains_address? address }.try(:amount)
rate = category.tax_rates.detect { |tax_rate| tax_rate.zone.include? address }.try(:amount)
rate || 0
end

View File

@@ -48,13 +48,9 @@ module Spree
after_create :associate_customers, :associate_orders
validates :email, 'valid_email_2/email': { mx: true }, if: :email_changed?
validate :limit_owned_enterprises
validates :uid, uniqueness: true, if: lambda { uid.present? }
# Same validation as in the openid_connect gem.
# This validator is totally outdated but we indirectly depend on it.
validates :uid, email: true, if: lambda { uid.present? }
validates_email :uid, if: lambda { uid.present? }
class DestroyWithOrdersError < StandardError; end

View File

@@ -14,11 +14,9 @@ module Spree
acts_as_paranoid
searchable_attributes :sku, :display_as, :display_name
searchable_associations :product, :default_price
searchable_associations :product, :option_values, :default_price
searchable_scopes :active, :deleted
NAME_FIELDS = ["display_name", "display_as", "weight", "unit_value", "unit_description"].freeze
belongs_to :product, -> { with_deleted }, touch: true, class_name: 'Spree::Product'
delegate_belongs_to :product, :name, :description, :permalink, :available_on,
@@ -31,6 +29,9 @@ module Spree
has_many :stock_items, dependent: :destroy, inverse_of: :variant
has_many :stock_locations, through: :stock_items
has_many :stock_movements
has_and_belongs_to_many :option_values, join_table: :spree_option_values_variants
has_many :images, -> { order(:position) }, as: :viewable,
dependent: :destroy,
class_name: "Spree::Image"
@@ -72,16 +73,14 @@ module Spree
before_validation :ensure_unit_value
before_validation :update_weight_from_unit_value, if: ->(v) { v.product.present? }
before_save :convert_variant_weight_to_decimal
before_save :assign_units, if: ->(variant) {
variant.new_record? || variant.changed_attributes.keys.intersection(NAME_FIELDS).any?
}
after_save :save_default_price
after_save :update_units
after_create :create_stock_items
after_create :set_position
before_save :convert_variant_weight_to_decimal
around_destroy :destruction
# default variant scope only lists non-deleted variants
@@ -178,6 +177,10 @@ module Spree
order_cycle).fees_name_by_type_for self
end
def option_value(opt_name)
option_values.detect { |o| o.option_type.name == opt_name }.try(:presentation)
end
def price_in(currency)
prices.select{ |price| price.currency == currency }.first ||
Spree::Price.new(variant_id: id, currency: currency)

Some files were not shown because too many files have changed in this diff Show More