Merge branch 'master' into prevent-generate-invoices-when-distributor-cannot-invoice

This commit is contained in:
Mohamed ABDELLANI
2024-04-22 10:55:39 +01:00
committed by GitHub
840 changed files with 20817 additions and 12021 deletions

View File

@@ -14,3 +14,4 @@ SITE_URL="test.host"
OPENID_APP_ID="test-provider"
OPENID_APP_SECRET="12345"
OPENID_REFRESH_TOKEN="dummy-refresh-token"

View File

@@ -1,3 +1,8 @@
# Dependabot configuration
#
# The `directory` and `schedule.interval` options are mandatory.
# Most of the configuration here is not used for security updates though.
version: 2
updates:
@@ -5,7 +10,7 @@ updates:
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10
# Only specific requirements are specified in Gemfile, so don't touch it.
versioning-strategy: lockfile-only
@@ -13,5 +18,6 @@ updates:
directory: "/"
schedule:
interval: "daily"
# All versions are specified in package.json, so please update them.
versioning-strategy: increase

View File

@@ -7,10 +7,11 @@ jobs:
name: runner / rubocop
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v1
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- run: git show --no-patch # the commit being tested (which is often a merge due to actions/checkout@v3)
@@ -21,6 +22,8 @@ jobs:
rubocop_extensions: rubocop-rails:gemfile rubocop-rspec:gemfile
reporter: github-pr-check
level: error
filter_mode: nofilter
use_bundler: true
fail_on_error: true
prettier:
name: runner / prettier

View File

@@ -10,12 +10,15 @@ RSpec:
FactoryBot:
Enabled: false
# Enabled rules
Capybara/NegationMatcher:
Enabled: true
EnforcedStyle: not_to
RSpec/ExpectChange:
Enabled: true
EnforcedStyle: block
RSpec/MultipleExpectations:
Max: 5 # Default 1
RSpec/MultipleMemoizedHelpers:
Max: 10 # Default 5
RSpec/NotToNot:
Enabled: true

View File

@@ -3,7 +3,6 @@
# These are the rules we agreed upon and we work towards.
AllCops:
NewCops: enable
SuggestExtensions: false
Exclude:
- bin/**/*
- db/**/*
@@ -125,6 +124,9 @@ Rails/SkipsModelValidations:
- update_column
- update_columns
Rails/WhereExists:
EnforcedStyle: where # Cf. conversion https://github.com/openfoodfoundation/openfoodnetwork/pull/12363
Style/Documentation:
Enabled: false

View File

