@spree/docs 0.1.0

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 (183) hide show
  1. package/README.md +54 -0
  2. package/dist/api-reference/platform/authentication.md +38 -0
  3. package/dist/api-reference/store-api/authentication.md +188 -0
  4. package/dist/api-reference/store-api/errors.md +277 -0
  5. package/dist/api-reference/store-api/idempotency.md +129 -0
  6. package/dist/api-reference/store-api/introduction.md +34 -0
  7. package/dist/api-reference/store-api/localization.md +279 -0
  8. package/dist/api-reference/store-api/metadata.md +160 -0
  9. package/dist/api-reference/store-api/monetary-amounts.md +65 -0
  10. package/dist/api-reference/store-api/querying.md +399 -0
  11. package/dist/api-reference/store-api/rate-limitting.md +103 -0
  12. package/dist/api-reference/store-api/relations.md +185 -0
  13. package/dist/api-reference/storefront/authentication.md +88 -0
  14. package/dist/api-reference/tutorials/adyen-integration-guide-for-android.md +165 -0
  15. package/dist/api-reference/tutorials/adyen-integration-guide-for-ios.md +194 -0
  16. package/dist/api-reference/tutorials/quick-checkout-with-stripe.md +248 -0
  17. package/dist/api-reference/v2/fetching-multiple-resources.md +26 -0
  18. package/dist/api-reference/v2/filtering-and-sorting.md +53 -0
  19. package/dist/api-reference/v2/introduction.md +22 -0
  20. package/dist/api-reference/v2/pagination.md +37 -0
  21. package/dist/api-reference/webhooks-events.md +883 -0
  22. package/dist/developer/admin/admin.md +205 -0
  23. package/dist/developer/admin/authentication.md +59 -0
  24. package/dist/developer/admin/components.md +711 -0
  25. package/dist/developer/admin/custom-css.md +243 -0
  26. package/dist/developer/admin/custom-javascript.md +116 -0
  27. package/dist/developer/admin/extending-ui.md +1964 -0
  28. package/dist/developer/admin/form-builder.md +444 -0
  29. package/dist/developer/admin/helper-methods.md +531 -0
  30. package/dist/developer/admin/navigation.md +805 -0
  31. package/dist/developer/admin/tables.md +491 -0
  32. package/dist/developer/advanced/adding_spree_to_rails_app.md +106 -0
  33. package/dist/developer/cli/quickstart.md +137 -0
  34. package/dist/developer/contributing/creating-an-extension.md +258 -0
  35. package/dist/developer/contributing/developing-spree.md +339 -0
  36. package/dist/developer/contributing/quickstart.md +32 -0
  37. package/dist/developer/contributing/updating-extensions.md +67 -0
  38. package/dist/developer/core-concepts/addresses.md +265 -0
  39. package/dist/developer/core-concepts/adjustments.md +107 -0
  40. package/dist/developer/core-concepts/architecture.md +177 -0
  41. package/dist/developer/core-concepts/calculators.md +323 -0
  42. package/dist/developer/core-concepts/customers.md +230 -0
  43. package/dist/developer/core-concepts/events.md +624 -0
  44. package/dist/developer/core-concepts/imports-exports.md +698 -0
  45. package/dist/developer/core-concepts/inventory.md +191 -0
  46. package/dist/developer/core-concepts/markets.md +250 -0
  47. package/dist/developer/core-concepts/media.md +167 -0
  48. package/dist/developer/core-concepts/metafields.md +187 -0
  49. package/dist/developer/core-concepts/orders.md +328 -0
  50. package/dist/developer/core-concepts/payments.md +710 -0
  51. package/dist/developer/core-concepts/pricing.md +163 -0
  52. package/dist/developer/core-concepts/products.md +360 -0
  53. package/dist/developer/core-concepts/promotions.md +322 -0
  54. package/dist/developer/core-concepts/reports.md +206 -0
  55. package/dist/developer/core-concepts/search-filtering.md +237 -0
  56. package/dist/developer/core-concepts/shipments.md +212 -0
  57. package/dist/developer/core-concepts/slugs.md +111 -0
  58. package/dist/developer/core-concepts/staff-roles.md +123 -0
  59. package/dist/developer/core-concepts/store-credits-gift-cards.md +317 -0
  60. package/dist/developer/core-concepts/stores.md +117 -0
  61. package/dist/developer/core-concepts/taxes.md +135 -0
  62. package/dist/developer/core-concepts/translations.md +120 -0
  63. package/dist/developer/core-concepts/users.md +299 -0
  64. package/dist/developer/core-concepts/webhooks.md +378 -0
  65. package/dist/developer/create-spree-app/quickstart.md +158 -0
  66. package/dist/developer/customization/api.md +93 -0
  67. package/dist/developer/customization/authentication.md +88 -0
  68. package/dist/developer/customization/checkout.md +204 -0
  69. package/dist/developer/customization/configuration.md +55 -0
  70. package/dist/developer/customization/decorators.md +523 -0
  71. package/dist/developer/customization/dependencies.md +232 -0
  72. package/dist/developer/customization/emails.md +21 -0
  73. package/dist/developer/customization/extensions.md +92 -0
  74. package/dist/developer/customization/metadata.md +236 -0
  75. package/dist/developer/customization/model-preferences.md +130 -0
  76. package/dist/developer/customization/permissions.md +265 -0
  77. package/dist/developer/customization/quickstart.md +229 -0
  78. package/dist/developer/customization/routes.md +24 -0
  79. package/dist/developer/customization/v4/admin-panel.md +78 -0
  80. package/dist/developer/customization/v4/authentication.md +210 -0
  81. package/dist/developer/customization/v4/checkout.md +212 -0
  82. package/dist/developer/customization/v4/deface.md +251 -0
  83. package/dist/developer/customization/v4/images.md +86 -0
  84. package/dist/developer/customization/v4/storefront.md +450 -0
  85. package/dist/developer/deployment/assets.md +87 -0
  86. package/dist/developer/deployment/aws.md +335 -0
  87. package/dist/developer/deployment/caching.md +27 -0
  88. package/dist/developer/deployment/cdn.md +39 -0
  89. package/dist/developer/deployment/database.md +155 -0
  90. package/dist/developer/deployment/docker.md +128 -0
  91. package/dist/developer/deployment/emails.md +77 -0
  92. package/dist/developer/deployment/environment_variables.md +111 -0
  93. package/dist/developer/deployment/heroku.md +51 -0
  94. package/dist/developer/deployment/render.md +95 -0
  95. package/dist/developer/getting-started/quickstart.md +82 -0
  96. package/dist/developer/how-to/custom-payment-method.md +374 -0
  97. package/dist/developer/how-to/custom-promotion.md +373 -0
  98. package/dist/developer/how-to/custom-report.md +387 -0
  99. package/dist/developer/how-to/custom-search-provider.md +230 -0
  100. package/dist/developer/multi-store/quickstart.md +71 -0
  101. package/dist/developer/multi-store/setup.md +38 -0
  102. package/dist/developer/multi-tenant/configuration.md +41 -0
  103. package/dist/developer/multi-tenant/core-concepts.md +75 -0
  104. package/dist/developer/multi-tenant/installation.md +96 -0
  105. package/dist/developer/multi-tenant/quickstart.md +20 -0
  106. package/dist/developer/multi-vendor/installation.md +45 -0
  107. package/dist/developer/multi-vendor/quickstart.md +17 -0
  108. package/dist/developer/sdk/admin/quickstart.md +22 -0
  109. package/dist/developer/sdk/authentication.md +89 -0
  110. package/dist/developer/sdk/configuration.md +225 -0
  111. package/dist/developer/sdk/quickstart.md +82 -0
  112. package/dist/developer/sdk/store/account.md +67 -0
  113. package/dist/developer/sdk/store/cart-checkout.md +140 -0
  114. package/dist/developer/sdk/store/markets.md +151 -0
  115. package/dist/developer/sdk/store/payments.md +96 -0
  116. package/dist/developer/sdk/store/products.md +149 -0
  117. package/dist/developer/sdk/store/wishlists.md +52 -0
  118. package/dist/developer/security/pci_compliance.md +15 -0
  119. package/dist/developer/security/security_policy.md +68 -0
  120. package/dist/developer/storefront/blocks.md +285 -0
  121. package/dist/developer/storefront/custom-css.md +260 -0
  122. package/dist/developer/storefront/custom-javascript.md +166 -0
  123. package/dist/developer/storefront/helper-methods.md +1288 -0
  124. package/dist/developer/storefront/links.md +298 -0
  125. package/dist/developer/storefront/nextjs/architecture.md +150 -0
  126. package/dist/developer/storefront/nextjs/customization.md +141 -0
  127. package/dist/developer/storefront/nextjs/deployment.md +180 -0
  128. package/dist/developer/storefront/nextjs/quickstart.md +92 -0
  129. package/dist/developer/storefront/nextjs/spree-next-package.md +314 -0
  130. package/dist/developer/storefront/pages.md +163 -0
  131. package/dist/developer/storefront/sections.md +569 -0
  132. package/dist/developer/storefront/storefront.md +56 -0
  133. package/dist/developer/storefront/themes.md +161 -0
  134. package/dist/developer/tutorial/admin.md +134 -0
  135. package/dist/developer/tutorial/extending-models.md +380 -0
  136. package/dist/developer/tutorial/file-uploads.md +121 -0
  137. package/dist/developer/tutorial/introduction.md +33 -0
  138. package/dist/developer/tutorial/model.md +41 -0
  139. package/dist/developer/tutorial/page-builder.md +487 -0
  140. package/dist/developer/tutorial/rich-text.md +73 -0
  141. package/dist/developer/tutorial/seo.md +332 -0
  142. package/dist/developer/tutorial/storefront.md +352 -0
  143. package/dist/developer/tutorial/testing.md +558 -0
  144. package/dist/developer/upgrades/2.0-to-2.1.md +46 -0
  145. package/dist/developer/upgrades/2.1-to-2.2.md +59 -0
  146. package/dist/developer/upgrades/2.2-to-2.3.md +44 -0
  147. package/dist/developer/upgrades/2.3-to-2.4.md +42 -0
  148. package/dist/developer/upgrades/3.0-to-3.1.md +47 -0
  149. package/dist/developer/upgrades/3.1-to-3.2.md +34 -0
  150. package/dist/developer/upgrades/3.2-to-3.3.md +70 -0
  151. package/dist/developer/upgrades/3.3-to-3.4.md +36 -0
  152. package/dist/developer/upgrades/3.4-to-3.5.md +44 -0
  153. package/dist/developer/upgrades/3.5-to-3.6.md +40 -0
  154. package/dist/developer/upgrades/3.6-to-3.7.md +62 -0
  155. package/dist/developer/upgrades/3.7-to-4.0.md +152 -0
  156. package/dist/developer/upgrades/4.0-to-4.1.md +92 -0
  157. package/dist/developer/upgrades/4.1-to-4.2.md +109 -0
  158. package/dist/developer/upgrades/4.10-to-5.0.md +129 -0
  159. package/dist/developer/upgrades/4.2-to-4.3.md +100 -0
  160. package/dist/developer/upgrades/4.3-to-4.4.md +125 -0
  161. package/dist/developer/upgrades/4.4-to-4.5.md +94 -0
  162. package/dist/developer/upgrades/4.5-to-4.6.md +119 -0
  163. package/dist/developer/upgrades/4.6-to-4.7.md +39 -0
  164. package/dist/developer/upgrades/4.8-to-4.9.md +24 -0
  165. package/dist/developer/upgrades/4.9-to-4.10.md +24 -0
  166. package/dist/developer/upgrades/4.x-to-4.8.md +52 -0
  167. package/dist/developer/upgrades/5.0-to-5.1.md +28 -0
  168. package/dist/developer/upgrades/5.1-to-5.2.md +127 -0
  169. package/dist/developer/upgrades/5.2-to-5.3.md +338 -0
  170. package/dist/developer/upgrades/5.3-to-5.4.md +248 -0
  171. package/dist/developer/upgrades/quickstart.md +36 -0
  172. package/dist/integrations/analytics/google-analytics.md +64 -0
  173. package/dist/integrations/analytics/google-tag-manager.md +78 -0
  174. package/dist/integrations/integrations.md +39 -0
  175. package/dist/integrations/marketing/klaviyo.md +99 -0
  176. package/dist/integrations/payments/adyen.md +90 -0
  177. package/dist/integrations/payments/paypal.md +41 -0
  178. package/dist/integrations/payments/razorpay.md +45 -0
  179. package/dist/integrations/payments/stripe.md +109 -0
  180. package/dist/integrations/search/meilisearch.md +236 -0
  181. package/dist/integrations/sso-mfa-social-login/admin-dashboard.md +57 -0
  182. package/dist/integrations/sso-mfa-social-login/storefront.md +56 -0
  183. package/package.json +27 -0
