Merge branch 'master' into 12295-translation-fixes-on-return-authorizaton

This commit is contained in:
Arun
2024-03-22 18:16:57 +05:30
committed by GitHub
981 changed files with 52602 additions and 8522 deletions

View File

@@ -1,11 +1,17 @@
# ENV vars for the test environment
# Override locally with `.env.test.local`
OFN_REDIS_JOBS_URL="redis://localhost:6379/2"
SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
STRIPE_SECRET_TEST_API_KEY="bogus_key"
STRIPE_INSTANCE_SECRET_KEY="bogus_key"
STRIPE_CUSTOMER="bogus_customer"
STRIPE_ACCOUNT="bogus_account"
STRIPE_CLIENT_ID="bogus_client_id"
STRIPE_PUBLIC_TEST_API_KEY="bogus_stripe_publishable_key"
SITE_URL="test.host"
OPENID_APP_ID="test-provider"
OPENID_APP_SECRET="12345"
OPENID_REFRESH_TOKEN="dummy-refresh-token"

View File

@@ -39,11 +39,11 @@ assignees: ''
</details>
- [ ] Notify [#instance-managers]:
> @instance_managers The new release has been deployed.
- [ ] Nudge next release manager
- [ ] [Create issue] for next release and confirm with next release manager in [#core-devs].
The full process is described at https://github.com/openfoodfoundation/openfoodnetwork/wiki/Releasing.
[Ready To Go]: #zenhub
[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
[releases]: https://github.com/openfoodfoundation/openfoodnetwork/releases
@@ -51,3 +51,5 @@ The full process is described at https://github.com/openfoodfoundation/openfoodn
[#testing]: https://openfoodnetwork.slack.com/app_redirect?channel=C02TZ6X00
[Deploy to Staging]: https://github.com/openfoodfoundation/openfoodnetwork/actions/workflows/stage.yml
[#global-community]: https://app.slack.com/client/T02G54U79/C59ADD8F2
[Create issue]: https://github.com/openfoodfoundation/openfoodnetwork/issues/new?assignees=&labels=&projects=&template=release.md&title=Release
[#core-devs]: https://openfoodnetwork.slack.com/archives/GK2T38QPJ

View File

@@ -22,7 +22,7 @@
Changelog Category (reviewers may add a label for the release notes):
- [x] User facing changes
- [ ] User facing changes
- [ ] API changes (V0, V1, DFC or Webhook)
- [ ] Technical changes only
- [ ] Feature toggled

11
.github/release.yml vendored
View File

@@ -1,8 +1,9 @@
changelog:
# Categorise according to what an instance manager needs to know
categories:
# Posted in advance for #instance-managers
- title: "User-facing changes 👀"
# Add the right label if anything appears in here.
# Then re-generate the release notes.
- title: "❓❓❓ Uncategorised ❓❓❓"
labels:
- '*'
exclude:
@@ -11,6 +12,12 @@ changelog:
- dependencies
- feature toggled
- technical changes only
- user facing changes
# Posted in advance for #instance-managers
- title: "User-facing changes 👀"
labels:
- user facing changes
- title: "API changes ⚠️"
labels:

View File

@@ -18,7 +18,7 @@ jobs:
uses: reviewdog/action-rubocop@v2
with:
rubocop_version: gemfile
rubocop_extensions: rubocop-rails:gemfile
rubocop_extensions: rubocop-rails:gemfile rubocop-rspec:gemfile
reporter: github-pr-check
level: error
fail_on_error: true

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn pretty-quick --check --staged

View File

@@ -4,7 +4,9 @@
#
# The configuration is split into three files. Look into those files for more details.
#
require: rubocop-rails
require:
- rubocop-rails
- rubocop-rspec
inherit_from:
# The automatically generated todo list to ignore all current violations.
@@ -13,9 +15,10 @@ inherit_from:
# The relaxed style rules as a common starting point which we can refine.
- .rubocop_relaxed_styleguide.yml
# Our Open Food Network style guide. If you want to see all violations,
# Our Open Food Network style guides. If you want to see all violations,
# then use only that configuration:
#
# bundle exec rubocop -c .rubocop_styleguide.yml
#
- .rubocop_styleguide.yml
- .rubocop_rspec_styleguide.yml

View File

@@ -0,0 +1,24 @@
# OFN styleguide for rubocop-rspec
# Because there are so many, we will disable by default, and enable rules as needed.
Capybara:
Enabled: false
RSpec:
Enabled: false
FactoryBot:
Enabled: false
# Enabled rules
Capybara/NegationMatcher:
Enabled: true
EnforcedStyle: not_to
RSpec/ExpectChange:
Enabled: true
EnforcedStyle: block
RSpec/NotToNot:
Enabled: true

View File

@@ -5,22 +5,55 @@ AllCops:
NewCops: enable
SuggestExtensions: false
Exclude:
- 'bin/**/*'
- 'db/**/*'
- 'config/**/*'
- 'script/**/*'
- 'vendor/**/*'
- 'node_modules/**/*'
- bin/**/*
- db/**/*
- config/**/*
- script/**/*
- vendor/**/*
- node_modules/**/*
# Excluding: inadequate Naming/FileName rule rejects GemFile name with camelcase
- 'engines/web/Gemfile'
- engines/web/Gemfile
## OFN SETTINGS
#
# Cop settings that have been agreed upon by the OFN community
Bundler/DuplicatedGem:
Enabled: false
Layout/LineLength:
Enabled: true
Max: 100
Layout/MultilineMethodCallIndentation:
Enabled: true
EnforcedStyle: indented
# Don't think this is a big issue, mostly picking up RPSEC scope definitions
# with lamdas and RSpec '.to change{}' blocks
Lint/AmbiguousBlockAssociation:
Enabled: false
Lint/MissingSuper:
Exclude:
- app/components/**/*
Lint/RaiseException:
Enabled: true
Lint/StructNewOverride:
Enabled: true
# Heaps of offences (> 100) in specs, mostly in situations where two or more
# instances of a model are required, but only one is referenced. Difficult to
# fix without making the spec look messy or rewriting it.
# Should definitely fix at some point.
Lint/UselessAssignment:
Exclude:
- spec/**/*
Metrics:
Enabled: true
Metrics/AbcSize:
Max: 30 # default 17
Metrics/BlockLength:
AllowedMethods: [
"class_eval",
@@ -46,13 +79,31 @@ Metrics/BlockLength:
"xdescribe",
]
Metrics/MethodLength:
Enabled: true
Max: 25 # default 10
Metrics/ParameterLists:
CountKeywordArgs: false
Metrics/PerceivedComplexity:
Enabled: true
Max: 14 # default 8
Naming/PredicateName:
Enabled: false
Naming/VariableNumber:
AllowedIdentifiers:
- street_address_1
- street_address_2
AllowedPatterns:
- _v[\d]+
Rails/ApplicationRecord:
Exclude:
# Migrations should not contain application code:
- "db/migrate/*.rb"
- db/migrate/*.rb
# Allow many-to-many associations without explicit model.
# - It avoids the additional code of a model class.
@@ -61,23 +112,23 @@ Rails/ApplicationRecord:
Rails/HasAndBelongsToMany:
Enabled: false
Rails/SkipsModelValidations:
AllowedMethods:
- "touch"
- "touch_all"
- "update_all"
- "update_attribute"
- "update_column"
- "update_columns"
Rails/OutputSafety:
Exclude:
- 'spec/**/*'
- spec/**/*
Rails/SkipsModelValidations:
AllowedMethods:
- touch
- touch_all
- update_all
- update_attribute
- update_column
- update_columns
Style/Documentation:
Enabled: false
Style/StringLiterals:
Style/FormatStringToken:
Enabled: false
Style/HashSyntax:
@@ -87,62 +138,5 @@ Style/HashSyntax:
Style/Send:
Enabled: true
Layout/MultilineMethodCallIndentation:
Enabled: true
EnforcedStyle: indented
Layout/LineLength:
Enabled: true
Max: 100
Lint/RaiseException:
Enabled: true
Lint/StructNewOverride:
Enabled: true
Naming/VariableNumber:
AllowedIdentifiers:
- street_address_1
- street_address_2
AllowedPatterns:
- _v[\d]+
Bundler/DuplicatedGem:
Enabled: false
## TEMPORARY/CONTESTED SETTINGS
#
# These are still to be decided upon, but recommended for inclusion by
# oeoeaio after scrutinising offenses the codebase
# Don't think this is a big issue, mostly picking up RPSEC scope definitions
# with lamdas and RSpec '.to change{}' blocks
Lint/AmbiguousBlockAssociation:
Enabled: false
# Heaps of offences (> 100) in specs, mostly in situations where two or more
# instances of a model are required, but only one is referenced. Difficult to
# fix without making the spec look messy or rewriting it.
# Should definitely fix at some point.
Lint/UselessAssignment:
Exclude:
- spec/**/*
Lint/MissingSuper:
Exclude:
- 'app/components/**/*'
Metrics/AbcSize:
Max: 30 # default 17
Metrics/MethodLength:
Enabled: true
Max: 25 # default 10
Metrics/PerceivedComplexity:
Enabled: true
Max: 14 # default 8
Naming/PredicateName:
Style/StringLiterals:
Enabled: false

View File

@@ -1,26 +1,16 @@
# This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 1400 --no-auto-gen-timestamp`
# using RuboCop version 1.57.2.
# using RuboCop version 1.62.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
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
# URISchemes: http, https
Layout/LineLength:
Exclude:
- 'app/models/spree/payment.rb'
- 'spec/system/admin/tag_rules_spec.rb'
# Offense count: 17
# Offense count: 16
# Configuration parameters: AllowedMethods.
# AllowedMethods: enums
Lint/ConstantDefinitionInBlock:
Exclude:
- 'lib/active_merchant/billing/gateways/stripe_payment_intents_decorator.rb'
- 'lib/tasks/import_product_images.rake'
- 'lib/tasks/users.rake'
- 'spec/controllers/spree/admin/base_controller_spec.rb'
@@ -116,13 +106,19 @@ Lint/RedundantSafeNavigation:
Exclude:
- 'app/models/spree/payment.rb'
# Offense count: 1
Lint/SelfAssignment:
Exclude:
- 'app/models/spree/order/checkout.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AutoCorrect.
Lint/UselessMethodDefinition:
Exclude:
- 'app/models/spree/gateway.rb'
# Offense count: 27
# Offense count: 26
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
Metrics/AbcSize:
Exclude:
@@ -146,10 +142,10 @@ Metrics/AbcSize:
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/spree/core/controller_helpers/order.rb'
- 'lib/tasks/enterprises.rake'
- 'spec/services/order_checkout_restart_spec.rb'
- 'spec/services/orders/checkout_restart_service_spec.rb'
# Offense count: 44
# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
# Offense count: 9
# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
# AllowedMethods: refine
Metrics/BlockLength:
Exclude:
@@ -159,26 +155,6 @@ Metrics/BlockLength:
- 'app/models/spree/shipment.rb'
- 'lib/spree/core/controller_helpers/common.rb'
- 'lib/tasks/data.rake'
- 'spec/factories.rb'
- 'spec/factories/address_factory.rb'
- 'spec/factories/enterprise_factory.rb'
- 'spec/factories/line_item_factory.rb'
- 'spec/factories/order_cycle_factory.rb'
- 'spec/factories/order_factory.rb'
- 'spec/factories/product_factory.rb'
- 'spec/factories/shipment_factory.rb'
- 'spec/factories/shipping_method_factory.rb'
- 'spec/factories/subscription_factory.rb'
- 'spec/factories/user_factory.rb'
- 'spec/factories/variant_factory.rb'
- 'spec/requests/checkout/failed_checkout_spec.rb'
- 'spec/requests/checkout/stripe_sca_spec.rb'
- 'spec/support/cancan_helper.rb'
- 'spec/support/matchers/select2_matchers.rb'
- 'spec/support/matchers/table_matchers.rb'
- 'spec/system/consumer/shopping/checkout_spec.rb'
- 'spec/system/consumer/shopping/checkout_stripe_spec.rb'
- 'spec/system/consumer/shopping/variant_overrides_spec.rb'
# Offense count: 1
# Configuration parameters: CountBlocks, Max.
@@ -225,8 +201,8 @@ Metrics/ClassLength:
- 'app/serializers/api/cached_enterprise_serializer.rb'
- 'app/serializers/api/enterprise_shopfront_serializer.rb'
- 'app/services/cart_service.rb'
- 'app/services/order_cycle_form.rb'
- 'app/services/order_syncer.rb'
- 'app/services/orders/sync_service.rb'
- 'app/services/order_cycles/form_service.rb'
- 'engines/order_management/app/services/order_management/order/updater.rb'
- 'lib/open_food_network/enterprise_fee_calculator.rb'
- 'lib/open_food_network/order_cycle_form_applicator.rb'
@@ -414,21 +390,6 @@ Naming/VariableNumber:
- 'spec/models/spree/tax_rate_spec.rb'
- 'spec/requests/api/orders_spec.rb'
# Offense count: 11
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowedMethods, AllowedPatterns.
# AllowedMethods: order, limit, select, lock
Rails/FindEach:
Exclude:
- 'app/controllers/admin/order_cycles_controller.rb'
- 'app/jobs/subscription_confirm_job.rb'
- 'app/services/orders_bulk_cancel_service.rb'
- 'app/services/products_renderer.rb'
- 'lib/tasks/data.rake'
- 'lib/tasks/subscriptions/debug.rake'
- 'spec/system/admin/bulk_order_management_spec.rb'
- 'spec/system/admin/enterprise_relationships_spec.rb'
# Offense count: 11
# Configuration parameters: Include.
# Include: app/models/**/*.rb
@@ -440,7 +401,7 @@ Rails/HasManyOrHasOneDependent:
- 'app/models/spree/tax_rate.rb'
- 'app/models/spree/variant.rb'
# Offense count: 25
# Offense count: 26
# Configuration parameters: Include.
# Include: app/helpers/**/*.rb
Rails/HelperInstanceVariable:
@@ -520,21 +481,7 @@ Rails/NegateInclude:
- 'lib/spree/localized_number.rb'
- 'spec/support/matchers/table_matchers.rb'
# Offense count: 16
Rails/OutputSafety:
Exclude:
- 'app/helpers/angular_form_helper.rb'
- 'app/helpers/application_helper.rb'
- 'app/helpers/reports_helper.rb'
- 'app/helpers/spree/admin/base_helper.rb'
- 'app/helpers/spree/admin/navigation_helper.rb'
- 'app/helpers/spree/admin/orders_helper.rb'
- 'app/helpers/spree/admin/zones_helper.rb'
- 'lib/reporting/queries/query_builder.rb'
- 'lib/reporting/queries/query_interface.rb'
- 'lib/spree/money.rb'
# Offense count: 31
# Offense count: 32
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/Pluck:
Exclude:
@@ -553,6 +500,7 @@ Rails/Pluck:
- 'spec/lib/reports/lettuce_share_report_spec.rb'
- 'spec/lib/reports/users_and_enterprises_report_spec.rb'
- 'spec/serializers/api/admin/for_order_cycle/supplied_product_serializer_spec.rb'
- 'spec/support/api_helper.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
@@ -620,13 +568,11 @@ Rails/ResponseParsedBody:
- 'spec/controllers/spree/credit_cards_controller_spec.rb'
- 'spec/controllers/user_registrations_controller_spec.rb'
# Offense count: 3
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/RootPathnameMethods:
Exclude:
- 'spec/lib/reports/orders_and_fulfillment/order_cycle_customer_totals_report_spec.rb'
- 'spec/models/terms_of_service_file_spec.rb'
- 'spec/system/admin/configuration/terms_of_service_files_spec.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
@@ -642,14 +588,6 @@ Rails/SkipsModelValidations:
- 'app/models/variant_override.rb'
- 'spec/models/spree/line_item_spec.rb'
# Offense count: 3
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/SquishedSQLHeredocs:
Exclude:
- 'app/queries/customers_with_balance.rb'
- 'app/queries/outstanding_balance.rb'
- 'spec/queries/outstanding_balance_spec.rb'
# Offense count: 7
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
@@ -658,8 +596,8 @@ Rails/TimeZone:
Exclude:
- 'app/models/spree/gateway/pay_pal_express.rb'
- 'spec/controllers/spree/credit_cards_controller_spec.rb'
- 'spec/services/customer_order_cancellation_spec.rb'
- 'spec/services/order_cycle_webhook_service_spec.rb'
- 'spec/services/orders/customer_cancellation_service_spec.rb'
- 'spec/services/order_cycles/webhook_service_spec.rb'
# Offense count: 1
# Configuration parameters: TransactionMethods.
@@ -696,7 +634,7 @@ Rails/UnusedRenderContent:
- 'app/controllers/api/v0/taxons_controller.rb'
- 'app/controllers/api/v0/variants_controller.rb'
# Offense count: 55
# Offense count: 54
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/WhereEquals:
Exclude:
@@ -718,7 +656,6 @@ Rails/WhereEquals:
- 'app/models/spree/shipping_method.rb'
- 'app/models/spree/variant.rb'
- 'app/models/subscription.rb'
- 'app/queries/payments_requiring_action.rb'
- 'app/serializers/api/enterprise_shopfront_serializer.rb'
- 'app/serializers/api/order_serializer.rb'
- 'lib/open_food_network/enterprise_fee_calculator.rb'
@@ -799,14 +736,6 @@ Style/ClassAndModuleChildren:
- 'spec/controllers/spree/admin/base_controller_spec.rb'
- 'spec/models/spree/payment_method_spec.rb'
# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns.
# SupportedStyles: annotated, template, unannotated
# AllowedMethods: redirect
Style/FormatStringToken:
EnforcedStyle: unannotated
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
@@ -841,6 +770,22 @@ Style/HashConversion:
- 'spec/controllers/admin/inventory_items_controller_spec.rb'
- 'spec/controllers/admin/variant_overrides_controller_spec.rb'
# Offense count: 13
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowedReceivers.
# AllowedReceivers: Thread.current
Style/HashEachMethods:
Exclude:
- 'app/controllers/admin/enterprises_controller.rb'
- 'app/controllers/spree/admin/shipping_methods_controller.rb'
- 'app/forms/enterprise_fees_bulk_update.rb'
- 'app/models/product_import/entry_processor.rb'
- 'app/models/spree/preferences/configuration.rb'
- 'app/services/sets/model_set.rb'
- 'lib/reporting/reports/sales_tax/sales_tax_totals_by_producer.rb'
- 'spec/models/product_importer_spec.rb'
- 'spec/support/cancan_helper.rb'
# Offense count: 1
# Configuration parameters: MinBranchesCount.
Style/HashLikeCase:
@@ -915,17 +860,22 @@ Style/PreferredHashMethods:
Exclude:
- 'app/controllers/api/v0/shipments_controller.rb'
# Offense count: 3
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Methods.
Style/RedundantArgument:
Exclude:
- 'engines/dfc_provider/app/services/authorization_control.rb'
- 'spec/support/query_counter.rb'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
Style/RedundantAssignment:
Exclude:
- 'spec/models/database_spec.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowComments.
# Configuration parameters: AutoCorrect, AllowComments.
Style/RedundantInitialize:
Exclude:
- 'spec/models/spree/gateway_spec.rb'
@@ -937,6 +887,12 @@ Style/RedundantInterpolation:
- '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.
@@ -988,7 +944,7 @@ Style/SlicingWithRange:
- 'engines/order_management/app/services/order_management/subscriptions/validator.rb'
- 'lib/discourse/single_sign_on.rb'
# Offense count: 28
# Offense count: 25
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Mode.
Style/StringConcatenation:
@@ -1010,6 +966,5 @@ Style/StringConcatenation:
- 'spec/models/spree/line_item_spec.rb'
- 'spec/models/spree/product_spec.rb'
- 'spec/services/embedded_page_service_spec.rb'
- 'spec/support/api_helper.rb'
- 'spec/support/features/datepicker_helper.rb'
- 'spec/system/admin/products_spec.rb'

View File

@@ -6,28 +6,9 @@ This is a general guide to setting up an Open Food Network **development environ
Head to our wiki on [Learning Rails](https://github.com/openfoodfoundation/openfoodnetwork/wiki/Learning-Rails) to find some good starting points.
### Requirements
The fastest way to make it work locally is to use Docker, you only need to setup git, see the [Docker setup guide](docker/README.md).
Otherwise, for a local setup you will need:
* Ruby and bundler (check current Ruby version in [.ruby-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.ruby-version) file)
- To manage versions, it's recommended to use [rbenv](https://github.com/rbenv/rbenv) or [RVM](https://rvm.io/)
* Node and yarn (check current Node version in [.node-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.node-version) file)
- [nodevn](https://github.com/nodenv/nodenv) is recommended.
* PostgreSQL database
* Redis (for background jobs)
* Chrome (for testing)
The following guides will provide OS-specific step-by-step instructions to get these requirements installed:
- [Ubuntu Setup Guide][ubuntu]
- [Debian Setup Guide][debian]
- [OSX Setup Guide][osx]
For those new to Rails, the following tutorial will help get you up to speed with configuring a [Rails environment](http://guides.rubyonrails.org/getting_started.html).
### Get it
So you have set up your local environment according to the requirements listed above. If you're planning on contributing code to the project (which we [LOVE](CONTRIBUTING.md)), it is a good idea to begin by forking this repo using the `Fork` button in the top-right corner of this screen. You should then be able to use `git clone` to copy your fork onto your local machine:
If you're planning on contributing code to the project (which we [LOVE](CONTRIBUTING.md)), it is a good idea to begin by forking this repo using the `Fork` button in the top-right corner of this screen. You should then be able to use `git clone` to copy your fork onto your local machine:
git clone git@github.com:YOUR_GITHUB_USERNAME_HERE/openfoodnetwork.git
@@ -43,6 +24,27 @@ Fetch the latest version of `master` from `upstream` (ie. the main repo):
git fetch upstream master
### Installation
This project needs specific ruby/bundler versions as well as node/yarn specific versions. For a local setup you will need:
* Install or change your Ruby version according to the one specified at [.ruby-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.ruby-version) file.
- To manage versions, it's recommended to use [rbenv](https://github.com/rbenv/rbenv).
* Install [nodenv](https://github.com/nodenv/nodenv) to ensure the correct [.node-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.node-version) is used.
- [nodevn](https://github.com/nodenv/nodenv) is recommended as a node version manager.
* PostgreSQL database
* Redis (for background jobs)
* Chrome (for testing)
The following guides will provide OS-specific step-by-step instructions to get these requirements installed:
- [Ubuntu Setup Guide][ubuntu]
- [Debian Setup Guide][debian]
- [OSX Setup Guide][osx]
For those new to Rails, the following tutorial will help get you up to speed with configuring a [Rails environment](http://guides.rubyonrails.org/getting_started.html).
Another way to make it work locally would be using Docker. See the [Docker setup guide](docker/README.md).
### Get it running
First, you need to create the database user the app will use by manually typing the following in your terminal:
@@ -53,7 +55,8 @@ sudo --login --user=postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PA
This will create the "ofn" user as superuser and allowing it to create databases. If this command fails, check the [troubleshooting section](#creating-the-database) for an alternative.
Next, it is _strongly recommended_ to run the setup script.
Next, it is _strongly recommended_ to run the setup script:
```sh
./script/setup
```

16
Gemfile
View File

@@ -5,7 +5,7 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
ruby File.read('.ruby-version').chomp
gem 'dotenv-rails', require: 'dotenv/rails-now' # Load ENV vars before other gems
gem 'dotenv', require: 'dotenv/load' # Load ENV vars before other gems
gem 'rails'
@@ -17,7 +17,7 @@ gem "image_processing"
gem 'activemerchant', '>= 1.78.0'
gem 'angular-rails-templates', '>= 0.3.0'
gem 'awesome_nested_set'
gem 'ransack', '~> 2.6.0'
gem 'ransack', '~> 4.1.0'
gem 'responders'
gem 'rexml'
gem 'webpacker', '~> 5'
@@ -74,7 +74,7 @@ gem 'rswag-ui'
gem 'omniauth_openid_connect'
gem 'omniauth-rails_csrf_protection'
gem 'openid_connect', '~> 1.3'
gem 'openid_connect'
gem 'angularjs-rails', '1.8.0'
gem 'bugsnag'
@@ -93,14 +93,13 @@ gem 'bootsnap', require: false
gem 'geocoder'
gem 'gmaps4rails'
gem 'mimemagic', '> 0.3.5'
gem 'paper_trail', '~> 12.1'
gem 'paper_trail'
gem 'rack-rewrite'
gem 'rack-timeout'
gem 'roadie-rails'
gem 'hiredis'
gem 'puma'
gem 'redis', '>= 4.0', require: ['redis', 'redis/connection/hiredis']
gem 'redis'
gem 'sidekiq'
gem 'sidekiq-scheduler'
@@ -143,6 +142,8 @@ gem "private_address_check"
gem 'newrelic_rpm'
gem 'invisible_captcha'
group :production, :staging do
gem 'sd_notify' # For better Systemd process management. Used by Puma.
end
@@ -160,6 +161,7 @@ group :test, :development do
gem 'letter_opener', '>= 1.4.1'
gem 'rspec-rails', ">= 3.5.2"
gem 'rspec-retry', require: false
gem 'rspec-sql'
gem 'rswag'
gem 'shoulda-matchers'
gem 'stimulus_reflex_testing'
@@ -185,8 +187,10 @@ group :development do
gem 'rails-erd'
gem 'rubocop'
gem 'rubocop-rails'
gem 'rubocop-rspec'
gem 'spring'
gem 'spring-commands-rspec'
gem 'spring-commands-rubocop'
gem 'web-console'
gem 'rack-mini-profiler', '< 3.0.0'

View File

@@ -90,7 +90,7 @@ GEM
rails-html-sanitizer (~> 1.1, >= 1.2.0)
active_model_serializers (0.8.4)
activemodel (>= 3.0)
active_storage_validations (1.1.3)
active_storage_validations (1.1.4)
activejob (>= 5.2.0)
activemodel (>= 5.2.0)
activestorage (>= 5.2.0)
@@ -98,17 +98,18 @@ GEM
activejob (7.0.8)
activesupport (= 7.0.8)
globalid (>= 0.3.6)
activemerchant (1.123.0)
activemerchant (1.133.0)
activesupport (>= 4.2)
builder (>= 2.1.2, < 4.0.0)
i18n (>= 0.6.9)
nokogiri (~> 1.4)
rexml (~> 3.2.5)
activemodel (7.0.8)
activesupport (= 7.0.8)
activerecord (7.0.8)
activemodel (= 7.0.8)
activesupport (= 7.0.8)
activerecord-import (1.5.1)
activerecord-import (1.6.0)
activerecord (>= 4.2)
activerecord-postgresql-adapter (0.0.1)
pg
@@ -135,7 +136,7 @@ GEM
activerecord (>= 6.1, < 7.2)
acts_as_list (1.0.4)
activerecord (>= 4.2)
addressable (2.8.5)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
aes_key_wrap (1.1.0)
afm (0.2.2)
@@ -151,21 +152,21 @@ GEM
arel-helpers (2.14.0)
activerecord (>= 3.1.0, < 8)
ast (2.4.2)
attr_required (1.0.1)
attr_required (1.0.2)
awesome_nested_set (3.6.0)
activerecord (>= 4.0.0, < 7.2)
aws-eventstream (1.3.0)
aws-partitions (1.860.0)
aws-sdk-core (3.189.0)
aws-partitions (1.899.0)
aws-sdk-core (3.191.4)
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.74.0)
aws-sdk-core (~> 3, >= 3.188.0)
aws-sdk-kms (1.78.0)
aws-sdk-core (~> 3, >= 3.191.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.141.0)
aws-sdk-core (~> 3, >= 3.189.0)
aws-sdk-s3 (1.146.0)
aws-sdk-core (~> 3, >= 3.191.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.8)
aws-sigv4 (1.8.0)
@@ -174,14 +175,14 @@ GEM
bcp47_spec (0.2.1)
bcrypt (3.1.19)
bigdecimal (3.0.2)
bindata (2.4.15)
bindata (2.5.0)
bindex (0.8.1)
bootsnap (1.17.0)
bootsnap (1.18.3)
msgpack (~> 1.2)
bugsnag (6.26.0)
bugsnag (6.26.3)
concurrent-ruby (~> 1.0)
builder (3.2.4)
bullet (7.1.4)
bullet (7.1.6)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
cable_ready (5.0.1)
@@ -191,11 +192,11 @@ GEM
railties (>= 5.2)
thread-local (>= 1.1.0)
cancancan (1.15.0)
capybara (3.39.2)
capybara (3.40.0)
addressable
matrix
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
nokogiri (~> 1.11)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
@@ -216,12 +217,13 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
combine_pdf (1.0.24)
combine_pdf (1.0.26)
matrix
ruby-rc4 (>= 0.1.5)
concurrent-ruby (1.2.2)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
crack (0.4.5)
crack (1.0.0)
bigdecimal
rexml
crass (1.0.6)
css_parser (1.16.0)
@@ -235,12 +237,12 @@ GEM
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
datafoodconsortium-connector (1.0.0.pre.alpha.9)
datafoodconsortium-connector (1.0.0.pre.alpha.10)
virtual_assembly-semantizer (~> 1.0, >= 1.0.5)
date (3.3.3)
debug (1.8.0)
irb (>= 1.5.0)
reline (>= 0.3.1)
date (3.3.4)
debug (1.9.1)
irb (~> 1.10)
reline (>= 0.3.8)
debugger-linecache (1.2.0)
devise (4.9.3)
bcrypt (~> 3.0)
@@ -254,13 +256,12 @@ GEM
devise (>= 4.9.0)
devise-token_authenticatable (1.1.0)
devise (>= 4.0.0, < 5.0.0)
diff-lcs (1.5.0)
diff-lcs (1.5.1)
digest (3.1.1)
docile (1.4.0)
dotenv (2.8.1)
dotenv-rails (2.8.1)
dotenv (= 2.8.1)
railties (>= 3.2)
dotenv (3.1.0)
email_validator (2.2.4)
activemodel
erubi (1.12.0)
et-orbi (1.2.7)
tzinfo
@@ -271,20 +272,19 @@ GEM
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faraday (2.7.12)
base64
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday (2.9.0)
faraday-net_http (>= 2.0, < 3.2)
faraday-follow_redirects (0.3.0)
faraday (>= 1, < 3)
faraday-net_http (3.0.2)
faraday-net_http (3.1.0)
net-http
ferrum (0.14)
addressable (~> 2.5)
concurrent-ruby (~> 1.1)
webrick (~> 1.7)
websocket-driver (>= 0.6, < 0.8)
ffaker (2.23.0)
ffi (1.15.5)
ffi (1.16.3)
flipper (0.26.2)
concurrent-ruby (< 2)
flipper-active_record (0.26.2)
@@ -326,17 +326,16 @@ GEM
good_migrations (0.2.1)
activerecord (>= 3.1)
railties (>= 3.1)
haml (5.2.2)
temple (>= 0.8.0)
haml (6.3.0)
temple (>= 0.8.2)
thor
tilt
hashdiff (1.0.1)
hashdiff (1.1.0)
hashery (2.1.2)
hashie (5.0.0)
highline (2.0.3)
hiredis (0.6.3)
htmlentities (4.3.4)
httpclient (2.8.3)
i18n (1.14.1)
i18n (1.14.4)
concurrent-ruby (~> 1.0)
i18n-js (3.9.2)
i18n (>= 0.6.6)
@@ -345,10 +344,13 @@ GEM
ruby-vips (>= 2.0.17, < 3)
immigrant (0.3.6)
activerecord (>= 3.0)
io-console (0.6.0)
invisible_captcha (2.2.0)
rails (>= 5.2)
io-console (0.7.1)
ipaddress (0.8.3)
irb (1.6.4)
reline (>= 0.3.0)
irb (1.11.0)
rdoc
reline (>= 0.3.8)
jmespath (1.6.2)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
@@ -357,16 +359,17 @@ GEM
jquery-ui-rails (4.2.1)
railties (>= 3.2.16)
json (2.7.1)
json-canonicalization (0.3.2)
json-jwt (1.16.3)
json-canonicalization (1.0.0)
json-jwt (1.16.6)
activesupport (>= 4.2)
aes_key_wrap
base64
bindata
faraday (~> 2.0)
faraday-follow_redirects
json-ld (3.3.0)
json-ld (3.3.1)
htmlentities (~> 4.3)
json-canonicalization (~> 0.3, >= 0.3.2)
json-canonicalization (~> 1.0)
link_header (~> 0.0, >= 0.0.8)
multi_json (~> 1.15)
rack (>= 2.2, < 4)
@@ -378,16 +381,17 @@ GEM
rspec (>= 2.0, < 4.0)
jsonapi-serializer (2.2.0)
activesupport (>= 4.2)
jwt (2.7.1)
knapsack_pro (6.0.1)
jwt (2.8.1)
base64
knapsack_pro (6.0.4)
rake
language_server-protocol (3.17.0.3)
launchy (2.5.0)
addressable (~> 2.7)
letter_opener (1.8.1)
launchy (2.5.2)
addressable (~> 2.8)
letter_opener (1.9.0)
launchy (>= 2.2, < 3)
link_header (0.0.8)
listen (3.8.0)
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
loofah (2.22.0)
@@ -401,36 +405,37 @@ GEM
marcel (1.0.2)
matrix (0.4.2)
method_source (1.0.0)
mime-types (3.5.1)
mime-types (3.5.2)
mime-types-data (~> 3.2015)
mime-types-data (3.2023.0808)
mime-types-data (3.2023.1205)
mimemagic (0.4.3)
nokogiri (~> 1)
rake
mini_magick (4.11.0)
mini_mime (1.1.5)
mini_portile2 (2.8.5)
minitest (5.20.0)
monetize (1.12.0)
minitest (5.22.3)
monetize (1.13.0)
money (~> 6.12)
money (6.16.0)
i18n (>= 0.6.4, <= 2)
msgpack (1.7.2)
multi_json (1.15.0)
multi_xml (0.6.0)
net-imap (0.4.2)
net-http (0.4.1)
uri
net-imap (0.4.10)
date
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.2.1)
net-protocol (0.2.2)
timeout
net-smtp (0.4.0)
net-smtp (0.4.0.1)
net-protocol
newrelic_rpm (9.6.0)
base64
nio4r (2.5.9)
nokogiri (1.15.5)
newrelic_rpm (9.7.1)
nio4r (2.7.0)
nokogiri (1.16.3)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
oauth2 (1.4.11)
@@ -439,37 +444,39 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 4)
omniauth (2.1.1)
omniauth (2.1.2)
hashie (>= 3.4.6)
rack (>= 2.2.3)
rack-protection
omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
omniauth_openid_connect (0.6.1)
omniauth_openid_connect (0.7.1)
omniauth (>= 1.9, < 3)
openid_connect (~> 1.1)
openid_connect (1.4.2)
openid_connect (~> 2.2)
openid_connect (2.3.0)
activemodel
attr_required (>= 1.0.0)
json-jwt (>= 1.15.0)
net-smtp
rack-oauth2 (~> 1.21)
swd (~> 1.3)
email_validator
faraday (~> 2.0)
faraday-follow_redirects
json-jwt (>= 1.16)
mail
rack-oauth2 (~> 2.2)
swd (~> 2.0)
tzinfo
validate_email
validate_url
webfinger (~> 1.2)
webfinger (~> 2.0)
orm_adapter (0.5.0)
pagy (5.10.1)
activesupport
paper_trail (12.3.0)
activerecord (>= 5.2)
request_store (~> 1.1)
parallel (1.23.0)
paper_trail (15.1.0)
activerecord (>= 6.1)
request_store (~> 1.4)
parallel (1.24.0)
paranoia (2.6.3)
activerecord (>= 5.1, < 7.2)
parser (3.2.2.4)
parser (3.3.0.5)
ast (~> 2.4.1)
racc
paypal-sdk-core (0.3.4)
@@ -477,7 +484,7 @@ GEM
xml-simple
paypal-sdk-merchant (1.117.2)
paypal-sdk-core (~> 0.3.0)
pdf-reader (2.11.0)
pdf-reader (2.12.0)
Ascii85 (~> 1.0)
afm (~> 0.2.1)
hashery (~> 2.0)
@@ -488,25 +495,29 @@ GEM
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
psych (5.1.2)
stringio
public_suffix (5.0.4)
puma (6.4.0)
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)
rack (2.2.8)
rack (2.2.8.1)
rack-mini-profiler (2.3.4)
rack (>= 1.2.0)
rack-oauth2 (1.21.3)
rack-oauth2 (2.2.1)
activesupport
attr_required
httpclient
faraday (~> 2.0)
faraday-follow_redirects
json-jwt (>= 1.11.0)
rack (>= 2.1.0)
rack-protection (3.0.5)
rack
rack-protection (3.2.0)
base64 (>= 0.1.0)
rack (~> 2.2, >= 2.2.4)
rack-proxy (0.7.6)
rack
rack-rewrite (1.5.1)
@@ -543,7 +554,7 @@ GEM
rails-html-sanitizer (1.6.0)
loofah (~> 2.21)
nokogiri (~> 1.14)
rails-i18n (7.0.8)
rails-i18n (7.0.9)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8)
rails_safe_tasks (1.0.0)
@@ -556,9 +567,9 @@ GEM
zeitwerk (~> 2.5)
rainbow (3.1.1)
rake (13.1.0)
ransack (2.6.0)
activerecord (>= 6.0.4)
activesupport (>= 6.0.4)
ransack (4.1.1)
activerecord (>= 6.1.5)
activesupport (>= 6.1.5)
i18n
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
@@ -566,12 +577,15 @@ GEM
rdf (3.3.1)
bcp47_spec (~> 0.2)
link_header (~> 0.0, >= 0.0.8)
rdoc (6.6.2)
psych (>= 4.0.0)
redcarpet (3.6.0)
redis (4.8.1)
redis-client (0.18.0)
redis (5.1.0)
redis-client (>= 0.17.0)
redis-client (0.20.0)
connection_pool
regexp_parser (2.8.2)
reline (0.3.3)
regexp_parser (2.9.0)
reline (0.4.1)
io-console (~> 0.5)
request_store (1.5.1)
rack (>= 1.4)
@@ -588,32 +602,35 @@ GEM
rodf (1.2.0)
builder (>= 3.0)
rubyzip (>= 1.0)
roo (2.10.0)
roo (2.10.1)
nokogiri (~> 1)
rubyzip (>= 1.3.0, < 3.0.0)
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
rspec-mocks (~> 3.12.0)
rspec-core (3.12.2)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.3)
rspec (3.13.0)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.0)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.6)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-rails (6.1.0)
rspec-support (~> 3.13.0)
rspec-rails (6.1.2)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
rspec-core (~> 3.12)
rspec-expectations (~> 3.12)
rspec-mocks (~> 3.12)
rspec-support (~> 3.12)
rspec-core (~> 3.13)
rspec-expectations (~> 3.13)
rspec-mocks (~> 3.13)
rspec-support (~> 3.13)
rspec-retry (0.6.2)
rspec-core (> 3.3)
rspec-support (3.12.1)
rspec-sql (0.0.1)
activesupport
rspec
rspec-support (3.13.1)
rswag (2.13.0)
rswag-api (= 2.13.0)
rswag-specs (= 2.13.0)
@@ -629,31 +646,38 @@ GEM
rswag-ui (2.13.0)
actionpack (>= 3.1, < 7.2)
railties (>= 3.1, < 7.2)
rubocop (1.58.0)
rubocop (1.62.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.2.2.4)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.30.0, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.30.0)
parser (>= 3.2.1.0)
rubocop-rails (2.22.2)
rubocop-ast (1.31.2)
parser (>= 3.3.0.4)
rubocop-capybara (2.20.0)
rubocop (~> 1.41)
rubocop-factory_bot (2.25.1)
rubocop (~> 1.41)
rubocop-rails (2.24.0)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rspec (2.27.1)
rubocop (~> 1.40)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
ruby-graphviz (1.2.5)
rexml
ruby-progressbar (1.13.0)
ruby-rc4 (0.1.5)
ruby-vips (2.1.4)
ffi (~> 1.12)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
rufus-scheduler (3.8.2)
fugit (~> 1.1, >= 1.1.6)
@@ -669,13 +693,13 @@ GEM
tilt (>= 1.1, < 3)
sd_notify (0.1.1)
semantic_range (3.0.0)
shoulda-matchers (5.3.0)
shoulda-matchers (6.2.0)
activesupport (>= 5.2.0)
sidekiq (7.2.0)
sidekiq (7.2.2)
concurrent-ruby (< 2)
connection_pool (>= 2.3.0)
rack (>= 2.2.4)
redis-client (>= 0.14.0)
redis-client (>= 0.19.0)
sidekiq-scheduler (5.0.3)
rufus-scheduler (~> 3.2)
sidekiq (>= 6, < 8)
@@ -692,6 +716,8 @@ GEM
spring (4.1.3)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
spring-commands-rubocop (0.4.0)
spring (>= 1.0)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
@@ -719,35 +745,35 @@ GEM
stimulus_reflex_testing (0.3.0)
stimulus_reflex (>= 3.3.0)
stringex (2.8.6)
stripe (10.2.0)
swd (1.3.0)
stringio (3.1.0)
stripe (10.12.0)
swd (2.0.3)
activesupport (>= 3)
attr_required (>= 0.0.5)
httpclient (>= 2.4)
faraday (~> 2.0)
faraday-follow_redirects
temple (0.8.2)
thor (1.3.0)
thor (1.3.1)
thread-local (1.1.0)
tilt (2.3.0)
timecop (0.9.8)
timeout (0.4.0)
timeout (0.4.1)
ttfunk (1.7.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)
uniform_notifier (1.16.0)
valid_email2 (5.1.1)
uri (0.13.0)
valid_email2 (5.2.1)
activemodel (>= 3.2)
mail (~> 2.5)
validate_email (0.1.6)
activemodel (>= 3.0)
mail (>= 2.2.5)
validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
validates_lengths_from_database (0.8.0)
activerecord (>= 4)
vcr (6.2.0)
view_component (3.8.0)
view_component (3.11.0)
activesupport (>= 5.2.0, < 8.0)
concurrent-ruby (~> 1.0)
method_source (~> 1.0)
@@ -764,10 +790,11 @@ GEM
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
webfinger (1.2.0)
webfinger (2.1.3)
activesupport
httpclient (>= 2.4)
webmock (3.18.1)
faraday (~> 2.0)
faraday-follow_redirects
webmock (3.23.0)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@@ -788,7 +815,7 @@ GEM
xml-simple (1.1.8)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.6.12)
zeitwerk (2.6.13)
PLATFORMS
ruby
@@ -832,7 +859,7 @@ DEPENDENCIES
devise-token_authenticatable
dfc_provider!
digest
dotenv-rails
dotenv
factory_bot_rails (= 6.2.0)
faraday
ffaker
@@ -847,11 +874,11 @@ DEPENDENCIES
good_migrations
haml
highline (= 2.0.3)
hiredis
i18n
i18n-js (~> 3.9.0)
image_processing
immigrant
invisible_captcha
jquery-rails (= 4.4.0)
jquery-ui-rails (~> 4.2)
json
@@ -869,10 +896,10 @@ DEPENDENCIES
oauth2 (~> 1.4.7)
omniauth-rails_csrf_protection
omniauth_openid_connect
openid_connect (~> 1.3)
openid_connect
order_management!
pagy (~> 5.1)
paper_trail (~> 12.1)
paper_trail
paranoia (~> 2.4)
paypal-sdk-merchant (= 1.117.2)
pdf-reader
@@ -889,20 +916,22 @@ DEPENDENCIES
rails-erd
rails-i18n
rails_safe_tasks (~> 1.0)
ransack (~> 2.6.0)
ransack (~> 4.1.0)
redcarpet
redis (>= 4.0)
redis
responders
rexml
roadie-rails
roo
rspec-rails (>= 3.5.2)
rspec-retry
rspec-sql
rswag
rswag-api
rswag-ui
rubocop
rubocop-rails
rubocop-rspec
sd_notify
select2-rails!
shoulda-matchers
@@ -912,6 +941,7 @@ DEPENDENCIES
spreadsheet_architect
spring
spring-commands-rspec
spring-commands-rubocop
state_machines-activerecord
stimulus_reflex (= 3.5.0.rc3)
stimulus_reflex_testing

View File

@@ -32,7 +32,7 @@ We also have a [Super Admin Guide][super-admin-guide] to help with configuration
## Testing
If you'd like to help out with testing, please introduce yourself on the #testing channel on [Slack][slack-invite] and download the [ZenHub browser extension][zenhub] to view the development pipeline. Also, do have a look in our [Welcome New QAs board][welcome-qa] for some good first issues, both on manual and automated testing (RSpec/Capybara).
If you'd like to help out with testing, please introduce yourself on the #testing channel on [Slack][slack-invite]. Also, do have a look in our [Welcome New QAs board][welcome-qa] for some good first issues, both on manual and automated testing (RSpec/Capybara).
We use [BrowserStack](https://www.browserstack.com/) as a manual testing tool. BrowserStack provides open source projects with unlimited and free of charge accounts. A big thanks to them!
@@ -44,7 +44,7 @@ We use [KnapsackPro](https://knapsackpro.com/) for optimal parallelisation of ou
## Licence
Copyright (c) 2012 - 2022 Open Food Foundation, released under the AGPL licence.
Copyright (c) 2012 - 2024 Open Food Foundation, released under the AGPL licence.
[survey]: https://docs.google.com/a/eaterprises.com.au/forms/d/1zxR5vSiU9CigJ9cEaC8-eJLgYid8CR8er7PPH9Mc-30/edit#
[slack-invite]: https://join.slack.com/t/openfoodnetwork/shared_invite/zt-9sjkjdlu-r02kUMP1zbrTgUhZhYPF~A
@@ -53,4 +53,3 @@ Copyright (c) 2012 - 2022 Open Food Foundation, released under the AGPL licence.
[super-admin-guide]: https://ofn-user-guide.gitbook.io/ofn-super-admin-guide
[welcome-dev]: https://github.com/orgs/openfoodfoundation/projects/5
[welcome-qa]: https://github.com/orgs/openfoodfoundation/projects/6
[zenhub]: https://www.zenhub.com/extension

View File

@@ -43,7 +43,8 @@ angular.module('admin.payments').factory 'Payment', (AdminStripeElements, curren
submit: =>
munged = @preprocess()
PaymentResource.create({order_id: munged.order_id}, munged, (response, headers, status) ->
document.body.innerHTML = Object.values(response).join('')
rawHtml = Object.values(response).join('').replace('[object Object]true', '')
document.body.innerHTML = rawHtml
$window.history.pushState({}, '', "/admin/orders/" + munged.order_id + "/payments")
, (response) ->
StatusMessage.display 'error', t("spree.admin.payments.source_forms.stripe.error_saving_payment")

View File

@@ -32,9 +32,6 @@ jQuery(function($) {
});
}
// Make flash messages dissapear
setTimeout('$(".flash").fadeOut()', 5000);
// Highlight hovered table column
$('table tbody tr td.actions a').hover(function(){
var tr = $(this).closest('tr');

View File

@@ -1,22 +1,6 @@
// Shipments AJAX API
$(document).ready(function() {
handle_ship_click = function(){
var link = $(this);
var shipment_number = link.data('shipment-number');
var url = Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number + "/ship.json");
$.ajax({
type: "PUT",
url: url
}).done(function( msg ) {
window.location.reload();
}).error(function( msg ) {
console.log(msg);
});
}
$('.admin-order-edit-form a.ship').click(handle_ship_click);
//handle shipping method edit click
$('a.edit-method').click(toggleMethodEdit);
$('a.cancel-method').click(toggleMethodEdit);

View File

@@ -1,3 +1,2 @@
%li{ ng: { class: "{active: selector.active}" } }
%a{ "tooltip" => "{{selector.object.value}}", "tooltip-placement" => "bottom",
ng: { transclude: true, class: "{active: selector.active, 'has-tip': selector.object.value}" } }
%li{ "ng-class": "{active: selector.active}" }
%a{ tooltip: "{{selector.object.value}}", "tooltip-placement": "bottom", "ng-transclude": true, "ng-class": "{active: selector.active, 'has-tip': selector.object.value}" }

View File

@@ -1,8 +1,8 @@
.sixteen.columns.alpha.omega.alert-row{ ng: { show: '!dismissed' } }
.sixteen.columns.alpha.omega.alert-row{ "ng-show": '!dismissed' }
.fifteen.columns.pad.alpha
%span.message.text-big{ ng: { bind: 'message'} }
%span.message.text-big{ "ng-bind": 'message' }
&nbsp;&nbsp;&nbsp;
%input{ type: 'button', ng: { value: "buttonText", show: 'buttonText && buttonAction', click: "buttonAction()" } }
%input{ type: 'button', "ng-value": "buttonText", "ng-show": 'buttonText && buttonAction', "ng-click": "buttonAction()" }
.one.column.omega.pad.text-center
%a.close{ href: "#", ng: { click: "dismiss()" } }
%a.close{ href: "#", "ng-click": "dismiss()" }
&times;

View File

@@ -1,13 +1,13 @@
.ofn-drop-down.ofn-drop-down-v2.right#columns-dropdown{ ng: { controller: 'ColumnsDropdownCtrl' } }
.ofn-drop-down.ofn-drop-down-v2.right#columns-dropdown{ "ng-controller": 'ColumnsDropdownCtrl' }
.ofn-drop-down-label
= "&nbsp; #{t('admin.columns')}".html_safe
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
%div.menu{ 'ng-show' => "expanded" }
.menu_items
.menu_item{ ng: { repeat: "column in columns", click: "toggle(column);" } }
%input.redesigned-input{ type: "checkbox", ng: { checked: "column.visible" } }
.menu_item{ "ng-repeat": "column in columns", "ng-click": "toggle(column);" }
%input.redesigned-input{ type: "checkbox", "ng-checked": "column.visible" }
{{ column.name }}
%hr
%div.menu_item.text-center
%input.fullwidth.orange{ type: "button", ng: { value: "saved() ? 'Saved': 'Saving'", show: "saved() || saving", disabled: "saved()" } }
%input.fullwidth.red{ type: "button", :value => t('admin.column_save_as_default').html_safe, ng: { show: "!saved() && !saving", click: "saveColumnPreferences(action)"} }
%input.fullwidth.orange{ type: "button", "ng-value": "saved() ? 'Saved': 'Saving'", "ng-show": "saved() || saving", "ng-disabled": "saved()" }
%input.fullwidth.red{ type: "button", value: t('admin.column_save_as_default').html_safe, "ng-show": "!saved() && !saving", "ng-click": "saveColumnPreferences(action)" }

View File

@@ -1,8 +1,8 @@
#confirm-dialog{ ng: { class: "dialog_class" } }
#confirm-dialog{ "ng-class": "dialog_class" }
.message.clearfix.margin-bottom-30
.icon.text-center
%i.icon-question-sign
.text{ ng: { bind: "::message" } }
.text{ "ng-bind": "::message" }
.action-buttons.text-center
%button.cancel{ ng: { click: "close()", bind: "::cancelText" } }
%button.confirm.red{ ng: { click: "confirm()", bind: "::confirmText" } }
%button.cancel{ "ng-click": "close()", "ng-bind": "::cancelText" }
%button.confirm.red{ "ng-click": "confirm()", "ng-bind": "::confirmText" }

View File

@@ -1,12 +1,12 @@
#edit-address-dialog
%h2 {{ addressType === 'bill_address' ? "#{t('admin.customers.index.edit_bill_address')}" : "#{t('admin.customers.index.edit_ship_address')}" }}
%form{ name: 'edit_address_form', novalidate: true, ng: { submit: 'updateAddress()'}}
%form{ name: 'edit_address_form', novalidate: true, "ng-submit": 'updateAddress()' }
.row
{{ 'admin.customers.index.required_fileds' | t }}
(
%span.required *
)
.error{ ng: { repeat: "error in errors", bind: "error" } }
.error{ "ng-repeat": "error in errors", "ng-bind": "error" }
%table.no-borders
%tr
@@ -14,61 +14,55 @@
{{ 'first_name' | t }}
%span.required *
%td
%input{ type: 'text', name: 'firstname', required: true, ng: { model: 'address.firstname'} }
%input{ type: 'text', name: 'firstname', required: true, "ng-model": 'address.firstname' }
%tr
%td
{{ 'last_name' | t }}
%span.required *
%td
%input{ type: 'text', name: 'lastname', required: true, ng: { model: 'address.lastname'} }
%input{ type: 'text', name: 'lastname', required: true, "ng-model": 'address.lastname' }
%tr
%td
{{ 'address' | t }}
%span.required *
%td
%input{ type: 'text', name: 'address1', required: true, ng: { model: 'address.address1'} }
%input{ type: 'text', name: 'address1', required: true, "ng-model": 'address.address1' }
%tr
%td
{{ 'address2' | t }}
%td
%input{ type: 'text', name: 'address2', ng: { model: 'address.address2'} }
%input{ type: 'text', name: 'address2', "ng-model": 'address.address2' }
%tr
%td
{{ 'city' | t }}
%span.required *
%td
%input{ type: 'text', name: 'city', required: true, ng: { model: 'address.city'} }
%input{ type: 'text', name: 'city', required: true, "ng-model": 'address.city' }
%tr
%td
{{ 'postcode' | t }}
%span.required *
%td
%input{ type: 'text', name: 'zipcode', required: true, ng: { model: 'address.zipcode'} }
%input{ type: 'text', name: 'zipcode', required: true, "ng-model": 'address.zipcode' }
%tr
%td
{{ 'country' | t }}
%span.required *
%td
%input.ofn-select2.fullwidth#country_id{ type: 'number',
name: 'country_id', required: true,
placeholder: "{{ 'admin.customers.index.select_country' | t }}",
data: 'availableCountries', ng: { model: 'address.country_id' } }
%input.ofn-select2.fullwidth#country_id{ type: 'number', name: 'country_id', required: true, placeholder: "{{ 'admin.customers.index.select_country' | t }}", data: 'availableCountries', "ng-model": 'address.country_id' }
%tr
%td
{{ 'state' | t }}
%span.required *
%td
%input.ofn-select2.fullwidth#state_id{ type: 'number',
name: 'state_id', required: true,
placeholder: "{{ 'admin.customers.index.select_state' | t }}",
data: 'states', ng: { model: 'address.state_id' } }
%input.ofn-select2.fullwidth#state_id{ type: 'number', name: 'state_id', required: true, placeholder: "{{ 'admin.customers.index.select_state' | t }}", data: 'states', "ng-model": 'address.state_id' }
%tr
%td
{{ 'phone' | t }}
%span.required *
%td
%input{ type: 'text', name: 'phone', required: true, ng: { model: 'address.phone'} }
%input{ type: 'text', name: 'phone', required: true, "ng-model": 'address.phone' }
.text-center
%input.button.red.icon-plus{ type: 'submit', value: t('admin.customers.index.update_address')}

View File

@@ -1,9 +1,9 @@
#info-dialog{ ng: { class: "dialog_class" } }
#info-dialog{ "ng-class": "dialog_class" }
.message.clearfix.margin-bottom-30
.icon.text-center
%i{ ng: { class: "icon_class" } }
%i{ "ng-class": "icon_class" }
.text
{{ message }}
.action-buttons.text-center
%button{ ng: { click: "close()" } }
%button{ "ng-click": "close()" }
= t(:ok)

View File

@@ -1,15 +1,15 @@
%h4.modal-title
= t('js.admin.orders.index.compiling_invoices')
%p.message{ ng: { show: 'message' } }
%p.message{ "ng-show": 'message' }
{{message}}
%p.error{ ng: { show: 'error' } }
%p.error{ "ng-show": 'error' }
{{error}}
%img.spinner{ src: image_path("/spinning-circles.svg"), ng: { show: "loading" } }
%p{ ng: { show: "loading" } }
%img.spinner{ src: image_path("/spinning-circles.svg"), "ng-show": "loading" }
%p{ "ng-show": "loading" }
= t('js.admin.orders.index.please_wait')
%a.button{ target: '_blank', ng: { click: 'showBulkInvoice()', href: '/admin/orders/invoices/{{invoice_id}}', show: "!loading && !error" } }
%a.button{ target: '_blank', "ng-click": 'showBulkInvoice()', "ng-href": '/admin/orders/invoices/{{invoice_id}}', "ng-show": "!loading && !error" }
= t('js.admin.orders.index.view_file')

View File

@@ -1,10 +1,10 @@
%a.close-reveal-modal{"ng-click" => "$close()"}
%i.fa.fa-times-circle{'aria-hidden' => "true"}
%form#image_upload{ name: 'form', novalidate: true, enctype: 'multipart/form-data', multipart: true, ng: { controller: "ProductImageCtrl" } }
%form#image_upload{ name: 'form', novalidate: true, enctype: 'multipart/form-data', multipart: true, "ng-controller": "ProductImageCtrl" }
%div.image-preview
%img.spinner{ src: image_path("/spinning-circles.svg"), ng: { hide: "!imageUploader.isUploading" }}
%img.preview{ng: {src: "{{imagePreview}}", class: "{'faded': imageUploader.isUploading}"}}
%img.spinner{ src: image_path("/spinning-circles.svg"), "ng-hide": "!imageUploader.isUploading" }
%img.preview{ "ng-src": "{{imagePreview}}", "ng-class": "{'faded': imageUploader.isUploading}" }
%label{for: 'image-upload', class: 'button'} {{ 'admin.products.index.upload_an_image' | t }}
%input#image-upload{hidden: true, type: 'file', 'nv-file-select' => true, uploader: "imageUploader"}

View File

@@ -2,14 +2,14 @@
.text-normal.margin-bottom-30.text-center
{{ 'js.admin.customers.index.add_a_new_customer_for' | t:{ shop_name: CurrentShop.shop.name } }}
%form{ name: 'new_customer_form', novalidate: true, ng: { submit: "addCustomer()" }}
%form{ name: 'new_customer_form', novalidate: true, "ng-submit": "addCustomer()" }
.text-center.margin-bottom-30
%input.fullwidth{ type: 'email', name: 'email', required: true, placeholder: "{{ 'js.admin.customers.index.customer_placeholder' | t }}", ng: { model: "email" } }
%div{ ng: { show: "submitted && new_customer_form.$pristine" } }
.error{ ng: { show: "(new_customer_form.email.$error.email || new_customer_form.email.$error.required)" } }
%input.fullwidth{ type: 'email', name: 'email', required: true, placeholder: "{{ 'js.admin.customers.index.customer_placeholder' | t }}", "ng-model": "email" }
%div{ "ng-show": "submitted && new_customer_form.$pristine" }
.error{ "ng-show": "(new_customer_form.email.$error.email || new_customer_form.email.$error.required)" }
{{ 'js.admin.customers.index.valid_email_error' | t }}
.error{ ng: { repeat: "error in errors", bind: "error" } }
.error{ "ng-repeat": "error in errors", "ng-bind": "error" }
.text-center
%input.button.red.icon-plus{ type: 'submit', value: "{{ 'js.admin.customers.index.add_customer' | t }}" }

View File

@@ -4,7 +4,7 @@
.text-center.margin-bottom-30
-# %select.fullwidth{ 'select2-min-search' => 5, 'ng-model' => 'newRuleType', 'ng-options' => 'ruleType.id as ruleType.name for ruleType in availableRuleTypes' }
%input.ofn-select2.fullwidth{ :id => 'rule_type_selector', ng: { model: "ruleType" }, data: "ruleTypes", 'min-search' => "5" }
%input.ofn-select2.fullwidth{ id: 'rule_type_selector', data: "ruleTypes", "min-search": "5", "ng-model": "ruleType" }
.text-center
%input.button.red.icon-plus{ type: 'button', value: "{{ 'js.admin.new_tag_rule_dialog.add_rule' | t }}", ng: { click: 'addRule(tagGroup, ruleType)' } }
%input.button.red.icon-plus{ type: 'button', value: "{{ 'js.admin.new_tag_rule_dialog.add_rule' | t }}", "ng-click": 'addRule(tagGroup, ruleType)' }

View File

@@ -3,16 +3,16 @@
%td#available-order-cycles
{{ 'js.admin.order_cycles.schedules.available' | t }}
.order-cycles
.order-cycle{ ng: { repeat: 'orderCycle in orderCycles | available:selectedOrderCycles as availableOrderCycles', click: 'selections.available = orderCycle', dblclick: 'add(orderCycle)', class: '{selected: selections.available == orderCycle}' } }
.order-cycle{ "ng-repeat": 'orderCycle in orderCycles | available:selectedOrderCycles as availableOrderCycles', "ng-click": 'selections.available = orderCycle', "ng-dblclick": 'add(orderCycle)', "ng-class": '{selected: selections.available == orderCycle}' }
{{ orderCycle.name }}
%td#add-remove-buttons
%a.add.button{ href: 'javascript:void(0)', ng: { click: 'add()' } }
%a.add.button{ href: 'javascript:void(0)', "ng-click": 'add()' }
%i.icon-chevron-right
%a.remove.button{ href: 'javascript:void(0)', ng: { click: 'remove()' } }
%a.remove.button{ href: 'javascript:void(0)', "ng-click": 'remove()' }
%i.icon-chevron-left
%td#selected-order-cycles
{{ 'js.admin.order_cycles.schedules.selected' | t }}
.order-cycles
.order-cycle{ ng: { repeat: 'orderCycle in selectedOrderCycles', click: 'selections.selected = orderCycle', dblclick: 'remove(orderCycle)', class: '{selected: selections.selected == orderCycle}' } }
.order-cycle{ "ng-repeat": 'orderCycle in selectedOrderCycles', "ng-click": 'selections.selected = orderCycle', "ng-dblclick": 'remove(orderCycle)', "ng-class": '{selected: selections.selected == orderCycle}' }
{{ orderCycle.name }}
.error{ ng: { repeat: "error in errors", bind: "error" } }
.error{ "ng-repeat": "error in errors", "ng-bind": "error" }

View File

@@ -1,2 +1,2 @@
%td{ colspan: "{{columnCount}}", ng: { if: "template" } }
.panel{ ng: { include: "template" } }
%td{ colspan: "{{columnCount}}", "ng-if": "template" }
.panel{ "ng-include": "template" }

View File

@@ -1,7 +1,7 @@
.row.enterprise_package_panel{ ng: { controller: 'indexPackagePanelCtrl' } }
.row.enterprise_package_panel{ "ng-controller": 'indexPackagePanelCtrl' }
.alpha.eight.columns
%div{ ng: { if: "!enterprise.is_primary_producer", switch: "enterprise.sells" } }
.info{ ng: { switch: { when: "none" } } }
%div{ "ng-if": "!enterprise.is_primary_producer", "ng-switch": "enterprise.sells" }
.info{ "ng-switch-when": "none" }
%h3
{{ 'js.admin.panels.enterprise_package.hub_profile' | t }}
@@ -15,7 +15,7 @@
%p
{{ 'js.admin.panels.enterprise_package.hub_profile_text2' | t }}
.info{ ng: { switch: { when: "any" } } }
.info{ "ng-switch-when": "any" }
%h3
{{ 'js.admin.panels.enterprise_package.hub_shop' | t }}
@@ -28,7 +28,7 @@
%p
{{ 'js.admin.panels.enterprise_package.hub_shop_text3' | t }}
.info{ ng: { switch: { default: true } } }
.info{ "ng-switch-default": true }
%h3
{{ 'js.admin.panels.enterprise_package.choose_package' | t }}
%i.icon-arrow-right
@@ -40,8 +40,8 @@
%p
{{ 'js.admin.panels.enterprise_package.choose_package_text2' | t }}
%div{ ng: { if: "enterprise.is_primary_producer", switch: "enterprise.sells" } }
.info{ ng: { switch: { when: "none" } } }
%div{ "ng-if": "enterprise.is_primary_producer", "ng-switch": "enterprise.sells" }
.info{ "ng-switch-when": "none" }
%h3
{{ 'js.admin.panels.enterprise_package.profile_only' | t }}
@@ -58,7 +58,7 @@
%p
{{ 'js.admin.panels.enterprise_package.profile_only_text3' | t }}
.info{ ng: { switch: { when: "own" } } }
.info{ "ng-switch-when": "own" }
%h3
{{ 'js.admin.panels.enterprise_package.producer_shop' | t }}
@@ -68,7 +68,7 @@
%p
{{ 'js.admin.panels.enterprise_package.producer_shop_text2' | t }}
.info{ ng: { switch: { when: "any" } } }
.info{ "ng-switch-when": "any" }
%h3
{{ 'js.admin.panels.enterprise_package.producer_hub' | t }}
@@ -81,7 +81,7 @@
%p
{{ 'js.admin.panels.enterprise_package.producer_hub_text3' | t }}
.info{ ng: { switch: { default: true } } }
.info{ "ng-switch-default": true }
%h3
{{ 'js.admin.panels.enterprise_package.choose_package' | t }}
%i.icon-arrow-right
@@ -93,9 +93,9 @@
%p
{{ 'js.admin.panels.enterprise_package.choose_package_text2' | t }}
.omega.eight.columns{ ng: { switch: "enterprise.is_primary_producer" } }
%div{ ng: { switch: { when: "false" } } }
%a.button.selector.hub-profile{ ng: { click: "enterprise.owned && (enterprise.sells='none')", class: "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" } }
.omega.eight.columns{ "ng-switch": "enterprise.is_primary_producer" }
%div{ "ng-switch-when": "false" }
%a.button.selector.hub-profile{ "ng-click": "enterprise.owned && (enterprise.sells='none')", "ng-class": "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" }
.top
%h3
{{ 'js.admin.panels.enterprise_package.profile_only' | t }}
@@ -103,15 +103,15 @@
{{ 'js.admin.panels.enterprise_package.get_listing' | t }}
.bottom
{{ 'js.admin.panels.enterprise_package.always_free' | t }}
%a.button.selector.hub{ ng: { click: "enterprise.owned && (enterprise.sells='any')", class: "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" } }
%a.button.selector.hub{ "ng-click": "enterprise.owned && (enterprise.sells='any')", "ng-class": "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" }
.top
%h3
{{ 'js.admin.panels.enterprise_package.hub_shop' | t }}
%p
{{ 'js.admin.panels.enterprise_package.sell_produce_others' | t }}
%div{ ng: { switch: { when: "true" } } }
%a.button.selector.producer-profile{ ng: { click: "enterprise.owned && (enterprise.sells='none')", class: "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" } }
%div{ "ng-switch-when": "true" }
%a.button.selector.producer-profile{ "ng-click": "enterprise.owned && (enterprise.sells='none')", "ng-class": "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" }
.top
%h3
{{ 'js.admin.panels.enterprise_package.profile_only' | t }}
@@ -119,27 +119,27 @@
{{ 'js.admin.panels.enterprise_package.get_listing' | t }}
.bottom
{{ 'js.admin.panels.enterprise_package.always_free' | t }}
%a.button.selector.producer-shop{ ng: { click: "enterprise.owned && (enterprise.sells='own')", class: "{selected: enterprise.sells=='own', disabled: !enterprise.owned}" } }
%a.button.selector.producer-shop{ "ng-click": "enterprise.owned && (enterprise.sells='own')", "ng-class": "{selected: enterprise.sells=='own', disabled: !enterprise.owned}" }
.top
%h3
{{ 'js.admin.panels.enterprise_package.producer_shop' | t }}
%p
{{ 'js.admin.panels.enterprise_package.sell_own_produce' | t }}
%a.button.selector.producer-hub{ ng: { click: "enterprise.owned && (enterprise.sells='any')", class: "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" } }
%a.button.selector.producer-hub{ "ng-click": "enterprise.owned && (enterprise.sells='any')", "ng-class": "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" }
.top
%h3
{{ 'js.admin.panels.enterprise_package.producer_hub' | t }}
%p
{{ 'js.admin.panels.enterprise_package.sell_both' | t }}
%a.button.update.fullwidth{ ng: { show: "enterprise.owned", class: "{disabled: saved() && !saving, saving: saving}", click: "save()" } }
%span{ ng: {hide: "saved() || saving" } }
%a.button.update.fullwidth{ "ng-show": "enterprise.owned", "ng-class": "{disabled: saved() && !saving, saving: saving}", "ng-click": "save()" }
%span{ "ng-hide": "saved() || saving" }
{{ 'js.admin.panels.save' | t }}
%i.icon-save
%span{ ng: {show: "saved() && !saving" } }
%span{ "ng-show": "saved() && !saving" }
{{ 'js.admin.panels.saved' | t }}
%i.icon-ok-sign
%span{ ng: {show: "saving" } }
%span{ "ng-show": "saving" }
{{ 'js.admin.panels.saving' | t }}
%i.icon-refresh

View File

@@ -1,6 +1,6 @@
.row.enterprise_producer_panel{ ng: { controller: 'indexProducerPanelCtrl' } }
.row.enterprise_producer_panel{ "ng-controller": 'indexProducerPanelCtrl' }
.alpha.eight.columns
.info{ ng: { show: "enterprise.is_primary_producer==true" } }
.info{ "ng-show": "enterprise.is_primary_producer==true" }
%h3
{{ 'js.admin.panels.enterprise_producer.producer' | t }}
%p
@@ -8,7 +8,7 @@
%p
{{ 'js.admin.panels.enterprise_producer.producer_text2' | t }}
.info{ ng: { show: "enterprise.is_primary_producer==false" } }
.info{ "ng-show": "enterprise.is_primary_producer==false" }
%h3
{{ 'js.admin.panels.enterprise_producer.non_producer' | t }}
%p
@@ -17,7 +17,7 @@
{{ 'js.admin.panels.enterprise_producer.non_producer_text2' | t }}
.omega.eight.columns
%a.button.selector.producer{ ng: { click: 'enterprise.owned && changeToProducer()', class: "{selected: enterprise.is_primary_producer==true, disabled: !enterprise.owned}" } }
%a.button.selector.producer{ "ng-click": 'enterprise.owned && changeToProducer()', "ng-class": "{selected: enterprise.is_primary_producer==true, disabled: !enterprise.owned}" }
.top
%h3
{{ 'js.admin.panels.enterprise_producer.producer' | t }}
@@ -26,7 +26,7 @@
.bottom
{{ 'js.admin.panels.enterprise_producer.producer_example' | t }}
%a.button.selector.non-producer{ ng: { click: 'enterprise.owned && changeToNonProducer()', class: "{selected: enterprise.is_primary_producer==false, disabled: !enterprise.owned}" } }
%a.button.selector.non-producer{ "ng-click": 'enterprise.owned && changeToNonProducer()', "ng-class": "{selected: enterprise.is_primary_producer==false, disabled: !enterprise.owned}" }
.top
%h3
{{ 'js.admin.panels.enterprise_producer.non_producer' | t }}
@@ -35,13 +35,13 @@
.bottom
{{ 'js.admin.panels.enterprise_producer.non_producer_example' | t }}
%a.button.update.fullwidth{ ng: { show: "enterprise.owned", class: "{disabled: saved() && !saving, saving: saving}", click: "save()" } }
%span{ ng: {hide: "saved() || saving" } }
%a.button.update.fullwidth{ "ng-show": "enterprise.owned", "ng-class": "{disabled: saved() && !saving, saving: saving}", "ng-click": "save()" }
%span{ "ng-hide": "saved() || saving" }
{{ 'js.admin.panels.save' | t }}
%i.icon-save
%span{ ng: {show: "saved() && !saving" } }
%span{ "ng-show": "saved() && !saving" }
{{ 'js.admin.panels.saved' | t }}
%i.icon-ok-sign
%span{ ng: {show: "saving" } }
%span{ "ng-show": "saving" }
{{ 'js.admin.panels.saving' | t }}
%i.icon-refresh

View File

@@ -1,10 +1,10 @@
.row.enterprise_status_panel{ ng: { controller: 'indexStatusPanelCtrl' } }
.row.enterprise_status_panel{ "ng-controller": 'indexStatusPanelCtrl' }
.alpha.omega.sixteen.columns
%h4.status-ok.text-center{ ng: { show: "issues.length == 0 && warnings.length == 0" } }
%h4.status-ok.text-center{ "ng-show": "issues.length == 0 && warnings.length == 0" }
%i.icon-ok-sign
{{ 'js.admin.panels.enterprise_status.status_title' | t:{ name: object.name } }}
%table{ ng: { show: "issues.length > 0 || warnings.length > 0" } }
%table{ "ng-show": "issues.length > 0 || warnings.length > 0" }
%thead
%th.severity
{{ 'js.admin.panels.enterprise_status.severity' | t }}
@@ -12,17 +12,17 @@
{{ 'js.admin.panels.enterprise_status.description' | t }}
%th.resolve
{{ 'js.admin.panels.enterprise_status.resolve' | t }}
%tr{ ng: { repeat: "issue in issues"} }
%tr{ "ng-repeat": "issue in issues" }
%td.severity
%i.icon-warning-sign.issue
%td.description
%span{ ng: { bind: "::issue.description" } }
%span{ "ng-bind": "::issue.description" }
%td.resolve
%div{ ng: { bind: { html: "issue.link" } } }
%tr{ ng: { repeat: "warning in warnings"} }
%div{ "ng-bind-html": "issue.link" }
%tr{ "ng-repeat": "warning in warnings" }
%td.severity
%i.icon-warning-sign.warning
%td.description
%span{ ng: { bind: "::warning.description" } }
%span{ "ng-bind": "::warning.description" }
%td.resolve
%div{ ng: { bind: { html: "warning.link" } } }
%div{ "ng-bind-html": "warning.link" }

View File

@@ -1,9 +1,9 @@
#save-bar.animate-show{ ng: { show: 'dirty || persist || StatusMessage.active()' } }
#save-bar.animate-show{ "ng-show": 'dirty || persist || StatusMessage.active()' }
.container
.seven.columns.alpha
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage == ''", style: 'StatusMessage.statusMessage.style' } }
%h5#status-message{ "ng-show": "StatusMessage.invalidMessage == ''", "ng-style": 'StatusMessage.statusMessage.style' }
{{ StatusMessage.statusMessage.text || "&nbsp;" }}
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage !== ''" }, style: 'color: #C85136' }
%h5#status-message{ style: 'color: #C85136', "ng-show": "StatusMessage.invalidMessage !== ''" }
{{ StatusMessage.invalidMessage || "&nbsp;" }}
.nine.columns.omega.text-right{ ng: { transclude: true } }
.nine.columns.omega.text-right{ "ng-transclude": true }

View File

@@ -1,24 +1,24 @@
#schedule-dialog
.text-normal.margin-bottom-30.text-center
%span{ ng: { hide: 'schedule.id' } }
%span{ "ng-hide": 'schedule.id' }
{{ 'js.admin.order_cycles.schedules.adding_a_new_schedule' | t }}
%span{ ng: { show: 'schedule.id' } }
%span{ "ng-show": 'schedule.id' }
{{ 'js.admin.order_cycles.schedules.updating_a_schedule' | t }}
%form{ name: 'schedule_form', novalidate: true, ng: { submit: "submit()" }}
%form{ name: 'schedule_form', novalidate: true, "ng-submit": "submit()" }
.text-center.margin-bottom-20
%input.fullwidth{ type: 'text', name: 'name', required: true, placeholder: "{{ 'js.admin.order_cycles.schedules.schedule_name_placeholder' | t }}", ng: { model: "schedule.name" } }
%div{ ng: { show: "submitted && schedule_form.$pristine" } }
.error{ ng: { show: "(schedule_form.name.$error.required)" } }
%input.fullwidth{ type: 'text', name: 'name', required: true, placeholder: "{{ 'js.admin.order_cycles.schedules.schedule_name_placeholder' | t }}", "ng-model": "schedule.name" }
%div{ "ng-show": "submitted && schedule_form.$pristine" }
.error{ "ng-show": "(schedule_form.name.$error.required)" }
{{ 'js.admin.order_cycles.schedules.name_required_error' | t }}
.order-cycles-selector.text-center.margin-bottom-30
.text-center
%input.button{ type: 'submit', value: "{{ 'js.admin.order_cycles.schedules.create_schedule' | t }}", ng: { hide: 'schedule.id' } }
%input.button{ type: 'submit', value: "{{ 'js.admin.order_cycles.schedules.update_schedule' | t }}", ng: { show: 'schedule.id' } }
%span{ ng: { show: 'schedule.id' } } or
%input.button.red{ type: 'button', value: "{{ 'js.admin.order_cycles.schedules.delete_schedule' | t }}", ng: { show: 'schedule.id', click: 'delete()'} }
%input.button{ type: 'button', value: "{{ 'actions.cancel' | t }}", ng: { click: 'close()' } }
%input.button{ type: 'submit', value: "{{ 'js.admin.order_cycles.schedules.create_schedule' | t }}", "ng-hide": 'schedule.id' }
%input.button{ type: 'submit', value: "{{ 'js.admin.order_cycles.schedules.update_schedule' | t }}", "ng-show": 'schedule.id' }
%span{ "ng-show": 'schedule.id' } or
%input.button.red{ type: 'button', value: "{{ 'js.admin.order_cycles.schedules.delete_schedule' | t }}", "ng-show": 'schedule.id', "ng-click": 'delete()' }
%input.button{ type: 'button', value: "{{ 'actions.cancel' | t }}", "ng-click": 'close()' }

View File

@@ -1,8 +1,8 @@
.tag-template
%div
%span.tag-with-rules{ ng: { if: "data.rules" }, "ofn-with-tip" => "{{ 'admin.tag_has_rules' | t:{num: data.rules} }}" }
%span.tag-with-rules{ "ofn-with-tip": "{{ 'admin.tag_has_rules' | t:{num: data.rules} }}", "ng-if": "data.rules" }
{{$getDisplayText()}}
%span{ ng: { if: "!data.rules" } }
%span{ "ng-if": "!data.rules" }
{{$getDisplayText()}}
%a.remove-button{ ng: {click: "$removeTag()"} }
%a.remove-button{ "ng-click": "$removeTag()" }
&#10006;

View File

@@ -1,11 +1,11 @@
.autocomplete-template
%span.tag-with-rules{ ng: { if: "data.rules" } }
%span.tag-with-rules{ "ng-if": "data.rules" }
{{$getDisplayText()}}
%span.tag-with-rules{ ng: { if: "data.rules == 1" } }
%span.tag-with-rules{ "ng-if": "data.rules == 1" }
&mdash;
{{ 'admin.has_one_rule' | t }}
%span.tag-with-rules{ ng: { if: "data.rules > 1" } }
%span.tag-with-rules{ "ng-if": "data.rules > 1" }
&mdash;
{{ 'admin.has_n_rules' | t:{ num: data.rules } }}
%span{ ng: { if: "!data.rules" } }
%span{ "ng-if": "!data.rules" }
{{$getDisplayText()}}

View File

@@ -1,7 +1,3 @@
%div
%input{ type: "number",
id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_calculator_attributes_preferred_flat_percent",
min: -100,
max: 100,
ng: { model: "rule.calculator.preferred_flat_percent" }, 'invert-number' => true }
%input{ type: "number", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_calculator_attributes_preferred_flat_percent", min: -100, max: 100, "invert-number": true, "ng-model": "rule.calculator.preferred_flat_percent" }
%span.text-normal %

View File

@@ -1,11 +1,5 @@
%div
%input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_order_cycles_visibility",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_order_cycles_visibility]",
ng: { model: "rule.preferred_matched_order_cycles_visibility", if: "!rule.is_default" },
data: 'visibilityOptions', "min-search" => 5 }
%input{ type: "hidden",
id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_order_cycles_visibility",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_order_cycles_visibility]",
ng: { value: "'hidden'", if: "rule.is_default" } }
%span.text-normal{ ng: { if: "rule.is_default" } }
%input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_order_cycles_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_order_cycles_visibility]", data: 'visibilityOptions', "min-search": 5, "ng-model": "rule.preferred_matched_order_cycles_visibility", "ng-if": "!rule.is_default" }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_order_cycles_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_order_cycles_visibility]", "ng-value": "'hidden'", "ng-if": "rule.is_default" }
%span.text-normal{ "ng-if": "rule.is_default" }
=t(:not_visible)

View File

@@ -1,11 +1,5 @@
%div
%input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_payment_methods_visibility",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_payment_methods_visibility]",
ng: { model: "rule.preferred_matched_payment_methods_visibility", if: "!rule.is_default" },
data: 'visibilityOptions', "min-search" => 5 }
%input{ type: "hidden",
id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_payment_methods_visibility",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_payment_methods_visibility]",
ng: { value: "'hidden'", if: "rule.is_default" } }
%span.text-normal{ ng: { if: "rule.is_default" } }
%input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_payment_methods_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_payment_methods_visibility]", data: 'visibilityOptions', "min-search": 5, "ng-model": "rule.preferred_matched_payment_methods_visibility", "ng-if": "!rule.is_default" }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_payment_methods_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_payment_methods_visibility]", "ng-value": "'hidden'", "ng-if": "rule.is_default" }
%span.text-normal{ "ng-if": "rule.is_default" }
= t(:not_visible)

View File

@@ -1,11 +1,5 @@
%div
%input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_variants_visibility",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_variants_visibility]",
ng: { model: "rule.preferred_matched_variants_visibility", if: "!rule.is_default" },
data: 'visibilityOptions', "min-search" => 5 }
%input{ type: "hidden",
id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_variants_visibility",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_variants_visibility]",
ng: { value: "'hidden'", if: "rule.is_default" } }
%span.text-normal{ ng: { if: "rule.is_default" } }
%input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_variants_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_variants_visibility]", data: 'visibilityOptions', "min-search": 5, "ng-model": "rule.preferred_matched_variants_visibility", "ng-if": "!rule.is_default" }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_variants_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_variants_visibility]", "ng-value": "'hidden'", "ng-if": "rule.is_default" }
%span.text-normal{ "ng-if": "rule.is_default" }
= t(:not_visible)

