spree_api 6.0.0.rc3 → 6.0.0.rc5

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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/spree/api/v3/admin/email_template_lookup.rb +6 -1
  3. data/app/controllers/concerns/spree/api/v3/admin/reporting_authorization.rb +17 -8
  4. data/app/controllers/concerns/spree/api/v3/admin/store_context.rb +7 -0
  5. data/app/controllers/concerns/spree/api/v3/admin_authentication.rb +61 -0
  6. data/app/controllers/concerns/spree/api/v3/allowed_redirect_url.rb +27 -0
  7. data/app/controllers/concerns/spree/api/v3/cart_resolvable.rb +8 -2
  8. data/app/controllers/concerns/spree/api/v3/error_handler.rb +49 -0
  9. data/app/controllers/concerns/spree/api/v3/params_normalizer.rb +76 -7
  10. data/app/controllers/concerns/spree/api/v3/scoped_authorization.rb +66 -5
  11. data/app/controllers/spree/api/v3/admin/companies/addresses_controller.rb +7 -1
  12. data/app/controllers/spree/api/v3/admin/companies/tax_exemption_certificates_controller.rb +11 -5
  13. data/app/controllers/spree/api/v3/admin/concerns/external_references.rb +3 -0
  14. data/app/controllers/spree/api/v3/admin/custom_fields_controller.rb +30 -8
  15. data/app/controllers/spree/api/v3/admin/customers/addresses_controller.rb +4 -0
  16. data/app/controllers/spree/api/v3/admin/customers_controller.rb +1 -6
  17. data/app/controllers/spree/api/v3/admin/exports_controller.rb +15 -3
  18. data/app/controllers/spree/api/v3/admin/gift_cards_controller.rb +1 -8
  19. data/app/controllers/spree/api/v3/admin/imports_controller.rb +12 -3
  20. data/app/controllers/spree/api/v3/admin/markets_controller.rb +3 -1
  21. data/app/controllers/spree/api/v3/admin/me_controller.rb +21 -1
  22. data/app/controllers/spree/api/v3/admin/oauth/application_tokens_controller.rb +56 -0
  23. data/app/controllers/spree/api/v3/admin/oauth/applications_controller.rb +55 -0
  24. data/app/controllers/spree/api/v3/admin/oauth/authorizations_controller.rb +294 -0
  25. data/app/controllers/spree/api/v3/admin/order_groups_controller.rb +1 -1
  26. data/app/controllers/spree/api/v3/admin/orders/fulfillments_controller.rb +8 -0
  27. data/app/controllers/spree/api/v3/admin/orders/items_controller.rb +1 -0
  28. data/app/controllers/spree/api/v3/admin/orders/returns_controller.rb +7 -0
  29. data/app/controllers/spree/api/v3/admin/orders_controller.rb +20 -7
  30. data/app/controllers/spree/api/v3/admin/package_types_controller.rb +2 -3
  31. data/app/controllers/spree/api/v3/admin/products/variants_controller.rb +3 -4
  32. data/app/controllers/spree/api/v3/admin/products_controller.rb +5 -3
  33. data/app/controllers/spree/api/v3/admin/purchase_orders_controller.rb +1 -0
  34. data/app/controllers/spree/api/v3/admin/reporting_controller.rb +20 -14
  35. data/app/controllers/spree/api/v3/admin/stock_levels_controller.rb +1 -1
  36. data/app/controllers/spree/api/v3/admin/store_controller.rb +1 -1
  37. data/app/controllers/spree/api/v3/admin/tags_controller.rb +8 -5
  38. data/app/controllers/spree/api/v3/admin/tax_exemption_reasons_controller.rb +58 -0
  39. data/app/controllers/spree/api/v3/admin/tax_rates_controller.rb +0 -1
  40. data/app/controllers/spree/api/v3/admin/translations/batches_controller.rb +17 -11
  41. data/app/controllers/spree/api/v3/admin/variants_controller.rb +96 -1
  42. data/app/controllers/spree/api/v3/oauth/authorizations_controller.rb +72 -0
  43. data/app/controllers/spree/api/v3/oauth/metadata_controller.rb +57 -0
  44. data/app/controllers/spree/api/v3/oauth/server_metadata_controller.rb +57 -0
  45. data/app/controllers/spree/api/v3/resource_controller.rb +36 -5
  46. data/app/controllers/spree/api/v3/seller/package_types_controller.rb +2 -2
  47. data/app/controllers/spree/api/v3/seller/products_controller.rb +7 -61
  48. data/app/controllers/spree/api/v3/seller/resource_controller.rb +2 -6
  49. data/app/controllers/spree/api/v3/store/carts/addresses_controller.rb +53 -0
  50. data/app/controllers/spree/api/v3/store/carts_controller.rb +7 -8
  51. data/app/controllers/spree/api/v3/store/companies/addresses_controller.rb +4 -0
  52. data/app/controllers/spree/api/v3/store/customer/addresses_controller.rb +4 -0
  53. data/app/controllers/spree/api/v3/store/customer/data_requests_controller.rb +19 -1
  54. data/app/controllers/spree/api/v3/store/customer/password_resets_controller.rb +3 -14
  55. data/app/controllers/spree/api/v3/store/data_request_confirmations_controller.rb +23 -0
  56. data/app/controllers/spree/api/v3/store/data_requests_controller.rb +56 -0
  57. data/app/controllers/spree/api/v3/store/newsletter_subscribers_controller.rb +2 -11
  58. data/app/request_schemas/spree/api/v3/admin/email_template_preview_request_schema.rb +4 -0
  59. data/app/request_schemas/spree/api/v3/admin/variant_request_schema.rb +21 -0
  60. data/app/request_schemas/spree/api/v3/seller/price_request_schema.rb +20 -0
  61. data/app/request_schemas/spree/api/v3/seller/variant_request_schema.rb +19 -0
  62. data/app/request_schemas/spree/api/v3/variant_request_schema.rb +8 -13
  63. data/app/serializers/spree/api/v3/address_owner_serializer.rb +27 -0
  64. data/app/serializers/spree/api/v3/address_serializer.rb +5 -1
  65. data/app/serializers/spree/api/v3/admin/address_owner_serializer.rb +10 -0
  66. data/app/serializers/spree/api/v3/admin/address_serializer.rb +1 -10
  67. data/app/serializers/spree/api/v3/admin/api_key_serializer.rb +1 -1
  68. data/app/serializers/spree/api/v3/admin/catalog_assignment_serializer.rb +1 -1
  69. data/app/serializers/spree/api/v3/admin/catalog_price_serializer.rb +1 -1
  70. data/app/serializers/spree/api/v3/admin/collection_rule_serializer.rb +1 -1
  71. data/app/serializers/spree/api/v3/admin/collection_serializer.rb +1 -1
  72. data/app/serializers/spree/api/v3/admin/commission_line_serializer.rb +2 -2
  73. data/app/serializers/spree/api/v3/admin/commission_rate_serializer.rb +1 -1
  74. data/app/serializers/spree/api/v3/admin/consent_record_serializer.rb +1 -1
  75. data/app/serializers/spree/api/v3/admin/email_template_serializer.rb +1 -1
  76. data/app/serializers/spree/api/v3/admin/fulfillment_serializer.rb +2 -0
  77. data/app/serializers/spree/api/v3/admin/line_item_serializer.rb +2 -0
  78. data/app/serializers/spree/api/v3/admin/oauth_access_token_serializer.rb +62 -0
  79. data/app/serializers/spree/api/v3/admin/oauth_application_serializer.rb +56 -0
  80. data/app/serializers/spree/api/v3/admin/oauth_consent_request_serializer.rb +25 -0
  81. data/app/serializers/spree/api/v3/admin/oauth_protected_resource_serializer.rb +27 -0
  82. data/app/serializers/spree/api/v3/admin/oauth_redirect_serializer.rb +17 -0
  83. data/app/serializers/spree/api/v3/admin/order_serializer.rb +1 -1
  84. data/app/serializers/spree/api/v3/admin/package_type_serializer.rb +3 -3
  85. data/app/serializers/spree/api/v3/admin/payment_capture_event_serializer.rb +16 -0
  86. data/app/serializers/spree/api/v3/admin/payment_serializer.rb +12 -1
  87. data/app/serializers/spree/api/v3/admin/permission_serializer.rb +1 -1
  88. data/app/serializers/spree/api/v3/admin/price_list_serializer.rb +1 -1
  89. data/app/serializers/spree/api/v3/admin/purchase_order_serializer.rb +1 -1
  90. data/app/serializers/spree/api/v3/admin/return_serializer.rb +2 -0
  91. data/app/serializers/spree/api/v3/admin/seller_requirement_status_serializer.rb +1 -1
  92. data/app/serializers/spree/api/v3/admin/seller_serializer.rb +2 -2
  93. data/app/serializers/spree/api/v3/admin/seller_transfer_serializer.rb +1 -1
  94. data/app/serializers/spree/api/v3/admin/shipping_label_serializer.rb +3 -3
  95. data/app/serializers/spree/api/v3/admin/stock_location_serializer.rb +1 -1
  96. data/app/serializers/spree/api/v3/admin/stock_receipt_item_serializer.rb +2 -2
  97. data/app/serializers/spree/api/v3/admin/stock_receipt_serializer.rb +1 -1
  98. data/app/serializers/spree/api/v3/admin/stock_transfer_serializer.rb +1 -1
  99. data/app/serializers/spree/api/v3/admin/store_serializer.rb +1 -0
  100. data/app/serializers/spree/api/v3/admin/tax_exemption_certificate_serializer.rb +7 -3
  101. data/app/serializers/spree/api/v3/admin/tax_exemption_reason_serializer.rb +20 -0
  102. data/app/serializers/spree/api/v3/admin/tax_identifier_serializer.rb +2 -2
  103. data/app/serializers/spree/api/v3/admin/tax_line_serializer.rb +1 -1
  104. data/app/serializers/spree/api/v3/admin/tax_rate_serializer.rb +1 -2
  105. data/app/serializers/spree/api/v3/admin/variant_serializer.rb +1 -1
  106. data/app/serializers/spree/api/v3/admin/webhook_delivery_serializer.rb +4 -1
  107. data/app/serializers/spree/api/v3/base_serializer.rb +41 -5
  108. data/app/serializers/spree/api/v3/claim_serializer.rb +1 -1
  109. data/app/serializers/spree/api/v3/collection_serializer.rb +1 -1
  110. data/app/serializers/spree/api/v3/company_invitation_serializer.rb +1 -1
  111. data/app/serializers/spree/api/v3/company_serializer.rb +1 -1
  112. data/app/serializers/spree/api/v3/data_request_confirmation_requested_event_serializer.rb +28 -0
  113. data/app/serializers/spree/api/v3/data_request_event_serializer.rb +1 -1
  114. data/app/serializers/spree/api/v3/data_request_serializer.rb +8 -4
  115. data/app/serializers/spree/api/v3/delivery_serializer.rb +1 -1
  116. data/app/serializers/spree/api/v3/delivery_zone_member_serializer.rb +1 -1
  117. data/app/serializers/spree/api/v3/discount_serializer.rb +1 -1
  118. data/app/serializers/spree/api/v3/file_upload_serializer.rb +1 -1
  119. data/app/serializers/spree/api/v3/fulfillment_serializer.rb +1 -1
  120. data/app/serializers/spree/api/v3/media_event_serializer.rb +1 -1
  121. data/app/serializers/spree/api/v3/media_serializer.rb +1 -1
  122. data/app/serializers/spree/api/v3/option_type_serializer.rb +1 -1
  123. data/app/serializers/spree/api/v3/order_group_serializer.rb +2 -2
  124. data/app/serializers/spree/api/v3/order_serializer.rb +4 -2
  125. data/app/serializers/spree/api/v3/payment_serializer.rb +1 -1
  126. data/app/serializers/spree/api/v3/product_filter_availability_option_serializer.rb +1 -1
  127. data/app/serializers/spree/api/v3/product_filter_option_serializer.rb +1 -1
  128. data/app/serializers/spree/api/v3/seller/address_serializer.rb +3 -2
  129. data/app/serializers/spree/api/v3/seller/order_serializer.rb +3 -3
  130. data/app/serializers/spree/api/v3/seller/package_type_serializer.rb +3 -3
  131. data/app/serializers/spree/api/v3/seller/profile_serializer.rb +2 -2
  132. data/app/serializers/spree/api/v3/seller/requirement_status_serializer.rb +1 -1
  133. data/app/serializers/spree/api/v3/seller/tax_identifier_serializer.rb +1 -1
  134. data/app/serializers/spree/api/v3/seller/variant_serializer.rb +1 -1
  135. data/app/serializers/spree/api/v3/shipping_label_event_serializer.rb +1 -1
  136. data/app/serializers/spree/api/v3/stock_movement_serializer.rb +1 -1
  137. data/app/serializers/spree/api/v3/stock_receipt_event_serializer.rb +1 -1
  138. data/app/serializers/spree/api/v3/variant_serializer.rb +2 -2
  139. data/app/services/spree/api/v3/filter_table.rb +22 -3
  140. data/app/services/spree/api/v3/request_schema.rb +64 -17
  141. data/config/locales/en.yml +1 -0
  142. data/config/routes.rb +80 -12
  143. data/lib/spree/api/agent_membership_map.rb +73 -0
  144. data/lib/spree/api/agent_resource_map.rb +350 -0
  145. data/lib/spree/api/agent_workflow_endpoints.rb +113 -0
  146. data/lib/spree/api/agent_write_schemas.rb +131 -0
  147. data/lib/spree/api/dependencies.rb +5 -4
  148. data/lib/spree/api/engine.rb +40 -0
  149. data/lib/spree/api/enum_types.rb +118 -0
  150. data/lib/spree/api/extension_fields.rb +167 -0
  151. data/lib/spree/api/oauth.rb +243 -0
  152. data/lib/spree/api/openapi/filter_documentation.rb +8 -1
  153. data/lib/spree/api/typelizer_generation.rb +5 -0
  154. data/lib/spree/api.rb +8 -0
  155. data/lib/typelizer/open_enums.rb +48 -10
  156. metadata +37 -6
