spree_core 5.4.0.beta → 5.4.0.beta3

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.
Files changed (161) hide show
  1. checksums.yaml +4 -4
  2. data/app/finders/spree/products/find.rb +2 -42
  3. data/app/helpers/spree/base_helper.rb +1 -54
  4. data/app/mailers/spree/base_mailer.rb +4 -3
  5. data/app/models/concerns/spree/adjustment_source.rb +0 -8
  6. data/app/models/concerns/spree/admin_user_methods.rb +0 -2
  7. data/app/models/concerns/spree/image_methods.rb +4 -0
  8. data/app/models/concerns/spree/metadata.rb +10 -0
  9. data/app/models/concerns/spree/product_scopes.rb +21 -49
  10. data/app/models/concerns/spree/stores/markets.rb +7 -7
  11. data/app/models/concerns/spree/vat_price_calculation.rb +2 -2
  12. data/app/models/spree/ability.rb +5 -5
  13. data/app/models/spree/address.rb +3 -2
  14. data/app/models/spree/adjustable/promotion_accumulator.rb +1 -1
  15. data/app/models/spree/adjustment.rb +1 -20
  16. data/app/models/spree/api_key.rb +60 -2
  17. data/app/models/spree/country.rb +7 -3
  18. data/app/models/spree/credit_card.rb +0 -27
  19. data/app/models/spree/current.rb +38 -2
  20. data/app/models/spree/exports/products.rb +0 -6
  21. data/app/models/spree/gateway/bogus.rb +9 -9
  22. data/app/models/spree/gateway.rb +0 -3
  23. data/app/models/spree/gift_card_batch.rb +4 -0
  24. data/app/models/spree/image/configuration/active_storage.rb +0 -2
  25. data/app/models/spree/image.rb +7 -31
  26. data/app/models/spree/log_entry.rb +8 -5
  27. data/app/models/spree/market.rb +1 -2
  28. data/app/models/spree/market_country.rb +17 -0
  29. data/app/models/spree/newsletter_subscriber.rb +0 -3
  30. data/app/models/spree/order/checkout.rb +0 -18
  31. data/app/models/spree/order.rb +22 -16
  32. data/app/models/spree/payment/gateway_options.rb +2 -2
  33. data/app/models/spree/payment/processing.rb +5 -5
  34. data/app/models/spree/payment.rb +1 -1
  35. data/app/models/spree/payment_connection_error.rb +3 -0
  36. data/app/models/spree/payment_method/check.rb +1 -1
  37. data/app/models/spree/payment_method/store_credit.rb +5 -5
  38. data/app/models/spree/payment_response.rb +70 -0
  39. data/app/models/spree/permission_sets/default_customer.rb +0 -5
  40. data/app/models/spree/permission_sets/product_display.rb +0 -2
  41. data/app/models/spree/permission_sets/product_management.rb +0 -2
  42. data/app/models/spree/product.rb +5 -85
  43. data/app/models/spree/promotion.rb +2 -14
  44. data/app/models/spree/promotion_handler/coupon.rb +3 -3
  45. data/app/models/spree/prototype.rb +0 -3
  46. data/app/models/spree/refund.rb +6 -2
  47. data/app/models/spree/reimbursement.rb +0 -2
  48. data/app/models/spree/shipment.rb +0 -1
  49. data/app/models/spree/shipment_handler.rb +0 -2
  50. data/app/models/spree/shipping_category.rb +3 -3
  51. data/app/models/spree/store.rb +10 -49
  52. data/app/models/spree/store_credit.rb +4 -0
  53. data/app/models/spree/tax_category.rb +1 -11
  54. data/app/models/spree/taxon.rb +0 -16
  55. data/app/models/spree/variant.rb +2 -40
  56. data/app/models/spree/zone.rb +1 -4
  57. data/app/services/spree/cart/add_item.rb +8 -4
  58. data/app/services/spree/cart/create.rb +13 -2
  59. data/app/services/spree/newsletter/subscribe.rb +2 -2
  60. data/app/services/spree/products/duplicator.rb +0 -12
  61. data/app/services/spree/products/prepare_nested_attributes.rb +0 -12
  62. data/app/subscribers/spree/invitation_email_subscriber.rb +1 -1
  63. data/config/locales/en.yml +4 -20
  64. data/db/migrate/20210914000000_spree_four_three.rb +0 -38
  65. data/db/migrate/20210915064329_add_metadata_to_spree_multiple_tables.rb +0 -1
  66. data/db/migrate/20260226000000_add_locale_to_spree_orders.rb +5 -0
  67. data/db/migrate/20260226100000_add_token_digest_to_spree_api_keys.rb +21 -0
  68. data/lib/spree/core/configuration.rb +0 -3
  69. data/lib/spree/core/controller_helpers/strong_parameters.rb +1 -2
  70. data/lib/spree/core/dependencies.rb +2 -10
  71. data/lib/spree/core/engine.rb +0 -13
  72. data/lib/spree/core/pricing/resolver.rb +1 -9
  73. data/lib/spree/core/version.rb +1 -1
  74. data/lib/spree/core.rb +10 -26
  75. data/lib/spree/permitted_attributes.rb +2 -14
  76. data/lib/spree/testing_support/factories/order_factory.rb +1 -0
  77. data/lib/spree/testing_support/factories/payment_method_factory.rb +1 -6
  78. data/lib/spree/testing_support/factories/product_factory.rb +0 -7
  79. data/lib/spree/testing_support/factories/prototype_factory.rb +0 -2
  80. data/lib/tasks/cli.rake +50 -0
  81. data/lib/tasks/core.rake +0 -265
  82. metadata +9 -111
  83. data/app/finders/spree/posts/find.rb +0 -137
  84. data/app/finders/spree/product_properties/find_available.rb +0 -20
  85. data/app/helpers/spree/mail_helper.rb +0 -27
  86. data/app/jobs/spree/api_key_touch_job.rb +0 -9
  87. data/app/mailers/spree/test_mailer.rb +0 -8
  88. data/app/models/action_text/video_embed.rb +0 -13
  89. data/app/models/concerns/spree/filter_param.rb +0 -21
  90. data/app/models/concerns/spree/has_one_link.rb +0 -42
  91. data/app/models/concerns/spree/linkable.rb +0 -9
  92. data/app/models/concerns/spree/previewable.rb +0 -17
  93. data/app/models/spree/data_feed/google.rb +0 -15
  94. data/app/models/spree/data_feed.rb +0 -42
  95. data/app/models/spree/gateway/bogus_simple.rb +0 -24
  96. data/app/models/spree/newsletter_subscriber/emails.rb +0 -12
  97. data/app/models/spree/order/emails.rb +0 -24
  98. data/app/models/spree/post.rb +0 -108
  99. data/app/models/spree/post_category.rb +0 -46
  100. data/app/models/spree/product_property.rb +0 -51
  101. data/app/models/spree/property.rb +0 -86
  102. data/app/models/spree/property_prototype.rb +0 -9
  103. data/app/models/spree/reimbursement/emails.rb +0 -11
  104. data/app/models/spree/shipment/emails.rb +0 -11
  105. data/app/models/spree/store_favicon_image.rb +0 -20
  106. data/app/models/spree/store_logo.rb +0 -4
  107. data/app/models/spree/store_mailer_logo.rb +0 -7
  108. data/app/models/spree/taxon_image/configuration/active_storage.rb +0 -26
  109. data/app/models/spree/taxon_image.rb +0 -28
  110. data/app/presenters/spree/filters/properties_presenter.rb +0 -23
  111. data/app/presenters/spree/filters/property_presenter.rb +0 -42
  112. data/app/services/spree/data_feeds/google/optional_attributes.rb +0 -23
  113. data/app/services/spree/data_feeds/google/optional_sub_attributes.rb +0 -21
  114. data/app/services/spree/data_feeds/google/products_list.rb +0 -14
  115. data/app/services/spree/data_feeds/google/required_attributes.rb +0 -68
  116. data/app/services/spree/data_feeds/google/rss.rb +0 -109
  117. data/app/sorters/spree/posts/sort.rb +0 -40
  118. data/app/views/action_text/video_embeds/_thumbnail.html.erb +0 -1
  119. data/app/views/action_text/video_embeds/_video_embed.html.erb +0 -3
  120. data/app/views/layouts/action_text/contents/_content.html.erb +0 -3
  121. data/app/views/spree/test_mailer/test_email.html.erb +0 -40
  122. data/app/views/spree/test_mailer/test_email.text.erb +0 -4
  123. data/config/initializers/oembed.rb +0 -1
  124. data/db/migrate/20221229132350_create_spree_data_feed_settings.rb +0 -14
  125. data/db/migrate/20230109084253_create_product_property_translations.rb +0 -24
  126. data/db/migrate/20230109105943_create_property_translations.rb +0 -24
  127. data/db/migrate/20230415155958_rename_data_feed_settings_table.rb +0 -5
  128. data/db/migrate/20230415160828_rename_data_feed_table_columns.rb +0 -7
  129. data/db/migrate/20230415161226_add_indexes_to_data_feeds_table.rb +0 -5
  130. data/db/migrate/20230512094803_rename_data_feeds_column_provider_to_type.rb +0 -5
  131. data/db/migrate/20240914153106_add_display_on_to_spree_properties.rb +0 -5
  132. data/db/migrate/20240915144935_add_position_to_spree_properties.rb +0 -6
  133. data/db/migrate/20250121160028_create_spree_posts_and_spree_post_categories.rb +0 -33
  134. data/db/migrate/20250127083740_add_kind_to_spree_properties.rb +0 -5
  135. data/db/migrate/20250217171018_create_action_text_video_embeds.rb +0 -11
  136. data/db/migrate/20250305121657_remove_spree_posts_indices.rb +0 -7
  137. data/db/migrate/20250730154601_add_unique_index_on_spree_properties_name.rb +0 -24
  138. data/db/sample_data/posts.rb +0 -7
  139. data/lib/generators/spree/cursor_rules/cursor_rules_generator.rb +0 -19
  140. data/lib/generators/spree/cursor_rules/templates/spree_rules.mdc +0 -387
  141. data/lib/spree/core/controller_helpers/search.rb +0 -17
  142. data/lib/spree/core/importer/order.rb +0 -244
  143. data/lib/spree/core/importer/product.rb +0 -67
  144. data/lib/spree/core/importer.rb +0 -9
  145. data/lib/spree/core/product_filters.rb +0 -218
  146. data/lib/spree/core/query_filters/comparable.rb +0 -50
  147. data/lib/spree/core/query_filters/date.rb +0 -8
  148. data/lib/spree/core/query_filters/number.rb +0 -8
  149. data/lib/spree/core/query_filters/text.rb +0 -36
  150. data/lib/spree/core/query_filters.rb +0 -11
  151. data/lib/spree/core/search/base.rb +0 -144
  152. data/lib/spree/testing_support/factories/favicon_image_factory.rb +0 -9
  153. data/lib/spree/testing_support/factories/google_data_feed_factory.rb +0 -7
  154. data/lib/spree/testing_support/factories/post_category_factory.rb +0 -7
  155. data/lib/spree/testing_support/factories/post_factory.rb +0 -22
  156. data/lib/spree/testing_support/factories/product_property_factory.rb +0 -7
  157. data/lib/spree/testing_support/factories/property_factory.rb +0 -28
  158. data/lib/spree/testing_support/factories/taxon_image_factory.rb +0 -9
  159. data/lib/spree/testing_support/flash.rb +0 -25
  160. data/lib/spree/testing_support/flatpickr_capybara.rb +0 -124
  161. data/lib/tasks/exchanges.rake +0 -66