View File

@@ -1,12 +1,6 @@
%div
%input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_shipping_methods_visibility",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_shipping_methods_visibility]",
ng: { model: "rule.preferred_matched_shipping_methods_visibility", if: "!rule.is_default" },
data: 'visibilityOptions', "min-search" => 5 }
%input{ type: "hidden",
id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_shipping_methods_visibility",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_shipping_methods_visibility]",
ng: { value: "'hidden'", if: "rule.is_default" } }
%span.text-normal{ ng: { if: "rule.is_default" } }
%input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_shipping_methods_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_shipping_methods_visibility]", data: 'visibilityOptions', "min-search": 5, "ng-model": "rule.preferred_matched_shipping_methods_visibility", "ng-if": "!rule.is_default" }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_shipping_methods_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_shipping_methods_visibility]", "ng-value": "'hidden'", "ng-if": "rule.is_default" }
%span.text-normal{ "ng-if": "rule.is_default" }
= t(:not_visible)

View File

@@ -6,45 +6,27 @@
%col.actions{ width: "10%" }
%tr
%td
%input{ type: "hidden",
id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_id",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][id]",
ng: { value: "rule.id" } }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_id", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][id]", "ng-value": "rule.id" }
%input{ type: "hidden",
id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_type",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][type]",
ng: { value: "rule.type" } }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_type", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][type]", "ng-value": "rule.type" }
%input{ type: "hidden",
id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_priority",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][priority]",
ng: { value: "tagGroup.startIndex + $index" } }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_priority", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][priority]", "ng-value": "tagGroup.startIndex + $index" }
%input{ type: "hidden",
id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_is_default",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][is_default]",
ng: { value: "rule.is_default" } }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_is_default", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][is_default]", "ng-value": "rule.is_default" }
%input{ type: "hidden",
id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_customer_tags",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_customer_tags]",
ng: { value: "rule.preferred_customer_tags" } }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_customer_tags", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_customer_tags]", "ng-value": "rule.preferred_customer_tags" }
%input{ type: "hidden",
id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_{{opt[rule.type].taggable}}_tags",
name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_{{opt[rule.type].taggable}}_tags]",
ng: { value: "opt[rule.type].tagListFor(rule)" } }
%input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_{{opt[rule.type].taggable}}_tags", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_{{opt[rule.type].taggable}}_tags]", "ng-value": "opt[rule.type].tagListFor(rule)" }
%span.text-normal {{ opt[rule.type].textTop }}
%td
%tags-with-translation{ object: "rule", max: 1, "tags-attr" => "{{opt[rule.type].tagsAttr}}", "tag-list-attr" => "{{opt[rule.type].tagListAttr}}" }
%td.actions{ rowspan: 2 }
%a{ ng: { click: "deleteTagRule(tagGroup || defaultTagGroup, rule)" }, :class => "delete-tag-rule icon-trash no-text" }
%a{ class: "delete-tag-rule icon-trash no-text", "ng-click": "deleteTagRule(tagGroup || defaultTagGroup, rule)" }
%tr
%td
%span.text-normal {{ opt[rule.type].textBottom }}
%td
%div{ ng: { include: "opt[rule.type].inputTemplate"} }
%div{ "ng-include": "opt[rule.type].inputTemplate" }
%hr

