spree_core 5.6.1 → 6.0.0.beta2

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 (1602) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +30 -0
  3. data/README.md +2 -3
  4. data/app/finders/spree/line_items/find_by_variant.rb +5 -3
  5. data/app/finders/spree/sellers/panel_url.rb +47 -0
  6. data/app/finders/spree/stores/dashboard_url.rb +76 -0
  7. data/app/helpers/spree/images_helper.rb +1 -1
  8. data/app/jobs/spree/carts/reap_expired_job.rb +43 -0
  9. data/app/jobs/spree/collections/regenerate_time_based_job.rb +18 -0
  10. data/app/jobs/spree/data_requests/process_job.rb +42 -0
  11. data/app/jobs/spree/events/subscriber_job.rb +1 -1
  12. data/app/jobs/spree/images/migrate_product_media_job.rb +80 -0
  13. data/app/jobs/spree/images/save_from_url_job.rb +70 -13
  14. data/app/jobs/spree/imports/create_categories_job.rb +23 -14
  15. data/app/jobs/spree/imports/create_rows_job.rb +3 -62
  16. data/app/jobs/spree/imports/process_group_job.rb +1 -1
  17. data/app/jobs/spree/imports/process_job.rb +164 -0
  18. data/app/jobs/spree/imports/process_rows_job.rb +4 -71
  19. data/app/jobs/spree/orders/finalize_stale_drafts_job.rb +29 -0
  20. data/app/jobs/spree/payments/handle_webhook_job.rb +1 -1
  21. data/app/jobs/spree/price_lists/sync_currencies_job.rb +42 -0
  22. data/app/jobs/spree/product_types/apply_to_products_job.rb +74 -0
  23. data/app/jobs/spree/products/auto_match_collections_job.rb +14 -0
  24. data/app/jobs/spree/products/auto_match_taxons_job.rb +6 -7
  25. data/app/jobs/spree/products/refresh_metrics_job.rb +1 -1
  26. data/app/jobs/spree/products/touch_categories_job.rb +15 -0
  27. data/app/jobs/spree/products/touch_taxons_job.rb +7 -6
  28. data/app/jobs/spree/sample_data/load_job.rb +13 -0
  29. data/app/jobs/spree/seller_payouts/sweep_due_job.rb +57 -0
  30. data/app/jobs/spree/seller_payouts/sweep_seller_job.rb +70 -0
  31. data/app/jobs/spree/seller_transfers/execute_pending_due_job.rb +56 -0
  32. data/app/jobs/spree/seller_transfers/execute_pending_job.rb +52 -0
  33. data/app/jobs/spree/shipping_labels/store_file_job.rb +17 -0
  34. data/app/jobs/spree/stock_locations/stock_items/create_job.rb +7 -5
  35. data/app/jobs/spree/stock_locations/stock_levels/create_job.rb +13 -0
  36. data/app/jobs/spree/stock_reservations/expire_job.rb +1 -1
  37. data/app/jobs/spree/tax_identifiers/validate_job.rb +14 -0
  38. data/app/jobs/spree/variants/remove_line_item_job.rb +29 -1
  39. data/app/mailers/spree/admin_user_mailer.rb +6 -35
  40. data/app/mailers/spree/base_mailer.rb +1 -20
  41. data/app/mailers/spree/invitation_mailer.rb +1 -1
  42. data/app/mailers/spree/seller_user_mailer.rb +47 -0
  43. data/app/models/concerns/spree/account_lockout.rb +38 -0
  44. data/app/models/concerns/spree/acted_by.rb +210 -0
  45. data/app/models/concerns/spree/actor.rb +51 -0
  46. data/app/models/concerns/spree/admin_user_methods.rb +53 -42
  47. data/app/models/concerns/spree/calculated_adjustments.rb +8 -14
  48. data/app/models/concerns/spree/calculator/currency_amounts.rb +43 -0
  49. data/app/models/concerns/spree/capture_method.rb +33 -0
  50. data/app/models/concerns/spree/custom_field_filterable.rb +163 -0
  51. data/app/models/concerns/spree/customer_methods.rb +421 -0
  52. data/app/models/concerns/spree/deprecated_customer_alias.rb +22 -0
  53. data/app/models/concerns/spree/derived_number.rb +66 -0
  54. data/app/models/concerns/spree/has_address_book.rb +89 -0
  55. data/app/models/concerns/spree/has_custom_fields.rb +360 -0
  56. data/app/models/concerns/spree/has_external_references.rb +95 -0
  57. data/app/models/concerns/spree/has_iso_geography.rb +71 -0
  58. data/app/models/concerns/spree/has_library_media.rb +94 -0
  59. data/app/models/concerns/spree/has_list_position.rb +15 -0
  60. data/app/models/concerns/spree/has_number.rb +139 -0
  61. data/app/models/concerns/spree/has_status.rb +76 -0
  62. data/app/models/concerns/spree/has_tracking_carrier.rb +18 -0
  63. data/app/models/concerns/spree/instruments_gateway_calls.rb +20 -0
  64. data/app/models/concerns/spree/integration_backed_provider.rb +89 -0
  65. data/app/models/concerns/spree/integrations_concern.rb +15 -2
  66. data/app/models/concerns/spree/label_translatable.rb +37 -0
  67. data/app/models/concerns/spree/marketplace/order_group.rb +35 -0
  68. data/app/models/concerns/spree/metadata.rb +18 -22
  69. data/app/models/concerns/spree/named_type.rb +11 -1
  70. data/app/models/concerns/spree/parameterizable_name.rb +5 -5
  71. data/app/models/concerns/spree/password_policy.rb +37 -0
  72. data/app/models/concerns/spree/payment_session_transitions.rb +93 -0
  73. data/app/models/concerns/spree/payment_source_concern.rb +1 -1
  74. data/app/models/concerns/spree/personal_data_matching.rb +120 -0
  75. data/app/models/concerns/spree/preference_schema.rb +120 -19
  76. data/app/models/concerns/spree/prefixed_id.rb +25 -3
  77. data/app/models/concerns/spree/presentation_translatable.rb +4 -37
  78. data/app/models/concerns/spree/product_scopes.rb +51 -191
  79. data/app/models/concerns/spree/provider_failure_policy.rb +44 -0
  80. data/app/models/concerns/spree/publishable.rb +7 -7
  81. data/app/models/concerns/spree/purchase/addresses.rb +258 -0
  82. data/app/models/concerns/spree/purchase/channel.rb +19 -0
  83. data/app/models/concerns/spree/purchase/checkout_steps.rb +68 -0
  84. data/app/models/concerns/spree/purchase/company.rb +96 -0
  85. data/app/models/concerns/spree/purchase/currency.rb +29 -0
  86. data/app/models/concerns/spree/purchase/digital_items.rb +60 -0
  87. data/app/models/concerns/spree/purchase/freight.rb +52 -0
  88. data/app/models/concerns/spree/purchase/gift_cards.rb +70 -0
  89. data/app/models/concerns/spree/purchase/lifecycle.rb +53 -0
  90. data/app/models/concerns/spree/purchase/line_item_currencies.rb +38 -0
  91. data/app/models/concerns/spree/purchase/line_item_lookup.rb +25 -0
  92. data/app/models/concerns/spree/purchase/locale.rb +30 -0
  93. data/app/models/concerns/spree/purchase/market.rb +29 -0
  94. data/app/models/concerns/spree/purchase/payment_processing.rb +93 -0
  95. data/app/models/concerns/spree/purchase/purchase_order.rb +94 -0
  96. data/app/models/concerns/spree/purchase/quantity_rules.rb +136 -0
  97. data/app/models/concerns/spree/purchase/store_credits.rb +100 -0
  98. data/app/models/concerns/spree/purchase/taxation.rb +146 -0
  99. data/app/models/concerns/spree/purchase/totals.rb +116 -0
  100. data/app/models/concerns/spree/purchase/validations.rb +45 -0
  101. data/app/models/concerns/spree/receivable.rb +103 -0
  102. data/app/models/concerns/spree/receivable_item.rb +78 -0
  103. data/app/models/concerns/spree/refund_methods.rb +19 -0
  104. data/app/models/concerns/spree/sanitizable_rich_text.rb +109 -0
  105. data/app/models/concerns/spree/search_indexable.rb +5 -2
  106. data/app/models/concerns/spree/single_store_resource.rb +3 -1
  107. data/app/models/concerns/spree/store_data_sources.rb +75 -0
  108. data/app/models/concerns/spree/store_preferences.rb +49 -0
  109. data/app/models/concerns/spree/stores/markets.rb +75 -28
  110. data/app/models/concerns/spree/stores/setup.rb +10 -0
  111. data/app/models/concerns/spree/translatable_resource.rb +25 -6
  112. data/app/models/concerns/spree/typed_adjustment_line.rb +55 -0
  113. data/app/models/concerns/spree/user_address.rb +13 -3
  114. data/app/models/concerns/spree/user_management.rb +18 -6
  115. data/app/models/concerns/spree/user_methods.rb +5 -330
  116. data/app/models/concerns/spree/user_payment_source.rb +1 -1
  117. data/app/models/concerns/spree/user_roles.rb +18 -11
  118. data/app/models/concerns/spree/validates_store_uniqueness.rb +53 -0
  119. data/app/models/concerns/spree/vat_price_calculation.rb +58 -6
  120. data/app/models/concerns/spree/webhook_payload_redaction.rb +1 -1
  121. data/app/models/spree/ability.rb +147 -43
  122. data/app/models/spree/address.rb +387 -118
  123. data/app/models/spree/adjusters/base.rb +26 -0
  124. data/app/models/spree/adjusters/largest_remainder.rb +24 -0
  125. data/app/models/spree/adjusters/promotion.rb +253 -0
  126. data/app/models/spree/admin_user.rb +26 -0
  127. data/app/models/spree/allowed_origin.rb +1 -1
  128. data/app/models/spree/api_key.rb +34 -31
  129. data/app/models/spree/asset.rb +14 -211
  130. data/app/models/spree/authentication/strategies/base_strategy.rb +54 -3
  131. data/app/models/spree/authentication/strategies/email_password_strategy.rb +11 -3
  132. data/app/models/spree/authentication/strategies/oidc_strategy.rb +330 -0
  133. data/app/models/spree/authentication/strategy_registry.rb +83 -0
  134. data/app/models/spree/base.rb +83 -12
  135. data/app/models/spree/base_analytics_event_handler.rb +1 -1
  136. data/app/models/spree/calculator/shipping/digital_delivery.rb +5 -2
  137. data/app/models/spree/calculator/shipping/flat_rate.rb +10 -5
  138. data/app/models/spree/calculator/shipping/flexi_rate.rb +6 -0
  139. data/app/models/spree/calculator/shipping/per_item.rb +8 -3
  140. data/app/models/spree/calculator/shipping/price_sack.rb +6 -0
  141. data/app/models/spree/calculator/tiered_flat_rate.rb +5 -1
  142. data/app/models/spree/calculator.rb +10 -0
  143. data/app/models/spree/cart.rb +285 -0
  144. data/app/models/spree/carts/item_warning.rb +28 -0
  145. data/app/models/spree/carts/resolved_item.rb +37 -0
  146. data/app/models/spree/catalog.rb +503 -0
  147. data/app/models/spree/catalog_assignment.rb +41 -0
  148. data/app/models/spree/catalog_order_minimum.rb +26 -0
  149. data/app/models/spree/catalog_price.rb +100 -0
  150. data/app/models/spree/catalog_price_tier.rb +36 -0
  151. data/app/models/spree/catalog_product.rb +23 -0
  152. data/app/models/spree/catalog_quantity_rule.rb +46 -0
  153. data/app/models/spree/catalogs/product_term.rb +43 -0
  154. data/app/models/spree/category.rb +430 -12
  155. data/app/models/spree/channel/gating.rb +1 -2
  156. data/app/models/spree/channel.rb +38 -4
  157. data/app/models/spree/channel_stock_location.rb +8 -0
  158. data/app/models/spree/claim.rb +69 -0
  159. data/app/models/spree/claim_line_item.rb +46 -0
  160. data/app/models/spree/claim_reason.rb +24 -0
  161. data/app/models/spree/classification.rb +7 -49
  162. data/app/models/spree/collection.rb +269 -0
  163. data/app/models/spree/collection_rule.rb +58 -0
  164. data/app/models/spree/collection_rules/available_on.rb +21 -0
  165. data/app/models/spree/collection_rules/sale.rb +18 -0
  166. data/app/models/spree/collection_rules/tag.rb +21 -0
  167. data/app/models/spree/commission_line.rb +85 -0
  168. data/app/models/spree/commission_rate.rb +370 -0
  169. data/app/models/spree/commission_rate_value.rb +47 -0
  170. data/app/models/spree/commission_rule.rb +77 -0
  171. data/app/models/spree/commission_rule_product.rb +19 -0
  172. data/app/models/spree/commission_rules/category_rule.rb +33 -0
  173. data/app/models/spree/commission_rules/item_total_rule.rb +31 -0
  174. data/app/models/spree/commission_rules/product_rule.rb +55 -0
  175. data/app/models/spree/commission_rules/seller_rule.rb +33 -0
  176. data/app/models/spree/commission_tax.rb +51 -0
  177. data/app/models/spree/commissions/context.rb +71 -0
  178. data/app/models/spree/company.rb +347 -0
  179. data/app/models/spree/company_invitation.rb +81 -0
  180. data/app/models/spree/company_membership.rb +25 -0
  181. data/app/models/spree/consent_record.rb +125 -0
  182. data/app/models/spree/country.rb +128 -52
  183. data/app/models/spree/coupon_code.rb +18 -5
  184. data/app/models/spree/credit_card.rb +10 -9
  185. data/app/models/spree/current.rb +152 -18
  186. data/app/models/spree/custom_field.rb +160 -7
  187. data/app/models/spree/custom_field_definition/search_capabilities.rb +98 -0
  188. data/app/models/spree/custom_field_definition.rb +219 -5
  189. data/app/models/spree/custom_fields/boolean.rb +15 -0
  190. data/app/models/spree/custom_fields/json.rb +38 -0
  191. data/app/models/spree/custom_fields/long_text.rb +11 -0
  192. data/app/models/spree/custom_fields/number.rb +23 -0
  193. data/app/models/spree/custom_fields/rich_text.rb +23 -0
  194. data/app/models/spree/custom_fields/short_text.rb +15 -0
  195. data/app/models/spree/customer.rb +34 -0
  196. data/app/models/spree/customer_group.rb +12 -12
  197. data/app/models/spree/customer_group_user.rb +6 -4
  198. data/app/models/spree/data_feed.rb +1 -1
  199. data/app/models/spree/data_request.rb +98 -0
  200. data/app/models/spree/delivery.rb +169 -0
  201. data/app/models/spree/delivery_method.rb +566 -0
  202. data/app/models/spree/delivery_method_rule.rb +55 -0
  203. data/app/models/spree/delivery_method_rule_product.rb +8 -0
  204. data/app/models/spree/delivery_method_rules/channel_rule.rb +41 -0
  205. data/app/models/spree/delivery_method_rules/company_rule.rb +24 -0
  206. data/app/models/spree/delivery_method_rules/excluded_products_rule.rb +48 -0
  207. data/app/models/spree/delivery_method_rules/item_total_rule.rb +19 -0
  208. data/app/models/spree/delivery_method_rules/volume_rule.rb +35 -0
  209. data/app/models/spree/delivery_method_rules/weight_rule.rb +19 -0
  210. data/app/models/spree/delivery_method_service.rb +24 -0
  211. data/app/models/spree/delivery_method_stock_location.rb +11 -0
  212. data/app/models/spree/delivery_origin_group.rb +73 -0
  213. data/app/models/spree/delivery_origin_group_location.rb +9 -0
  214. data/app/models/spree/delivery_profile.rb +141 -0
  215. data/app/models/spree/delivery_profiles/digital.rb +20 -0
  216. data/app/models/spree/delivery_profiles/shipping.rb +12 -0
  217. data/app/models/spree/delivery_rate.rb +164 -0
  218. data/app/models/spree/delivery_rate_provider/base.rb +120 -0
  219. data/app/models/spree/delivery_rate_provider/estimate.rb +44 -0
  220. data/app/models/spree/delivery_rate_provider/freight.rb +41 -0
  221. data/app/models/spree/delivery_rate_provider/internal.rb +28 -0
  222. data/app/models/spree/delivery_rate_provider/service_catalog.rb +48 -0
  223. data/app/models/spree/delivery_zone.rb +64 -0
  224. data/app/models/spree/delivery_zone_member.rb +98 -0
  225. data/app/models/spree/digital.rb +12 -16
  226. data/app/models/spree/digital_asset.rb +144 -0
  227. data/app/models/spree/digital_asset_provider/base.rb +95 -0
  228. data/app/models/spree/digital_asset_provider/file.rb +20 -0
  229. data/app/models/spree/digital_delivery.rb +28 -0
  230. data/app/models/spree/digital_link.rb +31 -11
  231. data/app/models/spree/discount.rb +51 -0
  232. data/app/models/spree/event.rb +6 -6
  233. data/app/models/spree/exchange.rb +50 -44
  234. data/app/models/spree/exchange_line_item.rb +52 -0
  235. data/app/models/spree/export.rb +128 -21
  236. data/app/models/spree/exports/customers.rb +2 -2
  237. data/app/models/spree/exports/gift_cards.rb +2 -2
  238. data/app/models/spree/exports/newsletter_subscribers.rb +2 -2
  239. data/app/models/spree/exports/orders.rb +35 -5
  240. data/app/models/spree/exports/price_list_prices.rb +107 -0
  241. data/app/models/spree/exports/products.rb +4 -4
  242. data/app/models/spree/exports/purchase_orders.rb +39 -0
  243. data/app/models/spree/exports/report.rb +95 -0
  244. data/app/models/spree/external_reference.rb +77 -0
  245. data/app/models/spree/external_video.rb +117 -0
  246. data/app/models/spree/fee.rb +40 -0
  247. data/app/models/spree/freight_summary/line.rb +257 -0
  248. data/app/models/spree/freight_summary.rb +180 -0
  249. data/app/models/spree/fulfillment/custom_events.rb +96 -0
  250. data/app/models/spree/fulfillment.rb +973 -0
  251. data/app/models/spree/fulfillment_changer.rb +170 -0
  252. data/app/models/spree/fulfillment_item.rb +148 -0
  253. data/app/models/spree/fulfillment_provider/base.rb +130 -0
  254. data/app/models/spree/fulfillment_provider/digital.rb +44 -0
  255. data/app/models/spree/fulfillment_provider/manual.rb +15 -0
  256. data/app/models/spree/fulfillment_provider/pickup.rb +38 -0
  257. data/app/models/spree/fulfillment_provider/pickup_point.rb +24 -0
  258. data/app/models/spree/fulfilment_changer.rb +3 -164
  259. data/app/models/spree/gateway/bogus.rb +5 -4
  260. data/app/models/spree/gateway/custom_payment_source_method.rb +5 -5
  261. data/app/models/spree/gateway.rb +2 -2
  262. data/app/models/spree/gateway_customer.rb +12 -4
  263. data/app/models/spree/gift_card.rb +140 -48
  264. data/app/models/spree/gift_card_batch.rb +2 -2
  265. data/app/models/spree/import.rb +136 -98
  266. data/app/models/spree/import_mapping.rb +1 -1
  267. data/app/models/spree/import_row.rb +35 -31
  268. data/app/models/spree/import_schemas/price_list_prices.rb +13 -0
  269. data/app/models/spree/import_schemas/products.rb +8 -1
  270. data/app/models/spree/import_schemas/purchase_orders.rb +19 -0
  271. data/app/models/spree/imports/price_list_prices.rb +72 -0
  272. data/app/models/spree/imports/purchase_orders.rb +28 -0
  273. data/app/models/spree/integration.rb +128 -5
  274. data/app/models/spree/inventory_provider/base.rb +30 -0
  275. data/app/models/spree/inventory_provider/internal.rb +22 -0
  276. data/app/models/spree/inventory_unit.rb +3 -139
  277. data/app/models/spree/invitation.rb +53 -47
  278. data/app/models/spree/label_purchase.rb +29 -0
  279. data/app/models/spree/line_item.rb +240 -106
  280. data/app/models/spree/market.rb +106 -15
  281. data/app/models/spree/market_country.rb +10 -5
  282. data/app/models/spree/media.rb +497 -0
  283. data/app/models/spree/media_usage_reference.rb +27 -0
  284. data/app/models/spree/metafield.rb +14 -102
  285. data/app/models/spree/metafield_definition.rb +11 -128
  286. data/app/models/spree/metafields.rb +32 -0
  287. data/app/models/spree/newsletter_subscriber.rb +10 -9
  288. data/app/models/spree/number_generators/base.rb +56 -0
  289. data/app/models/spree/number_generators/random.rb +23 -0
  290. data/app/models/spree/number_generators/sequential.rb +88 -0
  291. data/app/models/spree/number_sequence.rb +86 -0
  292. data/app/models/spree/option_type.rb +18 -32
  293. data/app/models/spree/option_type_product_type.rb +8 -0
  294. data/app/models/spree/option_value.rb +19 -13
  295. data/app/models/spree/option_value_variant.rb +0 -1
  296. data/app/models/spree/order.rb +681 -576
  297. data/app/models/spree/order_cancellation_reason.rb +25 -0
  298. data/app/models/spree/order_group.rb +244 -0
  299. data/app/models/spree/order_inventory.rb +42 -33
  300. data/app/models/spree/order_merger.rb +11 -6
  301. data/app/models/spree/order_promotion.rb +32 -6
  302. data/app/models/spree/order_routing/has_strategy_preference.rb +1 -2
  303. data/app/models/spree/order_routing/rules/minimize_splits.rb +6 -6
  304. data/app/models/spree/order_routing/strategy/rules.rb +12 -8
  305. data/app/models/spree/order_routing_rule.rb +5 -15
  306. data/app/models/spree/order_updater.rb +131 -163
  307. data/app/models/spree/package_type.rb +191 -0
  308. data/app/models/spree/payment/custom_events.rb +52 -5
  309. data/app/models/spree/payment/gateway_options.rb +20 -5
  310. data/app/models/spree/payment/processing.rb +95 -116
  311. data/app/models/spree/payment.rb +299 -123
  312. data/app/models/spree/payment_method/check.rb +4 -3
  313. data/app/models/spree/payment_method/session_instrumentation.rb +34 -0
  314. data/app/models/spree/payment_method/store_credit.rb +4 -2
  315. data/app/models/spree/payment_method.rb +106 -9
  316. data/app/models/spree/payment_session.rb +150 -53
  317. data/app/models/spree/payment_setup_session.rb +4 -37
  318. data/app/models/spree/payment_source.rb +5 -4
  319. data/app/models/spree/payment_split.rb +90 -0
  320. data/app/models/spree/payout_provider/base.rb +218 -0
  321. data/app/models/spree/payout_provider/system.rb +36 -0
  322. data/app/models/spree/permission_sets.rb +17 -0
  323. data/app/models/spree/pickup_point_option.rb +28 -0
  324. data/app/models/spree/pickup_point_provider/base.rb +24 -0
  325. data/app/models/spree/policy.rb +12 -11
  326. data/app/models/spree/price.rb +91 -5
  327. data/app/models/spree/price_adjustment_tier.rb +63 -0
  328. data/app/models/spree/price_history.rb +1 -1
  329. data/app/models/spree/price_list.rb +266 -142
  330. data/app/models/spree/price_rule.rb +51 -7
  331. data/app/models/spree/price_rules/channel_rule.rb +3 -0
  332. data/app/models/spree/price_rules/customer_group_rule.rb +10 -1
  333. data/app/models/spree/price_rules/market_rule.rb +6 -0
  334. data/app/models/spree/price_rules/user_rule.rb +12 -3
  335. data/app/models/spree/price_rules/volume_rule.rb +6 -0
  336. data/app/models/spree/price_rules/zone_rule.rb +5 -13
  337. data/app/models/spree/pricing_provider/base.rb +51 -0
  338. data/app/models/spree/pricing_provider/internal.rb +277 -0
  339. data/app/models/spree/pricing_provider.rb +28 -0
  340. data/app/models/spree/product.rb +528 -544
  341. data/app/models/spree/product_category.rb +65 -0
  342. data/app/models/spree/product_collection.rb +20 -0
  343. data/app/models/spree/product_option_type.rb +0 -1
  344. data/app/models/spree/product_promotion_rule.rb +0 -1
  345. data/app/models/spree/product_publication.rb +0 -1
  346. data/app/models/spree/product_submission.rb +72 -0
  347. data/app/models/spree/product_type.rb +116 -0
  348. data/app/models/spree/product_type_category.rb +8 -0
  349. data/app/models/spree/product_type_custom_field_definition.rb +39 -0
  350. data/app/models/spree/promotion/actions/create_adjustment.rb +9 -7
  351. data/app/models/spree/promotion/actions/create_item_adjustments.rb +11 -16
  352. data/app/models/spree/promotion/actions/create_line_items.rb +19 -8
  353. data/app/models/spree/promotion/actions/free_shipping.rb +12 -18
  354. data/app/models/spree/promotion/rules/category.rb +181 -0
  355. data/app/models/spree/promotion/rules/channel.rb +3 -3
  356. data/app/models/spree/promotion/rules/country.rb +43 -17
  357. data/app/models/spree/promotion/rules/currency.rb +2 -2
  358. data/app/models/spree/promotion/rules/customer_group.rb +13 -5
  359. data/app/models/spree/promotion/rules/first_order.rb +3 -3
  360. data/app/models/spree/promotion/rules/item_total.rb +2 -2
  361. data/app/models/spree/promotion/rules/market.rb +3 -3
  362. data/app/models/spree/promotion/rules/one_use_per_user.rb +4 -4
  363. data/app/models/spree/promotion/rules/option_value.rb +34 -11
  364. data/app/models/spree/promotion/rules/product.rb +3 -5
  365. data/app/models/spree/promotion/rules/taxon.rb +10 -131
  366. data/app/models/spree/promotion/rules/user.rb +14 -22
  367. data/app/models/spree/promotion/rules/user_logged_in.rb +3 -3
  368. data/app/models/spree/promotion.rb +59 -21
  369. data/app/models/spree/promotion_action.rb +52 -11
  370. data/app/models/spree/promotion_action_line_item.rb +1 -1
  371. data/app/models/spree/promotion_handler/coupon.rb +44 -16
  372. data/app/models/spree/promotion_handler/free_shipping.rb +1 -1
  373. data/app/models/spree/promotion_handler/promotion_duplicator.rb +1 -1
  374. data/app/models/spree/promotion_rule.rb +2 -9
  375. data/app/models/spree/promotion_rule_category.rb +22 -0
  376. data/app/models/spree/promotion_rule_taxon.rb +7 -6
  377. data/app/models/spree/promotion_rule_user.rb +2 -2
  378. data/app/models/spree/prototype.rb +3 -13
  379. data/app/models/spree/purchase_order.rb +95 -0
  380. data/app/models/spree/purchase_order_item.rb +40 -0
  381. data/app/models/spree/quantity_rule.rb +114 -0
  382. data/app/models/spree/refresh_token.rb +20 -2
  383. data/app/models/spree/refund.rb +96 -42
  384. data/app/models/spree/refund_reason.rb +32 -11
  385. data/app/models/spree/return.rb +151 -0
  386. data/app/models/spree/return_authorization_reason.rb +11 -6
  387. data/app/models/spree/return_line_item.rb +42 -0
  388. data/app/models/spree/return_reason.rb +26 -0
  389. data/app/models/spree/rich_text_sanitizer.rb +99 -0
  390. data/app/models/spree/role.rb +179 -4
  391. data/app/models/spree/role_user.rb +5 -21
  392. data/app/models/spree/saved_report.rb +56 -0
  393. data/app/models/spree/search_provider/base.rb +8 -2
  394. data/app/models/spree/search_provider/database.rb +73 -22
  395. data/app/models/spree/search_provider.rb +29 -0
  396. data/app/models/spree/seller.rb +475 -0
  397. data/app/models/spree/seller_balance.rb +86 -0
  398. data/app/models/spree/seller_payout.rb +116 -0
  399. data/app/models/spree/seller_requirement.rb +274 -0
  400. data/app/models/spree/seller_requirement_custom_field.rb +38 -0
  401. data/app/models/spree/seller_requirement_status.rb +144 -0
  402. data/app/models/spree/seller_requirement_submission.rb +91 -0
  403. data/app/models/spree/seller_requirements/accept_terms.rb +104 -0
  404. data/app/models/spree/seller_requirements/attestation.rb +22 -0
  405. data/app/models/spree/seller_requirements/billing_address.rb +13 -0
  406. data/app/models/spree/seller_requirements/complete_profile.rb +26 -0
  407. data/app/models/spree/seller_requirements/delivery_method.rb +20 -0
  408. data/app/models/spree/seller_requirements/document.rb +62 -0
  409. data/app/models/spree/seller_requirements/minimum_products.rb +20 -0
  410. data/app/models/spree/seller_requirements/operator_review.rb +23 -0
  411. data/app/models/spree/seller_requirements/package_type.rb +40 -0
  412. data/app/models/spree/seller_requirements/payout_account.rb +55 -0
  413. data/app/models/spree/seller_requirements/policy.rb +43 -0
  414. data/app/models/spree/seller_requirements/required_custom_fields.rb +56 -0
  415. data/app/models/spree/seller_requirements/returns_address.rb +19 -0
  416. data/app/models/spree/seller_transfer.rb +166 -0
  417. data/app/models/spree/setup_tasks.rb +3 -6
  418. data/app/models/spree/shipment.rb +3 -560
  419. data/app/models/spree/shipping_calculator.rb +7 -0
  420. data/app/models/spree/shipping_document.rb +32 -0
  421. data/app/models/spree/shipping_label.rb +146 -0
  422. data/app/models/spree/shipping_method.rb +3 -146
  423. data/app/models/spree/shipping_rate.rb +3 -97
  424. data/app/models/spree/state.rb +57 -23
  425. data/app/models/spree/stock/availability_validator.rb +1 -1
  426. data/app/models/spree/stock/content_item.rb +1 -0
  427. data/app/models/spree/stock/coordinator.rb +69 -9
  428. data/app/models/spree/stock/estimator.rb +198 -33
  429. data/app/models/spree/stock/inventory_unit_builder.rb +6 -1
  430. data/app/models/spree/stock/package.rb +145 -14
  431. data/app/models/spree/stock/packer.rb +20 -3
  432. data/app/models/spree/stock/prioritizer.rb +27 -4
  433. data/app/models/spree/stock/quantifier.rb +63 -38
  434. data/app/models/spree/stock/splitter/base.rb +3 -1
  435. data/app/models/spree/stock/splitter/delivery_profile.rb +41 -0
  436. data/app/models/spree/stock_item.rb +3 -147
  437. data/app/models/spree/stock_level/custom_events.rb +48 -0
  438. data/app/models/spree/stock_level.rb +305 -0
  439. data/app/models/spree/stock_location.rb +165 -87
  440. data/app/models/spree/stock_movement/custom_events.rb +1 -1
  441. data/app/models/spree/stock_movement.rb +166 -22
  442. data/app/models/spree/stock_receipt.rb +61 -0
  443. data/app/models/spree/stock_receipt_item.rb +35 -0
  444. data/app/models/spree/stock_reservation.rb +114 -11
  445. data/app/models/spree/stock_transfer.rb +84 -59
  446. data/app/models/spree/stock_transfer_item.rb +25 -0
  447. data/app/models/spree/store.rb +431 -107
  448. data/app/models/spree/store_credit.rb +88 -21
  449. data/app/models/spree/store_credit_category.rb +21 -28
  450. data/app/models/spree/store_credit_event.rb +1 -1
  451. data/app/models/spree/store_credit_type.rb +10 -2
  452. data/app/models/spree/store_payment_method.rb +0 -1
  453. data/app/models/spree/store_product.rb +2 -2
  454. data/app/models/spree/store_promotion.rb +0 -1
  455. data/app/models/spree/storefront/access_denied.rb +22 -0
  456. data/app/models/spree/storefront/access_policy.rb +180 -0
  457. data/app/models/spree/subscriber.rb +6 -6
  458. data/app/models/spree/supplier.rb +76 -0
  459. data/app/models/spree/tax_category.rb +15 -5
  460. data/app/models/spree/tax_exemption/item_override.rb +22 -0
  461. data/app/models/spree/tax_exemption.rb +81 -0
  462. data/app/models/spree/tax_exemption_certificate.rb +95 -0
  463. data/app/models/spree/tax_identifier.rb +209 -0
  464. data/app/models/spree/tax_identifiers/validation_result.rb +41 -0
  465. data/app/models/spree/tax_identifiers/validator/base.rb +53 -0
  466. data/app/models/spree/tax_identifiers/validator/eu_vat.rb +76 -0
  467. data/app/models/spree/tax_line.rb +79 -0
  468. data/app/models/spree/tax_provider/base.rb +150 -0
  469. data/app/models/spree/tax_provider/internal.rb +227 -0
  470. data/app/models/spree/tax_rate.rb +63 -100
  471. data/app/models/spree/taxon.rb +13 -514
  472. data/app/models/spree/taxon_rule.rb +8 -9
  473. data/app/models/spree/taxonomy.rb +8 -42
  474. data/app/models/spree/user_identity.rb +2 -2
  475. data/app/models/spree/variant.rb +629 -150
  476. data/app/models/spree/variant_media.rb +6 -7
  477. data/app/models/spree/webhook_endpoint.rb +2 -1
  478. data/app/models/spree/wished_item.rb +3 -31
  479. data/app/models/spree/wishlist.rb +17 -10
  480. data/app/models/spree/wishlist_item/custom_events.rb +42 -0
  481. data/app/models/spree/wishlist_item.rb +37 -0
  482. data/app/models/spree/zone.rb +24 -182
  483. data/app/models/spree/zone_member.rb +5 -8
  484. data/app/presenters/spree/csv/custom_fields_helper.rb +25 -0
  485. data/app/presenters/spree/csv/customer_presenter.rb +7 -6
  486. data/app/presenters/spree/csv/gift_card_presenter.rb +9 -8
  487. data/app/presenters/spree/csv/newsletter_subscriber_presenter.rb +7 -6
  488. data/app/presenters/spree/csv/order_line_item_presenter.rb +53 -21
  489. data/app/presenters/spree/csv/price_list_price_presenter.rb +41 -0
  490. data/app/presenters/spree/csv/product_variant_presenter.rb +22 -21
  491. data/app/presenters/spree/csv/purchase_order_item_presenter.rb +50 -0
  492. data/app/presenters/spree/data_feeds/google_presenter.rb +24 -10
  493. data/app/presenters/spree/filters/options_presenter.rb +2 -2
  494. data/app/services/spree/addresses/create.rb +70 -27
  495. data/app/services/spree/addresses/helper.rb +17 -54
  496. data/app/services/spree/addresses/update.rb +53 -31
  497. data/app/services/spree/carts/associate.rb +46 -0
  498. data/app/services/spree/carts/check_availability.rb +72 -0
  499. data/app/services/spree/carts/create.rb +7 -1
  500. data/app/services/spree/carts/destroy.rb +71 -0
  501. data/app/services/spree/carts/empty.rb +42 -0
  502. data/app/services/spree/carts/partition_by_seller.rb +71 -0
  503. data/app/services/spree/carts/price_items.rb +119 -0
  504. data/app/services/spree/carts/remove_item.rb +42 -0
  505. data/app/services/spree/carts/remove_line_item.rb +42 -0
  506. data/app/services/spree/carts/remove_out_of_stock_items.rb +104 -0
  507. data/app/services/spree/carts/set_quantity.rb +39 -0
  508. data/app/services/spree/carts/split_by_seller.rb +475 -0
  509. data/app/services/spree/carts/update.rb +157 -51
  510. data/app/services/spree/catalogs/resolve_prices.rb +192 -0
  511. data/app/services/spree/catalogs/resolve_quantity_rules.rb +115 -0
  512. data/app/services/spree/catalogs/set_assignments.rb +59 -0
  513. data/app/services/spree/catalogs/set_order_minimums.rb +44 -0
  514. data/app/services/spree/catalogs/set_product_terms.rb +83 -0
  515. data/app/services/spree/categories/add_products.rb +51 -0
  516. data/app/services/spree/categories/remove_products.rb +72 -0
  517. data/app/services/spree/checkout/add_store_credit.rb +11 -79
  518. data/app/services/spree/checkout/advance.rb +38 -38
  519. data/app/services/spree/checkout/remove_store_credit.rb +7 -12
  520. data/app/services/spree/collections/add_products.rb +42 -0
  521. data/app/services/spree/collections/regenerate_products.rb +67 -0
  522. data/app/services/spree/collections/remove_products.rb +65 -0
  523. data/app/services/spree/commissions/calculate_line.rb +132 -0
  524. data/app/services/spree/commissions/commission_order.rb +185 -0
  525. data/app/services/spree/commissions/resolve_rate.rb +114 -0
  526. data/app/services/spree/commissions/resolve_tax_rate.rb +78 -0
  527. data/app/services/spree/companies/add_member.rb +38 -0
  528. data/app/services/spree/company_invitations/accept.rb +60 -0
  529. data/app/services/spree/coupon_codes/coupon_codes_handler.rb +14 -2
  530. data/app/services/spree/customers/data_export.rb +372 -0
  531. data/app/services/spree/data_requests/create.rb +77 -0
  532. data/app/services/spree/deliveries/create.rb +56 -0
  533. data/app/services/spree/deliveries/destroy.rb +39 -0
  534. data/app/services/spree/deliveries/upsert_primary.rb +43 -0
  535. data/app/services/spree/fulfillments/recalculate_delivery.rb +62 -0
  536. data/app/services/spree/fulfillments/stand_down_provider.rb +47 -0
  537. data/app/services/spree/imports/row_processors/customer.rb +8 -8
  538. data/app/services/spree/imports/row_processors/price_list_price.rb +164 -0
  539. data/app/services/spree/imports/row_processors/product_variant.rb +193 -57
  540. data/app/services/spree/imports/row_processors/purchase_order.rb +157 -0
  541. data/app/services/spree/media/destroy.rb +88 -0
  542. data/app/services/spree/media/usage.rb +175 -0
  543. data/app/services/spree/newsletter/link_user.rb +4 -3
  544. data/app/services/spree/newsletter/subscribe.rb +3 -3
  545. data/app/services/spree/newsletter/verify.rb +6 -2
  546. data/app/services/spree/order_groups/allocate_payments.rb +101 -0
  547. data/app/services/spree/orders/approve.rb +11 -24
  548. data/app/services/spree/orders/build_fulfillments.rb +29 -0
  549. data/app/services/spree/orders/create.rb +38 -17
  550. data/app/services/spree/orders/create_from_cart.rb +88 -0
  551. data/app/services/spree/orders/create_user_account.rb +14 -67
  552. data/app/services/spree/orders/discounts/create.rb +84 -0
  553. data/app/services/spree/orders/discounts/destroy.rb +25 -0
  554. data/app/services/spree/orders/discounts/update.rb +29 -0
  555. data/app/services/spree/orders/fees/create.rb +28 -0
  556. data/app/services/spree/orders/fees/destroy.rb +22 -0
  557. data/app/services/spree/orders/fees/update.rb +25 -0
  558. data/app/services/spree/orders/remove_item.rb +13 -0
  559. data/app/services/spree/orders/remove_line_item.rb +13 -0
  560. data/app/services/spree/orders/update.rb +56 -11
  561. data/app/services/spree/orders/update_item.rb +105 -0
  562. data/app/services/spree/orders/update_statuses.rb +109 -0
  563. data/app/services/spree/payments/create.rb +31 -5
  564. data/app/services/spree/prices/bulk_upsert.rb +156 -30
  565. data/app/services/spree/product_submissions/close.rb +46 -0
  566. data/app/services/spree/product_types/apply_to_products.rb +167 -0
  567. data/app/services/spree/products/auto_match_collections.rb +75 -0
  568. data/app/services/spree/products/auto_match_taxons.rb +7 -54
  569. data/app/services/spree/products/duplicator.rb +16 -22
  570. data/app/services/spree/products/for_context.rb +54 -0
  571. data/app/services/spree/products/select_buy_box.rb +98 -0
  572. data/app/services/spree/purchases/amount_due_at_checkout.rb +33 -0
  573. data/app/services/spree/returns/eligibility_validator.rb +53 -0
  574. data/app/services/spree/sample_data/helper.rb +3 -3
  575. data/app/services/spree/sample_data/import_builder.rb +8 -4
  576. data/app/services/spree/sample_data/import_runner.rb +7 -4
  577. data/app/services/spree/sample_data/loader.rb +15 -6
  578. data/app/services/spree/search_provider/custom_field_schema.rb +153 -0
  579. data/app/services/spree/seeds/admin_user.rb +72 -12
  580. data/app/services/spree/seeds/all.rb +24 -13
  581. data/app/services/spree/seeds/commission_rates.rb +53 -0
  582. data/app/services/spree/seeds/digital_delivery.rb +18 -8
  583. data/app/services/spree/seeds/product_types.rb +23 -0
  584. data/app/services/spree/seeds/returns_environment.rb +44 -16
  585. data/app/services/spree/seeds/roles.rb +6 -1
  586. data/app/services/spree/seeds/saved_reports.rb +107 -0
  587. data/app/services/spree/seeds/seller_requirements.rb +19 -0
  588. data/app/services/spree/seeds/stores.rb +1 -1
  589. data/app/services/spree/seeds/tax_categories.rb +9 -2
  590. data/app/services/spree/sellers/requirements.rb +129 -0
  591. data/app/services/spree/shipments/update.rb +4 -33
  592. data/app/services/spree/shipping_labels/confirm_refund.rb +47 -0
  593. data/app/services/spree/shipping_labels/store_file.rb +68 -0
  594. data/app/services/spree/stock_levels/bulk_upsert.rb +67 -0
  595. data/app/services/spree/stock_levels/correct.rb +69 -0
  596. data/app/services/spree/stock_levels/recount.rb +103 -0
  597. data/app/services/spree/stock_locations/stock_items/create.rb +8 -13
  598. data/app/services/spree/stock_locations/stock_levels/create.rb +25 -0
  599. data/app/services/spree/stock_reservations/extend.rb +9 -5
  600. data/app/services/spree/stock_reservations/release.rb +4 -3
  601. data/app/services/spree/stock_reservations/reserve.rb +43 -32
  602. data/app/services/spree/store_credits/apply.rb +93 -0
  603. data/app/services/spree/store_credits/remove.rb +17 -0
  604. data/app/services/spree/stores/provision_defaults.rb +269 -0
  605. data/app/services/spree/tax/resolve_exemptions.rb +44 -0
  606. data/app/services/spree/tax/unusable_exemption_error.rb +8 -0
  607. data/app/services/spree/tax_identifiers/validate.rb +77 -0
  608. data/app/services/spree/taxons/add_products.rb +6 -39
  609. data/app/services/spree/taxons/regenerate_products.rb +8 -53
  610. data/app/services/spree/taxons/remove_products.rb +6 -60
  611. data/app/services/spree/variants/remove_line_items.rb +6 -1
  612. data/app/subscribers/spree/event_log_subscriber.rb +1 -1
  613. data/app/subscribers/spree/order_commission_subscriber.rb +23 -0
  614. data/app/subscribers/spree/order_placed_subscriber.rb +58 -0
  615. data/app/subscribers/spree/order_status_subscriber.rb +82 -0
  616. data/app/subscribers/spree/payment_split_subscriber.rb +71 -0
  617. data/app/subscribers/spree/product_metrics_subscriber.rb +2 -2
  618. data/app/subscribers/spree/seller_onboarding_subscriber.rb +32 -0
  619. data/app/subscribers/spree/seller_transfer_reversal_subscriber.rb +26 -0
  620. data/app/subscribers/spree/seller_transfer_subscriber.rb +26 -0
  621. data/app/subscribers/spree/seller_user_email_subscriber.rb +27 -0
  622. data/app/subscribers/spree/tax_identifier_validation_subscriber.rb +26 -0
  623. data/app/validators/spree/addresses/phone_validator.rb +2 -2
  624. data/app/validators/spree/attachment_content_type_validator.rb +183 -0
  625. data/app/validators/spree/password_length_validator.rb +23 -0
  626. data/app/views/spree/seller_user_mailer/password_reset_email.html.erb +8 -0
  627. data/app/views/spree/shared/_purchased_items_styles.html.erb +7 -0
  628. data/app/workflows/spree/carts/add_item.rb +234 -0
  629. data/app/workflows/spree/carts/complete.rb +516 -0
  630. data/app/workflows/spree/carts/merge.rb +90 -0
  631. data/app/workflows/spree/carts/recalculate.rb +54 -0
  632. data/app/workflows/spree/carts/recalculate_totals.rb +233 -0
  633. data/app/workflows/spree/carts/upsert_items.rb +368 -0
  634. data/app/workflows/spree/catalogs/activate.rb +51 -0
  635. data/app/workflows/spree/catalogs/create.rb +83 -0
  636. data/app/workflows/spree/catalogs/deactivate.rb +35 -0
  637. data/app/workflows/spree/catalogs/set_price_list.rb +130 -0
  638. data/app/workflows/spree/catalogs/update.rb +85 -0
  639. data/app/workflows/spree/claims/approve.rb +38 -0
  640. data/app/workflows/spree/claims/cancel.rb +37 -0
  641. data/app/workflows/spree/claims/create.rb +82 -0
  642. data/app/workflows/spree/claims/deny.rb +35 -0
  643. data/app/workflows/spree/claims/resolve.rb +166 -0
  644. data/app/workflows/spree/customers/anonymize.rb +502 -0
  645. data/app/workflows/spree/customers/create.rb +247 -0
  646. data/app/workflows/spree/data_requests/fulfill.rb +165 -0
  647. data/app/workflows/spree/deliveries/update_tracking.rb +96 -0
  648. data/app/workflows/spree/exchanges/approve.rb +38 -0
  649. data/app/workflows/spree/exchanges/cancel.rb +40 -0
  650. data/app/workflows/spree/exchanges/create.rb +90 -0
  651. data/app/workflows/spree/exchanges/fulfill.rb +146 -0
  652. data/app/workflows/spree/exchanges/receive.rb +86 -0
  653. data/app/workflows/spree/fulfillments/cancel.rb +87 -0
  654. data/app/workflows/spree/fulfillments/create.rb +346 -0
  655. data/app/workflows/spree/fulfillments/fulfill.rb +354 -0
  656. data/app/workflows/spree/fulfillments/mark_delivered.rb +67 -0
  657. data/app/workflows/spree/fulfillments/purchase_label.rb +40 -0
  658. data/app/workflows/spree/fulfillments/update.rb +123 -0
  659. data/app/workflows/spree/gift_cards/apply.rb +235 -0
  660. data/app/workflows/spree/gift_cards/cancel.rb +43 -0
  661. data/app/workflows/spree/gift_cards/redeem.rb +59 -0
  662. data/app/workflows/spree/gift_cards/remove.rb +78 -0
  663. data/app/workflows/spree/imports/complete.rb +45 -0
  664. data/app/workflows/spree/imports/complete_mapping.rb +50 -0
  665. data/app/workflows/spree/imports/retry_failed_rows.rb +46 -0
  666. data/app/workflows/spree/imports/start_mapping.rb +48 -0
  667. data/app/workflows/spree/imports/start_processing.rb +36 -0
  668. data/app/workflows/spree/invitations/accept.rb +50 -0
  669. data/app/workflows/spree/orders/add_item.rb +12 -0
  670. data/app/workflows/spree/orders/cancel.rb +261 -0
  671. data/app/workflows/spree/orders/complete.rb +169 -0
  672. data/app/workflows/spree/orders/recalculate.rb +11 -0
  673. data/app/workflows/spree/orders/recalculate_totals.rb +12 -0
  674. data/app/workflows/spree/orders/upsert_items.rb +28 -0
  675. data/app/workflows/spree/payment_sessions/complete.rb +44 -0
  676. data/app/workflows/spree/payments/capture.rb +109 -0
  677. data/app/workflows/spree/payments/handle_webhook.rb +100 -0
  678. data/app/workflows/spree/payments/process.rb +73 -0
  679. data/app/workflows/spree/payments/void.rb +72 -0
  680. data/app/workflows/spree/price_lists/activate.rb +49 -0
  681. data/app/workflows/spree/price_lists/create.rb +57 -0
  682. data/app/workflows/spree/price_lists/deactivate.rb +30 -0
  683. data/app/workflows/spree/price_lists/update.rb +117 -0
  684. data/app/workflows/spree/products/activate.rb +33 -0
  685. data/app/workflows/spree/products/approve.rb +52 -0
  686. data/app/workflows/spree/products/archive.rb +44 -0
  687. data/app/workflows/spree/products/create.rb +82 -0
  688. data/app/workflows/spree/products/destroy.rb +35 -0
  689. data/app/workflows/spree/products/draft.rb +49 -0
  690. data/app/workflows/spree/products/nested_attributes.rb +242 -0
  691. data/app/workflows/spree/products/propose.rb +62 -0
  692. data/app/workflows/spree/products/reject.rb +54 -0
  693. data/app/workflows/spree/products/update.rb +83 -0
  694. data/app/workflows/spree/purchase_orders/cancel.rb +57 -0
  695. data/app/workflows/spree/purchase_orders/close.rb +57 -0
  696. data/app/workflows/spree/purchase_orders/create.rb +68 -0
  697. data/app/workflows/spree/purchase_orders/mark_draft.rb +49 -0
  698. data/app/workflows/spree/purchase_orders/mark_ordered.rb +50 -0
  699. data/app/workflows/spree/purchase_orders/receive.rb +65 -0
  700. data/app/workflows/spree/purchase_orders/update.rb +56 -0
  701. data/app/workflows/spree/receivables/incoming_counter.rb +50 -0
  702. data/app/workflows/spree/refunds/create.rb +136 -0
  703. data/app/workflows/spree/refunds/order_payments.rb +129 -0
  704. data/app/workflows/spree/returns/approve.rb +47 -0
  705. data/app/workflows/spree/returns/cancel.rb +52 -0
  706. data/app/workflows/spree/returns/create.rb +135 -0
  707. data/app/workflows/spree/returns/purchase_label.rb +32 -0
  708. data/app/workflows/spree/returns/receive.rb +94 -0
  709. data/app/workflows/spree/returns/refund.rb +134 -0
  710. data/app/workflows/spree/seller_payouts/complete.rb +80 -0
  711. data/app/workflows/spree/seller_payouts/sweep.rb +192 -0
  712. data/app/workflows/spree/seller_requirement_submissions/accept.rb +40 -0
  713. data/app/workflows/spree/seller_requirement_submissions/create.rb +104 -0
  714. data/app/workflows/spree/seller_requirement_submissions/reject.rb +45 -0
  715. data/app/workflows/spree/seller_requirement_submissions/reviewable.rb +19 -0
  716. data/app/workflows/spree/seller_requirement_submissions/waive.rb +64 -0
  717. data/app/workflows/spree/seller_transfers/create.rb +131 -0
  718. data/app/workflows/spree/seller_transfers/reverse.rb +241 -0
  719. data/app/workflows/spree/sellers/approve.rb +88 -0
  720. data/app/workflows/spree/sellers/create.rb +72 -0
  721. data/app/workflows/spree/sellers/invite.rb +61 -0
  722. data/app/workflows/spree/sellers/reject.rb +52 -0
  723. data/app/workflows/spree/sellers/reopen_onboarding.rb +55 -0
  724. data/app/workflows/spree/sellers/start_onboarding.rb +46 -0
  725. data/app/workflows/spree/sellers/submit_for_review.rb +76 -0
  726. data/app/workflows/spree/sellers/suspend.rb +55 -0
  727. data/app/workflows/spree/shipping_labels/purchase.rb +154 -0
  728. data/app/workflows/spree/shipping_labels/record.rb +102 -0
  729. data/app/workflows/spree/shipping_labels/refund.rb +77 -0
  730. data/app/workflows/spree/stock_receipts/recording.rb +165 -0
  731. data/app/workflows/spree/stock_transfers/cancel.rb +103 -0
  732. data/app/workflows/spree/stock_transfers/close.rb +56 -0
  733. data/app/workflows/spree/stock_transfers/create.rb +63 -0
  734. data/app/workflows/spree/stock_transfers/mark_draft.rb +41 -0
  735. data/app/workflows/spree/stock_transfers/mark_in_transit.rb +91 -0
  736. data/app/workflows/spree/stock_transfers/mark_ready.rb +36 -0
  737. data/app/workflows/spree/stock_transfers/receive.rb +62 -0
  738. data/app/workflows/spree/stock_transfers/update.rb +55 -0
  739. data/app/workflows/spree/tax_exemption_certificates/verify.rb +48 -0
  740. data/app/workflows/spree/variants/create.rb +66 -0
  741. data/app/workflows/spree/variants/nested_attributes.rb +90 -0
  742. data/app/workflows/spree/variants/update.rb +55 -0
  743. data/config/brakeman.ignore +79 -79
  744. data/config/i18n-tasks.yml +4 -3
  745. data/config/initializers/mobility.rb +0 -4
  746. data/config/initializers/spree_store_setup_tasks.rb +4 -0
  747. data/config/locales/en.yml +952 -189
  748. data/config/routes.rb +11 -4
  749. data/db/migrate/20220201103922_add_first_name_and_last_name_to_spree_users.rb +7 -5
  750. data/db/migrate/20221215151408_add_selected_locale_to_spree_users.rb +6 -4
  751. data/db/migrate/20250127151258_add_phone_to_spree_users.rb +3 -1
  752. data/db/migrate/20250207084000_add_accepts_email_marketing_field_to_spree_users_table.rb +4 -2
  753. data/db/migrate/20250410061306_create_spree_invitations.rb +1 -1
  754. data/db/migrate/20250605131334_add_missing_fields_to_users.rb +24 -20
  755. data/db/migrate/20260315100000_add_product_media_support.rb +4 -1
  756. data/db/migrate/20260613000001_add_store_id_to_spree_role_users.rb +6 -3
  757. data/db/migrate/20260630000001_add_default_variant_id_to_spree_products.rb +9 -0
  758. data/db/migrate/20260708160054_create_spree_collections.rb +69 -0
  759. data/db/migrate/20260710000001_rename_taxons_to_categories.rb +28 -0
  760. data/db/migrate/20260727000001_add_unique_country_abbr_index_to_spree_states.rb +27 -4
  761. data/db/migrate/20260728000000_create_spree_customers_and_admin_users.rb +59 -0
  762. data/db/migrate/20260728000001_rename_prototypes_to_product_types.rb +59 -0
  763. data/db/migrate/20260728000002_create_spree_delivery_zones.rb +29 -0
  764. data/db/migrate/20260728000003_create_spree_carts.rb +58 -0
  765. data/db/migrate/20260728000003_rename_user_id_to_customer_id.rb +30 -0
  766. data/db/migrate/20260728000004_rename_shipping_to_fulfillment_and_delivery.rb +83 -0
  767. data/db/migrate/20260728000005_add_fulfillment_type_columns.rb +33 -0
  768. data/db/migrate/20260729000001_create_typed_adjustment_tables.rb +87 -0
  769. data/db/migrate/20260729100001_add_checkout_columns_to_spree_carts.rb +15 -0
  770. data/db/migrate/20260729100002_relax_owner_null_constraints.rb +7 -0
  771. data/db/migrate/20260729120001_rename_order_totals_to_standard_names.rb +7 -0
  772. data/db/migrate/20260729130001_create_spree_delivery_method_rules.rb +19 -0
  773. data/db/migrate/20260729140001_add_coupon_code_to_spree_orders.rb +6 -0
  774. data/db/migrate/20260729150001_add_store_to_delivery_methods_and_stock_locations.rb +6 -0
  775. data/db/migrate/20260802190001_create_spree_returns.rb +49 -0
  776. data/db/migrate/20260802210001_create_spree_exchanges_and_claims.rb +80 -0
  777. data/db/migrate/20260805100001_rename_return_authorization_reasons_to_return_reasons.rb +109 -0
  778. data/db/migrate/20260806000001_add_store_id_to_spree_tax_rates_and_categories.rb +16 -0
  779. data/db/migrate/20260806000001_create_spree_delivery_method_rule_products.rb +22 -0
  780. data/db/migrate/20260806000002_add_treatment_columns_to_spree_tax_lines.rb +19 -0
  781. data/db/migrate/20260806000003_create_spree_tax_identifiers.rb +41 -0
  782. data/db/migrate/20260806000004_add_tax_provider_to_spree_markets.rb +9 -0
  783. data/db/migrate/20260806000005_add_country_and_state_to_spree_tax_rates.rb +20 -0
  784. data/db/migrate/20260806100001_create_spree_product_type_custom_field_definitions.rb +17 -0
  785. data/db/migrate/20260806120001_add_rate_provider_to_delivery_methods.rb +7 -0
  786. data/db/migrate/20260806120002_add_carrier_columns_to_delivery_rates.rb +13 -0
  787. data/db/migrate/20260806130001_add_unique_store_type_index_to_spree_integrations.rb +9 -0
  788. data/db/migrate/20260807000001_add_permissions_to_spree_roles.rb +14 -0
  789. data/db/migrate/20260807000001_create_spree_companies.rb +66 -0
  790. data/db/migrate/20260807000003_create_spree_tax_exemption_certificates.rb +31 -0
  791. data/db/migrate/20260807120001_remove_claim_type_from_spree_claims.rb +5 -0
  792. data/db/migrate/20260807130001_add_country_state_codes_to_consumers.rb +28 -0
  793. data/db/migrate/20260807150001_reindex_spree_categories_by_store.rb +42 -0
  794. data/db/migrate/20260809000001_rename_metafields_to_custom_fields.rb +126 -0
  795. data/db/migrate/20260809100001_support_multi_rate_delivery_methods.rb +35 -0
  796. data/db/migrate/20260809120001_consolidate_metadata_columns.rb +120 -0
  797. data/db/migrate/20260809150001_promote_shipping_categories_to_delivery_profiles.rb +153 -0
  798. data/db/migrate/20260809170001_create_spree_channel_stock_locations.rb +17 -0
  799. data/db/migrate/20260809200001_add_rich_text_columns_for_action_text_removal.rb +11 -0
  800. data/db/migrate/20260810000001_rename_digitals_to_digital_assets.rb +38 -0
  801. data/db/migrate/20260811000001_add_delivery_tracking_to_fulfillments.rb +17 -0
  802. data/db/migrate/20260812000001_add_provider_type_to_spree_digital_assets.rb +7 -0
  803. data/db/migrate/20260812000002_add_metadata_to_spree_digital_assets.rb +11 -0
  804. data/db/migrate/20260813000001_create_spree_number_sequences.rb +13 -0
  805. data/db/migrate/20260813120001_add_setup_token_to_spree_stores.rb +11 -0
  806. data/db/migrate/20260813130001_add_capture_method_to_payment_methods.rb +17 -0
  807. data/db/migrate/20260813130001_add_unique_indexes_to_spree_data_feeds.rb +10 -0
  808. data/db/migrate/20260814000001_add_customs_fields_to_spree_variants.rb +7 -0
  809. data/db/migrate/20260814000001_rename_stock_items_to_stock_levels.rb +72 -0
  810. data/db/migrate/20260814000002_add_typed_stock_movements.rb +25 -0
  811. data/db/migrate/20260815000001_add_audience_to_spree_refresh_tokens.rb +14 -0
  812. data/db/migrate/20260815000002_add_resource_to_spree_roles.rb +190 -0
  813. data/db/migrate/20260816000001_create_spree_sellers.rb +59 -0
  814. data/db/migrate/20260816000002_add_seller_to_spree_products.rb +10 -0
  815. data/db/migrate/20260816210001_create_spree_commission_rates.rb +229 -0
  816. data/db/migrate/20260816210002_create_spree_commission_lines.rb +72 -0
  817. data/db/migrate/20260817000001_add_seller_to_spree_line_items.rb +15 -0
  818. data/db/migrate/20260817000002_add_seller_and_delivery_profile_to_spree_variants.rb +17 -0
  819. data/db/migrate/20260818000001_create_spree_external_references.rb +33 -0
  820. data/db/migrate/20260818000001_create_spree_seller_requirements.rb +60 -0
  821. data/db/migrate/20260818000001_rename_spree_payments_state_to_status.rb +5 -0
  822. data/db/migrate/20260818000002_add_price_source_to_spree_line_items.rb +9 -0
  823. data/db/migrate/20260818090001_rename_assets_to_media.rb +10 -0
  824. data/db/migrate/20260819000001_create_spree_seller_requirement_custom_fields.rb +15 -0
  825. data/db/migrate/20260819000001_replace_payment_method_display_on_with_storefront_visible.rb +25 -0
  826. data/db/migrate/20260819000002_rename_spree_gift_cards_state_to_status.rb +5 -0
  827. data/db/migrate/20260820000001_add_seller_business_identity_and_stock_locations.rb +17 -0
  828. data/db/migrate/20260820000001_create_spree_order_groups.rb +33 -0
  829. data/db/migrate/20260820000002_add_order_group_to_spree_orders_and_payments.rb +14 -0
  830. data/db/migrate/20260820000003_create_spree_payment_splits.rb +21 -0
  831. data/db/migrate/20260820000004_add_order_to_spree_refunds.rb +32 -0
  832. data/db/migrate/20260821000001_add_claimed_amount_to_spree_payment_splits.rb +14 -0
  833. data/db/migrate/20260822000001_create_spree_seller_transfers.rb +94 -0
  834. data/db/migrate/20260822000002_create_spree_seller_payouts.rb +48 -0
  835. data/db/migrate/20260822000003_add_payout_account_to_spree_sellers.rb +11 -0
  836. data/db/migrate/20260824000001_add_store_to_spree_media.rb +16 -0
  837. data/db/migrate/20260825000001_create_spree_catalogs.rb +40 -0
  838. data/db/migrate/20260826000001_add_owner_to_spree_addresses.rb +30 -0
  839. data/db/migrate/20260826000002_create_spree_product_submissions.rb +28 -0
  840. data/db/migrate/20260827000001_add_seller_to_spree_exports.rb +5 -0
  841. data/db/migrate/20260828100001_unify_import_and_export_tenancy.rb +79 -0
  842. data/db/migrate/20260830000001_add_catalog_to_spree_price_lists.rb +47 -0
  843. data/db/migrate/20260830000002_add_po_number_to_purchases.rb +16 -0
  844. data/db/migrate/20260830000002_add_price_adjustment_to_spree_price_lists.rb +14 -0
  845. data/db/migrate/20260831000001_add_description_to_spree_catalogs.rb +7 -0
  846. data/db/migrate/20260831000002_add_quantity_rules_to_spree_variants.rb +18 -0
  847. data/db/migrate/20260831000003_create_spree_catalog_terms.rb +35 -0
  848. data/db/migrate/20260831100001_create_spree_package_types.rb +38 -0
  849. data/db/migrate/20260831100002_add_carton_details_to_spree_variants.rb +10 -0
  850. data/db/migrate/20260831100003_add_unpriced_to_spree_delivery_rates.rb +8 -0
  851. data/db/migrate/20260901000001_add_seller_to_spree_delivery_methods.rb +11 -0
  852. data/db/migrate/20260902000001_add_store_and_filter_key_to_custom_field_definitions.rb +143 -0
  853. data/db/migrate/20260902000002_add_eu_legal_compliance.rb +68 -0
  854. data/db/migrate/20260902000002_replace_order_cancellation_tables_with_cancel_columns.rb +88 -0
  855. data/db/migrate/20260902100001_rename_wished_items_to_wishlist_items.rb +8 -0
  856. data/db/migrate/20260902100002_rename_promo_total_to_discount_total.rb +9 -0
  857. data/db/migrate/20260902100003_rename_address_name_and_postal_code_columns.rb +24 -0
  858. data/db/migrate/20260902100004_rename_option_presentation_to_label.rb +13 -0
  859. data/db/migrate/20260903000001_create_spree_shipping_labels_and_deliveries.rb +73 -0
  860. data/db/migrate/20260903120000_create_spree_saved_reports.rb +57 -0
  861. data/db/migrate/20260904000001_add_returns_enabled_to_stock_locations.rb +14 -0
  862. data/db/migrate/20260906000001_add_commission_total_to_orders.rb +22 -0
  863. data/db/migrate/20260907000001_add_inventory_operations.rb +113 -0
  864. data/db/migrate/20260907000001_add_min_quantity_to_spree_prices.rb +28 -0
  865. data/db/migrate/20260907000002_create_spree_price_adjustment_tiers.rb +19 -0
  866. data/db/migrate/20260908000001_add_seller_to_spree_package_types.rb +78 -0
  867. data/db/migrate/20260910000001_add_stock_receipts.rb +44 -0
  868. data/db/migrate/20260910000002_add_counters_to_spree_stock_levels.rb +6 -0
  869. data/db/migrate/20260910120000_index_reporting_time_columns.rb +12 -0
  870. data/db/migrate/20260912120000_index_reporting_lifetime_and_promotion_columns.rb +19 -0
  871. data/db/migrate/20260914000001_add_settlement_to_spree_seller_transfers.rb +18 -0
  872. data/db/migrate/20260914230000_add_store_to_spree_option_types.rb +46 -0
  873. data/db/migrate/20260916000001_order_operation_actors_polymorphic.rb +30 -0
  874. data/db/sample_data/collections.rb +58 -0
  875. data/db/sample_data/custom_field_definitions.rb +9 -0
  876. data/db/sample_data/digital_products.rb +26 -0
  877. data/db/sample_data/fulfillment.rb +88 -0
  878. data/db/sample_data/options.rb +25 -1
  879. data/db/sample_data/orders.rb +53 -30
  880. data/db/sample_data/payment_methods.rb +2 -2
  881. data/db/sample_data/price_list_prices.csv +25 -0
  882. data/db/sample_data/product_type_categories.rb +27 -0
  883. data/db/sample_data/product_types.rb +83 -0
  884. data/db/sample_data/products.csv +39 -38
  885. data/db/sample_data/wholesale.rb +42 -3
  886. data/lib/generators/spree/api_resource/api_resource_generator.rb +64 -6
  887. data/lib/generators/spree/api_resource/templates/admin_controller.rb.tt +5 -0
  888. data/lib/generators/spree/api_resource/templates/admin_controller_spec.rb.tt +3 -0
  889. data/lib/generators/spree/api_resource/templates/store_controller_spec.rb.tt +4 -1
  890. data/lib/generators/spree/authentication/dummy/templates/create_spree_admin_users.rb.tt +1 -0
  891. data/lib/generators/spree/controller_decorator/controller_decorator_generator.rb +4 -4
  892. data/lib/generators/spree/dummy/dummy_generator.rb +5 -12
  893. data/lib/generators/spree/dummy/templates/rails/test.rb +5 -0
  894. data/lib/generators/spree/model/model_generator.rb +46 -7
  895. data/lib/generators/spree/model/templates/create_table_migration.rb.tt +9 -0
  896. data/lib/generators/spree/model/templates/model.rb.tt +17 -7
  897. data/lib/spree/category_permalink_deduplicator.rb +328 -0
  898. data/lib/spree/checkout/default_requirements.rb +134 -0
  899. data/lib/spree/checkout/registry.rb +164 -0
  900. data/lib/spree/checkout/requirement.rb +49 -0
  901. data/lib/spree/checkout/requirements.rb +61 -0
  902. data/lib/spree/checkout/step.rb +52 -0
  903. data/lib/spree/core/configuration.rb +112 -63
  904. data/lib/spree/core/controller_helpers/store.rb +8 -9
  905. data/lib/spree/core/dependencies.rb +324 -71
  906. data/lib/spree/core/dependencies_helper.rb +4 -0
  907. data/lib/spree/core/engine.rb +360 -75
  908. data/lib/spree/core/number_generator.rb +37 -33
  909. data/lib/spree/core/permission_configuration.rb +183 -91
  910. data/lib/spree/core/permissions/default_catalog.rb +236 -0
  911. data/lib/spree/core/permissions/entry.rb +15 -0
  912. data/lib/spree/core/permissions/scope.rb +76 -0
  913. data/lib/spree/core/preferences/masking.rb +24 -2
  914. data/lib/spree/core/preferences/preferable.rb +52 -2
  915. data/lib/spree/core/preferences/preferable_class_methods.rb +41 -2
  916. data/lib/spree/core/preferences/runtime_configuration.rb +142 -9
  917. data/lib/spree/core/previews/admin_user_preview.rb +0 -8
  918. data/lib/spree/core/previews/invitation_preview.rb +1 -1
  919. data/lib/spree/core/previews/preview_data.rb +2 -2
  920. data/lib/spree/core/previews/seller_user_preview.rb +14 -0
  921. data/lib/spree/core/pricing/context.rb +28 -8
  922. data/lib/spree/core/pricing/price_resolution.rb +150 -0
  923. data/lib/spree/core/pricing/resolver.rb +17 -107
  924. data/lib/spree/core/ransack_configuration.rb +3 -3
  925. data/lib/spree/core/routes.rb +4 -6
  926. data/lib/spree/core/version.rb +1 -1
  927. data/lib/spree/core.rb +446 -59
  928. data/lib/spree/events/adapters/base.rb +42 -15
  929. data/lib/spree/hooks.rb +146 -0
  930. data/lib/spree/iso_data.rb +188 -0
  931. data/lib/spree/measurement.rb +113 -0
  932. data/lib/spree/money.rb +70 -0
  933. data/lib/spree/number_generators/registry.rb +80 -0
  934. data/lib/spree/reporting/adapters/base.rb +71 -0
  935. data/lib/spree/reporting/adapters/live.rb +677 -0
  936. data/lib/spree/reporting/counter_result.rb +19 -0
  937. data/lib/spree/reporting/counters.rb +33 -0
  938. data/lib/spree/reporting/default_vocabulary.rb +705 -0
  939. data/lib/spree/reporting/hydration.rb +64 -0
  940. data/lib/spree/reporting/query.rb +487 -0
  941. data/lib/spree/reporting/registry.rb +294 -0
  942. data/lib/spree/reporting/result.rb +10 -0
  943. data/lib/spree/reporting/schema.rb +124 -0
  944. data/lib/spree/reporting.rb +49 -0
  945. data/lib/spree/service_module.rb +6 -0
  946. data/lib/spree/store_scope_guard.rb +180 -0
  947. data/lib/spree/testing_support/action_text.rb +33 -0
  948. data/lib/spree/testing_support/common_rake.rb +4 -66
  949. data/lib/spree/testing_support/country_pool.rb +50 -0
  950. data/lib/spree/testing_support/extension_rake.rb +2 -6
  951. data/lib/spree/testing_support/factories/address_factory.rb +45 -15
  952. data/lib/spree/testing_support/factories/api_key_factory.rb +1 -1
  953. data/lib/spree/testing_support/factories/calculator_factory.rb +0 -3
  954. data/lib/spree/testing_support/factories/cart_factory.rb +60 -0
  955. data/lib/spree/testing_support/factories/catalog_factory.rb +37 -0
  956. data/lib/spree/testing_support/factories/category_factory.rb +21 -0
  957. data/lib/spree/testing_support/factories/claim_factory.rb +33 -0
  958. data/lib/spree/testing_support/factories/classification_factory.rb +3 -2
  959. data/lib/spree/testing_support/factories/collection_factory.rb +25 -0
  960. data/lib/spree/testing_support/factories/collection_rule_factory.rb +47 -0
  961. data/lib/spree/testing_support/factories/commission_rate_factory.rb +75 -0
  962. data/lib/spree/testing_support/factories/company_factory.rb +31 -0
  963. data/lib/spree/testing_support/factories/consent_record_factory.rb +10 -0
  964. data/lib/spree/testing_support/factories/country_factory.rb +28 -9
  965. data/lib/spree/testing_support/factories/custom_field_definition_factory.rb +79 -0
  966. data/lib/spree/testing_support/factories/custom_field_factory.rb +64 -0
  967. data/lib/spree/testing_support/factories/customer_group_user_factory.rb +1 -1
  968. data/lib/spree/testing_support/factories/data_request_factory.rb +17 -0
  969. data/lib/spree/testing_support/factories/delivery_factory.rb +13 -0
  970. data/lib/spree/testing_support/factories/delivery_method_factory.rb +47 -0
  971. data/lib/spree/testing_support/factories/delivery_origin_group_factory.rb +5 -0
  972. data/lib/spree/testing_support/factories/delivery_profile_factory.rb +9 -0
  973. data/lib/spree/testing_support/factories/delivery_rate_factory.rb +7 -0
  974. data/lib/spree/testing_support/factories/delivery_zone_factory.rb +34 -0
  975. data/lib/spree/testing_support/factories/digital_asset_factory.rb +13 -0
  976. data/lib/spree/testing_support/factories/digital_link_factory.rb +1 -1
  977. data/lib/spree/testing_support/factories/discount_factory.rb +9 -0
  978. data/lib/spree/testing_support/factories/exchange_factory.rb +50 -0
  979. data/lib/spree/testing_support/factories/external_reference_factory.rb +8 -0
  980. data/lib/spree/testing_support/factories/fee_factory.rb +8 -0
  981. data/lib/spree/testing_support/factories/fulfillment_factory.rb +32 -0
  982. data/lib/spree/testing_support/factories/fulfillment_item_factory.rb +17 -0
  983. data/lib/spree/testing_support/factories/gateway_customer_factory.rb +1 -1
  984. data/lib/spree/testing_support/factories/gift_card_factory.rb +2 -2
  985. data/lib/spree/testing_support/factories/import_factory.rb +33 -1
  986. data/lib/spree/testing_support/factories/line_item_factory.rb +10 -5
  987. data/lib/spree/testing_support/factories/market_factory.rb +5 -12
  988. data/lib/spree/testing_support/factories/media_factory.rb +36 -0
  989. data/lib/spree/testing_support/factories/newsletter_subscriber_factory.rb +1 -1
  990. data/lib/spree/testing_support/factories/number_sequence_factory.rb +7 -0
  991. data/lib/spree/testing_support/factories/options_factory.rb +5 -5
  992. data/lib/spree/testing_support/factories/order_factory.rb +39 -23
  993. data/lib/spree/testing_support/factories/order_group_factory.rb +29 -0
  994. data/lib/spree/testing_support/factories/package_type_factory.rb +36 -0
  995. data/lib/spree/testing_support/factories/payment_factory.rb +7 -7
  996. data/lib/spree/testing_support/factories/payment_method_factory.rb +5 -5
  997. data/lib/spree/testing_support/factories/payment_session_factory.rb +10 -6
  998. data/lib/spree/testing_support/factories/payment_setup_session_factory.rb +2 -2
  999. data/lib/spree/testing_support/factories/payment_split_factory.rb +10 -0
  1000. data/lib/spree/testing_support/factories/price_adjustment_tier_factory.rb +7 -0
  1001. data/lib/spree/testing_support/factories/price_factory.rb +6 -0
  1002. data/lib/spree/testing_support/factories/price_rule_factory.rb +0 -10
  1003. data/lib/spree/testing_support/factories/product_collection_factory.rb +8 -0
  1004. data/lib/spree/testing_support/factories/product_factory.rb +35 -7
  1005. data/lib/spree/testing_support/factories/product_type_custom_field_definition_factory.rb +10 -0
  1006. data/lib/spree/testing_support/factories/product_type_factory.rb +12 -0
  1007. data/lib/spree/testing_support/factories/promotion_factory.rb +4 -0
  1008. data/lib/spree/testing_support/factories/promotion_rule_factory.rb +1 -1
  1009. data/lib/spree/testing_support/factories/purchase_order_factory.rb +49 -0
  1010. data/lib/spree/testing_support/factories/refresh_token_factory.rb +2 -0
  1011. data/lib/spree/testing_support/factories/refund_factory.rb +1 -2
  1012. data/lib/spree/testing_support/factories/return_factory.rb +52 -0
  1013. data/lib/spree/testing_support/factories/return_reason_factory.rb +19 -0
  1014. data/lib/spree/testing_support/factories/role_factory.rb +1 -0
  1015. data/lib/spree/testing_support/factories/saved_report_factory.rb +7 -0
  1016. data/lib/spree/testing_support/factories/seller_factory.rb +34 -0
  1017. data/lib/spree/testing_support/factories/seller_payout_factory.rb +14 -0
  1018. data/lib/spree/testing_support/factories/seller_requirement_factory.rb +61 -0
  1019. data/lib/spree/testing_support/factories/seller_requirement_submission_factory.rb +32 -0
  1020. data/lib/spree/testing_support/factories/seller_transfer_factory.rb +21 -0
  1021. data/lib/spree/testing_support/factories/shipping_label_factory.rb +44 -0
  1022. data/lib/spree/testing_support/factories/state_factory.rb +18 -5
  1023. data/lib/spree/testing_support/factories/stock_level_factory.rb +24 -0
  1024. data/lib/spree/testing_support/factories/stock_location_factory.rb +12 -6
  1025. data/lib/spree/testing_support/factories/stock_movement_factory.rb +20 -3
  1026. data/lib/spree/testing_support/factories/stock_receipt_factory.rb +37 -0
  1027. data/lib/spree/testing_support/factories/stock_reservation_factory.rb +11 -8
  1028. data/lib/spree/testing_support/factories/stock_transfer_factory.rb +53 -6
  1029. data/lib/spree/testing_support/factories/store_credit_factory.rb +1 -3
  1030. data/lib/spree/testing_support/factories/supplier_factory.rb +17 -0
  1031. data/lib/spree/testing_support/factories/tax_exemption_certificate_factory.rb +18 -0
  1032. data/lib/spree/testing_support/factories/tax_identifier_factory.rb +33 -0
  1033. data/lib/spree/testing_support/factories/tax_line_factory.rb +19 -0
  1034. data/lib/spree/testing_support/factories/tax_rate_factory.rb +5 -2
  1035. data/lib/spree/testing_support/factories/taxon_factory.rb +6 -23
  1036. data/lib/spree/testing_support/factories/taxonomy_factory.rb +8 -0
  1037. data/lib/spree/testing_support/factories/user_factory.rb +8 -13
  1038. data/lib/spree/testing_support/factories/variant_factory.rb +1 -1
  1039. data/lib/spree/testing_support/factories/wishlist_factory.rb +1 -1
  1040. data/lib/spree/testing_support/factories/wishlist_item_factory.rb +10 -0
  1041. data/lib/spree/testing_support/factories/zone_factory.rb +2 -25
  1042. data/lib/spree/testing_support/factories/zone_member_factory.rb +2 -1
  1043. data/lib/spree/testing_support/factories.rb +1 -6
  1044. data/lib/spree/testing_support/imports.rb +16 -0
  1045. data/lib/spree/testing_support/label_provider.rb +49 -0
  1046. data/lib/spree/testing_support/lifecycle_events.rb +6 -1
  1047. data/lib/spree/testing_support/metadata.rb +38 -36
  1048. data/lib/spree/testing_support/order_walkthrough.rb +46 -36
  1049. data/lib/spree/testing_support/preferences.rb +39 -0
  1050. data/lib/spree/testing_support/purchase_order.rb +111 -0
  1051. data/lib/spree/testing_support/store.rb +6 -2
  1052. data/lib/spree/testing_support/tax_identifier_validators.rb +68 -0
  1053. data/lib/spree/translations.rb +139 -5
  1054. data/lib/spree/upgrades/5_4_to_5_5/manifest.yml +1 -1
  1055. data/lib/spree/upgrades/5_5_to_5_6/manifest.yml +17 -9
  1056. data/lib/spree/upgrades/5_6_to_6_0/manifest.yml +683 -0
  1057. data/lib/spree/validators.rb +74 -0
  1058. data/lib/spree/workflow.rb +335 -0
  1059. data/lib/tasks/backfill_actor_types.rake +35 -0
  1060. data/lib/tasks/backfill_custom_field_definition_stores.rake +82 -0
  1061. data/lib/tasks/backfill_import_export_tenancy.rake +93 -0
  1062. data/lib/tasks/backfill_library_media_placements.rake +29 -0
  1063. data/lib/tasks/backfill_media_store_ids.rake +70 -0
  1064. data/lib/tasks/backfill_reason_store_ids.rake +28 -0
  1065. data/lib/tasks/capture_methods_migration.rake +30 -0
  1066. data/lib/tasks/carts_migration.rake +113 -0
  1067. data/lib/tasks/categories_and_collections.rake +188 -0
  1068. data/lib/tasks/cli.rake +22 -3
  1069. data/lib/tasks/consolidate_metadata.rake +73 -0
  1070. data/lib/tasks/core.rake +1 -11
  1071. data/lib/tasks/deliveries_migration.rake +52 -0
  1072. data/lib/tasks/delivery_method_rules_migration.rake +51 -0
  1073. data/lib/tasks/delivery_migration.rake +213 -0
  1074. data/lib/tasks/delivery_profiles_migration.rake +109 -0
  1075. data/lib/tasks/fold_store_credit_categories.rake +40 -0
  1076. data/lib/tasks/fulfillment_statuses_migration.rake +59 -0
  1077. data/lib/tasks/inventory_operations_migration.rake +223 -0
  1078. data/lib/tasks/markets.rake +7 -2
  1079. data/lib/tasks/media.rake +2 -2
  1080. data/lib/tasks/media_class_rename.rake +56 -0
  1081. data/lib/tasks/migrate_country_state_codes.rake +228 -0
  1082. data/lib/tasks/migrate_promotion_option_value_rules.rake +124 -0
  1083. data/lib/tasks/migrate_returns.rake +322 -0
  1084. data/lib/tasks/migrate_rich_text_to_columns.rake +128 -0
  1085. data/lib/tasks/migrate_users_to_customers.rake +161 -0
  1086. data/lib/tasks/order_coupon_code_backfill.rake +32 -0
  1087. data/lib/tasks/order_market_backfill.rake +27 -0
  1088. data/lib/tasks/package_types.rake +62 -0
  1089. data/lib/tasks/price_history.rake +49 -6
  1090. data/lib/tasks/products.rake +21 -2
  1091. data/lib/tasks/remove_master_variant.rake +58 -0
  1092. data/lib/tasks/rich_text.rake +44 -0
  1093. data/lib/tasks/seller_sample_data.rake +142 -0
  1094. data/lib/tasks/setup.rake +18 -0
  1095. data/lib/tasks/single_store_associations.rake +11 -0
  1096. data/lib/tasks/stock.rake +15 -0
  1097. data/lib/tasks/store_binding_migration.rake +33 -0
  1098. data/lib/tasks/store_settings.rake +105 -0
  1099. data/lib/tasks/tax_zones_migration.rake +147 -0
  1100. data/lib/tasks/taxons.rake +9 -10
  1101. data/lib/tasks/typed_adjustments_migration.rake +189 -0
  1102. data/lib/tasks/typed_stock_movements_migration.rake +250 -0
  1103. data/lib/tasks/upgrade.rake +13 -8
  1104. data/spec/fixtures/files/eu_vat_numbers.txt +56 -0
  1105. data/spec/fixtures/files/price_list_prices_import.csv +4 -0
  1106. data/spec/fixtures/files/products_import.csv +1 -1
  1107. data/spec/fixtures/tiny-video.mp4 +0 -0
  1108. metadata +798 -546
  1109. data/app/assets/config/spree_core_manifest.js +0 -4
  1110. data/app/assets/images/google_on_white_hdpi.png +0 -0
  1111. data/app/assets/images/logo.png +0 -0
  1112. data/app/assets/images/payment_icons/ach.svg +0 -1
  1113. data/app/assets/images/payment_icons/achdirectdebit.svg +0 -1
  1114. data/app/assets/images/payment_icons/ada.svg +0 -1
  1115. data/app/assets/images/payment_icons/adyen.svg +0 -1
  1116. data/app/assets/images/payment_icons/affinbank.svg +0 -1
  1117. data/app/assets/images/payment_icons/affirm.svg +0 -1
  1118. data/app/assets/images/payment_icons/afterpay.svg +0 -1
  1119. data/app/assets/images/payment_icons/airtel_money.svg +0 -1
  1120. data/app/assets/images/payment_icons/airteltigo_mobile_money.svg +0 -1
  1121. data/app/assets/images/payment_icons/alandsbanken.svg +0 -1
  1122. data/app/assets/images/payment_icons/alipay.svg +0 -1
  1123. data/app/assets/images/payment_icons/alipay_hk.svg +0 -1
  1124. data/app/assets/images/payment_icons/alliancebank.svg +0 -1
  1125. data/app/assets/images/payment_icons/alma.svg +0 -1
  1126. data/app/assets/images/payment_icons/amazon.svg +0 -1
  1127. data/app/assets/images/payment_icons/ambank.svg +0 -1
  1128. data/app/assets/images/payment_icons/american_express.svg +0 -1
  1129. data/app/assets/images/payment_icons/apecoin.svg +0 -1
  1130. data/app/assets/images/payment_icons/apple_pay.svg +0 -1
  1131. data/app/assets/images/payment_icons/arbitrum.svg +0 -1
  1132. data/app/assets/images/payment_icons/atome.svg +0 -1
  1133. data/app/assets/images/payment_icons/authorizenet.svg +0 -1
  1134. data/app/assets/images/payment_icons/avalanche.svg +0 -1
  1135. data/app/assets/images/payment_icons/bacs.svg +0 -1
  1136. data/app/assets/images/payment_icons/bancontact.svg +0 -1
  1137. data/app/assets/images/payment_icons/bangkokbank.svg +0 -1
  1138. data/app/assets/images/payment_icons/bankislam.svg +0 -1
  1139. data/app/assets/images/payment_icons/bankmuamalat.svg +0 -1
  1140. data/app/assets/images/payment_icons/bankofamerica.svg +0 -1
  1141. data/app/assets/images/payment_icons/bankrakyat.svg +0 -1
  1142. data/app/assets/images/payment_icons/banktransfer.svg +0 -1
  1143. data/app/assets/images/payment_icons/base.svg +0 -1
  1144. data/app/assets/images/payment_icons/becs.svg +0 -1
  1145. data/app/assets/images/payment_icons/benefit.svg +0 -1
  1146. data/app/assets/images/payment_icons/billie.svg +0 -1
  1147. data/app/assets/images/payment_icons/bitcoin.svg +0 -1
  1148. data/app/assets/images/payment_icons/bitcoin_cash.svg +0 -1
  1149. data/app/assets/images/payment_icons/bizum.svg +0 -1
  1150. data/app/assets/images/payment_icons/blik.svg +0 -1
  1151. data/app/assets/images/payment_icons/bnbchain.svg +0 -1
  1152. data/app/assets/images/payment_icons/bogus.svg +0 -1
  1153. data/app/assets/images/payment_icons/bogus_app_coin.svg +0 -1
  1154. data/app/assets/images/payment_icons/boleto.svg +0 -1
  1155. data/app/assets/images/payment_icons/boost.svg +0 -1
  1156. data/app/assets/images/payment_icons/braintree.svg +0 -1
  1157. data/app/assets/images/payment_icons/buckaroopaybybank.svg +0 -1
  1158. data/app/assets/images/payment_icons/busd.svg +0 -1
  1159. data/app/assets/images/payment_icons/cartes_bancaires.svg +0 -1
  1160. data/app/assets/images/payment_icons/cash.svg +0 -1
  1161. data/app/assets/images/payment_icons/cashappafterpay.svg +0 -1
  1162. data/app/assets/images/payment_icons/cashapppay.svg +0 -1
  1163. data/app/assets/images/payment_icons/checkout_finance.svg +0 -1
  1164. data/app/assets/images/payment_icons/chinabank.svg +0 -1
  1165. data/app/assets/images/payment_icons/clearpay.svg +0 -1
  1166. data/app/assets/images/payment_icons/collector_bank.svg +0 -1
  1167. data/app/assets/images/payment_icons/dai.svg +0 -1
  1168. data/app/assets/images/payment_icons/dana.svg +0 -1
  1169. data/app/assets/images/payment_icons/dankort.svg +0 -1
  1170. data/app/assets/images/payment_icons/danske_bank.svg +0 -1
  1171. data/app/assets/images/payment_icons/deutschebank.svg +0 -1
  1172. data/app/assets/images/payment_icons/diners_club.svg +0 -1
  1173. data/app/assets/images/payment_icons/directbanktransferlatinamerica.svg +0 -1
  1174. data/app/assets/images/payment_icons/discover.svg +0 -1
  1175. data/app/assets/images/payment_icons/dogecoin.svg +0 -1
  1176. data/app/assets/images/payment_icons/duitnow.svg +0 -1
  1177. data/app/assets/images/payment_icons/eft_secure.svg +0 -1
  1178. data/app/assets/images/payment_icons/eftpos_au.svg +0 -1
  1179. data/app/assets/images/payment_icons/eftposau.svg +0 -1
  1180. data/app/assets/images/payment_icons/elo.svg +0 -1
  1181. data/app/assets/images/payment_icons/eos.svg +0 -1
  1182. data/app/assets/images/payment_icons/eps.svg +0 -1
  1183. data/app/assets/images/payment_icons/ethereum.svg +0 -1
  1184. data/app/assets/images/payment_icons/eurobonuscheckout.svg +0 -1
  1185. data/app/assets/images/payment_icons/ezcash.svg +0 -1
  1186. data/app/assets/images/payment_icons/fantom.svg +0 -1
  1187. data/app/assets/images/payment_icons/fpx.svg +0 -1
  1188. data/app/assets/images/payment_icons/gcash.svg +0 -1
  1189. data/app/assets/images/payment_icons/generic.svg +0 -1
  1190. data/app/assets/images/payment_icons/genericbank.svg +0 -1
  1191. data/app/assets/images/payment_icons/gift-card.svg +0 -1
  1192. data/app/assets/images/payment_icons/giropay.svg +0 -1
  1193. data/app/assets/images/payment_icons/glbepaypal.svg +0 -1
  1194. data/app/assets/images/payment_icons/gmobanktransfer.svg +0 -1
  1195. data/app/assets/images/payment_icons/gnosis.svg +0 -1
  1196. data/app/assets/images/payment_icons/google_pay.svg +0 -1
  1197. data/app/assets/images/payment_icons/google_wallet.svg +0 -1
  1198. data/app/assets/images/payment_icons/gopay.svg +0 -1
  1199. data/app/assets/images/payment_icons/grabpay.svg +0 -1
  1200. data/app/assets/images/payment_icons/gusd.svg +0 -1
  1201. data/app/assets/images/payment_icons/handelsbanken.svg +0 -1
  1202. data/app/assets/images/payment_icons/hongleongbank.svg +0 -1
  1203. data/app/assets/images/payment_icons/hypercard.svg +0 -1
  1204. data/app/assets/images/payment_icons/hypercash.svg +0 -1
  1205. data/app/assets/images/payment_icons/ideal.svg +0 -1
  1206. data/app/assets/images/payment_icons/in3viaideal.svg +0 -1
  1207. data/app/assets/images/payment_icons/inbank.svg +0 -1
  1208. data/app/assets/images/payment_icons/jcb.svg +0 -1
  1209. data/app/assets/images/payment_icons/kakao_pay.svg +0 -1
  1210. data/app/assets/images/payment_icons/kasikornbank.svg +0 -1
  1211. data/app/assets/images/payment_icons/klarna-pay-later.svg +0 -1
  1212. data/app/assets/images/payment_icons/klarna-pay-now.svg +0 -1
  1213. data/app/assets/images/payment_icons/klarna-slice-it.svg +0 -1
  1214. data/app/assets/images/payment_icons/klarna.svg +0 -1
  1215. data/app/assets/images/payment_icons/knet.svg +0 -1
  1216. data/app/assets/images/payment_icons/krungthaibank.svg +0 -1
  1217. data/app/assets/images/payment_icons/landbank.svg +0 -1
  1218. data/app/assets/images/payment_icons/laybuybyklarna.svg +0 -1
  1219. data/app/assets/images/payment_icons/litecoin.svg +0 -1
  1220. data/app/assets/images/payment_icons/lunchcheck.svg +0 -1
  1221. data/app/assets/images/payment_icons/mach.svg +0 -1
  1222. data/app/assets/images/payment_icons/mada.svg +0 -1
  1223. data/app/assets/images/payment_icons/maestro.svg +0 -1
  1224. data/app/assets/images/payment_icons/master.svg +0 -1
  1225. data/app/assets/images/payment_icons/mayabank.svg +0 -1
  1226. data/app/assets/images/payment_icons/maybank.svg +0 -1
  1227. data/app/assets/images/payment_icons/maybankm2u.svg +0 -1
  1228. data/app/assets/images/payment_icons/maybankqrpay.svg +0 -1
  1229. data/app/assets/images/payment_icons/mbway.svg +0 -1
  1230. data/app/assets/images/payment_icons/mcash.svg +0 -1
  1231. data/app/assets/images/payment_icons/medicinosbankas.svg +0 -1
  1232. data/app/assets/images/payment_icons/mercadopago.svg +0 -1
  1233. data/app/assets/images/payment_icons/metrobank.svg +0 -1
  1234. data/app/assets/images/payment_icons/mobilepay.svg +0 -1
  1235. data/app/assets/images/payment_icons/momopay.svg +0 -1
  1236. data/app/assets/images/payment_icons/monero.svg +0 -1
  1237. data/app/assets/images/payment_icons/mtn_mobile_money.svg +0 -1
  1238. data/app/assets/images/payment_icons/mybank.svg +0 -1
  1239. data/app/assets/images/payment_icons/naver_pay.svg +0 -1
  1240. data/app/assets/images/payment_icons/nelo.svg +0 -1
  1241. data/app/assets/images/payment_icons/netbanking.svg +0 -1
  1242. data/app/assets/images/payment_icons/novalnetdirectdebitach.svg +0 -1
  1243. data/app/assets/images/payment_icons/nubank.svg +0 -1
  1244. data/app/assets/images/payment_icons/ocbcbank.svg +0 -1
  1245. data/app/assets/images/payment_icons/offlinebanktransferlatinamerica.svg +0 -1
  1246. data/app/assets/images/payment_icons/ola_money.svg +0 -1
  1247. data/app/assets/images/payment_icons/oney.svg +0 -1
  1248. data/app/assets/images/payment_icons/onlinebanking.svg +0 -1
  1249. data/app/assets/images/payment_icons/onlinebanktransfer.svg +0 -1
  1250. data/app/assets/images/payment_icons/orangemobilemoney.svg +0 -1
  1251. data/app/assets/images/payment_icons/otpbank.svg +0 -1
  1252. data/app/assets/images/payment_icons/oxxo.svg +0 -1
  1253. data/app/assets/images/payment_icons/paybybank.svg +0 -1
  1254. data/app/assets/images/payment_icons/paybybankmollie.svg +0 -1
  1255. data/app/assets/images/payment_icons/paybybankus.svg +0 -1
  1256. data/app/assets/images/payment_icons/paycash.svg +0 -1
  1257. data/app/assets/images/payment_icons/payco.svg +0 -1
  1258. data/app/assets/images/payment_icons/payeverpaybybank.svg +0 -1
  1259. data/app/assets/images/payment_icons/payme.svg +0 -1
  1260. data/app/assets/images/payment_icons/paynow.svg +0 -1
  1261. data/app/assets/images/payment_icons/paynowmbank.svg +0 -1
  1262. data/app/assets/images/payment_icons/paypal.svg +0 -1
  1263. data/app/assets/images/payment_icons/paypo.svg +0 -1
  1264. data/app/assets/images/payment_icons/payrexxbanktransfer.svg +0 -1
  1265. data/app/assets/images/payment_icons/payrexxpaybybank.svg +0 -1
  1266. data/app/assets/images/payment_icons/paysafecash.svg +0 -1
  1267. data/app/assets/images/payment_icons/pix.svg +0 -1
  1268. data/app/assets/images/payment_icons/polygon.svg +0 -1
  1269. data/app/assets/images/payment_icons/przelewy24.svg +0 -1
  1270. data/app/assets/images/payment_icons/publicbank.svg +0 -1
  1271. data/app/assets/images/payment_icons/publicbank_pbe.svg +0 -1
  1272. data/app/assets/images/payment_icons/qr_promptpay.svg +0 -1
  1273. data/app/assets/images/payment_icons/rabobank.svg +0 -1
  1274. data/app/assets/images/payment_icons/revolut.svg +0 -1
  1275. data/app/assets/images/payment_icons/rhbbank.svg +0 -1
  1276. data/app/assets/images/payment_icons/riverty.svg +0 -1
  1277. data/app/assets/images/payment_icons/safetypaybanktransfer.svg +0 -1
  1278. data/app/assets/images/payment_icons/samsung_pay.svg +0 -1
  1279. data/app/assets/images/payment_icons/satispay.svg +0 -1
  1280. data/app/assets/images/payment_icons/scalapay.svg +0 -1
  1281. data/app/assets/images/payment_icons/scotiabank.svg +0 -1
  1282. data/app/assets/images/payment_icons/seabankid.svg +0 -1
  1283. data/app/assets/images/payment_icons/seabankph.svg +0 -1
  1284. data/app/assets/images/payment_icons/sepa_bank_transfer.svg +0 -1
  1285. data/app/assets/images/payment_icons/sepadebit.svg +0 -22
  1286. data/app/assets/images/payment_icons/sepadirectdebit.svg +0 -1
  1287. data/app/assets/images/payment_icons/seveneleven.svg +0 -1
  1288. data/app/assets/images/payment_icons/shib.svg +0 -1
  1289. data/app/assets/images/payment_icons/shopcash.svg +0 -1
  1290. data/app/assets/images/payment_icons/siauliubankas.svg +0 -1
  1291. data/app/assets/images/payment_icons/skeps.svg +0 -1
  1292. data/app/assets/images/payment_icons/snap_checkout.svg +0 -1
  1293. data/app/assets/images/payment_icons/sofort.svg +0 -1
  1294. data/app/assets/images/payment_icons/softbank.svg +0 -1
  1295. data/app/assets/images/payment_icons/spei.svg +0 -1
  1296. data/app/assets/images/payment_icons/storecredit.svg +0 -1
  1297. data/app/assets/images/payment_icons/stripe.svg +0 -1
  1298. data/app/assets/images/payment_icons/sveacheckout.svg +0 -1
  1299. data/app/assets/images/payment_icons/swedbank.svg +0 -1
  1300. data/app/assets/images/payment_icons/swish.svg +0 -1
  1301. data/app/assets/images/payment_icons/tbibank.svg +0 -1
  1302. data/app/assets/images/payment_icons/thanachartbank.svg +0 -1
  1303. data/app/assets/images/payment_icons/tnmmoney.svg +0 -1
  1304. data/app/assets/images/payment_icons/touchngo.svg +0 -1
  1305. data/app/assets/images/payment_icons/trustly.svg +0 -1
  1306. data/app/assets/images/payment_icons/twint.svg +0 -1
  1307. data/app/assets/images/payment_icons/uaevisa.svg +0 -1
  1308. data/app/assets/images/payment_icons/unionpay.svg +0 -1
  1309. data/app/assets/images/payment_icons/unzerbanktransfer.svg +0 -1
  1310. data/app/assets/images/payment_icons/upi.svg +0 -1
  1311. data/app/assets/images/payment_icons/usbank.svg +0 -1
  1312. data/app/assets/images/payment_icons/usdc.svg +0 -1
  1313. data/app/assets/images/payment_icons/usdt.svg +0 -1
  1314. data/app/assets/images/payment_icons/venmo.svg +0 -1
  1315. data/app/assets/images/payment_icons/vipps.svg +0 -1
  1316. data/app/assets/images/payment_icons/visa.svg +0 -1
  1317. data/app/assets/images/payment_icons/volksbank.svg +0 -1
  1318. data/app/assets/images/payment_icons/waavepaybybank.svg +0 -1
  1319. data/app/assets/images/payment_icons/walley.svg +0 -1
  1320. data/app/assets/images/payment_icons/wbtc.svg +0 -1
  1321. data/app/assets/images/payment_icons/wechatpay.svg +0 -1
  1322. data/app/assets/images/payment_icons/whishcheckout.svg +0 -1
  1323. data/app/assets/images/payment_icons/wingbank.svg +0 -1
  1324. data/app/assets/images/payment_icons/xrp.svg +0 -1
  1325. data/app/assets/images/payment_icons/zip.svg +0 -1
  1326. data/app/controllers/spree/base_controller.rb +0 -11
  1327. data/app/finders/concerns/spree/product_filterable.rb +0 -9
  1328. data/app/finders/spree/addresses/find.rb +0 -27
  1329. data/app/finders/spree/base_finder.rb +0 -14
  1330. data/app/finders/spree/countries/find.rb +0 -31
  1331. data/app/finders/spree/credit_cards/find.rb +0 -12
  1332. data/app/finders/spree/option_values/find_available.rb +0 -28
  1333. data/app/finders/spree/orders/find_complete.rb +0 -114
  1334. data/app/finders/spree/orders/find_current.rb +0 -24
  1335. data/app/finders/spree/orders/finder_helper.rb +0 -17
  1336. data/app/finders/spree/products/find.rb +0 -328
  1337. data/app/finders/spree/taxons/find.rb +0 -99
  1338. data/app/finders/spree/variants/find.rb +0 -30
  1339. data/app/finders/spree/variants/option_types_finder.rb +0 -21
  1340. data/app/helpers/spree/addresses_helper.rb +0 -97
  1341. data/app/helpers/spree/base_helper.rb +0 -132
  1342. data/app/helpers/spree/currency_helper.rb +0 -46
  1343. data/app/helpers/spree/integrations_helper.rb +0 -15
  1344. data/app/helpers/spree/locale_helper.rb +0 -44
  1345. data/app/helpers/spree/localized_names_helper.rb +0 -25
  1346. data/app/helpers/spree/products_helper.rb +0 -147
  1347. data/app/helpers/spree/shipment_helper.rb +0 -12
  1348. data/app/javascript/spree/core/controllers/address_autocomplete_controller.js +0 -230
  1349. data/app/javascript/spree/core/controllers/address_form_controller.js +0 -98
  1350. data/app/javascript/spree/core/controllers/disable_submit_button_controller.js +0 -19
  1351. data/app/javascript/spree/core/controllers/enable_button_controller.js +0 -29
  1352. data/app/javascript/spree/core/helpers/address_autocomplete/google_places_suggestions_provider.js +0 -161
  1353. data/app/javascript/spree/core/helpers/debounce.js +0 -9
  1354. data/app/javascript/spree/core/helpers/scroll_lock.js +0 -25
  1355. data/app/jobs/spree/media/migrate_product_assets_job.rb +0 -83
  1356. data/app/jobs/spree/reports/generate_job.rb +0 -23
  1357. data/app/mailers/spree/report_mailer.rb +0 -20
  1358. data/app/models/concerns/spree/adjustment_source.rb +0 -58
  1359. data/app/models/concerns/spree/default_price.rb +0 -138
  1360. data/app/models/concerns/spree/display_on.rb +0 -50
  1361. data/app/models/concerns/spree/image_methods.rb +0 -40
  1362. data/app/models/concerns/spree/legacy_multi_store_support.rb +0 -48
  1363. data/app/models/concerns/spree/metafield_filterable.rb +0 -162
  1364. data/app/models/concerns/spree/metafields.rb +0 -256
  1365. data/app/models/concerns/spree/number_as_param.rb +0 -13
  1366. data/app/models/concerns/spree/store_scoped_resource.rb +0 -37
  1367. data/app/models/concerns/spree/unique_name.rb +0 -12
  1368. data/app/models/spree/adjustable/adjuster/base.rb +0 -25
  1369. data/app/models/spree/adjustable/adjuster/promotion.rb +0 -41
  1370. data/app/models/spree/adjustable/adjuster/tax.rb +0 -26
  1371. data/app/models/spree/adjustable/adjustments_updater.rb +0 -74
  1372. data/app/models/spree/adjustable/promotion_accumulator.rb +0 -78
  1373. data/app/models/spree/adjustment.rb +0 -130
  1374. data/app/models/spree/asset/support/active_storage.rb +0 -26
  1375. data/app/models/spree/calculator/default_tax.rb +0 -87
  1376. data/app/models/spree/calculator/returns/default_refund_amount.rb +0 -40
  1377. data/app/models/spree/checkout/default_requirements.rb +0 -51
  1378. data/app/models/spree/checkout/registry.rb +0 -112
  1379. data/app/models/spree/checkout/requirement.rb +0 -49
  1380. data/app/models/spree/checkout/requirements.rb +0 -56
  1381. data/app/models/spree/checkout/step.rb +0 -52
  1382. data/app/models/spree/customer_return.rb +0 -94
  1383. data/app/models/spree/image/configuration/active_storage.rb +0 -11
  1384. data/app/models/spree/image.rb +0 -6
  1385. data/app/models/spree/legacy_admin_user.rb +0 -31
  1386. data/app/models/spree/legacy_user.rb +0 -33
  1387. data/app/models/spree/log_entry.rb +0 -30
  1388. data/app/models/spree/metafield_definition/search_capabilities.rb +0 -104
  1389. data/app/models/spree/metafields/boolean.rb +0 -15
  1390. data/app/models/spree/metafields/json.rb +0 -37
  1391. data/app/models/spree/metafields/long_text.rb +0 -11
  1392. data/app/models/spree/metafields/number.rb +0 -23
  1393. data/app/models/spree/metafields/rich_text.rb +0 -18
  1394. data/app/models/spree/metafields/short_text.rb +0 -15
  1395. data/app/models/spree/option_type_prototype.rb +0 -9
  1396. data/app/models/spree/order/address_book.rb +0 -100
  1397. data/app/models/spree/order/checkout.rb +0 -350
  1398. data/app/models/spree/order/currency_updater.rb +0 -37
  1399. data/app/models/spree/order/digital.rb +0 -60
  1400. data/app/models/spree/order/gift_card.rb +0 -71
  1401. data/app/models/spree/order/payments.rb +0 -64
  1402. data/app/models/spree/order/store_credit.rb +0 -110
  1403. data/app/models/spree/order/webhooks.rb +0 -19
  1404. data/app/models/spree/order_approval.rb +0 -19
  1405. data/app/models/spree/order_cancellation.rb +0 -19
  1406. data/app/models/spree/order_contents.rb +0 -31
  1407. data/app/models/spree/order_routing/strategy/legacy.rb +0 -33
  1408. data/app/models/spree/payment/webhooks.rb +0 -15
  1409. data/app/models/spree/permission_sets/base.rb +0 -81
  1410. data/app/models/spree/permission_sets/configuration_management.rb +0 -64
  1411. data/app/models/spree/permission_sets/dashboard_display.rb +0 -17
  1412. data/app/models/spree/permission_sets/default_customer.rb +0 -83
  1413. data/app/models/spree/permission_sets/order_display.rb +0 -27
  1414. data/app/models/spree/permission_sets/order_management.rb +0 -33
  1415. data/app/models/spree/permission_sets/product_display.rb +0 -27
  1416. data/app/models/spree/permission_sets/product_management.rb +0 -28
  1417. data/app/models/spree/permission_sets/promotion_management.rb +0 -22
  1418. data/app/models/spree/permission_sets/role_management.rb +0 -21
  1419. data/app/models/spree/permission_sets/stock_display.rb +0 -19
  1420. data/app/models/spree/permission_sets/stock_management.rb +0 -19
  1421. data/app/models/spree/permission_sets/super_user.rb +0 -28
  1422. data/app/models/spree/permission_sets/user_display.rb +0 -19
  1423. data/app/models/spree/permission_sets/user_management.rb +0 -20
  1424. data/app/models/spree/product/webhooks.rb +0 -17
  1425. data/app/models/spree/prototype_taxon.rb +0 -9
  1426. data/app/models/spree/reimbursement/credit.rb +0 -27
  1427. data/app/models/spree/reimbursement/reimbursement_type_engine.rb +0 -44
  1428. data/app/models/spree/reimbursement/reimbursement_type_validator.rb +0 -15
  1429. data/app/models/spree/reimbursement.rb +0 -167
  1430. data/app/models/spree/reimbursement_performer.rb +0 -34
  1431. data/app/models/spree/reimbursement_tax_calculator.rb +0 -37
  1432. data/app/models/spree/reimbursement_type/credit.rb +0 -13
  1433. data/app/models/spree/reimbursement_type/exchange.rb +0 -9
  1434. data/app/models/spree/reimbursement_type/original_payment.rb +0 -13
  1435. data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +0 -64
  1436. data/app/models/spree/reimbursement_type/store_credit.rb +0 -20
  1437. data/app/models/spree/reimbursement_type.rb +0 -22
  1438. data/app/models/spree/report.rb +0 -140
  1439. data/app/models/spree/report_line_item.rb +0 -27
  1440. data/app/models/spree/report_line_items/products_performance.rb +0 -76
  1441. data/app/models/spree/report_line_items/sales_total.rb +0 -49
  1442. data/app/models/spree/reports/products_performance.rb +0 -44
  1443. data/app/models/spree/reports/sales_total.rb +0 -18
  1444. data/app/models/spree/return_authorization.rb +0 -109
  1445. data/app/models/spree/return_item/eligibility_validator/base_validator.rb +0 -24
  1446. data/app/models/spree/return_item/eligibility_validator/default.rb +0 -30
  1447. data/app/models/spree/return_item/eligibility_validator/inventory_shipped.rb +0 -16
  1448. data/app/models/spree/return_item/eligibility_validator/no_reimbursements.rb +0 -16
  1449. data/app/models/spree/return_item/eligibility_validator/order_completed.rb +0 -16
  1450. data/app/models/spree/return_item/eligibility_validator/rma_required.rb +0 -16
  1451. data/app/models/spree/return_item/eligibility_validator/time_since_purchase.rb +0 -16
  1452. data/app/models/spree/return_item/exchange_variant_eligibility/same_option_value.rb +0 -34
  1453. data/app/models/spree/return_item/exchange_variant_eligibility/same_product.rb +0 -9
  1454. data/app/models/spree/return_item.rb +0 -287
  1455. data/app/models/spree/returns_calculator.rb +0 -7
  1456. data/app/models/spree/search_provider/meilisearch.rb +0 -551
  1457. data/app/models/spree/shipment/custom_events.rb +0 -47
  1458. data/app/models/spree/shipment/webhooks.rb +0 -11
  1459. data/app/models/spree/shipment_handler.rb +0 -38
  1460. data/app/models/spree/shipping_category.rb +0 -25
  1461. data/app/models/spree/shipping_method_category.rb +0 -8
  1462. data/app/models/spree/shipping_method_zone.rb +0 -8
  1463. data/app/models/spree/state_change.rb +0 -12
  1464. data/app/models/spree/stock/splitter/digital.rb +0 -35
  1465. data/app/models/spree/stock/splitter/shipping_category.rb +0 -31
  1466. data/app/models/spree/stock_item/webhooks.rb +0 -6
  1467. data/app/models/spree/stock_movement/webhooks.rb +0 -6
  1468. data/app/models/spree/variant/webhooks.rb +0 -6
  1469. data/app/presenters/spree/csv/metafields_helper.rb +0 -17
  1470. data/app/presenters/spree/search_provider/product_presenter.rb +0 -158
  1471. data/app/presenters/spree/variant_presenter.rb +0 -81
  1472. data/app/presenters/spree/variants/option_types_presenter.rb +0 -87
  1473. data/app/services/spree/account/addresses/create.rb +0 -8
  1474. data/app/services/spree/account/addresses/update.rb +0 -8
  1475. data/app/services/spree/account/create.rb +0 -17
  1476. data/app/services/spree/account/update.rb +0 -15
  1477. data/app/services/spree/cart/add_item.rb +0 -63
  1478. data/app/services/spree/cart/associate.rb +0 -29
  1479. data/app/services/spree/cart/change_currency.rb +0 -27
  1480. data/app/services/spree/cart/create.rb +0 -26
  1481. data/app/services/spree/cart/destroy.rb +0 -50
  1482. data/app/services/spree/cart/empty.rb +0 -38
  1483. data/app/services/spree/cart/estimate_shipping_rates.rb +0 -39
  1484. data/app/services/spree/cart/recalculate.rb +0 -35
  1485. data/app/services/spree/cart/remove_item.rb +0 -39
  1486. data/app/services/spree/cart/remove_line_item.rb +0 -28
  1487. data/app/services/spree/cart/remove_out_of_stock_items.rb +0 -65
  1488. data/app/services/spree/cart/set_quantity.rb +0 -32
  1489. data/app/services/spree/cart/update.rb +0 -37
  1490. data/app/services/spree/carts/complete.rb +0 -50
  1491. data/app/services/spree/carts/upsert_items.rb +0 -76
  1492. data/app/services/spree/checkout/complete.rb +0 -23
  1493. data/app/services/spree/checkout/get_shipping_rates.rb +0 -52
  1494. data/app/services/spree/checkout/next.rb +0 -13
  1495. data/app/services/spree/checkout/select_shipping_method.rb +0 -63
  1496. data/app/services/spree/checkout/update.rb +0 -72
  1497. data/app/services/spree/credit_cards/destroy.rb +0 -25
  1498. data/app/services/spree/fulfillments/create.rb +0 -259
  1499. data/app/services/spree/gift_cards/apply.rb +0 -78
  1500. data/app/services/spree/gift_cards/redeem.rb +0 -17
  1501. data/app/services/spree/gift_cards/remove.rb +0 -39
  1502. data/app/services/spree/line_items/create.rb +0 -20
  1503. data/app/services/spree/line_items/destroy.rb +0 -18
  1504. data/app/services/spree/line_items/helper.rb +0 -11
  1505. data/app/services/spree/line_items/update.rb +0 -17
  1506. data/app/services/spree/orders/build_shipments.rb +0 -29
  1507. data/app/services/spree/orders/cancel.rb +0 -54
  1508. data/app/services/spree/orders/complete.rb +0 -53
  1509. data/app/services/spree/orders/update_contact_information.rb +0 -19
  1510. data/app/services/spree/orders/upsert_items.rb +0 -70
  1511. data/app/services/spree/payments/handle_webhook.rb +0 -57
  1512. data/app/services/spree/search_provider/metafield_schema.rb +0 -144
  1513. data/app/services/spree/seeds/countries.rb +0 -35
  1514. data/app/services/spree/seeds/default_reimbursement_types.rb +0 -12
  1515. data/app/services/spree/seeds/shipping_categories.rb +0 -12
  1516. data/app/services/spree/seeds/states.rb +0 -82
  1517. data/app/services/spree/seeds/stock_locations.rb +0 -18
  1518. data/app/services/spree/seeds/store_credit_categories.rb +0 -14
  1519. data/app/services/spree/seeds/zones.rb +0 -52
  1520. data/app/services/spree/shipments/add_item.rb +0 -32
  1521. data/app/services/spree/shipments/change_state.rb +0 -14
  1522. data/app/services/spree/shipments/create.rb +0 -48
  1523. data/app/services/spree/shipments/helper.rb +0 -23
  1524. data/app/services/spree/shipments/remove_item.rb +0 -55
  1525. data/app/services/spree/stores/settings_defaults_by_country.rb +0 -38
  1526. data/app/services/spree/themes/duplicator.rb +0 -40
  1527. data/app/sorters/spree/base_sorter.rb +0 -47
  1528. data/app/sorters/spree/classifications/sort.rb +0 -68
  1529. data/app/sorters/spree/products/sort.rb +0 -73
  1530. data/app/subscribers/spree/report_subscriber.rb +0 -26
  1531. data/app/views/spree/addresses/_form.html.erb +0 -59
  1532. data/app/views/spree/addresses/_suggestions_box.erb +0 -14
  1533. data/app/views/spree/admin_user_mailer/confirmation_email.html.erb +0 -7
  1534. data/app/views/spree/report_mailer/report_done.html.erb +0 -7
  1535. data/app/views/spree/shared/_google_places_api_loader.html.erb +0 -7
  1536. data/app/views/spree/shared/_mailer_line_item.html.erb +0 -12
  1537. data/app/views/spree/shared/_payment.html.erb +0 -52
  1538. data/config/importmap.rb +0 -15
  1539. data/config/initializers/carmen.rb +0 -23
  1540. data/config/initializers/rails61_fixes.rb +0 -1
  1541. data/db/migrate/20250120152208_add_default_locale_to_action_text_rich_texts.rb +0 -7
  1542. data/db/migrate/20250122113708_add_first_and_last_name_to_spree_admin_class.rb +0 -9
  1543. data/db/migrate/20250204162453_create_spree_reports.rb +0 -13
  1544. data/db/migrate/20250217171018_create_action_text_video_embeds.rb +0 -11
  1545. data/db/migrate/20250508060800_add_selected_locale_to_spree_admin_users.rb +0 -8
  1546. data/db/migrate/20260429000001_create_spree_order_cancellations.rb +0 -25
  1547. data/db/migrate/20260429000002_create_spree_order_approvals.rb +0 -22
  1548. data/db/sample_data/markets.rb +0 -64
  1549. data/db/sample_data/metafield_definitions.rb +0 -7
  1550. data/db/sample_data/shipping_methods.rb +0 -61
  1551. data/lib/generators/spree/authentication/custom/custom_generator.rb +0 -49
  1552. data/lib/generators/spree/authentication/custom/templates/authentication_helpers.rb.tt +0 -50
  1553. data/lib/generators/spree/authentication/devise/devise_generator.rb +0 -84
  1554. data/lib/generators/spree/authentication/devise/templates/authentication_helpers.rb.tt +0 -50
  1555. data/lib/generators/spree/dummy/templates/app/assets/config/manifest.js +0 -0
  1556. data/lib/spree/core/components.rb +0 -31
  1557. data/lib/spree/core/controller_helpers/auth.rb +0 -52
  1558. data/lib/spree/core/controller_helpers/currency.rb +0 -59
  1559. data/lib/spree/core/controller_helpers/locale.rb +0 -99
  1560. data/lib/spree/core/controller_helpers/order.rb +0 -172
  1561. data/lib/spree/core/controller_helpers/strong_parameters.rb +0 -48
  1562. data/lib/spree/core/controller_helpers/turbo.rb +0 -19
  1563. data/lib/spree/core/partials.rb +0 -42
  1564. data/lib/spree/core/previews/report_preview.rb +0 -40
  1565. data/lib/spree/permitted_attributes.rb +0 -337
  1566. data/lib/spree/testing_support/authorization_helpers.rb +0 -72
  1567. data/lib/spree/testing_support/caching.rb +0 -47
  1568. data/lib/spree/testing_support/capybara_config.rb +0 -45
  1569. data/lib/spree/testing_support/capybara_ext.rb +0 -83
  1570. data/lib/spree/testing_support/controller_requests.rb +0 -11
  1571. data/lib/spree/testing_support/factories/adjustment_factory.rb +0 -28
  1572. data/lib/spree/testing_support/factories/asset_factory.rb +0 -12
  1573. data/lib/spree/testing_support/factories/customer_return_factory.rb +0 -36
  1574. data/lib/spree/testing_support/factories/digital_factory.rb +0 -10
  1575. data/lib/spree/testing_support/factories/image_factory.rb +0 -11
  1576. data/lib/spree/testing_support/factories/inventory_unit_factory.rb +0 -18
  1577. data/lib/spree/testing_support/factories/log_entry_factory.rb +0 -6
  1578. data/lib/spree/testing_support/factories/metafield_definition_factory.rb +0 -82
  1579. data/lib/spree/testing_support/factories/metafield_factory.rb +0 -68
  1580. data/lib/spree/testing_support/factories/prototype_factory.rb +0 -9
  1581. data/lib/spree/testing_support/factories/reimbursement_credit_factory.rb +0 -7
  1582. data/lib/spree/testing_support/factories/reimbursement_factory.rb +0 -16
  1583. data/lib/spree/testing_support/factories/reimbursement_type_factory.rb +0 -7
  1584. data/lib/spree/testing_support/factories/report_factory.rb +0 -33
  1585. data/lib/spree/testing_support/factories/return_authorization_factory.rb +0 -21
  1586. data/lib/spree/testing_support/factories/return_item_factory.rb +0 -14
  1587. data/lib/spree/testing_support/factories/shipment_factory.rb +0 -21
  1588. data/lib/spree/testing_support/factories/shipping_category_factory.rb +0 -9
  1589. data/lib/spree/testing_support/factories/shipping_method_factory.rb +0 -31
  1590. data/lib/spree/testing_support/factories/shipping_rate_factory.rb +0 -7
  1591. data/lib/spree/testing_support/factories/state_change_factory.rb +0 -8
  1592. data/lib/spree/testing_support/factories/stock_item_factory.rb +0 -22
  1593. data/lib/spree/testing_support/factories/store_credit_category_factory.rb +0 -9
  1594. data/lib/spree/testing_support/factories/store_credit_type_factory.rb +0 -16
  1595. data/lib/spree/testing_support/factories/wished_variant_factory.rb +0 -6
  1596. data/lib/spree/webhooks.rb +0 -22
  1597. data/lib/tasks/role_users.rake +0 -21
  1598. data/vendor/javascript/@rails--request.js.js +0 -4
  1599. data/vendor/javascript/@stimulus-components--auto-submit.js +0 -4
  1600. data/vendor/javascript/stimulus-reveal-controller.js +0 -4
  1601. data/vendor/javascript/stimulus-textarea-autogrow.js +0 -4
  1602. data/vendor/javascript/tailwindcss-stimulus-components.js +0 -4