@@ -5,7 +5,7 @@ module Spree
5
5
  # Admin-only: certificates are back-office evidence with no storefront
6
6
  # surface, so there is no store serializer to extend.
7
7
  class TaxExemptionCertificateSerializer < V3::BaseSerializer
8
- typelize certificate_number: :string, reason_code: :string, status: [:string, enum: Spree::TaxExemptionCertificate.statuses, enum_type_name: 'TaxExemptionCertificateStatus'],
8
+ typelize certificate_number: :string, tax_exemption_reason_id: :string, status: [:string, enum: Spree::TaxExemptionCertificate.statuses, enum_type_name: 'TaxExemptionCertificateStatus'],
9
9
  issuing_authority: [:string, nullable: true],
10
10
  issued_at: [:string, nullable: true], expires_at: [:string, nullable: true],
11
11
  verified_at: [:string, nullable: true],
@@ -17,11 +17,15 @@ module Spree
17
17
  document_url: [:string, nullable: true],
18
18
  metadata: 'Record<string, unknown> | null'
19
19
 
20
- attributes :certificate_number, :reason_code, :status, :issuing_authority, :metadata,
20
+ attributes :certificate_number, :status, :issuing_authority, :metadata,
21
21
  issued_at: :iso8601, expires_at: :iso8601, verified_at: :iso8601,