View File

@@ -1,10 +1,2 @@
%tags-input{ template: 'admin/tag.html',
"placeholder" => t('admin.order_cycles.form.add_a_tag'),
ng: { model: 'object[tagsAttr]', class: "{'limit-reached': limitReached}"},
on: { tag: { added: 'tagAdded($tag)', removed:'tagRemoved()' } } }
%auto-complete{ ng: { if: "findTags" }, source: "findTags({query: $query})",
template: "admin/tag_autocomplete.html",
"min-length" => "0",
"load-on-focus" => "true",
"load-on-empty" => "true",
"max-results-to-show" => "32"}
%tags-input{ template: 'admin/tag.html', placeholder: t('admin.order_cycles.form.add_a_tag'), "ng-model": 'object[tagsAttr]', "ng-class": "{'limit-reached': limitReached}", "on-tag-added": 'tagAdded($tag)', "on-tag-removed": 'tagRemoved()' }
%auto-complete{ source: "findTags({query: $query})", template: "admin/tag_autocomplete.html", "min-length": "0", "load-on-focus": "true", "load-on-empty": "true", "max-results-to-show": "32", "ng-if": "findTags" }

View File

@@ -22,24 +22,22 @@
.variant-bulk-buy-quantity-label
{{ "js.shopfront.bulk_buy_modal.min_quantity" | t }}
%div
%button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "add(-1)", disabled: "!canAdd(-1)"}}>
%button.bulk-buy-add.variant-quantity{ type: "button", "ng-click": "add(-1)", "ng-disabled": "!canAdd(-1)" }>
-# U+FF0D Fullwidth Hyphen-Minus
%input.bulk-buy.variant-quantity{type: "number", min: "0", max: "{{ available() }}",
ng: {model: "variant.line_item.quantity", max: "Infinity"}}>
%button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "add(1)", disabled: "!canAdd(1)"}}
%input.bulk-buy.variant-quantity{ type: "number", min: "0", max: "{{ available() }}", "ng-model": "variant.line_item.quantity", "ng-max": "Infinity" }>
%button.bulk-buy-add.variant-quantity{ type: "button", "ng-click": "add(1)", "ng-disabled": "!canAdd(1)" }
-# U+FF0B Fullwidth Plus Sign
.columns.small-12.medium-6
.variant-bulk-buy-quantity-label
{{ "js.shopfront.bulk_buy_modal.max_quantity" | t }}
%div
%button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "addMax(-1)", disabled: "!canAddMax(-1)"}}>
%button.bulk-buy-add.variant-quantity{ type: "button", "ng-click": "addMax(-1)", "ng-disabled": "!canAddMax(-1)" }>
-# U+FF0D Fullwidth Hyphen-Minus
%input.bulk-buy.variant-quantity{type: "number", min: "0", max: "{{ available() }}",
ng: {model: "variant.line_item.max_quantity", max: "Infinity"}}>
%button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "addMax(1)", disabled: "!canAddMax(1)"}}
%input.bulk-buy.variant-quantity{ type: "number", min: "0", max: "{{ available() }}", "ng-model": "variant.line_item.max_quantity", "ng-max": "Infinity" }>
%button.bulk-buy-add.variant-quantity{ type: "button", "ng-click": "addMax(1)", "ng-disabled": "!canAddMax(1)" }
-# U+FF0B Fullwidth Plus Sign

