From 1068e3171824efd92e9c756937b08ef1d85d6560 Mon Sep 17 00:00:00 2001 From: Martin Meyerhoff Date: Wed, 28 May 2025 15:07:36 +0200 Subject: [PATCH 1/4] Country: Add dependent/inverse_of options This also removes a bogus spec that tested admin behavior when dealing with the situation where a country was removed that had been used for an address. This commit prevents that entire situation. The original issue that was solved with the PR that introduced the spec was https://github.com/spree/spree/issues/3571 - allowing admins to add/remove countries, but even here the original developers did not deal with dependent records. For the use case described there - i would like to change the countries my store services - I think using zones is a better solution that deleting the country. --- .rubocop_todo.yml | 501 +++++++++--------- .../admin/orders/customer_details_spec.rb | 14 - core/app/models/spree/address.rb | 4 +- core/app/models/spree/country.rb | 16 +- core/app/models/spree/state.rb | 4 +- 5 files changed, 267 insertions(+), 272 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 00c8965f1fd..5db334ee856 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-07-08 12:12:57 UTC using RuboCop version 1.76.0. +# on 2025-07-26 15:08:52 UTC using RuboCop version 1.76.0. # 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 @@ -12,9 +12,9 @@ # Include: **/*.gemspec Gemspec/OrderedDependencies: Exclude: - - "core/solidus_core.gemspec" + - 'core/solidus_core.gemspec' -# Offense count: 197 +# Offense count: 201 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: with_first_argument, with_fixed_indentation @@ -27,15 +27,15 @@ Layout/ArgumentAlignment: # SupportedStylesAlignWith: start_of_line, begin Layout/BeginEndAlignment: Exclude: - - "backend/app/controllers/spree/admin/orders_controller.rb" - - "core/app/models/spree/order.rb" - - "core/lib/spree/preferences/preferable_class_methods.rb" + - 'backend/app/controllers/spree/admin/orders_controller.rb' + - 'core/app/models/spree/order.rb' + - 'core/lib/spree/preferences/preferable_class_methods.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Layout/BlockEndNewline: Exclude: - - "core/spec/lib/spree/core/importer/order_spec.rb" + - 'core/spec/lib/spree/core/importer/order_spec.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). @@ -43,15 +43,15 @@ Layout/BlockEndNewline: # SupportedStyles: case, end Layout/CaseIndentation: Exclude: - - "core/app/models/spree/payment/processing.rb" + - 'core/app/models/spree/payment/processing.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Layout/ClosingHeredocIndentation: Exclude: - - "core/solidus_core.gemspec" + - 'core/solidus_core.gemspec' -# Offense count: 95 +# Offense count: 97 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLineAfterGuardClause: Enabled: false @@ -60,26 +60,26 @@ Layout/EmptyLineAfterGuardClause: # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLineAfterMagicComment: Exclude: - - "core/db/migrate/20200320144521_add_default_billng_flag_to_user_addresses.rb" - - "core/lib/spree/testing_support/flaky.rb" + - 'core/db/migrate/20200320144521_add_default_billng_flag_to_user_addresses.rb' + - 'core/lib/spree/testing_support/flaky.rb' # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines. Layout/EmptyLineBetweenDefs: Exclude: - - "core/app/models/spree/order.rb" - - "core/db/migrate/20160420181916_migrate_credit_cards_to_wallet_payment_sources.rb" - - "core/spec/lib/spree/core/role_configuration_spec.rb" + - 'core/app/models/spree/order.rb' + - 'core/db/migrate/20160420181916_migrate_credit_cards_to_wallet_payment_sources.rb' + - 'core/spec/lib/spree/core/role_configuration_spec.rb' # Offense count: 4 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLines: Exclude: - - "backend/spec/features/admin/store_credit_reasons_spec.rb" - - "core/db/default/spree/store_credit.rb" - - "core/spec/models/spree/concerns/active_storage_adapter/attachment_spec.rb" - - "core/spec/models/spree/refund_spec.rb" + - 'backend/spec/features/admin/store_credit_reasons_spec.rb' + - 'core/db/default/spree/store_credit.rb' + - 'core/spec/models/spree/concerns/active_storage_adapter/attachment_spec.rb' + - 'core/spec/models/spree/refund_spec.rb' # Offense count: 13 # This cop supports safe autocorrection (--autocorrect). @@ -87,15 +87,15 @@ Layout/EmptyLines: # AllowedMethods: alias_method, public, protected, private Layout/EmptyLinesAroundAttributeAccessor: Exclude: - - "core/app/models/spree/address/state_validator.rb" - - "core/app/models/spree/order_updater.rb" - - "core/app/models/spree/stock_quantities.rb" - - "core/app/models/spree/variant.rb" - - "core/lib/spree/app_configuration.rb" - - "core/lib/spree/preferences/configuration.rb" - - "core/spec/lib/spree/core/validators/email_spec.rb" - - "core/spec/models/spree/preferences/statically_configurable_spec.rb" - - "core/spec/models/spree/reimbursement_type/credit_spec.rb" + - 'core/app/models/spree/address/state_validator.rb' + - 'core/app/models/spree/order_updater.rb' + - 'core/app/models/spree/stock_quantities.rb' + - 'core/app/models/spree/variant.rb' + - 'core/lib/spree/app_configuration.rb' + - 'core/lib/spree/preferences/configuration.rb' + - 'core/spec/lib/spree/core/validators/email_spec.rb' + - 'core/spec/models/spree/preferences/statically_configurable_spec.rb' + - 'core/spec/models/spree/reimbursement_type/credit_spec.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). @@ -103,17 +103,17 @@ Layout/EmptyLinesAroundAttributeAccessor: # SupportedStyles: empty_lines, no_empty_lines Layout/EmptyLinesAroundBlockBody: Exclude: - - "core/spec/models/spree/order/checkout_spec.rb" + - 'core/spec/models/spree/order/checkout_spec.rb' # Offense count: 4 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. Layout/ExtraSpacing: Exclude: - - "api/spec/requests/jbuilder_cache_spec.rb" - - "api/spec/requests/spree/api/orders_spec.rb" - - "api/spec/requests/spree/api/taxons_spec.rb" - - "backend/spec/controllers/spree/admin/store_credits_controller_spec.rb" + - 'api/spec/requests/jbuilder_cache_spec.rb' + - 'api/spec/requests/spree/api/orders_spec.rb' + - 'api/spec/requests/spree/api/taxons_spec.rb' + - 'backend/spec/controllers/spree/admin/store_credits_controller_spec.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). @@ -121,20 +121,20 @@ Layout/ExtraSpacing: # SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses Layout/FirstArgumentIndentation: Exclude: - - "core/app/models/spree/stock/availability_validator.rb" - - "core/app/models/spree/stock/inventory_validator.rb" + - 'core/app/models/spree/stock/availability_validator.rb' + - 'core/app/models/spree/stock/inventory_validator.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Layout/HeredocIndentation: Exclude: - - "core/solidus_core.gemspec" + - 'core/solidus_core.gemspec' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Layout/LeadingEmptyLines: Exclude: - - "api/spec/support/have_attributes_matcher.rb" + - 'api/spec/support/have_attributes_matcher.rb' # Offense count: 5 # This cop supports safe autocorrection (--autocorrect). @@ -142,16 +142,16 @@ Layout/LeadingEmptyLines: # SupportedStyles: aligned, indented Layout/MultilineOperationIndentation: Exclude: - - "core/lib/spree/core/engine.rb" - - "core/lib/spree/core/importer/order.rb" - - "core/lib/spree/testing_support/factories/address_factory.rb" + - 'core/lib/spree/core/engine.rb' + - 'core/lib/spree/core/importer/order.rb' + - 'core/lib/spree/testing_support/factories/address_factory.rb' # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). Layout/RescueEnsureAlignment: Exclude: - - "backend/app/controllers/spree/admin/orders_controller.rb" - - "core/app/models/spree/order.rb" + - 'backend/app/controllers/spree/admin/orders_controller.rb' + - 'core/app/models/spree/order.rb' # Offense count: 4 # This cop supports safe autocorrection (--autocorrect). @@ -160,10 +160,10 @@ Layout/RescueEnsureAlignment: # SupportedStylesForRationalLiterals: space, no_space Layout/SpaceAroundOperators: Exclude: - - "api/spec/requests/spree/api/taxons_spec.rb" - - "backend/spec/features/admin/orders/order_details_spec.rb" - - "bin/rspec" - - "core/spec/models/spree/order/number_generator_spec.rb" + - 'api/spec/requests/spree/api/taxons_spec.rb' + - 'backend/spec/features/admin/orders/order_details_spec.rb' + - 'bin/rspec' + - 'core/spec/models/spree/order/number_generator_spec.rb' # Offense count: 8 # This cop supports safe autocorrection (--autocorrect). @@ -172,11 +172,11 @@ Layout/SpaceAroundOperators: # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceInsideBlockBraces: Exclude: - - "backend/spec/features/admin/orders/shipments_spec.rb" - - "core/lib/spree/migrations.rb" - - "core/spec/lib/spree/core/testing_support/factories/address_factory_spec.rb" - - "core/spec/lib/spree/preferences/preference_differentiator_spec.rb" - - "core/spec/models/spree/variant_spec.rb" + - 'backend/spec/features/admin/orders/shipments_spec.rb' + - 'core/lib/spree/migrations.rb' + - 'core/spec/lib/spree/core/testing_support/factories/address_factory_spec.rb' + - 'core/spec/lib/spree/preferences/preference_differentiator_spec.rb' + - 'core/spec/models/spree/variant_spec.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). @@ -185,21 +185,21 @@ Layout/SpaceInsideBlockBraces: # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceInsideHashLiteralBraces: Exclude: - - "api/spec/requests/spree/api/address_books_spec.rb" + - 'api/spec/requests/spree/api/address_books_spec.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowInHeredoc. Layout/TrailingWhitespace: Exclude: - - "core/lib/spree/core/stock_configuration.rb" + - 'core/lib/spree/core/stock_configuration.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Lint/AmbiguousOperator: Exclude: - - "bin/rails-application-template" - - "bin/rspec" + - 'bin/rails-application-template' + - 'bin/rspec' # Offense count: 42 # Configuration parameters: AllowedMethods. @@ -212,88 +212,88 @@ Lint/ConstantDefinitionInBlock: # Configuration parameters: AutoCorrect, AllowComments. Lint/EmptyConditionalBody: Exclude: - - "core/lib/spree/preferences/statically_configurable.rb" + - 'core/lib/spree/preferences/statically_configurable.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Lint/RedundantCopDisableDirective: Exclude: - - "Gemfile" + - 'Gemfile' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect, IgnoreEmptyBlocks, AllowUnusedKeywordArguments. Lint/UnusedBlockArgument: Exclude: - - "core/spec/models/spree/order_spec.rb" + - 'core/spec/models/spree/order_spec.rb' # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). Performance/RegexpMatch: Exclude: - - "core/app/models/spree/credit_card.rb" - - "core/spec/lib/search/variant_spec.rb" + - 'core/app/models/spree/credit_card.rb' + - 'core/spec/lib/search/variant_spec.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Performance/StringReplacement: Exclude: - - "core/lib/spree/testing_support/common_rake.rb" + - 'core/lib/spree/testing_support/common_rake.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Rails/ApplicationController: Exclude: - - "api/app/controllers/spree/api/base_controller.rb" + - 'api/app/controllers/spree/api/base_controller.rb' # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). Rails/ApplicationJob: Exclude: - - "legacy_promotions/app/jobs/spree/promotion_code_batch_job.rb" - - "promotions/app/jobs/solidus_promotions/promotion_code_batch_job.rb" + - 'legacy_promotions/app/jobs/spree/promotion_code_batch_job.rb' + - 'promotions/app/jobs/solidus_promotions/promotion_code_batch_job.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Rails/ApplicationMailer: Exclude: - - "core/app/mailers/spree/base_mailer.rb" + - 'core/app/mailers/spree/base_mailer.rb' # Offense count: 16 # This cop supports unsafe autocorrection (--autocorrect-all). Rails/ApplicationRecord: Exclude: - - "backend/spec/controllers/spree/admin/resource_controller_spec.rb" - - "core/app/models/spree/base.rb" - - "core/db/migrate/20160420181916_migrate_credit_cards_to_wallet_payment_sources.rb" - - "core/db/migrate/20170319191942_remove_order_id_from_inventory_units.rb" - - "core/db/migrate/20170412103617_transform_tax_rate_category_relation.rb" - - "core/db/migrate/20180322142651_add_amount_remaining_to_store_credit_events.rb" - - "core/db/migrate/20180710170104_create_spree_store_credit_reasons_table.rb" - - "core/spec/lib/calculated_adjustments_spec.rb" - - "core/spec/models/spree/validations/db_maximum_length_validator_spec.rb" - - "core/spec/models/spree/wallet_payment_source_spec.rb" - - "legacy_promotions/db/migrate/20190106184413_remove_code_from_spree_promotions.rb" + - 'backend/spec/controllers/spree/admin/resource_controller_spec.rb' + - 'core/app/models/spree/base.rb' + - 'core/db/migrate/20160420181916_migrate_credit_cards_to_wallet_payment_sources.rb' + - 'core/db/migrate/20170319191942_remove_order_id_from_inventory_units.rb' + - 'core/db/migrate/20170412103617_transform_tax_rate_category_relation.rb' + - 'core/db/migrate/20180322142651_add_amount_remaining_to_store_credit_events.rb' + - 'core/db/migrate/20180710170104_create_spree_store_credit_reasons_table.rb' + - 'core/spec/lib/calculated_adjustments_spec.rb' + - 'core/spec/models/spree/validations/db_maximum_length_validator_spec.rb' + - 'core/spec/models/spree/wallet_payment_source_spec.rb' + - 'legacy_promotions/db/migrate/20190106184413_remove_code_from_spree_promotions.rb' # Offense count: 11 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: NilOrEmpty, NotPresent, UnlessPresent. Rails/Blank: Exclude: - - "core/app/models/spree/credit_card.rb" - - "core/app/models/spree/line_item.rb" - - "core/app/models/spree/order.rb" - - "core/app/models/spree/reimbursement_type/exchange.rb" - - "core/app/models/spree/wallet_payment_source.rb" - - "core/app/models/spree/zone.rb" - - "core/lib/spree/core/importer/order.rb" - - "core/lib/spree/core/search/base.rb" + - 'core/app/models/spree/credit_card.rb' + - 'core/app/models/spree/line_item.rb' + - 'core/app/models/spree/order.rb' + - 'core/app/models/spree/reimbursement_type/exchange.rb' + - 'core/app/models/spree/wallet_payment_source.rb' + - 'core/app/models/spree/zone.rb' + - 'core/lib/spree/core/importer/order.rb' + - 'core/lib/spree/core/search/base.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Rails/ContentTag: Exclude: - - "core/app/helpers/spree/base_helper.rb" + - 'core/app/helpers/spree/base_helper.rb' # Offense count: 4 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -301,9 +301,9 @@ Rails/ContentTag: # SupportedStyles: strict, flexible Rails/Date: Exclude: - - "api/spec/requests/spree/api/orders_spec.rb" - - "core/app/helpers/spree/products_helper.rb" - - "core/spec/models/spree/log_entry_spec.rb" + - 'api/spec/requests/spree/api/orders_spec.rb' + - 'core/app/helpers/spree/products_helper.rb' + - 'core/spec/models/spree/log_entry_spec.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -313,14 +313,14 @@ Rails/Date: # AllowedReceivers: Gem::Specification, page Rails/DynamicFindBy: Exclude: - - "api/spec/requests/spree/api/variants_spec.rb" + - 'api/spec/requests/spree/api/variants_spec.rb' # Offense count: 1 # Configuration parameters: Include. # Include: **/app/**/*.rb, **/config/**/*.rb, **/lib/**/*.rb Rails/Exit: Exclude: - - "core/lib/generators/spree/custom_user/custom_user_generator.rb" + - 'core/lib/generators/spree/custom_user/custom_user_generator.rb' # Offense count: 4 # This cop supports safe autocorrection (--autocorrect). @@ -328,11 +328,11 @@ Rails/Exit: # SupportedStyles: slashes, arguments Rails/FilePath: Exclude: - - "core/lib/spree/app_configuration.rb" - - "core/lib/spree/testing_support/dummy_app.rb" - - "sample/lib/spree/sample.rb" + - 'core/lib/spree/app_configuration.rb' + - 'core/lib/spree/testing_support/dummy_app.rb' + - 'sample/lib/spree/sample.rb' -# Offense count: 42 +# Offense count: 41 # Configuration parameters: Include. # Include: **/app/models/**/*.rb Rails/HasManyOrHasOneDependent: @@ -343,13 +343,13 @@ Rails/HasManyOrHasOneDependent: # Include: **/app/helpers/**/*.rb Rails/HelperInstanceVariable: Exclude: - - "backend/app/helpers/spree/admin/base_helper.rb" - - "backend/app/helpers/spree/admin/orders_helper.rb" - - "core/app/helpers/spree/base_helper.rb" - - "core/app/helpers/spree/checkout_helper.rb" - - "core/app/helpers/spree/core/controller_helpers/common.rb" - - "core/app/helpers/spree/core/controller_helpers/order.rb" - - "core/app/helpers/spree/products_helper.rb" + - 'backend/app/helpers/spree/admin/base_helper.rb' + - 'backend/app/helpers/spree/admin/orders_helper.rb' + - 'core/app/helpers/spree/base_helper.rb' + - 'core/app/helpers/spree/checkout_helper.rb' + - 'core/app/helpers/spree/core/controller_helpers/common.rb' + - 'core/app/helpers/spree/core/controller_helpers/order.rb' + - 'core/app/helpers/spree/products_helper.rb' # Offense count: 15 # This cop supports safe autocorrection (--autocorrect). @@ -357,128 +357,127 @@ Rails/HelperInstanceVariable: # SupportedStyles: numeric, symbolic Rails/HttpStatus: Exclude: - - "api/app/controllers/spree/api/coupon_codes_controller.rb" - - "api/app/controllers/spree/api/inventory_units_controller.rb" - - "api/app/controllers/spree/api/option_types_controller.rb" - - "api/app/controllers/spree/api/option_values_controller.rb" - - "api/app/controllers/spree/api/payments_controller.rb" - - "api/app/controllers/spree/api/shipments_controller.rb" - - "api/app/controllers/spree/api/stock_items_controller.rb" - - "api/app/controllers/spree/api/users_controller.rb" - - "backend/app/controllers/spree/admin/locale_controller.rb" + - 'api/app/controllers/spree/api/coupon_codes_controller.rb' + - 'api/app/controllers/spree/api/inventory_units_controller.rb' + - 'api/app/controllers/spree/api/option_types_controller.rb' + - 'api/app/controllers/spree/api/option_values_controller.rb' + - 'api/app/controllers/spree/api/payments_controller.rb' + - 'api/app/controllers/spree/api/shipments_controller.rb' + - 'api/app/controllers/spree/api/stock_items_controller.rb' + - 'api/app/controllers/spree/api/users_controller.rb' + - 'backend/app/controllers/spree/admin/locale_controller.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Rails/IndexWith: Exclude: - - "core/lib/spree/core/search/variant.rb" - - "core/lib/spree/preferences/preferable.rb" + - 'core/lib/spree/core/search/variant.rb' + - 'core/lib/spree/preferences/preferable.rb' -# Offense count: 15 +# Offense count: 13 # Configuration parameters: IgnoreScopes, Include. # Include: **/app/models/**/*.rb Rails/InverseOf: Exclude: - - "core/app/models/spree/country.rb" - - "core/app/models/spree/credit_card.rb" - - "core/app/models/spree/inventory_unit.rb" - - "core/app/models/spree/order.rb" - - "core/app/models/spree/price.rb" - - "core/app/models/spree/refund.rb" - - "core/app/models/spree/return_authorization.rb" - - "core/app/models/spree/return_item.rb" - - "core/app/models/spree/shipping_method.rb" - - "core/app/models/spree/store_credit.rb" - - "core/app/models/spree/store_credit_type.rb" - - "core/app/models/spree/variant.rb" + - 'core/app/models/spree/credit_card.rb' + - 'core/app/models/spree/inventory_unit.rb' + - 'core/app/models/spree/order.rb' + - 'core/app/models/spree/price.rb' + - 'core/app/models/spree/refund.rb' + - 'core/app/models/spree/return_authorization.rb' + - 'core/app/models/spree/return_item.rb' + - 'core/app/models/spree/shipping_method.rb' + - 'core/app/models/spree/store_credit.rb' + - 'core/app/models/spree/store_credit_type.rb' + - 'core/app/models/spree/variant.rb' # Offense count: 24 # Configuration parameters: Include. # Include: **/app/controllers/**/*.rb, **/app/mailers/**/*.rb Rails/LexicallyScopedActionFilter: Exclude: - - "backend/app/controllers/spree/admin/adjustments_controller.rb" - - "backend/app/controllers/spree/admin/customer_returns_controller.rb" - - "backend/app/controllers/spree/admin/option_types_controller.rb" - - "backend/app/controllers/spree/admin/payment_methods_controller.rb" - - "backend/app/controllers/spree/admin/product_properties_controller.rb" - - "backend/app/controllers/spree/admin/products_controller.rb" - - "backend/app/controllers/spree/admin/reimbursements_controller.rb" - - "backend/app/controllers/spree/admin/return_authorizations_controller.rb" - - "backend/app/controllers/spree/admin/shipping_methods_controller.rb" - - "backend/app/controllers/spree/admin/stock_locations_controller.rb" - - "backend/app/controllers/spree/admin/store_credits_controller.rb" - - "backend/app/controllers/spree/admin/users_controller.rb" - - "backend/app/controllers/spree/admin/variants_controller.rb" - - "backend/app/controllers/spree/admin/zones_controller.rb" + - 'backend/app/controllers/spree/admin/adjustments_controller.rb' + - 'backend/app/controllers/spree/admin/customer_returns_controller.rb' + - 'backend/app/controllers/spree/admin/option_types_controller.rb' + - 'backend/app/controllers/spree/admin/payment_methods_controller.rb' + - 'backend/app/controllers/spree/admin/product_properties_controller.rb' + - 'backend/app/controllers/spree/admin/products_controller.rb' + - 'backend/app/controllers/spree/admin/reimbursements_controller.rb' + - 'backend/app/controllers/spree/admin/return_authorizations_controller.rb' + - 'backend/app/controllers/spree/admin/shipping_methods_controller.rb' + - 'backend/app/controllers/spree/admin/stock_locations_controller.rb' + - 'backend/app/controllers/spree/admin/store_credits_controller.rb' + - 'backend/app/controllers/spree/admin/users_controller.rb' + - 'backend/app/controllers/spree/admin/variants_controller.rb' + - 'backend/app/controllers/spree/admin/zones_controller.rb' # Offense count: 9 Rails/OutputSafety: Exclude: - - "backend/app/helpers/spree/admin/navigation_helper.rb" - - "backend/config/initializers/form_builder.rb" - - "core/app/helpers/spree/base_helper.rb" - - "core/app/helpers/spree/checkout_helper.rb" - - "core/app/helpers/spree/products_helper.rb" - - "core/app/models/spree/money.rb" + - 'backend/app/helpers/spree/admin/navigation_helper.rb' + - 'backend/config/initializers/form_builder.rb' + - 'core/app/helpers/spree/base_helper.rb' + - 'core/app/helpers/spree/checkout_helper.rb' + - 'core/app/helpers/spree/products_helper.rb' + - 'core/app/models/spree/money.rb' # Offense count: 9 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: NotNilAndNotEmpty, NotBlank, UnlessBlank. Rails/Present: Exclude: - - "api/app/controllers/spree/api/shipments_controller.rb" - - "api/app/controllers/spree/api/taxonomies_controller.rb" - - "api/app/controllers/spree/api/taxons_controller.rb" - - "backend/app/helpers/spree/admin/stock_movements_helper.rb" - - "core/app/models/concerns/spree/ordered_property_value_list.rb" - - "core/app/models/spree/stock/availability_validator.rb" - - "core/lib/spree/core/search/base.rb" - - "core/spec/models/spree/stock/availability_validator_spec.rb" + - 'api/app/controllers/spree/api/shipments_controller.rb' + - 'api/app/controllers/spree/api/taxonomies_controller.rb' + - 'api/app/controllers/spree/api/taxons_controller.rb' + - 'backend/app/helpers/spree/admin/stock_movements_helper.rb' + - 'core/app/models/concerns/spree/ordered_property_value_list.rb' + - 'core/app/models/spree/stock/availability_validator.rb' + - 'core/lib/spree/core/search/base.rb' + - 'core/spec/models/spree/stock/availability_validator_spec.rb' # Offense count: 8 # This cop supports safe autocorrection (--autocorrect). Rails/RedundantForeignKey: Exclude: - - "core/app/models/spree/credit_card.rb" - - "core/app/models/spree/order.rb" - - "core/app/models/spree/return_item.rb" - - "core/app/models/spree/shipping_rate.rb" - - "core/app/models/spree/user_address.rb" - - "core/app/models/spree/wallet_payment_source.rb" + - 'core/app/models/spree/credit_card.rb' + - 'core/app/models/spree/order.rb' + - 'core/app/models/spree/return_item.rb' + - 'core/app/models/spree/shipping_rate.rb' + - 'core/app/models/spree/user_address.rb' + - 'core/app/models/spree/wallet_payment_source.rb' # Offense count: 15 # This cop supports unsafe autocorrection (--autocorrect-all). Rails/ReflectionClassName: Exclude: - - "core/app/models/spree/credit_card.rb" - - "core/app/models/spree/order.rb" - - "core/app/models/spree/role_user.rb" - - "core/app/models/spree/store_credit.rb" - - "core/app/models/spree/user_address.rb" - - "core/app/models/spree/user_stock_location.rb" - - "core/app/models/spree/wallet_payment_source.rb" - - "legacy_promotions/app/models/spree/promotion/rules/user.rb" - - "legacy_promotions/app/models/spree/promotion_rule_user.rb" - - "promotions/app/models/solidus_promotions/condition_user.rb" - - "promotions/app/models/solidus_promotions/conditions/user.rb" + - 'core/app/models/spree/credit_card.rb' + - 'core/app/models/spree/order.rb' + - 'core/app/models/spree/role_user.rb' + - 'core/app/models/spree/store_credit.rb' + - 'core/app/models/spree/user_address.rb' + - 'core/app/models/spree/user_stock_location.rb' + - 'core/app/models/spree/wallet_payment_source.rb' + - 'legacy_promotions/app/models/spree/promotion/rules/user.rb' + - 'legacy_promotions/app/models/spree/promotion_rule_user.rb' + - 'promotions/app/models/solidus_promotions/condition_user.rb' + - 'promotions/app/models/solidus_promotions/conditions/user.rb' # Offense count: 22 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: ConvertTry. Rails/SafeNavigation: Exclude: - - "core/app/models/concerns/spree/user_methods.rb" - - "core/app/models/spree/order.rb" - - "core/app/models/spree/payment.rb" - - "core/app/models/spree/payment/processing.rb" - - "core/app/models/spree/role_user.rb" - - "core/app/models/spree/shipment.rb" - - "core/app/models/spree/taxon.rb" - - "core/app/models/spree/variant/pricing_options.rb" - - "core/app/models/spree/wallet.rb" - - "core/app/models/spree/wallet/default_payment_builder.rb" - - "core/spec/models/spree/variant/vat_price_generator_spec.rb" + - 'core/app/models/concerns/spree/user_methods.rb' + - 'core/app/models/spree/order.rb' + - 'core/app/models/spree/payment.rb' + - 'core/app/models/spree/payment/processing.rb' + - 'core/app/models/spree/role_user.rb' + - 'core/app/models/spree/shipment.rb' + - 'core/app/models/spree/taxon.rb' + - 'core/app/models/spree/variant/pricing_options.rb' + - 'core/app/models/spree/wallet.rb' + - 'core/app/models/spree/wallet/default_payment_builder.rb' + - 'core/spec/models/spree/variant/vat_price_generator_spec.rb' # Offense count: 50 # Configuration parameters: ForbiddenMethods, AllowedMethods. @@ -492,10 +491,10 @@ Rails/SkipsModelValidations: # SupportedStyles: strict, flexible Rails/TimeZone: Exclude: - - "api/spec/requests/spree/api/orders_spec.rb" - - "core/spec/helpers/base_helper_spec.rb" - - "core/spec/models/spree/order/outstanding_balance_integration_spec.rb" - - "core/spec/models/spree/tax/taxation_integration_spec.rb" + - 'api/spec/requests/spree/api/orders_spec.rb' + - 'core/spec/helpers/base_helper_spec.rb' + - 'core/spec/models/spree/order/outstanding_balance_integration_spec.rb' + - 'core/spec/models/spree/tax/taxation_integration_spec.rb' # Offense count: 21 # This cop supports safe autocorrection (--autocorrect). @@ -503,19 +502,19 @@ Rails/TimeZone: # SupportedStyles: separated, grouped Style/AccessorGrouping: Exclude: - - "backend/lib/spree/backend/action_callbacks.rb" - - "core/app/models/spree/legacy_user.rb" - - "core/app/models/spree/order.rb" - - "core/lib/generators/spree/dummy/dummy_generator.rb" - - "core/lib/spree/core/search/base.rb" - - "core/lib/spree/core/stock_configuration.rb" + - 'backend/lib/spree/backend/action_callbacks.rb' + - 'core/app/models/spree/legacy_user.rb' + - 'core/app/models/spree/order.rb' + - 'core/lib/generators/spree/dummy/dummy_generator.rb' + - 'core/lib/spree/core/search/base.rb' + - 'core/lib/spree/core/stock_configuration.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowOnConstant, AllowOnSelfClass. Style/CaseEquality: Exclude: - - "core/spec/models/spree/store_credit_spec.rb" + - 'core/spec/models/spree/store_credit_spec.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -523,7 +522,7 @@ Style/CaseEquality: # AllowedMethods: ==, equal?, eql? Style/ClassEqualityComparison: Exclude: - - "core/lib/spree/core.rb" + - 'core/lib/spree/core.rb' # Offense count: 6 # This cop supports safe autocorrection (--autocorrect). @@ -531,19 +530,19 @@ Style/ClassEqualityComparison: # Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE Style/CommentAnnotation: Exclude: - - "api/app/controllers/spree/api/address_books_controller.rb" - - "backend/app/controllers/spree/admin/products_controller.rb" - - "backend/app/controllers/spree/admin/resource_controller.rb" - - "core/app/models/spree/payment_method/store_credit.rb" - - "core/lib/spree/testing_support/rake.rb" - - "core/spec/models/spree/variant/scopes_spec.rb" + - 'api/app/controllers/spree/api/address_books_controller.rb' + - 'backend/app/controllers/spree/admin/products_controller.rb' + - 'backend/app/controllers/spree/admin/resource_controller.rb' + - 'core/app/models/spree/payment_method/store_credit.rb' + - 'core/lib/spree/testing_support/rake.rb' + - 'core/spec/models/spree/variant/scopes_spec.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Style/ExplicitBlockArgument: Exclude: - - "api/app/controllers/spree/api/base_controller.rb" - - "backend/app/controllers/spree/admin/base_controller.rb" + - 'api/app/controllers/spree/api/base_controller.rb' + - 'backend/app/controllers/spree/admin/base_controller.rb' # Offense count: 18 # This cop supports safe autocorrection (--autocorrect). @@ -551,11 +550,11 @@ Style/ExplicitBlockArgument: # SupportedStyles: braces, no_braces Style/HashAsLastArrayItem: Exclude: - - "api/app/controllers/spree/api/base_controller.rb" - - "api/app/controllers/spree/api/orders_controller.rb" - - "api/spec/requests/spree/api/orders_spec.rb" - - "backend/app/controllers/spree/admin/products_controller.rb" - - "core/lib/spree/permitted_attributes.rb" + - 'api/app/controllers/spree/api/base_controller.rb' + - 'api/app/controllers/spree/api/orders_controller.rb' + - 'api/spec/requests/spree/api/orders_spec.rb' + - 'backend/app/controllers/spree/admin/products_controller.rb' + - 'core/lib/spree/permitted_attributes.rb' # Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -563,49 +562,49 @@ Style/HashAsLastArrayItem: # AllowedReceivers: Thread.current Style/HashEachMethods: Exclude: - - "api/app/controllers/spree/api/orders_controller.rb" - - "core/app/models/spree/stock/differentiator.rb" - - "core/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb" + - 'api/app/controllers/spree/api/orders_controller.rb' + - 'core/app/models/spree/stock/differentiator.rb' + - 'core/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb' # Offense count: 1 Style/MixinUsage: Exclude: - - "bin/setup" + - 'bin/setup' # Offense count: 9 # Configuration parameters: AllowedMethods. # AllowedMethods: respond_to_missing? Style/OptionalBooleanParameter: Exclude: - - "api/app/controllers/spree/api/orders_controller.rb" - - "core/app/mailers/spree/order_mailer.rb" - - "core/app/mailers/spree/reimbursement_mailer.rb" - - "core/app/models/concerns/spree/user_address_book.rb" - - "core/app/models/spree/order.rb" - - "core/app/models/spree/simple_order_contents.rb" - - "core/app/models/spree/stock/estimator.rb" + - 'api/app/controllers/spree/api/orders_controller.rb' + - 'core/app/mailers/spree/order_mailer.rb' + - 'core/app/mailers/spree/reimbursement_mailer.rb' + - 'core/app/models/concerns/spree/user_address_book.rb' + - 'core/app/models/spree/order.rb' + - 'core/app/models/spree/simple_order_contents.rb' + - 'core/app/models/spree/stock/estimator.rb' # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowSafeAssignment, AllowInMultilineConditions. Style/ParenthesesAroundCondition: Exclude: - - "core/lib/spree/testing_support/dummy_app.rb" - - "core/spec/models/spree/concerns/active_storage_adapter/attachment_spec.rb" - - "core/spec/rails_helper.rb" + - 'core/lib/spree/testing_support/dummy_app.rb' + - 'core/spec/models/spree/concerns/active_storage_adapter/attachment_spec.rb' + - 'core/spec/rails_helper.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/RedundantAssignment: Exclude: - - "core/lib/spree/core/search/base.rb" + - 'core/lib/spree/core/search/base.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Style/RedundantBegin: Exclude: - - "core/app/models/spree/user_last_url_storer/rules/authentication_rule.rb" - - "core/spec/models/spree/stock/simple_coordinator_spec.rb" + - 'core/app/models/spree/user_last_url_storer/rules/authentication_rule.rb' + - 'core/spec/models/spree/stock/simple_coordinator_spec.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). @@ -613,71 +612,71 @@ Style/RedundantBegin: # AllowedMethods: nonzero? Style/RedundantCondition: Exclude: - - "backend/app/controllers/spree/admin/reimbursements_controller.rb" - - "backend/app/controllers/spree/admin/resource_controller.rb" + - 'backend/app/controllers/spree/admin/reimbursements_controller.rb' + - 'backend/app/controllers/spree/admin/resource_controller.rb' # Offense count: 4 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: SafeForConstants. Style/RedundantFetchBlock: Exclude: - - "core/spec/models/spree/preferences/scoped_store_spec.rb" - - "core/spec/models/spree/preferences/static_model_preferences_spec.rb" + - 'core/spec/models/spree/preferences/scoped_store_spec.rb' + - 'core/spec/models/spree/preferences/static_model_preferences_spec.rb' # Offense count: 5 # This cop supports safe autocorrection (--autocorrect). Style/RedundantFileExtensionInRequire: Exclude: - - "api/solidus_api.gemspec" - - "backend/solidus_backend.gemspec" - - "core/solidus_core.gemspec" - - "sample/solidus_sample.gemspec" - - "solidus.gemspec" + - 'api/solidus_api.gemspec' + - 'backend/solidus_backend.gemspec' + - 'core/solidus_core.gemspec' + - 'sample/solidus_sample.gemspec' + - 'solidus.gemspec' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/RedundantFreeze: Exclude: - - "core/app/models/spree/payment/cancellation.rb" + - 'core/app/models/spree/payment/cancellation.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Style/RedundantInterpolation: Exclude: - - "core/lib/spree/testing_support/common_rake.rb" + - 'core/lib/spree/testing_support/common_rake.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Style/RedundantParentheses: Exclude: - - "bin/console" - - "core/lib/generators/spree/dummy/dummy_generator.rb" + - 'bin/console' + - 'core/lib/generators/spree/dummy/dummy_generator.rb' # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). Style/RedundantRegexpEscape: Exclude: - - "core/lib/spree/testing_support/translations.rb" - - "core/spec/models/spree/calculator_spec.rb" + - 'core/lib/spree/testing_support/translations.rb' + - 'core/spec/models/spree/calculator_spec.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMultipleReturnValues. Style/RedundantReturn: Exclude: - - "core/lib/spree/preferences/store.rb" + - 'core/lib/spree/preferences/store.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/RedundantSelf: Exclude: - - "core/app/models/spree/product.rb" + - 'core/app/models/spree/product.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Style/RedundantSort: Exclude: - - "core/lib/spree/testing_support/factories/state_factory.rb" + - 'core/lib/spree/testing_support/factories/state_factory.rb' # Offense count: 30 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -691,18 +690,18 @@ Style/SafeNavigation: # Configuration parameters: Mode. Style/StringConcatenation: Exclude: - - "api/spec/spec_helper.rb" - - "bin/rspec" - - "core/app/helpers/spree/base_helper.rb" - - "core/app/models/spree/credit_card.rb" - - "core/app/models/spree/variant.rb" - - "core/lib/spree/testing_support/capybara_ext.rb" - - "core/spec/models/spree/product_spec.rb" - - "core/spec/models/spree/variant_spec.rb" - - "sample/db/samples/assets.rb" + - 'api/spec/spec_helper.rb' + - 'bin/rspec' + - 'core/app/helpers/spree/base_helper.rb' + - 'core/app/models/spree/credit_card.rb' + - 'core/app/models/spree/variant.rb' + - 'core/lib/spree/testing_support/capybara_ext.rb' + - 'core/spec/models/spree/product_spec.rb' + - 'core/spec/models/spree/variant_spec.rb' + - 'sample/db/samples/assets.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Style/ZeroLengthPredicate: Exclude: - - "core/app/models/spree/fulfilment_changer.rb" + - 'core/app/models/spree/fulfilment_changer.rb' diff --git a/backend/spec/features/admin/orders/customer_details_spec.rb b/backend/spec/features/admin/orders/customer_details_spec.rb index 4c77ac4d374..ce007aeaad9 100644 --- a/backend/spec/features/admin/orders/customer_details_spec.rb +++ b/backend/spec/features/admin/orders/customer_details_spec.rb @@ -147,20 +147,6 @@ end end - context "country associated was removed" do - let(:brazil) { create(:country, iso: "BR", name: "Brazil") } - - before do - order.bill_address.country.destroy - stub_spree_preferences(default_country_iso: brazil.iso) - end - - it "sets default country when displaying form" do - click_link "Customer" - expect(page).to have_field("order_bill_address_attributes_country_id", with: brazil.id, visible: false) - end - end - # Regression test for https://github.com/spree/spree/issues/942 context "errors when no shipping methods are available" do before do diff --git a/core/app/models/spree/address.rb b/core/app/models/spree/address.rb index f46cb2ac266..2893643b071 100644 --- a/core/app/models/spree/address.rb +++ b/core/app/models/spree/address.rb @@ -11,10 +11,10 @@ class Address < Spree::Base mattr_accessor :state_validator_class self.state_validator_class = Spree::Address::StateValidator - belongs_to :country, class_name: "Spree::Country", optional: true + belongs_to :country, class_name: "Spree::Country" belongs_to :state, class_name: "Spree::State", optional: true - validates :address1, :city, :country_id, :name, presence: true + validates :address1, :city, :name, presence: true validates :zipcode, presence: true, if: :require_zipcode? validates :phone, presence: true, if: :require_phone? diff --git a/core/app/models/spree/country.rb b/core/app/models/spree/country.rb index d06fc3cd694..7fd07983fb3 100644 --- a/core/app/models/spree/country.rb +++ b/core/app/models/spree/country.rb @@ -2,9 +2,19 @@ module Spree class Country < Spree::Base - has_many :states, -> { order(:name) }, dependent: :destroy - has_many :addresses, dependent: :nullify - has_many :prices, class_name: "Spree::Price", foreign_key: "country_iso", primary_key: "iso" + has_many :states, + -> { order(:name) }, + dependent: :destroy, + inverse_of: :country + has_many :addresses, + dependent: :restrict_with_error, + inverse_of: :country + has_many :prices, + class_name: "Spree::Price", + foreign_key: "country_iso", + primary_key: "iso", + dependent: :restrict_with_error, + inverse_of: :country validates :name, :iso_name, presence: true diff --git a/core/app/models/spree/state.rb b/core/app/models/spree/state.rb index fed9793c390..de4d2365488 100644 --- a/core/app/models/spree/state.rb +++ b/core/app/models/spree/state.rb @@ -2,10 +2,10 @@ module Spree class State < Spree::Base - belongs_to :country, class_name: 'Spree::Country', optional: true + belongs_to :country, class_name: 'Spree::Country' has_many :addresses, dependent: :nullify - validates :country, :name, presence: true + validates :name, presence: true scope :with_name_or_abbr, ->(name_or_abbr) do where( From 7771010e3b1e8d1da08c595d3610b8a9065cbcf4 Mon Sep 17 00:00:00 2001 From: Martin Meyerhoff Date: Wed, 9 Jul 2025 11:42:30 +0200 Subject: [PATCH 2/4] Change spree_countries.iso column to be unique We're using `spree_countries.iso` as a primary key between prices and countries. In order to add a foreign key constraint, primary keys have to be unique. This adds the necessary uniqueness constraint. A few tests had to be amended to not create the same country many times. --- admin/spec/features/zones_spec.rb | 11 +++++++---- .../spec/requests/solidus_admin/zones_spec.rb | 19 +++++++++++++------ api/spec/requests/spree/api/countries_spec.rb | 2 +- api/spec/requests/spree/api/states_spec.rb | 5 +++-- core/app/models/spree/country.rb | 3 ++- ...28094037_change_countries_iso_to_unique.rb | 9 +++++++++ core/spec/helpers/base_helper_spec.rb | 6 ++++-- .../lib/spree/core/importer/order_spec.rb | 2 +- core/spec/models/spree/country_spec.rb | 7 +++---- core/spec/models/spree/credit_card_spec.rb | 2 +- core/spec/models/spree/price_spec.rb | 2 +- core/spec/models/spree/tax_rate_spec.rb | 13 ++++++++----- core/spec/models/spree/zone_spec.rb | 12 +++++++----- 13 files changed, 60 insertions(+), 33 deletions(-) create mode 100644 core/db/migrate/20250628094037_change_countries_iso_to_unique.rb diff --git a/admin/spec/features/zones_spec.rb b/admin/spec/features/zones_spec.rb index 8cac03c4132..3d30e3deacb 100644 --- a/admin/spec/features/zones_spec.rb +++ b/admin/spec/features/zones_spec.rb @@ -4,17 +4,20 @@ describe "Zones", :js, type: :feature do before { sign_in create(:admin_user, email: 'admin@example.com') } + let(:canada) { create(:country, iso: "CA") } + let(:france) { create(:country, iso: "FR") } + let(:usa) { create(:country) } let(:states) do [ - create(:state, name: "Alberta", country: create(:country, iso: "CA")), - create(:state, name: "Manitoba", country: create(:country, iso: "CA")) + create(:state, name: "Alberta", country: canada), + create(:state, name: "Manitoba", country: canada) ] end it "lists zones and allows deleting them" do - create(:zone, name: "Europe") - create(:zone, name: "North America") + create(:zone, name: "Europe", countries: [france]) + create(:zone, name: "North America", countries: [usa, canada]) visit "/admin/zones" expect(page).to have_content("Europe") diff --git a/admin/spec/requests/solidus_admin/zones_spec.rb b/admin/spec/requests/solidus_admin/zones_spec.rb index 7c75fb6e063..06f780b0c9a 100644 --- a/admin/spec/requests/solidus_admin/zones_spec.rb +++ b/admin/spec/requests/solidus_admin/zones_spec.rb @@ -5,7 +5,9 @@ RSpec.describe "SolidusAdmin::ZonesController", type: :request do include_examples "CRUD resource requests", "zone" do - let(:countries) { create_list(:country, 2) } + let(:usa) { create(:country) } + let(:canada) { create(:country, iso: "CA") } + let(:countries) { [usa, canada] } let(:resource_class) { Spree::Zone } let(:valid_attributes) { { name: "Zone with countries", country_ids: countries.map(&:id) } } let(:invalid_attributes) { { name: "" } } @@ -15,16 +17,21 @@ end it "updates zone members" do - zone = create(:zone, :with_country) + brazil = create(:country, iso: "BR") + zone = create(:zone, countries: [brazil]) expect { patch solidus_admin.zone_path(zone), params: { zone: valid_attributes } }.to change(Spree::ZoneMember, :count).by(1) end end context "N+1" do - before do - create_list(:zone, 2, :with_country) - create_list(:zone, 2, :with_state) - end + let(:usa) { create(:country) } + let(:canada) { create(:country, iso: "CA") } + let(:new_york) { create(:state, state_code: "NY", country: usa) } + let(:north_carolina) { create(:state, state_code: "NC", country: usa) } + let!(:usa_zone) { create(:zone, countries: [usa]) } + let!(:canada_zone) { create(:zone, countries: [canada]) } + let!(:new_york_zone) { create(:zone, states: [new_york]) } + let!(:north_carolina_zone) { create(:zone, states: [north_carolina]) } let(:expected_count) do [ diff --git a/api/spec/requests/spree/api/countries_spec.rb b/api/spec/requests/spree/api/countries_spec.rb index e6a77f87fb7..bbb66ecf808 100644 --- a/api/spec/requests/spree/api/countries_spec.rb +++ b/api/spec/requests/spree/api/countries_spec.rb @@ -16,7 +16,7 @@ module Spree::Api end context "with two countries" do - before { @zambia = create(:country, name: "Zambia") } + before { @zambia = create(:country, iso: "ZA", name: "Zambia") } it "can view all countries" do get spree.api_countries_path diff --git a/api/spec/requests/spree/api/states_spec.rb b/api/spec/requests/spree/api/states_spec.rb index 5340362ff1d..117b8001fee 100644 --- a/api/spec/requests/spree/api/states_spec.rb +++ b/api/spec/requests/spree/api/states_spec.rb @@ -4,7 +4,8 @@ module Spree::Api describe 'States', type: :request do - let!(:state) { create(:state, name: "Victoria") } + let(:australia) { create(:country, iso: "AU") } + let!(:state) { create(:state, country: australia, name: "Victoria") } let(:attributes) { [:id, :name, :abbr, :country_id] } before do @@ -37,7 +38,7 @@ module Spree::Api end context "with two states" do - before { create(:state, name: "New South Wales") } + before { create(:state, country: australia, name: "New South Wales") } it "gets all states for a country" do country = create(:country, states_required: true) diff --git a/core/app/models/spree/country.rb b/core/app/models/spree/country.rb index 7fd07983fb3..88f640e275a 100644 --- a/core/app/models/spree/country.rb +++ b/core/app/models/spree/country.rb @@ -16,7 +16,8 @@ class Country < Spree::Base dependent: :restrict_with_error, inverse_of: :country - validates :name, :iso_name, presence: true + validates :name, :iso_name, :iso, presence: true + validates :iso, uniqueness: true self.allowed_ransackable_attributes = %w[name] diff --git a/core/db/migrate/20250628094037_change_countries_iso_to_unique.rb b/core/db/migrate/20250628094037_change_countries_iso_to_unique.rb new file mode 100644 index 00000000000..02fa0068fca --- /dev/null +++ b/core/db/migrate/20250628094037_change_countries_iso_to_unique.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class ChangeCountriesIsoToUnique < ActiveRecord::Migration[7.0] + def change + remove_index :spree_countries, :iso + + add_index :spree_countries, :iso, unique: true + end +end diff --git a/core/spec/helpers/base_helper_spec.rb b/core/spec/helpers/base_helper_spec.rb index 81219b4c43d..42525ebc31e 100644 --- a/core/spec/helpers/base_helper_spec.rb +++ b/core/spec/helpers/base_helper_spec.rb @@ -11,7 +11,9 @@ let(:country) { create(:country) } before do - 3.times { create(:country) } + create(:country, iso: "BR") + create(:country, iso: "DE") + create(:country, iso: "FR") end context "with no checkout zone defined" do @@ -24,7 +26,7 @@ end it "uses locales for country names" do - expect(available_countries).to include(having_attributes(name: "United States of America")) + expect(available_countries).to include(having_attributes(name: "Brazil")) end end diff --git a/core/spec/lib/spree/core/importer/order_spec.rb b/core/spec/lib/spree/core/importer/order_spec.rb index 56c244fb20d..b06645ee335 100644 --- a/core/spec/lib/spree/core/importer/order_spec.rb +++ b/core/spec/lib/spree/core/importer/order_spec.rb @@ -226,7 +226,7 @@ module Core } end - let(:other_state) { create(:state, name: "Uhuhuh", country: create(:country)) } + let(:other_state) { create(:state, name: "Uhuhuh", country: create(:country, iso: "BR")) } before do ship_address.delete(:state_id) diff --git a/core/spec/models/spree/country_spec.rb b/core/spec/models/spree/country_spec.rb index 7166ee77a68..8e00086866c 100644 --- a/core/spec/models/spree/country_spec.rb +++ b/core/spec/models/spree/country_spec.rb @@ -104,16 +104,15 @@ end describe '#prices' do - let(:country) { create(:country) } + let(:country) { create(:country, iso: "BR") } subject { country.prices } it { is_expected.to be_a(ActiveRecord::Associations::CollectionProxy) } context "if the country has associated prices" do - let!(:price_one) { create(:price) } - let!(:price_two) { create(:price) } + let!(:price_one) { create(:price, country:) } + let!(:price_two) { create(:price, country:) } let!(:price_three) { create(:price) } - let(:country) { create(:country, prices: [price_one, price_two]) } it { is_expected.to contain_exactly(price_one, price_two) } end diff --git a/core/spec/models/spree/credit_card_spec.rb b/core/spec/models/spree/credit_card_spec.rb index 6227e6d7791..0395bf8949c 100644 --- a/core/spec/models/spree/credit_card_spec.rb +++ b/core/spec/models/spree/credit_card_spec.rb @@ -88,7 +88,7 @@ def self.payment_states end let!(:persisted_card) { Spree::CreditCard.find(credit_card.id) } - let(:country) { create(:country, states_required: true) } + let(:country) { create(:country, iso: "BR", states_required: true) } let(:state) { create(:state, country:) } let(:valid_address_attributes) do { diff --git a/core/spec/models/spree/price_spec.rb b/core/spec/models/spree/price_spec.rb index 863d49c9c99..ed0bc478fa1 100644 --- a/core/spec/models/spree/price_spec.rb +++ b/core/spec/models/spree/price_spec.rb @@ -169,7 +169,7 @@ describe 'scopes' do describe '.for_any_country' do - let(:country) { create(:country) } + let(:country) { create(:country, iso: "BR") } let!(:fallback_price) { create(:price, country_iso: nil) } let!(:country_price) { create(:price, country:) } diff --git a/core/spec/models/spree/tax_rate_spec.rb b/core/spec/models/spree/tax_rate_spec.rb index 7205b8896f4..74596739efb 100644 --- a/core/spec/models/spree/tax_rate_spec.rb +++ b/core/spec/models/spree/tax_rate_spec.rb @@ -64,12 +64,14 @@ end context "when no rate zones match the tax zone" do - let(:rate_zone) { create(:zone, :with_country) } + let(:usa) { create(:country) } + let(:rate_zone) { create(:zone, countries: [usa]) } let!(:rate) { create :tax_rate, zone: rate_zone } context "when there is no default tax zone" do context "and the zone has no shared members with the rate zone" do - let(:zone) { create(:zone, :with_country) } + let(:canada) { create(:country, iso: "CA") } + let(:zone) { create(:zone, countries: [canada]) } it "should return an empty array" do expect(subject).to eq([]) @@ -94,8 +96,8 @@ end context "when the tax_zone is contained within a rate zone" do - let(:country1) { create :country } - let(:country2) { create :country } + let(:country1) { create :country, iso: "FR" } + let(:country2) { create :country, iso: "BR" } let(:rate_zone) { create(:zone, countries: [country1, country2]) } let(:zone) { create(:zone, countries: [country1]) } @@ -127,7 +129,8 @@ end context "when the zone is outside the default zone" do - let(:zone) { create(:zone, :with_country) } + let(:brazil) { create(:country, iso: "BR") } + let(:zone) { create(:zone, countries: [brazil]) } it { is_expected.to be_empty } end diff --git a/core/spec/models/spree/zone_spec.rb b/core/spec/models/spree/zone_spec.rb index ca15af5ee6d..b17b80d45eb 100644 --- a/core/spec/models/spree/zone_spec.rb +++ b/core/spec/models/spree/zone_spec.rb @@ -4,9 +4,11 @@ RSpec.describe Spree::Zone, type: :model do describe 'for_address' do - let(:new_york_address) { create(:address, state_code: "NY") } + let(:canada) { create(:country, iso: "CA") } + let(:usa) { create(:country, iso: "US") } + let(:new_york_address) { create(:address, state_code: "NY", country: usa) } let(:alabama_address) { create(:address) } - let(:canada_address) { create(:address, country_iso_code: "CA") } + let(:canada_address) { create(:address, country: canada) } let!(:new_york_zone) { create(:zone, states: [new_york_address.state]) } let!(:alabama_zone) { create(:zone, states: [alabama_address.state]) } @@ -101,7 +103,7 @@ it "should remove existing state members" do zone = create(:zone, name: 'foo', zone_members: []) state = create(:state) - country = create(:country) + country = create(:country, iso: "BR") zone.members.create(zoneable: state) country_member = zone.members.create(zoneable: country) zone.save @@ -159,8 +161,8 @@ context ".with_shared_members" do let!(:country) { create(:country) } - let!(:country2) { create(:country, name: 'OtherCountry') } - let!(:country3) { create(:country, name: 'TaxCountry') } + let!(:country2) { create(:country, iso: "MX", name: 'OtherCountry') } + let!(:country3) { create(:country, iso: "CA", name: 'TaxCountry') } subject(:zones_with_shared_members) { Spree::Zone.with_shared_members(zone) } From 5846069c86e93ee810c2c4e3adb8c0601fca9894 Mon Sep 17 00:00:00 2001 From: Martin Meyerhoff Date: Wed, 9 Jul 2025 12:20:13 +0200 Subject: [PATCH 3/4] Add foreign key constraint point to country table This adds database-level foreign key constraints to tables that reference the countries table. If trying to delete a country, and there are addresses referencing that country, we want to restrict that. Addresses are immutable and must stay valid. If there are prices referencing the country, the same applies, we want to restrict. If the only thing holding us back from deleting a country is states, we can delete the state records as well. --- ...20250709073151_add_country_foreign_keys.rb | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 core/db/migrate/20250709073151_add_country_foreign_keys.rb diff --git a/core/db/migrate/20250709073151_add_country_foreign_keys.rb b/core/db/migrate/20250709073151_add_country_foreign_keys.rb new file mode 100644 index 00000000000..18bb573060d --- /dev/null +++ b/core/db/migrate/20250709073151_add_country_foreign_keys.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +class AddCountryForeignKeys < ActiveRecord::Migration[7.0] + FOREIGN_KEY_VIOLATION_ERRORS = %w[PG::ForeignKeyViolation Mysql2::Error SQLite3::ConstraintException] + + def up + # Uncomment the following code to remove orphaned records if this migration fails + # + # say_with_time "Removing orphaned states (no corresponding country)" do + # Spree::State.left_joins(:country).where(spree_countries: { id: nil }).delete_all + # end + + begin + add_foreign_key :spree_states, :spree_countries, column: :country_id, null: false, on_delete: :cascade + rescue ActiveRecord::StatementInvalid => e + if e.cause.class.name.in?(FOREIGN_KEY_VIOLATION_ERRORS) + say <<~MSG + ⚠️ Foreign key constraint failed when adding :spree_states => :spree_countries. + To fix this: + 1. Uncomment the code that removes orphaned records. + 2. Rerun the migration. + Offending error: #{e.cause.class} - #{e.cause.message} + MSG + end + raise + end + + # Uncomment the following code to remove orphaned records if this migration fails + # + # say_with_time "Updating orphaned addresses (no corresponding country) to use default country" do + # Spree::Address.left_joins(:country).where(spree_countries: { id: nil }).update_all(country: Spree::Country.default) + # end + + begin + add_foreign_key :spree_addresses, :spree_countries, column: :country_id, null: false, on_delete: :restrict + rescue ActiveRecord::StatementInvalid => e + if e.cause.class.name.in?(FOREIGN_KEY_VIOLATION_ERRORS) + say <<~MSG + ⚠️ Foreign key constraint failed when adding :spree_addresses => :spree_countries. + To fix this: + 1. Uncomment the code that removes orphaned records. + 2. Rerun the migration. + Offending error: #{e.cause.class} - #{e.cause.message} + MSG + end + raise + end + # Uncomment the following code to remove orphaned records if this migration fails + # + # say_with_time "Deleting orphaned prices (country ID without corresponding country)" do + # Spree::Price.where.not(country_iso: nil).left_joins(:country).where(spree_countries: { iso: nil }).update_all(country_iso: Spree::Config.default_country_iso) + # end + + begin + add_foreign_key :spree_prices, :spree_countries, column: :country_iso, primary_key: :iso, null: true, on_delete: :restrict + rescue ActiveRecord::StatementInvalid => e + if e.cause.class.name.in?(FOREIGN_KEY_VIOLATION_ERRORS) + say <<~MSG + ⚠️ Foreign key constraint failed when adding :spree_prices => :spree_countries. + To fix this: + 1. Uncomment the code that removes orphaned records. + 2. Rerun the migration. + Offending error: #{e.cause.class} - #{e.cause.message} + MSG + end + raise + end + end + + def down + remove_foreign_key :spree_states, :spree_countries, column: :country_id, null: false, on_delete: :cascade + remove_foreign_key :spree_addresses, :spree_countries, column: :country_id, null: false, on_delete: :restrict + remove_foreign_key :spree_prices, :spree_countries, column: :country_id, null: true, on_delete: :restrict + end +end From 737f092c4c680e238085cedbbc81be097a330f9f Mon Sep 17 00:00:00 2001 From: Martin Meyerhoff Date: Wed, 9 Jul 2025 10:50:14 +0200 Subject: [PATCH 4/4] Add inverse_of to state/address, add FK constraint For the database, a state is optional on an address. What this commit ensures is that any state ID entered into the `state_id` column on `spree_addresses` is actually present in the `spree_states` table. --- core/app/models/spree/state.rb | 2 +- .../20250709084513_add_state_foreign_keys.rb | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 core/db/migrate/20250709084513_add_state_foreign_keys.rb diff --git a/core/app/models/spree/state.rb b/core/app/models/spree/state.rb index de4d2365488..e6123cd899d 100644 --- a/core/app/models/spree/state.rb +++ b/core/app/models/spree/state.rb @@ -3,7 +3,7 @@ module Spree class State < Spree::Base belongs_to :country, class_name: 'Spree::Country' - has_many :addresses, dependent: :nullify + has_many :addresses, dependent: :nullify, inverse_of: :state validates :name, presence: true diff --git a/core/db/migrate/20250709084513_add_state_foreign_keys.rb b/core/db/migrate/20250709084513_add_state_foreign_keys.rb new file mode 100644 index 00000000000..baeb812379e --- /dev/null +++ b/core/db/migrate/20250709084513_add_state_foreign_keys.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class AddStateForeignKeys < ActiveRecord::Migration[7.0] + FOREIGN_KEY_VIOLATION_ERRORS = %w[PG::ForeignKeyViolation Mysql2::Error SQLite3::ConstraintException] + + def up + # Uncomment the following code to remove orphaned records if this migration fails + # + # say_with_time "Resetting state IDs on addresses where the state record is no longer present" do + # Spree::Address.where.not(state_id: nil).left_joins(:state).where(spree_states: { id: nil }).update_all(state_id: nil) + # end + + add_foreign_key :spree_addresses, :spree_states, column: :state_id, null: true, on_delete: :restrict + rescue ActiveRecord::StatementInvalid => e + if e.cause.class.name.in?(FOREIGN_KEY_VIOLATION_ERRORS) + say <<~MSG + ⚠️ Foreign key constraint failed when adding :spree_addresses => :spree_states. + To fix this: + 1. Uncomment the code that removes orphaned records. + 2. Rerun the migration. + Offending error: #{e.cause.class} - #{e.cause.message} + MSG + end + raise + end + + def down + remove_foreign_key :spree_addresses, :spree_states, column: :state_id, null: true, on_delete: :restrict + end +end