@@ -1,50 +1,17 @@
# This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 1400 --no-auto-gen-timestamp`
# using RuboCop version 1.60.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: 16
# Configuration parameters: AllowedMethods.
# AllowedMethods: enums
Lint/ConstantDefinitionInBlock:
Exclude:
- 'lib/tasks/import_product_images.rake'
- 'lib/tasks/users.rake'
- 'spec/controllers/spree/admin/base_controller_spec.rb'
- 'spec/helpers/serializer_helper_spec.rb'
- 'spec/lib/reports/line_items_spec.rb'
- 'spec/models/spree/ability_spec.rb'
- 'spec/models/spree/gateway_spec.rb'
- 'spec/models/spree/preferences/configuration_spec.rb'
- 'spec/models/spree/preferences/preferable_spec.rb'
- 'spec/validators/date_time_string_validator_spec.rb'
- 'spec/validators/integer_array_validator_spec.rb'
# Offense count: 6
# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches.
Lint/DuplicateBranch:
Exclude:
- 'app/helpers/spree/admin/base_helper.rb'
- 'app/models/enterprise.rb'
- 'app/models/spree/calculator.rb'
- 'app/models/spree/preference.rb'
- 'app/models/spree/preferences/preferable.rb'
# Offense count: 2
Lint/DuplicateMethods:
Exclude:
- 'lib/discourse/single_sign_on.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
Lint/DuplicateRequire:
Exclude:
- 'spec/lib/open_food_network/scope_variants_to_search_spec.rb'
# Offense count: 18
# Offense count: 16
# Configuration parameters: AllowComments, AllowEmptyLambdas.
Lint/EmptyBlock:
Exclude:
@@ -113,6 +80,7 @@ Lint/SelfAssignment:
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AutoCorrect.
Lint/UselessMethodDefinition:
Exclude:
- 'app/models/spree/gateway.rb'
@@ -141,7 +109,7 @@ 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: 9
# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
@@ -200,8 +168,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/order_cycles/form_service.rb'
- 'app/services/orders/sync_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'
@@ -389,20 +357,213 @@ Naming/VariableNumber:
- 'spec/models/spree/tax_rate_spec.rb'
- 'spec/requests/api/orders_spec.rb'
# Offense count: 11
# Offense count: 142
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowedMethods, AllowedPatterns.
# AllowedMethods: order, limit, select, lock
Rails/FindEach:
# Configuration parameters: ResponseMethods.
# ResponseMethods: response, last_response
RSpecRails/HaveHttpStatus:
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'
- 'spec/controllers/admin/bulk_line_items_controller_spec.rb'
- 'spec/controllers/admin/order_cycles_controller_spec.rb'
- 'spec/controllers/admin/subscriptions_controller_spec.rb'
- 'spec/controllers/api/v0/base_controller_spec.rb'
- 'spec/controllers/api/v0/customers_controller_spec.rb'
- 'spec/controllers/api/v0/enterprises_controller_spec.rb'
- 'spec/controllers/api/v0/logos_controller_spec.rb'
- 'spec/controllers/api/v0/order_cycles_controller_spec.rb'
- 'spec/controllers/api/v0/orders_controller_spec.rb'
- 'spec/controllers/api/v0/product_images_controller_spec.rb'
- 'spec/controllers/api/v0/products_controller_spec.rb'
- 'spec/controllers/api/v0/promo_images_controller_spec.rb'
- 'spec/controllers/api/v0/reports/packing_report_spec.rb'
- 'spec/controllers/api/v0/reports_controller_spec.rb'
- 'spec/controllers/api/v0/shipments_controller_spec.rb'
- 'spec/controllers/api/v0/statuses_controller_spec.rb'
- 'spec/controllers/api/v0/taxons_controller_spec.rb'
- 'spec/controllers/api/v0/terms_and_conditions_controller_spec.rb'
- 'spec/controllers/api/v0/variants_controller_spec.rb'
- 'spec/controllers/cart_controller_spec.rb'
- 'spec/controllers/checkout_controller_spec.rb'
- 'spec/controllers/enterprises_controller_spec.rb'
- 'spec/controllers/line_items_controller_spec.rb'
- 'spec/controllers/shop_controller_spec.rb'
- 'spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb'
- 'spec/controllers/spree/admin/orders_controller_spec.rb'
- 'spec/controllers/spree/admin/products_controller_spec.rb'
- 'spec/controllers/spree/credit_cards_controller_spec.rb'
- 'spec/controllers/spree/orders_controller_spec.rb'
- 'spec/controllers/stripe/callbacks_controller_spec.rb'
- 'spec/controllers/stripe/webhooks_controller_spec.rb'
- 'spec/controllers/user_passwords_controller_spec.rb'
- 'spec/controllers/user_registrations_controller_spec.rb'
- 'spec/requests/admin/images_spec.rb'
- 'spec/requests/api/routes_spec.rb'
- 'spec/requests/checkout/failed_checkout_spec.rb'
- 'spec/requests/checkout/stripe_sca_spec.rb'
- 'spec/requests/home_controller_spec.rb'
- 'spec/requests/omniauth_callbacks_controller_spec.rb'
- 'spec/services/embedded_page_service_spec.rb'
- 'spec/support/api_helper.rb'
# Offense count: 8
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: numeric, symbolic, be_status
RSpecRails/HttpStatus:
Exclude:
- 'spec/controllers/spree/admin/products_controller_spec.rb'
- 'spec/requests/api/orders_spec.rb'
# Offense count: 146
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Inferences.
RSpecRails/InferredSpecType:
Exclude:
- 'engines/dfc_provider/spec/requests/addresses_spec.rb'
- 'engines/dfc_provider/spec/requests/catalog_items_spec.rb'
- 'engines/dfc_provider/spec/requests/enterprise_groups/affiliated_by_spec.rb'
- 'engines/dfc_provider/spec/requests/enterprise_groups_spec.rb'
- 'engines/dfc_provider/spec/requests/enterprises_spec.rb'
- 'engines/dfc_provider/spec/requests/offers_spec.rb'
- 'engines/dfc_provider/spec/requests/persons_spec.rb'
- 'engines/dfc_provider/spec/requests/social_medias_spec.rb'
- 'engines/dfc_provider/spec/requests/supplied_products_spec.rb'
- 'engines/web/spec/helpers/cookies_policy_helper_spec.rb'
- 'spec/controllers/admin/bulk_line_items_controller_spec.rb'
- 'spec/controllers/admin/column_preferences_controller_spec.rb'
- 'spec/controllers/admin/customers_controller_spec.rb'
- 'spec/controllers/admin/enterprises_controller_spec.rb'
- 'spec/controllers/admin/inventory_items_controller_spec.rb'
- 'spec/controllers/admin/invoice_settings_controller_spec.rb'
- 'spec/controllers/admin/matomo_settings_controller_spec.rb'
- 'spec/controllers/admin/order_cycles_controller_spec.rb'
- 'spec/controllers/admin/product_import_controller_spec.rb'
- 'spec/controllers/admin/proxy_orders_controller_spec.rb'
- 'spec/controllers/admin/reports_controller_spec.rb'
- 'spec/controllers/admin/schedules_controller_spec.rb'
- 'spec/controllers/admin/stripe_accounts_controller_spec.rb'
- 'spec/controllers/admin/stripe_connect_settings_controller_spec.rb'
- 'spec/controllers/admin/subscription_line_items_controller_spec.rb'
- 'spec/controllers/admin/subscriptions_controller_spec.rb'
- 'spec/controllers/admin/tag_rules_controller_spec.rb'
- 'spec/controllers/admin/terms_of_service_files_controller_spec.rb'
- 'spec/controllers/admin/variant_overrides_controller_spec.rb'
- 'spec/controllers/api/v0/customers_controller_spec.rb'
- 'spec/controllers/api/v0/enterprise_fees_controller_spec.rb'
- 'spec/controllers/api/v0/enterprises_controller_spec.rb'
- 'spec/controllers/api/v0/exchange_products_controller_spec.rb'
- 'spec/controllers/api/v0/logos_controller_spec.rb'
- 'spec/controllers/api/v0/order_cycles_controller_spec.rb'
- 'spec/controllers/api/v0/orders_controller_spec.rb'
- 'spec/controllers/api/v0/product_images_controller_spec.rb'
- 'spec/controllers/api/v0/products_controller_spec.rb'
- 'spec/controllers/api/v0/promo_images_controller_spec.rb'
- 'spec/controllers/api/v0/reports/packing_report_spec.rb'
- 'spec/controllers/api/v0/reports_controller_spec.rb'
- 'spec/controllers/api/v0/shipments_controller_spec.rb'
- 'spec/controllers/api/v0/shops_controller_spec.rb'
- 'spec/controllers/api/v0/statuses_controller_spec.rb'
- 'spec/controllers/api/v0/terms_and_conditions_controller_spec.rb'
- 'spec/controllers/api/v0/variants_controller_spec.rb'
- 'spec/controllers/base_controller_spec.rb'
- 'spec/controllers/cart_controller_spec.rb'
- 'spec/controllers/checkout_controller_spec.rb'
- 'spec/controllers/enterprises_controller_spec.rb'
- 'spec/controllers/groups_controller_spec.rb'
- 'spec/controllers/line_items_controller_spec.rb'
- 'spec/controllers/payment_gateways/paypal_controller_spec.rb'
- 'spec/controllers/payment_gateways/stripe_controller_spec.rb'
- 'spec/controllers/registration_controller_spec.rb'
- 'spec/controllers/shop_controller_spec.rb'
- 'spec/controllers/shops_controller_spec.rb'
- 'spec/controllers/spree/admin/adjustments_controller_spec.rb'
- 'spec/controllers/spree/admin/base_controller_spec.rb'
- 'spec/controllers/spree/admin/countries_controller_spec.rb'
- 'spec/controllers/spree/admin/general_settings_controller_spec.rb'
- 'spec/controllers/spree/admin/orders/customer_details_controller_spec.rb'
- 'spec/controllers/spree/admin/orders/invoices_spec.rb'
- 'spec/controllers/spree/admin/orders/payments/payments_controller_refunds_spec.rb'
- 'spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb'
- 'spec/controllers/spree/admin/orders_controller_spec.rb'
- 'spec/controllers/spree/admin/overview_controller_spec.rb'
- 'spec/controllers/spree/admin/payment_methods_controller_spec.rb'
- 'spec/controllers/spree/admin/products_controller_spec.rb'
- 'spec/controllers/spree/admin/return_authorizations_controller_spec.rb'
- 'spec/controllers/spree/admin/search_controller_spec.rb'
- 'spec/controllers/spree/admin/shipping_categories_controller_spec.rb'
- 'spec/controllers/spree/admin/shipping_methods_controller_spec.rb'
- 'spec/controllers/spree/admin/tax_rates_controller_spec.rb'
- 'spec/controllers/spree/admin/tax_settings_controller_spec.rb'
- 'spec/controllers/spree/admin/variants_controller_spec.rb'
- 'spec/controllers/spree/api_keys_controller_spec.rb'
- 'spec/controllers/spree/credit_cards_controller_spec.rb'
- 'spec/controllers/spree/orders_controller_spec.rb'
- 'spec/controllers/spree/user_sessions_controller_spec.rb'
- 'spec/controllers/spree/users_controller_spec.rb'
- 'spec/controllers/stripe/callbacks_controller_spec.rb'
- 'spec/controllers/stripe/webhooks_controller_spec.rb'
- 'spec/controllers/user_confirmations_controller_spec.rb'
- 'spec/controllers/user_passwords_controller_spec.rb'
- 'spec/controllers/user_registrations_controller_spec.rb'
- 'spec/controllers/webhook_endpoints_controller_spec.rb'
- 'spec/helpers/admin/enterprises_helper_spec.rb'
- 'spec/helpers/admin/orders_helper_spec.rb'
- 'spec/helpers/admin/reports_helper_spec.rb'
- 'spec/helpers/admin/subscriptions_helper_spec.rb'
- 'spec/helpers/application_helper_spec.rb'
- 'spec/helpers/checkout_helper_spec.rb'
- 'spec/helpers/i18n_helper_spec.rb'
- 'spec/helpers/injection_helper_spec.rb'
- 'spec/helpers/link_helper_spec.rb'
- 'spec/helpers/navigation_helper_spec.rb'
- 'spec/helpers/order_cycles_helper_spec.rb'
- 'spec/helpers/serializer_helper_spec.rb'
- 'spec/helpers/shared_helper_spec.rb'
- 'spec/helpers/shop_helper_spec.rb'
- 'spec/helpers/spree/admin/base_helper_spec.rb'
- 'spec/helpers/spree/admin/general_settings_helper_spec.rb'
- 'spec/helpers/spree/admin/orders_helper_spec.rb'
- 'spec/helpers/spree/orders_helper_spec.rb'
- 'spec/helpers/tax_helper_spec.rb'
- 'spec/helpers/terms_and_conditions_helper_spec.rb'
- 'spec/jobs/connect_app_job_spec.rb'
- 'spec/mailers/producer_mailer_spec.rb'
- 'spec/mailers/subscription_mailer_spec.rb'
- 'spec/models/column_preference_spec.rb'
- 'spec/models/connected_app_spec.rb'
- 'spec/models/customer_spec.rb'
- 'spec/models/invoice_spec.rb'
- 'spec/models/oidc_account_spec.rb'
- 'spec/models/proxy_order_spec.rb'
- 'spec/models/report_blob_spec.rb'
- 'spec/models/semantic_link_spec.rb'
- 'spec/models/spree/gateway/stripe_sca_spec.rb'
- 'spec/models/subscription_spec.rb'
- 'spec/models/tag_rule/filter_order_cycles_spec.rb'
- 'spec/models/tag_rule/filter_payment_methods_spec.rb'
- 'spec/models/tag_rule/filter_products_spec.rb'
- 'spec/models/tag_rule/filter_shipping_methods_spec.rb'
- 'spec/models/tag_rule_spec.rb'
- 'spec/models/webhook_endpoint_spec.rb'
- 'spec/requests/admin/images_spec.rb'
- 'spec/requests/admin/product_import_spec.rb'
- 'spec/requests/admin/vouchers_spec.rb'
- 'spec/requests/api/orders_spec.rb'
- 'spec/requests/api/routes_spec.rb'
- 'spec/requests/api/v1/customers_spec.rb'
- 'spec/requests/api_docs_spec.rb'
- 'spec/requests/checkout/failed_checkout_spec.rb'
- 'spec/requests/checkout/paypal_spec.rb'
- 'spec/requests/checkout/routes_spec.rb'
- 'spec/requests/checkout/stripe_sca_spec.rb'
- 'spec/requests/errors_spec.rb'
- 'spec/requests/home_controller_spec.rb'
- 'spec/requests/large_request_spec.rb'
- 'spec/requests/omniauth_callbacks_controller_spec.rb'
- 'spec/requests/spree/admin/overview_spec.rb'
- 'spec/requests/spree/admin/payments_spec.rb'
- 'spec/requests/voucher_adjustments_spec.rb'
- 'spec/routing/stripe_spec.rb'
# Offense count: 11
# Configuration parameters: Include.
@@ -415,31 +576,6 @@ Rails/HasManyOrHasOneDependent:
- 'app/models/spree/tax_rate.rb'
- 'app/models/spree/variant.rb'
# Offense count: 26
# Configuration parameters: Include.
# Include: app/helpers/**/*.rb
Rails/HelperInstanceVariable:
Exclude:
- 'app/helpers/injection_helper.rb'
- 'app/helpers/shared_helper.rb'
- 'app/helpers/spree/admin/orders_helper.rb'
- 'app/helpers/spree/orders_helper.rb'
# Offense count: 8
# Configuration parameters: Include.
# Include: spec/**/*.rb, test/**/*.rb
Rails/I18nLocaleAssignment:
Exclude:
- 'spec/controllers/user_registrations_controller_spec.rb'
- 'spec/helpers/i18n_helper_spec.rb'
- 'spec/models/spree/variant_spec.rb'
- 'spec/system/admin/order_cycles/list_spec.rb'
# Offense count: 3
Rails/I18nLocaleTexts:
Exclude:
- 'app/controllers/admin/stripe_accounts_controller.rb'
# Offense count: 22
# Configuration parameters: IgnoreScopes, Include.
# Include: app/models/**/*.rb
@@ -484,17 +620,6 @@ Rails/LexicallyScopedActionFilter:
- 'app/controllers/spree/admin/zones_controller.rb'
- 'app/controllers/spree/users_controller.rb'
# Offense count: 6
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/NegateInclude:
Exclude:
- 'app/controllers/admin/resource_controller.rb'
- 'app/models/calculator/weight.rb'
- 'app/models/product_import/spreadsheet_entry.rb'
- 'app/services/order_cart_reset.rb'
- 'lib/spree/localized_number.rb'
- 'spec/support/matchers/table_matchers.rb'
# Offense count: 32
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/Pluck:
@@ -562,7 +687,7 @@ Rails/RelativeDateConstant:
Exclude:
- 'lib/tasks/data/remove_transient_data.rb'
# Offense count: 58
# Offense count: 56
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Include.
# Include: spec/controllers/**/*.rb, spec/requests/**/*.rb, test/controllers/**/*.rb, test/integration/**/*.rb
@@ -602,14 +727,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.
@@ -618,8 +735,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/order_cycles/webhook_service_spec.rb'
- 'spec/services/orders/customer_cancellation_service_spec.rb'
# Offense count: 1
# Configuration parameters: TransactionMethods.
@@ -656,60 +773,12 @@ Rails/UnusedRenderContent:
- 'app/controllers/api/v0/taxons_controller.rb'
- 'app/controllers/api/v0/variants_controller.rb'
# Offense count: 55
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/WhereEquals:
Exclude:
- 'app/controllers/spree/admin/products_controller.rb'
- 'app/mailers/producer_mailer.rb'
- 'app/models/enterprise.rb'
- 'app/models/enterprise_fee.rb'
- 'app/models/enterprise_group.rb'
- 'app/models/enterprise_relationship.rb'
- 'app/models/exchange.rb'
- 'app/models/order_cycle.rb'
- 'app/models/product_import/entry_processor.rb'
- 'app/models/proxy_order.rb'
- 'app/models/schedule.rb'
- 'app/models/spree/line_item.rb'
- 'app/models/spree/order.rb'
- 'app/models/spree/payment_method.rb'
- 'app/models/spree/product.rb'
- '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'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/reporting/reports/products_and_inventory/base.rb'
- 'lib/tasks/data.rake'
- 'lib/tasks/data/anonymize_data.rake'
- 'lib/tasks/data/remove_transient_data.rb'
- 'spec/services/product_tag_rules_filterer_spec.rb'
# Offense count: 8
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: exists, where
Rails/WhereExists:
Exclude:
- 'app/controllers/spree/admin/overview_controller.rb'
- 'app/controllers/spree/admin/tax_rates_controller.rb'
- 'app/controllers/spree/user_sessions_controller.rb'
- 'app/models/spree/preferences/store.rb'
- 'lib/tasks/sample_data/customer_factory.rb'
- 'lib/tasks/sample_data/group_factory.rb'
- 'lib/tasks/sample_data/order_cycle_factory.rb'
- 'lib/tasks/sample_data/taxon_factory.rb'
# Offense count: 1
Security/Open:
Exclude:
- 'app/services/image_importer.rb'
# Offense count: 9
# Offense count: 7
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/ArrayIntersect:
Exclude:
@@ -718,7 +787,6 @@ Style/ArrayIntersect:
- 'app/models/tag_rule/filter_payment_methods.rb'
- 'app/models/tag_rule/filter_products.rb'
- 'app/models/tag_rule/filter_shipping_methods.rb'
- 'app/services/order_syncer.rb'
- 'lib/open_food_network/tag_rule_applicator.rb'
- 'spec/support/matchers/select2_matchers.rb'
@@ -883,13 +951,12 @@ 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).
@@ -899,7 +966,7 @@ Style/RedundantAssignment:
# 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'
@@ -911,6 +978,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.

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) or [RVM](https://rvm.io/).
* 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
```

View File

@@ -161,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'

View File

@@ -109,7 +109,7 @@ GEM
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
@@ -156,30 +156,30 @@ GEM
awesome_nested_set (3.6.0)
activerecord (>= 4.0.0, < 7.2)
aws-eventstream (1.3.0)
aws-partitions (1.883.0)
aws-sdk-core (3.191.0)
aws-partitions (1.914.0)
aws-sdk-core (3.192.0)
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.77.0)
aws-sdk-kms (1.79.0)
aws-sdk-core (~> 3, >= 3.191.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.143.0)
aws-sdk-core (~> 3, >= 3.191.0)
aws-sdk-s3 (1.147.0)
aws-sdk-core (~> 3, >= 3.192.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.8)
aws-sigv4 (1.8.0)
aws-eventstream (~> 1, >= 1.0.2)
base64 (0.2.0)
bcp47_spec (0.2.1)
bcrypt (3.1.19)
bcrypt (3.1.20)
bigdecimal (3.0.2)
bindata (2.4.15)
bindata (2.5.0)
bindex (0.8.1)
bootsnap (1.18.3)
msgpack (~> 1.2)
bugsnag (6.26.3)
bugsnag (6.26.4)
concurrent-ruby (~> 1.0)
builder (3.2.4)
bullet (7.1.6)
@@ -207,6 +207,7 @@ GEM
nokogiri (~> 1.10, >= 1.10.4)
rubyzip (>= 1.3.0, < 3)
cgi (0.3.6)
childprocess (5.0.0)
choice (0.2.0)
chronic (0.10.2)
coderay (1.1.3)
@@ -237,14 +238,14 @@ GEM
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
datafoodconsortium-connector (1.0.0.pre.alpha.10)
datafoodconsortium-connector (1.0.0.pre.alpha.12)
virtual_assembly-semantizer (~> 1.0, >= 1.0.5)
date (3.3.4)
debug (1.9.1)
debug (1.9.2)
irb (~> 1.10)
reline (>= 0.3.8)
debugger-linecache (1.2.0)
devise (4.9.3)
devise (4.9.4)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
@@ -256,7 +257,7 @@ 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 (3.1.0)
@@ -311,7 +312,7 @@ GEM
fog-xml (0.1.3)
fog-core
nokogiri (>= 1.5.11, < 2.0.0)
foreman (0.87.2)
foreman (0.88.1)
formatador (0.2.5)
fugit (1.8.1)
et-orbi (~> 1, >= 1.2.7)
@@ -326,15 +327,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.1.0)
hashery (2.1.2)
hashie (5.0.0)
highline (2.0.3)
htmlentities (4.3.4)
i18n (1.14.1)
i18n (1.14.4)
concurrent-ruby (~> 1.0)
i18n-js (3.9.2)
i18n (>= 0.6.6)
@@ -345,11 +347,11 @@ GEM
activerecord (>= 3.0)
invisible_captcha (2.2.0)
rails (>= 5.2)
io-console (0.7.1)
io-console (0.7.2)
ipaddress (0.8.3)
irb (1.11.0)
irb (1.12.0)
rdoc
reline (>= 0.3.8)
reline (>= 0.4.2)
jmespath (1.6.2)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
@@ -357,9 +359,9 @@ GEM
thor (>= 0.14, < 2.0)
jquery-ui-rails (4.2.1)
railties (>= 3.2.16)
json (2.7.1)
json (2.7.2)
json-canonicalization (1.0.0)
json-jwt (1.16.5)
json-jwt (1.16.6)
activesupport (>= 4.2)
aes_key_wrap
base64
@@ -380,15 +382,16 @@ GEM
rspec (>= 2.0, < 4.0)
jsonapi-serializer (2.2.0)
activesupport (>= 4.2)
jwt (2.8.0)
jwt (2.8.1)
base64
knapsack_pro (6.0.4)
rake
language_server-protocol (3.17.0.3)
launchy (2.5.2)
launchy (3.0.0)
addressable (~> 2.8)
letter_opener (1.9.0)
launchy (>= 2.2, < 3)
childprocess (~> 5.0)
letter_opener (1.10.0)
launchy (>= 2.2, < 4)
link_header (0.0.8)
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
@@ -412,8 +415,8 @@ GEM
rake
mini_magick (4.11.0)
mini_mime (1.1.5)
mini_portile2 (2.8.5)
minitest (5.22.2)
mini_portile2 (2.8.6)
minitest (5.22.3)
monetize (1.13.0)
money (~> 6.12)
money (6.16.0)
@@ -430,11 +433,11 @@ GEM
net-protocol
net-protocol (0.2.2)
timeout
net-smtp (0.4.0.1)
net-smtp (0.5.0)
net-protocol
newrelic_rpm (9.7.1)
newrelic_rpm (9.8.0)
nio4r (2.7.0)
nokogiri (1.16.2)
nokogiri (1.16.4)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
oauth2 (1.4.11)
@@ -504,7 +507,7 @@ GEM
railties (>= 4.2)
raabro (1.4.0)
racc (1.7.3)
rack (2.2.8)
rack (2.2.9)
rack-mini-profiler (2.3.4)
rack (>= 1.2.0)
rack-oauth2 (2.2.1)
@@ -553,7 +556,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)
@@ -565,7 +568,7 @@ GEM
thor (~> 1.0)
zeitwerk (~> 2.5)
rainbow (3.1.1)
rake (13.1.0)
rake (13.2.1)
ransack (4.1.1)
activerecord (>= 6.1.5)
activesupport (>= 6.1.5)
@@ -576,15 +579,15 @@ GEM
rdf (3.3.1)
bcp47_spec (~> 0.2)
link_header (~> 0.0, >= 0.0.8)
rdoc (6.6.2)
rdoc (6.6.3.1)
psych (>= 4.0.0)
redcarpet (3.6.0)
redis (5.1.0)
redis-client (>= 0.17.0)
redis-client (0.20.0)
redis (5.2.0)
redis-client (>= 0.22.0)
redis-client (0.22.0)
connection_pool
regexp_parser (2.9.0)
reline (0.4.1)
reline (0.5.0)
io-console (~> 0.5)
request_store (1.5.1)
rack (>= 1.4)
@@ -604,29 +607,32 @@ GEM
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.1)
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.2)
activesupport
rspec
rspec-support (3.13.1)
rswag (2.13.0)
rswag-api (= 2.13.0)
rswag-specs (= 2.13.0)
@@ -642,7 +648,7 @@ GEM
rswag-ui (2.13.0)
actionpack (>= 3.1, < 7.2)
railties (>= 3.1, < 7.2)
rubocop (1.60.2)
rubocop (1.63.2)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
@@ -650,24 +656,27 @@ GEM
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-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.23.1)
rubocop-rails (2.24.1)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
rubocop-rspec (2.26.1)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rspec (2.29.1)
rubocop (~> 1.40)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
rubocop-rspec_rails (~> 2.28)
rubocop-rspec_rails (2.28.2)
rubocop (~> 1.40)
ruby-graphviz (1.2.5)
rexml
ruby-progressbar (1.13.0)
@@ -689,7 +698,7 @@ GEM
tilt (>= 1.1, < 3)
sd_notify (0.1.1)
semantic_range (3.0.0)
shoulda-matchers (6.1.0)
shoulda-matchers (6.2.0)
activesupport (>= 5.2.0)
sidekiq (7.2.2)
concurrent-ruby (< 2)
@@ -709,7 +718,7 @@ GEM
spreadsheet_architect (5.0.0)
caxlsx (>= 3.3.0, < 4)
rodf (>= 1.0.0, < 2)
spring (4.1.3)
spring (4.2.0)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
spring-commands-rubocop (0.4.0)
@@ -742,14 +751,14 @@ GEM
stimulus_reflex (>= 3.3.0)
stringex (2.8.6)
stringio (3.1.0)
stripe (10.10.0)
stripe (11.1.0)
swd (2.0.3)
activesupport (>= 3)
attr_required (>= 0.0.5)
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)
@@ -760,7 +769,7 @@ GEM
unicode-display_width (2.5.0)
uniform_notifier (1.16.0)
uri (0.13.0)
valid_email2 (5.2.1)
valid_email2 (5.2.3)
activemodel (>= 3.2)
mail (~> 2.5)
validate_url (1.0.15)
@@ -769,7 +778,7 @@ GEM
validates_lengths_from_database (0.8.0)
activerecord (>= 4)
vcr (6.2.0)
view_component (3.11.0)
view_component (3.12.0)
activesupport (>= 5.2.0, < 8.0)
concurrent-ruby (~> 1.0)
method_source (~> 1.0)
@@ -777,7 +786,7 @@ GEM
rails (>= 5.2, < 8.0)
stimulus_reflex (>= 3.5.0.pre2)
view_component (>= 2.28.0)
virtual_assembly-semantizer (1.0.5)
virtual_assembly-semantizer (1.1.1)
json-ld (~> 3.2, >= 3.2.3)
warden (1.2.9)
rack (>= 2.0.9)
@@ -921,6 +930,7 @@ DEPENDENCIES
roo
rspec-rails (>= 3.5.2)
rspec-retry
rspec-sql
rswag
rswag-api
rswag-ui

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!
@@ -53,4 +53,3 @@ Copyright (c) 2012 - 2024 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

@@ -48,7 +48,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
params = {
'q[name_cont]': $scope.q.query,
'q[supplier_id_eq]': $scope.q.producerFilter,
'q[primary_taxon_id_eq]': $scope.q.categoryFilter,
'q[variants_primary_taxon_id_eq]': $scope.q.categoryFilter,
'q[s]': $scope.sorting,
import_date: $scope.q.importDateFilter,
page: $scope.page,
@@ -136,6 +136,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
on_hand: null
price: null
tax_category_id: null
category_id: null
DisplayProperties.setShowVariants product.id, true
@@ -217,7 +218,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
filters:
'q[name_cont]': $scope.q.query
'q[supplier_id_eq]': $scope.q.producerFilter
'q[primary_taxon_id_eq]': $scope.q.categoryFilter
'q[variants_primary_taxon_id_eq]': $scope.q.categoryFilter
'q[s]': $scope.sorting
import_date: $scope.q.importDateFilter
page: $scope.page
@@ -332,9 +333,6 @@ filterSubmitProducts = (productsToFilter) ->
if product.hasOwnProperty("on_demand") and filteredVariants.length == 0 #only update if no variants present
filteredProduct.on_demand = product.on_demand
hasUpdatableProperty = true
if product.hasOwnProperty("category_id")
filteredProduct.primary_taxon_id = product.category_id
hasUpdatableProperty = true
if product.hasOwnProperty("inherits_properties")
filteredProduct.inherits_properties = product.inherits_properties
hasUpdatableProperty = true
@@ -375,6 +373,9 @@ filterSubmitVariant = (variant) ->
if variant.hasOwnProperty("tax_category_id")
filteredVariant.tax_category_id = variant.tax_category_id
hasUpdatableProperty = true
if variant.hasOwnProperty("category_id")
filteredVariant.primary_taxon_id = variant.category_id
hasUpdatableProperty = true
if variant.hasOwnProperty("display_as")
filteredVariant.display_as = variant.display_as
hasUpdatableProperty = true

View File

@@ -1,3 +1,4 @@
# Controller for "New Products" form (spree/admin/products/new)
angular.module("admin.products")
.controller "unitsCtrl", ($scope, VariantUnitManager, OptionValueNamer, UnitPrices, PriceParser) ->
$scope.product = { master: {} }
@@ -12,13 +13,15 @@ angular.module("admin.products")
$scope.variant_unit_options = VariantUnitManager.variantUnitOptions()
# Extract variant_unit and variant_unit_scale from dropdown variant_unit_with_scale,
# and update hidden product fields
$scope.processVariantUnitWithScale = ->
if $scope.product.variant_unit_with_scale
match = $scope.product.variant_unit_with_scale.match(/^([^_]+)_([\d\.]+)$/)
match = $scope.product.variant_unit_with_scale.match(/^([^_]+)_([\d\.]+)$/) # matches string like "weight_1000"
if match
$scope.product.variant_unit = match[1]
$scope.product.variant_unit_scale = parseFloat(match[2])
else
else # "items"
$scope.product.variant_unit = $scope.product.variant_unit_with_scale
$scope.product.variant_unit_scale = null
else if $scope.product.variant_unit
@@ -32,6 +35,8 @@ angular.module("admin.products")
else
$scope.product.variant_unit = $scope.product.variant_unit_scale = null
# Extract unit_value and unit_description from text field unit_value_with_description,
# and update hidden variant fields
$scope.processUnitValueWithDescription = ->
if $scope.product.master.hasOwnProperty("unit_value_with_description")
match = $scope.product.master.unit_value_with_description.match(/^([\d\.,]+(?= *|$)|)( *)(.*)$/)
@@ -45,6 +50,7 @@ angular.module("admin.products")
value = window.bigDecimal.divide(value, $scope.product.variant_unit_scale, 2) if $scope.product.master.unit_value && $scope.product.variant_unit_scale
$scope.product.master.unit_value_with_description = value + " " + $scope.product.master.unit_description
# Calculate unit price based on product price and variant_unit_scale
$scope.processUnitPrice = ->
price = $scope.product.price
scale = $scope.product.variant_unit_scale

View File

@@ -1,4 +1,5 @@
angular.module("admin.products").factory "OptionValueNamer", (VariantUnitManager) ->
# Javascript clone of VariantUnits::OptionValueNamer, for bulk product editing.
class OptionValueNamer
constructor: (@variant) ->

View File

@@ -68,7 +68,7 @@ angular.module('Darkswarm').controller "ProductsCtrl", ($scope, $sce, $filter, $
per_page: $scope.per_page,
'q[name_or_meta_keywords_or_variants_display_as_or_variants_display_name_or_supplier_name_cont]': $scope.query,
'q[with_properties][]': $scope.activeProperties,
'q[primary_taxon_id_in_any][]': $scope.activeTaxons
'q[variants_primary_taxon_id_in_any][]': $scope.activeTaxons
}
$scope.searchKeypress = (e)->

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

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

@@ -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

@@ -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

@@ -2,6 +2,138 @@
module Admin
class ProductsV3Controller < Spree::Admin::BaseController
def index; end
before_action :init_filters_params
before_action :init_pagination_params
def index
fetch_products
render "index", locals: { producers:, categories:, flash: }
end
def bulk_update
product_set = product_set_from_params
product_set.collection.each { |p| authorize! :update, p }
@products = product_set.collection # use instance variable mainly for testing
if product_set.save
flash[:success] = I18n.t('admin.products_v3.bulk_update.success')
redirect_to [:index,
{ page: @page, per_page: @per_page, search_term: @search_term,
producer_id: @producer_id, category_id: @category_id }]
elsif product_set.errors.present?
@error_counts = { saved: product_set.saved_count, invalid: product_set.invalid.count }
render "index", status: :unprocessable_entity, locals: { producers:, categories:, flash: }
end
end
def index_url(params)
"/admin/products?#{params.to_query}" # todo: fix routing so this can be automaticly generated
end
private
def init_filters_params
# params comes from the form
# _params comes from the url
# priority is given to params from the form (if present) over url params
@search_term = params[:search_term] || params[:_search_term]
@producer_id = params[:producer_id] || params[:_producer_id]
@category_id = params[:category_id] || params[:_category_id]
end
def init_pagination_params
# prority is given to element dataset (if present) over url params
@page = params[:page].presence || 1
@per_page = params[:per_page].presence || 15
end
def producers
producers = OpenFoodNetwork::Permissions.new(spree_current_user)
.managed_product_enterprises.is_primary_producer.by_name
producers.map { |p| [p.name, p.id] }
end
def categories
Spree::Taxon.order(:name).map { |c| [c.name, c.id] }
end
def fetch_products
product_query = OpenFoodNetwork::Permissions.new(spree_current_user)
.editable_products.merge(product_scope).ransack(ransack_query).result
@pagy, @products = pagy(product_query.order(:name), items: @per_page, page: @page,
size: [1, 2, 2, 1])
end
def product_scope
user = spree_current_user
scope = if user.has_spree_role?("admin") || user.enterprises.present?
Spree::Product
else
Spree::Product.active
end
scope.includes(product_query_includes).distinct
end
def ransack_query
query = {}
query.merge!(supplier_id_in: @producer_id) if @producer_id.present?
if @search_term.present?
query.merge!(Spree::Variant::SEARCH_KEY => @search_term)
end
query.merge!(variants_primary_taxon_id_in: @category_id) if @category_id.present?
query
end
# Optimise by pre-loading required columns
def product_query_includes
[
:image,
:supplier,
{ variants: [
:default_price,
:primary_taxon,
:product,
:stock_items,
:tax_category,
] },
]
end
# Similar to spree/admin/products_controller
def product_set_from_params
# Form field names:
# '[products][0][id]' (hidden field)
# '[products][0][name]'
# '[products][0][variants_attributes][0][id]' (hidden field)
# '[products][0][variants_attributes][0][display_name]'
#
# Resulting in params:
# "products" => {
# "0" => {
# "id" => "123"
# "name" => "Pommes",
# "variants_attributes" => {
# "0" => {
# "id" => "1234",
# "display_name" => "Large box",
# }
# }
# }
collection_hash = products_bulk_params[:products]
.transform_values { |product|
# Convert variants_attributes form hash to an array if present
product[:variants_attributes] &&= product[:variants_attributes].values
product
}.with_indifferent_access
Sets::ProductSet.new(collection_attributes: collection_hash)
end
def products_bulk_params
params.permit(products: ::PermittedAttributes::Product.attributes)
.to_h.with_indifferent_access
end
end
end

View File

@@ -229,7 +229,7 @@ module Admin
end
def member_action?
!collection_actions.include? action
collection_actions.exclude? action
end
def new_actions

View File

@@ -16,14 +16,14 @@ module Admin
authorize! :destroy, stripe_account
if stripe_account.deauthorize_and_destroy
flash[:success] = "Stripe account disconnected."
flash[:success] = I18n.t('stripe.success_code.disconnected')
else
flash[:error] = "Failed to disconnect Stripe."
flash[:error] = I18n.t('stripe.error_code.disconnect_failure')
end
redirect_to main_app.edit_admin_enterprise_path(stripe_account.enterprise)
rescue ActiveRecord::RecordNotFound
flash[:error] = "Failed to disconnect Stripe."
flash[:error] = I18n.t('stripe.error_code.disconnect_failure')
redirect_to spree.admin_dashboard_path
end

View File

@@ -70,22 +70,7 @@ module Api
end
def search_params
permitted_search_params = params.slice :q, :page, :per_page
if permitted_search_params.key? :q
permitted_search_params[:q].slice!(*permitted_ransack_params)
end
permitted_search_params
end
def permitted_ransack_params
[
"#{[:name, :meta_keywords, :variants_display_as,
:variants_display_name, :supplier_name]
.join('_or_')}_cont",
:with_properties, :primary_taxon_id_in_any
]
params.slice :q, :page, :per_page
end
def distributor
@@ -101,7 +86,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,7 @@ 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!
@@ -85,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

@@ -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

@@ -26,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

@@ -21,12 +21,11 @@ module Spree
@order = Order.find_by(number: params[:order_id])
if @order.distributor.can_invoice?
authorize! :invoice, @order
OrderInvoiceGenerator.new(@order).generate_or_update_latest_invoice
::Orders::GenerateInvoiceService.new(@order).generate_or_update_latest_invoice
else
flash[:error] = t(:must_have_valid_business_number,
enterprise_name: @order.distributor.name)
end
redirect_back(fallback_location: spree.admin_dashboard_path)
end

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

@@ -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!

View File

@@ -175,7 +175,7 @@ module Spree
Spree::Variant.
select('DISTINCT spree_variants.import_date').
joins(:product).
where('spree_products.supplier_id IN (?)', editable_enterprises.collect(&:id)).
where(spree_products: { supplier_id: editable_enterprises.collect(&:id) }).
where.not(spree_variants: { import_date: nil }).
where(spree_variants: { deleted_at: nil }).
order('spree_variants.import_date DESC')

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?
@@ -60,7 +60,7 @@ module Spree
@insufficient_stock_lines = []
@order = order_to_update
unless @order
flash[:error] = t(:order_not_found)
flash[:error] = t(:order_not_updated)
redirect_to(main_app.root_path) && return
end
@@ -82,7 +82,7 @@ module Spree
format.html do
if params.key?(:checkout)
@order.next_transition.run_callbacks if @order.cart?
redirect_to main_app.checkout_step_path(@order.checkout_steps.first)
redirect_to main_app.checkout_step_path("address")
elsif @order.complete?
redirect_to main_app.order_path(@order)
else
@@ -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

@@ -16,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
@@ -79,7 +79,7 @@ module Spree
private
def orders_collection
CompleteOrdersWithBalance.new(@user).query
CompleteOrdersWithBalanceQuery.new(@user).call
end
def load_object

View File

@@ -162,7 +162,7 @@ module Admin
def admin_inject_available_units
admin_inject_json "admin.products",
"availableUnits",
Spree::Config.available_units
CurrentConfig.get(:available_units)
end
def admin_inject_json(ng_module, name, data)

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|
{

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

View File

@@ -154,7 +154,6 @@ module InjectionHelper
end
def enterprise_injection_data
@enterprise_injection_data ||= OpenFoodNetwork::EnterpriseInjectionData.new
{ data: @enterprise_injection_data }
@enterprise_injection_data ||= { data: OpenFoodNetwork::EnterpriseInjectionData.new }
end
end

View File

@@ -2,7 +2,7 @@
module OrderHelper
def last_payment_method(order)
OrderPaymentFinder.new(order).last_payment&.payment_method
Orders::FindPaymentService.new(order).last_payment&.payment_method
end
def outstanding_balance_label(order)
@@ -16,6 +16,6 @@ module OrderHelper
end
def order_comparator(order)
OrderInvoiceComparator.new(order)
Orders::CompareInvoiceService.new(order)
end
end

View File

@@ -1,16 +1,6 @@
# frozen_string_literal: true
module SharedHelper
def distributor_link_class(distributor)
cart = current_order(true)
@active_distributors ||= Enterprise.distributors_with_active_order_cycles
klass = "shop-distributor"
klass += " empties-cart" unless cart.line_items.empty? || cart.distributor == distributor
klass += @active_distributors.include?(distributor) ? ' active' : ' inactive'
klass
end
def enterprise_user?
spree_current_user&.enterprises&.count.to_i > 0
end

View File

@@ -12,7 +12,7 @@ module Spree
id: "#{model}_#{method}_field")
end
def error_message_on(object, method, _options = {})
def error_message_on(object, method, options = {})
object = convert_to_model(object)
obj = object.respond_to?(:errors) ? object : instance_variable_get("@#{object}")
@@ -20,7 +20,7 @@ module Spree
# rubocop:disable Rails/OutputSafety
errors = obj.errors[method].map { |err| h(err) }.join('<br />').html_safe
# rubocop:enable Rails/OutputSafety
content_tag(:span, errors, class: 'formError')
content_tag(:span, errors, class: 'formError', **options)
else
''
end
@@ -33,8 +33,6 @@ module Spree
when :boolean
hidden_field_tag(name, 0) +
check_box_tag(name, 1, value, preference_field_options(options))
when :string
text_field_tag(name, value, preference_field_options(options))
when :password
password_field_tag(name, value, preference_field_options(options))
when :text
@@ -88,8 +86,6 @@ module Spree
{ size: 10, class: 'input_integer', step: :any }
when :boolean
{}
when :string
{ size: 10, class: 'input_string fullwidth' }
when :password
{ size: 10, class: 'password_string fullwidth' }
when :text

View File

@@ -3,20 +3,20 @@
module Spree
module Admin
module OrdersHelper
def event_links
def event_links(order)
links = []
links << cancel_event_link if @order.can_cancel?
links << resume_event_link if @order.can_resume?
links << cancel_event_link(order) if order.can_cancel?
links << resume_event_link(order) if order.can_resume?
links.join('&nbsp;').html_safe # rubocop:disable Rails/OutputSafety
end
def generate_invoice_button(order)
if order.distributor.can_invoice?
button_link_to t(:create_or_update_invoice), generate_admin_order_invoices_path(@order),
button_link_to t(:create_or_update_invoice), generate_admin_order_invoices_path(order),
data: { method: 'post' }, icon: 'icon-plus'
else
button_link_to t(:create_or_update_invoice), "#", data: {
confirm: t(:must_have_valid_business_number, enterprise_name: @order.distributor.name)
confirm: t(:must_have_valid_business_number, enterprise_name: order.distributor.name)
}, icon: 'icon-plus'
end
end
@@ -26,82 +26,81 @@ module Spree
end
def order_links(order)
@order ||= order
links = []
links << edit_order_link unless action_name == "edit"
links.concat(complete_order_links) if @order.complete? || @order.resumed?
links << ship_order_link if @order.ready_to_ship?
links << cancel_order_link if @order.can_cancel?
links << edit_order_link(order) unless action_name == "edit"
links.concat(complete_order_links(order)) if order.complete? || order.resumed?
links << ship_order_link if order.ready_to_ship?
links << cancel_order_link(order) if order.can_cancel?
links
end
private
def complete_order_links
[resend_confirmation_link] + invoice_links
def complete_order_links(order)
[resend_confirmation_link(order)] + invoice_links(order)
end
def invoice_links
def invoice_links(order)
return [] unless Spree::Config[:enable_invoices?]
[send_invoice_link, print_invoice_link]
[send_invoice_link(order), print_invoice_link(order)]
end
def send_invoice_link
if @order.distributor.can_invoice?
send_invoice_link_with_url
def send_invoice_link(order)
if order.distributor.can_invoice?
send_invoice_link_with_url(order)
else
send_invoice_link_without_url
send_invoice_link_without_url(order)
end
end
def print_invoice_link
if @order.distributor.can_invoice?
print_invoice_link_with_url
def print_invoice_link(order)
if order.distributor.can_invoice?
print_invoice_link_with_url(order)
else
notify_about_required_enterprise_number
notify_about_required_enterprise_number(order)
end
end
def edit_order_link
def edit_order_link(order)
{ name: t(:edit_order),
url: spree.edit_admin_order_path(@order),
url: spree.edit_admin_order_path(order),
icon: 'icon-edit' }
end
def resend_confirmation_link
def resend_confirmation_link(order)
{ name: t(:resend_confirmation),
url: spree.resend_admin_order_path(@order),
url: spree.resend_admin_order_path(order),
icon: 'icon-email',
confirm: t(:confirm_resend_order_confirmation) }
end
def send_invoice_link_with_url
def send_invoice_link_with_url(order)
{ name: t(:send_invoice),
url: invoice_admin_order_path(@order),
url: invoice_admin_order_path(order),
icon: 'icon-email',
confirm: t(:confirm_send_invoice) }
end
def send_invoice_link_without_url
def send_invoice_link_without_url(order)
{ name: t(:send_invoice),
url: "#",
icon: 'icon-email',
confirm: t(:must_have_valid_business_number, enterprise_name: @order.distributor.name) }
confirm: t(:must_have_valid_business_number, enterprise_name: order.distributor.name) }
end
def print_invoice_link_with_url
def print_invoice_link_with_url(order)
{ name: t(:print_invoice),
url: spree.print_admin_order_path(@order),
url: spree.print_admin_order_path(order),
icon: 'icon-print',
target: "_blank" }
end
def notify_about_required_enterprise_number
def notify_about_required_enterprise_number(order)
{ name: t(:print_invoice),
url: "#",
icon: 'icon-print',
confirm: t(:must_have_valid_business_number, enterprise_name: @order.distributor.name) }
confirm: t(:must_have_valid_business_number, enterprise_name: order.distributor.name) }
end
def ship_order_link
@@ -110,24 +109,24 @@ module Spree
icon: 'icon-truck' }
end
def cancel_order_link
def cancel_order_link(order)
{ name: t(:cancel_order),
url: spree.fire_admin_order_path(@order.number, e: 'cancel'),
url: spree.fire_admin_order_path(order.number, e: 'cancel'),
icon: 'icon-trash' }
end
def cancel_event_link
def cancel_event_link(order)
event_label = I18n.t("cancel", scope: "actions")
button_link_to(event_label,
fire_admin_order_url(@order, e: "cancel"),
fire_admin_order_url(order, e: "cancel"),
method: :put, icon: "icon-cancel", form_id: "cancel_order_form")
end
def resume_event_link
def resume_event_link(order)
event_label = I18n.t("resume", scope: "actions")
confirm_message = I18n.t("admin.orders.edit.order_sure_want_to", event: event_label)
button_link_to(event_label,
fire_admin_order_url(@order, e: "resume"),
fire_admin_order_url(order, e: "resume"),
method: :put, icon: "icon-resume",
data: { confirm: confirm_message })
end

View File

@@ -17,17 +17,18 @@ module Spree
def changeable_orders
# Only returns open order for the current user + shop + oc combo
return @changeable_orders unless @changeable_orders.nil?
return @changeable_orders = [] unless spree_current_user &&
current_distributor && current_order_cycle
return @changeable_orders = [] unless current_distributor.allow_order_changes?
@changeable_orders ||= if spree_current_user &&
current_order_cycle && current_distributor&.allow_order_changes?
@changeable_orders = Spree::Order.complete.where(
state: 'complete',
user_id: spree_current_user.id,
distributor_id: current_distributor.id,
order_cycle_id: current_order_cycle.id
)
Spree::Order.complete.where(
state: 'complete',
user_id: spree_current_user.id,
distributor_id: current_distributor.id,
order_cycle_id: current_order_cycle.id
)
else
[]
end
end
def changeable_orders_link_path

View File

@@ -7,9 +7,10 @@ class BulkInvoiceJob < ApplicationJob
def perform(order_ids, filepath, options = {})
@options = options
orders = sorted_orders(order_ids)
orders.filter!(&:invoiceable?) if OpenFoodNetwork::FeatureToggle.enabled?(:invoices,
current_user)
# The `find` method returns records in the same order as the given ids.
orders = Spree::Order.find(order_ids)
orders.each(&method(:generate_invoice))
ensure_directory_exists filepath
@@ -21,19 +22,13 @@ class BulkInvoiceJob < ApplicationJob
private
# Ensures the records are returned in the same order the ids were originally given in
def sorted_orders(order_ids)
orders_by_id = Spree::Order.where(id: order_ids).to_a.index_by(&:id)
order_ids.map { |id| orders_by_id[id.to_i] }
end
def renderer
@renderer ||= InvoiceRenderer.new
end
def generate_invoice(order)
renderer_data = if OpenFoodNetwork::FeatureToggle.enabled?(:invoices, current_user)
OrderInvoiceGenerator.new(order).generate_or_update_latest_invoice
Orders::GenerateInvoiceService.new(order).generate_or_update_latest_invoice
order.invoices.first.presenter
else
order

View File

@@ -4,7 +4,7 @@ class ConnectAppJob < ApplicationJob
include CableReady::Broadcaster
def perform(app, token, channel: nil)
url = "https://n8n.openfoodnetwork.org.uk/webhook/regen/connect-enterprise"
url = I18n.t("connect_app.url")
event = "connect-app"
enterprise = app.enterprise
payload = {

View File

@@ -5,7 +5,7 @@ class OrderCycleOpenedJob < ApplicationJob
def perform
ActiveRecord::Base.transaction do
recently_opened_order_cycles.find_each do |order_cycle|
OrderCycleWebhookService.create_webhook_job(order_cycle, 'order_cycle.opened')
OrderCycles::WebhookService.create_webhook_job(order_cycle, 'order_cycle.opened')
end
mark_as_opened(recently_opened_order_cycles)
end

View File

@@ -23,7 +23,7 @@ class SubscriptionConfirmJob < ApplicationJob
unconfirmed_proxy_orders.update_all(confirmed_at: Time.zone.now)
# Confirm these proxy orders
ProxyOrder.where(id: unconfirmed_proxy_orders_ids).each do |proxy_order|
ProxyOrder.where(id: unconfirmed_proxy_orders_ids).find_each do |proxy_order|
JobLogger.logger.info "Confirming Order for Proxy Order #{proxy_order.id}"
confirm_order!(proxy_order.order)
end

View File

@@ -52,7 +52,7 @@ class ProducerMailer < ApplicationMailer
def distributors_pickup_times_for(line_items)
@order_cycle.distributors.
joins(:distributed_orders).
where("spree_orders.id IN (?)", line_items.map(&:order_id).uniq).
where(spree_orders: { id: line_items.map(&:order_id).uniq }).
map do |distributor|
[distributor.name, @order_cycle.pickup_time_for(distributor)]
end

View File

@@ -52,7 +52,8 @@ module Spree
find_user(options[:current_user_id])
end
renderer_data = if OpenFoodNetwork::FeatureToggle.enabled?(:invoices, current_user)
OrderInvoiceGenerator.new(@order).generate_or_update_latest_invoice
::Orders::GenerateInvoiceService
.new(@order).generate_or_update_latest_invoice
@order.invoices.first.presenter
else
@order

View File

@@ -10,7 +10,7 @@ module Calculator
end
def set_preference(name, value)
if name == :unit_from_list && !["kg", "lb"].include?(value)
if name == :unit_from_list && ["kg", "lb"].exclude?(value)
calculable.errors.add(:preferred_unit_from_list, I18n.t(:calculator_preferred_unit_error))
else
__send__ self.class.preference_setter_method(name), value

View File

@@ -15,7 +15,7 @@ module OrderValidations
# Check that line_items in the current order are available from a newly selected distribution
def products_available_from_new_distribution
return if OrderCycleDistributedVariants.new(order_cycle, distributor)
return if OrderCycles::DistributedVariantsService.new(order_cycle, distributor)
.distributes_order_variants?(self)
errors.add(:base, I18n.t(:spree_order_availability_error))

View File

@@ -0,0 +1,19 @@
# frozen_string_literal: true
# Wraps repeatedly-called configs in a CurrentAttributes object so they only get fetched once
# per request at most, eg: CurrentConfig.get(:available_units) for Spree::Config[:available_units]
class CurrentConfig < ActiveSupport::CurrentAttributes
attribute :display_currency, :hide_cents, :currency_decimal_mark,
:currency_thousands_separator, :currency_symbol_position, :available_units
def get(config_key)
return public_send(config_key) unless public_send(config_key).nil?
public_send("#{config_key}=", Spree::Config.public_send(config_key))
end
def currency
ENV.fetch("CURRENCY")
end
end

View File

@@ -161,7 +161,7 @@ class Enterprise < ApplicationRecord
scope :is_hub, -> { where(sells: 'any') }
scope :supplying_variant_in, lambda { |variants|
joins(supplied_products: :variants).
where('spree_variants.id IN (?)', variants).
where(spree_variants: { id: variants }).
select('DISTINCT enterprises.*')
}
@@ -205,7 +205,7 @@ class Enterprise < ApplicationRecord
").
joins('INNER JOIN exchange_variants ON (exchange_variants.exchange_id = exchanges.id)').
joins('INNER JOIN spree_variants ON (spree_variants.id = exchange_variants.variant_id)').
where('spree_variants.product_id IN (?)', product_ids).select('DISTINCT enterprises.id')
where(spree_variants: { product_id: product_ids }).select('DISTINCT enterprises.id')
where(id: exchanges)
}
@@ -214,7 +214,7 @@ class Enterprise < ApplicationRecord
if user.has_spree_role?('admin')
where(nil)
else
joins(:enterprise_roles).where('enterprise_roles.user_id = ?', user.id)
joins(:enterprise_roles).where(enterprise_roles: { user_id: user.id })
end
}
@@ -369,10 +369,10 @@ class Enterprise < ApplicationRecord
:producer_shop # Producer with shopfront and supplies other hubs.
when "producer_sells_none"
:producer # Producer only supplies through others.
when "non_producer_sells_any"
:hub # Hub selling others products in order cycles.
when "non_producer_sells_own"
:hub # Wholesaler selling through own shopfront? Does this need a separate name or even exist?
when "non_producer_sells_any", "non_producer_sells_own"
# Hub selling others products in order cycles
# Or Wholesaler selling through own shopfront? Does this need a separate name or even exist?
:hub
when "non_producer_sells_none"
:hub_profile # Hub selling outside the system.
end
@@ -382,7 +382,7 @@ class Enterprise < ApplicationRecord
def distributed_taxons
Spree::Taxon.
joins(:products).
where('spree_products.id IN (?)', Spree::Product.in_distributor(self).select(&:id)).
where(spree_products: { id: Spree::Product.in_distributor(self).select(&:id) }).
select('DISTINCT spree_taxons.*')
end
@@ -398,7 +398,7 @@ class Enterprise < ApplicationRecord
def supplied_taxons
Spree::Taxon.
joins(:products).
where('spree_products.id IN (?)', Spree::Product.in_supplier(self).select(&:id)).
where(spree_products: { id: Spree::Product.in_supplier(self).select(&:id) }).
select('DISTINCT spree_taxons.*')
end
@@ -472,7 +472,7 @@ class Enterprise < ApplicationRecord
ExchangeVariant.joins(exchange: :order_cycle)
.merge(Exchange.outgoing)
.select("DISTINCT exchange_variants.variant_id, exchanges.receiver_id AS enterprise_id")
.where("exchanges.receiver_id = ?", id)
.where(exchanges: { receiver_id: id })
.merge(OrderCycle.active.with_distributor(id))
end

View File

@@ -32,7 +32,7 @@ class EnterpriseFee < ApplicationRecord
if user.has_spree_role?('admin')
where(nil)
else
where('enterprise_id IN (?)', user.enterprises.select(&:id))
where(enterprise_id: user.enterprises.select(&:id))
end
}
@@ -40,7 +40,7 @@ class EnterpriseFee < ApplicationRecord
joins(:calculator).where.not(spree_calculators: { type: PER_ORDER_CALCULATORS })
}
scope :per_order, lambda {
joins(:calculator).where('spree_calculators.type IN (?)', PER_ORDER_CALCULATORS)
joins(:calculator).where(spree_calculators: { type: PER_ORDER_CALCULATORS })
}
def self.clear_all_adjustments(order)

View File

@@ -41,7 +41,7 @@ class EnterpriseGroup < ApplicationRecord
if user.has_spree_role?('admin')
where(nil)
else
where('owner_id = ?', user.id)
where(owner_id: user.id)
end
}

View File

@@ -27,12 +27,12 @@ class EnterpriseRelationship < ApplicationRecord
where('parent_id IN (?) OR child_id IN (?)', enterprises.select(&:id), enterprises.select(&:id))
}
scope :permitting, ->(enterprise_ids) { where('child_id IN (?)', enterprise_ids) }
scope :permitted_by, ->(enterprise_ids) { where('parent_id IN (?)', enterprise_ids) }
scope :permitting, ->(enterprise_ids) { where(child_id: enterprise_ids) }
scope :permitted_by, ->(enterprise_ids) { where(parent_id: enterprise_ids) }
scope :with_permission, ->(permission) {
joins(:permissions).
where('enterprise_relationship_permissions.name = ?', permission)
where(enterprise_relationship_permissions: { name: permission })
}
scope :by_name, -> { with_enterprises.order('child_enterprises.name, parent_enterprises.name') }
@@ -108,6 +108,6 @@ class EnterpriseRelationship < ApplicationRecord
def child_variant_overrides
VariantOverride.unscoped.for_hubs(child)
.joins(variant: :product).where("spree_products.supplier_id IN (?)", parent)
.joins(variant: :product).where(spree_products: { supplier_id: parent })
end
end

View File

@@ -38,8 +38,8 @@ class Exchange < ApplicationRecord
scope :outgoing, -> { where(incoming: false) }
scope :from_enterprise, lambda { |enterprise| where(sender_id: enterprise) }
scope :to_enterprise, lambda { |enterprise| where(receiver_id: enterprise) }
scope :from_enterprises, lambda { |enterprises| where('exchanges.sender_id IN (?)', enterprises) }
scope :to_enterprises, lambda { |enterprises| where('exchanges.receiver_id IN (?)', enterprises) }
scope :from_enterprises, lambda { |enterprises| where(exchanges: { sender_id: enterprises }) }
scope :to_enterprises, lambda { |enterprises| where(exchanges: { receiver_id: enterprises }) }
scope :involving, lambda { |enterprises|
where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)', enterprises, enterprises).
select('DISTINCT exchanges.*')
@@ -48,7 +48,7 @@ class Exchange < ApplicationRecord
where('exchanges.incoming OR exchanges.receiver_id = ?', distributor)
}
scope :with_variant, lambda { |variant|
joins(:exchange_variants).where('exchange_variants.variant_id = ?', variant)
joins(:exchange_variants).where(exchange_variants: { variant_id: variant })
}
scope :with_any_variant, lambda { |variant_ids|
joins(:exchange_variants).
@@ -57,7 +57,7 @@ class Exchange < ApplicationRecord
}
scope :with_product, lambda { |product|
joins(:exchange_variants).
where('exchange_variants.variant_id IN (?)', product.variants.select(&:id))
where(exchange_variants: { variant_id: product.variants.select(&:id) })
}
scope :by_enterprise_name, -> {
joins('INNER JOIN enterprises AS sender ON (sender.id = exchanges.sender_id)').

View File

@@ -4,7 +4,7 @@ class Invoice < ApplicationRecord
self.belongs_to_required_by_default = false
belongs_to :order, class_name: 'Spree::Order'
serialize :data, Hash
serialize :data, Hash, coder: YAML
before_validation :serialize_order
after_create :cancel_previous_invoices
default_scope { order(created_at: :desc) }

View File

@@ -91,6 +91,14 @@ class Invoice
Spree::Money.new(shipment.amount + shipment.additional_tax_total, currency:)
end
def display_line_item_tax_rate(item)
all_tax_adjustments.select { |a|
a.adjustable.type == 'Spree::LineItem' && a.adjustable.id == item.id
}.map(&:originator).map { |tr|
number_to_percentage(tr.amount * 100, precision: 1)
}.join(", ")
end
def display_shipment_tax_rates
all_eligible_adjustments.select { |a|
a.originator.type == 'Spree::TaxRate' && a.adjustable_type == 'Spree::Shipment'

View File

@@ -3,11 +3,10 @@
class Invoice
class DataPresenter
class LineItem < Invoice::DataPresenter::Base
attributes :added_tax, :currency, :included_tax, :price_with_adjustments, :quantity,
attributes :id, :added_tax, :currency, :included_tax, :price_with_adjustments, :quantity,
:variant_id, :unit_price_price_and_unit, :unit_presentation,
:enterprise_fee_additional_tax, :enterprise_fee_included_tax
attributes_with_presenter :variant
array_attribute :tax_rates, class_name: 'TaxRate'
invoice_generation_attributes :added_tax, :included_tax, :price_with_adjustments,
:quantity, :variant_id
@@ -35,10 +34,6 @@ class Invoice
fee_tax = enterprise_fee_included_tax || 0.0
Spree::Money.new(price_with_adjustments - ((included_tax + fee_tax) / quantity), currency:)
end
def display_line_item_tax_rates
tax_rates.map { |tr| number_to_percentage(tr.amount * 100, precision: 1) }.join(", ")
end
end
end
end

View File

@@ -165,17 +165,17 @@ class OrderCycle < ApplicationRecord
def attachable_distributor_payment_methods
DistributorPaymentMethod.joins(:payment_method).
merge(Spree::PaymentMethod.available).
where("distributor_id IN (?)", distributor_ids)
where(distributor_id: distributor_ids)
end
def attachable_distributor_shipping_methods
DistributorShippingMethod.joins(:shipping_method).
merge(Spree::ShippingMethod.frontend).
where("distributor_id IN (?)", distributor_ids)
where(distributor_id: distributor_ids)
end
def clone!
OrderCycleClone.new(self).create
OrderCycles::CloneService.new(self).create
end
def variants

View File

@@ -56,7 +56,7 @@ module ProductImport
else
Spree::Variant.
joins(:product).
where('spree_products.supplier_id IN (?)', enterprise_id).
where(spree_products: { supplier_id: enterprise_id }).
count
end

View File

@@ -24,7 +24,6 @@ module ProductImport
def self.non_updatable_fields
{
category: :primary_taxon_id,
description: :description,
unit_type: :variant_unit_scale,
variant_unit_name: :variant_unit_name,
@@ -69,7 +68,7 @@ module ProductImport
def mark_as_new_variant(entry, product_id)
variant_attributes = entry.assignable_attributes.except(
'id', 'product_id', 'on_hand', 'on_demand', 'variant_unit', 'variant_unit_name',
'variant_unit_scale', 'primary_taxon_id'
'variant_unit_scale'
)
# Variant needs a product. Product needs to be assigned first in order for
# delegate to work. name= will fail otherwise.
@@ -398,7 +397,7 @@ module ProductImport
def mark_as_existing_variant(entry, existing_variant)
existing_variant.assign_attributes(
entry.assignable_attributes.except('id', 'product_id', 'variant_unit', 'variant_unit_name',
'variant_unit_scale', 'primary_taxon_id')
'variant_unit_scale')
)
check_on_hand_nil(entry, existing_variant)

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