View File

@@ -1,3 +1,3 @@
%ul
%active-selector{ ng: { repeat: "selector in allSelectors", show: "ifDefined(selector.fits, true)" } }
%active-selector{ "ng-repeat": "selector in allSelectors", "ng-show": "ifDefined(selector.fits, true)" }
%span{"ng-bind" => "::selector.object.name"}

View File

@@ -5,5 +5,5 @@
.small-12.columns.text-center
{{ helpText }}
.row.text-center
%button.primary.small{ ng: { click: '$close()' } }
%button.primary.small{ "ng-click": '$close()' }
= t(:ok)

View File

@@ -1,4 +1,4 @@
.row.pad-top{ng: { if: 'enterprise.is_distributor' } }
.row.pad-top{ "ng-if": 'enterprise.is_distributor' }
.cta-container.small-12.columns
.row
.small-4.columns
@@ -14,7 +14,7 @@
{{'hubs_delivery' | t}}
.row
.columns.small-12
%a.cta-hub{"ng-href" => "{{::enterprise.path}}#/shop", "ng-attr-target" => "{{ embedded_layout ? '_blank' : undefined}}",
%a.cta-hub{"ng-href" => "{{::enterprise.path}}#/shop_panel", "ng-attr-target" => "{{ embedded_layout ? '_blank' : undefined}}",
"ng-class" => "{primary: enterprise.active, secondary: !enterprise.active}",
"ng-click" => "$close()",
"ofn-change-hub" => "enterprise"}

View File

@@ -12,7 +12,7 @@
.row
.columns.small-12
%a.cta-hub{"ng-repeat" => "hub in enterprise.hubs | filter:{id: '!'+enterprise.id} | orderBy:'-active'",
"ng-href" => "{{::hub.path}}#/shop", "ofn-empties-cart" => "hub",
"ng-href" => "{{::hub.path}}#/shop_panel", "ofn-empties-cart" => "hub",
"ng-class" => "::{primary: hub.active, secondary: !hub.active}",
"ng-click" => "$close()",
"ofn-change-hub" => "hub"}

View File

@@ -1,6 +1,6 @@
.joyride-tip-guide.price_breakdown{ng: {class: "{ in: tt_isOpen, fade: tt_animation }", show: "tt_isOpen"}}
.joyride-tip-guide.price_breakdown{ "ng-class": "{ in: tt_isOpen, fade: tt_animation }", "ng-show": "tt_isOpen" }
%span.joyride-nub.top
.background{ng: {click: "tt_isOpen = false"}}
.background{ "ng-click": "tt_isOpen = false" }
.joyride-content-wrapper
%h6 {{ "js.shopfront.price_breakdown" | t }}
%ul

View File

@@ -1,5 +1,5 @@
.joyride-tip-guide.question-mark-tooltip{class: "{{ context }}", ng: {class: "{ in: tt_isOpen, fade: tt_animation }", show: "tt_isOpen"}}
.background{ng: {click: "tt_isOpen = false"}}
.joyride-tip-guide.question-mark-tooltip{ class: "{{ context }}", "ng-class": "{ in: tt_isOpen, fade: tt_animation }", "ng-show": "tt_isOpen" }
.background{ "ng-click": "tt_isOpen = false" }
.joyride-content-wrapper
{{ key | t }}
%span.joyride-nub.bottom

View File

@@ -1,14 +1,29 @@
# frozen_string_literal: true
class ConfirmModalComponent < ModalComponent
def initialize(id:, confirm_actions: nil, reflex: nil, controller: nil, message: nil,
confirm_reflexes: nil)
# @param actions_alignment_class [String] possible classes: 'justify-space-around', 'justify-end'
def initialize(
id:,
reflex: nil,
controller: nil,
message: nil,
confirm_actions: nil,
confirm_reflexes: nil,
confirm_button_class: :primary,
confirm_button_text: I18n.t('js.admin.modals.confirm'),
cancel_button_text: I18n.t('js.admin.modals.cancel'),
actions_alignment_class: 'justify-space-around'
)
super(id:, close_button: true)
@confirm_actions = confirm_actions
@reflex = reflex
@confirm_reflexes = confirm_reflexes
@controller = controller
@message = message
@confirm_button_class = confirm_button_class
@confirm_button_text = confirm_button_text
@cancel_button_text = cancel_button_text
@actions_alignment_class = actions_alignment_class
end
private

View File

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

View File

@@ -1,4 +0,0 @@
.modal-actions {
display: flex;
justify-content: space-around;
}

View File

@@ -1,6 +1,6 @@
%div{ id: @id, "data-controller": "help-modal", "data-action": "keyup@document->help-modal#closeIfEscapeKey" }
.reveal-modal-bg.fade{ "data-help-modal-target": "background", "data-action": "click->help-modal#close" }
.reveal-modal.fade.small.help-modal{ "data-help-modal-target": "modal" }
.reveal-modal.fade.small.modal-component{ "data-help-modal-target": "modal" }
= content
- if close_button?

View File

@@ -1,10 +0,0 @@
.help-modal {
visibility: visible;
position: fixed;
top: 3em;
}
/* prevent arrow on selected admin menu item appearing above modal */
body.modal-open #admin-menu li.selected a::after {
z-index: 0;
}