@@ -0,0 +1,677 @@
1
+ module Spree
2
+ module Reporting
3
+ module Adapters
4
+ # Compiles reporting queries straight against the transactional schema
5
+ # through store-scoped associations. Default adapter — zero extra infra.
6
+ #
7
+ # Metrics aggregate on their own base relation (:orders or :line_items);
8
+ # one grouped SQL query runs per base per period and the row sets merge
9
+ # on dimension keys. Time buckets resolve in the store's timezone and
10
+ # zero-fill across the requested range. Ranked value-dimension queries
11
+ # push ORDER BY/LIMIT into SQL, and their comparison period only
12
+ # aggregates the surviving keys.
13
+ class Live < Base
14
+ # Roughly a century — long enough for any report a merchant would read,
15
+ # short enough that the widest one still fits in memory at day grain.
16
+ MAX_RANGE_DAYS = 36_600
17
+ # SQL operator and Ruby message for each metric-filter op, paired so the
18
+ # HAVING path and the Ruby path for include_empty cannot drift apart.
19
+ COMPARISONS = { eq: ['=', :==], gt: ['>', :>], gte: ['>=', :>=],
20
+ lt: ['<', :<], lte: ['<=', :<=] }.freeze
21
+ # Every hour bucket label, from the three adapters' SQL and the Ruby
22
+ # zero-fill alike, has to render identically or each bucket duplicates.
23
+ HOUR_BUCKET_FORMAT = '%Y-%m-%d %H:00:00'.freeze
24
+ # Upper bound on one population batch when filling empty rows.
25
+ MEMBER_BATCH_SIZE = 1_000
26
+
27
+ def execute(query)
28
+ @query = query
29
+
30
+ current = period_data(query.time_range, push_sort: sql_sortable?)
31
+ previous = query.compare? ? period_data(query.previous_time_range, key_filter: compare_key_filter(current)) : nil
32
+
33
+ Result.new(
34
+ meta: {
35
+ currency: query.currency,
36
+ time_range: query.time_range,
37
+ previous_time_range: query.compare? ? query.previous_time_range : nil,
38
+ metrics: query.metrics.map(&:name),
39
+ dimensions: query.dimensions.map { |d| { name: d[:dimension].name, grain: d[:grain] }.compact }
40
+ },
41
+ totals: build_totals(current, previous),
42
+ rows: build_rows(current, previous)
43
+ )
44
+ end
45
+
46
+ private
47
+
48
+ # ---- period execution ----
49
+
50
+ def period_data(range, push_sort: false, key_filter: nil)
51
+ {
52
+ range: range,
53
+ totals: aggregate(range, grouped: false),
54
+ groups: query.dimensions.any? ? aggregate(range, grouped: true, push_sort: push_sort, key_filter: key_filter) : {}
55
+ }
56
+ end
57
+
58
+ # Runs one SQL query per metric base; returns { key_tuple => { metric => value } }.
59
+ # Ungrouped aggregates use the single [] key.
60
+ def aggregate(range, grouped:, push_sort: false, key_filter: nil)
61
+ rows = Hash.new { |h, k| h[k] = {} }
62
+ # Read by #abandoned_cutoff: a cart is judged by the clock at the end
63
+ # of the period being aggregated, not by today's.
64
+ self.aggregating_range = range
65
+
66
+ query.aggregated_metrics.group_by(&:base).each do |base, metrics|
67
+ scope = base_scope(base, range, grouped: grouped)
68
+ selects = metrics.map { |m| "#{resolve_sql(m.sql_for(grouped: grouped))} AS #{metric_alias(m)}" }
69
+
70
+ if grouped
71
+ dimension_selects = query.dimensions.map do |d|
72
+ "#{dimension_expression(d)} AS #{dimension_alias(d)}"
73
+ end
74
+ scope = scope.group(query.dimensions.map { |d| Arel.sql(group_by_term(d)) })
75
+ scope = apply_key_filter(scope, key_filter) if key_filter
76
+ scope = apply_metric_filters(scope, metrics) unless query.include_empty?
77
+ scope = apply_sql_sort(scope, metrics) if push_sort
78
+ selects = dimension_selects + selects
79
+ end
80
+
81
+ connection.select_all(scope.select(selects.map { |s| Arel.sql(s) }).to_sql).each do |row|
82
+ key = grouped ? query.dimensions.map { |d| dimension_key(d, row[dimension_alias(d)]) } : []
83
+ metrics.each { |m| rows[key][m.name] = cast_value(m, row[metric_alias(m)]) }
84
+ end
85
+ end
86
+
87
+ rows.each_value { |metrics| apply_derived(metrics) }
88
+ rows
89
+ end
90
+
91
+ # The grouped query carries the dimension joins; the ungrouped totals
92
+ # deliberately do not — a join that is not 1:1 with the base rows (a
93
+ # product in three categories, an order without a shipping address)
94
+ # would otherwise multiply or drop rows, and the Total row must equal
95
+ # the dimensionless figure for the same range and filters.
96
+ def base_scope(base, range, grouped:)
97
+ scope = base_relation(base, range)
98
+ scope = apply_dimension_joins(scope, base) if grouped
99
+ apply_filters(scope, base, range)
100
+ end
101
+
102
+ # The base's own registered relation, already store-scoped and
103
+ # range-filtered. Sales bases need no `complete` scope on top of the
104
+ # range predicate (merging it would replace the range condition — Rails
105
+ # merge overwrites same-column wheres), and exclude canceled orders
106
+ # explicitly: sales figures count what stayed sold.
107
+ def base_relation(base, range)
108
+ scope = query.registry.base!(base).relation.call(query.store, range, query.scope_currency)
109
+
110
+ # Association default orderings break grouped selects on PostgreSQL.
111
+ scope.reorder(nil)
112
+ end
113
+
114
+ # Dimension joins are declared from their own base; they only apply
115
+ # when the executing scope is that base (an :orders-based metric never
116
+ # joins line-item tables — validate_bases! guarantees compatibility).
117
+ def apply_dimension_joins(scope, base)
118
+ query.dimensions.each do |d|
119
+ scope = join_for(scope, d[:dimension], base)
120
+ end
121
+ scope
122
+ end
123
+
124
+ # Metric filters compile to HAVING and so apply to the grouped query
125
+ # only — the Total row stays the period's real figure rather than the
126
+ # sum of the rows that happened to survive the filter (Decision 13:
127
+ # rows need not sum to the total).
128
+ #
129
+ # Skipped entirely under include_empty: HAVING would drop the rows that
130
+ # do not match, and the population merge would then reintroduce them as
131
+ # zeros — so a bestseller would come back looking like it never sold.
132
+ # Those queries filter once, in Ruby, over the merged values.
133
+ #
134
+ # Only filters whose metric this base aggregates are applied; a query
135
+ # spanning two bases runs one SQL statement per base, and a HAVING
136
+ # naming another base's column would not compile.
137
+ def apply_metric_filters(scope, metrics)
138
+ names = metrics.map(&:name)
139
+
140
+ query.metric_filters.each do |filter|
141
+ metric = filter[:metric]
142
+ next unless names.include?(metric.name)
143
+
144
+ # The value rides as a bind rather than an interpolation: it is
145
+ # the only piece of request data that reaches the statement, and
146
+ # the aggregate beside it is registered SQL.
147
+ scope = scope.having(
148
+ Arel.sql("#{resolve_sql(metric.sql_for(grouped: true))} " \
149
+ "#{COMPARISONS.fetch(filter[:op]).first} ?"),
150
+ filter[:value]
151
+ )
152
+ end
153
+
154
+ scope
155
+ end
156
+
157
+ # A filter on a joined dimension narrows through an id subquery rather
158
+ # than joining the aggregating scope itself: `category IN (a, b)` must
159
+ # keep a line item once even when its product sits in both categories.
160
+ def apply_filters(scope, base, range)
161
+ query.filters.each do |filter|
162
+ dimension = filter[:dimension]
163
+ predicate = in_predicate(qualified_column(dimension), filter[:values])
164
+
165
+ if dimension.joins.blank?
166
+ scope = scope.where(predicate)
167
+ else
168
+ # Bounded like the outer query (store, currency, range) so the
169
+ # planner starts from the same indexed slice.
170
+ table = scope.klass.arel_table
171
+ ids = join_for(base_relation(base, range), dimension, base).where(predicate).select(table[:id])
172
+ scope = scope.where(table[:id].in(ids.arel))
173
+ end
174
+ end
175
+ scope
176
+ end
177
+
178
+ # `column IN (values)` as an Arel node. The column is a validated
179
+ # identifier (see #qualified_column) and the values bind as parameters,
180
+ # so no request data reaches the statement as text.
181
+ def in_predicate(column, values)
182
+ node = Arel::Nodes::SqlLiteral.new(column)
183
+ present, null = Array(values).partition { |value| !value.nil? }
184
+ # `IN (NULL)` never matches, so a NULL group would silently drop out
185
+ # of the comparison period and report a zero previous value.
186
+ return node.in(present) if null.empty?
187
+ return node.eq(nil) if present.empty?
188
+
189
+ node.in(present).or(node.eq(nil))
190
+ end
191
+
192
+ # Dimension joins are declared from the dimension's own base. Reaching
193
+ # an :orders dimension (e.g. the ship address) from the :line_items
194
+ # base goes through the order association.
195
+ # LEFT joins, always. A dimension's association is frequently optional —
196
+ # an order need not have a shipping address, a product need not be in a
197
+ # category — and an inner join drops those rows from the breakdown
198
+ # while the ungrouped Total still counts them, so the rows quietly fail
199
+ # to add up. Left-joining produces a NULL key instead, which is a real
200
+ # group the hydration already renders as "Unassigned".
201
+ # How a dimension's joins are reached from the executing base is
202
+ # registry data (`reaches`), so nothing here branches on a base name:
203
+ # a dimension on this base joins directly, one on a reachable base
204
+ # joins through the association that base declared.
205
+ def join_for(scope, dimension, base)
206
+ return scope if dimension.joins.blank?
207
+
208
+ definition = query.registry.base!(base)
209
+ return scope unless definition.reaches?(dimension.base)
210
+
211
+ path = definition.path_to(dimension.base)
212
+ scope.left_joins(path ? { path => dimension.joins } : dimension.joins)
213
+ end
214
+
215
+ # ---- sorted/limited rankings ----
216
+
217
+ # ORDER BY + LIMIT can move into SQL when one base group carries every
218
+ # aggregated metric (so the sort metric and the limit apply to the
219
+ # same query) and the grouping is by value, not time buckets.
220
+ # include_empty is excluded: pushing LIMIT into the grouped SQL would
221
+ # cut the result before the dimension's full population is merged in,
222
+ # so the members with no rows — the ones being asked about — would be
223
+ # the first to fall off.
224
+ def sql_sortable?
225
+ return @sql_sortable unless @sql_sortable.nil?
226
+
227
+ @sql_sortable = query.sort && query.limit && query.time_dimension.nil? && query.dimensions.any? &&
228
+ !query.include_empty? && !sort_metric.derived? &&
229
+ query.aggregated_metrics.map(&:base).uniq.one? || false
230
+ end
231
+
232
+ def sort_metric
233
+ @sort_metric ||= query.registry.metric!(query.sort[:metric])
234
+ end
235
+
236
+ def apply_sql_sort(scope, metrics)
237
+ return scope unless metrics.any? { |m| m.name == sort_metric.name }
238
+
239
+ scope.order(Arel.sql("#{resolve_sql(sort_metric.sql_for(grouped: true))} " \
240
+ "#{query.sort[:direction] == :desc ? 'DESC' : 'ASC'}"))
241
+ .limit(query.limit)
242
+ end
243
+
244
+ # The comparison period of a ranked value-dimension query only needs
245
+ # the keys that made the current ranking — not every group in the
246
+ # store's history. Only kicks in when SQL limited the current period,
247
+ # so the IN clause is never larger than the limit.
248
+ def compare_key_filter(current)
249
+ return unless sql_sortable? && query.dimensions.size == 1
250
+
251
+ keys = current[:groups].keys.flatten
252
+ keys if keys.any?
253
+ end
254
+
255
+ def apply_key_filter(scope, keys)
256
+ scope.where(in_predicate(dimension_expression(query.dimensions.first), keys))
257
+ end
258
+
259
+ # ---- SQL expressions ----
260
+
261
+ def metric_alias(metric)
262
+ "m_#{metric.name}"
263
+ end
264
+
265
+ # A bare `column:` symbol is read from its own base's table, which the
266
+ # base declares. Guessing it from a two-way ternary silently read every
267
+ # base but :orders as line items.
268
+ def base_table(base)
269
+ resolve_sql(query.registry.base!(base).table)
270
+ end
271
+
272
+ # Registered fragments defer table names as %{placeholders} because
273
+ # model classes cannot load while initializers register the vocabulary.
274
+ # The cutoff rides along because it is the one value a fragment needs
275
+ # that is not a table name — see #abandoned_cutoff.
276
+ def resolve_sql(fragment)
277
+ format(fragment, placeholders)
278
+ end
279
+
280
+ # Table names never vary, so they are built once; the cutoff is the one
281
+ # placeholder that depends on which period is being aggregated.
282
+ def placeholders
283
+ table_placeholders.merge(abandoned_cutoff: abandoned_cutoff)
284
+ end
285
+
286
+ def table_placeholders
287
+ @table_placeholders ||= {
288
+ orders: Spree::Order.table_name,
289
+ line_items: Spree::LineItem.table_name,
290
+ variants: Spree::Variant.table_name,
291
+ products: Spree::Product.table_name,
292
+ addresses: Spree::Address.table_name,
293
+ product_categories: Spree::ProductCategory.table_name,
294
+ refunds: Spree::Refund.table_name,
295
+ fees: Spree::Fee.table_name,
296
+ commission_lines: Spree::CommissionLine.table_name,
297
+ payments: Spree::Payment.table_name,
298
+ stock_movements: Spree::StockMovement.table_name,
299
+ stock_levels: Spree::StockLevel.table_name,
300
+ carts: Spree::Cart.table_name,
301
+ discounts: Spree::Discount.table_name,
302
+ promotions: Spree::Promotion.table_name
303
+ }
304
+ end
305
+
306
+ # The instant a cart must have been quiet since to count as abandoned:
307
+ # the store's window measured back from the end of the period being
308
+ # aggregated, not from now.
309
+ #
310
+ # Measuring from now would make every unconverted cart in a comparison
311
+ # period abandoned — that whole window lies further in the past than
312
+ # any cutoff — so a year-on-year abandonment chart would show a
313
+ # fabricated collapse. A period's carts are judged by the clock at the
314
+ # end of that period.
315
+ def abandoned_cutoff
316
+ hours = query.store.preferred_abandoned_cart_after_hours.to_i.hours
317
+ connection.quote((aggregating_range.last - hours).utc)
318
+ end
319
+
320
+ # The period currently being aggregated. Defaults to the query's own
321
+ # range so anything reading placeholders before #aggregate runs (a sort
322
+ # term, a dimension expression) still resolves.
323
+ def aggregating_range
324
+ @aggregating_range || query.time_range
325
+ end
326
+
327
+ def aggregating_range=(range)
328
+ @aggregating_range = range
329
+ @placeholders = nil
330
+ end
331
+
332
+ def dimension_alias(dim)
333
+ "d_#{dim[:dimension].name}"
334
+ end
335
+
336
+ # What GROUP BY names for a dimension. A plain column repeats its
337
+ # expression, but an expression dimension groups by the SELECT alias:
338
+ # MySQL's only_full_group_by refuses to match a repeated expression
339
+ # containing a correlated subquery, seeing only the bare column inside
340
+ # it. All three databases accept grouping by the alias.
341
+ def group_by_term(dim)
342
+ dim[:dimension].expression? ? dimension_alias(dim) : dimension_expression(dim)
343
+ end
344
+
345
+ def dimension_expression(dim)
346
+ dimension = dim[:dimension]
347
+ return time_bucket_sql(qualified_column(dimension), dim[:grain]) if dimension.time?
348
+
349
+ qualified_column(dimension)
350
+ end
351
+
352
+ # A registered dimension's column, as a SQL identifier. Registered
353
+ # vocabulary is developer-authored, never request data, but this is
354
+ # the point where it becomes SQL — so the resolved fragment must look
355
+ # like `table.column` and nothing else. Anything richer is a
356
+ # registration bug and raises rather than reaching the database.
357
+ def qualified_column(dimension)
358
+ return resolve_sql(dimension.expression) if dimension.expression?
359
+
360
+ resolved = if dimension.column.is_a?(String)
361
+ resolve_sql(dimension.column)
362
+ else
363
+ "#{base_table(dimension.base)}.#{dimension.column}"
364
+ end
365
+
366
+ identifier!(resolved, dimension.name)
367
+ end
368
+
369
+ IDENTIFIER = /\A[a-z_][a-z0-9_]*(\.[a-z_][a-z0-9_]*)?\z/i
370
+
371
+ def identifier!(fragment, name)
372
+ return fragment if IDENTIFIER.match?(fragment)
373
+
374
+ raise InvalidQuery, "dimension #{name} resolved to an unusable column expression"
375
+ end
376
+
377
+ # Time buckets come back as Date on PostgreSQL and String elsewhere —
378
+ # normalize to ISO strings so keys merge and zero-fill consistently.
379
+ # Time keys are normalized to the string form #expected_buckets emits,
380
+ # because each database returns a different Ruby type for a bucket
381
+ # (Postgres a Time for date_trunc, SQLite a String) and the zero-fill
382
+ # has to match the aggregated rows exactly or every bucket duplicates.
383
+ def dimension_key(dim, raw)
384
+ return raw unless dim[:dimension].time?
385
+ return raw.to_s unless dim[:grain] == :hour
386
+
387
+ # MySQL and SQLite format the bucket themselves; only PostgreSQL's
388
+ # date_trunc hands back a Time.
389
+ raw.respond_to?(:strftime) ? raw.strftime(HOUR_BUCKET_FORMAT) : raw.to_s
390
+ end
391
+
392
+ # Store-timezone day/month buckets, per database. PostgreSQL converts
393
+ # properly (DST-aware); SQLite/MySQL shift by the timezone's current
394
+ # UTC offset — a documented approximation near DST boundaries.
395
+ # A grain this method does not know would return nil and silently
396
+ # group every row under one NULL bucket, so an unhandled grain raises
397
+ # here rather than producing a plausible-looking wrong series. Adding a
398
+ # grain means teaching this method and #expected_buckets together.
399
+ def time_bucket_sql(column, grain)
400
+ sql =
401
+ case connection.adapter_name
402
+ when /postgres/i
403
+ local = "#{column} AT TIME ZONE 'UTC' AT TIME ZONE '#{query.time_zone.tzinfo.identifier}'"
404
+ case grain
405
+ when :hour then "date_trunc('hour', #{local})"
406
+ when :day then "(#{local})::date"
407
+ when :week then "date_trunc('week', #{local})::date"
408
+ when :month then "date_trunc('month', #{local})::date"
409
+ end
410
+ when /mysql/i
411
+ local = "CONVERT_TZ(#{column}, '+00:00', '#{format_offset(utc_offset)}')"
412
+ case grain
413
+ when :hour then "DATE_FORMAT(#{local}, '#{HOUR_BUCKET_FORMAT}')"
414
+ when :day then "DATE(#{local})"
415
+ when :week then "DATE(DATE_SUB(#{local}, INTERVAL WEEKDAY(#{local}) DAY))"
416
+ when :month then "DATE_FORMAT(#{local}, '%Y-%m-01')"
417
+ end
418
+ else # SQLite — weeks start on Monday (ISO), matching the other adapters
419
+ case grain
420
+ when :hour then "strftime('#{HOUR_BUCKET_FORMAT}', #{column}, '#{utc_offset} seconds')"
421
+ when :day then "DATE(#{column}, '#{utc_offset} seconds')"
422
+ when :week then "DATE(#{column}, '#{utc_offset} seconds', '+1 day', 'weekday 1', '-7 days')"
423
+ when :month then "strftime('%Y-%m-01', #{column}, '#{utc_offset} seconds')"
424
+ end
425
+ end
426
+
427
+ sql || raise(ArgumentError, "reporting adapter has no SQL for the #{grain} grain")
428
+ end
429
+
430
+
431
+ def utc_offset
432
+ @utc_offset ||= query.time_zone.now.utc_offset
433
+ end
434
+
435
+ def format_offset(seconds)
436
+ sign = seconds.negative? ? '-' : '+'
437
+ format("#{sign}%02d:%02d", seconds.abs / 3600, (seconds.abs % 3600) / 60)
438
+ end
439
+
440
+ # ---- result assembly ----
441
+
442
+ # A per-group metric has no dimensionless figure: MIN() of every
443
+ # customer's lifetime value is one arbitrary customer's, not the
444
+ # store's, so it is reported as nil rather than rendered as a tile.
445
+ def build_totals(current, previous)
446
+ query.metrics.to_h do |metric|
447
+ next [metric.name, { value: nil }] if metric.per_group?
448
+
449
+ value = current[:totals].fetch([], {})[metric.name] || zero_for(metric)
450
+ prev = previous && (previous[:totals].fetch([], {})[metric.name] || zero_for(metric))
451
+ [metric.name, metric_payload(value, prev)]
452
+ end
453
+ end
454
+
455
+ def build_rows(current, previous)
456
+ return [] if query.dimensions.empty?
457
+
458
+ keys = row_keys(current)
459
+ prev_key_map = previous ? previous_key_map(keys) : {}
460
+ metrics_by_name = query.metrics.index_by(&:name)
461
+
462
+ rows = keys.map do |key|
463
+ metrics = metrics_by_name.transform_values do |metric|
464
+ value = current[:groups].fetch(key, {})[metric.name] || zero_for(metric)
465
+ prev = previous && (previous[:groups].fetch(prev_key_map.fetch(key, key), {})[metric.name] || zero_for(metric))
466
+ metric_payload(value, prev)
467
+ end
468
+
469
+ dimensions = query.dimensions.each_with_index.to_h do |d, index|
470
+ [d[:dimension].name, key[index]]
471
+ end
472
+
473
+ { dimensions: dimensions, metrics: metrics }
474
+ end
475
+
476
+ sort_rows(filter_merged_rows(rows))
477
+ end
478
+
479
+ # Grouped keys observed in the data, plus zero-filled time buckets
480
+ # covering the whole range (time-dimension queries chart every bucket).
481
+ def row_keys(current)
482
+ keys = current[:groups].keys
483
+ time_dim = query.time_dimension
484
+
485
+ if time_dim && query.dimensions.size == 1
486
+ return expected_buckets(query.time_range, time_dim[:grain]).map { |bucket| [bucket] }
487
+ end
488
+
489
+ empty_dim = query.dimensions.find { |d| d[:include_empty] }
490
+ return keys unless empty_dim
491
+
492
+ # The dimension's whole population leads, so members with no rows in
493
+ # the period still appear (and read zero through build_rows' own
494
+ # `|| zero_for`). This is what answers "which products never sold" —
495
+ # a HAVING alone cannot, because a product that never sold has no
496
+ # row to filter.
497
+ (empty_member_keys(empty_dim, keys) | keys)
498
+ end
499
+
500
+ # Members merged in by `include_empty` never went through the grouped
501
+ # SQL, so HAVING never saw them and they arrive zero-filled. Without
502
+ # re-applying the filters here, "which products never sold" would list
503
+ # every product: the bestsellers HAVING removed come back as zeros.
504
+ #
505
+ # Only needed for the include_empty path — everything else was already
506
+ # filtered in SQL, and re-testing it would be a second implementation
507
+ # of the same predicate.
508
+ def filter_merged_rows(rows)
509
+ return rows if query.metric_filters.empty? || !query.include_empty?
510
+
511
+ rows.select do |row|
512
+ query.metric_filters.all? do |filter|
513
+ value = row[:metrics].dig(filter[:metric].name, :value)
514
+ next true if value.nil?
515
+
516
+ value.to_d.public_send(COMPARISONS.fetch(filter[:op]).last, filter[:value])
517
+ end
518
+ end
519
+ end
520
+
521
+ # Applies any filter on the dimension itself to its own population. A
522
+ # filter on a *different* dimension cannot narrow this list — it
523
+ # describes the facts, not the members — so a query filtered by
524
+ # something else refuses rather than silently listing members the
525
+ # filter never applied to.
526
+ def narrow_population(relation, dimension)
527
+ query.filters.each do |filter|
528
+ next unless filter[:dimension].name == dimension.name
529
+
530
+ relation = relation.where(id: filter[:values])
531
+ end
532
+ relation
533
+ end
534
+
535
+ # Ids of every record the dimension can group by, store-scoped through
536
+ # its own lookup relation.
537
+ # Bounded by the query's own limit: these rows all read zero, so beyond
538
+ # a page of them there is nothing more to say, and an unbounded pluck
539
+ # would load a whole catalogue to render fifty rows.
540
+ def empty_member_keys(dim, observed)
541
+ relation = dim[:dimension].population&.call(query.store)
542
+ return [] if relation.nil?
543
+
544
+ # The aggregating scope is filtered, so the population must be too:
545
+ # asking "which of these shoes never sold" must not answer with every
546
+ # unsold product in the catalogue.
547
+ relation = narrow_population(relation, dim[:dimension])
548
+ # Ordered before limiting: without it two identical "which products
549
+ # never sold" requests can come back with different products.
550
+ relation = relation.reorder(:id)
551
+ # Only as many rows as the page can show, plus room to drop the ones
552
+ # that already have facts. Excluding `observed` through a NOT IN list
553
+ # instead would send one bind per observed key — tens of thousands on
554
+ # a large catalogue, past what PostgreSQL accepts at all — so the
555
+ # rows are fetched and filtered here.
556
+ wanted = query.limit
557
+ return fetch_member_keys(relation, observed) if wanted.nil?
558
+
559
+ keys = []
560
+ relation.in_batches(of: [wanted * 4, MEMBER_BATCH_SIZE].min) do |batch|
561
+ keys.concat(fetch_member_keys(batch, observed))
562
+ break if keys.size >= wanted
563
+ end
564
+ keys.first(wanted)
565
+ end
566
+
567
+ def fetch_member_keys(relation, observed)
568
+ seen = observed.to_set { |key| key.first }
569
+ relation.pluck(:id).reject { |id| seen.include?(id) }.map { |id| [id] }
570
+ end
571
+
572
+ # Every bucket in the range becomes a row, so an absurd range is an
573
+ # absurd allocation: `last_500000_days`, or a `since` in the year one,
574
+ # would build millions of rows here and take the process down long
575
+ # before any of them reached a chart. Refused rather than trimmed —
576
+ # silently charting a different range than was asked for is worse.
577
+ def expected_buckets(range, grain)
578
+ from = range.first.in_time_zone(query.time_zone).to_date
579
+ to = range.last.in_time_zone(query.time_zone).to_date
580
+ span = (to - from).to_i + 1
581
+ if span > MAX_RANGE_DAYS
582
+ raise InvalidQuery,
583
+ "time_range covers #{span} days, more than the #{MAX_RANGE_DAYS} a single report may chart"
584
+ end
585
+
586
+ case grain
587
+ when :hour then hour_buckets(range)
588
+ when :day then (from..to).map(&:to_s)
589
+ when :week then step_buckets(from.beginning_of_week(:monday), to) { |d| d + 7 }
590
+ when :month then step_buckets(from.beginning_of_month, to, &:next_month)
591
+ end
592
+ end
593
+
594
+ # Hour buckets are datetimes rather than dates, so they zero-fill from
595
+ # the range's own edges. Query::MAX_BUCKETS has already refused a range
596
+ # too wide to chart at this grain.
597
+ # Stepping by an hour crosses the repeated local hour on a DST
598
+ # fall-back, where two distinct instants render as the same wall-clock
599
+ # string — so the labels are de-duplicated. Keeping both would render
600
+ # that hour twice and collide the comparison-period keys.
601
+ def hour_buckets(range)
602
+ cursor = range.first.in_time_zone(query.time_zone).change(min: 0, sec: 0)
603
+ last = range.last.in_time_zone(query.time_zone)
604
+ buckets = []
605
+ while cursor <= last
606
+ buckets << cursor.strftime(HOUR_BUCKET_FORMAT)
607
+ cursor += 1.hour
608
+ end
609
+ buckets.uniq
610
+ end
611
+
612
+ def step_buckets(cursor, to)
613
+ buckets = []
614
+ while cursor <= to
615
+ buckets << cursor.to_s
616
+ cursor = yield(cursor)
617
+ end
618
+ buckets
619
+ end
620
+
621
+ # Aligns each current bucket with its previous-period counterpart by
622
+ # POSITION: the nth bucket of this period pairs with the nth bucket of
623
+ # the previous one. Date arithmetic on the bucket itself cannot do this
624
+ # — a range starting mid-week or mid-month has a partial first bucket
625
+ # whose start lies before the range, so shifting it lands outside the
626
+ # comparison window and orphans real data. Only the time component of
627
+ # a key moves; value-dimension components align 1:1.
628
+ def previous_key_map(keys)
629
+ time_dim = query.time_dimension
630
+ return {} unless time_dim
631
+
632
+ index = query.dimensions.index(time_dim)
633
+ pairs = bucket_pairs(time_dim[:grain])
634
+
635
+ keys.filter_map do |key|
636
+ counterpart = pairs[key[index].to_s]
637
+ next unless counterpart
638
+
639
+ shifted = key.dup
640
+ shifted[index] = counterpart
641
+ [key, shifted]
642
+ end.to_h
643
+ end
644
+
645
+ # Both periods span the same number of days, so their bucket counts
646
+ # differ by at most one partial edge. Pairing runs from the start —
647
+ # the nth bucket of this period with the nth of the previous — because
648
+ # the ranges share a start offset: a range beginning mid-month pairs
649
+ # its partial first bucket with the previous period's partial first
650
+ # bucket. A bucket past the end of the previous list has no
651
+ # counterpart and pairs with nothing rather than wrapping around.
652
+ def bucket_pairs(grain)
653
+ current = expected_buckets(query.time_range, grain)
654
+ previous = expected_buckets(query.previous_time_range, grain)
655
+
656
+ current.each_with_index.to_h { |bucket, position| [bucket, previous[position]] }
657
+ end
658
+
659
+ # SQL already ordered/limited the pushdown case; this re-sort is a
660
+ # no-op there and covers the multi-base fallback.
661
+ def sort_rows(rows)
662
+ if query.sort
663
+ metric = query.sort[:metric]
664
+ rows = rows.sort_by { |row| row[:metrics][metric][:value] }
665
+ rows.reverse! if query.sort[:direction] == :desc
666
+ end
667
+ rows = rows.first(query.limit) if query.limit
668
+ rows
669
+ end
670
+
671
+ def connection
672
+ Spree::Order.connection
673
+ end
674
+ end
675
+ end
676
+ end
677
+ end