22
22
  created_at: :iso8601, updated_at: :iso8601
23
23
 
24
- prefixed_id_attributes :company
24
+ prefixed_id_attributes :company, :tax_exemption_reason
25
+
26
+ one :tax_exemption_reason,
27
+ resource: proc { Spree.api.admin_tax_exemption_reason_serializer },
28
+ if: proc { expand?('tax_exemption_reason') }
25
29
 
26
30
  # The jurisdiction the certificate holds in, in the same vocabulary the
27
31
  # tax lines use.
@@ -0,0 +1,20 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ # Admin-only: exemption reasons are recorded on certificates, which are
6
+ # back-office evidence with no storefront surface.
7
+ class TaxExemptionReasonSerializer < V3::BaseSerializer
8
+ typelize name: :string, code: :string, active: :boolean, can_be_deleted: :boolean,
9
+ metadata: 'Record<string, unknown> | null'
10
+
11
+ attributes :name, :code, :active, :metadata, created_at: :iso8601, updated_at: :iso8601
12
+
13
+ # Lets the dashboard hide destructive controls instead of offering a
14
+ # delete that the model will refuse.
15
+ attribute :can_be_deleted, &:can_be_deleted?
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -3,10 +3,10 @@ module Spree
3
3
  module V3
4
4
  module Admin
5
5
  class TaxIdentifierSerializer < V3::TaxIdentifierSerializer
6
- typelize validation_status: [:string, nullable: true, enum: Spree::TaxIdentifier::VALIDATION_STATUSES],
6
+ typelize validation_status: [:string, nullable: true, enum: Spree::TaxIdentifier::VALIDATION_STATUSES, enum_type_name: 'TaxIdentifierValidationStatus'],
7
7
  validated_at: [:string, nullable: true],
8
8
  validation_evidence: ['Record<string, unknown>', nullable: true],
9
- source: [:string, nullable: true, enum: Spree::TaxIdentifier::SOURCES],
9
+ source: [:string, nullable: true, enum: Spree::TaxIdentifier::SOURCES, enum_type_name: 'TaxIdentifierSource'],
10
10
  validatable: :boolean,
11
11
  customer_id: [:string, nullable: true],
12
12
  cart_id: [:string, nullable: true],
@@ -7,7 +7,7 @@ module Spree
7
7
 
8
8
  typelize amount: [:string, nullable: false],
9
9
  provider_id: [:string, nullable: true], metadata: ['Record<string, unknown>', nullable: true],
10
- taxability_reason: [:string, nullable: true], country_code: [:string, nullable: true, enum: Spree::IsoData.country_codes, enum_type_name: 'CountryCode'],
10
+ taxability_reason: [:string, nullable: true, enum: Spree::TaxLine::BUILT_IN_TAXABILITY_REASONS, enum_type_name: 'TaxabilityReason'], country_code: [:string, nullable: true, enum: Spree::IsoData.country_codes, enum_type_name: 'CountryCode'],
11
11
  state_code: [:string, nullable: true], data: ['Record<string, unknown>', nullable: true],
12
12
  original_tax_line_id: [:string, nullable: true]
13
13
 
@@ -6,7 +6,6 @@ module Spree
6
6
  # customer-facing. What the customer sees is the label on a tax line.
7
7
  class TaxRateSerializer < V3::BaseSerializer
8
8
  typelize name: :string,
