mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-08 22:56:06 +00:00
Merge branch 'master' into update-to-rails-7.1
This commit is contained in:
12
.env
12
.env
@@ -10,10 +10,10 @@ TIMEZONE="Melbourne"
|
||||
DEFAULT_COUNTRY_CODE="AU"
|
||||
|
||||
# Locale for translation.
|
||||
LOCALE="en"
|
||||
LOCALE="en_AU"
|
||||
|
||||
# For multilingual - ENV doesn't have array so pass it as string with commas
|
||||
AVAILABLE_LOCALES="en,es"
|
||||
AVAILABLE_LOCALES="en_AU,es"
|
||||
|
||||
# Spree zone.
|
||||
CHECKOUT_ZONE="Australia"
|
||||
@@ -42,14 +42,6 @@ SMTP_PASSWORD="f00d"
|
||||
# Javascript error reporting via Bugsnag.
|
||||
# BUGSNAG_JS_KEY=""
|
||||
|
||||
# SingleSignOn login for Discourse
|
||||
#
|
||||
# DISCOURSE_SSO_SECRET should be a random string. It must be the same as provided to your Discourse instance.
|
||||
# DISCOURSE_SSO_SECRET=""
|
||||
#
|
||||
# DISCOURSE_URL must be the URL of your Discourse instance.
|
||||
# DISCOURSE_URL="https://noticeboard.openfoodnetwork.org.au"
|
||||
|
||||
# see="https://developers.google.com/maps/documentation/javascript/get-api-key
|
||||
# GOOGLE_MAPS_API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
# see https://developers.google.com/maps/documentation/javascript/localization#Region
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
#
|
||||
# cp .env.development .env.local
|
||||
|
||||
# Locale for translation. Using a locale other than `en` tests the
|
||||
# successful fallback to `en`. You will also see up-to-date text used
|
||||
# in production
|
||||
LOCALE="en_AU"
|
||||
|
||||
VERBOSE_QUERY_LOGS=true
|
||||
|
||||
SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# ENV vars for the test environment
|
||||
# Override locally with `.env.test.local`
|
||||
|
||||
# Locale for translation.
|
||||
LOCALE="en_TEST"
|
||||
|
||||
OFN_REDIS_JOBS_URL="redis://localhost:6379/2"
|
||||
|
||||
SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/release.md
vendored
2
.github/ISSUE_TEMPLATE/release.md
vendored
@@ -45,7 +45,7 @@ The full process is described at https://github.com/openfoodfoundation/openfoodn
|
||||
|
||||
[Ready To Go]: https://github.com/orgs/openfoodfoundation/projects/8?filterQuery=status%3A%22Ready+to+go+%F0%9F%9A%80%22
|
||||
[Transifex pull request]: https://github.com/openfoodfoundation/openfoodnetwork/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+head%3Atransifex
|
||||
[Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?tag=v&title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A
|
||||
[Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A
|
||||
[releases]: https://github.com/openfoodfoundation/openfoodnetwork/releases
|
||||
[#instance-managers]: https://app.slack.com/client/T02G54U79/CG7NJ966B
|
||||
[#testing]: https://openfoodnetwork.slack.com/app_redirect?channel=C02TZ6X00
|
||||
|
||||
65
.github/workflows/build.yml
vendored
65
.github/workflows/build.yml
vendored
@@ -55,6 +55,7 @@ jobs:
|
||||
with:
|
||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||
|
||||
# JS is required in order for webpacker to compile, in order to render templates containing image urls
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
@@ -64,8 +65,7 @@ jobs:
|
||||
|
||||
- name: Set up database
|
||||
run: |
|
||||
bundle exec rake db:create
|
||||
bundle exec rake db:schema:load
|
||||
bin/rake db:create db:schema:load
|
||||
|
||||
- name: Run tests
|
||||
env:
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/controllers/**/*_spec.rb}"
|
||||
run: |
|
||||
git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
|
||||
bundle exec rake knapsack_pro:rspec
|
||||
bin/rake knapsack_pro:rspec
|
||||
|
||||
models:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -106,10 +106,10 @@ jobs:
|
||||
# [n] - where the n is a number of parallel jobs you want to run your tests on.
|
||||
# Use a higher number if you have slow tests to split them between more parallel jobs.
|
||||
# Remember to update the value of the `ci_node_index` below to (0..n-1).
|
||||
ci_node_total: [5]
|
||||
ci_node_total: [4]
|
||||
# Indexes for parallel jobs (starting from zero).
|
||||
# E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
|
||||
ci_node_index: [0, 1, 2, 3, 4]
|
||||
ci_node_index: [0, 1, 2, 3]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -123,20 +123,11 @@ jobs:
|
||||
with:
|
||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Set up database
|
||||
run: |
|
||||
bundle exec rake db:create
|
||||
bundle exec rake db:schema:load
|
||||
bin/rake db:create db:schema:load
|
||||
|
||||
- name: Run tests
|
||||
|
||||
env:
|
||||
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC: 09476e2ce491c12083df62768667c674
|
||||
KNAPSACK_PRO_CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
|
||||
@@ -150,9 +141,8 @@ jobs:
|
||||
# https://knapsackpro.com/faq/question/how-to-split-slow-rspec-test-files-by-test-examples-by-individual-it
|
||||
#KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES: true
|
||||
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/models/**/*_spec.rb}"
|
||||
|
||||
run: |
|
||||
bundle exec rake knapsack_pro:rspec
|
||||
bin/rake knapsack_pro:rspec
|
||||
|
||||
system_admin:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -175,10 +165,10 @@ jobs:
|
||||
# [n] - where the n is a number of parallel jobs you want to run your tests on.
|
||||
# Use a higher number if you have slow tests to split them between more parallel jobs.
|
||||
# Remember to update the value of the `ci_node_index` below to (0..n-1).
|
||||
ci_node_total: [13]
|
||||
ci_node_total: [14]
|
||||
# Indexes for parallel jobs (starting from zero).
|
||||
# E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
|
||||
ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
||||
ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -201,8 +191,7 @@ jobs:
|
||||
|
||||
- name: Set up database
|
||||
run: |
|
||||
bundle exec rake db:create
|
||||
bundle exec rake db:schema:load
|
||||
bin/rake db:create db:schema:load
|
||||
|
||||
- name: Run tests
|
||||
|
||||
@@ -221,7 +210,7 @@ jobs:
|
||||
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/system/admin/**/*_spec.rb}"
|
||||
|
||||
run: |
|
||||
bundle exec rake knapsack_pro:queue:rspec
|
||||
bin/rake knapsack_pro:queue:rspec
|
||||
|
||||
- name: Archive failed tests screenshots
|
||||
if: failure()
|
||||
@@ -279,8 +268,7 @@ jobs:
|
||||
|
||||
- name: Set up database
|
||||
run: |
|
||||
bundle exec rake db:create
|
||||
bundle exec rake db:schema:load
|
||||
bin/rake db:create db:schema:load
|
||||
|
||||
- name: Run tests
|
||||
|
||||
@@ -299,7 +287,7 @@ jobs:
|
||||
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/system/consumer/**/*_spec.rb}"
|
||||
|
||||
run: |
|
||||
bundle exec rake knapsack_pro:queue:rspec
|
||||
bin/rake knapsack_pro:queue:rspec
|
||||
|
||||
- name: Archive failed tests screenshots
|
||||
if: failure()
|
||||
@@ -348,6 +336,7 @@ jobs:
|
||||
with:
|
||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||
|
||||
# JS is required in order for webpacker to compile, in order to render templates linking to mail.css
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
@@ -357,8 +346,7 @@ jobs:
|
||||
|
||||
- name: Set up database
|
||||
run: |
|
||||
bundle exec rake db:create
|
||||
bundle exec rake db:schema:load
|
||||
bin/rake db:create db:schema:load
|
||||
|
||||
- name: Run tests
|
||||
|
||||
@@ -377,7 +365,7 @@ jobs:
|
||||
KNAPSACK_PRO_TEST_FILE_PATTERN: "{spec/lib/**/*_spec.rb,spec/migrations/**/*_spec.rb,spec/serializers/**/*_spec.rb,engines/**/*_spec.rb}"
|
||||
|
||||
run: |
|
||||
bundle exec rake knapsack_pro:rspec
|
||||
bin/rake knapsack_pro:rspec
|
||||
|
||||
- name: Archive failed tests screenshots
|
||||
if: failure()
|
||||
@@ -426,6 +414,7 @@ jobs:
|
||||
with:
|
||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||
|
||||
# JS is required in order for webpacker to compile, in order to render templates linking to mail.css
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
@@ -435,11 +424,9 @@ jobs:
|
||||
|
||||
- name: Set up database
|
||||
run: |
|
||||
bundle exec rake db:create
|
||||
bundle exec rake db:schema:load
|
||||
bin/rake db:create db:schema:load
|
||||
|
||||
- name: Run tests
|
||||
|
||||
env:
|
||||
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC: e3b8800198d2d89b70c7edbdd85f8fd8
|
||||
KNAPSACK_PRO_CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
|
||||
@@ -453,10 +440,8 @@ jobs:
|
||||
# https://knapsackpro.com/faq/question/how-to-split-slow-rspec-test-files-by-test-examples-by-individual-it
|
||||
#KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES: true
|
||||
KNAPSACK_PRO_TEST_FILE_EXCLUDE_PATTERN: "{engines/**/*_spec.rb,spec/models/**/*_spec.rb,spec/controllers/**/*_spec.rb,spec/serializers/**/*_spec.rb,spec/lib/**/*_spec.rb,spec/migrations/**/*_spec.rb,spec/system/**/*_spec.rb}"
|
||||
|
||||
|
||||
run: |
|
||||
bundle exec rake knapsack_pro:rspec
|
||||
bin/rake knapsack_pro:rspec
|
||||
|
||||
non_knapsack_jest_karma:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -476,11 +461,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup redis
|
||||
uses: supercharge/redis-github-action@1.4.0
|
||||
with:
|
||||
redis-version: 6
|
||||
|
||||
# Rails is required for the Karma rake script
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
@@ -493,12 +474,8 @@ jobs:
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Set up database
|
||||
run: |
|
||||
bundle exec rake db:create
|
||||
bundle exec rake db:schema:load
|
||||
- name: Run JS tests
|
||||
run: bundle exec rake karma:run
|
||||
run: bin/rake karma:run
|
||||
|
||||
- name: Run jest tests
|
||||
run: yarn jest
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
# The configuration is split into three files. Look into those files for more details.
|
||||
#
|
||||
require:
|
||||
- rubocop-capybara
|
||||
- rubocop-factory_bot
|
||||
- rubocop-rails
|
||||
- rubocop-rspec
|
||||
- rubocop-rspec_rails
|
||||
|
||||
inherit_from:
|
||||
|
||||
# The automatically generated todo list to ignore all current violations.
|
||||
|
||||
@@ -1,15 +1,39 @@
|
||||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 1400 --no-auto-gen-timestamp`
|
||||
# using RuboCop version 1.62.1.
|
||||
# using RuboCop version 1.64.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: 2
|
||||
Lint/DuplicateMethods:
|
||||
# Offense count: 1
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines.
|
||||
Layout/EmptyLineBetweenDefs:
|
||||
Exclude:
|
||||
- 'lib/discourse/single_sign_on.rb'
|
||||
- 'app/services/products_renderer.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
Layout/EmptyLines:
|
||||
Exclude:
|
||||
- 'app/services/products_renderer.rb'
|
||||
|
||||
# Offense count: 6
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
||||
# SupportedStyles: aligned, indented, indented_relative_to_receiver
|
||||
Layout/MultilineMethodCallIndentation:
|
||||
Exclude:
|
||||
- 'app/services/products_renderer.rb'
|
||||
|
||||
# Offense count: 2
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
||||
# SupportedStyles: aligned, indented
|
||||
Layout/MultilineOperationIndentation:
|
||||
Exclude:
|
||||
- 'app/services/products_renderer.rb'
|
||||
|
||||
# Offense count: 16
|
||||
# Configuration parameters: AllowComments, AllowEmptyLambdas.
|
||||
@@ -27,11 +51,10 @@ Lint/EmptyBlock:
|
||||
- 'spec/jobs/subscription_placement_job_spec.rb'
|
||||
- 'spec/models/product_import/entry_validator_spec.rb'
|
||||
|
||||
# Offense count: 6
|
||||
# Offense count: 4
|
||||
# Configuration parameters: AllowComments.
|
||||
Lint/EmptyClass:
|
||||
Exclude:
|
||||
- 'spec/controllers/spree/admin/base_controller_spec.rb'
|
||||
- 'spec/lib/reports/report_loader_spec.rb'
|
||||
|
||||
# Offense count: 1
|
||||
@@ -85,7 +108,7 @@ Lint/UselessMethodDefinition:
|
||||
Exclude:
|
||||
- 'app/models/spree/gateway.rb'
|
||||
|
||||
# Offense count: 26
|
||||
# Offense count: 24
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
||||
Metrics/AbcSize:
|
||||
Exclude:
|
||||
@@ -104,11 +127,9 @@ Metrics/AbcSize:
|
||||
- 'app/models/spree/order/checkout.rb'
|
||||
- 'app/models/spree/preferences/preferable_class_methods.rb'
|
||||
- 'app/models/spree/return_authorization.rb'
|
||||
- 'lib/discourse/single_sign_on.rb'
|
||||
- 'lib/open_food_network/order_cycle_form_applicator.rb'
|
||||
- 'lib/open_food_network/order_cycle_permissions.rb'
|
||||
- 'lib/spree/core/controller_helpers/order.rb'
|
||||
- 'lib/tasks/enterprises.rake'
|
||||
- 'spec/services/orders/checkout_restart_service_spec.rb'
|
||||
|
||||
# Offense count: 9
|
||||
@@ -129,7 +150,7 @@ Metrics/BlockNesting:
|
||||
Exclude:
|
||||
- 'app/models/spree/payment/processing.rb'
|
||||
|
||||
# Offense count: 46
|
||||
# Offense count: 47
|
||||
# Configuration parameters: CountComments, Max, CountAsOne.
|
||||
Metrics/ClassLength:
|
||||
Exclude:
|
||||
@@ -164,6 +185,7 @@ Metrics/ClassLength:
|
||||
- 'app/models/spree/user.rb'
|
||||
- 'app/models/spree/variant.rb'
|
||||
- 'app/models/spree/zone.rb'
|
||||
- 'app/reflexes/admin/orders_reflex.rb'
|
||||
- 'app/reflexes/products_reflex.rb'
|
||||
- 'app/serializers/api/cached_enterprise_serializer.rb'
|
||||
- 'app/serializers/api/enterprise_shopfront_serializer.rb'
|
||||
@@ -180,7 +202,7 @@ Metrics/ClassLength:
|
||||
- 'lib/reporting/reports/enterprise_fee_summary/scope.rb'
|
||||
- 'lib/reporting/reports/xero_invoices/base.rb'
|
||||
|
||||
# Offense count: 34
|
||||
# Offense count: 32
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
||||
Metrics/CyclomaticComplexity:
|
||||
Exclude:
|
||||
@@ -190,7 +212,6 @@ Metrics/CyclomaticComplexity:
|
||||
- 'app/helpers/checkout_helper.rb'
|
||||
- 'app/helpers/order_cycles_helper.rb'
|
||||
- 'app/helpers/spree/admin/navigation_helper.rb'
|
||||
- 'app/models/enterprise.rb'
|
||||
- 'app/models/enterprise_relationship.rb'
|
||||
- 'app/models/product_import/entry_validator.rb'
|
||||
- 'app/models/spree/ability.rb'
|
||||
@@ -204,7 +225,6 @@ Metrics/CyclomaticComplexity:
|
||||
- 'app/models/spree/tax_rate.rb'
|
||||
- 'app/models/spree/variant.rb'
|
||||
- 'app/models/spree/zone.rb'
|
||||
- 'lib/discourse/single_sign_on.rb'
|
||||
- 'lib/open_food_network/enterprise_issue_validator.rb'
|
||||
- 'lib/reporting/reports/xero_invoices/base.rb'
|
||||
- 'lib/spree/core/controller_helpers/order.rb'
|
||||
@@ -212,7 +232,7 @@ Metrics/CyclomaticComplexity:
|
||||
- 'lib/spree/localized_number.rb'
|
||||
- 'spec/models/product_importer_spec.rb'
|
||||
|
||||
# Offense count: 25
|
||||
# Offense count: 24
|
||||
# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
|
||||
Metrics/MethodLength:
|
||||
Exclude:
|
||||
@@ -226,7 +246,6 @@ Metrics/MethodLength:
|
||||
- 'app/models/spree/order/checkout.rb'
|
||||
- 'app/models/spree/payment/processing.rb'
|
||||
- 'app/models/spree/preferences/preferable_class_methods.rb'
|
||||
- 'lib/discourse/single_sign_on.rb'
|
||||
- 'lib/open_food_network/order_cycle_form_applicator.rb'
|
||||
- 'lib/open_food_network/order_cycle_permissions.rb'
|
||||
- 'lib/reporting/reports/enterprise_fee_summary/scope.rb'
|
||||
@@ -398,7 +417,6 @@ RSpecRails/HaveHttpStatus:
|
||||
- 'spec/controllers/user_registrations_controller_spec.rb'
|
||||
- 'spec/requests/admin/images_spec.rb'
|
||||
- 'spec/requests/api/routes_spec.rb'
|
||||
- 'spec/requests/checkout/failed_checkout_spec.rb'
|
||||
- 'spec/requests/checkout/stripe_sca_spec.rb'
|
||||
- 'spec/requests/home_controller_spec.rb'
|
||||
- 'spec/requests/omniauth_callbacks_controller_spec.rb'
|
||||
@@ -414,7 +432,7 @@ RSpecRails/HttpStatus:
|
||||
- 'spec/controllers/spree/admin/products_controller_spec.rb'
|
||||
- 'spec/requests/api/orders_spec.rb'
|
||||
|
||||
# Offense count: 146
|
||||
# Offense count: 144
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: Inferences.
|
||||
RSpecRails/InferredSpecType:
|
||||
@@ -518,7 +536,6 @@ RSpecRails/InferredSpecType:
|
||||
- 'spec/helpers/navigation_helper_spec.rb'
|
||||
- 'spec/helpers/order_cycles_helper_spec.rb'
|
||||
- 'spec/helpers/serializer_helper_spec.rb'
|
||||
- 'spec/helpers/shared_helper_spec.rb'
|
||||
- 'spec/helpers/shop_helper_spec.rb'
|
||||
- 'spec/helpers/spree/admin/base_helper_spec.rb'
|
||||
- 'spec/helpers/spree/admin/general_settings_helper_spec.rb'
|
||||
@@ -552,7 +569,6 @@ RSpecRails/InferredSpecType:
|
||||
- 'spec/requests/api/routes_spec.rb'
|
||||
- 'spec/requests/api/v1/customers_spec.rb'
|
||||
- 'spec/requests/api_docs_spec.rb'
|
||||
- 'spec/requests/checkout/failed_checkout_spec.rb'
|
||||
- 'spec/requests/checkout/paypal_spec.rb'
|
||||
- 'spec/requests/checkout/routes_spec.rb'
|
||||
- 'spec/requests/checkout/stripe_sca_spec.rb'
|
||||
@@ -565,17 +581,6 @@ RSpecRails/InferredSpecType:
|
||||
- 'spec/requests/voucher_adjustments_spec.rb'
|
||||
- 'spec/routing/stripe_spec.rb'
|
||||
|
||||
# Offense count: 11
|
||||
# Configuration parameters: Include.
|
||||
# Include: app/models/**/*.rb
|
||||
Rails/HasManyOrHasOneDependent:
|
||||
Exclude:
|
||||
- 'app/models/enterprise.rb'
|
||||
- 'app/models/spree/address.rb'
|
||||
- 'app/models/spree/stock_item.rb'
|
||||
- 'app/models/spree/tax_rate.rb'
|
||||
- 'app/models/spree/variant.rb'
|
||||
|
||||
# Offense count: 22
|
||||
# Configuration parameters: IgnoreScopes, Include.
|
||||
# Include: app/models/**/*.rb
|
||||
@@ -660,27 +665,6 @@ Rails/RedundantActiveRecordAllMethod:
|
||||
- 'app/models/spree/variant.rb'
|
||||
- 'spec/system/admin/product_import_spec.rb'
|
||||
|
||||
# Offense count: 20
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Rails/RedundantPresenceValidationOnBelongsTo:
|
||||
Exclude:
|
||||
- 'app/models/enterprise_fee.rb'
|
||||
- 'app/models/exchange.rb'
|
||||
- 'app/models/inventory_item.rb'
|
||||
- 'app/models/order_cycle.rb'
|
||||
- 'app/models/spree/address.rb'
|
||||
- 'app/models/spree/line_item.rb'
|
||||
- 'app/models/spree/order.rb'
|
||||
- 'app/models/spree/product_property.rb'
|
||||
- 'app/models/spree/return_authorization.rb'
|
||||
- 'app/models/spree/state.rb'
|
||||
- 'app/models/spree/stock_item.rb'
|
||||
- 'app/models/spree/stock_movement.rb'
|
||||
- 'app/models/spree/tax_rate.rb'
|
||||
- 'app/models/subscription_line_item.rb'
|
||||
- 'app/models/tag_rule.rb'
|
||||
- 'app/models/variant_override.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Rails/RelativeDateConstant:
|
||||
@@ -762,17 +746,6 @@ Rails/UnknownEnv:
|
||||
Exclude:
|
||||
- 'app/models/spree/app_configuration.rb'
|
||||
|
||||
# Offense count: 7
|
||||
# Configuration parameters: Severity.
|
||||
Rails/UnusedRenderContent:
|
||||
Exclude:
|
||||
- 'app/controllers/admin/bulk_line_items_controller.rb'
|
||||
- 'app/controllers/admin/tag_rules_controller.rb'
|
||||
- 'app/controllers/api/v0/enterprise_fees_controller.rb'
|
||||
- 'app/controllers/api/v0/products_controller.rb'
|
||||
- 'app/controllers/api/v0/taxons_controller.rb'
|
||||
- 'app/controllers/api/v0/variants_controller.rb'
|
||||
|
||||
# Offense count: 1
|
||||
Security/Open:
|
||||
Exclude:
|
||||
@@ -797,7 +770,7 @@ Style/CaseEquality:
|
||||
Exclude:
|
||||
- 'spec/models/spree/payment_spec.rb'
|
||||
|
||||
# Offense count: 25
|
||||
# Offense count: 23
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: nested, compact
|
||||
@@ -824,10 +797,9 @@ Style/ClassAndModuleChildren:
|
||||
- 'app/serializers/api/taxon_serializer.rb'
|
||||
- 'app/serializers/api/variant_serializer.rb'
|
||||
- 'lib/open_food_network/locking.rb'
|
||||
- 'spec/controllers/spree/admin/base_controller_spec.rb'
|
||||
- 'spec/models/spree/payment_method_spec.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# Offense count: 2
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: always, always_true, never
|
||||
@@ -877,12 +849,6 @@ Style/HashEachMethods:
|
||||
- 'spec/models/product_importer_spec.rb'
|
||||
- 'spec/support/cancan_helper.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# Configuration parameters: MinBranchesCount.
|
||||
Style/HashLikeCase:
|
||||
Exclude:
|
||||
- 'app/models/enterprise.rb'
|
||||
|
||||
# Offense count: 4
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Style/MapToHash:
|
||||
@@ -971,19 +937,6 @@ Style/RedundantInitialize:
|
||||
Exclude:
|
||||
- 'spec/models/spree/gateway_spec.rb'
|
||||
|
||||
# Offense count: 2
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Style/RedundantInterpolation:
|
||||
Exclude:
|
||||
- 'lib/tasks/karma.rake'
|
||||
- 'spec/base_spec_helper.rb'
|
||||
|
||||
# Offense count: 8
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
Style/RedundantLineContinuation:
|
||||
Exclude:
|
||||
- 'lib/reporting/reports/enterprise_fee_summary/scope.rb'
|
||||
|
||||
# Offense count: 19
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||
@@ -1026,36 +979,10 @@ Style/Send:
|
||||
- 'spec/services/variant_units/option_value_namer_spec.rb'
|
||||
- 'spec/support/localized_number_helper.rb'
|
||||
|
||||
# Offense count: 4
|
||||
# Offense count: 3
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Style/SlicingWithRange:
|
||||
Exclude:
|
||||
- 'app/helpers/spree/admin/navigation_helper.rb'
|
||||
- 'app/services/embedded_page_service.rb'
|
||||
- 'engines/order_management/app/services/order_management/subscriptions/validator.rb'
|
||||
- 'lib/discourse/single_sign_on.rb'
|
||||
|
||||
# Offense count: 25
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: Mode.
|
||||
Style/StringConcatenation:
|
||||
Exclude:
|
||||
- 'app/controllers/admin/stripe_connect_settings_controller.rb'
|
||||
- 'app/helpers/discourse_helper.rb'
|
||||
- 'app/helpers/enterprises_helper.rb'
|
||||
- 'app/helpers/spree/admin/base_helper.rb'
|
||||
- 'app/mailers/spree/user_mailer.rb'
|
||||
- 'app/models/enterprise.rb'
|
||||
- 'app/models/spree/credit_card.rb'
|
||||
- 'app/models/spree/payment_method.rb'
|
||||
- 'app/serializers/api/cached_enterprise_serializer.rb'
|
||||
- 'app/serializers/api/enterprise_shopfront_list_serializer.rb'
|
||||
- 'app/services/embedded_page_service.rb'
|
||||
- 'app/services/products_renderer.rb'
|
||||
- 'lib/spree/api/controller_setup.rb'
|
||||
- 'lib/spree/core/environment_extension.rb'
|
||||
- 'spec/models/spree/line_item_spec.rb'
|
||||
- 'spec/models/spree/product_spec.rb'
|
||||
- 'spec/services/embedded_page_service_spec.rb'
|
||||
- 'spec/support/features/datepicker_helper.rb'
|
||||
- 'spec/system/admin/products_spec.rb'
|
||||
|
||||
9
Gemfile
9
Gemfile
@@ -19,7 +19,6 @@ gem 'angular-rails-templates', '>= 0.3.0'
|
||||
gem 'awesome_nested_set'
|
||||
gem 'ransack', '~> 4.1.0'
|
||||
gem 'responders'
|
||||
gem 'rexml'
|
||||
gem 'webpacker', '~> 5'
|
||||
|
||||
gem 'i18n'
|
||||
@@ -103,8 +102,10 @@ gem 'redis'
|
||||
gem 'sidekiq'
|
||||
gem 'sidekiq-scheduler'
|
||||
|
||||
gem "cable_ready", "5.0.1"
|
||||
gem "stimulus_reflex", "3.5.0.rc3"
|
||||
gem "cable_ready"
|
||||
gem "stimulus_reflex"
|
||||
|
||||
gem "turbo-rails"
|
||||
|
||||
gem 'combine_pdf'
|
||||
gem 'wicked_pdf'
|
||||
@@ -164,7 +165,7 @@ group :test, :development do
|
||||
gem 'rspec-sql'
|
||||
gem 'rswag'
|
||||
gem 'shoulda-matchers'
|
||||
gem 'stimulus_reflex_testing'
|
||||
gem 'stimulus_reflex_testing', github: "podia/stimulus_reflex_testing", branch: :main
|
||||
gem 'timecop'
|
||||
end
|
||||
|
||||
|
||||
127
Gemfile.lock
127
Gemfile.lock
@@ -17,6 +17,14 @@ GIT
|
||||
sass-rails
|
||||
thor (>= 0.14)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/podia/stimulus_reflex_testing.git
|
||||
revision: abac2ee34de347c589795b4d1a8e83e0baafb201
|
||||
branch: main
|
||||
specs:
|
||||
stimulus_reflex_testing (0.3.1)
|
||||
stimulus_reflex (>= 3.3.0)
|
||||
|
||||
PATH
|
||||
remote: engines/catalog
|
||||
specs:
|
||||
@@ -165,17 +173,17 @@ GEM
|
||||
awesome_nested_set (3.6.0)
|
||||
activerecord (>= 4.0.0, < 7.2)
|
||||
aws-eventstream (1.3.0)
|
||||
aws-partitions (1.914.0)
|
||||
aws-sdk-core (3.192.0)
|
||||
aws-partitions (1.929.0)
|
||||
aws-sdk-core (3.196.1)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.8)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.79.0)
|
||||
aws-sdk-core (~> 3, >= 3.191.0)
|
||||
aws-sdk-kms (1.81.0)
|
||||
aws-sdk-core (~> 3, >= 3.193.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.147.0)
|
||||
aws-sdk-core (~> 3, >= 3.192.0)
|
||||
aws-sdk-s3 (1.151.0)
|
||||
aws-sdk-core (~> 3, >= 3.194.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.8)
|
||||
aws-sigv4 (1.8.0)
|
||||
@@ -194,10 +202,11 @@ GEM
|
||||
bullet (7.1.6)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
cable_ready (5.0.1)
|
||||
cable_ready (5.0.5)
|
||||
actionpack (>= 5.2)
|
||||
actionview (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
observer (~> 0.1)
|
||||
railties (>= 5.2)
|
||||
thread-local (>= 1.1.0)
|
||||
cancancan (1.15.0)
|
||||
@@ -230,14 +239,15 @@ GEM
|
||||
combine_pdf (1.0.26)
|
||||
matrix
|
||||
ruby-rc4 (>= 0.1.5)
|
||||
concurrent-ruby (1.2.3)
|
||||
concurrent-ruby (1.3.1)
|
||||
connection_pool (2.4.1)
|
||||
crack (1.0.0)
|
||||
bigdecimal
|
||||
rexml
|
||||
crass (1.0.6)
|
||||
css_parser (1.16.0)
|
||||
css_parser (1.17.1)
|
||||
addressable
|
||||
csv (3.3.0)
|
||||
cuprite (0.15)
|
||||
capybara (~> 3.0)
|
||||
ferrum (~> 0.14.0)
|
||||
@@ -262,14 +272,14 @@ GEM
|
||||
warden (~> 1.2.3)
|
||||
devise-encryptable (0.2.0)
|
||||
devise (>= 2.1.0)
|
||||
devise-i18n (1.12.0)
|
||||
devise-i18n (1.12.1)
|
||||
devise (>= 4.9.0)
|
||||
devise-token_authenticatable (1.1.0)
|
||||
devise (>= 4.0.0, < 5.0.0)
|
||||
diff-lcs (1.5.1)
|
||||
digest (3.1.1)
|
||||
docile (1.4.0)
|
||||
dotenv (3.1.0)
|
||||
dotenv (3.1.2)
|
||||
drb (2.2.0)
|
||||
ruby2_keywords
|
||||
email_validator (2.2.4)
|
||||
@@ -297,16 +307,17 @@ GEM
|
||||
websocket-driver (>= 0.6, < 0.8)
|
||||
ffaker (2.23.0)
|
||||
ffi (1.16.3)
|
||||
flipper (0.26.2)
|
||||
flipper (1.3.0)
|
||||
concurrent-ruby (< 2)
|
||||
flipper-active_record (0.26.2)
|
||||
flipper-active_record (1.3.0)
|
||||
activerecord (>= 4.2, < 8)
|
||||
flipper (~> 0.26.2)
|
||||
flipper-ui (0.26.2)
|
||||
flipper (~> 1.3.0)
|
||||
flipper-ui (1.3.0)
|
||||
erubi (>= 1.0.0, < 2.0.0)
|
||||
flipper (~> 0.26.2)
|
||||
rack (>= 1.4, < 3)
|
||||
rack-protection (>= 1.5.3, <= 4.0.0)
|
||||
flipper (~> 1.3.0)
|
||||
rack (>= 1.4, < 4)
|
||||
rack-protection (>= 1.5.3, < 5.0.0)
|
||||
rack-session (>= 1.0.2, < 3.0.0)
|
||||
sanitize (< 7)
|
||||
fog-aws (2.0.1)
|
||||
fog-core (~> 1.38)
|
||||
@@ -331,7 +342,9 @@ GEM
|
||||
fuubar (2.5.1)
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
geocoder (1.8.2)
|
||||
geocoder (1.8.3)
|
||||
base64 (>= 0.1.0)
|
||||
csv (>= 3.0.0)
|
||||
globalid (1.2.1)
|
||||
activesupport (>= 6.1)
|
||||
gmaps4rails (2.1.2)
|
||||
@@ -347,7 +360,7 @@ GEM
|
||||
hashie (5.0.0)
|
||||
highline (2.0.3)
|
||||
htmlentities (4.3.4)
|
||||
i18n (1.14.4)
|
||||
i18n (1.14.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-js (3.9.2)
|
||||
i18n (>= 0.6.6)
|
||||
@@ -395,7 +408,7 @@ GEM
|
||||
activesupport (>= 4.2)
|
||||
jwt (2.8.1)
|
||||
base64
|
||||
knapsack_pro (6.0.4)
|
||||
knapsack_pro (7.4.0)
|
||||
rake
|
||||
language_server-protocol (3.17.0.3)
|
||||
launchy (3.0.0)
|
||||
@@ -417,7 +430,7 @@ GEM
|
||||
net-smtp
|
||||
marcel (1.0.2)
|
||||
matrix (0.4.2)
|
||||
method_source (1.0.0)
|
||||
method_source (1.1.0)
|
||||
mime-types (3.5.2)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2023.1205)
|
||||
@@ -427,7 +440,7 @@ GEM
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.6)
|
||||
minitest (5.22.3)
|
||||
minitest (5.23.1)
|
||||
monetize (1.13.0)
|
||||
money (~> 6.12)
|
||||
money (6.16.0)
|
||||
@@ -447,22 +460,25 @@ GEM
|
||||
timeout
|
||||
net-smtp (0.5.0)
|
||||
net-protocol
|
||||
newrelic_rpm (9.8.0)
|
||||
newrelic_rpm (9.9.0)
|
||||
nio4r (2.7.0)
|
||||
nokogiri (1.16.4)
|
||||
nokogiri (1.16.5)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
nokogiri-html5-inference (0.3.0)
|
||||
nokogiri (~> 1.14)
|
||||
oauth2 (1.4.11)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
jwt (>= 1.0, < 3.0)
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 4)
|
||||
observer (0.1.2)
|
||||
omniauth (2.1.2)
|
||||
hashie (>= 3.4.6)
|
||||
rack (>= 2.2.3)
|
||||
rack-protection
|
||||
omniauth-rails_csrf_protection (1.0.1)
|
||||
omniauth-rails_csrf_protection (1.0.2)
|
||||
actionpack (>= 4.2)
|
||||
omniauth (~> 2.0)
|
||||
omniauth_openid_connect (0.7.1)
|
||||
@@ -490,7 +506,7 @@ GEM
|
||||
parallel (1.24.0)
|
||||
paranoia (2.6.3)
|
||||
activerecord (>= 5.1, < 7.2)
|
||||
parser (3.3.0.5)
|
||||
parser (3.3.2.0)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
paypal-sdk-core (0.3.4)
|
||||
@@ -511,14 +527,14 @@ GEM
|
||||
method_source (~> 1.0)
|
||||
psych (5.1.2)
|
||||
stringio
|
||||
public_suffix (5.0.4)
|
||||
public_suffix (5.0.5)
|
||||
puma (6.4.2)
|
||||
nio4r (~> 2.0)
|
||||
query_count (1.1.1)
|
||||
activerecord (>= 4.2)
|
||||
railties (>= 4.2)
|
||||
raabro (1.4.0)
|
||||
racc (1.7.3)
|
||||
racc (1.8.0)
|
||||
rack (2.2.9)
|
||||
rack-mini-profiler (2.3.4)
|
||||
rack (>= 1.2.0)
|
||||
@@ -602,9 +618,9 @@ GEM
|
||||
redcarpet (3.6.0)
|
||||
redis (5.2.0)
|
||||
redis-client (>= 0.22.0)
|
||||
redis-client (0.22.0)
|
||||
redis-client (0.22.1)
|
||||
connection_pool
|
||||
regexp_parser (2.9.0)
|
||||
regexp_parser (2.9.2)
|
||||
reline (0.5.0)
|
||||
io-console (~> 0.5)
|
||||
request_store (1.5.1)
|
||||
@@ -612,11 +628,12 @@ GEM
|
||||
responders (3.1.1)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
rexml (3.2.6)
|
||||
roadie (5.2.0)
|
||||
rexml (3.2.8)
|
||||
strscan (>= 3.0.9)
|
||||
roadie (5.2.1)
|
||||
css_parser (~> 1.4)
|
||||
nokogiri (~> 1.15)
|
||||
roadie-rails (3.1.0)
|
||||
roadie-rails (3.2.0)
|
||||
railties (>= 5.1, < 8.0)
|
||||
roadie (~> 5.0)
|
||||
rodf (1.2.0)
|
||||
@@ -666,7 +683,7 @@ GEM
|
||||
rswag-ui (2.13.0)
|
||||
actionpack (>= 3.1, < 7.2)
|
||||
railties (>= 3.1, < 7.2)
|
||||
rubocop (1.63.2)
|
||||
rubocop (1.64.1)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (>= 3.17.0)
|
||||
parallel (~> 1.10)
|
||||
@@ -677,8 +694,8 @@ GEM
|
||||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 3.0)
|
||||
rubocop-ast (1.31.2)
|
||||
parser (>= 3.3.0.4)
|
||||
rubocop-ast (1.31.3)
|
||||
parser (>= 3.3.1.0)
|
||||
rubocop-capybara (2.20.0)
|
||||
rubocop (~> 1.41)
|
||||
rubocop-factory_bot (2.25.1)
|
||||
@@ -688,12 +705,12 @@ GEM
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 1.33.0, < 2.0)
|
||||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
rubocop-rspec (2.29.1)
|
||||
rubocop-rspec (2.29.2)
|
||||
rubocop (~> 1.40)
|
||||
rubocop-capybara (~> 2.17)
|
||||
rubocop-factory_bot (~> 2.22)
|
||||
rubocop-rspec_rails (~> 2.28)
|
||||
rubocop-rspec_rails (2.28.2)
|
||||
rubocop-rspec_rails (2.28.3)
|
||||
rubocop (~> 1.40)
|
||||
ruby-graphviz (1.2.5)
|
||||
rexml
|
||||
@@ -705,7 +722,7 @@ GEM
|
||||
rubyzip (2.3.2)
|
||||
rufus-scheduler (3.8.2)
|
||||
fugit (~> 1.1, >= 1.1.6)
|
||||
sanitize (6.0.2)
|
||||
sanitize (6.1.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
sass (3.4.25)
|
||||
@@ -719,7 +736,7 @@ GEM
|
||||
semantic_range (3.0.0)
|
||||
shoulda-matchers (6.2.0)
|
||||
activesupport (>= 5.2.0)
|
||||
sidekiq (7.2.2)
|
||||
sidekiq (7.2.4)
|
||||
concurrent-ruby (< 2)
|
||||
connection_pool (>= 2.3.0)
|
||||
rack (>= 2.2.4)
|
||||
@@ -737,7 +754,7 @@ GEM
|
||||
spreadsheet_architect (5.0.0)
|
||||
caxlsx (>= 3.3.0, < 4)
|
||||
rodf (>= 1.0.0, < 2)
|
||||
spring (4.2.0)
|
||||
spring (4.2.1)
|
||||
spring-commands-rspec (1.0.4)
|
||||
spring (>= 0.9.1)
|
||||
spring-commands-rubocop (0.4.0)
|
||||
@@ -756,21 +773,21 @@ GEM
|
||||
state_machines-activerecord (0.9.0)
|
||||
activerecord (>= 6.0)
|
||||
state_machines-activemodel (>= 0.9.0)
|
||||
stimulus_reflex (3.5.0.rc3)
|
||||
stimulus_reflex (3.5.1)
|
||||
actioncable (>= 5.2, < 8)
|
||||
actionpack (>= 5.2, < 8)
|
||||
actionview (>= 5.2, < 8)
|
||||
activesupport (>= 5.2, < 8)
|
||||
cable_ready (~> 5.0)
|
||||
nokogiri (~> 1.0)
|
||||
nokogiri-html5-inference (~> 0.3)
|
||||
rack (>= 2, < 4)
|
||||
railties (>= 5.2, < 8)
|
||||
redis (>= 4.0, < 6.0)
|
||||
stimulus_reflex_testing (0.3.0)
|
||||
stimulus_reflex (>= 3.3.0)
|
||||
stringex (2.8.6)
|
||||
stringio (3.1.0)
|
||||
stripe (11.1.0)
|
||||
strscan (3.1.0)
|
||||
swd (2.0.3)
|
||||
activesupport (>= 3)
|
||||
attr_required (>= 0.0.5)
|
||||
@@ -783,6 +800,10 @@ GEM
|
||||
timecop (0.9.8)
|
||||
timeout (0.4.1)
|
||||
ttfunk (1.7.0)
|
||||
turbo-rails (2.0.5)
|
||||
actionpack (>= 6.0.0)
|
||||
activejob (>= 6.0.0)
|
||||
railties (>= 6.0.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
unicode-display_width (2.5.0)
|
||||
@@ -797,7 +818,7 @@ GEM
|
||||
validates_lengths_from_database (0.8.0)
|
||||
activerecord (>= 4)
|
||||
vcr (6.2.0)
|
||||
view_component (3.12.0)
|
||||
view_component (3.12.1)
|
||||
activesupport (>= 5.2.0, < 8.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
method_source (~> 1.0)
|
||||
@@ -818,7 +839,7 @@ GEM
|
||||
activesupport
|
||||
faraday (~> 2.0)
|
||||
faraday-follow_redirects
|
||||
webmock (3.23.0)
|
||||
webmock (3.23.1)
|
||||
addressable (>= 2.8.0)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
@@ -835,11 +856,11 @@ GEM
|
||||
chronic (>= 0.6.3)
|
||||
wicked_pdf (2.6.3)
|
||||
activesupport
|
||||
wkhtmltopdf-binary (0.12.6.6)
|
||||
wkhtmltopdf-binary (0.12.6.7)
|
||||
xml-simple (1.1.8)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.6.13)
|
||||
zeitwerk (2.6.15)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
@@ -865,7 +886,7 @@ DEPENDENCIES
|
||||
bootsnap
|
||||
bugsnag
|
||||
bullet
|
||||
cable_ready (= 5.0.1)
|
||||
cable_ready
|
||||
cancancan (~> 1.15.0)
|
||||
capybara
|
||||
catalog!
|
||||
@@ -944,7 +965,6 @@ DEPENDENCIES
|
||||
redcarpet
|
||||
redis
|
||||
responders
|
||||
rexml
|
||||
roadie-rails
|
||||
roo
|
||||
rspec-rails (>= 3.5.2)
|
||||
@@ -967,11 +987,12 @@ DEPENDENCIES
|
||||
spring-commands-rspec
|
||||
spring-commands-rubocop
|
||||
state_machines-activerecord
|
||||
stimulus_reflex (= 3.5.0.rc3)
|
||||
stimulus_reflex_testing
|
||||
stimulus_reflex
|
||||
stimulus_reflex_testing!
|
||||
stringex (~> 2.8.5)
|
||||
stripe
|
||||
timecop
|
||||
turbo-rails
|
||||
valid_email2
|
||||
validates_lengths_from_database
|
||||
vcr
|
||||
|
||||
@@ -31,7 +31,7 @@ angular.module("admin.indexUtils").factory 'Columns', ($rootScope, $http, $injec
|
||||
savePreferences: (action_name) =>
|
||||
$http
|
||||
method: "PUT"
|
||||
url: "/admin/column_preferences/bulk_update"
|
||||
url: "/admin/column_preferences/bulk_update.json"
|
||||
data:
|
||||
action_name: action_name
|
||||
column_preferences: (preference for column_name, preference of @columns)
|
||||
|
||||
@@ -42,8 +42,17 @@ angular.module('Darkswarm').controller "RegistrationCtrl", ($scope, Registration
|
||||
$scope.toggleAddressConfirmed = ->
|
||||
$scope.addressConfirmed = !$scope.addressConfirmed
|
||||
if $scope.addressConfirmed
|
||||
$scope.setLatLongIfUsingOpenStreetMap()
|
||||
$scope.enterprise.address.latitude = $scope.latLong.latitude
|
||||
$scope.enterprise.address.longitude = $scope.latLong.longitude
|
||||
else
|
||||
$scope.enterprise.address.latitude = null
|
||||
$scope.enterprise.address.longitude = null
|
||||
|
||||
# When OpenStreetMaps is enabled the latitude and longitude are calculated via a Stimulus
|
||||
# controller, so they need to be read from data properties to be accessible here.
|
||||
$scope.setLatLongIfUsingOpenStreetMap = ->
|
||||
openStreetMap = document.getElementById("open-street-map")
|
||||
if !$scope.latLong && openStreetMap && openStreetMap.dataset.latitude && openStreetMap.dataset.longitude
|
||||
$scope.latLong = { latitude: openStreetMap.dataset.latitude, longitude: openStreetMap.dataset.longitude }
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
.menu_items
|
||||
.menu_item{ "ng-repeat": "column in columns", "ng-click": "toggle(column);" }
|
||||
%input.redesigned-input{ type: "checkbox", "ng-checked": "column.visible" }
|
||||
{{ column.name }}
|
||||
%input{ type: "checkbox", "ng-checked": "column.visible" }
|
||||
%span
|
||||
{{ column.name }}
|
||||
%hr
|
||||
%div.menu_item.text-center
|
||||
%input.fullwidth.orange{ type: "button", "ng-value": "saved() ? 'Saved': 'Saving'", "ng-show": "saved() || saving", "ng-disabled": "saved()" }
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
class HelpModalComponent < ModalComponent
|
||||
def initialize(id:, close_button: true)
|
||||
super(id:, close_button:)
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
%div.menu_items
|
||||
- @options.each do |option|
|
||||
%label.menu_item{ "data-multiple-checked-select-target": "option", "data-value": option[1], "data-label": option[0] }
|
||||
%input.redesigned-input{ type: "checkbox", checked: @selected.include?(option[1]), name: "#{@name}[]", value: option[1] }
|
||||
= option[0]
|
||||
%input{ type: "checkbox", checked: @selected.include?(option[1]), name: "#{@name}[]", value: option[1] }
|
||||
%span= option[0]
|
||||
|
||||
43
app/components/searchable_dropdown_component.rb
Normal file
43
app/components/searchable_dropdown_component.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SearchableDropdownComponent < ViewComponent::Base
|
||||
REMOVED_SEARCH_PLUGIN = { 'tom-select-options-value': '{ "plugins": [] }' }.freeze
|
||||
MINIMUM_OPTIONS_FOR_SEARCH_FIELD = 11 # at least 11 options are required for the search field
|
||||
|
||||
def initialize(
|
||||
form:,
|
||||
name:,
|
||||
options:,
|
||||
selected_option:,
|
||||
placeholder_value:,
|
||||
include_blank: false,
|
||||
aria_label: ''
|
||||
)
|
||||
@f = form
|
||||
@name = name
|
||||
@options = options
|
||||
@selected_option = selected_option
|
||||
@placeholder_value = placeholder_value
|
||||
@include_blank = include_blank
|
||||
@aria_label = aria_label
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :f, :name, :options, :selected_option, :placeholder_value, :include_blank, :aria_label
|
||||
|
||||
def classes
|
||||
"fullwidth #{remove_search_plugin? ? 'no-input' : ''}"
|
||||
end
|
||||
|
||||
def data
|
||||
{
|
||||
controller: "tom-select",
|
||||
'tom-select-placeholder-value': placeholder_value
|
||||
}.merge(remove_search_plugin? ? REMOVED_SEARCH_PLUGIN : {})
|
||||
end
|
||||
|
||||
def remove_search_plugin?
|
||||
@remove_search_plugin ||= options.count < MINIMUM_OPTIONS_FOR_SEARCH_FIELD
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1 @@
|
||||
= f.select name, options_for_select(options, selected_option), { include_blank: }, class: classes, data:, 'aria-label': aria_label
|
||||
@@ -35,7 +35,7 @@ module Admin
|
||||
order.with_lock do
|
||||
if order.contents.update_item(@line_item, line_item_params)
|
||||
# No Content, does not trigger ng resource auto-update
|
||||
render body: nil, status: :no_content
|
||||
head :no_content
|
||||
else
|
||||
render json: { errors: @line_item.errors }, status: :precondition_failed
|
||||
end
|
||||
@@ -49,7 +49,7 @@ module Admin
|
||||
authorize! :update, order
|
||||
|
||||
order.contents.remove(@line_item.variant)
|
||||
render body: nil, status: :no_content # No Content, does not trigger ng resource auto-update
|
||||
head :no_content # No Content, does not trigger ng resource auto-update
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -4,17 +4,25 @@ module Admin
|
||||
class ColumnPreferencesController < Admin::ResourceController
|
||||
before_action :load_collection, only: [:bulk_update]
|
||||
|
||||
respond_to :json
|
||||
|
||||
def bulk_update
|
||||
@cp_set.collection.each { |cp| authorize! :bulk_update, cp }
|
||||
|
||||
if @cp_set.save
|
||||
render json: @cp_set.collection, each_serializer: Api::Admin::ColumnPreferenceSerializer
|
||||
elsif @cp_set.errors.present?
|
||||
render json: { errors: @cp_set.errors }, status: :bad_request
|
||||
else
|
||||
render body: nil, status: :internal_server_error
|
||||
respond_to do |format|
|
||||
if @cp_set.save
|
||||
format.json {
|
||||
render json: @cp_set.collection, each_serializer: Api::Admin::ColumnPreferenceSerializer
|
||||
}
|
||||
format.turbo_stream {
|
||||
flash.now[:success] = t('.success')
|
||||
render :bulk_update, locals: { action: permitted_params[:action_name] }
|
||||
}
|
||||
else
|
||||
format.json { render json: { errors: @cp_set.errors }, status: :bad_request }
|
||||
format.turbo_stream {
|
||||
flash.now[:error] = @cp_set.errors.full_messages.to_sentence
|
||||
render :bulk_update, locals: { action: permitted_params[:action_name] }
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -28,11 +36,26 @@ module Admin
|
||||
end
|
||||
|
||||
def load_collection
|
||||
collection_hash = Hash[permitted_params[:column_preferences].
|
||||
each_with_index.map { |cp, i| [i, cp] }]
|
||||
collection_hash.select!{ |_i, cp| cp[:action_name] == permitted_params[:action_name] }
|
||||
@cp_set = Sets::ColumnPreferenceSet.new(@column_preferences,
|
||||
collection_attributes: collection_hash)
|
||||
collection_attributes = nil
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
collection_attributes = Hash[permitted_params[:column_preferences].
|
||||
each_with_index.map { |cp, i| [i, cp] }]
|
||||
collection_attributes.select!{ |_i, cp|
|
||||
cp[:action_name] == permitted_params[:action_name]
|
||||
}
|
||||
end
|
||||
format.all do
|
||||
# Inject action name and user ID for each column_preference
|
||||
collection_attributes = permitted_params[:column_preferences].to_h.each_value { |cp|
|
||||
cp[:action_name] = permitted_params[:action_name]
|
||||
cp[:user_id] = spree_current_user.id
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@cp_set = Sets::ColumnPreferenceSet.new(@column_preferences, collection_attributes:)
|
||||
end
|
||||
|
||||
def collection
|
||||
|
||||
43
app/controllers/admin/connected_apps_controller.rb
Normal file
43
app/controllers/admin/connected_apps_controller.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class ConnectedAppsController < ApplicationController
|
||||
def create
|
||||
authorize! :admin, enterprise
|
||||
|
||||
app = ConnectedApp.create!(enterprise_id: enterprise.id)
|
||||
|
||||
ConnectAppJob.perform_later(
|
||||
app, spree_current_user.spree_api_key,
|
||||
channel: SessionChannel.for_request(request),
|
||||
)
|
||||
|
||||
render_panel
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize! :admin, enterprise
|
||||
|
||||
app = enterprise.connected_apps.first
|
||||
app.destroy
|
||||
|
||||
WebhookDeliveryJob.perform_later(
|
||||
app.data["destroy"],
|
||||
"disconnect-app",
|
||||
nil
|
||||
)
|
||||
|
||||
render_panel
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def enterprise
|
||||
@enterprise ||= Enterprise.find(params.require(:enterprise_id))
|
||||
end
|
||||
|
||||
def render_panel
|
||||
redirect_to "#{edit_admin_enterprise_path(enterprise)}#/connected_apps_panel"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -20,7 +20,7 @@ module Admin
|
||||
|
||||
catalog_url = params.require(:catalog_url)
|
||||
|
||||
json_catalog = DfcRequest.new(spree_current_user).get(catalog_url)
|
||||
json_catalog = fetch_catalog(catalog_url)
|
||||
graph = DfcIo.import(json_catalog)
|
||||
|
||||
# * First step: import all products for given enterprise.
|
||||
@@ -34,6 +34,16 @@ module Admin
|
||||
|
||||
private
|
||||
|
||||
def fetch_catalog(url)
|
||||
if url =~ /food-data-collaboration/
|
||||
fdc_json = FdcRequest.new(spree_current_user).call(url)
|
||||
fdc_message = JSON.parse(fdc_json)
|
||||
fdc_message["products"]
|
||||
else
|
||||
DfcRequest.new(spree_current_user).call(url)
|
||||
end
|
||||
end
|
||||
|
||||
# Most of this code is the same as in the DfcProvider::SuppliedProductsController.
|
||||
def import_product(subject, enterprise)
|
||||
return unless subject.is_a? DataFoodConsortium::Connector::SuppliedProduct
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# rubocop:disable Metrics/ClassLength
|
||||
module Admin
|
||||
class ProductsV3Controller < Spree::Admin::BaseController
|
||||
helper ProductsHelper
|
||||
|
||||
before_action :init_filters_params
|
||||
before_action :init_pagination_params
|
||||
|
||||
def index
|
||||
fetch_products
|
||||
render "index", locals: { producers:, categories:, flash: }
|
||||
render "index", locals: { producers:, categories:, tax_category_options:, flash: }
|
||||
end
|
||||
|
||||
def bulk_update
|
||||
@@ -24,7 +27,44 @@ module Admin
|
||||
elsif product_set.errors.present?
|
||||
@error_counts = { saved: product_set.saved_count, invalid: product_set.invalid.count }
|
||||
|
||||
render "index", status: :unprocessable_entity, locals: { producers:, categories:, flash: }
|
||||
render "index", status: :unprocessable_entity,
|
||||
locals: { producers:, categories:, tax_category_options:, flash: }
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@record = ProductScopeQuery.new(
|
||||
spree_current_user,
|
||||
{ id: params[:id] }
|
||||
).find_product
|
||||
|
||||
status = :ok
|
||||
if @record.destroy
|
||||
flash.now[:success] = t('.delete_product.success')
|
||||
else
|
||||
flash.now[:error] = t('.delete_product.error')
|
||||
status = :internal_server_error
|
||||
end
|
||||
|
||||
respond_with do |format|
|
||||
format.turbo_stream { render :destroy_product_variant, status: }
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_variant
|
||||
@record = Spree::Variant.active.find(params[:id])
|
||||
authorize! :delete, @record
|
||||
|
||||
status = :ok
|
||||
if VariantDeleter.new.delete(@record)
|
||||
flash.now[:success] = t('.delete_variant.success')
|
||||
else
|
||||
flash.now[:error] = t('.delete_variant.error')
|
||||
status = :internal_server_error
|
||||
end
|
||||
|
||||
respond_with do |format|
|
||||
format.turbo_stream { render :destroy_product_variant, status: }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -47,6 +87,7 @@ module Admin
|
||||
# prority is given to element dataset (if present) over url params
|
||||
@page = params[:page].presence || 1
|
||||
@per_page = params[:per_page].presence || 15
|
||||
@q = params.permit(q: {})[:q] || { s: 'name asc' }
|
||||
end
|
||||
|
||||
def producers
|
||||
@@ -59,6 +100,10 @@ module Admin
|
||||
Spree::Taxon.order(:name).map { |c| [c.name, c.id] }
|
||||
end
|
||||
|
||||
def tax_category_options
|
||||
Spree::TaxCategory.order(:name).pluck(:name, :id)
|
||||
end
|
||||
|
||||
def fetch_products
|
||||
product_query = OpenFoodNetwork::Permissions.new(spree_current_user)
|
||||
.editable_products.merge(product_scope).ransack(ransack_query).result
|
||||
@@ -84,6 +129,8 @@ module Admin
|
||||
query.merge!(Spree::Variant::SEARCH_KEY => @search_term)
|
||||
end
|
||||
query.merge!(variants_primary_taxon_id_in: @category_id) if @category_id.present?
|
||||
query.merge!(@q) if @q
|
||||
|
||||
query
|
||||
end
|
||||
|
||||
@@ -137,3 +184,4 @@ module Admin
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ClassLength
|
||||
|
||||
@@ -146,7 +146,7 @@ module Admin
|
||||
return nil if parent_data.blank?
|
||||
|
||||
@parent ||= parent_data[:model_class].
|
||||
public_send("find_by", parent_data[:find_by] => params["#{model_name}_id"])
|
||||
find_by(parent_data[:find_by] => params["#{model_name}_id"])
|
||||
instance_variable_set("@#{model_name}", @parent)
|
||||
end
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ module Admin
|
||||
|
||||
def obfuscated_secret_key
|
||||
key = Stripe.api_key
|
||||
key.first(8) + "****" + key.last(4)
|
||||
"#{key.first(8)}****#{key.last(4)}"
|
||||
end
|
||||
|
||||
def settings_params
|
||||
|
||||
@@ -5,7 +5,7 @@ module Admin
|
||||
respond_to :json
|
||||
|
||||
respond_override destroy: { json: {
|
||||
success: lambda { render body: nil, status: :no_content }
|
||||
success: lambda { head :no_content }
|
||||
} }
|
||||
|
||||
def map_by_tag
|
||||
|
||||
@@ -9,7 +9,7 @@ module Api
|
||||
authorize! :destroy, enterprise_fee
|
||||
|
||||
if enterprise_fee.destroy
|
||||
render plain: I18n.t(:successfully_removed), status: :no_content
|
||||
head :no_content
|
||||
else
|
||||
render plain: enterprise_fee.errors.full_messages.first, status: :forbidden
|
||||
end
|
||||
|
||||
@@ -42,8 +42,9 @@ module Api
|
||||
authorize! :delete, Spree::Product
|
||||
@product = product_finder.find_product
|
||||
authorize! :delete, @product
|
||||
@product.destroyed_by = current_api_user
|
||||
@product.destroy
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer, status: :no_content
|
||||
head :no_content
|
||||
end
|
||||
|
||||
def bulk_products
|
||||
|
||||
@@ -55,7 +55,7 @@ module Api
|
||||
def destroy
|
||||
authorize! :delete, Spree::Taxon
|
||||
taxon.destroy
|
||||
render json: taxon, serializer: Api::TaxonSerializer, status: :no_content
|
||||
head :no_content
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -44,7 +44,7 @@ module Api
|
||||
authorize! :delete, @variant
|
||||
|
||||
VariantDeleter.new.delete(@variant)
|
||||
render json: @variant, serializer: Api::VariantSerializer, status: :no_content
|
||||
head :no_content
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -28,7 +28,6 @@ class ApplicationController < ActionController::Base
|
||||
helper 'injection'
|
||||
helper 'markdown'
|
||||
helper 'footer_links'
|
||||
helper 'discourse'
|
||||
helper 'checkout'
|
||||
helper 'link'
|
||||
helper 'terms_and_conditions'
|
||||
@@ -61,7 +60,7 @@ class ApplicationController < ActionController::Base
|
||||
rescue StandardError
|
||||
'unknown'
|
||||
end}")
|
||||
super(options, response_status)
|
||||
super
|
||||
end
|
||||
|
||||
def set_checkout_redirect
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'discourse/single_sign_on'
|
||||
|
||||
class DiscourseSsoController < ApplicationController
|
||||
include SharedHelper
|
||||
include DiscourseHelper
|
||||
|
||||
before_action :require_config
|
||||
|
||||
def login
|
||||
if require_activation?
|
||||
redirect_to discourse_url
|
||||
else
|
||||
redirect_to discourse_login_url
|
||||
end
|
||||
end
|
||||
|
||||
def sso
|
||||
if spree_current_user
|
||||
begin
|
||||
redirect_to sso_url
|
||||
rescue TypeError
|
||||
render plain: "Bad SingleSignOn request.", status: :bad_request
|
||||
end
|
||||
else
|
||||
redirect_to login_path
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sso_url
|
||||
secret = discourse_sso_secret!
|
||||
sso = Discourse::SingleSignOn.parse(request.query_string, secret)
|
||||
sso.email = spree_current_user.email
|
||||
sso.username = spree_current_user.login
|
||||
sso.external_id = spree_current_user.id
|
||||
sso.sso_secret = secret
|
||||
sso.admin = admin_user?
|
||||
sso.require_activation = require_activation?
|
||||
sso.to_url(discourse_sso_url)
|
||||
end
|
||||
|
||||
def require_config
|
||||
raise ActionController::RoutingError, 'Not Found' unless discourse_configured?
|
||||
end
|
||||
|
||||
def require_activation?
|
||||
!admin_user? && !spree_current_user.confirmed?
|
||||
end
|
||||
end
|
||||
@@ -3,6 +3,8 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class ImagesController < ::Admin::ResourceController
|
||||
helper ::Admin::ProductsHelper
|
||||
|
||||
# This will make resource controller redirect correctly after deleting product images.
|
||||
# This can be removed after upgrading to Spree 2.1.
|
||||
# See here https://github.com/spree/spree/commit/334a011d2b8e16355e4ae77ae07cd93f7cbc8fd1
|
||||
@@ -17,7 +19,10 @@ module Spree
|
||||
def new
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
|
||||
render layout: !request.xhr?
|
||||
respond_with do |format|
|
||||
format.turbo_stream { render :edit }
|
||||
format.all { render layout: !request.xhr? }
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@@ -32,13 +37,14 @@ module Spree
|
||||
|
||||
if @object.save
|
||||
flash[:success] = flash_message_for(@object, :successfully_created)
|
||||
redirect_to location_after_save
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to location_after_save }
|
||||
format.turbo_stream { render :update }
|
||||
end
|
||||
else
|
||||
respond_with(@object)
|
||||
respond_with_error(@object.errors)
|
||||
end
|
||||
rescue ActiveStorage::IntegrityError
|
||||
@object.errors.add :attachment, :integrity_error
|
||||
respond_with(@object)
|
||||
end
|
||||
|
||||
def update
|
||||
@@ -47,13 +53,14 @@ module Spree
|
||||
|
||||
if @object.update(permitted_resource_params)
|
||||
flash[:success] = flash_message_for(@object, :successfully_updated)
|
||||
redirect_to location_after_save
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to location_after_save }
|
||||
format.turbo_stream
|
||||
end
|
||||
else
|
||||
respond_with(@object)
|
||||
respond_with_error(@object.errors)
|
||||
end
|
||||
rescue ActiveStorage::IntegrityError
|
||||
@object.errors.add :attachment, :integrity_error
|
||||
respond_with(@object)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@@ -103,6 +110,14 @@ module Spree
|
||||
:attachment, :viewable_id, :alt
|
||||
)
|
||||
end
|
||||
|
||||
def respond_with_error(errors)
|
||||
@errors = errors.map(&:full_message)
|
||||
respond_to do |format|
|
||||
format.html { respond_with(@object) }
|
||||
format.turbo_stream { render :edit }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,8 +19,13 @@ module Spree
|
||||
|
||||
def generate
|
||||
@order = Order.find_by(number: params[:order_id])
|
||||
authorize! :invoice, @order
|
||||
::Orders::GenerateInvoiceService.new(@order).generate_or_update_latest_invoice
|
||||
if @order.distributor.can_invoice?
|
||||
authorize! :invoice, @order
|
||||
::Orders::GenerateInvoiceService.new(@order).generate_or_update_latest_invoice
|
||||
else
|
||||
flash[:error] = t(:must_have_valid_business_number,
|
||||
enterprise_name: @order.distributor.name)
|
||||
end
|
||||
redirect_back(fallback_location: spree.admin_dashboard_path)
|
||||
end
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ module Admin
|
||||
producers.size == 1 ? producers.first.id : nil
|
||||
end
|
||||
|
||||
def managed_by_user?(enterprise)
|
||||
enterprise.in?(spree_current_user.enterprises)
|
||||
end
|
||||
|
||||
def enterprise_side_menu_items(enterprise)
|
||||
is_shop = enterprise.sells != "none"
|
||||
show_properties = !!enterprise.is_primary_producer
|
||||
|
||||
@@ -11,9 +11,25 @@ module Admin
|
||||
def order_adjustments_for_display(order)
|
||||
order.adjustments +
|
||||
voucher_included_tax_representations(order) +
|
||||
additional_tax_total_representation(order) +
|
||||
order.all_adjustments.payment_fee.eligible
|
||||
end
|
||||
|
||||
def additional_tax_total_representation(order)
|
||||
adjustment = Spree::Adjustment.additional.tax.where(
|
||||
order_id: order.id, adjustable_type: 'Spree::Adjustment'
|
||||
).sum(:amount)
|
||||
|
||||
return [] unless adjustment != 0
|
||||
|
||||
[
|
||||
AdjustmentData.new(
|
||||
I18n.t("admin.orders.edit.tax_on_fees"),
|
||||
adjustment
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
def voucher_included_tax_representations(order)
|
||||
return [] unless VoucherAdjustmentsService.new(order).voucher_included_tax.negative?
|
||||
|
||||
|
||||
13
app/helpers/admin/products_helper.rb
Normal file
13
app/helpers/admin/products_helper.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
module ProductsHelper
|
||||
def product_image_form_path(product)
|
||||
if product.image.present?
|
||||
edit_admin_product_image_path(product.id, product.image.id)
|
||||
else
|
||||
new_admin_product_image_path(product.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,6 +8,6 @@ class BulkFormBuilder < ActionView::Helpers::FormBuilder
|
||||
opts[:class] = "#{opts[:class]} changed".strip
|
||||
end
|
||||
|
||||
super(field, **opts)
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module DiscourseHelper
|
||||
def discourse_configured?
|
||||
discourse_url.present?
|
||||
end
|
||||
|
||||
def discourse_url
|
||||
ENV.fetch('DISCOURSE_URL', nil)
|
||||
end
|
||||
|
||||
def discourse_login_url
|
||||
discourse_url + '/login'
|
||||
end
|
||||
|
||||
def discourse_sso_url
|
||||
discourse_url + '/session/sso_login'
|
||||
end
|
||||
|
||||
def discourse_url!
|
||||
discourse_url || raise('Missing Discourse URL')
|
||||
end
|
||||
|
||||
def discourse_sso_secret!
|
||||
ENV['DISCOURSE_SSO_SECRET'] || raise('Missing SSO secret')
|
||||
end
|
||||
end
|
||||
@@ -31,7 +31,7 @@ module EnterprisesHelper
|
||||
|
||||
def enterprises_options(enterprises)
|
||||
enterprises.map { |enterprise|
|
||||
[enterprise.name + ": " + enterprise.address.address1 + ", " + enterprise.address.city,
|
||||
["#{enterprise.name}: #{enterprise.address.address1}, #{enterprise.address.city}",
|
||||
enterprise.id.to_i]
|
||||
}
|
||||
end
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
module MapHelper
|
||||
def using_google_maps?
|
||||
ENV["GOOGLE_MAPS_API_KEY"].present? || google_maps_configured_with_geocoder_api_key?
|
||||
!ContentConfig.open_street_map_enabled && (
|
||||
ENV["GOOGLE_MAPS_API_KEY"].present? || google_maps_configured_with_geocoder_api_key?
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -108,7 +108,7 @@ module Spree
|
||||
|
||||
object.preferences.keys.map { |key|
|
||||
preference_label = form.label("preferred_#{key}",
|
||||
Spree.t(key.to_s.gsub("_from_list", "")) + ": ")
|
||||
"#{Spree.t(key.to_s.gsub('_from_list', ''))}: ")
|
||||
preference_field = preference_field_for(
|
||||
form,
|
||||
"preferred_#{key}",
|
||||
@@ -120,7 +120,7 @@ module Spree
|
||||
|
||||
def link_to_add_fields(name, target, options = {})
|
||||
name = '' if options[:no_text]
|
||||
css_classes = options[:class] ? options[:class] + " spree_add_fields" : "spree_add_fields"
|
||||
css_classes = options[:class] ? "#{options[:class]} spree_add_fields" : "spree_add_fields"
|
||||
link_to_with_icon('icon-plus',
|
||||
name,
|
||||
'javascript:',
|
||||
|
||||
@@ -34,6 +34,10 @@ module Spree
|
||||
links
|
||||
end
|
||||
|
||||
def order_shipment_ready?(order)
|
||||
order.ready_to_ship?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def complete_order_links(order)
|
||||
|
||||
@@ -17,7 +17,7 @@ class ConnectAppJob < ApplicationJob
|
||||
|
||||
return unless channel
|
||||
|
||||
selector = "#edit_enterprise_#{enterprise.id} #connected-app-discover-regen"
|
||||
selector = "#connected-app-discover-regen.enterprise_#{enterprise.id}"
|
||||
html = ApplicationController.render(
|
||||
partial: "admin/enterprises/form/connected_apps",
|
||||
locals: { enterprise: },
|
||||
|
||||
@@ -25,7 +25,7 @@ module Spree
|
||||
@user = user
|
||||
I18n.with_locale valid_locale(@user) do
|
||||
mail(to: user.email,
|
||||
subject: t(:welcome_to) + ' ' + Spree::Config[:site_name])
|
||||
subject: "#{t(:welcome_to)} #{Spree::Config[:site_name]}")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ module FilePreferences
|
||||
if has_preference?("#{key}_blob_id")
|
||||
:file
|
||||
else
|
||||
super(key)
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
18
app/models/concerns/log_destroy_performer.rb
Normal file
18
app/models/concerns/log_destroy_performer.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'active_support/concern'
|
||||
|
||||
module LogDestroyPerformer
|
||||
extend ActiveSupport::Concern
|
||||
included do
|
||||
attr_accessor :destroyed_by
|
||||
|
||||
after_destroy :log_who_destroyed
|
||||
|
||||
def log_who_destroyed
|
||||
return if destroyed_by.nil?
|
||||
|
||||
Rails.logger.info "#{self.class} #{id} deleted by #{destroyed_by.id}"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -43,25 +43,31 @@ class Enterprise < ApplicationRecord
|
||||
foreign_key: 'supplier_id',
|
||||
dependent: :destroy
|
||||
has_many :supplied_variants, through: :supplied_products, source: :variants
|
||||
has_many :distributed_orders, class_name: 'Spree::Order', foreign_key: 'distributor_id'
|
||||
has_many :distributed_orders, class_name: 'Spree::Order',
|
||||
foreign_key: 'distributor_id',
|
||||
dependent: :restrict_with_exception
|
||||
belongs_to :address, class_name: 'Spree::Address'
|
||||
belongs_to :business_address, optional: true, class_name: 'Spree::Address', dependent: :destroy
|
||||
has_many :enterprise_fees
|
||||
has_many :enterprise_fees, dependent: :restrict_with_exception
|
||||
has_many :enterprise_roles, dependent: :destroy
|
||||
has_many :users, through: :enterprise_roles
|
||||
belongs_to :owner, class_name: 'Spree::User',
|
||||
inverse_of: :owned_enterprises
|
||||
has_many :distributor_payment_methods,
|
||||
inverse_of: :distributor, foreign_key: :distributor_id
|
||||
inverse_of: :distributor,
|
||||
foreign_key: :distributor_id,
|
||||
dependent: :restrict_with_exception
|
||||
has_many :distributor_shipping_methods,
|
||||
inverse_of: :distributor, foreign_key: :distributor_id
|
||||
inverse_of: :distributor,
|
||||
foreign_key: :distributor_id,
|
||||
dependent: :restrict_with_exception
|
||||
has_many :payment_methods, through: :distributor_payment_methods
|
||||
has_many :shipping_methods, through: :distributor_shipping_methods
|
||||
has_many :customers, dependent: :destroy
|
||||
has_many :inventory_items, dependent: :destroy
|
||||
has_many :tag_rules, dependent: :destroy
|
||||
has_one :stripe_account, dependent: :destroy
|
||||
has_many :vouchers
|
||||
has_many :vouchers, dependent: :restrict_with_exception
|
||||
has_many :connected_apps, dependent: :destroy
|
||||
has_one :custom_tab, dependent: :destroy
|
||||
|
||||
@@ -128,6 +134,7 @@ class Enterprise < ApplicationRecord
|
||||
|
||||
after_create :set_default_contact
|
||||
after_create :relate_to_owners_enterprises
|
||||
|
||||
after_rollback :restore_permalink
|
||||
after_touch :touch_distributors
|
||||
after_create_commit :send_welcome_email
|
||||
@@ -240,6 +247,16 @@ class Enterprise < ApplicationRecord
|
||||
count(distinct: true)
|
||||
end
|
||||
|
||||
# Remove any unsupported HTML.
|
||||
def long_description
|
||||
HtmlSanitizer.sanitize(super)
|
||||
end
|
||||
|
||||
# Remove any unsupported HTML.
|
||||
def long_description=(html)
|
||||
super(HtmlSanitizer.sanitize(html))
|
||||
end
|
||||
|
||||
def contact
|
||||
contact = users.where(enterprise_roles: { receives_notifications: true }).first
|
||||
contact || owner
|
||||
@@ -359,7 +376,7 @@ class Enterprise < ApplicationRecord
|
||||
def category
|
||||
# Make this crazy logic human readable so we can argue about it sanely.
|
||||
cat = is_primary_producer ? "producer_" : "non_producer_"
|
||||
cat << ("sells_" + sells)
|
||||
cat << ("sells_#{sells}")
|
||||
|
||||
# Map backend cases to front end cases.
|
||||
case cat
|
||||
@@ -493,7 +510,7 @@ class Enterprise < ApplicationRecord
|
||||
end
|
||||
|
||||
def correct_whatsapp_url(phone_number)
|
||||
phone_number && ("https://wa.me/" + phone_number.tr('+ ', ''))
|
||||
phone_number && "https://wa.me/#{phone_number.tr('+ ', '')}"
|
||||
end
|
||||
|
||||
def correct_instagram_url(url)
|
||||
|
||||
@@ -21,7 +21,6 @@ class EnterpriseFee < ApplicationRecord
|
||||
|
||||
validates :fee_type, inclusion: { in: FEE_TYPES }
|
||||
validates :name, presence: true
|
||||
validates :enterprise_id, presence: true
|
||||
|
||||
before_save :ensure_valid_tax_category_settings
|
||||
|
||||
|
||||
@@ -74,6 +74,16 @@ class EnterpriseGroup < ApplicationRecord
|
||||
permalink
|
||||
end
|
||||
|
||||
# Remove any unsupported HTML.
|
||||
def long_description
|
||||
HtmlSanitizer.sanitize(super)
|
||||
end
|
||||
|
||||
# Remove any unsupported HTML.
|
||||
def long_description=(html)
|
||||
super(HtmlSanitizer.sanitize(html))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sanitize_permalink
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
# shopfront (outgoing products). But the set of shown products can be smaller
|
||||
# than all incoming products.
|
||||
class Exchange < ApplicationRecord
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
acts_as_taggable
|
||||
|
||||
belongs_to :order_cycle
|
||||
@@ -24,7 +22,6 @@ class Exchange < ApplicationRecord
|
||||
has_many :exchange_fees, dependent: :destroy
|
||||
has_many :enterprise_fees, through: :exchange_fees
|
||||
|
||||
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
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class InventoryItem < ApplicationRecord
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
belongs_to :enterprise
|
||||
belongs_to :variant, class_name: "Spree::Variant"
|
||||
|
||||
validates :variant_id, uniqueness: { scope: :enterprise_id }
|
||||
validates :enterprise, presence: true
|
||||
validates :variant, presence: true
|
||||
validates :visible,
|
||||
inclusion: { in: [true, false], message: I18n.t(:inventory_item_visibility_error) }
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
require 'open_food_network/scope_variant_to_hub'
|
||||
|
||||
class OrderCycle < ApplicationRecord
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
searchable_attributes :orders_open_at, :orders_close_at, :coordinator_id
|
||||
searchable_scopes :active, :inactive, :active_or_complete, :upcoming, :closed, :not_closed,
|
||||
:dated, :undated, :soonest_opening, :soonest_closing, :most_recently_closed
|
||||
@@ -44,7 +42,7 @@ class OrderCycle < ApplicationRecord
|
||||
before_update :reset_processed_at, if: :will_save_change_to_orders_close_at?
|
||||
after_save :sync_subscriptions, if: :opening?
|
||||
|
||||
validates :name, :coordinator_id, presence: true
|
||||
validates :name, presence: true
|
||||
validate :orders_close_at_after_orders_open_at?
|
||||
|
||||
preference :product_selection_from_coordinator_inventory_only, :boolean, default: false
|
||||
@@ -255,7 +253,7 @@ class OrderCycle < ApplicationRecord
|
||||
end
|
||||
|
||||
def pickup_time_for(distributor)
|
||||
exchange_for_distributor(distributor)&.pickup_time || distributor.next_collection_at
|
||||
exchange_for_distributor(distributor)&.pickup_time
|
||||
end
|
||||
|
||||
def pickup_instructions_for(distributor)
|
||||
|
||||
@@ -192,7 +192,7 @@ module Spree
|
||||
OpenFoodNetwork::Permissions.new(user).managed_product_enterprises.include? product.supplier
|
||||
end
|
||||
|
||||
can [:admin, :index], :products_v3
|
||||
can [:admin, :index, :bulk_update, :destroy, :destroy_variant], :products_v3
|
||||
|
||||
can [:create], Spree::Variant
|
||||
can [:admin, :index, :read, :edit,
|
||||
|
||||
@@ -4,19 +4,17 @@ module Spree
|
||||
class Address < ApplicationRecord
|
||||
include AddressDisplay
|
||||
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
searchable_attributes :firstname, :lastname, :phone, :full_name, :full_name_reversed,
|
||||
:full_name_with_comma, :full_name_with_comma_reversed
|
||||
searchable_associations :country, :state
|
||||
|
||||
belongs_to :country, class_name: "Spree::Country"
|
||||
belongs_to :state, class_name: "Spree::State"
|
||||
belongs_to :state, class_name: "Spree::State", optional: true
|
||||
|
||||
has_one :enterprise, dependent: :restrict_with_exception
|
||||
has_many :shipments
|
||||
has_many :shipments, dependent: :restrict_with_exception
|
||||
|
||||
validates :address1, :city, :country, :phone, presence: true
|
||||
validates :address1, :city, :phone, presence: true
|
||||
validates :company, presence: true, unless: -> { first_name.blank? || last_name.blank? }
|
||||
validates :firstname, :lastname, presence: true, if: -> do
|
||||
company.blank? || company == 'unused'
|
||||
|
||||
@@ -30,7 +30,7 @@ module Spree
|
||||
|
||||
def expiry=(expiry)
|
||||
self[:month], self[:year] = expiry.split(" / ")
|
||||
self[:year] = "20" + self[:year]
|
||||
self[:year] = "20#{self[:year]}"
|
||||
end
|
||||
|
||||
def number=(num)
|
||||
|
||||
@@ -7,8 +7,6 @@ module Spree
|
||||
include VariantUnits::VariantAndLineItemNaming
|
||||
include LineItemStockChanges
|
||||
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
searchable_attributes :price, :quantity, :order_id, :variant_id, :tax_category_id
|
||||
searchable_associations :order, :order_cycle, :variant, :product, :supplier, :tax_category
|
||||
searchable_scopes :with_tax, :without_tax
|
||||
@@ -19,7 +17,7 @@ module Spree
|
||||
belongs_to :variant, -> { with_deleted }, class_name: "Spree::Variant"
|
||||
has_one :product, through: :variant
|
||||
has_one :supplier, through: :product
|
||||
belongs_to :tax_category, class_name: "Spree::TaxCategory"
|
||||
belongs_to :tax_category, class_name: "Spree::TaxCategory", optional: true
|
||||
|
||||
has_many :adjustments, as: :adjustable, dependent: :destroy
|
||||
|
||||
@@ -28,7 +26,6 @@ module Spree
|
||||
before_validation :copy_tax_category
|
||||
before_validation :copy_dimensions
|
||||
|
||||
validates :variant, presence: true
|
||||
validates :quantity, numericality: {
|
||||
only_integer: true,
|
||||
greater_than: -1,
|
||||
|
||||
@@ -8,8 +8,6 @@ module Spree
|
||||
include Balance
|
||||
include SetUnusedAddressFields
|
||||
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
searchable_attributes :number, :state, :shipment_state, :payment_state, :distributor_id,
|
||||
:order_cycle_id, :email, :total, :customer_id
|
||||
searchable_associations :shipping_method, :bill_address, :distributor
|
||||
@@ -33,13 +31,13 @@ module Spree
|
||||
|
||||
token_resource
|
||||
|
||||
belongs_to :user, class_name: "Spree::User"
|
||||
belongs_to :created_by, class_name: "Spree::User"
|
||||
belongs_to :user, class_name: "Spree::User", optional: true
|
||||
belongs_to :created_by, class_name: "Spree::User", optional: true
|
||||
|
||||
belongs_to :bill_address, class_name: 'Spree::Address'
|
||||
belongs_to :bill_address, class_name: 'Spree::Address', optional: true
|
||||
alias_attribute :billing_address, :bill_address
|
||||
|
||||
belongs_to :ship_address, class_name: 'Spree::Address'
|
||||
belongs_to :ship_address, class_name: 'Spree::Address', optional: true
|
||||
alias_attribute :shipping_address, :ship_address
|
||||
|
||||
has_many :state_changes, as: :stateful, dependent: :destroy
|
||||
@@ -70,9 +68,9 @@ module Spree
|
||||
dependent: :destroy
|
||||
has_many :invoices, dependent: :restrict_with_exception
|
||||
|
||||
belongs_to :order_cycle
|
||||
belongs_to :distributor, class_name: 'Enterprise'
|
||||
belongs_to :customer
|
||||
belongs_to :order_cycle, optional: true
|
||||
belongs_to :distributor, class_name: 'Enterprise', optional: true
|
||||
belongs_to :customer, optional: true
|
||||
has_one :proxy_order, dependent: :destroy
|
||||
has_one :subscription, through: :proxy_order
|
||||
|
||||
|
||||
@@ -155,6 +155,7 @@ module Spree
|
||||
if adjustment
|
||||
adjustment.originator = payment_method
|
||||
adjustment.label = adjustment_label
|
||||
adjustment.amount = payment_method.compute_amount(self)
|
||||
adjustment.save
|
||||
elsif !processing_refund? && payment_method.present?
|
||||
payment_method.create_adjustment(adjustment_label, self, true)
|
||||
|
||||
@@ -118,7 +118,7 @@ module Spree
|
||||
end
|
||||
|
||||
def self.clean_name
|
||||
i18n_key = "spree.admin.payment_methods.providers." + name.demodulize.downcase
|
||||
i18n_key = "spree.admin.payment_methods.providers.#{name.demodulize.downcase}"
|
||||
I18n.t(i18n_key)
|
||||
end
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@ module Spree
|
||||
ActiveMerchant::Billing::Response.new(true, "", {}, {})
|
||||
end
|
||||
|
||||
def payment_source_class
|
||||
nil
|
||||
end
|
||||
|
||||
def source_required?
|
||||
false
|
||||
end
|
||||
|
||||
@@ -24,10 +24,6 @@ module Spree
|
||||
amount
|
||||
end
|
||||
|
||||
def price_changed?
|
||||
amount_changed?
|
||||
end
|
||||
|
||||
def price=(price)
|
||||
self[:amount] = parse_price(price)
|
||||
end
|
||||
|
||||
@@ -23,6 +23,7 @@ require 'open_food_network/property_merge'
|
||||
module Spree
|
||||
class Product < ApplicationRecord
|
||||
include ProductStock
|
||||
include LogDestroyPerformer
|
||||
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
@@ -303,6 +304,16 @@ module Spree
|
||||
)
|
||||
end
|
||||
|
||||
# Remove any unsupported HTML.
|
||||
def description
|
||||
HtmlSanitizer.sanitize(super)
|
||||
end
|
||||
|
||||
# Remove any unsupported HTML.
|
||||
def description=(html)
|
||||
super(HtmlSanitizer.sanitize(html))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_units
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
|
||||
module Spree
|
||||
class ProductProperty < ApplicationRecord
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
belongs_to :product, class_name: "Spree::Product", touch: true
|
||||
belongs_to :property, class_name: 'Spree::Property'
|
||||
|
||||
validates :property, presence: true
|
||||
validates :value, length: { maximum: 255 }
|
||||
|
||||
default_scope -> { order("#{table_name}.position") }
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
module Spree
|
||||
class ReturnAuthorization < ApplicationRecord
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
acts_as_paranoid
|
||||
|
||||
belongs_to :order, class_name: 'Spree::Order', inverse_of: :return_authorizations
|
||||
@@ -13,7 +11,6 @@ module Spree
|
||||
before_save :force_positive_amount
|
||||
before_create :generate_number
|
||||
|
||||
validates :order, presence: true
|
||||
validates :amount, numericality: true
|
||||
validate :must_have_shipped_units
|
||||
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
module Spree
|
||||
class State < ApplicationRecord
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
belongs_to :country, class_name: 'Spree::Country'
|
||||
|
||||
validates :country, :name, presence: true
|
||||
validates :name, presence: true
|
||||
|
||||
def self.find_all_by_name_or_abbr(name_or_abbr)
|
||||
where('name = ? OR abbr = ?', name_or_abbr, name_or_abbr)
|
||||
|
||||
@@ -2,15 +2,12 @@
|
||||
|
||||
module Spree
|
||||
class StockItem < ApplicationRecord
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
acts_as_paranoid
|
||||
|
||||
belongs_to :stock_location, class_name: 'Spree::StockLocation', inverse_of: :stock_items
|
||||
belongs_to :variant, -> { with_deleted }, class_name: 'Spree::Variant'
|
||||
has_many :stock_movements
|
||||
has_many :stock_movements, dependent: :destroy
|
||||
|
||||
validates :stock_location, :variant, presence: true
|
||||
validates :variant_id, uniqueness: { scope: [:stock_location_id, :deleted_at] }
|
||||
validates :count_on_hand, numericality: { greater_than_or_equal_to: 0, unless: :backorderable? }
|
||||
|
||||
|
||||
@@ -2,22 +2,15 @@
|
||||
|
||||
module Spree
|
||||
class StockMovement < ApplicationRecord
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
belongs_to :stock_item, class_name: 'Spree::StockItem'
|
||||
belongs_to :originator, polymorphic: true
|
||||
belongs_to :originator, polymorphic: true, optional: true
|
||||
|
||||
after_create :update_stock_item_quantity
|
||||
|
||||
validates :stock_item, presence: true
|
||||
validates :quantity, presence: true
|
||||
|
||||
scope :recent, -> { order('created_at DESC') }
|
||||
|
||||
def readonly?
|
||||
!new_record?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_stock_item_quantity
|
||||
|
||||
@@ -14,17 +14,14 @@ end
|
||||
|
||||
module Spree
|
||||
class TaxRate < ApplicationRecord
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
acts_as_paranoid
|
||||
include CalculatedAdjustments
|
||||
|
||||
belongs_to :zone, class_name: "Spree::Zone", inverse_of: :tax_rates
|
||||
belongs_to :zone, class_name: "Spree::Zone", inverse_of: :tax_rates, optional: true
|
||||
belongs_to :tax_category, class_name: "Spree::TaxCategory", inverse_of: :tax_rates
|
||||
has_many :adjustments, as: :originator
|
||||
has_many :adjustments, as: :originator, dependent: nil
|
||||
|
||||
validates :amount, presence: true, numericality: true
|
||||
validates :tax_category, presence: true
|
||||
validates_with DefaultTaxZoneValidator
|
||||
|
||||
scope :by_zone, ->(zone) { where(zone_id: zone) }
|
||||
|
||||
@@ -32,8 +32,8 @@ module Spree
|
||||
|
||||
delegate :name, :name=, :description, :description=, :meta_keywords, to: :product
|
||||
|
||||
has_many :inventory_units, inverse_of: :variant
|
||||
has_many :line_items, inverse_of: :variant
|
||||
has_many :inventory_units, inverse_of: :variant, dependent: nil
|
||||
has_many :line_items, inverse_of: :variant, dependent: nil
|
||||
|
||||
has_many :stock_items, dependent: :destroy, inverse_of: :variant
|
||||
has_many :stock_locations, through: :stock_items
|
||||
@@ -49,11 +49,11 @@ module Spree
|
||||
has_many :prices,
|
||||
class_name: 'Spree::Price',
|
||||
dependent: :destroy
|
||||
delegate :display_price, :display_amount, :price, :price_changed?, :price=,
|
||||
delegate :display_price, :display_amount, :price, :price=,
|
||||
:currency, :currency=,
|
||||
to: :find_or_build_default_price
|
||||
|
||||
has_many :exchange_variants
|
||||
has_many :exchange_variants, dependent: nil
|
||||
has_many :exchanges, through: :exchange_variants
|
||||
has_many :variant_overrides, dependent: :destroy
|
||||
has_many :inventory_items, dependent: :destroy
|
||||
@@ -199,6 +199,11 @@ module Spree
|
||||
price_in(currency).try(:amount)
|
||||
end
|
||||
|
||||
def changed?
|
||||
# We consider the variant changed if associated price is changed (it is saved after_save)
|
||||
super || default_price.changed?
|
||||
end
|
||||
|
||||
# can_supply? is implemented in VariantStock
|
||||
def in_stock?(quantity = 1)
|
||||
can_supply?(quantity)
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubscriptionLineItem < ApplicationRecord
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
belongs_to :subscription, inverse_of: :subscription_line_items
|
||||
belongs_to :variant, -> { with_deleted }, class_name: 'Spree::Variant'
|
||||
|
||||
validates :subscription, presence: true
|
||||
validates :variant, presence: true
|
||||
validates :quantity, presence: true, numericality: { only_integer: true }
|
||||
|
||||
default_scope { order('id ASC') }
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TagRule < ApplicationRecord
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
belongs_to :enterprise
|
||||
|
||||
preference :customer_tags, :string, default: ""
|
||||
|
||||
validates :enterprise, presence: true
|
||||
|
||||
scope :for, ->(enterprise) { where(enterprise_id: enterprise) }
|
||||
scope :prioritised, -> { order('priority ASC') }
|
||||
|
||||
|
||||
@@ -6,15 +6,11 @@ class VariantOverride < ApplicationRecord
|
||||
extend Spree::LocalizedNumber
|
||||
include StockSettingsOverrideValidation
|
||||
|
||||
self.belongs_to_required_by_default = false
|
||||
|
||||
acts_as_taggable
|
||||
|
||||
belongs_to :hub, class_name: 'Enterprise'
|
||||
belongs_to :variant, class_name: 'Spree::Variant'
|
||||
|
||||
validates :hub, presence: true
|
||||
validates :variant, presence: true
|
||||
# Default stock can be nil, indicating stock should not be reset or zero, meaning reset to zero.
|
||||
# Need to ensure this can be set by the user.
|
||||
validates :default_stock, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class ConnectedAppReflex < ApplicationReflex
|
||||
def create
|
||||
authorize! :admin, enterprise
|
||||
|
||||
app = ConnectedApp.create!(enterprise_id: enterprise.id)
|
||||
|
||||
# Avoid race condition by sending before enqueuing job:
|
||||
broadcast_partial
|
||||
|
||||
ConnectAppJob.perform_later(
|
||||
app, current_user.spree_api_key,
|
||||
channel: SessionChannel.for_request(request),
|
||||
)
|
||||
morph :nothing
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize! :admin, enterprise
|
||||
|
||||
app = enterprise.connected_apps.first
|
||||
app.destroy
|
||||
|
||||
broadcast_partial
|
||||
|
||||
WebhookDeliveryJob.perform_later(
|
||||
app.data["destroy"],
|
||||
"disconnect-app",
|
||||
nil
|
||||
)
|
||||
morph :nothing
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def enterprise
|
||||
@enterprise ||= Enterprise.find(element.dataset.enterprise_id)
|
||||
end
|
||||
|
||||
def broadcast_partial
|
||||
selector = "#edit_enterprise_#{enterprise.id} #connected-app-discover-regen"
|
||||
html = ApplicationController.render(
|
||||
partial: "admin/enterprises/form/connected_apps",
|
||||
locals: { enterprise: },
|
||||
)
|
||||
|
||||
# Avoid race condition by sending before enqueuing job:
|
||||
cable_ready.morph(selector:, html:).broadcast
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -21,7 +21,8 @@ module Admin
|
||||
def ship
|
||||
@order.send_shipment_email = false unless params[:send_shipment_email]
|
||||
if @order.ship
|
||||
return set_param_for_controller if request.url.match?('edit')
|
||||
paths = %w[edit customer payments adjustments invoices return_authorizations].freeze
|
||||
return set_param_for_controller if Regexp.union(paths).match? request.url
|
||||
|
||||
morph dom_id(@order), render(partial: "spree/admin/orders/table_row",
|
||||
locals: { order: @order.reload, success: true })
|
||||
|
||||
@@ -21,46 +21,6 @@ class ProductsReflex < ApplicationReflex
|
||||
fetch_and_render_products_with_flash
|
||||
end
|
||||
|
||||
def delete_product
|
||||
id = current_id_from_element(element)
|
||||
product = product_finder(id).find_product
|
||||
authorize! :delete, product
|
||||
|
||||
if product.destroy
|
||||
flash[:success] = I18n.t('admin.products_v3.delete_product.success')
|
||||
else
|
||||
flash[:error] = I18n.t('admin.products_v3.delete_product.error')
|
||||
end
|
||||
|
||||
fetch_and_render_products_with_flash
|
||||
end
|
||||
|
||||
def delete_variant
|
||||
id = current_id_from_element(element)
|
||||
variant = Spree::Variant.active.find(id)
|
||||
authorize! :delete, variant
|
||||
|
||||
if VariantDeleter.new.delete(variant)
|
||||
flash[:success] = I18n.t('admin.products_v3.delete_variant.success')
|
||||
else
|
||||
flash[:error] = I18n.t('admin.products_v3.delete_variant.error')
|
||||
end
|
||||
|
||||
fetch_and_render_products_with_flash
|
||||
end
|
||||
|
||||
def edit_image
|
||||
id = current_id_from_element(element)
|
||||
product = product_finder(id).find_product
|
||||
image = product.image
|
||||
|
||||
image = Spree::Image.new(viewable: product) if product.image.blank?
|
||||
|
||||
morph "#modal-component",
|
||||
render(partial: "admin/products_v3/edit_image",
|
||||
locals: { product:, image:, return_url: url })
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init_filters_params
|
||||
@@ -89,8 +49,8 @@ class ProductsReflex < ApplicationReflex
|
||||
html: render(partial: "admin/products_v3/content",
|
||||
locals: { products: @products, pagy: @pagy, search_term: @search_term,
|
||||
producer_options: producers, producer_id: @producer_id,
|
||||
category_options: categories, category_id: @category_id,
|
||||
flashes: flash })
|
||||
category_options: categories, tax_category_options:,
|
||||
category_id: @category_id, flashes: flash })
|
||||
)
|
||||
|
||||
cable_ready.replace_state(
|
||||
@@ -125,6 +85,10 @@ class ProductsReflex < ApplicationReflex
|
||||
Spree::Taxon.order(:name).map { |c| [c.name, c.id] }
|
||||
end
|
||||
|
||||
def tax_category_options
|
||||
Spree::TaxCategory.order(:name).pluck(:name, :id)
|
||||
end
|
||||
|
||||
def fetch_products
|
||||
product_query = OpenFoodNetwork::Permissions.new(current_user)
|
||||
.editable_products.merge(product_scope).ransack(ransack_query).result(distinct: true)
|
||||
@@ -210,12 +174,4 @@ class ProductsReflex < ApplicationReflex
|
||||
params.permit(products: ::PermittedAttributes::Product.attributes)
|
||||
.to_h.with_indifferent_access
|
||||
end
|
||||
|
||||
def product_finder(id)
|
||||
ProductScopeQuery.new(current_user, { id: })
|
||||
end
|
||||
|
||||
def current_id_from_element(element)
|
||||
element.dataset.current_id
|
||||
end
|
||||
end
|
||||
|
||||
@@ -131,7 +131,7 @@ module Api
|
||||
producer_shop: "map_003-producer-shop.svg",
|
||||
producer: "map_001-producer-only.svg",
|
||||
}
|
||||
"/map_icons/" + (icons[enterprise.category] || "map_001-producer-only.svg")
|
||||
"/map_icons/#{icons[enterprise.category] || 'map_001-producer-only.svg'}"
|
||||
end
|
||||
|
||||
# Choose regular icon font for enterprises.
|
||||
|
||||
@@ -21,7 +21,7 @@ module Api
|
||||
producer_shop: "map_003-producer-shop.svg",
|
||||
producer: "map_001-producer-only.svg",
|
||||
}
|
||||
"/map_icons/" + (icons[enterprise.category] || "map_001-producer-only.svg")
|
||||
"/map_icons/#{icons[enterprise.category] || 'map_001-producer-only.svg'}"
|
||||
end
|
||||
|
||||
def icon_font
|
||||
|
||||
@@ -59,7 +59,7 @@ class EmbeddedPageService
|
||||
def set_logout_redirect
|
||||
return unless enterprise_slug
|
||||
|
||||
@session[:shopfront_redirect] = '/' + enterprise_slug + '/shop?embedded_shopfront=true'
|
||||
@session[:shopfront_redirect] = "/#{enterprise_slug}/shop?embedded_shopfront=true"
|
||||
end
|
||||
|
||||
def enterprise_slug
|
||||
|
||||
21
app/services/html_sanitizer.rb
Normal file
21
app/services/html_sanitizer.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Keeps only allowed HTML.
|
||||
#
|
||||
# We store some rich text as HTML in attributes of models like Enterprise.
|
||||
# We offer an editor which supports certain tags but you can't insert just any
|
||||
# HTML, which would be dangerous.
|
||||
class HtmlSanitizer
|
||||
# div is required by Trix editor
|
||||
ALLOWED_TAGS = %w[h1 h2 h3 h4 div p br b i u a strong em del pre blockquote ul ol li hr
|
||||
figure].freeze
|
||||
ALLOWED_ATTRIBUTES = %w[href target].freeze
|
||||
ALLOWED_TRIX_DATA_ATTRIBUTES = %w[data-trix-attachment].freeze
|
||||
|
||||
def self.sanitize(html)
|
||||
@sanitizer ||= Rails::HTML5::SafeListSanitizer.new
|
||||
@sanitizer.sanitize(
|
||||
html, tags: ALLOWED_TAGS, attributes: (ALLOWED_ATTRIBUTES + ALLOWED_TRIX_DATA_ATTRIBUTES)
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -68,23 +68,26 @@ class ProductsRenderer
|
||||
end
|
||||
|
||||
def products_order
|
||||
if (distributor.preferred_shopfront_product_sorting_method == "by_producer") &&
|
||||
distributor.preferred_shopfront_producer_order.present?
|
||||
distributor
|
||||
.preferred_shopfront_producer_order
|
||||
.split(",").map { |id| "spree_products.supplier_id=#{id} DESC" }
|
||||
.join(", ") + ", spree_products.name ASC, spree_products.id ASC"
|
||||
if distributor.preferred_shopfront_product_sorting_method == "by_producer" &&
|
||||
distributor.preferred_shopfront_producer_order.present?
|
||||
order_by_producer = distributor
|
||||
.preferred_shopfront_producer_order
|
||||
.split(",").map { |id| "spree_products.supplier_id=#{id} DESC" }
|
||||
.join(", ")
|
||||
"#{order_by_producer}, spree_products.name ASC, spree_products.id ASC"
|
||||
elsif distributor.preferred_shopfront_product_sorting_method == "by_category" &&
|
||||
distributor.preferred_shopfront_taxon_order.present?
|
||||
distributor
|
||||
.preferred_shopfront_taxon_order
|
||||
.split(",").map { |id| "first_variant.primary_taxon_id=#{id} DESC" }
|
||||
.join(", ") + ", spree_products.name ASC, spree_products.id ASC"
|
||||
distributor.preferred_shopfront_taxon_order.present?
|
||||
order_by_category = distributor
|
||||
.preferred_shopfront_taxon_order
|
||||
.split(",").map { |id| "first_variant.primary_taxon_id=#{id} DESC" }
|
||||
.join(", ")
|
||||
"#{order_by_category}, spree_products.name ASC, spree_products.id ASC"
|
||||
else
|
||||
"spree_products.name ASC, spree_products.id"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def variants_for_shop
|
||||
@variants_for_shop ||= begin
|
||||
scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor)
|
||||
|
||||
@@ -67,11 +67,12 @@ module Sets
|
||||
|
||||
product.assign_attributes(product_related_attrs)
|
||||
|
||||
return true unless product.changed?
|
||||
|
||||
validate_presence_of_unit_value_in_product(product)
|
||||
|
||||
changed = product.changed?
|
||||
success = product.errors.empty? && product.save
|
||||
count_result(success && changed)
|
||||
count_result(success)
|
||||
success
|
||||
end
|
||||
|
||||
@@ -104,7 +105,8 @@ module Sets
|
||||
def create_or_update_variant(product, variant_attributes)
|
||||
variant = find_model(product.variants, variant_attributes[:id])
|
||||
if variant.present?
|
||||
variant.update(variant_attributes.except(:id))
|
||||
variant.assign_attributes(variant_attributes.except(:id))
|
||||
variant.save if variant.changed?
|
||||
else
|
||||
variant = create_variant(product, variant_attributes)
|
||||
end
|
||||
|
||||
23
app/views/admin/column_preferences/_form.html.haml
Normal file
23
app/views/admin/column_preferences/_form.html.haml
Normal file
@@ -0,0 +1,23 @@
|
||||
= form_with url: bulk_update_admin_column_preferences_path, method: :put,
|
||||
id: :bulk_admin_column_preferences_form, class: "column-preferences",
|
||||
html: { 'data-controller': "column-preferences" } do |f|
|
||||
= hidden_field_tag :action_name, action
|
||||
|
||||
/ DC: this makes my Chrome DevTools crash when inspecting the <details> element. If problem continues, we need to use a different method.
|
||||
%details.ofn-drop-down.ofn-drop-down-v2.right{ 'data-controller': "dropdown" }
|
||||
%summary.ofn-drop-down-label
|
||||
= t('admin.columns')
|
||||
%span.icon-caret
|
||||
|
||||
.menu
|
||||
.menu_items
|
||||
- ColumnPreference.for(spree_current_user, action).each_with_index do |column_preference, index|
|
||||
= f.fields_for("column_preferences", column_preference, index:) do |cp_form|
|
||||
= cp_form.hidden_field :id
|
||||
= cp_form.hidden_field :column_name
|
||||
%label.menu_item
|
||||
= cp_form.check_box :visible, 'data-column-name': column_preference.column_name
|
||||
%span= t("admin.products_page.columns." + column_preference.column_name)
|
||||
|
||||
.actions
|
||||
= f.submit t('admin.column_save_as_default'), class: "secondary fullwidth"
|
||||
@@ -0,0 +1,3 @@
|
||||
= turbo_stream.replace "bulk_admin_column_preferences_form" do
|
||||
= render partial: "admin/shared/flashes", locals: { flashes: flash } if defined? flash
|
||||
= render partial: 'form', locals: { action: }
|
||||
@@ -9,6 +9,11 @@
|
||||
%fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }}
|
||||
%legend= t(".#{ item[:name] }.legend")
|
||||
|
||||
- when 'connected_apps'
|
||||
-# Don't render this item here in the main form.
|
||||
-# The panel contains its own form and we can't nest forms in forms.
|
||||
-# Otherwise we add multiple authenticity tokens and Rails denies updates.
|
||||
|
||||
- else
|
||||
%fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }}
|
||||
%legend= t(".#{ item[:form_name] || item[:name] }.legend")
|
||||
|
||||
@@ -11,11 +11,4 @@
|
||||
%input.red{ type: "button", value: t(:update), "ng-click": "submit()", "ng-disabled": "!enterprise_form.$dirty" }
|
||||
%input{ type: "button", "ng-value": "enterprise_form.$dirty ? '#{t(:cancel)}' : '#{t(:close)}'", "ng-click": "cancel('#{main_app.admin_enterprises_path}')" }
|
||||
|
||||
.row{ data: {
|
||||
controller: "tabs-and-panels", "tabs-and-panels-class-name-value": "selected" }}
|
||||
.sixteen.columns.alpha
|
||||
.four.columns.alpha
|
||||
= render 'admin/shared/side_menu'
|
||||
.one.column
|
||||
.eleven.columns.omega.fullwidth_inputs
|
||||
= render 'form', f: f
|
||||
= render 'form', f: f
|
||||
|
||||
@@ -13,4 +13,14 @@
|
||||
|
||||
= render 'admin/enterprises/form_data'
|
||||
|
||||
= render 'admin/enterprises/ng_form', action: 'edit'
|
||||
.row{ data: { controller: "tabs-and-panels", "tabs-and-panels-class-name-value": "selected" }}
|
||||
.sixteen.columns.alpha
|
||||
.four.columns.alpha
|
||||
= render 'admin/shared/side_menu'
|
||||
.one.column
|
||||
.eleven.columns.omega.fullwidth_inputs
|
||||
= render 'admin/enterprises/ng_form', action: 'edit'
|
||||
|
||||
%fieldset.alpha.no-border-bottom{ id: "connected_apps_panel", data: { "tabs-and-panels-target": "panel" }}
|
||||
%legend= t("admin.enterprises.form.connected_apps.legend")
|
||||
= render "admin/enterprises/form/connected_apps", enterprise: @enterprise
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
- enterprise ||= f.object
|
||||
#connected-app-discover-regen
|
||||
%div{ id: "connected-app-discover-regen", class: "enterprise_#{enterprise.id}" }
|
||||
.connected-app__head
|
||||
%div
|
||||
%h3= t ".title"
|
||||
%p= t ".tagline"
|
||||
%div
|
||||
- if enterprise.connected_apps.empty?
|
||||
%button{ data: {reflex: "click->Admin::ConnectedApp#create", enterprise_id: enterprise.id} }
|
||||
= t ".enable"
|
||||
= button_to t(".enable"), admin_enterprise_connected_apps_path(enterprise.id), method: :post, disabled: !managed_by_user?(enterprise)
|
||||
-# This is only seen by super-admins:
|
||||
%em= t(".need_to_be_manager") unless managed_by_user?(enterprise)
|
||||
- elsif enterprise.connected_apps.connecting.present?
|
||||
%button{ disabled: true }
|
||||
%i.spinner.fa.fa-spin.fa-circle-o-notch
|
||||
|
||||
= t ".loading"
|
||||
- else
|
||||
%button{ data: {reflex: "click->Admin::ConnectedApp#destroy", enterprise_id: enterprise.id} }
|
||||
= t ".disable"
|
||||
= button_to t(".disable"), admin_enterprise_connected_app_path(0, enterprise_id: enterprise.id), method: :delete
|
||||
|
||||
.connected-app__connection
|
||||
- if enterprise.connected_apps.ready.present?
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
.container.results
|
||||
.sixteen.columns
|
||||
= render partial: 'sort', locals: { pagy: pagy, search_term: search_term, producer_id: producer_id, category_id: category_id }
|
||||
= render partial: 'table', locals: { products: products }
|
||||
= render partial: 'table', locals: { products:, producer_options:, category_options:, tax_category_options: }
|
||||
- if pagy.present? && pagy.pages > 1
|
||||
= render partial: 'admin/shared/stimulus_pagination', locals: { pagy: pagy }
|
||||
- else
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
cancel_button_text: t("#{base_translation_key}.cancellation_text"),
|
||||
confirm_button_class: :red,
|
||||
actions_alignment_class: 'justify-end',
|
||||
confirm_reflexes: "click->products#delete_#{object_type}",
|
||||
confirm_actions: "click->modal#close",
|
||||
controller: "products",
|
||||
confirm_actions: "click->products#delete_#{object_type} click->modal#close",
|
||||
)
|
||||
= render delete_modal do
|
||||
%h2.margin-bottom-20.black-text
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
= render ModalComponent.new id: "#modal_edit_product_image_#{image.id}", instant: true, close_button: false, modal_class: :fit do
|
||||
%h2= t(".title")
|
||||
|
||||
-# Display image in the same way it appears in the shopfront popup
|
||||
%p= image_tag image.persisted? ? image.url(:large) : Spree::Image.default_image_url(:large), width: 433, height: 433
|
||||
|
||||
-# Submit to controller, because StimulusReflex doesn't support file uploads
|
||||
= form_for [:admin, product, image],
|
||||
html: { multipart: true }, data: { controller: "form" } do |f|
|
||||
%input{ type: :hidden, name: :return_url, value: return_url}
|
||||
= f.hidden_field :viewable_id, value: product.id
|
||||
|
||||
.modal-actions.justify-end
|
||||
%input{ class: "secondary relaxed", type: 'button', value: t('.close'), "data-action": "click->modal#close" }
|
||||
-# label.button provides a handy shortcut to open the file selector on click. Unfortunately this trick isn't keyboard accessible though..
|
||||
= f.label :attachment, t(".upload"), class: "button primary relaxed icon-upload-alt"
|
||||
= f.file_field :attachment, accept: "image/*", style: "display: none", "data-action": "change->form#submit"
|
||||
@@ -1,6 +1,7 @@
|
||||
= form_with url: admin_products_path, id: "filters", method: :get, data: { "search-target": "form", 'turbo-frame': "_self" } do
|
||||
= hidden_field_tag :page, nil, class: "page"
|
||||
= hidden_field_tag :per_page, nil, class: "per-page"
|
||||
= hidden_field_tag '[q][s]', params.dig(:q, :s) || 'name asc', class: 'sort', 'data-default': 'name asc'
|
||||
|
||||
.query
|
||||
.search-input
|
||||
|
||||
3
app/views/admin/products_v3/_product_image.html.haml
Normal file
3
app/views/admin/products_v3/_product_image.html.haml
Normal file
@@ -0,0 +1,3 @@
|
||||
%a.image-field{ href: product_image_form_path(product), 'data-turbo-stream': true }
|
||||
= image_tag product.image&.url(:mini) || Spree::Image.default_image_url(:mini), width: 40, height: 40
|
||||
.button.secondary.mini= t('admin.products_page.image.edit')
|
||||
@@ -1,15 +1,13 @@
|
||||
%td.with-image
|
||||
%a.image-field{ href: admin_product_images_path(product), data: { controller: "modal", reflex: "click->products#edit_image", "current-id": product.id} }
|
||||
= image_tag product.image&.url(:mini) || Spree::Image.default_image_url(:mini), width: 40, height: 40
|
||||
.button.secondary.mini= t('admin.products_page.image.edit')
|
||||
%td.field.align-left.header.naked_inputs
|
||||
%td.col-image.with-image{ id: "image-#{product.id}" }
|
||||
= render partial: "product_image", locals: { product: }
|
||||
%td.col-name.field.align-left.header.naked_inputs
|
||||
= f.hidden_field :id
|
||||
= f.text_field :name, 'aria-label': t('admin.products_page.columns.name')
|
||||
= error_message_on product, :name
|
||||
%td.field.naked_inputs
|
||||
%td.col-sku.field.naked_inputs
|
||||
= f.text_field :sku, 'aria-label': t('admin.products_page.columns.sku')
|
||||
= error_message_on product, :sku
|
||||
%td.multi-field.naked_inputs{ 'data-controller': 'toggle-control', 'data-toggle-control-match-value': 'items' }
|
||||
%td.col-unit_scale.field.naked_inputs{ 'data-controller': 'toggle-control', 'data-toggle-control-match-value': 'items' }
|
||||
= f.hidden_field :variant_unit
|
||||
= f.hidden_field :variant_unit_scale
|
||||
= f.select :variant_unit_with_scale,
|
||||
@@ -21,18 +19,23 @@
|
||||
.field
|
||||
= f.text_field :variant_unit_name, 'aria-label': t('items'), 'data-toggle-control-target': 'control', style: (product.variant_unit == "items" ? "" : "display: none")
|
||||
= error_message_on product, :variant_unit_name, 'data-toggle-control-target': 'control'
|
||||
%td.align-right
|
||||
%td.col-unit.align-right
|
||||
-# empty
|
||||
%td.align-right
|
||||
%td.col-price.align-right
|
||||
-# empty
|
||||
%td.align-right
|
||||
%td.col-on_hand.align-right
|
||||
-# empty
|
||||
%td.align-left
|
||||
.content= product.supplier&.name
|
||||
%td.align-left
|
||||
%td.col-producer.naked_inputs
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :supplier_id,
|
||||
aria_label: t('.producer_field_name'),
|
||||
options: producer_options,
|
||||
selected_option: product.supplier_id,
|
||||
placeholder_value: t('admin.products_v3.filters.search_for_producers')))
|
||||
%td.col-category.align-left
|
||||
-# empty
|
||||
%td.align-left
|
||||
%td.align-left
|
||||
%td.col-tax_category.align-left
|
||||
%td.col-inherits_properties.align-left
|
||||
.content= product.inherits_properties ? 'YES' : 'NO' #TODO: consider using https://github.com/RST-J/human_attribute_values, else use I18n.t (also below)
|
||||
%td.align-right
|
||||
= render(VerticalEllipsisMenu::Component.new) do
|
||||
@@ -40,5 +43,5 @@
|
||||
= link_to t('admin.products_page.actions.clone'), clone_admin_product_path(product), 'data-turbo': false
|
||||
%a{ "data-controller": "modal-link", "data-action": "click->modal-link#setModalDataSetOnConfirm click->modal-link#open",
|
||||
"data-modal-link-target-value": "product-delete-modal", "class": "delete",
|
||||
"data-modal-link-modal-dataset-value": {'data-current-id': product.id}.to_json }
|
||||
"data-modal-link-modal-dataset-value": {'data-delete-path': admin_product_destroy_path(product)}.to_json }
|
||||
= t('admin.products_page.actions.delete')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#sort
|
||||
%div
|
||||
%div.pagination-description
|
||||
- if pagy.present?
|
||||
= t(".pagination.total_html", total: pagy.count, from: pagy.from, to: pagy.to)
|
||||
|
||||
@@ -13,3 +13,6 @@
|
||||
options_for_select([15, 25, 50, 100].collect{|i| [t('.pagination.per_page.per_page', num: i), i]}, pagy&.items),
|
||||
class: "no-input per-page",
|
||||
data: { controller: "tom-select search", action: "change->search#changePerPage", "tom-select-options-value": '{ "plugins": [] }'}
|
||||
|
||||
/ Columns dropdown
|
||||
= render partial: "admin/column_preferences/form", locals: { action: "products_v3_index" }
|
||||
|
||||
@@ -13,20 +13,21 @@
|
||||
= hidden_field_tag :producer_id, @producer_id
|
||||
= hidden_field_tag :category_id, @category_id
|
||||
|
||||
%table.products
|
||||
%table.products{ 'data-column-preferences-target': "table" }
|
||||
%colgroup
|
||||
%col{ width:"56" }= # Img (size + padding)
|
||||
%col= # (grow to fill) Name
|
||||
%col{ width:"5%"}
|
||||
%col{ width:"8%"}
|
||||
%col{ width:"8%"}
|
||||
%col{ width:"5%"}
|
||||
%col{ width:"10%"}
|
||||
%col{ width:"15%"}= # Producer
|
||||
%col{ width:"8%"}
|
||||
%col{ width:"8%"}
|
||||
%col{ width:"8%"}
|
||||
%col{ width:"8%"}= # Actions
|
||||
-# The `min-width` property works in Chrome but not Firefox so is considered progressive enhancement.
|
||||
%col.col-image{ width:"56px" }= # (image size + padding)
|
||||
%col.col-name{ style:"min-width: 6em" }= # (grow to fill)
|
||||
%col.col-sku{ width:"8%", style:"min-width: 6em" }
|
||||
%col.col-unit_scale{ width:"8%" }
|
||||
%col.col-unit{ width:"8%" }
|
||||
%col.col-price{ width:"5%", style:"min-width: 5em" }
|
||||
%col.col-on_hand{ width:"10%"}
|
||||
%col.col-producer{ style:"min-width: 6em" }= # (grow to fill)
|
||||
%col.col-category{ width:"8%" }
|
||||
%col.col-tax_category{ width:"8%" }
|
||||
%col.col-inherits_properties{ width:"5%" }
|
||||
%col{ width:"5%", style:"min-width: 3em"}= # Actions
|
||||
%thead
|
||||
%tr
|
||||
%td.form-actions-wrapper{ colspan: 12 }
|
||||
@@ -48,35 +49,36 @@
|
||||
= t('.reset')
|
||||
= form.submit t('.save'), class: "medium"
|
||||
%tr
|
||||
%th.align-left= # image
|
||||
%th.align-left.with-input= t('admin.products_page.columns.name')
|
||||
%th.align-left.with-input= t('admin.products_page.columns.sku')
|
||||
%th.align-left.with-input= t('admin.products_page.columns.unit_scale')
|
||||
%th.align-left.with-input= t('admin.products_page.columns.unit')
|
||||
%th.align-left.with-input= t('admin.products_page.columns.price')
|
||||
%th.align-left.with-input= t('admin.products_page.columns.on_hand')
|
||||
%th.align-left= t('admin.products_page.columns.producer')
|
||||
%th.align-left= t('admin.products_page.columns.category')
|
||||
%th.align-left= t('admin.products_page.columns.tax_category')
|
||||
%th.align-left= t('admin.products_page.columns.inherits_properties')
|
||||
%th.col-image.align-left= # image
|
||||
= render partial: 'spree/admin/shared/stimulus_sortable_header',
|
||||
locals: { column: :name, sorted: params.dig(:q, :s), default: 'name asc' }
|
||||
%th.align-left.col-sku.with-input= t('admin.products_page.columns.sku')
|
||||
%th.align-left.col-unit_scale.with-input= t('admin.products_page.columns.unit_scale')
|
||||
%th.align-left.col-unit.with-input= t('admin.products_page.columns.unit')
|
||||
%th.align-left.col-price.with-input= t('admin.products_page.columns.price')
|
||||
%th.align-left.col-on_hand.with-input= t('admin.products_page.columns.on_hand')
|
||||
%th.align-left.col-producer= t('admin.products_page.columns.producer')
|
||||
%th.align-left.col-category= t('admin.products_page.columns.category')
|
||||
%th.align-left.col-tax_category= t('admin.products_page.columns.tax_category')
|
||||
%th.align-left.col-inherits_properties= t('admin.products_page.columns.inherits_properties')
|
||||
%th.align-right= t('admin.products_page.columns.actions')
|
||||
- products.each_with_index do |product, product_index|
|
||||
= form.fields_for("products", product, index: product_index) do |product_form|
|
||||
%tbody.relaxed{ data: { 'record-id': product_form.object.id,
|
||||
%tbody.relaxed{ id: dom_id(product), data: { 'record-id': product_form.object.id,
|
||||
controller: "nested-form product",
|
||||
action: 'rails-nested-form:add->bulk-form#registerElements' } }
|
||||
action: 'rails-nested-form:add->bulk-form#registerElements rails-nested-form:remove->bulk-form#toggleFormChanged' } }
|
||||
%tr
|
||||
= render partial: 'product_row', locals: { product:, f: product_form }
|
||||
= render partial: 'product_row', locals: { f: product_form, product:, producer_options: }
|
||||
|
||||
- product.variants.each_with_index do |variant, variant_index|
|
||||
= form.fields_for("products][#{product_index}][variants_attributes][", variant, index: variant_index) do |variant_form|
|
||||
%tr.condensed{ 'data-controller': "variant" }
|
||||
= render partial: 'variant_row', locals: { variant:, f: variant_form }
|
||||
%tr.condensed{ id: dom_id(variant), 'data-controller': "variant", 'class': "nested-form-wrapper", 'data-new-record': variant.new_record? ? "true" : false }
|
||||
= render partial: 'variant_row', locals: { variant:, f: variant_form, category_options:, tax_category_options: }
|
||||
|
||||
= form.fields_for("products][#{product_index}][variants_attributes][NEW_RECORD", product.variants.build) do |new_variant_form|
|
||||
%template{ 'data-nested-form-target': "template" }
|
||||
%tr.condensed{ 'data-controller': "variant" }
|
||||
= render partial: 'variant_row', locals: { variant: new_variant_form.object, f: new_variant_form }
|
||||
%tr.condensed{ 'data-controller': "variant", 'class': "nested-form-wrapper", 'data-new-record': "true" }
|
||||
= render partial: 'variant_row', locals: { variant: new_variant_form.object, f: new_variant_form, category_options:, tax_category_options: }
|
||||
|
||||
%tr{ 'data-nested-form-target': "target" }
|
||||
%tr.condensed
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
%td
|
||||
%td.col-image
|
||||
-# empty
|
||||
%td.field.naked_inputs
|
||||
%td.col-name.field.naked_inputs
|
||||
= f.hidden_field :id
|
||||
= f.text_field :display_name, 'aria-label': t('admin.products_page.columns.name'), placeholder: variant.product.name
|
||||
= error_message_on variant, :display_name
|
||||
%td.field.naked_inputs
|
||||
%td.col-sku.field.naked_inputs
|
||||
= f.text_field :sku, 'aria-label': t('admin.products_page.columns.sku')
|
||||
= error_message_on variant, :sku
|
||||
%td
|
||||
%td.col-unit_scale
|
||||
-# empty
|
||||
%td.field.popout{'data-controller': "popout", 'data-popout-update-display-value': "false"}
|
||||
%td.col-unit.field.popout{'data-controller': "popout", 'data-popout-update-display-value': "false"}
|
||||
= f.button :unit_to_display, class: "popout__button", 'aria-label': t('admin.products_page.columns.unit'), 'data-popout-target': "button" do
|
||||
= variant.unit_to_display # Show the generated summary of unit values
|
||||
%div.popout__container{ style: 'display: none;', 'data-controller': 'toggle-control', 'data-popout-target': "dialog" }
|
||||
@@ -25,10 +25,10 @@
|
||||
= f.label :display_as, t('admin.products_page.columns.display_as')
|
||||
= f.text_field :display_as, placeholder: VariantUnits::OptionValueNamer.new(variant).name
|
||||
= error_message_on variant, :unit_value
|
||||
%td.field.naked_inputs
|
||||
%td.col-price.field.naked_inputs
|
||||
= f.text_field :price, 'aria-label': t('admin.products_page.columns.price'), value: number_to_currency(variant.price, unit: '')&.strip # TODO: add a spec to prove that this formatting is necessary. If so, it should be in a shared form helper for currency inputs
|
||||
= error_message_on variant, :price
|
||||
%td.field.popout{'data-controller': "popout"}
|
||||
%td.col-on_hand.field.popout{'data-controller': "popout"}
|
||||
%button.popout__button{'data-popout-target': "button", 'aria-label': t('admin.products_page.columns.on_hand')}
|
||||
= variant.on_demand ? t(:on_demand) : variant.on_hand
|
||||
%div.popout__container{ style: 'display: none;', 'data-controller': 'toggle-control', 'data-popout-target': "dialog" }
|
||||
@@ -39,20 +39,35 @@
|
||||
= f.label :on_demand do
|
||||
= f.check_box :on_demand, 'data-action': 'change->toggle-control#disableIfPresent change->popout#closeIfChecked'
|
||||
= t(:on_demand)
|
||||
%td.align-left
|
||||
.content= variant.product.supplier&.name # same as product
|
||||
%td.align-left
|
||||
.content= variant.primary_taxon&.name
|
||||
%td.align-left
|
||||
.content= (variant.tax_category_id ? variant.tax_category&.name : t('.none_tax_category')) # TODO: convert to dropdown
|
||||
%td.align-left
|
||||
%td.col-producer.align-left
|
||||
-# empty producer name
|
||||
%td.col-category.field.naked_inputs
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :primary_taxon_id,
|
||||
options: category_options,
|
||||
selected_option: variant.primary_taxon_id,
|
||||
aria_label: t('.category_field_name'),
|
||||
placeholder_value: t('admin.products_v3.filters.search_for_categories')))
|
||||
%td.col-tax_category.field.naked_inputs
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :tax_category_id,
|
||||
options: tax_category_options,
|
||||
selected_option: variant.tax_category_id,
|
||||
include_blank: t('.none_tax_category'),
|
||||
aria_label: t('.tax_category_field_name'),
|
||||
placeholder_value: t('.search_for_tax_categories')))
|
||||
= error_message_on variant, :tax_category
|
||||
%td.col-inherits_properties.align-left
|
||||
-# empty
|
||||
%td.align-right
|
||||
- if variant.persisted?
|
||||
= render(VerticalEllipsisMenu::Component.new) do
|
||||
= render(VerticalEllipsisMenu::Component.new) do
|
||||
- if variant.persisted?
|
||||
= link_to t('admin.products_page.actions.edit'), edit_admin_product_variant_path(variant.product, variant)
|
||||
- if variant.product.variants.size > 1
|
||||
%a{ "data-controller": "modal-link", "data-action": "click->modal-link#setModalDataSetOnConfirm click->modal-link#open",
|
||||
"data-modal-link-target-value": "variant-delete-modal", "class": "delete",
|
||||
"data-modal-link-modal-dataset-value": {'data-current-id': variant.id}.to_json }
|
||||
"data-modal-link-modal-dataset-value": {'data-delete-path': admin_destroy_variant_path(variant)}.to_json }
|
||||
= t('admin.products_page.actions.delete')
|
||||
- else
|
||||
%a{ 'data-action': "nested-form#remove", class: 'delete' }
|
||||
= t('admin.products_page.actions.remove')
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
- # @record can either be Product or Variant
|
||||
- unless flash[:error]
|
||||
= turbo_stream.remove(dom_id(@record))
|
||||
= turbo_stream.append "flashes" do
|
||||
= render(partial: 'admin/shared/flashes', locals: { flashes: flash })
|
||||
@@ -15,8 +15,8 @@
|
||||
= render partial: "content", locals: { products: @products, pagy: @pagy, search_term: @search_term,
|
||||
producer_options: producers, producer_id: @producer_id,
|
||||
category_options: categories, category_id: @category_id,
|
||||
flashes: flash }
|
||||
tax_category_options:, flashes: flash }
|
||||
- %w[product variant].each do |object_type|
|
||||
= render partial: 'delete_modal', locals: { object_type: }
|
||||
#modal-component
|
||||
|
||||
#edit_image_modal
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
.row.date-range-filter
|
||||
.alpha.two.columns= label_tag nil, t(:date_range)
|
||||
.omega.fourteen.columns
|
||||
= f.text_field "#{field}_gt", :class => 'datetimepicker datepicker-from', :placeholder => t(:start), data: { controller: "flatpickr", "flatpickr-enable-time-value": true, "flatpickr-default-date-value": "startOfDay" }, value: start_date
|
||||
%span.range-divider
|
||||
%i.icon-arrow-right
|
||||
= f.text_field "#{field}_lt", :class => 'datetimepicker datepicker-to', :placeholder => t(:stop), data: { controller: "flatpickr", "flatpickr-enable-time-value": true, "flatpickr-default-date-value": "endOfDay" }, value: end_date
|
||||
.field-block.omega.four.columns
|
||||
.date-range-fields{ data: { controller: "flatpickr", "flatpickr-mode-value": "range", "flatpickr-enable-time-value": true , "flatpickr-default-hour": 0 } }
|
||||
= text_field_tag nil, nil, class: "datepicker fullwidth", data: { "flatpickr-target": "instance", action: "flatpickr_clear@window->flatpickr#clear" }
|
||||
= text_field_tag "q[#{field}_gt]", nil, data: { "flatpickr-target": "start" }, style: "display: none", value: start_date
|
||||
= text_field_tag "q[#{field}_lt]", nil, data: { "flatpickr-target": "end" }, style: "display: none", value: end_date
|
||||
|
||||
@@ -95,12 +95,7 @@
|
||||
%div.checkout-input{"data-shippingmethod-target": "shippingMethodDescription", "data-shippingmethodid": shipping_method.id , style: "display: #{ship_method_is_selected ? 'block' : 'none'}" }
|
||||
#distributor_address.panel
|
||||
- if shipping_method.description.present?
|
||||
%span #{shipping_method.description}
|
||||
%br/
|
||||
%br/
|
||||
- if @order.order_cycle.pickup_time_for(@order.distributor)
|
||||
= t :checkout_ready_for
|
||||
= @order.order_cycle.pickup_time_for(@order.distributor)
|
||||
= simple_format(html_escape(shipping_method.description))
|
||||
|
||||
= f.error_message_on :shipping_method, standalone: true
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user