spree_api 6.0.0.rc1 → 6.0.0.rc3

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 (371) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +0 -17
  3. data/app/controllers/concerns/spree/api/v3/admin/product_membership.rb +10 -0
  4. data/app/controllers/concerns/spree/api/v3/admin/stock_receipt_actions.rb +26 -4
  5. data/app/controllers/concerns/spree/api/v3/admin/subclassed_resource.rb +14 -28
  6. data/app/controllers/concerns/spree/api/v3/admin/validation_details.rb +1 -1
  7. data/app/controllers/concerns/spree/api/v3/cart_resolvable.rb +3 -2
  8. data/app/controllers/concerns/spree/api/v3/channel_resolution.rb +1 -1
  9. data/app/controllers/concerns/spree/api/v3/company_address_writes.rb +3 -1
  10. data/app/controllers/concerns/spree/api/v3/error_handler.rb +48 -2
  11. data/app/controllers/concerns/spree/api/v3/file_uploads.rb +35 -0
  12. data/app/controllers/concerns/spree/api/v3/money_params.rb +52 -0
  13. data/app/controllers/concerns/spree/api/v3/orders/claim_actions.rb +1 -5
  14. data/app/controllers/concerns/spree/api/v3/orders/delivery_actions.rb +6 -6
  15. data/app/controllers/concerns/spree/api/v3/orders/exchange_actions.rb +1 -4
  16. data/app/controllers/concerns/spree/api/v3/orders/fulfillment_actions.rb +1 -5
  17. data/app/controllers/concerns/spree/api/v3/orders/return_actions.rb +1 -4
  18. data/app/controllers/concerns/spree/api/v3/orders/shipping_label_actions.rb +5 -21
  19. data/app/controllers/concerns/spree/api/v3/params_normalizer.rb +5 -0
  20. data/app/controllers/concerns/spree/api/v3/purchase_serialization.rb +26 -0
  21. data/app/controllers/concerns/spree/api/v3/request_params.rb +65 -0
  22. data/app/controllers/concerns/spree/api/v3/store/search_provider_support.rb +8 -3
  23. data/app/controllers/concerns/spree/api/v3/strict_money_params.rb +94 -0
  24. data/app/controllers/spree/api/v3/admin/admin_users_controller.rb +10 -1
  25. data/app/controllers/spree/api/v3/admin/allowed_origins_controller.rb +8 -1
  26. data/app/controllers/spree/api/v3/admin/api_keys_controller.rb +15 -7
  27. data/app/controllers/spree/api/v3/admin/base_controller.rb +1 -0
  28. data/app/controllers/spree/api/v3/admin/catalogs/order_minimums_controller.rb +3 -1
  29. data/app/controllers/spree/api/v3/admin/catalogs/quantity_rules_controller.rb +7 -5
  30. data/app/controllers/spree/api/v3/admin/catalogs_controller.rb +60 -33
  31. data/app/controllers/spree/api/v3/admin/categories/products_controller.rb +4 -1
  32. data/app/controllers/spree/api/v3/admin/categories_controller.rb +40 -20
  33. data/app/controllers/spree/api/v3/admin/channels_controller.rb +39 -6
  34. data/app/controllers/spree/api/v3/admin/claim_reasons_controller.rb +5 -1
  35. data/app/controllers/spree/api/v3/admin/collections/products_controller.rb +4 -1
  36. data/app/controllers/spree/api/v3/admin/collections_controller.rb +44 -15
  37. data/app/controllers/spree/api/v3/admin/commission_rates_controller.rb +60 -12
  38. data/app/controllers/spree/api/v3/admin/companies/addresses_controller.rb +11 -0
  39. data/app/controllers/spree/api/v3/admin/companies/memberships_controller.rb +9 -1
  40. data/app/controllers/spree/api/v3/admin/companies/tax_exemption_certificates_controller.rb +19 -18
  41. data/app/controllers/spree/api/v3/admin/companies/tax_identifiers_controller.rb +6 -1
  42. data/app/controllers/spree/api/v3/admin/companies_controller.rb +17 -3
  43. data/app/controllers/spree/api/v3/admin/countries_controller.rb +5 -1
  44. data/app/controllers/spree/api/v3/admin/custom_field_definitions_controller.rb +21 -11
  45. data/app/controllers/spree/api/v3/admin/custom_fields_controller.rb +12 -5
  46. data/app/controllers/spree/api/v3/admin/customer_groups_controller.rb +9 -1
  47. data/app/controllers/spree/api/v3/admin/customers/addresses_controller.rb +12 -7
  48. data/app/controllers/spree/api/v3/admin/customers/store_credits_controller.rb +11 -9
  49. data/app/controllers/spree/api/v3/admin/customers/tax_identifiers_controller.rb +6 -1
  50. data/app/controllers/spree/api/v3/admin/customers_controller.rb +35 -7
  51. data/app/controllers/spree/api/v3/admin/data_requests_controller.rb +8 -0
  52. data/app/controllers/spree/api/v3/admin/delivery_method_rules_controller.rb +1 -1
  53. data/app/controllers/spree/api/v3/admin/delivery_methods/rules_controller.rb +15 -7
  54. data/app/controllers/spree/api/v3/admin/delivery_methods_controller.rb +92 -57
  55. data/app/controllers/spree/api/v3/admin/delivery_profiles/origin_groups_controller.rb +8 -1
  56. data/app/controllers/spree/api/v3/admin/delivery_profiles_controller.rb +13 -1
  57. data/app/controllers/spree/api/v3/admin/delivery_zones_controller.rb +21 -5
  58. data/app/controllers/spree/api/v3/admin/email_templates/drafts_controller.rb +14 -1
  59. data/app/controllers/spree/api/v3/admin/email_templates/previews_controller.rb +4 -0
  60. data/app/controllers/spree/api/v3/admin/email_templates/publications_controller.rb +7 -0
  61. data/app/controllers/spree/api/v3/admin/email_templates/restorations_controller.rb +8 -0
  62. data/app/controllers/spree/api/v3/admin/email_templates/test_emails_controller.rb +4 -0
  63. data/app/controllers/spree/api/v3/admin/exports_controller.rb +23 -6
  64. data/app/controllers/spree/api/v3/admin/files_controller.rb +29 -0
  65. data/app/controllers/spree/api/v3/admin/gift_card_batches_controller.rb +20 -1
  66. data/app/controllers/spree/api/v3/admin/gift_cards_controller.rb +19 -1
  67. data/app/controllers/spree/api/v3/admin/imports_controller.rb +36 -9
  68. data/app/controllers/spree/api/v3/admin/integrations_controller.rb +13 -1
  69. data/app/controllers/spree/api/v3/admin/invitation_acceptances_controller.rb +9 -2
  70. data/app/controllers/spree/api/v3/admin/invitations_controller.rb +8 -1
  71. data/app/controllers/spree/api/v3/admin/locales_controller.rb +1 -1
  72. data/app/controllers/spree/api/v3/admin/markets_controller.rb +30 -9
  73. data/app/controllers/spree/api/v3/admin/me_controller.rb +12 -3
  74. data/app/controllers/spree/api/v3/admin/media_controller.rb +22 -9
  75. data/app/controllers/spree/api/v3/admin/media_library_controller.rb +4 -0
  76. data/app/controllers/spree/api/v3/admin/option_types_controller.rb +24 -7
  77. data/app/controllers/spree/api/v3/admin/order_cancellation_reasons_controller.rb +5 -1
  78. data/app/controllers/spree/api/v3/admin/order_routing_rules_controller.rb +13 -1
  79. data/app/controllers/spree/api/v3/admin/orders/claims_controller.rb +39 -1
  80. data/app/controllers/spree/api/v3/admin/orders/deliveries_controller.rb +19 -1
  81. data/app/controllers/spree/api/v3/admin/orders/discount_codes_controller.rb +5 -1
  82. data/app/controllers/spree/api/v3/admin/orders/discounts_controller.rb +29 -5
  83. data/app/controllers/spree/api/v3/admin/orders/exchanges_controller.rb +36 -1
  84. data/app/controllers/spree/api/v3/admin/orders/fees_controller.rb +22 -1
  85. data/app/controllers/spree/api/v3/admin/orders/fulfillments_controller.rb +66 -7
  86. data/app/controllers/spree/api/v3/admin/orders/gift_cards_controller.rb +6 -2
  87. data/app/controllers/spree/api/v3/admin/orders/items_controller.rb +11 -1
  88. data/app/controllers/spree/api/v3/admin/orders/labels_controller.rb +35 -2
  89. data/app/controllers/spree/api/v3/admin/orders/payments_controller.rb +16 -5
  90. data/app/controllers/spree/api/v3/admin/orders/refunds_controller.rb +6 -0
  91. data/app/controllers/spree/api/v3/admin/orders/returns_controller.rb +37 -1
  92. data/app/controllers/spree/api/v3/admin/orders/store_credits_controller.rb +6 -3
  93. data/app/controllers/spree/api/v3/admin/orders_controller.rb +74 -60
  94. data/app/controllers/spree/api/v3/admin/package_types_controller.rb +30 -5
  95. data/app/controllers/spree/api/v3/admin/password_resets_controller.rb +17 -0
  96. data/app/controllers/spree/api/v3/admin/payment_methods_controller.rb +38 -22
  97. data/app/controllers/spree/api/v3/admin/policies_controller.rb +8 -4
  98. data/app/controllers/spree/api/v3/admin/price_lists_controller.rb +27 -15
  99. data/app/controllers/spree/api/v3/admin/prices_controller.rb +51 -4
  100. data/app/controllers/spree/api/v3/admin/product_types_controller.rb +25 -8
  101. data/app/controllers/spree/api/v3/admin/products/digital_assets_controller.rb +27 -10
  102. data/app/controllers/spree/api/v3/admin/products/variants_controller.rb +9 -14
  103. data/app/controllers/spree/api/v3/admin/products_controller.rb +121 -70
  104. data/app/controllers/spree/api/v3/admin/promotion_actions_controller.rb +11 -2
  105. data/app/controllers/spree/api/v3/admin/promotion_rules_controller.rb +10 -1
  106. data/app/controllers/spree/api/v3/admin/promotions_controller.rb +50 -14
  107. data/app/controllers/spree/api/v3/admin/purchase_orders_controller.rb +54 -7
  108. data/app/controllers/spree/api/v3/admin/refund_reasons_controller.rb +5 -1
  109. data/app/controllers/spree/api/v3/admin/reporting/saved_reports_controller.rb +9 -1
  110. data/app/controllers/spree/api/v3/admin/reporting_controller.rb +34 -0
  111. data/app/controllers/spree/api/v3/admin/resource_controller.rb +1 -0
  112. data/app/controllers/spree/api/v3/admin/return_reasons_controller.rb +5 -1
  113. data/app/controllers/spree/api/v3/admin/roles_controller.rb +13 -1
  114. data/app/controllers/spree/api/v3/admin/seller_payouts_controller.rb +6 -0
  115. data/app/controllers/spree/api/v3/admin/seller_requirement_submissions_controller.rb +15 -0
  116. data/app/controllers/spree/api/v3/admin/seller_requirements_controller.rb +32 -6
  117. data/app/controllers/spree/api/v3/admin/sellers_controller.rb +75 -22
  118. data/app/controllers/spree/api/v3/admin/setup_controller.rb +54 -15
  119. data/app/controllers/spree/api/v3/admin/stock_levels_controller.rb +38 -9
  120. data/app/controllers/spree/api/v3/admin/stock_locations_controller.rb +41 -9
  121. data/app/controllers/spree/api/v3/admin/stock_transfers_controller.rb +48 -7
  122. data/app/controllers/spree/api/v3/admin/store_controller.rb +122 -53
  123. data/app/controllers/spree/api/v3/admin/store_credits_controller.rb +1 -7
  124. data/app/controllers/spree/api/v3/admin/suppliers_controller.rb +16 -6
  125. data/app/controllers/spree/api/v3/admin/tax_categories_controller.rb +7 -4
  126. data/app/controllers/spree/api/v3/admin/tax_rates_controller.rb +21 -3
  127. data/app/controllers/spree/api/v3/admin/tracking_carriers_controller.rb +4 -0
  128. data/app/controllers/spree/api/v3/admin/translations/batches_controller.rb +17 -9
  129. data/app/controllers/spree/api/v3/admin/translations_controller.rb +4 -0
  130. data/app/controllers/spree/api/v3/admin/translations_coverage_controller.rb +5 -0
  131. data/app/controllers/spree/api/v3/admin/updates_controller.rb +39 -0
  132. data/app/controllers/spree/api/v3/admin/webhook_endpoints_controller.rb +23 -1
  133. data/app/controllers/spree/api/v3/base_controller.rb +2 -0
  134. data/app/controllers/spree/api/v3/resource_controller.rb +63 -10
  135. data/app/controllers/spree/api/v3/seller/auth_controller.rb +8 -0
  136. data/app/controllers/spree/api/v3/seller/base_controller.rb +1 -0
  137. data/app/controllers/spree/api/v3/seller/countries_controller.rb +2 -2
  138. data/app/controllers/spree/api/v3/seller/delivery_methods_controller.rb +49 -60
  139. data/app/controllers/spree/api/v3/seller/exports_controller.rb +18 -1
  140. data/app/controllers/spree/api/v3/seller/files_controller.rb +27 -0
  141. data/app/controllers/spree/api/v3/seller/imports_controller.rb +25 -9
  142. data/app/controllers/spree/api/v3/seller/invitation_acceptances_controller.rb +9 -1
  143. data/app/controllers/spree/api/v3/seller/me_controller.rb +14 -3
  144. data/app/controllers/spree/api/v3/seller/onboarding_controller.rb +11 -0
  145. data/app/controllers/spree/api/v3/seller/orders/claims_controller.rb +35 -0
  146. data/app/controllers/spree/api/v3/seller/orders/deliveries_controller.rb +12 -0
  147. data/app/controllers/spree/api/v3/seller/orders/exchanges_controller.rb +33 -0
  148. data/app/controllers/spree/api/v3/seller/orders/fulfillments_controller.rb +28 -2
  149. data/app/controllers/spree/api/v3/seller/orders/labels_controller.rb +22 -1
  150. data/app/controllers/spree/api/v3/seller/orders/notes_controller.rb +8 -1
  151. data/app/controllers/spree/api/v3/seller/orders/returns_controller.rb +32 -0
  152. data/app/controllers/spree/api/v3/seller/orders_controller.rb +35 -14
  153. data/app/controllers/spree/api/v3/seller/package_types_controller.rb +21 -9
  154. data/app/controllers/spree/api/v3/seller/password_resets_controller.rb +10 -0
  155. data/app/controllers/spree/api/v3/seller/policies_controller.rb +12 -4
  156. data/app/controllers/spree/api/v3/seller/products_controller.rb +114 -28
  157. data/app/controllers/spree/api/v3/seller/profile_controller.rb +33 -10
  158. data/app/controllers/spree/api/v3/seller/requirement_submissions_controller.rb +12 -5
  159. data/app/controllers/spree/api/v3/seller/resource_controller.rb +12 -0
  160. data/app/controllers/spree/api/v3/seller/stock_locations_controller.rb +44 -13
  161. data/app/controllers/spree/api/v3/seller/tax_identifiers_controller.rb +10 -1
  162. data/app/controllers/spree/api/v3/seller/team_controller.rb +4 -0
  163. data/app/controllers/spree/api/v3/store/auth_controller.rb +10 -0
  164. data/app/controllers/spree/api/v3/store/carts/discount_codes_controller.rb +8 -1
  165. data/app/controllers/spree/api/v3/store/carts/fulfillments_controller.rb +10 -2
  166. data/app/controllers/spree/api/v3/store/carts/gift_cards_controller.rb +8 -1
  167. data/app/controllers/spree/api/v3/store/carts/items_controller.rb +17 -5
  168. data/app/controllers/spree/api/v3/store/carts/payment_sessions_controller.rb +20 -6
  169. data/app/controllers/spree/api/v3/store/carts/payments_controller.rb +11 -0
  170. data/app/controllers/spree/api/v3/store/carts/po_documents_controller.rb +24 -26
  171. data/app/controllers/spree/api/v3/store/carts/store_credits_controller.rb +19 -1
  172. data/app/controllers/spree/api/v3/store/carts/tax_identifiers_controller.rb +5 -1
  173. data/app/controllers/spree/api/v3/store/carts_controller.rb +54 -47
  174. data/app/controllers/spree/api/v3/store/companies/addresses_controller.rb +12 -4
  175. data/app/controllers/spree/api/v3/store/companies/members_controller.rb +4 -0
  176. data/app/controllers/spree/api/v3/store/companies_controller.rb +5 -1
  177. data/app/controllers/spree/api/v3/store/company_invitations_controller.rb +9 -1
  178. data/app/controllers/spree/api/v3/store/customer/addresses_controller.rb +8 -7
  179. data/app/controllers/spree/api/v3/store/customer/data_requests_controller.rb +5 -0
  180. data/app/controllers/spree/api/v3/store/customer/password_resets_controller.rb +17 -0
  181. data/app/controllers/spree/api/v3/store/customer/payment_setup_sessions_controller.rb +10 -5
  182. data/app/controllers/spree/api/v3/store/customer/tax_identifiers_controller.rb +5 -1
  183. data/app/controllers/spree/api/v3/store/customers_controller.rb +28 -2
  184. data/app/controllers/spree/api/v3/store/newsletter_subscribers_controller.rb +22 -0
  185. data/app/controllers/spree/api/v3/store/orders/claims_controller.rb +11 -2
  186. data/app/controllers/spree/api/v3/store/orders/returns_controller.rb +10 -1
  187. data/app/controllers/spree/api/v3/store/orders_controller.rb +47 -10
  188. data/app/controllers/spree/api/v3/store/products_controller.rb +8 -0
  189. data/app/controllers/spree/api/v3/store/wishlist_items_controller.rb +10 -4
  190. data/app/controllers/spree/api/v3/store/wishlists_controller.rb +12 -4
  191. data/app/request_schemas/spree/api/v3/address_request_schema.rb +27 -0
  192. data/app/request_schemas/spree/api/v3/admin/address_request_schema.rb +13 -0
  193. data/app/request_schemas/spree/api/v3/admin/bulk_tags_request_schema.rb +17 -0
  194. data/app/request_schemas/spree/api/v3/admin/catalog_order_minimum_request_schema.rb +17 -0
  195. data/app/request_schemas/spree/api/v3/admin/email_template_language_request_schema.rb +17 -0
  196. data/app/request_schemas/spree/api/v3/admin/email_template_preview_request_schema.rb +32 -0
  197. data/app/request_schemas/spree/api/v3/admin/price_adjustment_tier_request_schema.rb +21 -0
  198. data/app/request_schemas/spree/api/v3/admin/price_list_price_request_schema.rb +21 -0
  199. data/app/request_schemas/spree/api/v3/admin/price_list_rule_request_schema.rb +19 -0
  200. data/app/request_schemas/spree/api/v3/admin/product_membership_reposition_request_schema.rb +17 -0
  201. data/app/request_schemas/spree/api/v3/admin/promotion_action_calculator_request_schema.rb +18 -0
  202. data/app/request_schemas/spree/api/v3/admin/promotion_action_line_item_request_schema.rb +15 -0
  203. data/app/request_schemas/spree/api/v3/admin/promotion_action_request_schema.rb +19 -0
  204. data/app/request_schemas/spree/api/v3/admin/promotion_rule_request_schema.rb +22 -0
  205. data/app/request_schemas/spree/api/v3/admin/reason_request_schema.rb +14 -0
  206. data/app/request_schemas/spree/api/v3/admin/tax_identifier_request_schema.rb +15 -0
  207. data/app/request_schemas/spree/api/v3/custom_field_value_request_schema.rb +15 -0
  208. data/app/request_schemas/spree/api/v3/file_upload_request_schema.rb +20 -0
  209. data/app/request_schemas/spree/api/v3/media_request_schema.rb +27 -0
  210. data/app/request_schemas/spree/api/v3/price_request_schema.rb +17 -0
  211. data/app/request_schemas/spree/api/v3/stock_level_request_schema.rb +18 -0
  212. data/app/request_schemas/spree/api/v3/store/address_request_schema.rb +31 -0
  213. data/app/request_schemas/spree/api/v3/tax_identifier_request_schema.rb +18 -0
  214. data/app/request_schemas/spree/api/v3/variant_option_request_schema.rb +13 -0
  215. data/app/request_schemas/spree/api/v3/variant_request_schema.rb +52 -0
  216. data/app/serializers/concerns/spree/api/v3/purchase_summary.rb +48 -0
  217. data/app/serializers/spree/api/v3/address_serializer.rb +4 -2
  218. data/app/serializers/spree/api/v3/admin/address_serializer.rb +3 -0
  219. data/app/serializers/spree/api/v3/admin/applied_promotion_serializer.rb +3 -1
  220. data/app/serializers/spree/api/v3/admin/cart_serializer.rb +3 -1
  221. data/app/serializers/spree/api/v3/admin/catalog_order_minimum_serializer.rb +3 -2
  222. data/app/serializers/spree/api/v3/admin/catalog_price_serializer.rb +3 -4
  223. data/app/serializers/spree/api/v3/admin/catalog_price_tier_serializer.rb +4 -3
  224. data/app/serializers/spree/api/v3/admin/channel_serializer.rb +5 -7
  225. data/app/serializers/spree/api/v3/admin/claim_line_item_serializer.rb +7 -0
  226. data/app/serializers/spree/api/v3/admin/claim_serializer.rb +5 -0
  227. data/app/serializers/spree/api/v3/admin/commission_line_serializer.rb +7 -18
  228. data/app/serializers/spree/api/v3/admin/commission_rate_serializer.rb +7 -11
  229. data/app/serializers/spree/api/v3/admin/commission_rule_serializer.rb +0 -2
  230. data/app/serializers/spree/api/v3/admin/country_serializer.rb +4 -0
  231. data/app/serializers/spree/api/v3/admin/customer_serializer.rb +3 -7
  232. data/app/serializers/spree/api/v3/admin/delivery_method_rule_serializer.rb +0 -2
  233. data/app/serializers/spree/api/v3/admin/delivery_method_serializer.rb +14 -10
  234. data/app/serializers/spree/api/v3/admin/delivery_method_service_serializer.rb +9 -1
  235. data/app/serializers/spree/api/v3/admin/delivery_rate_serializer.rb +8 -0
  236. data/app/serializers/spree/api/v3/admin/discount_serializer.rb +3 -1
  237. data/app/serializers/spree/api/v3/admin/exchange_line_item_serializer.rb +5 -0
  238. data/app/serializers/spree/api/v3/admin/exchange_serializer.rb +5 -0
  239. data/app/serializers/spree/api/v3/admin/fee_serializer.rb +3 -1
  240. data/app/serializers/spree/api/v3/admin/file_upload_serializer.rb +11 -0
  241. data/app/serializers/spree/api/v3/admin/file_upload_target_serializer.rb +10 -0
  242. data/app/serializers/spree/api/v3/admin/fulfillment_serializer.rb +13 -11
  243. data/app/serializers/spree/api/v3/admin/gift_card_serializer.rb +3 -3
  244. data/app/serializers/spree/api/v3/admin/import_serializer.rb +2 -2
  245. data/app/serializers/spree/api/v3/admin/integration_serializer.rb +1 -3
  246. data/app/serializers/spree/api/v3/admin/line_item_serializer.rb +12 -12
  247. data/app/serializers/spree/api/v3/admin/locale_serializer.rb +13 -0
  248. data/app/serializers/spree/api/v3/admin/market_serializer.rb +3 -0
  249. data/app/serializers/spree/api/v3/admin/media_serializer.rb +3 -2
  250. data/app/serializers/spree/api/v3/admin/order_group_serializer.rb +2 -0
  251. data/app/serializers/spree/api/v3/admin/order_routing_rule_serializer.rb +1 -3
  252. data/app/serializers/spree/api/v3/admin/order_serializer.rb +33 -33
  253. data/app/serializers/spree/api/v3/admin/payment_method_serializer.rb +1 -4
  254. data/app/serializers/spree/api/v3/admin/payment_serializer.rb +6 -3
  255. data/app/serializers/spree/api/v3/admin/payment_split_serializer.rb +8 -9
  256. data/app/serializers/spree/api/v3/admin/price_adjustment_tier_serializer.rb +3 -6
  257. data/app/serializers/spree/api/v3/admin/price_history_serializer.rb +6 -2
  258. data/app/serializers/spree/api/v3/admin/price_list_serializer.rb +1 -5
  259. data/app/serializers/spree/api/v3/admin/price_rule_serializer.rb +1 -3
  260. data/app/serializers/spree/api/v3/admin/price_serializer.rb +4 -0
  261. data/app/serializers/spree/api/v3/admin/product_serializer.rb +2 -0
  262. data/app/serializers/spree/api/v3/admin/promotion_action_serializer.rb +4 -6
  263. data/app/serializers/spree/api/v3/admin/promotion_rule_serializer.rb +0 -2
  264. data/app/serializers/spree/api/v3/admin/purchase_order_item_serializer.rb +5 -11
  265. data/app/serializers/spree/api/v3/admin/purchase_order_serializer.rb +4 -4
  266. data/app/serializers/spree/api/v3/admin/refund_serializer.rb +7 -3
  267. data/app/serializers/spree/api/v3/admin/reporting_result_serializer.rb +9 -2
  268. data/app/serializers/spree/api/v3/admin/return_line_item_serializer.rb +8 -0
  269. data/app/serializers/spree/api/v3/admin/return_serializer.rb +8 -4
  270. data/app/serializers/spree/api/v3/admin/seller_balance_serializer.rb +15 -10
  271. data/app/serializers/spree/api/v3/admin/seller_payout_serializer.rb +3 -5
  272. data/app/serializers/spree/api/v3/admin/seller_serializer.rb +3 -1
  273. data/app/serializers/spree/api/v3/admin/seller_transfer_serializer.rb +7 -8
  274. data/app/serializers/spree/api/v3/admin/shipping_label_serializer.rb +4 -4
  275. data/app/serializers/spree/api/v3/admin/stock_location_serializer.rb +3 -0
  276. data/app/serializers/spree/api/v3/admin/stock_movement_serializer.rb +2 -6
  277. data/app/serializers/spree/api/v3/admin/store_credit_event_serializer.rb +5 -0
  278. data/app/serializers/spree/api/v3/admin/store_credit_serializer.rb +9 -4
  279. data/app/serializers/spree/api/v3/admin/store_serializer.rb +17 -98
  280. data/app/serializers/spree/api/v3/admin/supplier_serializer.rb +1 -1
  281. data/app/serializers/spree/api/v3/admin/tax_exemption_certificate_serializer.rb +3 -1
  282. data/app/serializers/spree/api/v3/admin/tax_line_serializer.rb +4 -2
  283. data/app/serializers/spree/api/v3/admin/tax_rate_serializer.rb +2 -12
  284. data/app/serializers/spree/api/v3/admin/update_status_serializer.rb +28 -0
  285. data/app/serializers/spree/api/v3/admin/variant_serializer.rb +7 -3
  286. data/app/serializers/spree/api/v3/base_serializer.rb +112 -7
  287. data/app/serializers/spree/api/v3/cart_serializer.rb +16 -30
  288. data/app/serializers/spree/api/v3/claim_line_item_serializer.rb +5 -13
  289. data/app/serializers/spree/api/v3/claim_serializer.rb +2 -3
  290. data/app/serializers/spree/api/v3/country_serializer.rb +2 -1
  291. data/app/serializers/spree/api/v3/currency_serializer.rb +9 -1
  292. data/app/serializers/spree/api/v3/customer_serializer.rb +3 -2
  293. data/app/serializers/spree/api/v3/delivery_rate_serializer.rb +8 -12
  294. data/app/serializers/spree/api/v3/delivery_zone_member_serializer.rb +1 -1
  295. data/app/serializers/spree/api/v3/discount_serializer.rb +4 -1
  296. data/app/serializers/spree/api/v3/exchange_line_item_serializer.rb +2 -8
  297. data/app/serializers/spree/api/v3/exchange_serializer.rb +2 -3
  298. data/app/serializers/spree/api/v3/file_upload_serializer.rb +28 -0
  299. data/app/serializers/spree/api/v3/file_upload_target_serializer.rb +17 -0
  300. data/app/serializers/spree/api/v3/gift_card_batch_serializer.rb +2 -4
  301. data/app/serializers/spree/api/v3/gift_card_serializer.rb +4 -2
  302. data/app/serializers/spree/api/v3/import_serializer.rb +0 -12
  303. data/app/serializers/spree/api/v3/line_item_serializer.rb +15 -5
  304. data/app/serializers/spree/api/v3/locale_serializer.rb +3 -1
  305. data/app/serializers/spree/api/v3/market_serializer.rb +5 -5
  306. data/app/serializers/spree/api/v3/media_serializer.rb +7 -2
  307. data/app/serializers/spree/api/v3/order_group_serializer.rb +12 -4
  308. data/app/serializers/spree/api/v3/order_serializer.rb +5 -23
  309. data/app/serializers/spree/api/v3/payment_session_serializer.rb +2 -4
  310. data/app/serializers/spree/api/v3/price_history_serializer.rb +11 -6
  311. data/app/serializers/spree/api/v3/price_serializer.rb +17 -10
  312. data/app/serializers/spree/api/v3/product_filter_price_range_serializer.rb +10 -4
  313. data/app/serializers/spree/api/v3/product_serializer.rb +3 -3
  314. data/app/serializers/spree/api/v3/refund_serializer.rb +8 -5
  315. data/app/serializers/spree/api/v3/return_line_item_serializer.rb +3 -11
  316. data/app/serializers/spree/api/v3/return_serializer.rb +3 -6
  317. data/app/serializers/spree/api/v3/seller/address_serializer.rb +12 -0
  318. data/app/serializers/spree/api/v3/seller/balance_serializer.rb +15 -10
  319. data/app/serializers/spree/api/v3/seller/claim_line_item_serializer.rb +7 -0
  320. data/app/serializers/spree/api/v3/seller/claim_serializer.rb +5 -0
  321. data/app/serializers/spree/api/v3/seller/country_serializer.rb +19 -0
  322. data/app/serializers/spree/api/v3/seller/delivery_method_rule_serializer.rb +1 -3
  323. data/app/serializers/spree/api/v3/seller/delivery_method_serializer.rb +4 -8
  324. data/app/serializers/spree/api/v3/seller/delivery_rate_serializer.rb +9 -8
  325. data/app/serializers/spree/api/v3/seller/exchange_line_item_serializer.rb +5 -0
  326. data/app/serializers/spree/api/v3/seller/exchange_serializer.rb +5 -0
  327. data/app/serializers/spree/api/v3/seller/file_upload_serializer.rb +11 -0
  328. data/app/serializers/spree/api/v3/seller/file_upload_target_serializer.rb +10 -0
  329. data/app/serializers/spree/api/v3/seller/import_serializer.rb +2 -2
  330. data/app/serializers/spree/api/v3/seller/media_serializer.rb +2 -5
  331. data/app/serializers/spree/api/v3/seller/order_line_item_serializer.rb +4 -3
  332. data/app/serializers/spree/api/v3/seller/order_serializer.rb +16 -16
  333. data/app/serializers/spree/api/v3/seller/payment_split_serializer.rb +8 -9
  334. data/app/serializers/spree/api/v3/seller/payout_serializer.rb +2 -4
  335. data/app/serializers/spree/api/v3/seller/price_history_serializer.rb +12 -0
  336. data/app/serializers/spree/api/v3/seller/price_serializer.rb +13 -0
  337. data/app/serializers/spree/api/v3/seller/product_serializer.rb +2 -0
  338. data/app/serializers/spree/api/v3/seller/profile_serializer.rb +7 -7
  339. data/app/serializers/spree/api/v3/seller/return_line_item_serializer.rb +8 -0
  340. data/app/serializers/spree/api/v3/seller/return_serializer.rb +8 -5
  341. data/app/serializers/spree/api/v3/seller/state_serializer.rb +10 -0
  342. data/app/serializers/spree/api/v3/seller/stock_location_serializer.rb +3 -0
  343. data/app/serializers/spree/api/v3/seller/transfer_serializer.rb +6 -7
  344. data/app/serializers/spree/api/v3/seller/variant_serializer.rb +8 -4
  345. data/app/serializers/spree/api/v3/stock_location_serializer.rb +8 -1
  346. data/app/serializers/spree/api/v3/store_credit_event_serializer.rb +1 -3
  347. data/app/serializers/spree/api/v3/store_credit_serializer.rb +3 -5
  348. data/app/serializers/spree/api/v3/tax_line_serializer.rb +2 -4
  349. data/app/serializers/spree/api/v3/variant_serializer.rb +3 -3
  350. data/app/services/spree/api/v3/filter_predicates.rb +29 -0
  351. data/app/services/spree/api/v3/filter_table.rb +337 -0
  352. data/app/services/spree/api/v3/filters_aggregator.rb +2 -2
  353. data/app/services/spree/api/v3/request_schema.rb +430 -0
  354. data/app/services/spree/webhooks/deliver_webhook.rb +7 -3
  355. data/config/initializers/typelizer.rb +1 -1
  356. data/config/routes.rb +5 -3
  357. data/lib/spree/api/configuration.rb +5 -0
  358. data/lib/spree/api/dependencies.rb +7 -6
  359. data/lib/spree/api/middleware/request_size_limit.rb +24 -3
  360. data/lib/spree/api/openapi/filter_documentation.rb +136 -0
  361. data/lib/spree/api/openapi/schema_helper.rb +126 -80
  362. data/lib/spree/api/preference_families.rb +46 -0
  363. data/lib/spree/api/preference_types.rb +138 -0
  364. data/lib/spree/api/typelizer_generation.rb +4 -0
  365. data/lib/tasks/filters.rake +12 -0
  366. data/lib/typelizer/open_enums.rb +10 -0
  367. metadata +60 -10
  368. data/app/controllers/concerns/spree/api/v3/address_params.rb +0 -17
  369. data/app/controllers/concerns/spree/api/v3/direct_uploads.rb +0 -35
  370. data/app/controllers/spree/api/v3/admin/direct_uploads_controller.rb +0 -54
  371. data/app/controllers/spree/api/v3/seller/direct_uploads_controller.rb +0 -21