@@ -0,0 +1,698 @@
1
+ ---
2
+ title: Imports & Exports
3
+ ---
4
+
5
+ ## Overview
6
+
7
+ Spree provides a comprehensive bulk data import and export system for managing large datasets. The system supports CSV file processing with configurable field mapping, asynchronous processing via background jobs, and real-time progress tracking in the admin interface.
8
+
9
+ ### Import/Export System Diagram
10
+
11
+ ```mermaid
12
+ erDiagram
13
+ Import {
14
+ string number
15
+ string type
16
+ string status
17
+ bigint owner_id
18
+ string owner_type
19
+ bigint user_id
20
+ }
21
+
22
+ ImportMapping {
23
+ string schema_field
24
+ string file_column
25
+ }
26
+
27
+ ImportRow {
28
+ integer row_number
29
+ text data
30
+ string status
31
+ text validation_errors
32
+ }
33
+
34
+ ImportSchema {
35
+ array fields
36
+ }
37
+
38
+ RowProcessor {
39
+ hash attributes
40
+ }
41
+
42
+ Export {
43
+ string number
44
+ string type
45
+ string format
46
+ jsonb search_params
47
+ }
48
+
49
+ Store {
50
+ string name
51
+ }
52
+
53
+ Import ||--|| Store : "belongs to (owner)"
54
+ Import ||--o{ ImportMapping : "has many"
55
+ Import ||--o{ ImportRow : "has many"
56
+ Import ||--|| ImportSchema : "uses"
57
+ ImportRow ||--|| RowProcessor : "processed by"
58
+ Export ||--|| Store : "belongs to"
59
+ ```
60
+
61
+ ## Architecture
62
+
63
+ The import/export system uses several design patterns:
64
+
65
+ 1. **Single Table Inheritance (STI)**: Import and Export types inherit from base classes
66
+ 2. **State Machine**: Imports progress through states (pending → mapping → processing → completed)
67
+ 3. **Schema Definition**: ImportSchema classes define expected fields and validation
68
+ 4. **Row Processors**: Transform CSV rows into database records
69
+ 5. **Event-Driven Processing**: Background jobs handle heavy lifting asynchronously
70
+ 6. **Registry Pattern**: Types registered in `Spree.import_types` and `Spree.export_types`
71
+
72
+ ---
73
+
74
+ ## Exports
75
+
76
+ Exports generate CSV files from filtered database records.
77
+
78
+ ### Built-in Export Types
79
+
80
+ | Type | Description | Multi-line |
81
+ |------|-------------|------------|
82
+ | `Spree::Exports::Products` | Products with all variants | Yes |
83
+ | `Spree::Exports::Orders` | Orders with line items | Yes |
84
+ | `Spree::Exports::Customers` | Customer accounts | No |
85
+ | `Spree::Exports::GiftCards` | Gift cards | No |
86
+ | `Spree::Exports::NewsletterSubscribers` | Newsletter subscribers | No |
87
+
88
+ ### Export Model
89
+
90
+ The base `Spree::Export` class provides:
91
+
92
+ ```ruby
93
+ module Spree
94
+ class Export < Spree.base_class
95
+ # Associations
96
+ belongs_to :store
97
+ belongs_to :user # Admin who created export
98
+
99
+ # Attachments
100
+ has_one_attached :attachment # Generated CSV file
101
+
102
+ # Key methods
103
+ def csv_headers # Define column headers
104
+ def scope # Base query with store/vendor filtering
105
+ def scope_includes # Eager loading associations
106
+ def records_to_export # Apply ransack filters
107
+ def multi_line_csv? # True if records produce multiple rows
108
+ def generate # Create CSV and attach file
109
+ end
110
+ end
111
+ ```
112
+
113
+ ### Creating a Custom Exporter
114
+
115
+ **Step 1: Create the Export Class**
116
+
117
+ ```ruby app/models/spree/exports/subscriptions.rb
118
+ module Spree
119
+ module Exports
120
+ class Subscriptions < Spree::Export
121
+ # Define CSV column headers
122
+ def csv_headers
123
+ %w[id email plan_name status created_at] + metafields_headers
124
+ end
125
+
126
+ # Eager load associations to avoid N+1 queries
127
+ def scope_includes
128
+ [:user, :plan, { metafields: :metafield_definition }]
129
+ end
130
+
131
+ # Override scope if needed (e.g., exclude cancelled)
132
+ def scope
133
+ super.where.not(status: 'cancelled')
134
+ end
135
+
136
+ # Set to true if each record produces multiple CSV rows
137
+ def multi_line_csv?
138
+ false
139
+ end
140
+ end
141
+ end
142
+ end
143
+ ```
144
+
145
+ **Step 2: Add `to_csv` Method to Your Model**
146
+
147
+ ```ruby app/models/spree/subscription.rb
148
+ module Spree
149
+ class Subscription < Spree.base_class
150
+ def to_csv(store)
151
+ [
152
+ id,
153
+ user&.email,
154
+ plan&.name,
155
+ status,
156
+ created_at.iso8601
157
+ ] + metafields_csv_values(store)
158
+ end
159
+
160
+ private
161
+
162
+ def metafields_csv_values(store)
163
+ Spree::MetafieldDefinition.for_resource_type(self.class.name).order(:namespace, :key).map do |definition|
164
+ metafields.find { |m| m.metafield_definition_id == definition.id }&.value
165
+ end
166
+ end
167
+ end
168
+ end
169
+ ```
170
+
171
+ **Step 3: Register the Export Type**
172
+
173
+ ```ruby config/initializers/spree.rb
174
+ Rails.application.config.after_initialize do
175
+ Spree.export_types << Spree::Exports::Subscriptions
176
+ end
177
+ ```
178
+
179
+ **Step 4: Add Translations**
180
+
181
+ ```yaml config/locales/en.yml
182
+ en:
183
+ spree:
184
+ subscriptions: Subscriptions
185
+ ```
186
+
187
+ ### Multi-line Exports
188
+
189
+ For exports where each record produces multiple rows (like products with variants):
190
+
191
+ ```ruby app/models/spree/exports/orders_with_items.rb
192
+ module Spree
193
+ module Exports
194
+ class OrdersWithItems < Spree::Export
195
+ def multi_line_csv?
196
+ true
197
+ end
198
+
199
+ def csv_headers
200
+ %w[order_number line_item_sku quantity price]
201
+ end
202
+
203
+ def scope_includes
204
+ [line_items: :variant]
205
+ end
206
+ end
207
+ end
208
+ end
209
+ ```
210
+
211
+ ```ruby app/models/spree/order.rb
212
+ # In the Order model
213
+ def to_csv(store)
214
+ line_items.map do |item|
215
+ [number, item.variant.sku, item.quantity, item.price]
216
+ end
217
+ end
218
+ ```
219
+
220
+ ### Export Filtering
221
+
222
+ Exports support Ransack filtering via `search_params`:
223
+
224
+ ```ruby
225
+ # In admin, users can filter before exporting
226
+ export = Spree::Exports::Products.new(
227
+ store: current_store,
228
+ user: current_user,
229
+ search_params: { name_cont: 'shirt', status_eq: 'active' }.to_json,
230
+ record_selection: 'filtered' # or 'all' to ignore filters
231
+ )
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Imports
237
+
238
+ Imports process CSV files to create or update database records.
239
+
240
+ ### Built-in Import Types
241
+
242
+ | Type | Description |
243
+ |------|-------------|
244
+ | `Spree::Imports::Products` | Products and variants |
245
+
246
+ ### Import Workflow
247
+
248
+ ```
249
+ 1. Upload CSV → pending
250
+ 2. Auto-map columns → mapping
251
+ 3. User confirms mapping → completed_mapping
252
+ 4. Parse rows (CreateRowsJob) → processing
253
+ 5. Process rows (ProcessRowsJob) → completed/failed
254
+ ```
255
+
256
+ ### Import Components
257
+
258
+ #### Import Model
259
+
260
+ ```ruby
261
+ module Spree
262
+ class Import < Spree.base_class
263
+ # Associations
264
+ belongs_to :owner, polymorphic: true # Store or Vendor
265
+ belongs_to :user
266
+ has_many :mappings # Field mappings
267
+ has_many :rows # CSV rows to process
268
+
269
+ # State machine
270
+ state_machine initial: :pending do
271
+ event :start_mapping do
272
+ transition to: :mapping
273
+ end
274
+ event :complete_mapping do
275
+ transition from: :mapping, to: :completed_mapping
276
+ end
277
+ event :start_processing do
278
+ transition from: :completed_mapping, to: :processing
279
+ end
280
+ event :complete do
281
+ transition from: :processing, to: :completed
282
+ end
283
+ event :fail do
284
+ transition to: :failed
285
+ end
286
+ end
287
+
288
+ # Key methods
289
+ def import_schema # Returns schema class instance
290
+ def row_processor_class # Returns processor class
291
+ def schema_fields # Fields from schema + metafields
292
+ def mapping_done? # All required fields mapped?
293
+ end
294
+ end
295
+ ```
296
+
297
+ #### Import Schema
298
+
299
+ Defines expected CSV fields:
300
+
301
+ ```ruby
302
+ module Spree
303
+ class ImportSchema
304
+ FIELDS = []
305
+
306
+ def fields
307
+ self.class::FIELDS
308
+ end
309
+
310
+ def required_fields
311
+ FIELDS.select { |f| f[:required] }.map { |f| f[:name] }
312
+ end
313
+
314
+ def optional_fields
315
+ FIELDS.reject { |f| f[:required] }.map { |f| f[:name] }
316
+ end
317
+ end
318
+ end
319
+ ```
320
+
321
+ #### Import Mapping
322
+
323
+ Maps CSV columns to schema fields:
324
+
325
+ ```ruby
326
+ module Spree
327
+ class ImportMapping < Spree.base_class
328
+ belongs_to :import
329
+
330
+ # Attributes
331
+ # schema_field - target field name from schema
332
+ # file_column - CSV column header
333
+
334
+ def try_to_auto_assign_file_column(csv_headers)
335
+ # Matches by parameterized name comparison
336
+ self.file_column = csv_headers.find do |header|
337
+ header.parameterize.underscore == schema_field.parameterize.underscore
338
+ end
339
+ end
340
+ end
341
+ end
342
+ ```
343
+
344
+ #### Import Row
345
+
346
+ Represents a single CSV row:
347
+
348
+ ```ruby
349
+ module Spree
350
+ class ImportRow < Spree.base_class
351
+ belongs_to :import, counter_cache: :rows_count
352
+ belongs_to :item, polymorphic: true, optional: true # Created record
353
+
354
+ # Attributes
355
+ # row_number - position in CSV
356
+ # data - JSON-serialized row data
357
+ # status - pending/processing/completed/failed
358
+ # validation_errors - error message if failed
359
+
360
+ def process!
361
+ start_processing!
362
+ self.item = import.row_processor_class.new(self).process!
363
+ complete!
364
+ rescue StandardError => e
365
+ self.validation_errors = e.message
366
+ fail!
367
+ end
368
+
369
+ def to_schema_hash
370
+ # Maps CSV data using import.mappings
371
+ end
372
+ end
373
+ end
374
+ ```
375
+
376
+ #### Row Processor
377
+
378
+ Transforms row data into database records:
379
+
380
+ ```ruby
381
+ module Spree
382
+ module Imports
383
+ module RowProcessors
384
+ class Base
385
+ def initialize(row)
386
+ @row = row
387
+ @import = row.import
388
+ @attributes = row.to_schema_hash
389
+ end
390
+
391
+ attr_reader :row, :import, :attributes
392
+
393
+ def process!
394
+ raise NotImplementedError
395
+ end
396
+ end
397
+ end
398
+ end
399
+ end
400
+ ```
401
+
402
+ ### Creating a Custom Importer
403
+
404
+ **Step 1: Create the Import Class**
405
+
406
+ ```ruby app/models/spree/imports/subscriptions.rb
407
+ module Spree
408
+ module Imports
409
+ class Subscriptions < Spree::Import
410
+ def row_processor_class
411
+ Spree::Imports::RowProcessors::Subscription
412
+ end
413
+ end
414
+ end
415
+ end
416
+ ```
417
+
418
+ **Step 2: Define the Schema**
419
+
420
+ ```ruby app/models/spree/import_schemas/subscriptions.rb
421
+ module Spree
422
+ module ImportSchemas
423
+ class Subscriptions < Spree::ImportSchema
424
+ FIELDS = [
425
+ { name: 'email', label: 'Customer Email', required: true },
426
+ { name: 'plan_name', label: 'Plan Name', required: true },
427
+ { name: 'status', label: 'Status', required: true },
428
+ { name: 'start_date', label: 'Start Date' },
429
+ { name: 'billing_interval', label: 'Billing Interval' },
430
+ { name: 'amount', label: 'Amount' },
431
+ { name: 'currency', label: 'Currency' }
432
+ ].freeze
433
+ end
434
+ end
435
+ end
436
+ ```
437
+
438
+ **Step 3: Create the Row Processor**
439
+
440
+ ```ruby app/services/spree/imports/row_processors/subscription.rb
441
+ module Spree
442
+ module Imports
443
+ module RowProcessors
444
+ class Subscription < Base
445
+ def process!
446
+ user = find_or_create_user
447
+ plan = find_plan
448
+
449
+ subscription = Spree::Subscription.find_or_initialize_by(
450
+ user: user,
451
+ plan: plan
452
+ )
453
+
454
+ subscription.status = attributes['status'] if attributes['status'].present?
455
+ subscription.start_date = parse_date(attributes['start_date']) if attributes['start_date'].present?
456
+ subscription.billing_interval = attributes['billing_interval'] if attributes['billing_interval'].present?
457
+
458
+ if attributes['amount'].present?
459
+ currency = attributes['currency'].presence || import.store.default_currency
460
+ subscription.set_price(currency, attributes['amount'])
461
+ end
462
+
463
+ subscription.save!
464
+ subscription
465
+ end
466
+
467
+ private
468
+
469
+ def find_or_create_user
470
+ email = attributes['email'].strip.downcase
471
+ Spree.user_class.find_or_create_by!(email: email)
472
+ end
473
+
474
+ def find_plan
475
+ Spree::Plan.find_by!(name: attributes['plan_name'].strip)
476
+ end
477
+
478
+ def parse_date(date_string)
479
+ Date.parse(date_string)
480
+ rescue ArgumentError
481
+ nil
482
+ end
483
+ end
484
+ end
485
+ end
486
+ end
487
+ ```
488
+
489
+ **Step 4: Register the Import Type**
490
+
491
+ ```ruby config/initializers/spree.rb
492
+ Rails.application.config.after_initialize do
493
+ Spree.import_types << Spree::Imports::Subscriptions
494
+ end
495
+ ```
496
+
497
+ **Step 5: Add Translations**
498
+
499
+ ```yaml config/locales/en.yml
500
+ en:
501
+ spree:
502
+ subscriptions: Subscriptions
503
+ ```
504
+
505
+ ### Products Import Schema
506
+
507
+ The built-in products import supports these fields:
508
+
509
+ **Required Fields:**
510
+ - `slug` - Product URL slug
511
+ - `sku` - Variant SKU
512
+ - `name` - Product name
513
+ - `price` - Variant price
514
+
515
+ **Optional Fields:**
516
+ - `status` - Product status (active/draft/archived)
517
+ - `description` - Product description
518
+ - `meta_title`, `meta_description`, `meta_keywords` - SEO metadata
519
+ - `tags` - Product tags
520
+ - `compare_at_price` - Original price for sale display
521
+ - `currency` - Price currency
522
+ - `width`, `height`, `depth`, `dimensions_unit` - Dimensions
523
+ - `weight`, `weight_unit` - Weight
524
+ - `available_on`, `discontinue_on` - Availability dates
525
+ - `track_inventory` - Enable inventory tracking
526
+ - `inventory_count`, `inventory_backorderable` - Stock settings
527
+ - `tax_category`, `shipping_category` - Category assignments
528
+ - `image1_src`, `image2_src`, `image3_src` - Image URLs
529
+ - `option1_name`, `option1_value` through `option3_name`, `option3_value` - Variant options
530
+ - `category1`, `category2`, `category3` - Taxon assignments (format: "Taxonomy -> Taxon -> Child Taxon")
531
+
532
+ ### Handling Multi-Variant Products
533
+
534
+ The products import handles variants intelligently:
535
+
536
+ 1. **Master variant rows** (no option values): Create/update the product and its master variant
537
+ 2. **Non-master variant rows** (with option values): Create additional variants for an existing product
538
+
539
+ ```csv
540
+ slug,sku,name,price,option1_name,option1_value,option2_name,option2_value
541
+ my-tshirt,TSHIRT-001,My T-Shirt,29.99,,,
542
+ my-tshirt,TSHIRT-S-RED,My T-Shirt,29.99,Size,Small,Color,Red
543
+ my-tshirt,TSHIRT-M-RED,My T-Shirt,29.99,Size,Medium,Color,Red
544
+ my-tshirt,TSHIRT-L-RED,My T-Shirt,29.99,Size,Large,Color,Red
545
+ ```
546
+
547
+ ### Metafield Support
548
+
549
+ Both imports and exports support metafields dynamically:
550
+
551
+ **Export:** Metafield definitions are automatically added as CSV columns using the format `metafield.{namespace}.{key}`.
552
+
553
+ **Import:** Map CSV columns to metafield definitions. The system automatically detects columns matching the metafield pattern and updates the corresponding metafield values.
554
+
555
+ ---
556
+
557
+ ## Background Jobs
558
+
559
+ ### CreateRowsJob
560
+
561
+ Parses CSV and creates ImportRow records:
562
+
563
+ ```ruby
564
+ module Spree
565
+ module Imports
566
+ class CreateRowsJob < Spree::BaseJob
567
+ queue_as Spree.queues.imports
568
+
569
+ def perform(import_id)
570
+ import = Spree::Import.find(import_id)
571
+ # Stream CSV, batch insert rows
572
+ # Then enqueue ProcessRowsJob
573
+ end
574
+ end
575
+ end
576
+ end
577
+ ```
578
+
579
+ ### ProcessRowsJob
580
+
581
+ Processes pending rows through row processors:
582
+
583
+ ```ruby
584
+ module Spree
585
+ module Imports
586
+ class ProcessRowsJob < Spree::BaseJob
587
+ queue_as Spree.queues.imports
588
+
589
+ def perform(import_id)
590
+ import = Spree::Import.find(import_id)
591
+ import.rows.pending_and_failed.find_each do |row|
592
+ row.process!
593
+ end
594
+ import.complete!
595
+ end
596
+ end
597
+ end
598
+ end
599
+ ```
600
+
601
+ ### GenerateJob (Exports)
602
+
603
+ Generates CSV files for exports:
604
+
605
+ ```ruby
606
+ module Spree
607
+ module Exports
608
+ class GenerateJob < Spree::BaseJob
609
+ queue_as Spree.queues.exports
610
+
611
+ def perform(export_id)
612
+ export = Spree::Export.find(export_id)
613
+ export.generate
614
+ end
615
+ end
616
+ end
617
+ end
618
+ ```
619
+
620
+ ---
621
+
622
+ ## Events
623
+
624
+ ### Import Events
625
+
626
+ | Event | Trigger |
627
+ |-------|---------|
628
+ | `import.created` | Import record created |
629
+ | `import.completed` | All rows processed |
630
+
631
+ ### Export Events
632
+
633
+ | Event | Trigger |
634
+ |-------|---------|
635
+ | `export.created` | Export record created (triggers generation) |
636
+
637
+ ### Import Row Events
638
+
639
+ | Event | Trigger |
640
+ |-------|---------|
641
+ | `import_row.completed` | Row processed successfully |
642
+ | `import_row.failed` | Row processing failed |
643
+
644
+ ---
645
+
646
+ ## Configuration
647
+
648
+ ### Queue Configuration
649
+
650
+ ```ruby config/initializers/spree.rb
651
+ # Configure job queues
652
+ Spree.queues.imports = :imports
653
+ Spree.queues.exports = :exports
654
+ ```
655
+
656
+ ### Preferences
657
+
658
+ Imports support configurable delimiter:
659
+
660
+ ```ruby
661
+ import.preferred_delimiter = ';' # Default: ','
662
+ ```
663
+
664
+ ---
665
+
666
+ ## Key Files Reference
667
+
668
+ | File | Purpose |
669
+ |------|---------|
670
+ | `core/app/models/spree/import.rb` | Base import model |
671
+ | `core/app/models/spree/export.rb` | Base export model |
672
+ | `core/app/models/spree/import_schema.rb` | Base schema class |
673
+ | `core/app/models/spree/import_mapping.rb` | Field mapping model |
674
+ | `core/app/models/spree/import_row.rb` | Row model with processing |
675
+ | `core/app/models/spree/imports/products.rb` | Products import type |
676
+ | `core/app/models/spree/import_schemas/products.rb` | Products schema |
677
+ | `core/app/services/spree/imports/row_processors/base.rb` | Base processor |
678
+ | `core/app/services/spree/imports/row_processors/product_variant.rb` | Products processor |
679
+ | `core/app/models/spree/exports/products.rb` | Products export |
680
+ | `core/app/models/spree/exports/orders.rb` | Orders export |
681
+ | `core/app/models/spree/exports/customers.rb` | Customers export |
682
+ | `core/app/jobs/spree/imports/create_rows_job.rb` | Row creation job |
683
+ | `core/app/jobs/spree/imports/process_rows_job.rb` | Row processing job |
684
+ | `core/app/jobs/spree/exports/generate_job.rb` | Export generation job |
685
+ | `admin/app/controllers/spree/admin/imports_controller.rb` | Admin imports controller |
686
+ | `admin/app/controllers/spree/admin/exports_controller.rb` | Admin exports controller |
687
+
688
+ ## Permissions
689
+
690
+ Access is controlled via CanCanCan:
691
+
692
+ ```ruby
693
+ # Allow admin to manage imports/exports
694
+ can :manage, Spree::Import
695
+ can :manage, Spree::Export
696
+ ```
697
+
698
+ Records are filtered by `current_ability` ensuring users only export data they have access to.