spree_core 5.3.6 → 5.4.0.beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/finders/spree/orders/find_complete.rb +33 -2
- data/app/finders/spree/stores/find_default.rb +17 -0
- data/app/finders/spree/variants/visible_finder.rb +1 -0
- data/app/helpers/spree/addresses_helper.rb +1 -2
- data/app/helpers/spree/base_helper.rb +5 -4
- data/app/jobs/spree/api_key_touch_job.rb +9 -0
- data/app/jobs/spree/images/save_from_url_job.rb +23 -47
- data/app/models/concerns/spree/admin_user_methods.rb +32 -0
- data/app/models/concerns/spree/number_as_param.rb +5 -3
- data/app/models/concerns/spree/prefixed_id.rb +82 -0
- data/app/models/concerns/spree/product_scopes.rb +33 -18
- data/app/models/concerns/spree/publishable.rb +47 -47
- data/app/models/concerns/spree/{multi_store_resource.rb → store_scoped_resource.rb} +1 -10
- data/app/models/concerns/spree/stores/markets.rb +124 -0
- data/app/models/concerns/spree/user_methods.rb +7 -7
- data/app/models/concerns/spree/user_payment_source.rb +2 -0
- data/app/models/concerns/spree/user_roles.rb +1 -0
- data/app/models/spree/ability.rb +13 -0
- data/app/models/spree/address.rb +30 -0
- data/app/models/spree/adjustment.rb +2 -0
- data/app/models/spree/api_key.rb +47 -0
- data/app/models/spree/asset.rb +2 -0
- data/app/models/spree/authentication/strategies/base_strategy.rb +55 -0
- data/app/models/spree/authentication/strategies/email_password_strategy.rb +47 -0
- data/app/models/spree/base.rb +1 -0
- data/app/models/spree/calculator.rb +2 -0
- data/app/models/spree/country.rb +56 -0
- data/app/models/spree/coupon_code.rb +2 -0
- data/app/models/spree/credit_card.rb +2 -0
- data/app/models/spree/current.rb +9 -4
- data/app/models/spree/customer_group.rb +2 -0
- data/app/models/spree/customer_return.rb +2 -0
- data/app/models/spree/data_feed.rb +2 -0
- data/app/models/spree/digital.rb +2 -0
- data/app/models/spree/digital_link.rb +2 -0
- data/app/models/spree/export.rb +5 -4
- data/app/models/spree/fulfilment_changer.rb +8 -2
- data/app/models/spree/gateway/bogus.rb +60 -0
- data/app/models/spree/gateway_customer.rb +2 -0
- data/app/models/spree/gift_card.rb +2 -0
- data/app/models/spree/gift_card_batch.rb +2 -4
- data/app/models/spree/image.rb +18 -0
- data/app/models/spree/import.rb +5 -3
- data/app/models/spree/import_mapping.rb +2 -0
- data/app/models/spree/import_row.rb +2 -0
- data/app/models/spree/import_schemas/customers.rb +21 -0
- data/app/models/spree/imports/customers.rb +9 -0
- data/app/models/spree/integration.rb +2 -0
- data/app/models/spree/inventory_unit.rb +2 -0
- data/app/models/spree/invitation.rb +6 -6
- data/app/models/spree/legacy_admin_user.rb +31 -0
- data/app/models/spree/legacy_user.rb +19 -1
- data/app/models/spree/line_item.rb +15 -4
- data/app/models/spree/log_entry.rb +2 -0
- data/app/models/spree/market.rb +83 -0
- data/app/models/spree/market_country.rb +25 -0
- data/app/models/spree/metafield.rb +2 -0
- data/app/models/spree/metafield_definition.rb +2 -0
- data/app/models/spree/newsletter_subscriber.rb +2 -0
- data/app/models/spree/option_type.rb +2 -0
- data/app/models/spree/option_value.rb +2 -0
- data/app/models/spree/order/address_book.rb +2 -1
- data/app/models/spree/order.rb +75 -47
- data/app/models/spree/payment.rb +6 -1
- data/app/models/spree/payment_capture_event.rb +2 -0
- data/app/models/spree/payment_method.rb +59 -1
- data/app/models/spree/payment_session.rb +109 -0
- data/app/models/spree/payment_sessions/bogus.rb +4 -0
- data/app/models/spree/payment_setup_session.rb +79 -0
- data/app/models/spree/payment_source.rb +2 -0
- data/app/models/spree/permission_sets/default_customer.rb +19 -0
- data/app/models/spree/policy.rb +2 -0
- data/app/models/spree/post.rb +2 -0
- data/app/models/spree/post_category.rb +2 -0
- data/app/models/spree/price.rb +26 -2
- data/app/models/spree/price_list.rb +2 -0
- data/app/models/spree/price_rule.rb +2 -0
- data/app/models/spree/price_rules/market_rule.rb +19 -0
- data/app/models/spree/product.rb +41 -29
- data/app/models/spree/promotion/rules/country.rb +1 -1
- data/app/models/spree/promotion.rb +3 -1
- data/app/models/spree/promotion_action.rb +2 -0
- data/app/models/spree/promotion_category.rb +2 -0
- data/app/models/spree/promotion_rule.rb +2 -0
- data/app/models/spree/prototype.rb +2 -0
- data/app/models/spree/refund.rb +2 -4
- data/app/models/spree/refund_reason.rb +2 -0
- data/app/models/spree/reimbursement/credit.rb +2 -0
- data/app/models/spree/reimbursement.rb +2 -0
- data/app/models/spree/reimbursement_type.rb +2 -0
- data/app/models/spree/report.rb +3 -1
- data/app/models/spree/return_authorization.rb +2 -0
- data/app/models/spree/return_authorization_reason.rb +2 -0
- data/app/models/spree/return_item.rb +2 -0
- data/app/models/spree/role.rb +2 -0
- data/app/models/spree/shipment.rb +11 -1
- data/app/models/spree/shipping_category.rb +2 -0
- data/app/models/spree/shipping_method.rb +2 -0
- data/app/models/spree/shipping_method_category.rb +2 -0
- data/app/models/spree/shipping_rate.rb +2 -0
- data/app/models/spree/state_change.rb +2 -0
- data/app/models/spree/stock_item.rb +2 -0
- data/app/models/spree/stock_location.rb +2 -0
- data/app/models/spree/stock_movement.rb +2 -0
- data/app/models/spree/stock_transfer.rb +2 -1
- data/app/models/spree/store.rb +110 -179
- data/app/models/spree/store_credit.rb +2 -4
- data/app/models/spree/store_credit_category.rb +2 -0
- data/app/models/spree/store_credit_event.rb +2 -0
- data/app/models/spree/store_credit_type.rb +2 -0
- data/app/models/spree/store_product.rb +2 -0
- data/app/models/spree/tax_category.rb +2 -0
- data/app/models/spree/tax_rate.rb +2 -0
- data/app/models/spree/taxon.rb +4 -1
- data/app/models/spree/taxon_image.rb +8 -0
- data/app/models/spree/taxon_rule.rb +2 -0
- data/app/models/spree/taxonomy.rb +2 -0
- data/app/models/spree/user_identity.rb +81 -0
- data/app/models/spree/variant.rb +13 -3
- data/app/models/spree/webhook_delivery.rb +2 -0
- data/app/models/spree/webhook_endpoint.rb +2 -17
- data/app/models/spree/wished_item.rb +15 -0
- data/app/models/spree/wishlist.rb +2 -8
- data/app/models/spree/zone.rb +2 -6
- data/app/presenters/spree/filters/price_presenter.rb +1 -0
- data/app/presenters/spree/filters/price_range_presenter.rb +1 -0
- data/app/presenters/spree/filters/quantified_price_range_presenter.rb +1 -0
- data/app/presenters/spree/product_summary_presenter.rb +1 -0
- data/app/services/spree/addresses/helper.rb +22 -3
- data/app/services/spree/cart/associate.rb +19 -6
- data/app/services/spree/checkout/select_shipping_method.rb +13 -1
- data/app/services/spree/classifications/reposition.rb +5 -0
- data/app/services/spree/data_feeds/google/required_attributes.rb +8 -3
- data/app/services/spree/gift_cards/apply.rb +4 -5
- data/app/services/spree/imports/row_processors/customer.rb +70 -0
- data/app/services/spree/orders/approve.rb +5 -3
- data/app/services/spree/orders/cancel.rb +9 -4
- data/app/services/spree/products/prepare_nested_attributes.rb +1 -1
- data/app/services/spree/sample_data/import_runner.rb +54 -0
- data/app/services/spree/sample_data/loader.rb +78 -0
- data/app/services/spree/seeds/admin_user.rb +2 -3
- data/app/services/spree/seeds/all.rb +1 -0
- data/app/services/spree/seeds/api_keys.rb +16 -0
- data/app/services/spree/seeds/stores.rb +2 -4
- data/app/sorters/spree/orders/sort.rb +4 -0
- data/app/subscribers/spree/product_metrics_subscriber.rb +4 -4
- data/config/brakeman.ignore +120 -0
- data/config/locales/en.yml +20 -5
- data/db/migrate/20250923141900_create_spree_user_identities.rb +17 -0
- data/db/migrate/20260123000000_create_spree_api_keys.rb +19 -0
- data/db/migrate/20260131000000_add_thumbnail_id_to_spree_variants_and_products.rb +9 -0
- data/db/migrate/20260213000000_create_spree_payment_sessions.rb +27 -0
- data/db/migrate/20260218000000_create_spree_payment_setup_sessions.rb +24 -0
- data/db/migrate/20260220000000_create_spree_markets.rb +29 -0
- data/db/sample_data/customers.csv +21 -0
- data/db/sample_data/metafield_definitions.rb +7 -0
- data/db/sample_data/orders.rb +131 -0
- data/db/sample_data/payment_methods.rb +17 -0
- data/db/sample_data/posts.rb +7 -0
- data/db/sample_data/products.csv +1083 -0
- data/db/sample_data/promotions.rb +8 -0
- data/db/sample_data/shipping_methods.rb +39 -0
- data/lib/generators/spree/authentication/devise/devise_generator.rb +2 -2
- data/lib/generators/spree/authentication/dummy/dummy_generator.rb +54 -0
- data/lib/generators/spree/authentication/dummy/templates/authentication_helpers.rb.tt +52 -0
- data/lib/generators/spree/authentication/dummy/templates/create_spree_admin_users.rb.tt +33 -0
- data/lib/generators/spree/dummy/dummy_generator.rb +1 -1
- data/lib/spree/core/configuration.rb +1 -1
- data/lib/spree/core/controller_helpers/common.rb +6 -0
- data/lib/spree/core/controller_helpers/currency.rb +5 -0
- data/lib/spree/core/controller_helpers/order.rb +5 -1
- data/lib/spree/core/controller_helpers/store.rb +1 -1
- data/lib/spree/core/dependencies.rb +1 -1
- data/lib/spree/core/engine.rb +17 -4
- data/lib/spree/core/pricing/context.rb +6 -3
- data/lib/spree/core/product_filters.rb +14 -0
- data/lib/spree/core/query_filters/comparable.rb +4 -0
- data/lib/spree/core/query_filters/text.rb +4 -0
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +4 -4
- data/lib/spree/database_type_utilities.rb +7 -0
- data/lib/spree/events.rb +17 -10
- data/lib/spree/money.rb +2 -9
- data/lib/spree/permitted_attributes.rb +19 -7
- data/lib/spree/testing_support/capybara_config.rb +2 -2
- data/lib/spree/testing_support/common_rake.rb +15 -4
- data/lib/spree/testing_support/factories/api_key_factory.rb +19 -0
- data/lib/spree/testing_support/factories/custom_domain_factory.rb +7 -5
- data/lib/spree/testing_support/factories/import_factory.rb +12 -0
- data/lib/spree/testing_support/factories/market_factory.rb +35 -0
- data/lib/spree/testing_support/factories/order_factory.rb +3 -1
- data/lib/spree/testing_support/factories/payment_method_factory.rb +2 -0
- data/lib/spree/testing_support/factories/payment_session_factory.rb +47 -0
- data/lib/spree/testing_support/factories/payment_setup_session_factory.rb +31 -0
- data/lib/spree/testing_support/factories/price_rule_factory.rb +10 -0
- data/lib/spree/testing_support/factories/user_identity_factory.rb +15 -0
- data/lib/spree/testing_support/store.rb +3 -2
- data/lib/spree/webhooks.rb +7 -7
- data/lib/tasks/images.rake +20 -0
- data/lib/tasks/markets.rake +40 -0
- data/lib/tasks/sample_data.rake +15 -0
- data/spec/fixtures/files/customers_import.csv +4 -0
- metadata +88 -63
- data/LICENSE.md +0 -57
- data/app/finders/spree/stores/find_current.rb +0 -28
- data/app/models/spree/custom_domain.rb +0 -59
- data/app/presenters/spree/csv/formula_sanitizer.rb +0 -28
- data/app/serializers/spree/events/asset_serializer.rb +0 -22
- data/app/serializers/spree/events/base_serializer.rb +0 -61
- data/app/serializers/spree/events/customer_return_serializer.rb +0 -20
- data/app/serializers/spree/events/digital_link_serializer.rb +0 -20
- data/app/serializers/spree/events/digital_serializer.rb +0 -18
- data/app/serializers/spree/events/export_serializer.rb +0 -22
- data/app/serializers/spree/events/gift_card_batch_serializer.rb +0 -24
- data/app/serializers/spree/events/gift_card_serializer.rb +0 -29
- data/app/serializers/spree/events/image_serializer.rb +0 -9
- data/app/serializers/spree/events/import_row_serializer.rb +0 -23
- data/app/serializers/spree/events/import_serializer.rb +0 -24
- data/app/serializers/spree/events/invitation_serializer.rb +0 -28
- data/app/serializers/spree/events/line_item_serializer.rb +0 -31
- data/app/serializers/spree/events/newsletter_subscriber_serializer.rb +0 -21
- data/app/serializers/spree/events/order_serializer.rb +0 -39
- data/app/serializers/spree/events/payment_serializer.rb +0 -24
- data/app/serializers/spree/events/post_category_serializer.rb +0 -20
- data/app/serializers/spree/events/post_serializer.rb +0 -26
- data/app/serializers/spree/events/price_serializer.rb +0 -22
- data/app/serializers/spree/events/product_serializer.rb +0 -24
- data/app/serializers/spree/events/promotion_serializer.rb +0 -32
- data/app/serializers/spree/events/refund_serializer.rb +0 -23
- data/app/serializers/spree/events/reimbursement_serializer.rb +0 -22
- data/app/serializers/spree/events/report_serializer.rb +0 -23
- data/app/serializers/spree/events/return_authorization_serializer.rb +0 -22
- data/app/serializers/spree/events/return_item_serializer.rb +0 -27
- data/app/serializers/spree/events/shipment_serializer.rb +0 -24
- data/app/serializers/spree/events/stock_item_serializer.rb +0 -22
- data/app/serializers/spree/events/stock_movement_serializer.rb +0 -22
- data/app/serializers/spree/events/stock_transfer_serializer.rb +0 -22
- data/app/serializers/spree/events/store_credit_serializer.rb +0 -30
- data/app/serializers/spree/events/user_serializer.rb +0 -18
- data/app/serializers/spree/events/variant_serializer.rb +0 -34
- data/app/serializers/spree/events/wished_item_serializer.rb +0 -20
- data/app/serializers/spree/events/wishlist_serializer.rb +0 -22
|
@@ -85,8 +85,14 @@ module Spree
|
|
|
85
85
|
|
|
86
86
|
def reduce_units_quantities(reduced_quantity, units)
|
|
87
87
|
units.each do |unit|
|
|
88
|
-
unit.quantity > reduced_quantity
|
|
89
|
-
|
|
88
|
+
if unit.quantity > reduced_quantity
|
|
89
|
+
unit.update(quantity: unit.quantity - reduced_quantity)
|
|
90
|
+
reduced_quantity = 0
|
|
91
|
+
else
|
|
92
|
+
reduced_quantity -= unit.quantity
|
|
93
|
+
unit.destroy!
|
|
94
|
+
end
|
|
95
|
+
break if reduced_quantity.zero?
|
|
90
96
|
end
|
|
91
97
|
reduced_quantity
|
|
92
98
|
end
|
|
@@ -84,6 +84,66 @@ module Spree
|
|
|
84
84
|
CreditCard
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
+
def session_required?
|
|
88
|
+
true
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def payment_session_class
|
|
92
|
+
PaymentSessions::Bogus
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def create_payment_session(order:, amount: nil, external_data: {})
|
|
96
|
+
payment_session_class.create(
|
|
97
|
+
order: order,
|
|
98
|
+
payment_method: self,
|
|
99
|
+
amount: amount.presence || order.total_minus_store_credits,
|
|
100
|
+
currency: order.currency,
|
|
101
|
+
status: 'pending',
|
|
102
|
+
external_id: "bogus_#{SecureRandom.hex(12)}",
|
|
103
|
+
external_data: external_data.merge('client_secret' => "bogus_secret_#{SecureRandom.hex(8)}"),
|
|
104
|
+
customer: order.user
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def update_payment_session(payment_session:, amount: nil, external_data: {})
|
|
109
|
+
attrs = {}
|
|
110
|
+
attrs[:amount] = amount if amount.present?
|
|
111
|
+
attrs[:external_data] = payment_session.external_data.merge(external_data) if external_data.present?
|
|
112
|
+
payment_session.update(attrs)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def complete_payment_session(payment_session:, params: {})
|
|
116
|
+
payment_session.complete
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def setup_session_supported?
|
|
120
|
+
true
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def create_payment_setup_session(customer:, external_data: {})
|
|
124
|
+
payment_setup_sessions.create(
|
|
125
|
+
customer: customer,
|
|
126
|
+
status: 'pending',
|
|
127
|
+
external_id: "bogus_seti_#{SecureRandom.hex(12)}",
|
|
128
|
+
external_client_secret: "bogus_seti_secret_#{SecureRandom.hex(8)}",
|
|
129
|
+
external_data: external_data
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def complete_payment_setup_session(setup_session:, params: {})
|
|
134
|
+
credit_card = CreditCard.create!(
|
|
135
|
+
user: setup_session.customer,
|
|
136
|
+
payment_method: self,
|
|
137
|
+
name: 'Bogus Card',
|
|
138
|
+
last_digits: '4242',
|
|
139
|
+
month: '12',
|
|
140
|
+
year: 1.year.from_now.year.to_s,
|
|
141
|
+
gateway_customer_profile_id: "BGS-#{Array.new(6) { rand(6) }.join}"
|
|
142
|
+
)
|
|
143
|
+
setup_session.update!(payment_source: credit_card)
|
|
144
|
+
setup_session.complete
|
|
145
|
+
end
|
|
146
|
+
|
|
87
147
|
def actions
|
|
88
148
|
%w(capture void credit)
|
|
89
149
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class GiftCardBatch < Spree.base_class
|
|
3
|
+
has_prefix_id :gcb
|
|
4
|
+
|
|
3
5
|
extend DisplayMoney
|
|
4
6
|
include Spree::SingleStoreResource
|
|
5
7
|
|
|
@@ -30,10 +32,6 @@ module Spree
|
|
|
30
32
|
|
|
31
33
|
money_methods :amount
|
|
32
34
|
|
|
33
|
-
def amount=(amount)
|
|
34
|
-
self[:amount] = Spree::LocalizedNumber.parse(amount)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
35
|
self.whitelisted_ransackable_attributes = %w[prefix]
|
|
38
36
|
|
|
39
37
|
def generate_gift_cards
|
data/app/models/spree/image.rb
CHANGED
|
@@ -5,6 +5,9 @@ module Spree
|
|
|
5
5
|
include Spree::ImageMethods
|
|
6
6
|
|
|
7
7
|
after_commit :touch_product_variants, if: :should_touch_product_variants?, on: :update
|
|
8
|
+
after_commit :update_variant_thumbnail, on: [:create, :destroy]
|
|
9
|
+
after_commit :update_variant_thumbnail_on_reorder, on: :update, if: :saved_change_to_position?
|
|
10
|
+
after_commit :update_variant_thumbnail_on_viewable_change, on: :update, if: :saved_change_to_viewable_id?
|
|
8
11
|
|
|
9
12
|
after_create :increment_viewable_image_count
|
|
10
13
|
after_destroy :decrement_viewable_image_count
|
|
@@ -84,5 +87,20 @@ module Spree
|
|
|
84
87
|
Spree::Variant.decrement_counter(:image_count, viewable_id)
|
|
85
88
|
Spree::Product.decrement_counter(:total_image_count, viewable.product_id)
|
|
86
89
|
end
|
|
90
|
+
|
|
91
|
+
def update_variant_thumbnail
|
|
92
|
+
return unless viewable.is_a?(Spree::Variant)
|
|
93
|
+
|
|
94
|
+
viewable.update_thumbnail!
|
|
95
|
+
viewable.product.update_thumbnail!
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def update_variant_thumbnail_on_reorder
|
|
99
|
+
update_variant_thumbnail
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def update_variant_thumbnail_on_viewable_change
|
|
103
|
+
update_variant_thumbnail
|
|
104
|
+
end
|
|
87
105
|
end
|
|
88
106
|
end
|
data/app/models/spree/import.rb
CHANGED
|
@@ -2,9 +2,9 @@ require 'csv'
|
|
|
2
2
|
|
|
3
3
|
module Spree
|
|
4
4
|
class Import < Spree.base_class
|
|
5
|
-
|
|
6
|
-
include Spree::NumberAsParam
|
|
5
|
+
has_prefix_id :imp
|
|
7
6
|
|
|
7
|
+
include Spree::NumberIdentifier
|
|
8
8
|
include Spree::Core::NumberGenerator.new(prefix: 'IM')
|
|
9
9
|
|
|
10
10
|
publishes_lifecycle_events
|
|
@@ -226,7 +226,7 @@ module Spree
|
|
|
226
226
|
end
|
|
227
227
|
|
|
228
228
|
def event_serializer_class
|
|
229
|
-
Spree::
|
|
229
|
+
'Spree::Api::V3::ImportSerializer'.safe_constantize
|
|
230
230
|
end
|
|
231
231
|
|
|
232
232
|
class << self
|
|
@@ -250,6 +250,8 @@ module Spree
|
|
|
250
250
|
|
|
251
251
|
# eg. Spree::Imports::Orders => Spree::Order
|
|
252
252
|
def model_class
|
|
253
|
+
return Spree.user_class if to_s == 'Spree::Imports::Customers'
|
|
254
|
+
|
|
253
255
|
klass = "Spree::#{to_s.demodulize.singularize}".safe_constantize
|
|
254
256
|
|
|
255
257
|
raise NameError, "Missing model class for #{self}" unless klass
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module ImportSchemas
|
|
3
|
+
class Customers < Spree::ImportSchema
|
|
4
|
+
FIELDS = [
|
|
5
|
+
{ name: 'email', label: 'Email', required: true },
|
|
6
|
+
{ name: 'first_name', label: 'First Name' },
|
|
7
|
+
{ name: 'last_name', label: 'Last Name' },
|
|
8
|
+
{ name: 'phone', label: 'Phone' },
|
|
9
|
+
{ name: 'accepts_email_marketing', label: 'Accepts Email Marketing' },
|
|
10
|
+
{ name: 'tags', label: 'Tags' },
|
|
11
|
+
{ name: 'company', label: 'Company' },
|
|
12
|
+
{ name: 'address1', label: 'Address 1' },
|
|
13
|
+
{ name: 'address2', label: 'Address 2' },
|
|
14
|
+
{ name: 'city', label: 'City' },
|
|
15
|
+
{ name: 'province_code', label: 'Province Code' },
|
|
16
|
+
{ name: 'country_code', label: 'Country Code' },
|
|
17
|
+
{ name: 'zip', label: 'Zip' }
|
|
18
|
+
].freeze
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class Invitation < Spree.base_class
|
|
3
|
+
has_prefix_id :inv
|
|
4
|
+
|
|
3
5
|
has_secure_token
|
|
4
6
|
acts_as_paranoid
|
|
5
7
|
|
|
@@ -121,13 +123,11 @@ module Spree
|
|
|
121
123
|
def invitee_already_exists
|
|
122
124
|
return if resource.blank?
|
|
123
125
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
resource.users.exists?(email: email)
|
|
128
|
-
end
|
|
126
|
+
# Check role_users directly for better performance and to avoid potential source_type issues
|
|
127
|
+
user_to_check = invitee || Spree.admin_user_class.find_by(email: email)
|
|
128
|
+
return if user_to_check.blank?
|
|
129
129
|
|
|
130
|
-
if exists
|
|
130
|
+
if resource.role_users.exists?(user: user_to_check)
|
|
131
131
|
errors.add(:email, 'already exists')
|
|
132
132
|
end
|
|
133
133
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Default implementation of AdminUser for staff/admin accounts.
|
|
2
|
+
# This class is separate from LegacyUser to support distinct customer vs staff models.
|
|
3
|
+
require 'bcrypt'
|
|
4
|
+
|
|
5
|
+
module Spree
|
|
6
|
+
class LegacyAdminUser < Spree.base_class
|
|
7
|
+
include Spree::AdminUserMethods
|
|
8
|
+
|
|
9
|
+
self.table_name = 'spree_admin_users'
|
|
10
|
+
|
|
11
|
+
attr_accessor :password, :password_confirmation
|
|
12
|
+
|
|
13
|
+
validates :email, presence: true, uniqueness: { case_sensitive: false }
|
|
14
|
+
|
|
15
|
+
before_save :encrypt_password, if: :password
|
|
16
|
+
|
|
17
|
+
# Simple password validation for testing purposes
|
|
18
|
+
# In production, Spree.admin_user_class should be overridden with a proper auth solution (e.g., Devise)
|
|
19
|
+
def valid_password?(check_password)
|
|
20
|
+
return false if encrypted_password.blank?
|
|
21
|
+
|
|
22
|
+
BCrypt::Password.new(encrypted_password) == check_password
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def encrypt_password
|
|
28
|
+
self.encrypted_password = BCrypt::Password.create(password)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
# Default implementation of User.
|
|
1
|
+
# Default implementation of User.
|
|
2
|
+
require 'bcrypt'
|
|
3
|
+
|
|
2
4
|
module Spree
|
|
3
5
|
class LegacyUser < Spree.base_class
|
|
4
6
|
include Spree::UserAddress
|
|
@@ -10,5 +12,21 @@ module Spree
|
|
|
10
12
|
attr_accessor :password, :password_confirmation
|
|
11
13
|
|
|
12
14
|
validates :email, presence: true, uniqueness: { case_sensitive: false }
|
|
15
|
+
|
|
16
|
+
before_save :encrypt_password, if: :password
|
|
17
|
+
|
|
18
|
+
# Simple password validation for testing purposes
|
|
19
|
+
# In production, Spree.user_class should be overridden with a proper auth solution (e.g., Devise)
|
|
20
|
+
def valid_password?(check_password)
|
|
21
|
+
return false if encrypted_password.blank?
|
|
22
|
+
|
|
23
|
+
BCrypt::Password.new(encrypted_password) == check_password
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def encrypt_password
|
|
29
|
+
self.encrypted_password = BCrypt::Password.create(password)
|
|
30
|
+
end
|
|
13
31
|
end
|
|
14
32
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class LineItem < Spree.base_class
|
|
3
|
+
has_prefix_id :li # Spree-specific: line item
|
|
4
|
+
|
|
3
5
|
include Spree::Metafields
|
|
4
6
|
include Spree::Metadata
|
|
5
7
|
|
|
@@ -28,10 +30,12 @@ module Spree
|
|
|
28
30
|
|
|
29
31
|
validates :variant, :order, presence: true
|
|
30
32
|
|
|
33
|
+
DB_INTEGER_MAX = (2**31) - 1
|
|
34
|
+
|
|
31
35
|
# numericality: :less_than_or_equal_to validation is due to the restriction at the database level
|
|
32
36
|
# https://github.com/spree/spree/issues/2695#issuecomment-143314161
|
|
33
37
|
validates :quantity, numericality: {
|
|
34
|
-
in: 0..
|
|
38
|
+
in: 0..DB_INTEGER_MAX,
|
|
35
39
|
only_integer: true, message: Spree.t('validation.must_be_int')
|
|
36
40
|
}
|
|
37
41
|
|
|
@@ -47,8 +51,15 @@ module Spree
|
|
|
47
51
|
|
|
48
52
|
after_create :update_tax_charge
|
|
49
53
|
|
|
50
|
-
delegate :
|
|
51
|
-
delegate :brand, :category, to: :product
|
|
54
|
+
delegate :sku, :should_track_inventory?, :product, :options_text, :slug, :product_id, :dimensions_unit, :weight_unit, :option_values, to: :variant
|
|
55
|
+
delegate :name, :description, :brand, :category, to: :product
|
|
56
|
+
|
|
57
|
+
# Returns the thumbnail image for this line item
|
|
58
|
+
# Prefers variant thumbnail, falls back to product thumbnail
|
|
59
|
+
# @return [Spree::Image, nil]
|
|
60
|
+
def thumbnail
|
|
61
|
+
variant.thumbnail || product.thumbnail
|
|
62
|
+
end
|
|
52
63
|
delegate :tax_zone, to: :order
|
|
53
64
|
delegate :digital?, :can_supply?, to: :variant
|
|
54
65
|
|
|
@@ -217,7 +228,7 @@ module Spree
|
|
|
217
228
|
#
|
|
218
229
|
# @return [Integer]
|
|
219
230
|
def maximum_quantity
|
|
220
|
-
@maximum_quantity ||= variant.backorderable? ?
|
|
231
|
+
@maximum_quantity ||= variant.backorderable? ? DB_INTEGER_MAX : variant.total_on_hand
|
|
221
232
|
end
|
|
222
233
|
|
|
223
234
|
# Returns true if the line item variant has digital assets
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class Market < Spree.base_class
|
|
3
|
+
has_prefix_id :mkt
|
|
4
|
+
|
|
5
|
+
include Spree::SingleStoreResource
|
|
6
|
+
|
|
7
|
+
acts_as_paranoid
|
|
8
|
+
acts_as_list scope: :store_id
|
|
9
|
+
|
|
10
|
+
#
|
|
11
|
+
# Associations
|
|
12
|
+
#
|
|
13
|
+
belongs_to :store, class_name: 'Spree::Store', touch: true
|
|
14
|
+
has_many :market_countries, class_name: 'Spree::MarketCountry', dependent: :destroy
|
|
15
|
+
has_many :countries, through: :market_countries, class_name: 'Spree::Country'
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
# Validations
|
|
19
|
+
#
|
|
20
|
+
validates :store, presence: true
|
|
21
|
+
validates :name, presence: true, uniqueness: { scope: spree_base_uniqueness_scope + [:store_id] }
|
|
22
|
+
validates :currency, presence: true
|
|
23
|
+
validates :default_locale, presence: true
|
|
24
|
+
validates :countries, presence: true
|
|
25
|
+
|
|
26
|
+
#
|
|
27
|
+
# Callbacks
|
|
28
|
+
#
|
|
29
|
+
before_save :ensure_single_default
|
|
30
|
+
|
|
31
|
+
#
|
|
32
|
+
# Scopes
|
|
33
|
+
#
|
|
34
|
+
scope :default, -> { where(default: true) }
|
|
35
|
+
|
|
36
|
+
# Find the market that contains the given country for a store
|
|
37
|
+
#
|
|
38
|
+
# @param country [Spree::Country] the country to look up
|
|
39
|
+
# @param store [Spree::Store] the store to scope to
|
|
40
|
+
# @return [Spree::Market, nil]
|
|
41
|
+
def self.for_country(country, store:)
|
|
42
|
+
return nil unless country && store
|
|
43
|
+
|
|
44
|
+
joins(:market_countries)
|
|
45
|
+
.where(store_id: store.id)
|
|
46
|
+
.where(spree_market_countries: { country_id: country.id })
|
|
47
|
+
.order(:position)
|
|
48
|
+
.first
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns the default market for a store, or falls back to the first by position
|
|
52
|
+
#
|
|
53
|
+
# @param store [Spree::Store]
|
|
54
|
+
# @return [Spree::Market, nil]
|
|
55
|
+
def self.default_for_store(store)
|
|
56
|
+
return nil unless store
|
|
57
|
+
|
|
58
|
+
store.markets.default.first || store.markets.order(:position).first
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Returns the first country by name from this market's countries
|
|
62
|
+
#
|
|
63
|
+
# @return [Spree::Country, nil]
|
|
64
|
+
def default_country
|
|
65
|
+
countries.order(:name).first
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Returns supported locales as an array, always including default_locale
|
|
69
|
+
#
|
|
70
|
+
# @return [Array<String>]
|
|
71
|
+
def supported_locales_list
|
|
72
|
+
@supported_locales_list ||= (supported_locales.to_s.split(',').map(&:strip) << default_locale).compact.uniq.sort
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def ensure_single_default
|
|
78
|
+
return unless default? && default_changed?
|
|
79
|
+
|
|
80
|
+
self.class.where(store_id: store_id, default: true).where.not(id: id).update_all(default: false)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class MarketCountry < Spree.base_class
|
|
3
|
+
self.table_name = 'spree_market_countries'
|
|
4
|
+
|
|
5
|
+
belongs_to :market, class_name: 'Spree::Market'
|
|
6
|
+
belongs_to :country, class_name: 'Spree::Country'
|
|
7
|
+
|
|
8
|
+
validates :market, :country, presence: true
|
|
9
|
+
validates :country_id, uniqueness: { scope: :market_id }
|
|
10
|
+
validate :country_covered_by_shipping_zone
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def country_covered_by_shipping_zone
|
|
15
|
+
return if market.blank? || country.blank?
|
|
16
|
+
|
|
17
|
+
store = market.store
|
|
18
|
+
return if store.blank?
|
|
19
|
+
|
|
20
|
+
unless store.countries_with_shipping_coverage.exists?(id: country.id)
|
|
21
|
+
errors.add(:country, :not_in_shipping_zone)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -79,7 +79,8 @@ module Spree
|
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def find_existing_address(attributes)
|
|
82
|
-
|
|
82
|
+
# Exclude virtual attributes that are handled by Address model callbacks
|
|
83
|
+
address_attributes = attributes.except(:state_name, :country_iso, :state_abbr)
|
|
83
84
|
state_name = attributes[:state_name]
|
|
84
85
|
|
|
85
86
|
scope = Spree::Address.not_deleted.where(address_attributes)
|