View File

@@ -1,9 +1,11 @@
# frozen_string_literal: true
class ModalComponent < ViewComponent::Base
def initialize(id:, close_button: true)
def initialize(id:, close_button: true, instant: false, modal_class: :small)
@id = id
@close_button = close_button
@instant = instant
@modal_class = modal_class
end
private

View File

@@ -0,0 +1,8 @@
%div{ id: @id, "data-controller": "modal", "data-action": "keyup@document->modal#closeIfEscapeKey", "data-modal-instant-value": @instant }
.reveal-modal-bg.fade{ "data-modal-target": "background", "data-action": "click->modal#close" }
.reveal-modal.fade.modal-component{ "data-modal-target": "modal", class: @modal_class }
= content
- if close_button?
.text-center
%input{ class: "button icon-plus #{close_button_class}", type: 'button', value: t('js.admin.modals.close'), "data-action": "click->modal#close" }

View File

@@ -0,0 +1,56 @@
// class name 'modal' is already taken by 'custom-alert' and 'custom-confirm'.
.modal-component {
visibility: visible;
position: fixed;
top: 3em;
min-height: auto; // reset from reveal-modal
&.in {
padding: 1.2rem;
}
h1,
h2,
h3,
h4,
h5,
h6,
p {
margin-bottom: 0.5em;
}
img {
// Ensure image fits in container
max-width: 100%;
height: auto;
}
}
/* prevent arrow on selected admin menu item appearing above modal */
body.modal-open #admin-menu li.selected a::after {
z-index: 0;
}
.modal-actions {
display: flex;
text-align: center; // Ensure text inside fullwidth buttons are centred on small screens
&.justify-space-around {
justify-content: space-around;
}
&.justify-end {
justify-content: flex-end;
input[type="button"] {
margin: 0 5px;
}
@media only screen and (max-width: 1024px) {
flex-direction: column;
justify-content: space-around;
input[type="button"] {
margin: 5px 0;
}
}
}
}