9
- included_in_price: :boolean,
10
9
  show_rate_in_label: :boolean,
11
10
  tax_category_id: [:string, nullable: true],
12
11
  store_id: [:string, nullable: true],
@@ -15,7 +14,7 @@ module Spree
15
14
  metadata: ['Record<string, unknown>', nullable: true],
16
15
  deleted_at: [:string, nullable: true]
17
16
 
18
- attributes :name, :included_in_price, :show_rate_in_label, :metadata,
17
+ attributes :name, :show_rate_in_label, :metadata,
19
18
  created_at: :iso8601, updated_at: :iso8601, deleted_at: :iso8601
20
19
 
21
20
  rate_attributes :rate, :rate_percent
@@ -24,7 +24,7 @@ module Spree
24
24
  deleted_at: [:string, nullable: true],
25
25
  delivery_profile_id: [:string, nullable: true],
26
26
  minimum_order_quantity: ['number | null'], order_multiple: ['number | null'],
27
- purchase_unit: [:string, nullable: true, enum: Spree::Variant::PURCHASE_UNITS],
27
+ purchase_unit: [:string, nullable: true, enum: Spree::Variant::PURCHASE_UNITS, enum_type_name: 'PurchaseUnit'],
28
28
  carton_package_type_id: [:string, nullable: true],
29
29
  carton_weight: [:string, nullable: true],
30
30
  cartons_per_pallet: ['number | null'],
@@ -32,8 +32,11 @@ module Spree
32
32
  # shipped still hold live credentials in the column. Null when the
33
33
  # caller cannot read the record the event is about.
34
34
  attribute :payload do |delivery|
35
+ # Fails closed: a reader that passes no gate gets nothing. The
36
+ # payload keeps the record body by design, so a caller holding
37
+ # `read_webhooks` alone must not see customer or payment data.
35
38
  visible = params[:payload_visible]
36
- next nil if visible && !visible.call(delivery)
39
+ next nil unless visible&.call(delivery)
37
40
 
38
41
  Spree::WebhookPayloadRedaction.split(delivery.payload).first
39
42
  end
@@ -126,7 +126,7 @@ module Spree
126
126
  names = model.exposed_preference_names.to_a - except if names.empty?
127
127
  definitions = model.preference_definitions
128
128
  names.each do |name|
129
- typelize name => preference_type_hint(Spree::PreferenceSchema::JsonSchema.property_schema(definitions.fetch(name)))
129
+ typelize name => preference_type_hint(Spree::PreferenceSchema::JsonSchema.property_schema(definitions.fetch(name)), name)
130
130
  reader = :"preferred_#{name}"
131
131
  attribute(name) do |object|
132
132
  value = object.public_send(reader)
@@ -138,12 +138,29 @@ module Spree
138
138
  TYPELIZER_TYPES = { 'string' => :string, 'integer' => :number, 'number' => :number, 'boolean' => :boolean }.freeze
139
139
  private_constant :TYPELIZER_TYPES
140
140
 
141
- def self.preference_type_hint(schema)
141
+ # The shared name of each setting's value list, so it is defined once
142
+ # in `@spree/core` rather than inline wherever the setting appears.
143
+ PREFERENCE_LIST_NAMES = {
144
+ capture_method: 'CaptureMethod',
145
+ default_payouts_schedule_interval: 'PayoutScheduleInterval',
146
+ document_number_format: 'DocumentNumberFormat',
147
+ email_font: 'EmailFont',
148
+ inventory_provider_failure_policy: 'ProviderFailurePolicy',
149
+ pricing_provider_failure_policy: 'ProviderFailurePolicy',
150
+ storefront_access: 'StorefrontAccess',
151
+ unit_system: 'UnitSystem'
152
+ }.freeze
153
+ private_constant :PREFERENCE_LIST_NAMES
154
+
155
+ def self.preference_type_hint(schema, name)
142
156
  types = Array(schema['type'])
143
157
  hint = TYPELIZER_TYPES.fetch((types - ['null']).first, :unknown)
144
158
  options = {}
145
159
  options[:nullable] = true if types.include?('null')
146
- options[:enum] = schema['enum'].compact if schema['enum']
160
+ if schema['enum']
161
+ options[:enum] = schema['enum'].compact
162
+ options[:enum_type_name] = PREFERENCE_LIST_NAMES[name.to_sym] if PREFERENCE_LIST_NAMES.key?(name.to_sym)
163
+ end
147
164
  options.empty? ? hint : [hint, options]
148
165
  end
149
166
  private_class_method :preference_type_hint
@@ -224,6 +241,20 @@ module Spree
224
241
 
225
242
  private
226
243
 
244
+ # Adds the fields extensions registered for this class
245
+ # (Spree.api.extension_fields), read from the registry on every
246
+ # render as Spree.ransack is, so nothing is copied onto the class. A
247
+ # field never replaces an attribute the serializer writes itself.
248
+ def attributes_to_hash(object, hash)
249
+ super
250
+ Spree.api.extension_fields.for(self.class).each do |field|
251
+ next if @_attributes.key?(field.name)
252
+
253
+ set_key_and_attribute_body_from(object, field.name, field.reader, hash)
254
+ end
255
+ hash
256
+ end
257
+
227
258
  # Override Alba's fetch_attribute to automatically inject nested expand params
228
259
  # into child serializers via nested_params.
229
260
  # Uses `key` (the public API name, e.g. :categories) for expand lookup,
@@ -263,6 +294,10 @@ module Spree
263
294
  currency: current_currency,
264
295
  store: current_store,
265
296
  user: current_user,
297
+ company: params[:company],
298
+ market: params[:market],
299
+ channel: params[:channel],
300
+ country: params[:country],
266
301
  quantity: quantity
267
302
  )
268
303
  end
@@ -281,8 +316,9 @@ module Spree
281
316
  return Spree::QuantityRule.new if current_store.nil?
282
317
 
283
318
  Spree::Current.quantity_rules_resolver_for(
284
- company: Spree::Current.standing_company_for(current_user),
285
- user: current_user
319
+ company: params.key?(:company) ? params[:company] : Spree::Current.standing_company_for(current_user),
320
+ user: current_user,
321
+ channel: params[:channel]
286
322
  ).call(variant)
287
323
  end
288
324
 
@@ -7,7 +7,7 @@ module Spree
7
7
  class ClaimSerializer < BaseSerializer