@@ -13,6 +13,47 @@ module Spree
13
13
  before_action :require_ids!, only: [:bulk_destroy]
14
14
  before_action :require_prices!, only: [:bulk_upsert]
15
15
 
16
+ # variant_id and price_list_id resolve through the current store's
17
+ # scopes (+permitted_params+), so a foreign or unknown id 404s.
18
+ request_params :price do |body|
19
+ body.attribute :variant_id, :id, required: true, on: :create, example: 'variant_xY9'
20
+ body.attribute :currency, :currency, required: true, on: :create, example: 'USD'
21
+ body.attribute :min_quantity, :integer,
22
+ description: 'The quantity a line must reach for this price to apply. Omit for the bottom rung of the ladder. Anything above 1 requires a `price_list_id`.'
23
+ body.attribute :amount, :decimal, nullable: true, example: '19.99'
24
+ body.attribute :compare_at_amount, :decimal, nullable: true, example: '19.99'
25
+ body.attribute :price_list_id, :id,
26
+ nullable: true,
27
+ example: 'pl_aBc123',
28
+ description: 'Omit or null for a base price; a price list for a list override.'
29
+ end
30
+
31
+ request_params 'PriceBulkUpsertParams', action: :bulk_upsert do |body|
32
+ body.attribute :prices, :array,
33
+ required: true,
34
+ as: 'PriceBulkUpsertRowParams',
35
+ description: 'Send an empty list to change nothing.' do |price|
36
+ price.attribute :id, :id,
37
+ nullable: true,
38
+ example: 'price_aBc123',
39
+ description: 'Present: updates that row. Absent: upserts on `(variant_id, currency, price_list_id, min_quantity)`.'
40
+ price.attribute :variant_id, :id, required: true, example: 'variant_xY9'
41
+ price.attribute :currency, :currency, required: true, example: 'USD'
42
+ price.attribute :price_list_id, :id,
43
+ nullable: true,
44
+ example: 'pl_aBc123',
45
+ description: 'Omit or null for a base price.'
46
+ price.attribute :min_quantity, :integer,
47
+ description: "The rung of the variant's ladder. Omit for the bottom rung; anything above 1 requires a `price_list_id`. A variant carries at most ten rungs on one list per currency."
48
+ price.attribute :amount, :decimal, nullable: true, example: '19.99'
49
+ price.attribute :compare_at_amount, :decimal, nullable: true, example: '19.99'
50
+ end
51
+ end
52
+
53
+ request_params 'PriceBulkDestroyParams', action: :bulk_destroy do |body|
54
+ body.attribute :ids, :array, of: :id, required: true, example: %w[price_aBc123]
55
+ end
56
+
16
57
  # Bulk-upserts prices on the unique key
