spree_core 6.0.0.rc1 → 6.0.0.rc2
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/README.md +3 -3
- data/app/jobs/spree/search_provider/index_job.rb +4 -0
- data/app/jobs/spree/uploads/purge_unattached_job.rb +49 -0
- data/app/mailers/spree/base_mailer.rb +4 -3
- data/app/models/concerns/spree/admin_user_methods.rb +2 -0
- data/app/models/concerns/spree/calculated_adjustments.rb +41 -13
- data/app/models/concerns/spree/calculator/currency_amounts.rb +1 -1
- data/app/models/concerns/spree/calculator/tiers.rb +1 -14
- data/app/models/concerns/spree/canonical_decimals.rb +61 -0
- data/app/models/concerns/spree/customer_methods.rb +8 -4
- data/app/models/concerns/spree/derived_number.rb +11 -13
- data/app/models/concerns/spree/preference_schema/json_schema.rb +139 -0
- data/app/models/concerns/spree/preference_schema.rb +59 -21
- data/app/models/concerns/spree/product_scopes.rb +5 -1
- data/app/models/concerns/spree/purchase/payment_processing.rb +1 -1
- data/app/models/concerns/spree/purchase/purchase_order.rb +7 -2
- data/app/models/concerns/spree/purchase/store_credits.rb +36 -1
- data/app/models/concerns/spree/purchase/totals.rb +1 -8
- data/app/models/concerns/spree/ransackable_attributes.rb +46 -0
- data/app/models/concerns/spree/signed_id_attachments.rb +54 -0
- data/app/models/concerns/spree/stores/email_branding.rb +2 -1
- data/app/models/concerns/spree/stores/markets.rb +5 -2
- data/app/models/concerns/spree/typed_adjustment_line.rb +4 -5
- data/app/models/concerns/spree/typed_associations.rb +6 -18
- data/app/models/concerns/spree/vat_price_calculation.rb +5 -6
- data/app/models/spree/address.rb +1 -1
- data/app/models/spree/adjusters/largest_remainder.rb +14 -1
- data/app/models/spree/adjusters/promotion.rb +3 -4
- data/app/models/spree/allowed_origin.rb +3 -0
- data/app/models/spree/base.rb +29 -0
- data/app/models/spree/calculator/flat_percent_item_total.rb +1 -1
- data/app/models/spree/calculator/flat_rate.rb +2 -2
- data/app/models/spree/calculator/flexi_rate.rb +3 -3
- data/app/models/spree/calculator/percent_on_line_item.rb +1 -1
- data/app/models/spree/calculator/price_sack.rb +4 -4
- data/app/models/spree/calculator/shipping/digital_delivery.rb +2 -2
- data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +6 -4
- data/app/models/spree/calculator/shipping/flat_rate.rb +4 -4
- data/app/models/spree/calculator/shipping/flexi_rate.rb +3 -3
- data/app/models/spree/calculator/shipping/per_item.rb +2 -2
- data/app/models/spree/calculator/shipping/price_sack.rb +4 -4
- data/app/models/spree/calculator/tiered_flat_rate.rb +2 -2
- data/app/models/spree/calculator/tiered_percent.rb +1 -1
- data/app/models/spree/catalog.rb +1 -0
- data/app/models/spree/catalog_price.rb +0 -7
- data/app/models/spree/catalog_price_tier.rb +0 -8
- data/app/models/spree/category.rb +3 -1
- data/app/models/spree/channel/gating.rb +2 -1
- data/app/models/spree/channel.rb +2 -0
- data/app/models/spree/claim.rb +1 -0
- data/app/models/spree/collection.rb +2 -0
- data/app/models/spree/commission_rate.rb +5 -1
- data/app/models/spree/commission_rules/category_rule.rb +2 -5
- data/app/models/spree/commission_rules/item_total_rule.rb +2 -2
- data/app/models/spree/commission_rules/seller_rule.rb +2 -5
- data/app/models/spree/company.rb +1 -0
- data/app/models/spree/custom_field_definition.rb +11 -12
- data/app/models/spree/customer_group.rb +1 -0
- data/app/models/spree/data_request.rb +1 -0
- data/app/models/spree/delivery_method.rb +6 -1
- data/app/models/spree/delivery_method_rules/channel_rule.rb +2 -5
- data/app/models/spree/delivery_method_rules/item_total_rule.rb +2 -2
- data/app/models/spree/delivery_profile.rb +1 -0
- data/app/models/spree/delivery_rate.rb +3 -3
- data/app/models/spree/delivery_zone.rb +1 -1
- data/app/models/spree/exchange.rb +1 -0
- data/app/models/spree/exports/report.rb +2 -2
- data/app/models/spree/file_upload.rb +155 -0
- data/app/models/spree/fulfillment.rb +13 -3
- data/app/models/spree/fulfillment_group.rb +6 -1
- data/app/models/spree/gateway/bogus.rb +2 -0
- data/app/models/spree/gateway.rb +1 -1
- data/app/models/spree/gift_card.rb +6 -7
- data/app/models/spree/gift_card_batch.rb +3 -4
- data/app/models/spree/import.rb +3 -0
- data/app/models/spree/integration.rb +1 -1
- data/app/models/spree/line_item.rb +2 -2
- data/app/models/spree/market.rb +7 -2
- data/app/models/spree/media.rb +1 -9
- data/app/models/spree/newsletter_subscriber.rb +1 -0
- data/app/models/spree/option_type.rb +2 -1
- data/app/models/spree/option_value.rb +2 -1
- data/app/models/spree/order.rb +5 -5
- data/app/models/spree/order_group.rb +7 -2
- data/app/models/spree/package_type.rb +1 -0
- data/app/models/spree/payment/gateway_options.rb +1 -1
- data/app/models/spree/payment.rb +3 -10
- data/app/models/spree/payment_method/check.rb +2 -0
- data/app/models/spree/payment_method/store_credit.rb +11 -9
- data/app/models/spree/payment_method.rb +42 -2
- data/app/models/spree/policy.rb +1 -0
- data/app/models/spree/price.rb +17 -14
- data/app/models/spree/price_adjustment_tier.rb +4 -0
- data/app/models/spree/price_list.rb +13 -7
- data/app/models/spree/price_rules/channel_rule.rb +2 -5
- data/app/models/spree/price_rules/customer_group_rule.rb +2 -5
- data/app/models/spree/price_rules/market_rule.rb +2 -5
- data/app/models/spree/price_rules/user_rule.rb +1 -3
- data/app/models/spree/product/channels.rb +9 -2
- data/app/models/spree/product.rb +23 -23
- data/app/models/spree/product_type.rb +1 -0
- data/app/models/spree/promotion/actions/create_line_items.rb +1 -1
- data/app/models/spree/promotion/rules/category.rb +1 -1
- data/app/models/spree/promotion/rules/channel.rb +2 -5
- data/app/models/spree/promotion/rules/country.rb +3 -4
- data/app/models/spree/promotion/rules/currency.rb +1 -1
- data/app/models/spree/promotion/rules/customer_group.rb +2 -5
- data/app/models/spree/promotion/rules/item_total.rb +2 -2
- data/app/models/spree/promotion/rules/market.rb +2 -5
- data/app/models/spree/promotion/rules/option_value.rb +2 -3
- data/app/models/spree/promotion/rules/product.rb +1 -1
- data/app/models/spree/promotion.rb +1 -0
- data/app/models/spree/purchase_order.rb +2 -0
- data/app/models/spree/refund.rb +13 -15
- data/app/models/spree/return.rb +1 -0
- data/app/models/spree/saved_report.rb +1 -0
- data/app/models/spree/search_provider/base.rb +4 -0
- data/app/models/spree/seller.rb +8 -1
- data/app/models/spree/seller_payout.rb +1 -0
- data/app/models/spree/seller_requirement.rb +1 -0
- data/app/models/spree/seller_requirement_submission.rb +1 -0
- data/app/models/spree/seller_transfer.rb +1 -0
- data/app/models/spree/shipping_label.rb +1 -0
- data/app/models/spree/stock/estimator.rb +1 -1
- data/app/models/spree/stock_level.rb +2 -0
- data/app/models/spree/stock_location.rb +1 -0
- data/app/models/spree/stock_transfer.rb +2 -0
- data/app/models/spree/store.rb +37 -12
- data/app/models/spree/store_credit.rb +5 -4
- data/app/models/spree/supplier.rb +1 -0
- data/app/models/spree/tax_category.rb +1 -0
- data/app/models/spree/tax_exemption_certificate.rb +1 -0
- data/app/models/spree/tax_provider/internal.rb +12 -9
- data/app/models/spree/tax_rate.rb +29 -6
- data/app/models/spree/taxon.rb +1 -1
- data/app/models/spree/update_status.rb +25 -0
- data/app/models/spree/variant.rb +6 -9
- data/app/models/spree/webhook_delivery.rb +1 -0
- data/app/models/spree/webhook_endpoint.rb +36 -0
- data/app/presenters/spree/csv/order_line_item_presenter.rb +10 -6
- data/app/presenters/spree/csv/price_list_price_presenter.rb +3 -8
- data/app/presenters/spree/csv/product_variant_presenter.rb +17 -8
- data/app/presenters/spree/csv/purchase_order_item_presenter.rb +1 -3
- data/app/services/spree/carts/update.rb +3 -3
- data/app/services/spree/imports/row_processors/product_variant.rb +12 -0
- data/app/services/spree/locales.rb +39 -5
- data/app/services/spree/orders/create.rb +3 -3
- data/app/services/spree/orders/discounts/create.rb +8 -8
- data/app/services/spree/prices/bulk_upsert.rb +32 -25
- data/app/services/spree/seeds/stores.rb +1 -1
- data/app/services/spree/stores/provision_defaults.rb +13 -16
- data/app/services/spree/update_check.rb +160 -0
- data/app/services/spree/uploads/assign_stores.rb +92 -0
- data/app/validators/spree/attachment_content_type_validator.rb +19 -2
- data/app/validators/spree/locale_codes_validator.rb +22 -0
- data/app/workflows/spree/fulfillments/create.rb +2 -2
- data/app/workflows/spree/payments/capture.rb +20 -5
- data/app/workflows/spree/payments/process.rb +3 -1
- data/app/workflows/spree/price_lists/update.rb +4 -4
- data/app/workflows/spree/purchase_orders/create.rb +3 -1
- data/app/workflows/spree/shipping_labels/record.rb +2 -2
- data/app/workflows/spree/stock_transfers/create.rb +3 -1
- data/config/currency_exponents.json +180 -0
- data/config/locales/content_locales.rb +9 -0
- data/config/locales/en.yml +10 -2
- data/db/migrate/20261009000001_change_money_columns_to_decimal_19_4.rb +116 -0
- data/db/migrate/20261010000001_add_store_id_to_active_storage_blobs.rb +12 -0
- data/lib/generators/spree/dummy/templates/rails/test.rb +21 -2
- data/lib/spree/core/configuration.rb +18 -0
- data/lib/spree/core/engine.rb +26 -0
- data/lib/spree/core/preferences/invalid_preferences.rb +18 -0
- data/lib/spree/core/preferences/json_conversion.rb +5 -1
- data/lib/spree/core/preferences/masking.rb +51 -1
- data/lib/spree/core/preferences/preferable.rb +212 -26
- data/lib/spree/core/preferences/preferable_class_methods.rb +121 -4
- data/lib/spree/core/ransack_configuration.rb +13 -2
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +6 -3
- data/lib/spree/iso_data.rb +7 -0
- data/lib/spree/localized_number.rb +6 -0
- data/lib/spree/migrations.rb +4 -1
- data/lib/spree/money.rb +196 -15
- data/lib/spree/reporting/adapters/base.rb +7 -6
- data/lib/spree/reporting/registry.rb +13 -0
- data/lib/spree/testing_support/factories/product_factory.rb +9 -0
- data/lib/spree/upgrades/5_6_to_6_0/manifest.yml +22 -1
- data/lib/spree/uploads.rb +101 -0
- data/lib/tasks/markets.rake +2 -1
- data/lib/tasks/migrate_users_to_customers.rake +23 -18
- data/lib/tasks/money.rake +46 -0
- data/lib/tasks/seller_sample_data.rake +2 -2
- data/lib/tasks/store_settings.rake +13 -6
- data/lib/tasks/typed_adjustments_migration.rake +1 -1
- data/lib/tasks/uploads.rake +19 -0
- metadata +35 -6
- data/app/jobs/spree/products/touch_categories_job.rb +0 -15
- data/app/jobs/spree/products/touch_taxons_job.rb +0 -13
|
@@ -72,12 +72,17 @@ module Spree
|
|
|
72
72
|
fulfillments.sum { |fulfillment| fulfillment.discounted_cost.to_d }
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
# @return [String] the currency the parcel was quoted in
|
|
76
|
+
def currency
|
|
77
|
+
primary.currency
|
|
78
|
+
end
|
|
79
|
+
|
|
75
80
|
# @return [String, Spree::Money] the forwarder's placeholder while a
|
|
76
81
|
# freight rate is unpriced, money otherwise
|
|
77
82
|
def display_cost(**options)
|
|
78
83
|
return primary.display_cost(**options) if unpriced?
|
|
79
84
|
|
|
80
|
-
Spree::Money.new(cost, { currency:
|
|
85
|
+
Spree::Money.new(cost, { currency: currency }.merge(options))
|
|
81
86
|
end
|
|
82
87
|
|
|
83
88
|
# @return [Boolean]
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class Gateway::Bogus < Gateway
|
|
3
|
+
self.accepts_money_amounts = true
|
|
4
|
+
|
|
3
5
|
TEST_VISA = ['4111111111111111', '4012888888881881', '4222222222222']
|
|
4
6
|
TEST_MC = ['5500000000000004', '5555555555554444', '5105105105105100', '2223000010309703']
|
|
5
7
|
TEST_AMEX = ['378282246310005', '371449635398431', '378734493671000', '340000000000009']
|
data/app/models/spree/gateway.rb
CHANGED
|
@@ -5,6 +5,9 @@ module Spree
|
|
|
5
5
|
extend DisplayMoney
|
|
6
6
|
include Spree::SingleStoreResource
|
|
7
7
|
include Spree::HasCustomFields
|
|
8
|
+
include Spree::CanonicalDecimals
|
|
9
|
+
|
|
10
|
+
canonical_decimals :amount
|
|
8
11
|
include Spree::Security::GiftCards if defined?(Spree::Security::GiftCards)
|
|
9
12
|
|
|
10
13
|
publishes_lifecycle_events
|
|
@@ -62,10 +65,12 @@ module Spree
|
|
|
62
65
|
# Ransack
|
|
63
66
|
#
|
|
64
67
|
ransack_alias :state, :status # @deprecated filter alias — removed in 6.1
|
|
65
|
-
self.whitelisted_ransackable_attributes = %w[code customer_id status state gift_card_batch_id created_by_id]
|
|
68
|
+
self.whitelisted_ransackable_attributes = %w[code customer_id status state gift_card_batch_id created_by_id currency expires_at]
|
|
69
|
+
search_by :code
|
|
66
70
|
# `users` is the pre-6.0 name for `customers` — removed in 6.1.
|
|
67
71
|
self.whitelisted_ransackable_associations = %w[customers users orders batch]
|
|
68
72
|
self.whitelisted_ransackable_scopes = %w[active expired redeemed partially_redeemed]
|
|
73
|
+
self.ransackable_scope_types = %w[active expired redeemed partially_redeemed].index_with('boolean')
|
|
69
74
|
|
|
70
75
|
normalizes :code, with: ->(value) { value&.to_s&.squish&.presence }
|
|
71
76
|
|
|
@@ -82,12 +87,6 @@ module Spree
|
|
|
82
87
|
#
|
|
83
88
|
money_methods :amount, :amount_used, :amount_authorized, :amount_remaining
|
|
84
89
|
|
|
85
|
-
# Sets the amount
|
|
86
|
-
# @param amount [String]
|
|
87
|
-
def amount=(amount)
|
|
88
|
-
self[:amount] = Spree::LocalizedNumber.parse(amount)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
90
|
# Calculates the remaining amount
|
|
92
91
|
# @return [Decimal]
|
|
93
92
|
def amount_remaining
|
|
@@ -4,6 +4,9 @@ module Spree
|
|
|
4
4
|
|
|
5
5
|
extend DisplayMoney
|
|
6
6
|
include Spree::SingleStoreResource
|
|
7
|
+
include Spree::CanonicalDecimals
|
|
8
|
+
|
|
9
|
+
canonical_decimals :amount
|
|
7
10
|
|
|
8
11
|
publishes_lifecycle_events
|
|
9
12
|
|
|
@@ -34,10 +37,6 @@ module Spree
|
|
|
34
37
|
|
|
35
38
|
money_methods :amount
|
|
36
39
|
|
|
37
|
-
def amount=(amount)
|
|
38
|
-
self[:amount] = Spree::LocalizedNumber.parse(amount)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
40
|
self.whitelisted_ransackable_attributes = %w[prefix]
|
|
42
41
|
|
|
43
42
|
def generate_gift_cards
|
data/app/models/spree/import.rb
CHANGED
|
@@ -55,6 +55,7 @@ module Spree
|
|
|
55
55
|
# Ransack configuration
|
|
56
56
|
#
|
|
57
57
|
self.whitelisted_ransackable_attributes = %w[number type status seller_id]
|
|
58
|
+
search_by :number
|
|
58
59
|
# Lets an operator filter their list by who ran the job — the seller's
|
|
59
60
|
# name, not an id they would have to look up first.
|
|
60
61
|
self.whitelisted_ransackable_associations = %w[seller]
|
|
@@ -72,6 +73,7 @@ module Spree
|
|
|
72
73
|
# Attachments
|
|
73
74
|
#
|
|
74
75
|
has_one_attached :attachment, service: Spree.private_storage_service_name
|
|
76
|
+
signed_id_attachments :attachment
|
|
75
77
|
|
|
76
78
|
#
|
|
77
79
|
# Status
|
|
@@ -87,6 +89,7 @@ module Spree
|
|
|
87
89
|
# Preferences
|
|
88
90
|
#
|
|
89
91
|
preference :delimiter, :string, default: ','
|
|
92
|
+
exposes_preferences :delimiter
|
|
90
93
|
# Run the pipeline in-process instead of enqueuing it. For rake tasks, seeds
|
|
91
94
|
# and the console, where the caller needs the data to exist when the call
|
|
92
95
|
# returns and there may be no worker attached. Never set it from a request:
|
|
@@ -98,7 +98,7 @@ module Spree
|
|
|
98
98
|
group: klass.integration_group,
|
|
99
99
|
description: klass.human_description,
|
|
100
100
|
**klass.provider_listing,
|
|
101
|
-
|
|
101
|
+
schema: klass.preference_json_schema
|
|
102
102
|
}
|
|
103
103
|
end.sort_by { |entry| entry[:name] }
|
|
104
104
|
end
|
|
@@ -119,7 +119,7 @@ module Spree
|
|
|
119
119
|
|
|
120
120
|
self.whitelisted_ransackable_associations = %w[variant order tax_category]
|
|
121
121
|
self.whitelisted_ransackable_attributes = %w[variant_id order_id tax_category_id quantity
|
|
122
|
-
price cost_price
|
|
122
|
+
price cost_price adjustment_total
|
|
123
123
|
additional_tax_total discount_total included_tax_total
|
|
124
124
|
pre_tax_amount taxable_adjustment_total
|
|
125
125
|
non_taxable_adjustment_total]
|
|
@@ -231,7 +231,7 @@ module Spree
|
|
|
231
231
|
#
|
|
232
232
|
# @return [BigDecimal]
|
|
233
233
|
def amount
|
|
234
|
-
price * quantity
|
|
234
|
+
Spree::Money::Rounding.to_currency(price * quantity, currency)
|
|
235
235
|
end
|
|
236
236
|
|
|
237
237
|
# Returns the compare at amount (compare at price * quantity) of the line item
|
data/app/models/spree/market.rb
CHANGED
|
@@ -8,6 +8,9 @@ module Spree
|
|
|
8
8
|
acts_as_paranoid
|
|
9
9
|
acts_as_list scope: :store_id
|
|
10
10
|
|
|
11
|
+
self.whitelisted_ransackable_attributes = %w[currency default_locale]
|
|
12
|
+
search_by :name
|
|
13
|
+
|
|
11
14
|
#
|
|
12
15
|
# Associations
|
|
13
16
|
#
|
|
@@ -74,7 +77,9 @@ module Spree
|
|
|
74
77
|
inclusion: { in: ->(_market) { Spree.tax_providers.map(&:to_s) } },
|
|
75
78
|
allow_blank: true
|
|
76
79
|
validates :currency, presence: true
|
|
80
|
+
normalizes :default_locale, with: ->(value) { value.to_s.strip.presence }
|
|
77
81
|
validates :default_locale, presence: true
|
|
82
|
+
validates_with Spree::LocaleCodesValidator, attributes: %i[default_locale supported_locales]
|
|
78
83
|
validates :countries, presence: true
|
|
79
84
|
|
|
80
85
|
#
|
|
@@ -142,7 +147,7 @@ module Spree
|
|
|
142
147
|
#
|
|
143
148
|
# @return [Array<String>]
|
|
144
149
|
def supported_locales_list
|
|
145
|
-
@supported_locales_list ||= (supported_locales.to_s.split(',').map(&:strip) << default_locale).
|
|
150
|
+
@supported_locales_list ||= (supported_locales.to_s.split(',').map(&:strip) << default_locale).compact_blank.uniq.sort
|
|
146
151
|
end
|
|
147
152
|
|
|
148
153
|
# Accepts an Array of locale codes and persists them as a comma-separated
|
|
@@ -153,7 +158,7 @@ module Spree
|
|
|
153
158
|
# @param value [Array<String>, String, nil]
|
|
154
159
|
def supported_locales=(value)
|
|
155
160
|
@supported_locales_list = nil
|
|
156
|
-
normalized = value.is_a?(Array) ? value.
|
|
161
|
+
normalized = value.is_a?(Array) ? value.map { |code| code.to_s.strip }.reject(&:empty?).uniq.join(',') : value
|
|
157
162
|
super(normalized)
|
|
158
163
|
end
|
|
159
164
|
|
data/app/models/spree/media.rb
CHANGED
|
@@ -31,15 +31,6 @@ module Spree
|
|
|
31
31
|
EXTERNAL_URL_CUSTOM_FIELD_KEY = 'external.url'
|
|
32
32
|
MEDIA_TYPES = %w[image video external_video].freeze
|
|
33
33
|
|
|
34
|
-
# Domain attributes a client may write, shared by every write path — the
|
|
35
|
-
# media endpoints and the inline `media:` list on a product. Each path adds
|
|
36
|
-
# its own transport keys (an upload's signed id, a row id) on top. Adding a
|
|
37
|
-
# field here reaches both, so it can't be dropped on the one a client uses.
|
|
38
|
-
WRITABLE_ATTRIBUTES = %i[
|
|
39
|
-
alt position media_type external_video_url poster_signed_id
|
|
40
|
-
focal_point_x focal_point_y
|
|
41
|
-
].freeze
|
|
42
|
-
|
|
43
34
|
after_initialize { self.media_type ||= 'image' }
|
|
44
35
|
|
|
45
36
|
belongs_to :viewable, polymorphic: true, touch: true, optional: true
|
|
@@ -93,6 +84,7 @@ module Spree
|
|
|
93
84
|
|
|
94
85
|
self.whitelisted_ransackable_attributes = %w[alt media_type created_at updated_at]
|
|
95
86
|
self.whitelisted_ransackable_scopes = %w[attached unattached filename_cont]
|
|
87
|
+
self.ransackable_scope_types = { 'attached' => 'boolean', 'unattached' => 'boolean' }
|
|
96
88
|
|
|
97
89
|
delegate :key, :attached?, :variant, :variable?, :blob, :filename, :variation, to: :attachment
|
|
98
90
|
|
|
@@ -48,6 +48,7 @@ module Spree
|
|
|
48
48
|
#
|
|
49
49
|
self.whitelisted_ransackable_attributes = %w[email verified_at]
|
|
50
50
|
self.whitelisted_ransackable_scopes = %w[verified unverified]
|
|
51
|
+
self.ransackable_scope_types = %w[verified unverified].index_with('boolean')
|
|
51
52
|
|
|
52
53
|
def accepts_email_marketing
|
|
53
54
|
return customer.accepts_email_marketing if customer.present?
|
|
@@ -24,7 +24,8 @@ module Spree
|
|
|
24
24
|
self.whitelisted_ransackable_scopes = %w[search_by_name]
|
|
25
25
|
# `label` is what admin surfaces display, so it has to be filterable —
|
|
26
26
|
# matching Spree::OptionValue, which already allows it.
|
|
27
|
-
self.whitelisted_ransackable_attributes = %w[label name]
|
|
27
|
+
self.whitelisted_ransackable_attributes = %w[label name kind]
|
|
28
|
+
search_by :name, :label
|
|
28
29
|
|
|
29
30
|
normalizes :name, with: ->(value) { value&.to_s&.squish&.presence }
|
|
30
31
|
|
data/app/models/spree/order.rb
CHANGED
|
@@ -154,6 +154,7 @@ module Spree
|
|
|
154
154
|
order_group_id po_number
|
|
155
155
|
]
|
|
156
156
|
self.whitelisted_ransackable_scopes = %w[complete incomplete refunded partially_refunded search]
|
|
157
|
+
self.ransackable_scope_types = %w[complete incomplete refunded partially_refunded].index_with('boolean')
|
|
157
158
|
# A seller never sees the buyer's email, and a company member filtering the
|
|
158
159
|
# company's orders must not learn a colleague's; the risk flag and coupon
|
|
159
160
|
# are back-office data. `search` matches on the email too.
|
|
@@ -415,7 +416,7 @@ module Spree
|
|
|
415
416
|
|
|
416
417
|
# Returns the subtotal used for analytics integrations
|
|
417
418
|
# It's a sum of the item total and the promo total
|
|
418
|
-
# @return [Float]
|
|
419
|
+
# @return [Float] a JSON number, as analytics providers expect
|
|
419
420
|
def analytics_subtotal
|
|
420
421
|
(item_total + line_items.sum(:discount_total)).to_f
|
|
421
422
|
end
|
|
@@ -458,7 +459,7 @@ module Spree
|
|
|
458
459
|
end
|
|
459
460
|
|
|
460
461
|
def refunds_total
|
|
461
|
-
refunds
|
|
462
|
+
read_association_in_memory?(:refunds) ? refunds.sum(0.to_d, &:amount) : refunds.sum(:amount)
|
|
462
463
|
end
|
|
463
464
|
|
|
464
465
|
# Checks if the order is partially refunded
|
|
@@ -707,7 +708,7 @@ module Spree
|
|
|
707
708
|
#
|
|
708
709
|
# @return [BigDecimal]
|
|
709
710
|
def returned_items_refund_total
|
|
710
|
-
return refunds.select(&:for_returned_items?).sum(0.to_d, &:amount) if refunds
|
|
711
|
+
return refunds.select(&:for_returned_items?).sum(0.to_d, &:amount) if read_association_in_memory?(:refunds)
|
|
711
712
|
|
|
712
713
|
refunds.for_returned_items.sum(:amount)
|
|
713
714
|
end
|
|
@@ -829,7 +830,6 @@ module Spree
|
|
|
829
830
|
end
|
|
830
831
|
|
|
831
832
|
def fulfill!
|
|
832
|
-
fulfillments.each { |shipment| shipment.update!(self) if shipment.persisted? }
|
|
833
833
|
save!
|
|
834
834
|
update_statuses!
|
|
835
835
|
end
|
|
@@ -1274,7 +1274,7 @@ module Spree
|
|
|
1274
1274
|
end
|
|
1275
1275
|
|
|
1276
1276
|
def credit_card_nil_payment?(attributes)
|
|
1277
|
-
payments.store_credits.present? && attributes[:amount]
|
|
1277
|
+
payments.store_credits.present? && Spree::Money::Rounding.blank_or_zero?(attributes[:amount])
|
|
1278
1278
|
end
|
|
1279
1279
|
end
|
|
1280
1280
|
end
|
|
@@ -100,9 +100,14 @@ module Spree
|
|
|
100
100
|
# the children loaded already (a serializer renders them, a gateway call
|
|
101
101
|
# reads four of these in a row), and an aggregate per figure would issue a
|
|
102
102
|
# query each time even against rows sitting in memory.
|
|
103
|
+
#
|
|
104
|
+
# +tax_total+ is each child's own derivation rather than a column, summed
|
|
105
|
+
# like the rest: asking the children keeps the group's answer the one they
|
|
106
|
+
# gave, where re-deriving it here from the two halves above would be a
|
|
107
|
+
# second way to reach the same figure.
|
|
103
108
|
ROLLED_UP_TOTALS = %i[
|
|
104
109
|
total item_total delivery_total additional_tax_total included_tax_total
|
|
105
|
-
discount_total fee_total
|
|
110
|
+
tax_total discount_total fee_total
|
|
106
111
|
].freeze
|
|
107
112
|
|
|
108
113
|
ROLLED_UP_TOTALS.each do |figure|
|
|
@@ -112,7 +117,7 @@ module Spree
|
|
|
112
117
|
end
|
|
113
118
|
|
|
114
119
|
money_methods(*ROLLED_UP_TOTALS)
|
|
115
|
-
money_methods :gift_card_total
|
|
120
|
+
money_methods :gift_card_total, :amount_due
|
|
116
121
|
|
|
117
122
|
alias ship_total delivery_total
|
|
118
123
|
|
|
@@ -94,6 +94,7 @@ module Spree
|
|
|
94
94
|
# `seller_id` is what the operator's list filters by to see one seller's
|
|
95
95
|
# packaging, or (blank) the marketplace's own.
|
|
96
96
|
self.whitelisted_ransackable_attributes = %w[name kind default seller_id]
|
|
97
|
+
search_by :name
|
|
97
98
|
self.whitelisted_ransackable_associations = %w[seller]
|
|
98
99
|
|
|
99
100
|
# The unit the geometry is expressed in, falling back to what the store's
|
data/app/models/spree/payment.rb
CHANGED
|
@@ -6,6 +6,9 @@ module Spree
|
|
|
6
6
|
derives_number infix: 'P'
|
|
7
7
|
include Spree::HasCustomFields
|
|
8
8
|
include Spree::Metadata
|
|
9
|
+
include Spree::CanonicalDecimals
|
|
10
|
+
|
|
11
|
+
canonical_decimals :amount
|
|
9
12
|
if defined?(Spree::Security::Payments)
|
|
10
13
|
include Spree::Security::Payments
|
|
11
14
|
end
|
|
@@ -255,16 +258,6 @@ module Spree
|
|
|
255
258
|
end
|
|
256
259
|
end
|
|
257
260
|
|
|
258
|
-
def amount=(amount)
|
|
259
|
-
self[:amount] =
|
|
260
|
-
case amount
|
|
261
|
-
when String
|
|
262
|
-
separator = I18n.t('number.currency.format.separator')
|
|
263
|
-
number = amount.delete("^0-9-#{separator}\.").tr(separator, '.')
|
|
264
|
-
number.to_d if number.present?
|
|
265
|
-
end || amount
|
|
266
|
-
end
|
|
267
|
-
|
|
268
261
|
# Resizes a store credit payment that has not been taken yet. Completion
|
|
269
262
|
# takes it against the eligibility event recorded for its amount, so the
|
|
270
263
|
# new amount needs one of its own.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class PaymentMethod::StoreCredit < ::Spree::PaymentMethod
|
|
3
|
+
self.accepts_money_amounts = true
|
|
4
|
+
|
|
3
5
|
def payment_source_class
|
|
4
6
|
::Spree::StoreCredit
|
|
5
7
|
end
|
|
@@ -20,13 +22,13 @@ module Spree
|
|
|
20
22
|
payment.pending?
|
|
21
23
|
end
|
|
22
24
|
|
|
23
|
-
def authorize(
|
|
25
|
+
def authorize(money, store_credit, gateway_options = {})
|
|
24
26
|
if store_credit.nil?
|
|
25
27
|
Spree::PaymentResponse.new(false, I18n.t('spree.store_credit_payment_method.unable_to_find'), {}, {})
|
|
26
28
|
else
|
|
27
29
|
action = lambda do |store_credit|
|
|
28
30
|
store_credit.authorize(
|
|
29
|
-
|
|
31
|
+
money.to_d,
|
|
30
32
|
gateway_options[:currency],
|
|
31
33
|
action_originator: gateway_options[:originator]
|
|
32
34
|
)
|
|
@@ -35,10 +37,10 @@ module Spree
|
|
|
35
37
|
end
|
|
36
38
|
end
|
|
37
39
|
|
|
38
|
-
def capture(
|
|
40
|
+
def capture(money, auth_code, gateway_options = {})
|
|
39
41
|
action = lambda do |store_credit|
|
|
40
42
|
store_credit.capture(
|
|
41
|
-
|
|
43
|
+
money.to_d,
|
|
42
44
|
auth_code,
|
|
43
45
|
gateway_options[:currency],
|
|
44
46
|
action_originator: gateway_options[:originator]
|
|
@@ -47,9 +49,9 @@ module Spree
|
|
|
47
49
|
handle_action(action, :capture, auth_code)
|
|
48
50
|
end
|
|
49
51
|
|
|
50
|
-
def purchase(
|
|
52
|
+
def purchase(money, store_credit, gateway_options = {})
|
|
51
53
|
eligible_events = store_credit.store_credit_events.where(
|
|
52
|
-
amount:
|
|
54
|
+
amount: money.to_d,
|
|
53
55
|
action: Spree::StoreCredit::ELIGIBLE_ACTION
|
|
54
56
|
)
|
|
55
57
|
event = eligible_events.detect do |eligible_event|
|
|
@@ -60,7 +62,7 @@ module Spree
|
|
|
60
62
|
if event.blank?
|
|
61
63
|
Spree::PaymentResponse.new(false, I18n.t('spree.store_credit_payment_method.unable_to_find'), {}, {})
|
|
62
64
|
else
|
|
63
|
-
capture(
|
|
65
|
+
capture(money, event.authorization_code, gateway_options)
|
|
64
66
|
end
|
|
65
67
|
end
|
|
66
68
|
|
|
@@ -71,12 +73,12 @@ module Spree
|
|
|
71
73
|
handle_action(action, :void, auth_code)
|
|
72
74
|
end
|
|
73
75
|
|
|
74
|
-
def credit(
|
|
76
|
+
def credit(money, auth_code, gateway_options)
|
|
75
77
|
action = lambda do |store_credit|
|
|
76
78
|
currency = gateway_options[:currency] || store_credit.currency
|
|
77
79
|
originator = gateway_options[:originator]
|
|
78
80
|
|
|
79
|
-
store_credit.credit(
|
|
81
|
+
store_credit.credit(money.to_d, auth_code, currency, action_originator: originator)
|
|
80
82
|
end
|
|
81
83
|
|
|
82
84
|
handle_action(action, :credit, auth_code)
|
|
@@ -20,6 +20,13 @@ module Spree
|
|
|
20
20
|
include Spree::Security::PaymentMethods
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# Whether authorize, purchase, capture and credit take the amount as a
|
|
24
|
+
# Spree::Money (amount and currency) and convert it to the provider's own
|
|
25
|
+
# unit. Methods that have not opted in receive an integer in hundredths of
|
|
26
|
+
# the currency, which is wrong for yen and dinar; that bridge is removed in
|
|
27
|
+
# Spree 6.1.
|
|
28
|
+
class_attribute :accepts_money_amounts, default: false, instance_writer: false
|
|
29
|
+
|
|
23
30
|
# Blank falls back to the store, so a method only overrides when a merchant
|
|
24
31
|
# deliberately picks one — the meaning the auto_capture boolean already had.
|
|
25
32
|
normalizes :capture_method, with: ->(value) { value.presence }
|
|
@@ -48,8 +55,9 @@ module Spree
|
|
|
48
55
|
scope :storefront_visible, -> { where(storefront_visible: true) }
|
|
49
56
|
scope :admin_only, -> { where(storefront_visible: false) }
|
|
50
57
|
|
|
51
|
-
# Real
|
|
52
|
-
self.whitelisted_ransackable_attributes = %w[storefront_visible]
|
|
58
|
+
# Real columns, so admin clients filter them directly — no ransacker needed.
|
|
59
|
+
self.whitelisted_ransackable_attributes = %w[storefront_visible active type]
|
|
60
|
+
search_by :name
|
|
53
61
|
|
|
54
62
|
after_initialize :set_name, if: :new_record?
|
|
55
63
|
|
|
@@ -339,6 +347,27 @@ module Spree
|
|
|
339
347
|
self.storefront_visible = value.to_s != 'back_end'
|
|
340
348
|
end
|
|
341
349
|
|
|
350
|
+
|
|
351
|
+
# The amount in the form this payment method's gateway calls take.
|
|
352
|
+
#
|
|
353
|
+
# @param amount [BigDecimal] in the currency's own units ("19.99")
|
|
354
|
+
# @param currency [String]
|
|
355
|
+
# @return [Spree::Money, Integer] a Spree::Money for methods that accept
|
|
356
|
+
# money amounts, otherwise hundredths of the currency (deprecated)
|
|
357
|
+
def gateway_amount(amount, currency)
|
|
358
|
+
return Spree::Money.new(amount, currency: currency) if accepts_money_amounts
|
|
359
|
+
|
|
360
|
+
warn_about_hundredths
|
|
361
|
+
hundredths = Spree::Money::Rounding.to_hundredths(amount)
|
|
362
|
+
# A hundredth is the smallest amount these gateways can be sent; zero
|
|
363
|
+
# reads as "the whole amount" to many of them.
|
|
364
|
+
if hundredths.zero? && !amount.zero?
|
|
365
|
+
raise Spree::Core::GatewayError, "#{self.class.name} cannot take an amount below a hundredth of #{currency}"
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
hundredths
|
|
369
|
+
end
|
|
370
|
+
|
|
342
371
|
protected
|
|
343
372
|
|
|
344
373
|
def public_preference_keys
|
|
@@ -348,5 +377,16 @@ module Spree
|
|
|
348
377
|
def set_name
|
|
349
378
|
self.name ||= default_name
|
|
350
379
|
end
|
|
380
|
+
|
|
381
|
+
def warn_about_hundredths
|
|
382
|
+
return if self.class.instance_variable_get(:@warned_about_hundredths)
|
|
383
|
+
|
|
384
|
+
self.class.instance_variable_set(:@warned_about_hundredths, true)
|
|
385
|
+
Spree::Deprecation.warn(
|
|
386
|
+
"#{self.class.name} receives amounts as an integer in hundredths, which is wrong for currencies " \
|
|
387
|
+
'without two decimals. Set `self.accepts_money_amounts = true` and take a Spree::Money ' \
|
|
388
|
+
'(amount and currency); hundredths are removed in Spree 6.1.'
|
|
389
|
+
)
|
|
390
|
+
end
|
|
351
391
|
end
|
|
352
392
|
end
|
data/app/models/spree/policy.rb
CHANGED
data/app/models/spree/price.rb
CHANGED
|
@@ -11,12 +11,16 @@ module Spree
|
|
|
11
11
|
|
|
12
12
|
include Spree::VatPriceCalculation
|
|
13
13
|
include Spree::StorePreferences
|
|
14
|
+
include Spree::CanonicalDecimals
|
|
15
|
+
|
|
16
|
+
canonical_decimals :amount, :compare_at_amount, places: Spree::Money::Rounding::UNIT_PRICE_DECIMALS
|
|
14
17
|
|
|
15
18
|
publishes_lifecycle_events
|
|
16
19
|
|
|
17
20
|
acts_as_paranoid
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
# The largest amount decimal(19,4) holds.
|
|
23
|
+
MAXIMUM_AMOUNT = BigDecimal('999_999_999_999_999.9999')
|
|
20
24
|
|
|
21
25
|
# How many breaks one variant may carry on one list in one currency. A UI
|
|
22
26
|
# sanity bound rather than a technical one — but enforced here so no
|
|
@@ -24,7 +28,7 @@ module Spree
|
|
|
24
28
|
# every priced line (docs/plans/6.0-volume-pricing.md).
|
|
25
29
|
MAXIMUM_BREAKS_PER_VARIANT = 10
|
|
26
30
|
|
|
27
|
-
belongs_to :variant, -> { with_deleted }, class_name: 'Spree::Variant', inverse_of: :prices
|
|
31
|
+
belongs_to :variant, -> { with_deleted }, class_name: 'Spree::Variant', inverse_of: :prices
|
|
28
32
|
belongs_to :price_list, class_name: 'Spree::PriceList', optional: true
|
|
29
33
|
|
|
30
34
|
has_many :price_histories, class_name: 'Spree::PriceHistory', dependent: :delete_all
|
|
@@ -32,6 +36,8 @@ module Spree
|
|
|
32
36
|
before_validation :ensure_currency
|
|
33
37
|
before_save :remove_compare_at_amount_if_equals_amount
|
|
34
38
|
after_save :record_price_history, if: :should_record_price_history?
|
|
39
|
+
after_save :touch_variant, if: -> { saved_changes? && (amount.present? || amount_before_last_save.present?) }
|
|
40
|
+
after_destroy :touch_variant, if: -> { amount.present? }
|
|
35
41
|
|
|
36
42
|
# legacy behavior
|
|
37
43
|
validates :amount, allow_nil: true, numericality: {
|
|
@@ -100,10 +106,6 @@ module Spree
|
|
|
100
106
|
Spree::Money.new(amount || 0, currency: currency.upcase)
|
|
101
107
|
end
|
|
102
108
|
|
|
103
|
-
def amount=(amount)
|
|
104
|
-
self[:amount] = amount.blank? ? nil : Spree::LocalizedNumber.parse(amount)
|
|
105
|
-
end
|
|
106
|
-
|
|
107
109
|
# Returns the amount in cents
|
|
108
110
|
# @return [Integer]
|
|
109
111
|
def amount_in_cents
|
|
@@ -114,12 +116,6 @@ module Spree
|
|
|
114
116
|
Spree::Money.new(compare_at_amount || 0, currency: currency)
|
|
115
117
|
end
|
|
116
118
|
|
|
117
|
-
def compare_at_amount=(value)
|
|
118
|
-
calculated_value = Spree::LocalizedNumber.parse(value) if value.present?
|
|
119
|
-
|
|
120
|
-
self[:compare_at_amount] = calculated_value
|
|
121
|
-
end
|
|
122
|
-
|
|
123
119
|
# Returns the compare at amount for display
|
|
124
120
|
# @return [Spree::Money, nil]
|
|
125
121
|
def display_compare_at_amount
|
|
@@ -137,9 +133,7 @@ module Spree
|
|
|
137
133
|
end
|
|
138
134
|
|
|
139
135
|
alias_attribute :price, :amount
|
|
140
|
-
alias_method :price=, :amount=
|
|
141
136
|
alias_attribute :compare_at_price, :compare_at_amount
|
|
142
|
-
alias_method :compare_at_price=, :compare_at_amount=
|
|
143
137
|
|
|
144
138
|
# Whether this figure is the merchant's answer for the buyer's geography
|
|
145
139
|
# already. A price list narrowed by market or country states its prices for
|
|
@@ -342,6 +336,15 @@ module Spree
|
|
|
342
336
|
rungs.sort_by(&:first)
|
|
343
337
|
end
|
|
344
338
|
|
|
339
|
+
# Stands in for `touch: true` on the variant, skipping placeholder rows: a
|
|
340
|
+
# price list holds one without an amount for every variant and currency
|
|
341
|
+
# nobody has priced yet, and it charges nothing, so writing one changes no
|
|
342
|
+
# price a shopper sees — yet the touch would expire the product's cached
|
|
343
|
+
# responses and reindex it for search.
|
|
344
|
+
def touch_variant
|
|
345
|
+
variant&.touch_later
|
|
346
|
+
end
|
|
347
|
+
|
|
345
348
|
def should_record_price_history?
|
|
346
349
|
price_list_id.nil? &&
|
|
347
350
|
amount.present? &&
|
|
@@ -10,6 +10,10 @@ module Spree
|
|
|
10
10
|
# and not twenty percent off an already-discounted number
|
|
11
11
|
# (docs/plans/6.0-volume-pricing.md).
|
|
12
12
|
class PriceAdjustmentTier < Spree.base_class
|
|
13
|
+
include Spree::CanonicalDecimals
|
|
14
|
+
|
|
15
|
+
canonical_decimals :percentage, places: 3
|
|
16
|
+
|
|
13
17
|
has_prefix_id :pat
|
|
14
18
|
|
|
15
19
|
# How many bands one list may carry, matching the break cap so a merchant
|