8
8
  typelize number: :string,
9
9
  status: [:string, enum: Spree::Claim.statuses, enum_type_name: 'ClaimStatus'],
10
- resolution: [:string, nullable: true],
10
+ resolution: [:string, nullable: true, enum: Spree::Claim::RESOLUTIONS, enum_type_name: 'ClaimResolution'],
11
11
  order_id: [:string, nullable: true],
12
12
  reason_id: [:string, nullable: true],
13
13
  approved_at: [:string, nullable: true],
@@ -6,7 +6,7 @@ module Spree
6
6
  # back-office state exposed only by the Admin serializer.
7
7
  class CollectionSerializer < BaseSerializer
8
8
  typelize name: :string, permalink: :string, position: :number,
9
- sort_order: :string,
9
+ sort_order: [:string, enum: Spree::Collection::SORT_ORDERS, enum_type_name: 'CollectionSortOrder'],
10
10
  meta_title: [:string, nullable: true], meta_description: [:string, nullable: true], meta_keywords: [:string, nullable: true],
11
11
  description: :string, description_html: :string,
12
12
  image_url: [:string, nullable: true], square_image_url: [:string, nullable: true],
@@ -4,7 +4,7 @@ module Spree
4
4
  # A pending (or spent) invitation into a company node. The token is
5
5
  # deliberately absent — it travels only in the invite email.
6
6
  class CompanyInvitationSerializer < BaseSerializer
7
- typelize email: :string, company_id: :string, status: [:string, enum: %w[pending accepted revoked expired]],
7
+ typelize email: :string, company_id: :string, status: [:string, enum: %w[pending accepted revoked expired], enum_type_name: 'CompanyInvitationStatus'],
8
8
  expires_at: [:string, nullable: true]
9
9
 
10
10
  attributes :email, expires_at: :iso8601
@@ -7,7 +7,7 @@ module Spree
7
7
  # back-office fields stay in one place.
8
8
  class CompanySerializer < BaseSerializer
9
9
  typelize name: :string,
10
- kind: [:string, enum: Spree::Company::KINDS], parent_id: [:string, nullable: true],
10
+ kind: [:string, enum: Spree::Company::KINDS, enum_type_name: 'CompanyKind'], parent_id: [:string, nullable: true],
11
11
  po_number_required: :boolean,
12
12
  ancestors: 'Array<{id: string, name: string, kind: string}>'
13
13
 
@@ -0,0 +1,28 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ # The event shape for `data_request.confirmation_requested`: what a
5
+ # storefront that sends its own emails needs to ask the person to confirm
6
+ # a request they raised. `confirmation_token` is a live credential, so
7
+ # the event reaches only endpoints that name it.
8
+ class DataRequestConfirmationRequestedEventSerializer < DataRequestEventSerializer
9
+ typelize email: :string, confirmation_token: :string, store_id: :string,
10
+ redirect_url: [:string, optional: true]
11
+
12
+ attributes :email
13
+
14
+ attribute :confirmation_token do
15
+ params[:confirmation_token]
16
+ end
17
+
18
+ attribute :store_id do |data_request|
19
+ data_request.store.prefixed_id
20
+ end
21
+
22
+ attribute :redirect_url, if: proc { params[:redirect_url].present? } do
23
+ params[:redirect_url]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -11,7 +11,7 @@ module Spree
11
11
  # log. Subscribers get the fact that a request finished; fetching the
12
12
  # file stays an authenticated act by the person it belongs to.
13
13
  class DataRequestEventSerializer < BaseSerializer
14
- typelize number: :string, kind: [:string, enum: Spree::DataRequest::KINDS], status: [:string, enum: Spree::DataRequest.statuses, enum_type_name: 'DataRequestStatus'],
14
+ typelize number: :string, kind: [:string, enum: Spree::DataRequest::KINDS, enum_type_name: 'DataRequestKind'], status: [:string, enum: Spree::DataRequest.statuses, enum_type_name: 'DataRequestStatus'],
15
15
  requested_at: [:string, nullable: true],
16
16
  completed_at: [:string, nullable: true]
17
17
 
@@ -4,7 +4,7 @@ module Spree
4
4
  # Store API Data Request Serializer
5
5
  # A customer's own GDPR access or erasure request.
6
6
  class DataRequestSerializer < BaseSerializer
7
- typelize number: :string, kind: [:string, enum: Spree::DataRequest::KINDS], status: [:string, enum: Spree::DataRequest.statuses, enum_type_name: 'DataRequestStatus'],
7
+ typelize number: :string, kind: [:string, enum: Spree::DataRequest::KINDS, enum_type_name: 'DataRequestKind'], status: [:string, enum: Spree::DataRequest.statuses, enum_type_name: 'DataRequestStatus'],
8
8
  requested_at: [:string, nullable: true],
9
9
  completed_at: [:string, nullable: true],
10
10
  expires_at: [:string, nullable: true],
@@ -25,11 +25,15 @@ module Spree
25
25
  end
26
26
 
27
27
  # Present only while the file is still there — a link the storefront
28
- # can render without having to reason about expiry itself.
29
- attribute :download_url do |data_request, params|
28
+ # can render without having to reason about expiry itself — and only
29
+ # to a caller holding the confirmation token, never to the signed-in
30
+ # account, because the export covers guest orders placed at the address
31
+ # the request names.
32
+ attribute :download_url do |data_request|
33
+ next nil unless params[:download_url_visible]
30
34
  next nil unless data_request.downloadable?
31
35
 
32
- host = (params&.dig(:store) || data_request.store)&.formatted_url
36
+ host = (params[:store] || data_request.store)&.formatted_url
33
37
  next nil if host.blank?
34
38
 
35
39
  Spree::Api::DataRequestUrls.download_url(data_request, host)
@@ -10,7 +10,7 @@ module Spree
10
10
  carrier: [:string, nullable: true],
11
11
  carrier_name: [:string, nullable: true],
12
12
  service: [:string, nullable: true],
13
- status: [:string, enum: Spree::Delivery::STATUSES],
13
+ status: [:string, enum: Spree::Delivery::STATUSES, enum_type_name: 'DeliveryStatus'],
14
14
  estimated_delivery_at: [:string, nullable: true],
15
15
  delivered_at: [:string, nullable: true]
16
16
 
@@ -2,7 +2,7 @@ module Spree
2
2
  module Api
