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
|
@@ -19,9 +19,10 @@ module Spree
|
|
|
19
19
|
# @param expected_at [Date, nil]
|
|
20
20
|
# @param reference [String, nil] the supplier's own order number
|
|
21
21
|
# @param notes [String, nil]
|
|
22
|
+
# @param metadata [Hash, nil]
|
|
22
23
|
# @param created_by [Object, nil]
|
|
23
24
|
def perform(store:, supplier:, destination_location:, items: [], currency: nil,
|
|
24
|
-
expected_at: nil, cancel_by: nil, reference: nil, notes: nil, created_by: nil)
|
|
25
|
+
expected_at: nil, cancel_by: nil, reference: nil, notes: nil, metadata: nil, created_by: nil)
|
|
25
26
|
super
|
|
26
27
|
|
|
27
28
|
step :build_purchase_order
|
|
@@ -50,6 +51,7 @@ module Spree
|
|
|
50
51
|
created_by: created_by,
|
|
51
52
|
status: Spree::PurchaseOrder.default_status
|
|
52
53
|
)
|
|
54
|
+
@purchase_order.metadata = metadata if metadata
|
|
53
55
|
|
|
54
56
|
Array(items).each do |item|
|
|
55
57
|
@purchase_order.items.build(
|
|
@@ -13,7 +13,7 @@ module Spree
|
|
|
13
13
|
attr_reader :shipping_label
|
|
14
14
|
|
|
15
15
|
# @param owner [Spree::Fulfillment, Spree::Return]
|
|
16
|
-
# @param file [ActionDispatch::Http::UploadedFile, String] the file, or a signed
|
|
16
|
+
# @param file [ActionDispatch::Http::UploadedFile, String] the file, or a signed id from POST /files
|
|
17
17
|
# @param tracking_number [String] the number printed on the label
|
|
18
18
|
# @param carrier [String, nil] free text; detected from the number when omitted
|
|
19
19
|
# @param service [String, nil]
|
|
@@ -69,7 +69,7 @@ module Spree
|
|
|
69
69
|
failure(@shipping_label) unless @shipping_label.save
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
#
|
|
72
|
+
# An uploaded blob carries the type the client declared, which the
|
|
73
73
|
# attachment validator re-decides from the bytes; either way the
|
|
74
74
|
# extension is the merchant-visible answer, so it wins when it names a
|
|
75
75
|
# format Spree knows.
|
|
@@ -16,9 +16,10 @@ module Spree
|
|
|
16
16
|
# may legitimately open empty and gain lines as the merchant packs
|
|
17
17
|
# @param reference [String, nil] the merchant's own label for the trip
|
|
18
18
|
# @param notes [String, nil]
|
|
19
|
+
# @param metadata [Hash, nil]
|
|
19
20
|
# @param created_by [Object, nil]
|
|
20
21
|
def perform(store:, source_location:, destination_location:, items: [],
|
|
21
|
-
reference: nil, notes: nil, created_by: nil)
|
|
22
|
+
reference: nil, notes: nil, metadata: nil, created_by: nil)
|
|
22
23
|
super
|
|
23
24
|
|
|
24
25
|
step :build_transfer
|
|
@@ -46,6 +47,7 @@ module Spree
|
|
|
46
47
|
created_by: created_by,
|
|
47
48
|
status: Spree::StockTransfer.default_status
|
|
48
49
|
)
|
|
50
|
+
@stock_transfer.metadata = metadata if metadata
|
|
49
51
|
|
|
50
52
|
Array(items).each do |item|
|
|
51
53
|
@stock_transfer.items.build(variant: item[:variant], quantity_shipped: item[:quantity_shipped])
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
{
|
|
2
|
+
"AED": 2,
|
|
3
|
+
"AFN": 2,
|
|
4
|
+
"ALL": 2,
|
|
5
|
+
"AMD": 2,
|
|
6
|
+
"ANG": 2,
|
|
7
|
+
"AOA": 2,
|
|
8
|
+
"ARS": 2,
|
|
9
|
+
"AUD": 2,
|
|
10
|
+
"AWG": 2,
|
|
11
|
+
"AZN": 2,
|
|
12
|
+
"BAM": 2,
|
|
13
|
+
"BBD": 2,
|
|
14
|
+
"BDT": 2,
|
|
15
|
+
"BGN": 2,
|
|
16
|
+
"BHD": 3,
|
|
17
|
+
"BIF": 0,
|
|
18
|
+
"BMD": 2,
|
|
19
|
+
"BND": 2,
|
|
20
|
+
"BOB": 2,
|
|
21
|
+
"BRL": 2,
|
|
22
|
+
"BSD": 2,
|
|
23
|
+
"BTN": 2,
|
|
24
|
+
"BWP": 2,
|
|
25
|
+
"BYN": 2,
|
|
26
|
+
"BYR": 0,
|
|
27
|
+
"BZD": 2,
|
|
28
|
+
"CAD": 2,
|
|
29
|
+
"CDF": 2,
|
|
30
|
+
"CHF": 2,
|
|
31
|
+
"CLF": 4,
|
|
32
|
+
"CLP": 0,
|
|
33
|
+
"CNY": 2,
|
|
34
|
+
"COP": 2,
|
|
35
|
+
"CRC": 2,
|
|
36
|
+
"CUC": 2,
|
|
37
|
+
"CUP": 2,
|
|
38
|
+
"CVE": 2,
|
|
39
|
+
"CZK": 2,
|
|
40
|
+
"DJF": 0,
|
|
41
|
+
"DKK": 2,
|
|
42
|
+
"DOP": 2,
|
|
43
|
+
"DZD": 2,
|
|
44
|
+
"EEK": 2,
|
|
45
|
+
"EGP": 2,
|
|
46
|
+
"ERN": 2,
|
|
47
|
+
"ETB": 2,
|
|
48
|
+
"EUR": 2,
|
|
49
|
+
"FJD": 2,
|
|
50
|
+
"FKP": 2,
|
|
51
|
+
"GBP": 2,
|
|
52
|
+
"GEL": 2,
|
|
53
|
+
"GHS": 2,
|
|
54
|
+
"GIP": 2,
|
|
55
|
+
"GMD": 2,
|
|
56
|
+
"GNF": 0,
|
|
57
|
+
"GTQ": 2,
|
|
58
|
+
"GYD": 2,
|
|
59
|
+
"HKD": 2,
|
|
60
|
+
"HNL": 2,
|
|
61
|
+
"HRK": 2,
|
|
62
|
+
"HTG": 2,
|
|
63
|
+
"HUF": 2,
|
|
64
|
+
"IDR": 2,
|
|
65
|
+
"ILS": 2,
|
|
66
|
+
"INR": 2,
|
|
67
|
+
"IQD": 3,
|
|
68
|
+
"IRR": 2,
|
|
69
|
+
"ISK": 0,
|
|
70
|
+
"JMD": 2,
|
|
71
|
+
"JOD": 3,
|
|
72
|
+
"JPY": 0,
|
|
73
|
+
"KES": 2,
|
|
74
|
+
"KGS": 2,
|
|
75
|
+
"KHR": 2,
|
|
76
|
+
"KMF": 0,
|
|
77
|
+
"KPW": 2,
|
|
78
|
+
"KRW": 0,
|
|
79
|
+
"KWD": 3,
|
|
80
|
+
"KYD": 2,
|
|
81
|
+
"KZT": 2,
|
|
82
|
+
"LAK": 2,
|
|
83
|
+
"LBP": 2,
|
|
84
|
+
"LKR": 2,
|
|
85
|
+
"LRD": 2,
|
|
86
|
+
"LSL": 2,
|
|
87
|
+
"LTL": 2,
|
|
88
|
+
"LVL": 2,
|
|
89
|
+
"LYD": 3,
|
|
90
|
+
"MAD": 2,
|
|
91
|
+
"MDL": 2,
|
|
92
|
+
"MGA": 2,
|
|
93
|
+
"MKD": 2,
|
|
94
|
+
"MMK": 2,
|
|
95
|
+
"MNT": 2,
|
|
96
|
+
"MOP": 2,
|
|
97
|
+
"MRO": 2,
|
|
98
|
+
"MRU": 2,
|
|
99
|
+
"MTL": 2,
|
|
100
|
+
"MUR": 2,
|
|
101
|
+
"MVR": 2,
|
|
102
|
+
"MWK": 2,
|
|
103
|
+
"MXN": 2,
|
|
104
|
+
"MYR": 2,
|
|
105
|
+
"MZN": 2,
|
|
106
|
+
"NAD": 2,
|
|
107
|
+
"NGN": 2,
|
|
108
|
+
"NIO": 2,
|
|
109
|
+
"NOK": 2,
|
|
110
|
+
"NPR": 2,
|
|
111
|
+
"NZD": 2,
|
|
112
|
+
"OMR": 3,
|
|
113
|
+
"PAB": 2,
|
|
114
|
+
"PEN": 2,
|
|
115
|
+
"PGK": 2,
|
|
116
|
+
"PHP": 2,
|
|
117
|
+
"PKR": 2,
|
|
118
|
+
"PLN": 2,
|
|
119
|
+
"PYG": 0,
|
|
120
|
+
"QAR": 2,
|
|
121
|
+
"RON": 2,
|
|
122
|
+
"RSD": 2,
|
|
123
|
+
"RUB": 2,
|
|
124
|
+
"RWF": 0,
|
|
125
|
+
"SAR": 2,
|
|
126
|
+
"SBD": 2,
|
|
127
|
+
"SCR": 2,
|
|
128
|
+
"SDG": 2,
|
|
129
|
+
"SEK": 2,
|
|
130
|
+
"SGD": 2,
|
|
131
|
+
"SHP": 2,
|
|
132
|
+
"SKK": 2,
|
|
133
|
+
"SLE": 2,
|
|
134
|
+
"SLL": 2,
|
|
135
|
+
"SOS": 2,
|
|
136
|
+
"SRD": 2,
|
|
137
|
+
"SSP": 2,
|
|
138
|
+
"STD": 2,
|
|
139
|
+
"STN": 2,
|
|
140
|
+
"SVC": 2,
|
|
141
|
+
"SYP": 2,
|
|
142
|
+
"SZL": 2,
|
|
143
|
+
"THB": 2,
|
|
144
|
+
"TJS": 2,
|
|
145
|
+
"TMM": 2,
|
|
146
|
+
"TMT": 2,
|
|
147
|
+
"TND": 3,
|
|
148
|
+
"TOP": 2,
|
|
149
|
+
"TRY": 2,
|
|
150
|
+
"TTD": 2,
|
|
151
|
+
"TWD": 2,
|
|
152
|
+
"TZS": 2,
|
|
153
|
+
"UAH": 2,
|
|
154
|
+
"UGX": 0,
|
|
155
|
+
"USD": 2,
|
|
156
|
+
"UYI": 0,
|
|
157
|
+
"UYU": 2,
|
|
158
|
+
"UYW": 4,
|
|
159
|
+
"UZS": 2,
|
|
160
|
+
"VED": 2,
|
|
161
|
+
"VEF": 2,
|
|
162
|
+
"VES": 2,
|
|
163
|
+
"VND": 0,
|
|
164
|
+
"VUV": 0,
|
|
165
|
+
"WST": 2,
|
|
166
|
+
"XAF": 0,
|
|
167
|
+
"XCD": 2,
|
|
168
|
+
"XCG": 2,
|
|
169
|
+
"XOF": 0,
|
|
170
|
+
"XPF": 0,
|
|
171
|
+
"YER": 2,
|
|
172
|
+
"ZAR": 2,
|
|
173
|
+
"ZMK": 2,
|
|
174
|
+
"ZMW": 2,
|
|
175
|
+
"ZWD": 2,
|
|
176
|
+
"ZWG": 2,
|
|
177
|
+
"ZWL": 2,
|
|
178
|
+
"ZWN": 2,
|
|
179
|
+
"ZWR": 2
|
|
180
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# A store's content locales are the codes Spree::Locales::ALL lists,
|
|
2
|
+
# independent of the languages anything has translation files for. An API
|
|
3
|
+
# request sets I18n.locale (and so Mobility's) to its content locale, and both
|
|
4
|
+
# refuse a locale I18n does not know, so this registers every listed code:
|
|
5
|
+
# content can be stored and read in, say, Yoruba, while messages in it fall
|
|
6
|
+
# back along the i18n fallback chain. The backend ignores a locale with no
|
|
7
|
+
# keys at all, hence the one marker key. An app that limits
|
|
8
|
+
# `config.i18n.available_locales` keeps its own list.
|
|
9
|
+
Spree::Locales::ALL.to_h { |code| [code.to_sym, { spree_content_locale: true }] }
|
data/config/locales/en.yml
CHANGED
|
@@ -251,6 +251,7 @@ en:
|
|
|
251
251
|
must_belong_to_same_store: must belong to the same store
|
|
252
252
|
not_registered: is not a registered provider
|
|
253
253
|
spree_content_type_not_allowed: is not a file type we accept
|
|
254
|
+
too_many_decimals: has more than %{count} decimal places
|
|
254
255
|
models:
|
|
255
256
|
spree/allowed_origin:
|
|
256
257
|
attributes:
|
|
@@ -381,6 +382,7 @@ en:
|
|
|
381
382
|
base:
|
|
382
383
|
duplicate_quantity: each quantity break must have its own quantity
|
|
383
384
|
invalid_quantity: each quantity break must start at a whole number of units
|
|
385
|
+
malformed_price: a price must be a number like 16.50
|
|
384
386
|
negative_price: a price cannot be below zero
|
|
385
387
|
nothing_to_apply_to: has no rules, no catalog and no products, so it would apply to nothing. Add one before activating.
|
|
386
388
|
price_rises_with_quantity: a quantity break cannot cost more than the price a buyer pays below it
|
|
@@ -873,6 +875,7 @@ en:
|
|
|
873
875
|
invalid_status: Invalid status
|
|
874
876
|
invalid_storefront_access: is not a valid storefront access level
|
|
875
877
|
items_not_a_list: items must be a list of objects
|
|
878
|
+
money_preference_too_many_decimals: has more than %{count} decimal places for %{currency}
|
|
876
879
|
must_be_greater_than_min_amount: must be greater than or equal to the minimum
|
|
877
880
|
no_pickup_point_provider: this delivery method has no pickup point provider configured
|
|
878
881
|
not_cancellable: cannot be canceled once its goods have been dispatched
|
|
@@ -894,6 +897,7 @@ en:
|
|
|
894
897
|
stock_level_immutable: cannot be changed after the hold is taken — release the hold and take it again
|
|
895
898
|
store_association_can_not_be_changed: The store association can not be changed
|
|
896
899
|
store_is_already_set: Store is already set
|
|
900
|
+
unknown_calculator_type: "%{type} is not a calculator offered here"
|
|
897
901
|
unknown_custom_field_type: 'is not a known custom field type (expected one of: %{types})'
|
|
898
902
|
unknown_scopes: 'contains unknown scopes: %{scopes}'
|
|
899
903
|
unregistered_actor: is not a registered actor class
|
|
@@ -956,6 +960,7 @@ en:
|
|
|
956
960
|
image: Image
|
|
957
961
|
import: Import
|
|
958
962
|
import_has_no_failed_rows: This import has no failed rows to retry.
|
|
963
|
+
import_invalid_number: "%{column} must be a number like 16.50, got %{value}."
|
|
959
964
|
import_mailer:
|
|
960
965
|
greeting: Hi %{user_name},
|
|
961
966
|
import_done:
|
|
@@ -1000,6 +1005,7 @@ en:
|
|
|
1000
1005
|
link: Link
|
|
1001
1006
|
locale: Locale
|
|
1002
1007
|
locale_not_supported_by_store: is not supported by this store
|
|
1008
|
+
locales_not_listed: 'includes locales Spree does not support: %{codes}'
|
|
1003
1009
|
manual: Manual
|
|
1004
1010
|
market: Market
|
|
1005
1011
|
me_no_current_user: No admin user authenticated this request
|
|
@@ -1138,8 +1144,7 @@ en:
|
|
|
1138
1144
|
phone: Phone
|
|
1139
1145
|
pickup:
|
|
1140
1146
|
store_pickup: Store Pickup
|
|
1141
|
-
po_document_invalid: A purchase order document must be a PDF, image or
|
|
1142
|
-
po_document_invalid_signed_id: The purchase order document reference is not valid.
|
|
1147
|
+
po_document_invalid: A purchase order document must be a PDF, image, Word document, spreadsheet or CSV file of %{size} or less.
|
|
1143
1148
|
po_document_missing: This order has no attached purchase order document.
|
|
1144
1149
|
po_document_too_large: is larger than %{size}.
|
|
1145
1150
|
po_document_upload_incomplete: The purchase order document was not uploaded completely. Please try attaching it again.
|
|
@@ -1672,6 +1677,7 @@ en:
|
|
|
1672
1677
|
errors:
|
|
1673
1678
|
already_purchased: This parcel already has an active shipping label
|
|
1674
1679
|
file_fetch_failed: The label file could not be fetched from the carrier (HTTP %{status})
|
|
1680
|
+
file_renamed: Send the uploaded label's signed id as file_signed_id
|
|
1675
1681
|
file_required: An uploaded label needs a file
|
|
1676
1682
|
no_file_url: The carrier did not provide a file for this label
|
|
1677
1683
|
not_refund_requested: Only a label awaiting the carrier's refund decision can be confirmed refunded
|
|
@@ -1791,6 +1797,8 @@ en:
|
|
|
1791
1797
|
type: Type
|
|
1792
1798
|
unable_to_connect_to_gateway: Unable to connect to gateway.
|
|
1793
1799
|
updated_at: Updated at
|
|
1800
|
+
upload_reference_invalid: A signed reference in this request is invalid or has expired. For a file, upload it again.
|
|
1801
|
+
upload_wrong_visibility: must be uploaded with %{visibility} visibility
|
|
1794
1802
|
url: URL
|
|
1795
1803
|
user: User
|
|
1796
1804
|
validation:
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Widens every money column to decimal(19,4): room for a dinar's third decimal,
|
|
2
|
+
# sub-cent unit prices and very large amounts. Every stored value fits exactly,
|
|
3
|
+
# so no value changes. A column already at (19,4) is skipped, so an install
|
|
4
|
+
# that widened its largest tables online ahead of the upgrade runs this as a
|
|
5
|
+
# no-op for those (see the 5.6 to 6.0 upgrade guide).
|
|
6
|
+
class ChangeMoneyColumnsToDecimal194 < ActiveRecord::Migration[8.1]
|
|
7
|
+
COLUMNS = {
|
|
8
|
+
spree_adjustments: %i[amount],
|
|
9
|
+
spree_carts: %i[additional_tax_total adjustment_total delivery_total discount_total fee_total included_tax_total
|
|
10
|
+
item_total non_taxable_adjustment_total payment_total taxable_adjustment_total total],
|
|
11
|
+
spree_catalog_order_minimums: %i[amount],
|
|
12
|
+
spree_claim_line_items: %i[additional_tax_total included_tax_total refund_amount],
|
|
13
|
+
spree_commission_lines: %i[amount tax_amount total],
|
|
14
|
+
spree_commission_rate_values: %i[amount max_amount min_amount],
|
|
15
|
+
spree_delivery_method_services: %i[markup_flat],
|
|
16
|
+
spree_delivery_methods: %i[markup_flat],
|
|
17
|
+
spree_delivery_rates: %i[cost],
|
|
18
|
+
spree_discounts: %i[amount value],
|
|
19
|
+
spree_exchange_line_items: %i[new_additional_tax_total new_included_tax_total original_additional_tax_total
|
|
20
|
+
original_included_tax_total],
|
|
21
|
+
spree_fees: %i[amount],
|
|
22
|
+
spree_fulfillments: %i[additional_tax_total adjustment_total cost discount_total included_tax_total
|
|
23
|
+
non_taxable_adjustment_total pre_tax_amount taxable_adjustment_total],
|
|
24
|
+
spree_gift_card_batches: %i[amount],
|
|
25
|
+
spree_gift_cards: %i[amount amount_authorized amount_used],
|
|
26
|
+
spree_line_items: %i[additional_tax_total adjustment_total cost_price discount_total included_tax_total
|
|
27
|
+
non_taxable_adjustment_total pre_tax_amount price taxable_adjustment_total],
|
|
28
|
+
spree_orders: %i[additional_tax_total adjustment_total commission_amount_total commission_tax_total commission_total
|
|
29
|
+
delivery_total discount_total fee_total included_tax_total item_total non_taxable_adjustment_total
|
|
30
|
+
payment_total taxable_adjustment_total total],
|
|
31
|
+
spree_payment_capture_events: %i[amount],
|
|
32
|
+
spree_payment_sessions: %i[amount],
|
|
33
|
+
spree_payment_splits: %i[authorized_amount captured_amount claimed_amount refunded_amount],
|
|
34
|
+
spree_payments: %i[amount],
|
|
35
|
+
spree_price_histories: %i[amount compare_at_amount],
|
|
36
|
+
spree_prices: %i[amount compare_at_amount],
|
|
37
|
+
spree_products: %i[revenue],
|
|
38
|
+
spree_products_stores: %i[revenue],
|
|
39
|
+
spree_purchase_order_items: %i[unit_cost],
|
|
40
|
+
spree_refunds: %i[amount tax_amount],
|
|
41
|
+
spree_reimbursement_credits: %i[amount],
|
|
42
|
+
spree_reimbursements: %i[total],
|
|
43
|
+
spree_return_items: %i[additional_tax_total included_tax_total pre_tax_amount],
|
|
44
|
+
spree_return_line_items: %i[additional_tax_total included_tax_total pre_tax_amount],
|
|
45
|
+
spree_seller_payouts: %i[amount],
|
|
46
|
+
spree_seller_transfers: %i[amount settled_amount],
|
|
47
|
+
spree_sellers: %i[minimum_payout_amount],
|
|
48
|
+
spree_shipping_labels: %i[cost],
|
|
49
|
+
spree_stock_movements: %i[unit_cost],
|
|
50
|
+
spree_store_credit_events: %i[amount user_total_amount],
|
|
51
|
+
spree_store_credits: %i[amount amount_authorized amount_used],
|
|
52
|
+
spree_tax_lines: %i[amount],
|
|
53
|
+
spree_variants: %i[cost_price]
|
|
54
|
+
}.freeze
|
|
55
|
+
|
|
56
|
+
# What `down` restores: decimal(10,2) unless listed.
|
|
57
|
+
ORIGINAL = {
|
|
58
|
+
'spree_delivery_rates.cost' => [8, 2],
|
|
59
|
+
'spree_store_credit_events.amount' => [8, 2],
|
|
60
|
+
'spree_store_credit_events.user_total_amount' => [8, 2],
|
|
61
|
+
'spree_store_credits.amount' => [8, 2],
|
|
62
|
+
'spree_store_credits.amount_authorized' => [8, 2],
|
|
63
|
+
'spree_store_credits.amount_used' => [8, 2],
|
|
64
|
+
'spree_fulfillments.pre_tax_amount' => [12, 4],
|
|
65
|
+
'spree_line_items.pre_tax_amount' => [12, 4],
|
|
66
|
+
'spree_return_items.additional_tax_total' => [12, 4],
|
|
67
|
+
'spree_return_items.included_tax_total' => [12, 4],
|
|
68
|
+
'spree_return_items.pre_tax_amount' => [12, 4],
|
|
69
|
+
'spree_products.revenue' => [16, 4]
|
|
70
|
+
}.freeze
|
|
71
|
+
|
|
72
|
+
def up
|
|
73
|
+
deliberately_rewriting { COLUMNS.each_key { |table| retype(table) { [19, 4] } } }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def down
|
|
77
|
+
deliberately_rewriting do
|
|
78
|
+
COLUMNS.each_key { |table| retype(table) { |column| ORIGINAL.fetch("#{table}.#{column}", [10, 2]) } }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# The rewrite is the point of this migration and the upgrade guide documents
|
|
85
|
+
# it, so an app running strong_migrations is not stopped by it.
|
|
86
|
+
def deliberately_rewriting(&block)
|
|
87
|
+
respond_to?(:safety_assured) ? safety_assured(&block) : yield
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# One ALTER TABLE per table, so each is rewritten once rather than once per
|
|
91
|
+
# column. PostgreSQL keeps NOT NULL and the default through a type change,
|
|
92
|
+
# and Rails' MySQL adapter carries them over itself; passing them would add a
|
|
93
|
+
# SET NOT NULL that scans every row. SQLite rebuilds the table from the new
|
|
94
|
+
# definition, so it is told both.
|
|
95
|
+
def retype(table)
|
|
96
|
+
return unless table_exists?(table)
|
|
97
|
+
|
|
98
|
+
existing = connection.columns(table).index_by(&:name)
|
|
99
|
+
changes = COLUMNS[table].filter_map do |name|
|
|
100
|
+
column = existing[name.to_s]
|
|
101
|
+
next if column.nil?
|
|
102
|
+
|
|
103
|
+
precision, scale = yield(name)
|
|
104
|
+
[column, precision, scale] unless column.precision == precision && column.scale == scale
|
|
105
|
+
end
|
|
106
|
+
return if changes.empty?
|
|
107
|
+
|
|
108
|
+
change_table(table, bulk: true) do |definition|
|
|
109
|
+
changes.each do |column, precision, scale|
|
|
110
|
+
options = { precision: precision, scale: scale }
|
|
111
|
+
options.merge!(null: column.null, default: column.default) if connection.adapter_name.match?(/sqlite/i)
|
|
112
|
+
definition.change column.name, :decimal, **options
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Records which store owns every uploaded file, so a file from one store cannot
|
|
2
|
+
# be attached in another and storage can be measured and cleaned up per store.
|
|
3
|
+
# Active Storage ignores columns it does not define. Existing rows are filled
|
|
4
|
+
# in by `spree:uploads:assign_stores` in the 6.0 upgrade.
|
|
5
|
+
class AddStoreIdToActiveStorageBlobs < ActiveRecord::Migration[8.1]
|
|
6
|
+
def change
|
|
7
|
+
return unless table_exists?(:active_storage_blobs)
|
|
8
|
+
return if column_exists?(:active_storage_blobs, :store_id)
|
|
9
|
+
|
|
10
|
+
add_reference :active_storage_blobs, :store, null: true, index: true
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -42,9 +42,28 @@ Dummy::Application.configure do
|
|
|
42
42
|
# Print deprecation notices to the stderr
|
|
43
43
|
config.active_support.deprecation = :stderr
|
|
44
44
|
|
|
45
|
-
# Spree's own code must not call
|
|
45
|
+
# Spree's own code and specs must not call these deprecated APIs. A spec
|
|
46
|
+
# covering one on purpose stubs Spree::Deprecation.warn or silences it.
|
|
46
47
|
config.active_support.disallowed_deprecation = :raise
|
|
47
|
-
config.active_support.disallowed_deprecation_warnings = [
|
|
48
|
+
config.active_support.disallowed_deprecation_warnings = [
|
|
49
|
+
/Spree\.t is deprecated/,
|
|
50
|
+
/Spree::Product#(master|variants_including_master) is deprecated/,
|
|
51
|
+
/Spree::(Order|Cart)#(shipment_total|promo_total|item_count|shipping_discount) is deprecated/,
|
|
52
|
+
/Spree::Order#finalize! is deprecated/,
|
|
53
|
+
/Spree::Payment#state=? is deprecated/,
|
|
54
|
+
/Spree::Payment#(process|authorize|purchase|capture|void_transaction)! is deprecated/,
|
|
55
|
+
/Spree::(Category|Product)#classifications is deprecated/,
|
|
56
|
+
/Spree::Product#taxons=? is deprecated/,
|
|
57
|
+
/Spree::Promotion::Rules::Category#taxon/,
|
|
58
|
+
/Spree::Fulfillment#(add_)?shipping_method is deprecated/,
|
|
59
|
+
/Spree::Stock::Package#shipping_methods is deprecated/,
|
|
60
|
+
/Calling Spree::(Carts::AddItem|StockReservations::(Reserve|Extend)) with order: is deprecated/,
|
|
61
|
+
/Spree::Config\[:admin_url\] is deprecated/,
|
|
62
|
+
/private_metadata=? is deprecated/,
|
|
63
|
+
/Spree::Pricing::Resolver is deprecated/,
|
|
64
|
+
/Spree::Invitation#accept! is deprecated/,
|
|
65
|
+
/`preference :\w+, in:` is deprecated/
|
|
66
|
+
]
|
|
48
67
|
|
|
49
68
|
config.active_job.queue_adapter = :test
|
|
50
69
|
|
|
@@ -119,6 +119,19 @@ module Spree
|
|
|
119
119
|
# requirement, in bytes. Small on purpose: these are certificates and
|
|
120
120
|
# registrations, and an unbounded upload is a way to fill a disk.
|
|
121
121
|
preference :max_seller_document_upload_size, :integer, default: 20_971_520, env: 'SPREE_MAX_SELLER_DOCUMENT_UPLOAD_SIZE' # 20 MB
|
|
122
|
+
|
|
123
|
+
# Largest file the Admin and Seller APIs accept for upload straight to
|
|
124
|
+
# storage, in bytes. Digital products (courses, archives) can be large.
|
|
125
|
+
preference :max_upload_size, :integer, default: 5_368_709_120, env: 'SPREE_MAX_UPLOAD_SIZE' # 5 GB
|
|
126
|
+
|
|
127
|
+
# Largest file accepted as bytes in the upload request itself, in bytes.
|
|
128
|
+
# Lower, because those bytes pass through the application.
|
|
129
|
+
preference :max_multipart_upload_size, :integer, default: 10_485_760, env: 'SPREE_MAX_MULTIPART_UPLOAD_SIZE' # 10 MB
|
|
130
|
+
|
|
131
|
+
# Days an uploaded file that was never attached to anything is kept
|
|
132
|
+
# before the daily purge deletes it. Longer than an upload reference
|
|
133
|
+
# stays valid (one day), so nothing a client can still attach is lost.
|
|
134
|
+
preference :unattached_upload_retention_days, :integer, default: 2, env: 'SPREE_UNATTACHED_UPLOAD_RETENTION_DAYS'
|
|
122
135
|
preference :non_expiring_credit_types, :array, default: [], deprecated: 'Nothing reads this in Spree 6 — store credits no longer carry a category, and expiry lives on Spree::GiftCard'
|
|
123
136
|
preference :products_per_page, :integer, default: 12, deprecated: 'Nothing reads this in Spree 6 — pass per_page to the API instead'
|
|
124
137
|
preference :restock_inventory, :boolean, default: true, deprecated: 'Restocking is decided per line item by Spree::ReturnLineItem#resellable'
|
|
@@ -137,10 +150,15 @@ module Spree
|
|
|
137
150
|
preference :empty_cart_expiry_hours, :integer, default: 48, env: 'SPREE_EMPTY_CART_EXPIRY_HOURS'
|
|
138
151
|
preference :show_variant_full_price, :boolean, default: false, deprecated: 'Nothing reads this in Spree 6 — a storefront decides how it renders prices'
|
|
139
152
|
preference :show_products_without_price, :boolean, default: false, deprecated: 'Set it on the store instead'
|
|
153
|
+
# The daily update check also reports usage (see Spree::UpdateCheck).
|
|
154
|
+
# SPREE_TELEMETRY_DISABLED or DO_NOT_TRACK turns it off as well.
|
|
155
|
+
preference :telemetry_enabled, :boolean, default: true
|
|
140
156
|
preference :tax_using_ship_address, :boolean, default: true, deprecated: 'Set it on the store instead'
|
|
141
157
|
preference :track_inventory_levels, :boolean, default: true, deprecated: 'Set it on the store instead' # Determines whether to track on_hand values for variants / products.
|
|
142
158
|
preference :track_price_history, :boolean, default: true, deprecated: 'Set it on the store instead' # Records price changes for Omnibus Directive compliance. Disable for non-EU stores.
|
|
143
159
|
preference :price_history_retention_days, :integer, default: 30, deprecated: 'Set it on the store instead' # Days to retain price history records. Used by spree:price_history:prune rake task.
|
|
160
|
+
# SPREE_UPDATE_CHECK_DISABLED turns it off as well.
|
|
161
|
+
preference :update_check_enabled, :boolean, default: true
|
|
144
162
|
preference :use_user_locale, :boolean, default: true, deprecated: true
|
|
145
163
|
|
|
146
164
|
# Sets the path used for products, taxons and pages.
|
data/lib/spree/core/engine.rb
CHANGED
|
@@ -559,6 +559,25 @@ module Spree
|
|
|
559
559
|
)
|
|
560
560
|
end
|
|
561
561
|
|
|
562
|
+
# Deprecated constant aliases warn when their file loads. Leaving them
|
|
563
|
+
# out of eager loading keeps every boot quiet; they still load, and
|
|
564
|
+
# warn, the first time something references the old name.
|
|
565
|
+
DEPRECATED_ALIAS_FILES = %w[
|
|
566
|
+
app/models/concerns/spree/user_methods.rb
|
|
567
|
+
app/models/spree/asset.rb
|
|
568
|
+
app/models/spree/classification.rb
|
|
569
|
+
app/models/spree/promotion/rules/taxon.rb
|
|
570
|
+
app/models/spree/promotion_rule_taxon.rb
|
|
571
|
+
app/models/spree/return_authorization_reason.rb
|
|
572
|
+
app/models/spree/taxon.rb
|
|
573
|
+
app/services/spree/checkout/add_store_credit.rb
|
|
574
|
+
app/services/spree/checkout/remove_store_credit.rb
|
|
575
|
+
].freeze
|
|
576
|
+
|
|
577
|
+
initializer 'spree.deprecated_aliases.skip_eager_load' do
|
|
578
|
+
Rails.autoloaders.main.do_not_eager_load(*DEPRECATED_ALIAS_FILES.map { |path| root.join(path) })
|
|
579
|
+
end
|
|
580
|
+
|
|
562
581
|
# filter sensitive information during logging
|
|
563
582
|
initializer 'spree.params.filter' do |app|
|
|
564
583
|
app.config.filter_parameters += [
|
|
@@ -572,6 +591,13 @@ module Spree
|
|
|
572
591
|
]
|
|
573
592
|
end
|
|
574
593
|
|
|
594
|
+
# Records which store owns every uploaded file, and reports attaching a
|
|
595
|
+
# file to another store's record (docs/plans/6.0-uploads-and-file-ownership.md).
|
|
596
|
+
initializer 'spree.active_storage.store_ownership' do
|
|
597
|
+
ActiveSupport.on_load(:active_storage_blob) { include Spree::Uploads::BlobStoreOwnership }
|
|
598
|
+
ActiveSupport.on_load(:active_storage_attachment) { include Spree::Uploads::AttachmentStoreCheck }
|
|
599
|
+
end
|
|
600
|
+
|
|
575
601
|
initializer 'spree.core.checking_migrations' do |app|
|
|
576
602
|
app.config.after_initialize do
|
|
577
603
|
Migrations.new(config, engine_name).check unless Rails.env.test? || Spree::Config.disable_migration_check
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Preferences
|
|
3
|
+
# Raised when a `preferences` payload does not match the schema its type
|
|
4
|
+
# declares. Each failure names the value by a JSON pointer into the
|
|
5
|
+
# payload, so a client can show it next to the field it belongs to.
|
|
6
|
+
class InvalidPreferences < StandardError
|
|
7
|
+
# @return [Array<Hash{Symbol => String}>] `{ pointer:, message: }` per failure
|
|
8
|
+
attr_reader :failures
|
|
9
|
+
|
|
10
|
+
# @param failures [Array<Hash{Symbol => String}>] pointers relative to the preferences object
|
|
11
|
+
# @param prefix [String] where the preferences object sits in the request
|
|
12
|
+
def initialize(failures, prefix: '/preferences')
|
|
13
|
+
@failures = failures.map { |failure| failure.merge(pointer: "#{prefix}#{failure[:pointer]}") }
|
|
14
|
+
super(@failures.map { |failure| "#{failure[:pointer]}: #{failure[:message]}" }.join('; '))
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -60,8 +60,12 @@ module Spree
|
|
|
60
60
|
# @return [Array<String>]
|
|
61
61
|
def preference_tables
|
|
62
62
|
Rails.application.eager_load! if defined?(Rails.application) && Rails.application
|
|
63
|
+
# An anonymous class (a spec's throwaway model) has no name to derive a
|
|
64
|
+
# table from, and asking raises, so it is skipped.
|
|
63
65
|
owned = ActiveRecord::Base.descendants.filter_map do |model|
|
|
64
|
-
model.
|
|
66
|
+
next if model.name.nil? || model.abstract_class? || !model.include?(Spree::Preferences::Preferable)
|
|
67
|
+
|
|
68
|
+
model.table_name
|
|
65
69
|
end
|
|
66
70
|
|
|
67
71
|
connection.tables.select { |table| owned.include?(table) && connection.column_exists?(table, :preferences) }.sort
|
|
@@ -47,6 +47,25 @@ module Spree
|
|
|
47
47
|
value.is_a?(String) && value.start_with?(TOKEN)
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
# Money is written to at least the decimals of the currency the record
|
|
51
|
+
# names (a calculator's `currency` preference), keeping up to four like
|
|
52
|
+
# a unit price, so a per-item amount below a cent survives a round trip.
|
|
53
|
+
# A money preference with no currency of its own, such as an order-total
|
|
54
|
+
# rule that applies to any currency, keeps its exact decimal.
|
|
55
|
+
def self.wire_value(preferable, key, type, value)
|
|
56
|
+
case type
|
|
57
|
+
when :password then mask(value)
|
|
58
|
+
when :money
|
|
59
|
+
# Stored as the JSON text of a decimal.
|
|
60
|
+
amount = value.is_a?(String) ? BigDecimal(value, exception: false) : value
|
|
61
|
+
return value unless amount.is_a?(Numeric)
|
|
62
|
+
|
|
63
|
+
currency = preferable.money_preference_currency(key)
|
|
64
|
+
currency.present? ? Spree::Money::Rounding.format(amount, currency, unit_price: true) : Spree::Money::Rounding.format_decimal(amount)
|
|
65
|
+
else value
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
50
69
|
# Serializes a Preferable's `preferences` hash for the wire,
|
|
51
70
|
# masking `:password` values. Keys are stringified to match the
|
|
52
71
|
# wire shape expected by JSON clients — schema entries built by
|
|
@@ -59,14 +78,45 @@ module Spree
|
|
|
59
78
|
def self.serialize(preferable)
|
|
60
79
|
return {} if preferable.nil?
|
|
61
80
|
|
|
81
|
+
definitions = preferable.class.preference_definitions
|
|
62
82
|
preferable.preference_schema.each_with_object({}) do |field, hash|
|
|
63
83
|
# The stored value only, never the default — an unset secret must not
|
|
64
84
|
# reveal what it would fall back to — read through the record,
|
|
65
85
|
# because a secret lives in its own column.
|
|
66
86
|
value = preferable.stored_preference(field[:key]) { nil }
|
|
67
|
-
|
|
87
|
+
definition = definitions.fetch(field[:key])
|
|
88
|
+
hash[field[:key_string] || field[:key].to_s] =
|
|
89
|
+
if definition[:of] == :id
|
|
90
|
+
prefixed_ids(value, definition)
|
|
91
|
+
elsif %i[password money].include?(field[:type])
|
|
92
|
+
wire_value(preferable, field[:key], field[:type], value)
|
|
93
|
+
elsif definition[:keys] == :currency && definition[:values] == :money
|
|
94
|
+
money_by_currency(preferable.wire_preference_value(value, definition))
|
|
95
|
+
else
|
|
96
|
+
preferable.wire_preference_value(value, definition)
|
|
97
|
+
end
|
|
68
98
|
end
|
|
69
99
|
end
|
|
100
|
+
|
|
101
|
+
# Amounts keyed by currency (`{ "EUR" => "12.5" }`), each written with
|
|
102
|
+
# the decimals of its own currency, as every money value is.
|
|
103
|
+
def self.money_by_currency(value)
|
|
104
|
+
return value unless value.is_a?(Hash)
|
|
105
|
+
|
|
106
|
+
value.to_h do |currency, amount|
|
|
107
|
+
decimal = BigDecimal(amount.to_s, exception: false)
|
|
108
|
+
[currency, decimal ? Spree::Money::Rounding.format(decimal, currency, unit_price: true) : amount]
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Ids are stored as raw primary keys and leave as prefixed ids, the form
|
|
113
|
+
# every write accepts.
|
|
114
|
+
def self.prefixed_ids(value, definition)
|
|
115
|
+
return value unless value.is_a?(Array)
|
|
116
|
+
|
|
117
|
+
model = Spree::Preferences::Preferable.preference_model(definition)
|
|
118
|
+
value.map { |id| Spree::PrefixedId.prefixed_id?(id.to_s) ? id : model.prefixed_id_for(id) }
|
|
119
|
+
end
|
|
70
120
|
end
|
|
71
121
|
end
|
|
72
122
|
end
|