@@ -1,387 +0,0 @@
1
- ---
2
- alwaysApply: true
3
- ---
4
-
5
- # Cursor Rules for Spree Commerce Development
6
-
7
- ## General Development Guidelines
8
-
9
- ### Framework & Architecture
10
-
11
- - Spree is built on Ruby on Rails and follows MVC architecture
12
- - All Spree code must be namespaced under `Spree::` module
13
- - Spree is distributed as Rails engines with separate gems (core, admin, api, storefront, emails, etc.)
14
- - Follow Rails conventions and the Rails Security Guide
15
- - Prefer Rails idioms and standard patterns over custom solutions
16
-
17
- ### Code Organization
18
-
19
- - Place all models in `app/models/spree/` directory
20
- - Place all controllers in `app/controllers/spree/` directory
21
- - Place all views in `app/views/spree/` directory
22
- - Place all services in `app/services/spree/` directory
23
- - Place all mailers in `app/mailers/spree/` directory
24
- - Place all API serializers in `app/serializers/spree/` directory
25
- - Place all helpers in `app/helpers/spree/` directory
26
- - Place all jobs in `app/jobs/spree/` directory
27
- - Place all presenters in `app/presenters/spree/` directory
28
- - Use consistent file naming: `spree/product.rb` for `Spree::Product` class
29
- - Group related functionality into concerns when appropriate
30
- - Do not call `Spree::User` directly, use `Spree.user_class` instead
31
- - Do not call `Spree::AdminUser` directly, use `Spree.admin_user_class` instead
32
-
33
- ## Naming Conventions & Structure
34
-
35
- ### Classes & Modules
36
-
37
- ```ruby
38
- # ✅ Correct naming
39
- module Spree
40
- class Product < Spree.base_class
41
- end
42
- end
43
-
44
- module Spree
45
- module Admin
46
- class ProductsController < ResourceController
47
- end
48
- end
49
- end
50
-
51
- # ❌ Incorrect - missing namespace
52
- class Product < ApplicationRecord
53
- end
54
- ```
55
-
56
- Always inherit from `Spree.base_class` when creating models.
57
-
58
- ### File Paths
59
-
60
- - Models: `app/models/spree/product.rb`
61
- - Controllers: `app/controllers/spree/admin/products_controller.rb`
62
- - Views: `app/views/spree/admin/products/`
63
- - Decorators: `app/models/spree/product_decorator.rb`
64
-
65
- ## Model Development
66
-
67
- ### Model Patterns
68
-
69
- - Use ActiveRecord associations appropriately, always pass `class_name` and `dependent` options
70
- - Implement concerns for shared functionality
71
- - Use scopes for reusable query patterns
72
- - Include `Spree::Metafields` concern for models that need metadata support
73
-
74
- ```ruby
75
- # ✅ Good model structure
76
- class Spree::Product < Spree.base_class
77
- include Spree::Metafields
78
-
79
- has_many :variants, class_name: 'Spree::Variant', dependent: :destroy
80
- has_many :product_properties, class_name: 'Spree::ProductProperty', dependent: :destroy
81
- has_many :properties, through: :product_properties, source: :property
82
-
83
- scope :available, -> { where(available_on: ..Time.current) }
84
-
85
- validates :name, presence: true
86
- validates :slug, presence: true, uniqueness: { scope: spree_base_uniqueness_scope }
87
- end
88
- ```
89
-
90
- For uniqueness validation, always use `scope: spree_base_uniqueness_scope`
91
-
92
- ## Controller Development
93
-
94
- ### Controller Inheritance
95
-
96
- - Admin controllers inherit from `Spree::Admin::ResourceController` which handles most of CRUD operations
97
- - API controllers inherit from `Spree::Api::V2::BaseController`
98
- - Storefront controllers inherit from `Spree::StoreController`
99
-
100
- ### Parameter Handling
101
-
102
- - Always use strong parameters
103
-
104
- ```ruby
105
- # ✅ Proper parameter handling
106
- def permitted_product_params
107
- params.require(:product).permit(:name, :description, :price)
108
- end
109
- ```
110
-
111
- ## Customization & Extensions
112
-
113
- ### Spree::Dependencies System (Preferred Method)
114
-
115
- Dependencies allow you to replace parts of Spree core with custom implementations. This is the preferred method for customization.
116
-
117
- #### Global Customization
118
-
119
- In `config/initializers/spree.rb`:
120
-
121
- ```ruby
122
- # Single service replacement
123
- Spree::Dependencies.cart_add_item_service = 'MyAddToCartService'
124
-
125
- # Or using block syntax
126
- Spree.dependencies do |dependencies|
127
- dependencies.cart_add_item_service = 'MyAddToCartService'
128
- dependencies.checkout_complete_service = 'MyCheckoutCompleteService'
129
- end
130
- ```
131
-
132
- #### API Level Customization
133
-
134
- ```ruby
135
- # Storefront API specific
136
- Spree::Api::Dependencies.storefront_cart_serializer = 'MyCartSerializer'
137
- Spree::Api::Dependencies.storefront_cart_add_item_service = 'MyAddToCartService'
138
-
139
- # Platform API specific
140
- Spree::Api::Dependencies.platform_product_serializer = 'MyProductSerializer'
141
- ```
142
-
143
- #### Service Implementation
144
-
145
- ```ruby
146
- # ✅ Proper service inheritance
147
- class MyAddToCartService < Spree::Cart::AddItem
148
- def call(order:, variant:, quantity: nil, public_metadata: {}, private_metadata: {}, options: {})
149
- ApplicationRecord.transaction do
150
- run :add_to_line_item
151
- run Spree.cart_recalculate_service
152
- run :update_external_system
153
- end
154
- end
155
-
156
- private
157
-
158
- def update_external_system(result)
159
- # Custom logic here
160
- end
161
- end
162
- ```
163
-
164
- #### Available Injection Points
165
-
166
- Common dependencies you can override:
167
- - Cart services: `cart_add_item_service`, `cart_update_service`, `cart_remove_item_service`
168
- - Checkout services: `checkout_next_service`, `checkout_complete_service`
169
- - Order services: `order_approve_service`, `order_cancel_service`
170
- - Payment services: `payment_create_service`, `payment_process_service`
171
- - Ability classes: `ability_class`
172
- - Serializers: Various API serializers for different endpoints
173
-
174
- ### Decorators (Use Sparingly)
175
-
176
- Decorators should be a last resort - they make upgrades difficult. Use `Module.prepend` pattern for decorators.
177
-
178
- #### Model Decorators
179
-
180
- ```ruby
181
- # ✅ Proper decorator structure
182
- module Spree
183
- module ProductDecorator
184
- def self.prepended(base)
185
- base.has_many :videos, class_name: 'Spree::Video', dependent: :destroy
186
- base.before_validation :strip_whitespaces
187
- end
188
-
189
- def custom_name
190
- name.upcase
191
- end
192
-
193
- private
194
-
195
- def strip_whitespaces
196
- self.name = name.strip if name.present?
197
- end
198
- end
199
-
200
- Product.prepend(ProductDecorator)
201
- end
202
- ```
203
-
204
- #### Controller Decorators
205
-
206
- ```ruby
207
- # ✅ Controller decorator with dependency injection
208
- module Spree
209
- module Admin
210
- module ProductsControllerDecorator
211
- def self.prepended(base)
212
- base.before_action :load_custom_data, only: [:show, :edit]
213
- end
214
-
215
- def custom_action
216
- # Custom action implementation
217
- end
218
-
219
- private
220
-
221
- def load_custom_data
222
- @custom_data = fetch_custom_data
223
- end
224
- end
225
-
226
- ProductsController.prepend(ProductsControllerDecorator)
227
- end
228
- end
229
- ```
230
-
231
- ### View Customization
232
-
233
- #### Admin Panel Injection Points
234
-
235
- Use partial injection for admin customization:
236
-
237
- ```ruby
238
- # In config/initializers/spree.rb
239
- Rails.application.config.spree_admin.head_partials << 'spree/admin/shared/custom_head'
240
- Rails.application.config.spree_admin.body_end_partials << 'spree/admin/shared/custom_footer'
241
- ```
242
-
243
- Available injection points:
244
- - `head_partials` - Injects into `<head>` tag
245
- - `body_start_partials` - Injects at start of `<body>`
246
- - `body_end_partials` - Injects at end of `<body>`
247
-
248
- #### Storefront Themes
249
-
250
- Create custom themes for storefront customization:
251
-
252
- ```bash
253
- bin/rails g spree:storefront:theme MyTheme
254
- ```
255
-
256
- ```ruby
257
- # Register theme in config/initializers/spree.rb
258
- Spree.page_builder.themes << Spree::Themes::MyTheme
259
- ```
260
-
261
- #### View Overrides
262
-
263
- Override specific views by creating files in your app:
264
-
265
- ```
266
- app/views/themes/my_theme/spree/products/index.html.erb
267
- ```
268
-
269
- Do not override views for admin, only for storefront. Avoid overriding any views for Checkout or Cart.
270
-
271
- ### Authentication Integration
272
-
273
- ```ruby
274
- # In config/initializers/spree.rb
275
- Spree.user_class = 'User'
276
- Spree.admin_user_class = 'AdminUser'
277
-
278
- # Custom authentication
279
- Rails.application.config.to_prepare do
280
- Spree::ApplicationController.include MyAuthenticationModule
281
- end
282
- ```
283
-
284
- ## Testing Guidelines
285
-
286
- ### Test Structure
287
-
288
- - Use RSpec for testing
289
- - Place specs in `spec/` directory following Rails conventions
290
- - Use Spree's testing helpers and factories
291
-
292
- ```ruby
293
- # ✅ Proper test structure
294
- require 'spec_helper'
295
-
296
- RSpec.describe Spree::Product, type: :model do
297
- let(:product) { create(:product) }
298
-
299
- describe '#available?' do
300
- it 'returns true when product is available' do
301
- expect(product.available?).to be true
302
- end
303
- end
304
- end
305
- ```
306
-
307
- ### Factory Usage
308
-
309
- ```ruby
310
- # Use Spree factories
311
- create(:product, name: 'Test Product')
312
- create(:order_with_line_items)
313
- create(:user)
314
- ```
315
-
316
- ## Performance & Security
317
-
318
- ### Database Queries
319
-
320
- - Use includes/joins to avoid N+1 queries
321
- - Add database indexes for frequently queried fields
322
- - Use counter caches for associations that are counted frequently
323
-
324
- ### Security
325
-
326
- - Always use strong parameters in controllers
327
- - Sanitize user input
328
- - Use Spree's built-in authorization system (CanCanCan)
329
- - Validate file uploads and restrict file types
330
-
331
- ## Common Patterns
332
-
333
- ### Service Objects
334
-
335
- ```ruby
336
- # ✅ Spree service pattern
337
- module Spree
338
- class MyCustomService
339
- prepend Spree::ServiceModule::Base
340
-
341
- def call(param1:, param2: nil)
342
- # Service logic here
343
- success(result_data)
344
- rescue StandardError => e
345
- failure(e.message)
346
- end
347
- end
348
- end
349
- ```
350
-
351
- ### API Development
352
-
353
- ```ruby
354
- # ✅ Custom API endpoint
355
- module Spree
356
- module Api
357
- module V2
358
- class CustomController < Spree::Api::V2::BaseController
359
- def index
360
- render json: serialized_collection
361
- end
362
-
363
- private
364
-
365
- def serialized_collection
366
- Spree.api.storefront_product_serializer.new(
367
- collection,
368
- include: resource_includes,
369
- fields: sparse_fields
370
- ).serializable_hash
371
- end
372
- end
373
- end
374
- end
375
- end
376
- ```
377
-
378
- ## Avoid These Patterns
379
-
380
- ❌ Direct inheritance from Rails classes without Spree namespace
381
- ❌ Monkey patching without using decorators or dependencies
382
- ❌ Hard-coding configuration values
383
- ❌ Direct SQL queries without using ActiveRecord
384
- ❌ Creating models outside Spree namespace when extending core functionality
385
- ❌ Using class_eval decorators (use Module.prepend instead)
386
- ❌ Overriding entire view files when partial injection would work
387
- ❌ Modifying core Spree files directly
@@ -1,17 +0,0 @@
1
- module Spree
2
- module Core
3
- module ControllerHelpers
4
- module Search
5
- def build_searcher(params)
6
- Spree::Deprecation.warn("Spree::Core::ControllerHelpers::Search is deprecated and will be removed in Spree 5.5.")
7
-
8
- Spree.searcher_class.new(params).tap do |searcher|
9
- searcher.current_user = try_spree_current_user
10
- searcher.current_currency = current_currency&.upcase
11
- searcher.current_store = current_store
12
- end
13
- end
14
- end
15
- end
16
- end
17
- end
@@ -1,244 +0,0 @@
1
- module Spree
2
- module Core
3
- module Importer
4
- class Order
5
- def self.import(user, params)
6
- Spree::Deprecation.warn('Spree::Core::Importer::Order is deprecated and will be removed in Spree 5.5. Please use `Spree::Imports::Order` instead.')
7
-
8
- ensure_country_id_from_params params[:ship_address_attributes]
9
- ensure_state_id_from_params params[:ship_address_attributes]
10
- ensure_country_id_from_params params[:bill_address_attributes]
11
- ensure_state_id_from_params params[:bill_address_attributes]
12
-
13
- create_params = params.slice :currency
14
- order = Spree::Order.create! create_params
15
- order.associate_user!(user)
16
-
17
- shipments_attrs = params.delete(:shipments_attributes)
18
-
19
- create_line_items_from_params(params.delete(:line_items_attributes), order)
20
- create_shipments_from_params(shipments_attrs, order)
21
- create_adjustments_from_params(params.delete(:adjustments_attributes), order)
22
- create_payments_from_params(params.delete(:payments_attributes), order)
23
-
24
- if completed_at = params.delete(:completed_at)
25
- order.completed_at = completed_at
26
- order.state = 'complete'
27
- end
28
-
29
- params.delete(:user_id) unless user.try(:has_spree_role?, 'admin') && params.key?(:user_id)
30
-
31
- order.update!(params)
32
-
33
- order.create_proposed_shipments unless shipments_attrs.present?
34
-
35
- # Really ensure that the order totals & states are correct
36
- order.updater.update
37
- if shipments_attrs.present?
38
- order.shipments.each_with_index do |shipment, index|
39
- shipment.update_columns(cost: shipments_attrs[index][:cost].to_f) if shipments_attrs[index][:cost].present?
40
- end
41
- end
42
- order.reload
43
- rescue StandardError => e
44
- order.destroy if order&.persisted?
45
- raise e.message
46
- end
47
-
48
- def self.create_shipments_from_params(shipments_hash, order)
49
- return [] unless shipments_hash
50
-
51
- shipments_hash.each do |s|
52
- shipment = order.shipments.build
53
- shipment.tracking = s[:tracking]
54
- shipment.stock_location = Spree::StockLocation.find_by(admin_name: s[:stock_location]) ||
55
- Spree::StockLocation.find_by!(name: s[:stock_location])
56
- inventory_units = create_inventory_units_from_order_and_params(order, s[:inventory_units])
57
-
58
- inventory_units.each do |inventory_unit|
59
- inventory_unit.shipment = shipment
60
-
61
- if s[:shipped_at].present?
62
- inventory_unit.pending = false
63
- inventory_unit.state = 'shipped'
64
- end
65
-
66
- inventory_unit.save!
67
- end
68
-
69
- if s[:shipped_at].present?
70
- shipment.shipped_at = s[:shipped_at]
71
- shipment.state = 'shipped'
72
- end
73
-
74
- shipment.save!
75
-
76
- shipping_method = Spree::ShippingMethod.find_by(name: s[:shipping_method]) ||
77
- Spree::ShippingMethod.find_by!(admin_name: s[:shipping_method])
78
- rate = shipment.shipping_rates.create!(shipping_method: shipping_method, cost: s[:cost])
79
-
80
- shipment.selected_shipping_rate_id = rate.id
81
- shipment.update_amounts
82
-
83
- adjustments = s.delete(:adjustments_attributes)
84
- create_adjustments_from_params(adjustments, order, shipment)
85
- rescue StandardError => e
86
- raise "Order import shipments: #{e.message} #{s}"
87
- end
88
- end
89
-
90
- def self.create_inventory_units_from_order_and_params(order, inventory_unit_params)
91
- inventory_unit_params.each_with_object([]) do |inventory_unit_param, inventory_units|
92
- ensure_variant_id_from_params(inventory_unit_param)
93
- existing = inventory_units.detect { |unit| unit.variant_id == inventory_unit_param[:variant_id] }
94
- if existing
95
- existing.quantity += 1
96
- else
97
- line_item = order.line_items.detect { |ln| ln.variant_id == inventory_unit_param[:variant_id] }
98
- inventory_units << InventoryUnit.new(line_item: line_item, order_id: order.id, variant: line_item.variant, quantity: 1)
99
- end
100
- end
101
- end
102
-
103
- def self.create_line_items_from_params(line_items, order)
104
- return {} unless line_items
105
-
106
- line_items.each do |line_item|
107
- adjustments = line_item.delete(:adjustments_attributes)
108
- extra_params = line_item.except(:variant_id, :quantity, :sku)
109
- line_item = ensure_variant_id_from_params(line_item)
110
- variant = Spree::Variant.find(line_item[:variant_id])
111
- line_item = Cart::AddItem.call(order: order, variant: variant, quantity: line_item[:quantity]).value
112
- # Raise any errors with saving to prevent import succeeding with line items
113
- # failing silently.
114
- if extra_params.present?
115
- line_item.update!(extra_params)
116
- else
117
- line_item.save!
118
- end
119
- create_adjustments_from_params(adjustments, order, line_item)
120
- rescue StandardError => e
121
- raise "Order import line items: #{e.message} #{line_item}"
122
- end
123
- end
124
-
125
- def self.create_adjustments_from_params(adjustments, order, adjustable = nil)
126
- return [] unless adjustments
127
-
128
- adjustments.each do |a|
129
- adjustment = (adjustable || order).adjustments.build(
130
- order: order,
131
- amount: a[:amount].to_f,
132
- label: a[:label],
133
- source_type: source_type_from_adjustment(a)
134
- )
135
- adjustment.save!
136
- adjustment.close!
137
- rescue StandardError => e
138
- raise "Order import adjustments: #{e.message} #{a}"
139
- end
140
- end
141
-
142
- def self.create_payments_from_params(payments_hash, order)
143
- return [] unless payments_hash
144
-
145
- payments_hash.each do |p|
146
- payment = order.payments.build order: order
147
- payment.amount = p[:amount].to_f
148
- # Order API should be using state as that's the normal payment field.
149
- # spree_wombat serializes payment state as status so imported orders should fall back to status field.
150
- payment.state = p[:state] || p[:status] || 'completed'
151
- payment.created_at = p[:created_at] if p[:created_at]
152
- payment.payment_method = Spree::PaymentMethod.find_by!(name: p[:payment_method])
153
- payment.source = create_source_payment_from_params(p[:source], payment) if p[:source]
154
- payment.save!
155
- rescue StandardError => e
156
- raise "Order import payments: #{e.message} #{p}"
157
- end
158
- end
159
-
160
- def self.create_source_payment_from_params(source_hash, payment)
161
- Spree::CreditCard.create(
162
- month: source_hash[:month],
163
- year: source_hash[:year],
164
- cc_type: source_hash[:cc_type],
165
- last_digits: source_hash[:last_digits],
166
- name: source_hash[:name],
167
- payment_method: payment.payment_method,
168
- gateway_customer_profile_id: source_hash[:gateway_customer_profile_id],
169
- gateway_payment_profile_id: source_hash[:gateway_payment_profile_id],
170
- imported: true
171
- )
172
- rescue StandardError => e
173
- raise "Order import source payments: #{e.message} #{source_hash}"
174
- end
175
-
176
- def self.ensure_variant_id_from_params(hash)
177
- sku = hash.delete(:sku)
178
- unless hash[:variant_id].present?
179
- hash[:variant_id] = Spree::Variant.active.find_by!(sku: sku).id
180
- end
181
- hash
182
- rescue ActiveRecord::RecordNotFound => e
183
- raise "Ensure order import variant: Variant w/SKU #{sku} not found."
184
- rescue StandardError => e
185
- raise "Ensure order import variant: #{e.message} #{hash}"
186
- end
187
-
188
- def self.ensure_country_id_from_params(address)
189
- return if address.nil? || address[:country_id].present? || address[:country].nil?
190
-
191
- begin
192
- search = {}
193
- if name = address[:country]['name']
194
- search[:name] = name
195
- elsif iso_name = address[:country]['iso_name']
196
- search[:iso_name] = iso_name.upcase
197
- elsif iso = address[:country]['iso']
198
- search[:iso] = iso.upcase
199
- elsif iso3 = address[:country]['iso3']
200
- search[:iso3] = iso3.upcase
201
- end
202
-
203
- address.delete(:country)
204
- address[:country_id] = Spree::Country.where(search).first!.id
205
- rescue StandardError => e
206
- raise "Ensure order import address country: #{e.message} #{search}"
207
- end
208
- end
209
-
210
- def self.ensure_state_id_from_params(address)
211
- return if address.nil? || address[:state_id].present? || address[:state].nil?
212
-
213
- begin
214
- search = {}
215
- if name = address[:state]['name']
216
- search[:name] = name
217
- elsif abbr = address[:state]['abbr']
218
- search[:abbr] = abbr.upcase
219
- end
220
-
221
- address.delete(:state)
222
- search[:country_id] = address[:country_id]
223
-
224
- if state = Spree::State.where(search).first
225
- address[:state_id] = state.id
226
- else
227
- address[:state_name] = search[:name] || search[:abbr]
228
- end
229
- rescue StandardError => e
230
- raise "Ensure order import address state: #{e.message} #{search}"
231
- end
232
- end
233
-
234
- def self.source_type_from_adjustment(adjustment)
235
- if adjustment[:tax]
236
- 'Spree::TaxRate'
237
- elsif adjustment[:promotion]
238
- 'Spree::PromotionAction'
239
- end
240
- end
241
- end
242
- end
243
- end
244
- end
@@ -1,67 +0,0 @@
1
- module Spree
2
- module Core
3
- module Importer
4
- class Product
5
- attr_reader :product, :product_attrs, :variants_attrs, :options_attrs, :store
6
-
7
- def initialize(product, product_params, options = {})
8
- Spree::Deprecation.warn('Spree::Core::Importer::Product is deprecated and will be removed in Spree 5.5. Please use `Spree::Imports::Product` instead.')
9
-
10
- @store = options[:store] || Spree::Store.default
11
- @product = product || Spree::Product.new(product_params)
12
- @product.stores << @store if @product.stores.exclude?(@store)
13
-
14
- @product_attrs = product_params.to_h
15
- @variants_attrs = (options[:variants_attrs] || []).map(&:to_h)
16
- @options_attrs = options[:options_attrs] || []
17
- end
18
-
19
- def create
20
- if product.save
21
- variants_attrs.each do |variant_attribute|
22
- # make sure the product is assigned before the options=
23
- product.variants.create({ product: product }.merge(variant_attribute))
24
- end
25
-
26
- set_up_options
27
- end
28
-
29
- product
30
- end
31
-
32
- def update
33
- if product.update(product_attrs)
34
- variants_attrs.each do |variant_attribute|
35
- # update the variant if the id is present in the payload
36
- if variant_attribute['id'].present?
37
- product.variants.find(variant_attribute['id']).update(variant_attribute)
38
- else
39
- # make sure the product is assigned before the options=
40
- product.variants.create({ product: product }.merge(variant_attribute))
41
- end
42
- end
43
-
44
- set_up_options
45
- end
46
-
47
- product
48
- end
49
-
50
- private
51
-
52
- def set_up_options
53
- options_attrs.each do |name|
54
- option_type = Spree::OptionType.where(name: name).first_or_initialize do |option_type|
55
- option_type.presentation = name
56
- option_type.save!
57
- end
58
-
59
- unless product.option_types.include?(option_type)
60
- product.option_types << option_type
61
- end
62
- end
63
- end
64
- end
65
- end
66
- end
67
- end