3
3
  module V3
4
4
  class DeliveryZoneMemberSerializer < BaseSerializer
5
- typelize member_type: [:string, enum: Spree::DeliveryZoneMember::MEMBER_TYPES], country_code: [:string, nullable: true, enum: Spree::IsoData.country_codes, enum_type_name: 'CountryCode'],
5
+ typelize member_type: [:string, enum: Spree::DeliveryZoneMember::MEMBER_TYPES, enum_type_name: 'DeliveryZoneMemberType'], country_code: [:string, nullable: true, enum: Spree::IsoData.country_codes, enum_type_name: 'CountryCode'],
6
6
  country_name: [:string, nullable: true],
7
7
  state_code: [:string, nullable: true], state_name: [:string, nullable: true],
8
8
  postal_code_prefix: [:string, nullable: true],
@@ -5,7 +5,7 @@ module Spree
5
5
  # line item or fulfillment. The embedded `discounts` key on Cart/Order
6
6
  # stays the applied-promotion summary — see AppliedPromotionSerializer.
7
7
  class DiscountSerializer < BaseSerializer
8
- typelize label: :string, kind: [:string, enum: Spree::Discount::KINDS], code: [:string, nullable: true],
8
+ typelize label: :string, kind: [:string, enum: Spree::Discount::KINDS, enum_type_name: 'DiscountKind'], code: [:string, nullable: true],
9
9
  value: [:string, nullable: true], value_type: [:string, nullable: true],
10
10
  promotion_id: [:string, nullable: true], line_item_id: [:string, nullable: true],
11
11
  fulfillment_id: [:string, nullable: true]
@@ -15,7 +15,7 @@ module Spree
15
15
  filename: :string,
16
16
  content_type: :string,
17
17
  byte_size: :number,
18
- visibility: [:string, enum: Spree::FileUpload::VISIBILITIES],
18
+ visibility: [:string, enum: Spree::FileUpload::VISIBILITIES, enum_type_name: 'FileUploadVisibility'],
19
19
  expires_at: :string,
20
20
  upload: [:FileUploadTarget, nullable: true]
21
21
 
@@ -4,7 +4,7 @@ module Spree
4
4
  class FulfillmentSerializer < BaseSerializer
5
5
  typelize number: :string,
6
6
  status: [:string, enum: Spree::Fulfillment.statuses, enum_type_name: 'FulfillmentStatus'],
7
- fulfillment_type: [:string, enum: %w[shipping digital pickup pickup_point]],
7
+ fulfillment_type: [:string, enum: %w[shipping digital pickup pickup_point], enum_type_name: 'FulfillmentType'],
8
8
  tracking: [:string, nullable: true], tracking_url: [:string, nullable: true],
9
9
  fulfilled_at: [:string, nullable: true], delivered_at: [:string, nullable: true], unpriced: :boolean,
10
10
  items: 'Array<{ item_id: string; variant_id: string; quantity: number }>',
@@ -7,7 +7,7 @@ module Spree
7
7
  # `type` is not exposed: the old Spree::Image/Spree::Video STI collapsed
8
8
  # into one class, so the column is a legacy vestige with no subclasses
9
9
  # — `media_type` is the discriminator (see MediaSerializer).
10
- typelize media_type: [:string, enum: Spree::Media::MEDIA_TYPES], viewable_type: :string,
10
+ typelize media_type: [:string, enum: Spree::Media::MEDIA_TYPES, enum_type_name: 'MediaType'], viewable_type: :string,
11
11
  viewable_id: :string, position: [:number, nullable: true],
12
12
  alt: [:string, nullable: true]
13
13
 
@@ -5,7 +5,7 @@ module Spree
5
5
  typelize position: :number, alt: [:string, nullable: true],
6
6
  product_id: [:string, nullable: true],
7
7
  variant_ids: [:string, multi: true],
8
- media_type: [:string, enum: Spree::Media::MEDIA_TYPES],
8
+ media_type: [:string, enum: Spree::Media::MEDIA_TYPES, enum_type_name: 'MediaType'],
9
9
  focal_point_x: [:number, nullable: true],
10
10
  focal_point_y: [:number, nullable: true],
11
11
  external_video_url: [:string, nullable: true],
@@ -2,7 +2,7 @@ module Spree
2
2
  module Api
3
3
  module V3
4
4
  class OptionTypeSerializer < BaseSerializer
5
- typelize name: :string, label: :string, position: :number, kind: [:string, enum: Spree::OptionType::KINDS]
5
+ typelize name: :string, label: :string, position: :number, kind: [:string, enum: Spree::OptionType::KINDS, enum_type_name: 'OptionTypeKind']
6
6
 
7
7
  attributes :name, :label, :position, :kind
8
8
  end
@@ -9,8 +9,8 @@ module Spree
9
9
  # inside it, each carrying its own items, delivery and tracking.
10
10
  class OrderGroupSerializer < BaseSerializer
11
11
  typelize number: :string, email: [:string, nullable: true], currency: [:string, enum: Spree::Money::Rounding::CURRENCY_CODES, enum_type_name: 'CurrencyCode'],
12
- fulfillment_status: [:string, nullable: true, enum: Spree::Order::FULFILLMENT_STATUSES],
13
- payment_status: [:string, nullable: true, enum: Spree::Order::PAYMENT_STATUSES],
12
+ fulfillment_status: [:string, nullable: true, enum: Spree::Order::FULFILLMENT_STATUSES, enum_type_name: 'OrderFulfillmentStatus'],
13
+ payment_status: [:string, nullable: true, enum: Spree::Order::PAYMENT_STATUSES, enum_type_name: 'OrderPaymentStatus'],
14
14
  completed_at: [:string, nullable: true],
15
15
  billing_address: { nullable: true }, shipping_address: { nullable: true }
16
16
 
@@ -11,10 +11,11 @@ module Spree
11
11
  po_document_filename: [:string, nullable: true], po_document_byte_size: ['number | null'],
12
12
  currency: [:string, enum: Spree::Money::Rounding::CURRENCY_CODES, enum_type_name: 'CurrencyCode'], locale: [:string, nullable: true, enum: Spree::Locales::ALL, enum_type_name: 'LocaleCode'], total_quantity: :number,
13
13
  coupon_code: [:string, nullable: true],