View File

@@ -0,0 +1,8 @@
= render ConfirmModalComponent.new(id: dom_id(@order, :ship), confirm_reflexes: "click->Admin::OrdersReflex#ship", controller: "orders", reflex: "Admin::Orders#ship") do
%div{class: "margin-bottom-30"}
%p= t('spree.admin.orders.shipment.mark_as_shipped_message_html')
%div{class: "margin-bottom-30"}
= hidden_field_tag :id, @order.id
= label_tag do
= check_box_tag :send_shipment_email, "1", true
= t('spree.admin.orders.shipment.mark_as_shipped_label_message')

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
class ShipOrderComponent < ViewComponent::Base
def initialize(order:)
@order = order
end
end

View File

@@ -1,16 +1,13 @@
.vertical-ellipsis-menu {
position: relative;
width: $btn-relaxed-height;
i.fa-ellipsis-v {
cursor: pointer;
display: block;
height: $btn-relaxed-height;
width: $btn-relaxed-height;
line-height: $btn-relaxed-height;
text-align: center;
border-radius: 3px;
background-color: white;
padding: 9px 14px;
}
.vertical-ellipsis-menu-content {
@@ -20,7 +17,7 @@
padding-top: 5px;
padding-bottom: 5px;
background-color: white;
@include defaultBoxShadow;
box-shadow: $box-shadow;
border-radius: 3px;
min-width: 80px;
display: none;

View File

@@ -70,7 +70,7 @@ module Admin
def collection
if json_request? && params[:enterprise_id].present?
CustomersWithBalance.new(customers).query.
CustomersWithBalanceQuery.new(customers).call.
includes(
:enterprise,
{ bill_address: [:state, :country] },

View File

@@ -0,0 +1,50 @@
# frozen_string_literal: true
require "private_address_check"
require "private_address_check/tcpsocket_ext"
module Admin
class DfcProductImportsController < Spree::Admin::BaseController
# Define model class for `can?` permissions:
def model_class
self.class
end
def index
# The plan:
#
# * Fetch DFC catalog as JSON from URL.
enterprise = OpenFoodNetwork::Permissions.new(spree_current_user)
.managed_product_enterprises.is_primary_producer
.find(params.require(:enterprise_id))
catalog_url = params.require(:catalog_url)
json_catalog = DfcRequest.new(spree_current_user).get(catalog_url)
graph = DfcIo.import(json_catalog)
# * First step: import all products for given enterprise.
# * Second step: render table and let user decide which ones to import.
imported = graph.map do |subject|
import_product(subject, enterprise)
end
@count = imported.compact.count
end
private
# 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
variant = SuppliedProductBuilder.import_variant(subject, enterprise)
product = variant.product
product.save! if product.new_record?
variant.save! if variant.new_record?
variant
end
end
end

View File

@@ -38,7 +38,8 @@ module Admin
@enterprise_fee_set = EnterpriseFeesBulkUpdate.new(params)
if @enterprise_fee_set.save
redirect_to redirect_path, notice: I18n.t(:enterprise_fees_update_notice)
flash[:success] = I18n.t(:enterprise_fees_update_notice)
redirect_to redirect_path
else
redirect_to redirect_path,
flash: { error: @enterprise_fee_set.errors.full_messages.to_sentence }

View File

@@ -3,9 +3,8 @@
module Admin
class EnterpriseRolesController < Admin::ResourceController
def index
@enterprise_roles = EnterpriseRole.by_user_email
@users = Spree::User.order('spree_users.email')
@my_enterprises = @all_enterprises = Enterprise.by_name
@enterprise_roles, @users, @all_enterprises = Admin::EnterpriseRolesQuery.query
@my_enterprises = @all_enterprises
end
def create

View File

@@ -2,6 +2,13 @@
module Admin
class OidcSettingsController < Spree::Admin::BaseController
def index; end
def index
@account = spree_current_user.oidc_account
end
def destroy
spree_current_user.oidc_account&.destroy
redirect_to admin_oidc_settings_path
end
end
end

View File

@@ -45,7 +45,8 @@ module Admin
end
def create
@order_cycle_form = OrderCycleForm.new(@order_cycle, order_cycle_params, spree_current_user)
@order_cycle_form = OrderCycles::FormService.new(@order_cycle, order_cycle_params,
spree_current_user)
if @order_cycle_form.save
flash[:success] = t('.success')
@@ -61,7 +62,8 @@ module Admin
end
def update
@order_cycle_form = OrderCycleForm.new(@order_cycle, order_cycle_params, spree_current_user)
@order_cycle_form = OrderCycles::FormService.new(@order_cycle, order_cycle_params,
spree_current_user)
if @order_cycle_form.save
update_nil_subscription_line_items_price_estimate(@order_cycle)
@@ -98,7 +100,7 @@ module Admin
def update_nil_subscription_line_items_price_estimate(order_cycle)
order_cycle.schedules.each do |schedule|
Subscription.where(schedule_id: schedule.id).each do |subscription|
Subscription.where(schedule_id: schedule.id).find_each do |subscription|
shop = Enterprise.managed_by(spree_current_user).find_by(id: subscription.shop_id)
fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(shop, order_cycle)
subscription.subscription_line_items.nil_price_estimate.each do |line_item|

View File

@@ -10,6 +10,8 @@ module Admin
@product_categories = Spree::Taxon.order('name ASC').pluck(:name).uniq
@tax_categories = Spree::TaxCategory.order('name ASC').pluck(:name)
@shipping_categories = Spree::ShippingCategory.order('name ASC').pluck(:name)
@producers = OpenFoodNetwork::Permissions.new(spree_current_user).
managed_product_enterprises.is_primary_producer.by_name.to_a
end
def import

View File

@@ -21,18 +21,10 @@ module Admin
def show
@report = report_class.new(spree_current_user, params, render: render_data?)
@rendering_options = rendering_options # also stores user preferences
@background_reports = OpenFoodNetwork::FeatureToggle
.enabled?(:background_reports, spree_current_user)
if @background_reports && request.post?
rendering_options # stores user preferences
return background(report_format)
end
if params[:report_format].present?
export_report
if render_data?
render_in_background
else
show_report
end
@@ -40,13 +32,8 @@ module Admin
private
def export_report
send_data @report.render_as(report_format), filename: report_filename
end
def show_report
assign_view_data
@table = @report.render_as(:html) if render_data?
render "show"
end
@@ -55,7 +42,6 @@ module Admin
@report_subtypes = report_subtypes
@report_subtype = report_subtype
@report_title = report_title
@rendering_options = rendering_options
@data = Reporting::FrontendData.new(spree_current_user)
variant_id_in = params[:variant_id_in]&.compact_blank
@@ -72,7 +58,7 @@ module Admin
request.post?
end
def background(format)
def render_in_background
cable_ready[ScopedChannel.for_id(params[:uuid])]
.inner_html(
selector: "#report-table",
@@ -84,7 +70,8 @@ module Admin
ReportJob.perform_later(
report_class:, user: spree_current_user, params:,
format:, filename: report_filename,
format: report_format,
filename: report_filename,
channel: ScopedChannel.for_id(params[:uuid]),
)

View File

@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'open_food_network/permissions'
require 'order_management/subscriptions/proxy_order_syncer'
module Admin
class SchedulesController < Admin::ResourceController

View File

@@ -1,7 +1,5 @@
# frozen_string_literal: true
require 'api/admin/enterprise_serializer'
module Api
module V0
class EnterpriseAttachmentController < Api::V0::BaseController

View File

@@ -101,7 +101,8 @@ module Api
end
def distributed_products
OrderCycleDistributedProducts.new(distributor, order_cycle, customer).products_relation
OrderCycles::DistributedProductsService.new(distributor, order_cycle,
customer).products_relation
end
end
end

View File

@@ -47,7 +47,7 @@ module Api
def capture
authorize! :admin, order
payment_capture = OrderCaptureService.new(order)
payment_capture = Orders::CaptureService.new(order)
if payment_capture.call
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok

View File

@@ -21,7 +21,9 @@ module Api
@shipment.refresh_rates
@shipment.save!
OrderWorkflow.new(@order).advance_to_payment if @order.line_items.any?
Orders::WorkflowService.new(@order).advance_to_payment if @order.line_items.any?
@order.recreate_all_fees!
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
end
@@ -83,6 +85,8 @@ module Api
@order.contents.remove(variant, quantity, @shipment, restock_item)
@shipment.reload if @shipment.persisted?
@order.recreate_all_fees!
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
end

View File

@@ -59,7 +59,7 @@ module Api
def customer
@customer ||= if action_name == "show"
CustomersWithBalance.new(Customer.where(id: params[:id])).query.first!
CustomersWithBalanceQuery.new(Customer.where(id: params[:id])).call.first!
else
Customer.find(params[:id])
end
@@ -74,7 +74,7 @@ module Api
customers = customers.where(enterprise_id: params[:enterprise_id]) if params[:enterprise_id]
if @extra_customer_fields.include?(:balance)
customers = CustomersWithBalance.new(customers).query
customers = CustomersWithBalanceQuery.new(customers).call
end
customers.ransack(params[:q]).result.order(:id)

View File

@@ -128,7 +128,7 @@ class CheckoutController < BaseController
def advance_order_state
return if @order.complete?
OrderWorkflow.new(@order).advance_checkout(raw_params.slice(:shipping_method_id))
Orders::WorkflowService.new(@order).advance_checkout(raw_params.slice(:shipping_method_id))
end
def order_params

View File

@@ -65,7 +65,7 @@ module CheckoutCallbacks
def valid_order_line_items?
@order.insufficient_stock_lines.empty? &&
OrderCycleDistributedVariants.new(@order.order_cycle, @order.distributor).
OrderCycles::DistributedVariantsService.new(@order.order_cycle, @order.distributor).
distributes_order_variants?(@order)
end

View File

@@ -64,7 +64,7 @@ module OrderCompletion
return redirect_to order_failed_route(step: 'payment')
end
if OrderWorkflow.new(@order).next && @order.complete?
if Orders::WorkflowService.new(@order).next && @order.complete?
processing_succeeded
redirect_to order_completion_route
else

View File

@@ -6,7 +6,7 @@ module OrderStockCheck
def valid_order_line_items?
@order.insufficient_stock_lines.empty? &&
OrderCycleDistributedVariants.new(@order.order_cycle, @order.distributor).
OrderCycles::DistributedVariantsService.new(@order.order_cycle, @order.distributor).
distributes_order_variants?(@order)
end

View File

@@ -70,7 +70,7 @@ class EnterprisesController < BaseController
order = current_order(true)
# reset_distributor must be called before any call to current_customer or current_distributor
order_cart_reset = OrderCartReset.new(order, params[:id])
order_cart_reset = Orders::CartResetService.new(order, params[:id])
order_cart_reset.reset_distributor
order_cart_reset.reset_other!(spree_current_user, current_customer)
rescue ActiveRecord::RecordNotFound

View File

@@ -2,7 +2,7 @@
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def openid_connect
spree_current_user.link_from_omniauth(request.env["omniauth.auth"])
OidcAccount.link(spree_current_user, request.env["omniauth.auth"])
redirect_to admin_oidc_settings_path
end

View File

@@ -79,7 +79,7 @@ module PaymentGateways
end
def last_payment
@last_payment ||= OrderPaymentFinder.new(@order).last_payment
@last_payment ||= Orders::FindPaymentService.new(@order).last_payment
end
def cancel_incomplete_payments

View File

@@ -9,6 +9,7 @@ module Spree
helper 'admin/injection'
helper 'admin/orders'
helper 'admin/enterprises'
helper 'admin/terms_of_service'
helper 'enterprise_fees'
helper 'angular_form'
@@ -25,7 +26,7 @@ module Spree
def warn_invalid_order_cycles
return if flash[:notice].present?
warning = OrderCycleWarning.new(spree_current_user).call
warning = OrderCycles::WarningService.new(spree_current_user).call
flash[:notice] = warning if warning.present?
end

View File

@@ -32,10 +32,13 @@ module Spree
if @object.save
flash[:success] = flash_message_for(@object, :successfully_created)
redirect_to spree.admin_product_images_url(params[:product_id], @url_filters)
redirect_to location_after_save
else
respond_with(@object)
end
rescue ActiveStorage::IntegrityError
@object.errors.add :attachment, :integrity_error
respond_with(@object)
end
def update
@@ -44,10 +47,13 @@ module Spree
if @object.update(permitted_resource_params)
flash[:success] = flash_message_for(@object, :successfully_updated)
redirect_to spree.admin_product_images_url(params[:product_id], @url_filters)
redirect_to location_after_save
else
respond_with(@object)
end
rescue ActiveStorage::IntegrityError
@object.errors.add :attachment, :integrity_error
respond_with(@object)
end
def destroy
@@ -58,7 +64,7 @@ module Spree
flash[:success] = flash_message_for(@object, :successfully_removed)
end
redirect_to spree.admin_product_images_url(params[:product_id], @url_filters)
redirect_to location_after_save
end
private
@@ -76,7 +82,7 @@ module Spree
end
def location_after_save
spree.admin_product_images_url(@product)
params[:return_url] || spree.admin_product_images_url(params[:product_id], @url_filters)
end
def load_data

View File

@@ -20,7 +20,7 @@ module Spree
def generate
@order = Order.find_by(number: params[:order_id])
authorize! :invoice, @order
OrderInvoiceGenerator.new(@order).generate_or_update_latest_invoice
::Orders::GenerateInvoiceService.new(@order).generate_or_update_latest_invoice
redirect_back(fallback_location: spree.admin_dashboard_path)
end

View File

@@ -24,7 +24,7 @@ module Spree
end
refresh_shipment_rates
OrderWorkflow.new(@order).advance_to_payment
::Orders::WorkflowService.new(@order).advance_to_payment
flash[:success] = Spree.t('customer_details_updated')
redirect_to spree.admin_order_customer_path(@order)

View File

@@ -50,7 +50,7 @@ module Spree
return redirect_to spree.edit_admin_order_path(@order)
end
OrderWorkflow.new(@order).advance_to_payment
::Orders::WorkflowService.new(@order).advance_to_payment
if @order.complete?
redirect_to spree.edit_admin_order_path(@order)
@@ -104,7 +104,7 @@ module Spree
@order = if params[:invoice_id].present?
@order.invoices.find(params[:invoice_id]).presenter
else
OrderInvoiceGenerator.new(@order).generate_or_update_latest_invoice
::Orders::GenerateInvoiceService.new(@order).generate_or_update_latest_invoice
@order.invoices.first.presenter
end
end

View File

@@ -193,7 +193,9 @@ module Spree
def clear_preference_cache
@payment_method.calculator.preferences.each_key do |key|
Rails.cache.delete(@payment_method.calculator.preference_cache_key(key))
# Only persisted models have cache keys.
cache_key = @payment_method.calculator.preference_cache_key(key)
Rails.cache.delete(cache_key) if cache_key
end
end

View File

@@ -33,7 +33,7 @@ module Spree
return
end
OrderWorkflow.new(@order).complete! unless @order.completed?
::Orders::WorkflowService.new(@order).complete! unless @order.completed?
authorize_stripe_sca_payment
@payment.process_offline!
@@ -59,6 +59,8 @@ module Spree
flash[:error] = t(:cannot_perform_operation)
end
rescue StandardError => e
logger.error e.message
Bugsnag.notify(e)
flash[:error] = e.message
ensure
redirect_to request.referer

View File

@@ -82,8 +82,9 @@ module Spree
def clone
@new = @product.duplicate
@new.save
raise "Clone failed" unless @new.save
flash[:success] = t('.success')
redirect_to spree.admin_products_url
end

View File

@@ -117,8 +117,8 @@ module Spree
def taxon_params
params.require(:taxon).permit(
:name, :parent_id, :position, :icon, :description, :permalink,
:taxonomy_id, :meta_description, :meta_keywords, :meta_title
:name, :parent_id, :position, :icon, :description, :permalink, :taxonomy_id,
:meta_description, :meta_keywords, :meta_title, :dfc_id
)
end
end

View File

@@ -42,7 +42,7 @@ module Spree
# Patching to redirect to shop if order is empty
def edit
@insufficient_stock_lines = @order.insufficient_stock_lines
@unavailable_order_variants = OrderCycleDistributedVariants.
@unavailable_order_variants = OrderCycles::DistributedVariantsService.
new(current_order_cycle, current_distributor).unavailable_order_variants(@order)
if @order.line_items.empty?
@@ -102,7 +102,7 @@ module Spree
@order = Spree::Order.find_by!(number: params[:id])
authorize! :cancel, @order
if CustomerOrderCancellation.new(@order).call
if Orders::CustomerCancellationService.new(@order).call
flash[:success] = I18n.t(:orders_your_order_has_been_cancelled)
else
flash[:error] = I18n.t(:orders_could_not_cancel)

View File

@@ -8,6 +8,7 @@ module Spree
layout 'darkswarm'
invisible_captcha only: [:create], on_timestamp_spam: :render_alert_timestamp_error_message
skip_before_action :set_current_order, only: :show
prepend_before_action :load_object, only: [:show, :edit, :update]
prepend_before_action :authorize_actions, only: :new
@@ -15,7 +16,7 @@ module Spree
before_action :set_locale
def show
@payments_requiring_action = PaymentsRequiringAction.new(spree_current_user).query
@payments_requiring_action = PaymentsRequiringActionQuery.new(spree_current_user).call
@orders = orders_collection.includes(:line_items)
customers = spree_current_user.customers
@@ -78,7 +79,7 @@ module Spree
private
def orders_collection
CompleteOrdersWithBalance.new(@user).query
CompleteOrdersWithBalanceQuery.new(@user).call
end
def load_object
@@ -101,5 +102,16 @@ module Spree
def user_params
::PermittedAttributes::User.new(params).call
end
def render_alert_timestamp_error_message
render cable_ready: cable_car.inner_html(
"#signup-feedback",
partial("layouts/alert",
locals: {
type: "alert",
message: InvisibleCaptcha.timestamp_error_message
})
)
end
end
end

View File

@@ -21,15 +21,29 @@ module Admin
show_payment_methods = can?(:manage_payment_methods, enterprise) && is_shop
show_enterprise_fees = can?(:manage_enterprise_fees,
enterprise) && (is_shop || enterprise.is_primary_producer)
show_connected_apps = can?(:manage_connected_apps, enterprise) &&
feature?(:connected_apps, spree_current_user, enterprise)
build_enterprise_side_menu_items(is_shop, show_properties, show_shipping_methods,
show_payment_methods, show_enterprise_fees)
build_enterprise_side_menu_items(
is_shop:,
show_properties:,
show_shipping_methods:,
show_payment_methods:,
show_enterprise_fees:,
show_connected_apps:,
)
end
private
def build_enterprise_side_menu_items(is_shop, show_properties, show_shipping_methods,
show_payment_methods, show_enterprise_fees)
def build_enterprise_side_menu_items(
is_shop:,
show_properties:,
show_shipping_methods:,
show_payment_methods:,
show_enterprise_fees:,
show_connected_apps:
)
[
{ name: 'primary_details', icon_class: "icon-home", show: true, selected: 'selected' },
{ name: 'address', icon_class: "icon-map-marker", show: true },
@@ -50,6 +64,7 @@ module Admin
{ name: 'shop_preferences', icon_class: "icon-shopping-cart", show: is_shop },
{ name: 'users', icon_class: "icon-user", show: true },
{ name: 'white_label', icon_class: "icon-leaf", show: true },
{ name: 'connected_apps', icon_class: "icon-puzzle-piece", show: show_connected_apps },
]
end
end

View File

@@ -27,13 +27,6 @@ module Admin
Api::Admin::EnterpriseRelationshipSerializer
end
def admin_inject_enterprise_roles(enterprise_roles)
admin_inject_json_ams_array "ofn.admin",
"enterpriseRoles",
enterprise_roles,
Api::Admin::EnterpriseRoleSerializer
end
def admin_inject_payment_methods(payment_methods)
admin_inject_json_ams_array "admin.paymentMethods",
"paymentMethods",
@@ -137,13 +130,6 @@ module Admin
Api::Admin::TaxonSerializer
end
def admin_inject_users(users)
admin_inject_json_ams_array "ofn.admin",
"users",
users,
Api::Admin::UserSerializer
end
def admin_inject_variant_overrides(variant_overrides)
admin_inject_json_ams_array "admin.variantOverrides",
"variantOverrides",

View File

@@ -0,0 +1,25 @@
# frozen_string_literal: true
module Admin
module TermsOfServiceHelper
def tos_need_accepting?
return false unless spree_user_signed_in?
return false if Spree::Config.enterprises_require_tos == false
return false if TermsOfServiceFile.current.nil?
!accepted_tos?
end
private
def accepted_tos?
file_uploaded_at = TermsOfServiceFile.updated_at
current_spree_user.terms_of_service_accepted_at.present? &&
current_spree_user.terms_of_service_accepted_at > file_uploaded_at &&
current_spree_user.terms_of_service_accepted_at < DateTime.now
end
end
end

View File

@@ -9,7 +9,7 @@ module AngularFormHelper
text, value = option_text_and_value(element).map(&:to_s)
%(<option value="#{ERB::Util.html_escape(value)}"\
#{html_attributes}>#{ERB::Util.html_escape(text)}</option>)
end.join("\n").html_safe
end.join("\n").html_safe # rubocop:disable Rails/OutputSafety
end
def ng_options_from_collection_for_select(collection, value_method, text_method, angular_field)

View File

@@ -10,7 +10,7 @@ module ApplicationHelper
return "" unless obj && obj.errors[method].present?
errors = obj.errors[method].map { |err| h(err) }.join('<br />').html_safe
errors = obj.errors[method].map { |err| h(err) }.join('<br />').html_safe # rubocop:disable Rails/OutputSafety
if options[:standalone]
content_tag(
@@ -36,7 +36,7 @@ module ApplicationHelper
hreflang: locale.to_s.gsub("_", "-").downcase,
href: "#{request.protocol}#{request.host_with_port}/locales/#{locale}"
)
end.join("\n").html_safe
end.join("\n").html_safe # rubocop:disable Rails/OutputSafety
end
def ng_form_for(name, *args, &)

View File

@@ -59,7 +59,7 @@ module CheckoutHelper
end
def display_checkout_taxes_hash(order)
totals = OrderTaxAdjustmentsFetcher.new(order).totals
totals = Orders::FetchTaxAdjustmentsService.new(order).totals
totals.map do |tax_rate, tax_amount|
{
@@ -148,4 +148,10 @@ module CheckoutHelper
]
end
end
# Set the Page title of checkout process as step based like
# Checkout Details, Checkout Payment and Checkout Summary
def checkout_page_title
t("checkout_#{checkout_step}_title")
end
end

View File

@@ -12,11 +12,11 @@ module EnterprisesHelper
end
def available_shipping_methods
OrderAvailableShippingMethods.new(current_order, current_customer).to_a
Orders::AvailableShippingMethodsService.new(current_order, current_customer).to_a
end
def available_payment_methods
OrderAvailablePaymentMethods.new(current_order, current_customer).to_a
Orders::AvailablePaymentMethodsService.new(current_order, current_customer).to_a
end
def managed_enterprises

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