17
58
  # `(variant_id, currency, price_list_id, min_quantity)` — one row per
18
59
  # rung of a variant's ladder (docs/plans/6.0-volume-pricing.md).
@@ -67,6 +108,15 @@ module Spree
67
108
  )
68
109
  end
69
110
 
111
+ if (malformed = result.error&.value.try(:[], :malformed_amounts))
112
+ return render_error(
113
+ code: Spree::Api::V3::ErrorHandler::ERROR_CODES[:invalid_money_format],
114
+ message: I18n.t('activerecord.errors.models.spree/price_list.attributes.base.malformed_price').upcase_first,
115
+ status: :unprocessable_content,
116
+ details: { rows: malformed }
117
+ )
118
+ end
119
+
70
120
  if (negative = result.error&.value.try(:[], :invalid_amounts))
71
121
  return render_error(
72
122
  code: 'invalid_amount',
@@ -160,11 +210,8 @@ module Spree
160
210
  false
161
211
  end
162
212
 
163
- # Resolves variant_id / price_list_id through the current store's
164
- # scopes so a foreign or unknown id 404s instead of binding the
165
- # price to another store's record.
166
213
  def permitted_params
167
- permitted = params.permit(*model_additional_permitted_attributes, :variant_id, :currency, :min_quantity, :amount, :compare_at_amount, :price_list_id)
214
+ permitted = params.permit(*permitted_attributes)
168
215
  permitted[:variant_id] = store_variants.find_by_prefix_id!(permitted[:variant_id]).id if permitted[:variant_id].present?
169
216
  permitted[:price_list_id] = store_price_lists.find_by_prefix_id!(permitted[:price_list_id]).id if permitted[:price_list_id].present?
170
217
  permitted
@@ -11,6 +11,30 @@ module Spree
11
11
  class ProductTypesController < ResourceController
12
12
  scoped_resource :product_types
13
13
 
14
+ # The associations are a template for products created with this type:
15
+ # replace-sets that never change the products already carrying it.
16
+ request_params :product_type do |body|
17
+ body.attribute :name, :string, required: true, on: :create, example: 'Hoodie'
18
+ body.attribute :delivery_profile_id, :id,
19
+ nullable: true,
20
+ example: 'fp_86Rf07xd4z',
21
+ description: 'Delivery profile stamped onto products created with this type. Null leaves new products on the store default profile.'
22
+ body.attribute :option_type_ids, :array, of: :id,
23
+ description: 'Option types seeded onto products that get this type. Use apply to products to backfill existing ones.'
24
+ body.attribute :category_ids, :array, of: :id,
25
+ description: 'Categories seeded onto products that get this type. Categories from other stores are ignored.'
26
+ body.attribute :custom_field_definitions, :array,
27
+ as: 'ProductTypeCustomFieldDefinitionParams',
28
+ description: 'The custom field definitions the product form shows. Definitions not listed are removed from the type.' do |custom_field_definition|
29
+ custom_field_definition.attribute :id, :id,
30
+ required: true,
31
+ example: 'cfdef_abc123',
32
+ description: 'The custom field definition.'
33
+ custom_field_definition.attribute :required, :boolean
34
+ custom_field_definition.attribute :sort_order, :integer
35
+ end
36
+ end
37
+
14
38
  # Backfills this type's option types and categories onto the products
15
39
  # that already carry it. Additive and idempotent — nothing is removed.
16
40
  def apply_to_products
@@ -50,14 +74,7 @@ module Spree
50
74
  end
51
75
 
52
76
  def permitted_params
53
- attrs = params.permit(
54
- *model_additional_permitted_attributes,
55
- :name,
56
- :delivery_profile_id,
57
- option_type_ids: [],
58
- category_ids: [],
59
- custom_field_definitions: [:id, :required, :sort_order]
60
- )
77
+ attrs = params.permit(*permitted_attributes)
61
78
  attrs = scope_delivery_profile(attrs)
62
79
  scope_category_ids(attrs)
63
80
  end
@@ -6,6 +6,32 @@ module Spree
6
6
  class DigitalAssetsController < ResourceController
7
7
  scoped_resource :products
8
8
 
9
+ request_params :digital_asset do |body|
10
+ # Names a blob rather than a column, so it is attached
11
+ # separately (+attach_uploaded_file+).
12
+ body.attribute :signed_id, :string,
13
+ permit: false,
14
+ description: "A `signed_id` from `files.upload()` with `visibility: 'private'`. Required for an uploaded file, omitted for a provider-backed asset. On update it replaces the file for everyone who already bought it."
15
+ # Resolved through the parent rather than assigned, so an asset
16
+ # can't be pointed at a variant of another product or store.
17
+ body.attribute :variant_id, :id,
18
+ permit: false,
19
+ description: "Defaults to the product's default variant when omitted."
20
+ # The model validates it names a registered provider.
21
+ body.attribute :provider_type, :string,
22
+ description: 'A provider shorthand from the providers endpoint. Omit for an uploaded file.'
23
+ # Its keys are declared by the provider, so it lands verbatim
24
+ # under one key in the asset's metadata.
25
+ body.attribute :provider_settings, :hash,
26
+ description: "Values for the provider's settings schema, keyed by field name."
27
+ body.attribute :authorized_clicks, :integer,
28
+ nullable: true,
29
+ description: "Null means the store's download settings apply."
30
+ body.attribute :authorized_days, :integer,
31
+ nullable: true,
32
+ description: "Null means the store's download settings apply."
33
+ end
34
+
9
35
  # PATCH /api/v3/admin/products/:product_id/digital_assets/:id
10
36
  #
11
37
  # Replacing the file keeps every already-issued link working, since
@@ -84,17 +110,8 @@ module Spree
84
110
  [:variant, { attachment_attachment: :blob }]
85
111
  end
86
112
 
87
- # variant_id is resolved through the parent rather than assigned:
88
- # mass-assigning it would let a caller point an asset at a variant
89
- # of any product, in any store. signed_id likewise names a blob
90
- # rather than a column, so it is attached separately.
91
- # provider_type is mass-assignable: the model validates it names a
92
- # registered provider, so an unknown value is a 422, not a hazard.
93
- # provider_settings is an open hash — its keys are declared by the
94
- # provider, not fixed here — so it is permitted as arbitrary scalars
95
- # and lands verbatim under one key in the asset's metadata.
96
113
  def permitted_params
97
- params.permit(:authorized_clicks, :authorized_days, :provider_type, provider_settings: {})
114
+ params.permit(*declared_permitted_attributes)
98
115
  end
99
116
 
100
117
  private
@@ -6,6 +6,14 @@ module Spree
6
6
  class VariantsController < ResourceController
7
7
  scoped_resource :products
8
8
 
9
+ request_params :variant do |body|
10
+ body.external_references
11
+ body.include_attributes Spree::Api::V3::VariantRequestSchema
12
+ body.attribute :options, :array, of: Spree::Api::V3::VariantOptionRequestSchema,
13
+ required: true, on: :create,
14
+ description: 'One pair per option type the variant takes part in (size, color). Option types and values are created when missing. On update, omit to leave the option values untouched.'
15
+ end
16
+
9
17
  protected
10
18
 
11
19
  def model_class
@@ -42,20 +50,7 @@ module Spree
42
50
  end
43
51
 
44
52
  def permitted_params
45
- params.permit(
46
- *model_additional_permitted_attributes,
47
- :sku, :barcode,
48
- :cost_price, :cost_currency,
49
- :weight, :height, :width, :depth, :weight_unit, :dimensions_unit,
50
- :hs_code, :country_of_origin, :customs_description,
51
- :minimum_order_quantity, :order_multiple, :purchase_unit, :units_per_carton,
52
- :carton_package_type_id, :carton_weight, :cartons_per_pallet,
53
- :seller_id, :delivery_profile_id,
54
- :track_inventory, :preorderable, :preorder_ships_at, :backorder_limit, :tax_category_id, :position,
55
- options: [:name, :value],
56
- prices: [:amount, :compare_at_amount, :currency],
57
- stock_levels: [:stock_location_id, :count_on_hand, :backorderable]
58
- )
53
+ params.permit(*permitted_attributes)
59
54
  end
60
55
  end
61
56
  end
@@ -8,6 +8,82 @@ module Spree
8
8
 
9
9
  scoped_resource :products
10
10
 
11
+ # Product is purely a catalog grouping in API v3: every purchasable
12
+ # attribute lives on variants (docs/plans/6.0-remove-master-variant.md).
13
+ request_params :product do |body|
14
+ body.external_references
15
+ body.attribute :name, :string, required: true, on: :create, example: 'Premium T-Shirt'
16
+ body.attribute :description, :string,
17
+ description: 'Rich text HTML. Reads come back as this (plain text) plus `description_html`.'
18
+ body.attribute :slug, :string
19
+ body.attribute :status, :string, choices: Spree::Product::STATUSES, open: true
20
+ body.attribute :meta_title, :string
21
+ body.attribute :meta_description, :string
22
+ body.attribute :meta_keywords, :string
23
+ body.attribute :tax_category_id, :id
24
+ body.attribute :product_type_id, :id,
25
+ nullable: true,
26
+ description: 'The product type this product is created from (`pt_...`). Its option types and categories seed onto the product and its custom field definitions drive the product form. Editing the type later never rewrites existing products.'
27
+ body.attribute :delivery_profile_id, :id,
28
+ nullable: true,
29
+ description: 'The delivery profile deciding how this product ships (`fp_...`); null falls back to the store default.'
30
+ body.attribute :promotionable, :boolean
31
+ body.attribute :tags, :array, of: :string, example: %w[eco sale]
32
+ body.attribute :category_ids, :array, of: :id
33
+ # Reachable with `write_products` rather than `write_collections`:
34
+ # filing a product into its groupings is part of maintaining it.
35
+ body.attribute :collection_ids, :array, of: :id,
36
+ description: 'Manual collections this product belongs to. Automatic collections are kept as they are, since their rules decide membership.'
37
+ body.attribute :metadata, :hash
38
+ body.attribute :prices, :array, of: Spree::Api::V3::PriceRequestSchema,
39
+ description: "Shorthand for a simple (no-options) product: per-currency prices that forward to the product's sole variant. For products with options, set prices per variant under `variants` instead. Don't pass both."
40
+ body.attribute :custom_fields, :array, of: Spree::Api::V3::CustomFieldValueRequestSchema,
41
+ description: 'Custom field values, each validated against its definition.'
42
+ # `external_url` is left out on purpose: it makes the server fetch
43
+ # a URL the caller chose, so it belongs to trusted importers.
44
+ body.attribute :media, :array,
45
+ as: 'ProductMediaParams',
46
+ description: 'Media written with the product. Entries with `id` update an existing asset; entries with `signed_id` attach a fresh upload; an external video needs only its URL.' do |media_item|
47
+ media_item.attribute :id, :id
48
+ media_item.attribute :signed_id, :string, description: 'A `signed_id` from `files.upload()`.'
49
+ media_item.attribute :source_media_id, :id, description: 'Copies an asset from the media library.'
50
+ media_item.include_attributes Spree::Api::V3::MediaRequestSchema
51
+ media_item.attribute :variant_ids, :array, of: :id,
52
+ description: 'Replaces the variants this media is linked to. An empty list clears them.'
53
+ end
54
+ body.attribute :digital_assets, :array,
55
+ as: 'ProductDigitalAssetParams',
56
+ description: 'Downloadable files written with the product. Each entry carries a `signed_id` (an upload to private storage) or a `provider_type` (a provider-backed asset), and attaches to the default variant unless `variant_id` is given. An `id` updates that asset in place, so replaying the list does not duplicate it; removal uses the nested digital assets endpoint.' do |digital_asset|
57
+ digital_asset.attribute :id, :id
58
+ digital_asset.attribute :signed_id, :string
59
+ digital_asset.attribute :variant_id, :id
60
+ digital_asset.attribute :provider_type, :string,
61
+ description: 'A provider shorthand from the digital asset providers endpoint.'
62
+ digital_asset.attribute :provider_settings, :hash,
63
+ description: "Values for the provider's settings schema, keyed by field name."
64
+ digital_asset.attribute :authorized_clicks, :integer,
65
+ nullable: true,
66
+ description: "Null means the store's download settings apply."
67
+ digital_asset.attribute :authorized_days, :integer,
68
+ nullable: true,
69
+ description: "Null means the store's download settings apply."
70
+ end
71
+ body.attribute :product_publications, :array, as: 'ProductPublicationParams' do |product_publication|
72
+ product_publication.attribute :id, :id
73
+ product_publication.attribute :channel_id, :id
74
+ product_publication.attribute :published_at, :datetime, nullable: true
75
+ product_publication.attribute :unpublished_at, :datetime, nullable: true
76
+ end
77
+ body.attribute :variants, :array,
78
+ as: 'ProductVariantParams',
79
+ description: 'Every purchasable attribute (sku, prices, stock, weight, dimensions) lives on variants. An entry without `options` upserts onto the default variant of a simple product; pass `id` to update an existing variant.' do |variant|
80
+ variant.attribute :id, :id
81
+ variant.include_attributes Spree::Api::V3::VariantRequestSchema
82
+ variant.attribute :options, :array, of: Spree::Api::V3::VariantOptionRequestSchema,
83
+ description: 'One pair per option type the variant takes part in (size, color). Option types and values are created when missing.'
84
+ end
85
+ end
86
+
11
87
  before_action :require_ids!, only: [
12
88
  :bulk_status_update,
13
89
  :bulk_add_to_categories,
@@ -19,6 +95,42 @@ module Spree
19
95
  :bulk_destroy
20
96
  ]
21
97
 
98
+ request_params 'ProductRejectParams', action: :reject do |body|
99
+ body.attribute :reason, :string,
100
+ description: 'Why the submission was turned down. Sent back to the seller.'
101
+ end
102
+
103
+ request_params 'ProductBulkStatusParams', action: :bulk_status_update do |body|
104
+ body.attribute :ids, :array, of: :id, required: true, example: ['prod_UkLWZg9DAJ']
105
+ body.attribute :status, :string,
106
+ required: true,
107
+ choices: Spree::Product::STATUSES,
108
+ description: 'Products in review are skipped and counted in `skipped_in_review_count`.'
109
+ end
110
+
111
+ {
112
+ 'ProductBulkCategoriesParams' => [:category_ids, %i[bulk_add_to_categories bulk_remove_from_categories]],
113
+ 'ProductBulkCollectionsParams' => [:collection_ids, %i[bulk_add_to_collections bulk_remove_from_collections]],
114
+ 'ProductBulkChannelsParams' => [:channel_ids, %i[bulk_add_to_channels bulk_remove_from_channels]]
115
+ }.each do |name, (field, actions)|
116
+ actions.each do |action|
117
+ request_params(name, action: action) do |body|
118
+ body.attribute :ids, :array, of: :id, required: true
119
+ body.attribute field, :array, of: :id, required: true
120
+ end
121
+ end
122
+ end
123
+
124
+ %i[bulk_add_tags bulk_remove_tags].each do |action|
125
+ request_params('BulkTagsParams', action: action) { |body| body.include_attributes Spree::Api::V3::Admin::BulkTagsRequestSchema }
126
+ end
127
+
128
+ request_params 'ProductBulkDestroyParams', action: :bulk_destroy do |body|
129
+ body.attribute :ids, :array, of: :id,
130
+ required: true,
131
+ description: 'Products the caller cannot delete are skipped.'
132
+ end
133
+
22
134
  # POST /api/v3/admin/products/:id/clone
23
135
  def clone
24
136
  @resource = find_resource
@@ -155,6 +267,14 @@ module Spree
155
267
 
156
268
  protected
157
269
 
270
+ def additional_sort_fields
271
+ Spree::SearchProvider::Base::SORT_FIELDS
272
+ end
273
+
274
+ def custom_field_filters?
275
+ true
276
+ end
277
+
158
278
  def model_class
159
279
  Spree::Product
160
280
  end
@@ -201,76 +321,7 @@ module Spree
201
321
  end
202
322
 
203
323
  def permitted_params
204
- # Product is purely a catalog grouping in API v3. All purchasable
205
- # attributes (sku, barcode, price, weight, dimensions, stock,
206
- # track_inventory) live on variants. See
207
- # docs/plans/6.0-remove-master-variant.md.
208
- #
209
- # Top-level `prices` is a convenience for simple (no-options)
210
- # products: the merchant doesn't need to know about the default
211
- # variant, so they ship prices alongside name/status and the
212
- # `Spree::Product#prices=` setter forwards them to it.
213
- params.permit(
214
- *model_additional_permitted_attributes,
215
- :name, :description, :slug, :status,
216
- :meta_title, :meta_description, :meta_keywords,
217
- :tax_category_id, :product_type_id, :delivery_profile_id,
218
- :promotionable,
219
- tags: [],
220
- category_ids: [],
221
- # Manual collection membership only — the model setter ignores
222
- # incoming automatic collections and preserves the product's
223
- # existing ones, since those are materialized from their rules.
224
- #
225
- # Deliberately reachable with `write_products` rather than
226
- # `write_collections`: filing a product into its groupings is part
227
- # of maintaining the product, which is why `category_ids` and the
228
- # bulk membership endpoints have always worked this way too.
229
- # Creating or configuring a collection still needs
230
- # `write_collections`.
231
- collection_ids: [],
232
- metadata: {},
233
- prices: [:amount, :compare_at_amount, :currency],
234
- # Inline custom field values keyed by definition id. The model
235
- # setter (`Spree::HasCustomFields#custom_fields=`) validates each
236
- # entry against its definition. We permit `value` as a scalar AND
237
- # as `value: []` / `value: {}` so JSON custom_fields round-trip
238
- # whether the parsed payload is an array or an object, while
239
- # text/number/boolean ship scalars.
240
- custom_fields: [:id, :custom_field_definition_id, :value, { value: [] }, { value: {} }],
241
- # Inline media. Entries with `id` patch an existing asset.
242
- # Entries with `signed_id` create + attach a fresh upload; an
243
- # external video creates with a URL and no file. Lets the
244
- # dashboard ship media changes alongside the rest of the product
245
- # form. See `Spree::Products::NestedAttributes`.
246
- #
247
- # `external_url` is deliberately not permitted here: it makes the
248
- # server fetch a URL the caller chose, so it belongs to trusted
249
- # importers rather than to a request body.
250
- media: [*Spree::Media::WRITABLE_ATTRIBUTES, :id, :signed_id, :source_media_id, { variant_ids: [] }],
251
- # Inline digital files, so a new product ships its downloadable
252
- # files in the same request. Additive: an entry carries a
253
- # `signed_id` (uploaded file) or a `provider_type` (provider-backed
254
- # asset), and an `id` patches that asset in place rather than
255
- # building a duplicate — so replaying a product's list on update
256
- # does not accumulate copies. Removal uses the nested
257
- # digital_assets endpoint. See `Spree::Product#digital_assets=`.
258
- digital_assets: [:id, :signed_id, :variant_id, :provider_type, :authorized_clicks, :authorized_days, provider_settings: {}],
259
- product_publications: [:id, :channel_id, :published_at, :unpublished_at],
260
- variants: [
261
- :id, :sku, :barcode,
262
- :cost_price, :cost_currency,
263
- :weight, :height, :width, :depth, :weight_unit, :dimensions_unit,
264
- :hs_code, :country_of_origin, :customs_description,
265
- :minimum_order_quantity, :order_multiple, :purchase_unit, :units_per_carton,
266
- :carton_package_type_id, :carton_weight, :cartons_per_pallet,
267
- :seller_id, :delivery_profile_id,
268
- :track_inventory, :preorderable, :preorder_ships_at, :backorder_limit, :tax_category_id, :position,
269
- options: [:name, :value],
270
- prices: [:amount, :compare_at_amount, :currency],
271
- stock_levels: [:id, :stock_location_id, :count_on_hand, :backorderable]
272
- ]
273
- ).tap { |attrs| attrs.delete(:status) if review_status?(attrs[:status]) }
324
+ params.permit(*permitted_attributes).tap { |attrs| attrs.delete(:status) if review_status?(attrs[:status]) }
274
325
  end
275
326
 
276
327
  # A review status is an outcome, not a value to assign: `proposed`
@@ -13,6 +13,15 @@ module Spree
13
13
  subclassed_via -> { Spree.promotions.actions },
14
14
  unknown_type_error: 'unknown_promotion_action_type'
15
15
 
16
+ request_params :promotion_action do |body|
17
+ body.attribute :type, :string,
18
+ required: true, on: :create,
19
+ only: :create,
20
+ example: 'free_shipping',
21
+ description: 'The action kind, from `GET /promotion_actions/types`.'
22
+ body.include_attributes Spree::Api::V3::Admin::PromotionActionRequestSchema
23
+ end
24
+
16
25
  def types
17
26
  authorize! :read, model_class
18
27
 
@@ -38,7 +47,7 @@ module Spree
38
47
  {
39
48
  type: calc.api_type,
40
49
  label: calc.respond_to?(:description) ? calc.description : calc.to_s.demodulize.titleize,
41
- preference_schema: calc.respond_to?(:serialized_preference_schema) ? calc.serialized_preference_schema : []
50
+ schema: calc.preference_json_schema
42
51
  }
43
52
  end.sort_by { |entry| entry[:label].to_s }
44
53
 
@@ -56,7 +65,7 @@ module Spree
56
65
  end
57
66
 
58
67
  def permitted_params
59
- params.permit(*model_additional_permitted_attributes, :type, preferences: {})
68
+ params.permit(*permitted_attributes)
60
69
  end
61
70
 
62
71
  # `types`/`calculators` are read-only discovery — read scope + :show ability.
@@ -13,6 +13,15 @@ module Spree
13
13
  subclassed_via -> { Spree.promotions.rules },
14
14
  unknown_type_error: 'unknown_promotion_rule_type'
15
15
 
16
+ request_params :promotion_rule do |body|
17
+ body.attribute :type, :string,
18
+ required: true, on: :create,
19
+ only: :create,
20
+ example: 'currency',
21
+ description: 'The rule kind, from `GET /promotion_rules/types`.'
22
+ body.include_attributes Spree::Api::V3::Admin::PromotionRuleRequestSchema
23
+ end
24
+
16
25
  def types
17
26
  authorize! :read, model_class
18
27
 
@@ -30,7 +39,7 @@ module Spree
30
39
  end
31
40
 
32
41
  def permitted_params
33
- params.permit(*model_additional_permitted_attributes, :type, preferences: {})
42
+ params.permit(*permitted_attributes)
34
43
  end
35
44
 
36
45
  # `types` is read-only discovery — maps to the read scope + :show ability.
@@ -5,6 +5,51 @@ module Spree
5
5
  class PromotionsController < ResourceController
6
6
  scoped_resource :promotions
7
7
 
8
+ request_params :promotion do |body|
9
+ body.attribute :name, :string, required: true, on: :create
10
+ body.attribute :description, :string, nullable: true
11
+ body.attribute :code, :string,
12
+ nullable: true,
13
+ description: 'Required for single-code coupon promotions. Ignored when `multi_codes` is true.'
14
+ body.attribute :path, :string, nullable: true
15
+ body.attribute :starts_at, :datetime, nullable: true
16
+ body.attribute :expires_at, :datetime, nullable: true
17
+ body.attribute :usage_limit, :integer, nullable: true
18
+ body.attribute :match_policy, :string, choices: Spree::Promotion::MATCH_POLICIES
19
+ body.attribute :kind, :string,
20
+ choices: Spree::Promotion.kinds.keys,
21
+ description: '`coupon_code` needs a code (or `multi_codes`); `automatic` applies without one.'
22
+ body.attribute :multi_codes, :boolean,
23
+ description: 'Generates `number_of_codes` codes starting with `code_prefix`.'
24
+ body.attribute :number_of_codes, :integer, nullable: true
25
+ body.attribute :code_prefix, :string, nullable: true
26
+ body.attribute :promotion_category_id, :id, nullable: true
27
+ # Rules and actions are permitted below, with the keys each
28
+ # registered kind adds.
29
+ body.attribute :rules, :array,
30
+ permit: false,
31
+ as: 'PromotionRuleDraftParams',
32
+ description: 'The rules the promotion should have: entries with `id` update, entries without are created, and rules left out are removed.' do |rule|
33
+ rule.attribute :id, :id
34
+ rule.attribute :type, :string,
35
+ required: true,
36
+ example: 'currency',
37
+ description: 'The rule kind, from `GET /promotion_rules/types`.'
38
+ rule.include_attributes Spree::Api::V3::Admin::PromotionRuleRequestSchema
39
+ end
40
+ body.attribute :actions, :array,
41
+ permit: false,
42
+ as: 'PromotionActionDraftParams',
43
+ description: 'The actions the promotion should have, matched the same way as `rules`.' do |action|
44
+ action.attribute :id, :id
45
+ action.attribute :type, :string,
46
+ required: true,
47
+ example: 'free_shipping',
48
+ description: 'The action kind, from `GET /promotion_actions/types`.'
49
+ action.include_attributes Spree::Api::V3::Admin::PromotionActionRequestSchema
50
+ end
51
+ end
52
+
8
53
  protected
9
54
 
10
55
  def model_class
@@ -23,21 +68,12 @@ module Spree
23
68
  promotion_includes
24
69
  end
25
70
 
26
- # A single POST/PATCH /promotions can ship rules and actions
27
- # alongside the basics; `Promotion#rules=` / `actions=` reconcile
28
- # to the desired set. The nested allowlist below is the union of
29
- # every built-in rule/action's expected keys. Plugin-defined
30
- # subclasses can add to it — see `additional_permitted_attributes`
31
- # below.
71
+ # `Promotion#rules=` / `actions=` reconcile to the desired set. The
72
+ # nested allowlists are the union of every registered rule/action
73
+ # kind's keys, so plugin-defined kinds add to them through
74
+ # `additional_permitted_attributes`.
32
75
  def resource_permitted_attributes
33
- [
34
- :name, :description, :code, :path,
35
- :starts_at, :expires_at, :usage_limit, :match_policy,
36
- :kind, :multi_codes, :number_of_codes, :code_prefix,
37
- :promotion_category_id,
38
- rules: rule_attributes,
39
- actions: action_attributes
40
- ]
76
+ [*super, { rules: rule_attributes, actions: action_attributes }]
41
77
  end
42
78
 
43
79
  def rule_attributes
@@ -16,6 +16,53 @@ module Spree
16
16
 
17
17
  scoped_resource :purchasing
18
18
 
19
+ # The supplier, the warehouse and the lines are read from the raw
20
+ # params and resolved through the store (see ReceivableActions), so
21
+ # only the document's own fields are permitted.
22
+ request_params :purchase_order do |body|
23
+ body.attribute :supplier_id, :id, required: true, on: :create, permit: false, example: 'sup_1234567890'
24
+ body.attribute :destination_location_id, :id,
25
+ required: true, on: :create,
26
+ permit: false,
27
+ example: 'sloc_1234567890'
28
+ body.attribute :currency, :currency,
29
+ nullable: true,
30
+ example: 'USD',
31
+ description: "Defaults to the store's currency; set it for a foreign-currency order."
32
+ body.attribute :expected_at, :date,
33
+ nullable: true,
34
+ example: '2026-10-01',
35
+ description: 'The day the supplier promised. `null` clears it.'
36
+ body.attribute :cancel_by, :date,
37
+ nullable: true,
38
+ description: 'The day after which the goods are no longer wanted. `null` clears it.'
39
+ body.attribute :reference, :string,
40
+ nullable: true,
41
+ description: "The supplier's own order number. `null` clears it."
42
+ body.attribute :notes, :string, nullable: true
43
+ body.attribute :items, :array,
44
+ permit: false,
45
+ as: 'PurchaseOrderItemParams',
46
+ description: 'Sending the list replaces whatever the order currently holds.' do |item|
47
+ item.attribute :variant_id, :id, required: true, example: 'variant_1234567890'
48
+ item.attribute :quantity_ordered, :integer, required: true, example: 100
49
+ item.attribute :unit_cost, :decimal,
50
+ example: '12.50',
51
+ description: 'What the merchant agreed to pay per unit.'
52
+ end
53
+ body.attribute :metadata, :hash, nullable: true
54
+ end
55
+
56
+ request_params 'ReceivableCloseParams', action: :close do |body|
57
+ body.attribute :reason, :string, nullable: true, description: 'Why the balance is not coming.'
58
+ end
59
+
60
+ request_params 'PurchaseOrderCancelParams', action: :cancel do |body|
61
+ body.attribute :reason, :string,
62
+ example: 'Supplier went under',
63
+ description: "Appended to the order's notes."
64
+ end
65
+
19
66
  # The base registers this for show/update/destroy; a second
20
67
  # `before_action :set_resource` replaces that registration rather than
21
68
  # adding to it, so those three are listed again here.
@@ -109,6 +156,7 @@ module Spree
109
156
  cancel_by: params[:cancel_by],
110
157
  reference: params[:reference],
111
158
  notes: params[:notes],
159
+ metadata: metadata_from_params,
112
160
  created_by: try_spree_current_user
113
161
  }
114
162
  end
@@ -121,13 +169,6 @@ module Spree
121
169
  }
122
170
  end
123
171
 
124
- # The supplier and the warehouse are resolved through the store
125
- # instead, since an id the payload names has to be proved to belong
126
- # here.
127
- def resource_permitted_attributes
128
- [:currency, :expected_at, :cancel_by, :reference, :notes, { metadata: {} }]
129
- end
130
-
131
172
  def editable_attributes
132
173
  attributes = permitted_params.to_h.symbolize_keys
133
174
  attributes[:supplier] = supplier_from_params if params.key?(:supplier_id)
@@ -143,6 +184,12 @@ module Spree
143
184
 
144
185
  private
145
186
 
187
+ # Merchant data, stored as sent: never through the prefixed-id decoding
188
+ # `permitted_params` applies.
189
+ def metadata_from_params
190
+ params.permit(*declared_permitted_attributes)[:metadata]&.to_h
191
+ end
192
+
146
193
  # Read through `current_store` rather than the model constant: a
147
194
  # supplier id belonging to another store must 404, which no ability
148
195
  # check would catch.