14
- fulfillment_status: [:string, nullable: true, enum: Spree::Order::FULFILLMENT_STATUSES],
15
- payment_status: [:string, nullable: true, enum: Spree::Order::PAYMENT_STATUSES],
14
+ fulfillment_status: [:string, nullable: true, enum: Spree::Order::FULFILLMENT_STATUSES, enum_type_name: 'OrderFulfillmentStatus'],
15
+ payment_status: [:string, nullable: true, enum: Spree::Order::PAYMENT_STATUSES, enum_type_name: 'OrderPaymentStatus'],
16
16
  completed_at: [:string, nullable: true],
17
17
  withdrawal_period_ends_at: [:string, nullable: true], within_withdrawal_period: :boolean,
18
+ shipping_eq_billing_address: :boolean,
18
19
  billing_address: { nullable: true }, shipping_address: { nullable: true },
19
20
  gift_card: { nullable: true }, market: { nullable: true }
20
21
 
@@ -28,6 +29,7 @@ module Spree
28
29
  end
29
30
 
30
31
  attribute :within_withdrawal_period, &:within_withdrawal_period?
32
+ attribute :shipping_eq_billing_address, &:shipping_eq_billing_address?
31
33
 
32
34
  # The checkout handle this order was born from (nil for admin drafts).
33
35
  # Lets abandonment tooling match cart.* events to the conversion.
@@ -4,7 +4,7 @@ module Spree
4
4
  class PaymentSerializer < BaseSerializer
5
5
  typelize status: [:string, enum: Spree::Payment.statuses, enum_type_name: 'PaymentStatus'],
6
6
  payment_method_id: :string, response_code: [:string, nullable: true], number: :string,
7
- source_type: [:string, nullable: true, enum: %w[credit_card store_credit payment_source]],
7
+ source_type: [:string, nullable: true, enum: %w[credit_card store_credit payment_source], enum_type_name: 'PaymentSourceType'],
8
8
  source_id: [:string, nullable: true], source: 'CreditCard | StoreCredit | PaymentSource | null'
9
9
 
10
10
  prefixed_id_attributes :payment_method
@@ -2,7 +2,7 @@ module Spree
2
2
  module Api
3
3
  module V3
4
4
  class ProductFilterAvailabilityOptionSerializer < BaseSerializer
5
- typelize id: [:string, enum: %w[in_stock out_of_stock]], count: :number
5
+ typelize id: [:string, enum: %w[in_stock out_of_stock], enum_type_name: 'StockAvailability'], count: :number
6
6
 
7
7
  attributes :id, :count
8
8
  end
@@ -6,7 +6,7 @@ module Spree
6
6
  type: "'option'",
7
7
  name: :string,
8
8
  label: :string,
9
- kind: [:string, enum: Spree::OptionType::KINDS],
9
+ kind: [:string, enum: Spree::OptionType::KINDS, enum_type_name: 'OptionTypeKind'],
10
10
  options: [:ProductFilterOptionValue, multi: true]
11
11
 
12
12
  attributes :id, :type, :name, :label, :kind
@@ -3,8 +3,9 @@ module Spree
3
3
  module V3
4
4
  module Seller
5
5
  class AddressSerializer < V3::AddressSerializer
6
- # The Store API keeps these deprecated names until 6.1.
7
- %i[state_abbr country_iso].each { |key| _attributes.delete(key) }
6
+ # The Store API keeps these deprecated names until 6.1. A seller
7
+ # keeps no address book of the buyer's, so has no owner to read.
8
+ %i[state_abbr country_iso owner].each { |key| _attributes.delete(key) }
8
9
  end
9
10
  end
10
11
  end
@@ -23,9 +23,9 @@ module Spree
23
23
  class OrderSerializer < V3::BaseSerializer
24
24
  typelize number: :string, customer_note: [:string, nullable: true], currency: [:string, enum: Spree::Money::Rounding::CURRENCY_CODES, enum_type_name: 'CurrencyCode'],
25
25
  total_quantity: :number,
26
- fulfillment_status: [:string, nullable: true, enum: Spree::Order::FULFILLMENT_STATUSES],
27
- payment_status: [:string, nullable: true, enum: Spree::Order::PAYMENT_STATUSES],
28
- status: [:string, enum: Spree::Order::STATUSES], canceled_at: [:string, nullable: true],
26
+ fulfillment_status: [:string, nullable: true, enum: Spree::Order::FULFILLMENT_STATUSES, enum_type_name: 'OrderFulfillmentStatus'],
27
+ payment_status: [:string, nullable: true, enum: Spree::Order::PAYMENT_STATUSES, enum_type_name: 'OrderPaymentStatus'],
28
+ status: [:string, enum: Spree::Order::STATUSES, enum_type_name: 'OrderStatus'], canceled_at: [:string, nullable: true],
29
29
  cancel_reason_name: [:string, nullable: true], cancel_note: [:string, nullable: true],
30
30
  internal_note: [:string, nullable: true], internal_note_html: [:string, nullable: true]
31
31
 
@@ -15,14 +15,14 @@ module Spree
15
15
  # refuses to write them (docs/plans/6.0-seller-package-types.md).
16
16
  class PackageTypeSerializer < V3::BaseSerializer
17
17
  typelize name: :string,
18
- kind: [:string, enum: Spree::PackageType::KINDS],
18
+ kind: [:string, enum: Spree::PackageType::KINDS, enum_type_name: 'PackageTypeKind'],
19
19
  length: [:string, nullable: true],
20
20
  width: [:string, nullable: true],
21
21
  height: [:string, nullable: true],
22
- dimensions_unit: [:string, enum: Spree::Variant::DIMENSION_UNITS],
22
+ dimensions_unit: [:string, enum: Spree::Variant::DIMENSION_UNITS, enum_type_name: 'DimensionUnit'],
23
23
  weight: [:string, nullable: true],
24
24
  max_weight: [:string, nullable: true],
25
- weight_unit: [:string, enum: Spree::Variant::WEIGHT_UNITS],
25
+ weight_unit: [:string, enum: Spree::Variant::WEIGHT_UNITS, enum_type_name: 'WeightUnit'],
26
26
  volume: [:string, nullable: true],
27
27
  default: :boolean,
28
28
  editable: :boolean,
@@ -8,8 +8,8 @@ module Spree
8
8
  registration_number: [:string, nullable: true],
9
9
  contact_email: [:string, nullable: true],
10
10
  billing_email: [:string, nullable: true],
11
- tax_remittance: :string,
12
- payouts_schedule_interval: [:string, nullable: true],
11
+ tax_remittance: [:string, enum: Spree::Seller::TAX_REMITTANCES, enum_type_name: 'TaxRemittance'],
12
+ payouts_schedule_interval: [:string, nullable: true, enum: Spree::Seller::PAYOUT_INTERVALS, enum_type_name: 'PayoutScheduleInterval'],
13
13
  minimum_payout_amount: [:string, nullable: true],
14
14
  holiday_mode_until: [:string, nullable: true],
15
15
  terms_accepted_at: [:string, nullable: true],
@@ -8,7 +8,7 @@ module Spree
8
8
 
9
9
  typelize id: :string, kind: [:string, comment: 'Requirement kind. Built-in: accept_terms, complete_profile, billing_address, returns_address, delivery_method, package_type, minimum_products, payout_account, required_custom_fields, policy, attestation, operator_review, document. Extensions may register more.'], name: :string,
10
10
  description: [:string, nullable: true],
11
- required: :boolean, position: :number, status: [:string, enum: Spree::SellerRequirementStatus::STATUSES],
11
+ required: :boolean, position: :number, status: [:string, enum: Spree::SellerRequirementStatus::STATUSES, enum_type_name: 'SellerRequirementState'],
12
12
  blocking: :boolean, action_url: [:string, nullable: true],
13
13
  blocker: ['{ state: string; message: string | null } | null'],
14
14
  accepts_submissions: :boolean, requires_file: :boolean,
@@ -4,7 +4,7 @@ module Spree
4
4
  module Seller
5
5
  class TaxIdentifierSerializer < V3::BaseSerializer
6
6
  typelize kind: [:string, comment: 'Tax identifier kind, keyed to a registered validator. Built-in: eu_vat. Extensions may register more.'], value: :string,
7
- validation_status: [:string, nullable: true, enum: Spree::TaxIdentifier::VALIDATION_STATUSES],
7
+ validation_status: [:string, nullable: true, enum: Spree::TaxIdentifier::VALIDATION_STATUSES, enum_type_name: 'TaxIdentifierValidationStatus'],
8
8
  validated_at: [:string, nullable: true]
9
9
 
10
10
  attributes :kind, :value, :validation_status, validated_at: :iso8601
@@ -25,7 +25,7 @@ module Spree
25
25
  country_of_origin: [:string, nullable: true, enum: Spree::IsoData.country_codes, enum_type_name: 'CountryCode'],
26
26
  customs_description: [:string, nullable: true],
27
27
  minimum_order_quantity: ['number | null'], order_multiple: ['number | null'],
28
- purchase_unit: [:string, nullable: true, enum: Spree::Variant::PURCHASE_UNITS],
28
+ purchase_unit: [:string, nullable: true, enum: Spree::Variant::PURCHASE_UNITS, enum_type_name: 'PurchaseUnit'],
29
29
  metadata: 'Record<string, unknown>'
30
30
 
31
31
  attributes :metadata, :position, :cost_currency,
@@ -6,7 +6,7 @@ module Spree
6
6
  # stay behind the Admin API.
7
7
  class ShippingLabelEventSerializer < BaseSerializer
8
8
  typelize owner_id: :string,
9
- owner_type: [:string, enum: %w[fulfillment return]],
9
+ owner_type: [:string, enum: %w[fulfillment return], enum_type_name: 'ShippingLabelOwnerType'],
10
10
  status: [:string, enum: Spree::ShippingLabel.statuses, enum_type_name: 'ShippingLabelStatus'],
11
11
  carrier: [:string, nullable: true],
12
12
  service: [:string, nullable: true],
@@ -7,7 +7,7 @@ module Spree
7
7
  # `kind` is nullable for the lifetime of 6.0: a legacy row on an
8
8
  # install that has not run spree:migrate_stock_movements_to_typed_rows
9
9
  # has no kind yet.
10
- typelize quantity: :number, kind: [:string, nullable: true, enum: Spree::StockMovement::KINDS],
10
+ typelize quantity: :number, kind: [:string, nullable: true, enum: Spree::StockMovement::KINDS, enum_type_name: 'StockMovementKind'],
11
11
  reason: [:string, nullable: true], stock_level_id: [:string, nullable: true]
12
12
 
13
13
  attributes :quantity, :kind, :reason,
@@ -6,7 +6,7 @@ module Spree
6
6
  class StockReceiptEventSerializer < BaseSerializer
7
7
  typelize number: :string,
8
8
  received_at: :string,
9
- receivable_type: [:string, enum: %w[purchase_order stock_transfer]],
9
+ receivable_type: [:string, enum: %w[purchase_order stock_transfer], enum_type_name: 'StockReceiptReceivableType'],
10
10
  receivable_id: :string,
11
11
  quantity_accepted_total: :number,
12
12
  quantity_rejected_total: :number
@@ -10,11 +10,11 @@ module Spree
10
10
  purchasable: :boolean, in_stock: :boolean, backorderable: :boolean, preorder: :boolean,
11
11
  preorder_ships_at: [:string, nullable: true],
12
12
  weight: [:number, nullable: true], height: [:number, nullable: true], width: [:number, nullable: true], depth: [:number, nullable: true],
13
- weight_unit: [:string, enum: Spree::Variant::WEIGHT_UNITS], dimensions_unit: [:string, enum: Spree::Variant::DIMENSION_UNITS],
13
+ weight_unit: [:string, enum: Spree::Variant::WEIGHT_UNITS, enum_type_name: 'WeightUnit'], dimensions_unit: [:string, enum: Spree::Variant::DIMENSION_UNITS, enum_type_name: 'DimensionUnit'],
14
14
  price: 'Price',
15
15
  original_price: ['Price', nullable: true],
16
16
  minimum_order_quantity: :number, order_multiple: :number,
17
- purchase_unit: [:string, enum: Spree::Variant::PURCHASE_UNITS], units_per_carton: ['number | null'],
17
+ purchase_unit: [:string, enum: Spree::Variant::PURCHASE_UNITS, enum_type_name: 'PurchaseUnit'], units_per_carton: ['number | null'],
18
18
  seller_id: [:string, nullable: true]
19
19
 
20
20
  prefixed_id_attributes :product