workarea-core 3.4.45 → 3.5.0.beta.1

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 (427) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +52 -0
  3. data/app/assets/javascripts/workarea/core/modules/duplicate_id.js +1 -5
  4. data/app/assets/javascripts/workarea/core/modules/style_guide_autocomplete_fields.js +9 -42
  5. data/app/assets/javascripts/workarea/core/modules/url.js +14 -0
  6. data/app/controllers/workarea/application_controller.rb +5 -1
  7. data/app/controllers/workarea/authentication.rb +15 -26
  8. data/app/controllers/workarea/authorization.rb +3 -3
  9. data/app/controllers/workarea/current_referrer.rb +1 -2
  10. data/app/controllers/workarea/current_release.rb +1 -1
  11. data/app/controllers/workarea/current_segments.rb +52 -0
  12. data/app/controllers/workarea/current_tracking.rb +45 -0
  13. data/app/controllers/workarea/http_caching.rb +30 -0
  14. data/app/controllers/workarea/impersonation.rb +18 -44
  15. data/app/helpers/workarea/schema_org_helper.rb +21 -0
  16. data/app/mailers/workarea/application_mailer.rb +2 -4
  17. data/app/middleware/workarea/application_middleware.rb +44 -0
  18. data/app/middleware/workarea/query_cache_middleware.rb +55 -0
  19. data/app/middleware/workarea/release_server_middleware.rb +1 -1
  20. data/app/models/workarea/application_document.rb +1 -1
  21. data/app/models/workarea/bulk_action/product_edit.rb +6 -6
  22. data/app/models/workarea/calculate_percent_change.rb +1 -1
  23. data/app/models/workarea/catalog/category.rb +0 -2
  24. data/app/models/workarea/catalog/product.rb +19 -2
  25. data/app/models/workarea/checkout/collect_payment.rb +10 -1
  26. data/app/models/workarea/checkout/fraud/analyzer.rb +44 -0
  27. data/app/models/workarea/checkout/fraud/no_decision_analyzer.rb +14 -0
  28. data/app/models/workarea/checkout/fraud/test_analyzer.rb +26 -0
  29. data/app/models/workarea/checkout.rb +18 -6
  30. data/app/models/workarea/configuration/admin.rb +25 -0
  31. data/app/models/workarea/configuration/params.rb +45 -0
  32. data/app/models/workarea/content/asset.rb +5 -18
  33. data/app/models/workarea/content/block.rb +8 -2
  34. data/app/models/workarea/content/block_draft.rb +7 -2
  35. data/app/models/workarea/content/block_type.rb +2 -2
  36. data/app/models/workarea/content/block_type_definition.rb +4 -0
  37. data/app/models/workarea/content/preset.rb +1 -1
  38. data/app/models/workarea/content.rb +12 -82
  39. data/app/models/workarea/data_file/csv.rb +6 -8
  40. data/app/models/workarea/data_file/import.rb +9 -1
  41. data/app/models/workarea/data_file/json.rb +1 -20
  42. data/app/models/workarea/data_file/operation.rb +0 -4
  43. data/app/models/workarea/data_file/tax_rates.rb +3 -0
  44. data/app/models/workarea/featured_products.rb +8 -0
  45. data/app/models/workarea/fulfillment/policies/base.rb +21 -0
  46. data/app/models/workarea/fulfillment/policies/download.rb +21 -0
  47. data/app/models/workarea/fulfillment/policies/ignore.rb +11 -0
  48. data/app/models/workarea/fulfillment/policies/ship.rb +15 -0
  49. data/app/models/workarea/fulfillment/sku.rb +72 -0
  50. data/app/models/workarea/fulfillment/token.rb +36 -0
  51. data/app/models/workarea/inquiry.rb +1 -2
  52. data/app/models/workarea/insights/customer_acquisition.rb +5 -9
  53. data/app/models/workarea/insights/trending_products.rb +2 -2
  54. data/app/models/workarea/insights/trending_searches.rb +2 -2
  55. data/app/models/workarea/inventory/collection.rb +30 -0
  56. data/app/models/workarea/inventory/collection_status.rb +40 -0
  57. data/app/models/workarea/metrics/affinity.rb +33 -0
  58. data/app/models/workarea/metrics/country_by_day.rb +4 -0
  59. data/app/models/workarea/metrics/product_by_day.rb +3 -0
  60. data/app/models/workarea/metrics/product_by_week.rb +2 -0
  61. data/app/models/workarea/metrics/product_for_last_week.rb +8 -12
  62. data/app/models/workarea/metrics/sales_by_day.rb +5 -0
  63. data/app/models/workarea/metrics/search_by_day.rb +1 -3
  64. data/app/models/workarea/metrics/search_for_last_week.rb +5 -11
  65. data/app/models/workarea/metrics/segment_by_day.rb +31 -0
  66. data/app/models/workarea/metrics/sku_by_day.rb +3 -0
  67. data/app/models/workarea/metrics/tender_by_day.rb +11 -0
  68. data/app/models/workarea/metrics/update_user_aggregations.rb +77 -0
  69. data/app/models/workarea/metrics/user.rb +73 -87
  70. data/app/models/workarea/navigation/breadcrumbs.rb +10 -1
  71. data/app/models/workarea/order/fraud_decision.rb +18 -0
  72. data/app/models/workarea/order/item.rb +5 -0
  73. data/app/models/workarea/order/queries.rb +1 -0
  74. data/app/models/workarea/order/status.rb +10 -1
  75. data/app/models/workarea/order.rb +47 -4
  76. data/app/models/workarea/payment/credit_card.rb +12 -0
  77. data/app/models/workarea/payment.rb +7 -2
  78. data/app/models/workarea/pricing/cache_key.rb +9 -3
  79. data/app/models/workarea/pricing/calculator.rb +5 -1
  80. data/app/models/workarea/pricing/calculators/tax_calculator.rb +32 -5
  81. data/app/models/workarea/pricing/discount/application_group.rb +8 -3
  82. data/app/models/workarea/pricing/discount/free_gift.rb +2 -1
  83. data/app/models/workarea/pricing/discount/redemption.rb +1 -1
  84. data/app/models/workarea/pricing/discount.rb +2 -3
  85. data/app/models/workarea/pricing/item_tax_applier.rb +41 -0
  86. data/app/models/workarea/pricing/price.rb +1 -0
  87. data/app/models/workarea/pricing/request.rb +11 -0
  88. data/app/models/workarea/pricing/sku.rb +1 -0
  89. data/app/models/workarea/pricing/tax_application.rb +21 -0
  90. data/app/models/workarea/pricing/tax_applier.rb +12 -15
  91. data/app/models/workarea/pricing/unsupported_segmentation.rb +22 -0
  92. data/app/models/workarea/product_rule.rb +5 -0
  93. data/app/models/workarea/releasable.rb +83 -89
  94. data/app/models/workarea/release/activation.rb +45 -0
  95. data/app/models/workarea/release/changes.rb +45 -0
  96. data/app/models/workarea/release/changeset.rb +42 -52
  97. data/app/models/workarea/release/preview.rb +28 -0
  98. data/app/models/workarea/release/status.rb +2 -24
  99. data/app/models/workarea/release.rb +72 -99
  100. data/app/models/workarea/release_session.rb +11 -11
  101. data/app/models/workarea/search/admin/catalog_product.rb +1 -2
  102. data/app/models/workarea/search/admin/fulfillment_sku.rb +27 -0
  103. data/app/models/workarea/search/admin/order.rb +1 -1
  104. data/app/models/workarea/search/admin/pricing_discount.rb +1 -1
  105. data/app/models/workarea/search/admin/release.rb +1 -4
  106. data/app/models/workarea/search/admin/segment.rb +19 -0
  107. data/app/models/workarea/search/admin.rb +17 -39
  108. data/app/models/workarea/search/order_text.rb +23 -6
  109. data/app/models/workarea/search/storefront/category_query.rb +152 -0
  110. data/app/models/workarea/search/storefront/product/categories.rb +11 -45
  111. data/app/models/workarea/search/storefront.rb +33 -29
  112. data/app/models/workarea/segment/first_time_customer.rb +8 -0
  113. data/app/models/workarea/segment/first_time_visitor.rb +12 -0
  114. data/app/models/workarea/segment/life_cycle.rb +44 -0
  115. data/app/models/workarea/segment/loyal_customer.rb +12 -0
  116. data/app/models/workarea/segment/returning_customer.rb +14 -0
  117. data/app/models/workarea/segment/returning_visitor.rb +12 -0
  118. data/app/models/workarea/segment/rules/base.rb +16 -0
  119. data/app/models/workarea/segment/rules/browser_info.rb +29 -0
  120. data/app/models/workarea/segment/rules/geolocation.rb +29 -0
  121. data/app/models/workarea/segment/rules/geolocation_option.rb +53 -0
  122. data/app/models/workarea/segment/rules/last_order.rb +14 -0
  123. data/app/models/workarea/segment/rules/logged_in.rb +13 -0
  124. data/app/models/workarea/segment/rules/orders.rb +17 -0
  125. data/app/models/workarea/segment/rules/revenue.rb +17 -0
  126. data/app/models/workarea/segment/rules/sessions.rb +17 -0
  127. data/app/models/workarea/segment/rules/tags.rb +14 -0
  128. data/app/models/workarea/segment/rules/traffic_referrer.rb +18 -0
  129. data/app/models/workarea/segment.rb +54 -0
  130. data/app/models/workarea/segmentable.rb +66 -0
  131. data/app/models/workarea/sort.rb +14 -2
  132. data/app/models/workarea/tax/rate.rb +35 -1
  133. data/app/models/workarea/user/authorization.rb +19 -13
  134. data/app/models/workarea/user/login.rb +4 -0
  135. data/app/models/workarea/user/password_reset.rb +1 -3
  136. data/app/queries/workarea/categorization.rb +3 -3
  137. data/app/queries/workarea/featured_categorization.rb +53 -0
  138. data/app/queries/workarea/order_cancellation_metrics.rb +155 -0
  139. data/app/queries/workarea/order_item_details.rb +16 -3
  140. data/app/queries/workarea/order_metrics.rb +32 -3
  141. data/app/queries/workarea/product_primary_navigation.rb +13 -2
  142. data/app/queries/workarea/product_releases.rb +38 -0
  143. data/app/queries/workarea/recommendation/user_activity_based.rb +14 -6
  144. data/app/queries/workarea/reports/average_order_value.rb +1 -1
  145. data/app/queries/workarea/reports/customers.rb +14 -11
  146. data/app/queries/workarea/reports/first_time_vs_returning_sales.rb +1 -1
  147. data/app/queries/workarea/reports/group_by_time.rb +13 -17
  148. data/app/queries/workarea/reports/sales_by_category.rb +1 -1
  149. data/app/queries/workarea/reports/sales_by_country.rb +10 -3
  150. data/app/queries/workarea/reports/sales_by_discount.rb +1 -1
  151. data/app/queries/workarea/reports/sales_by_menu.rb +1 -1
  152. data/app/queries/workarea/reports/sales_by_product.rb +39 -4
  153. data/app/queries/workarea/reports/sales_by_sku.rb +39 -4
  154. data/app/queries/workarea/reports/sales_by_tender.rb +42 -0
  155. data/app/queries/workarea/reports/sales_by_traffic_referrer.rb +1 -1
  156. data/app/queries/workarea/reports/sales_over_time.rb +10 -3
  157. data/app/queries/workarea/reports/searches.rb +1 -1
  158. data/app/queries/workarea/reports/searches_over_time.rb +1 -1
  159. data/app/queries/workarea/reports/searches_without_results_over_time.rb +1 -1
  160. data/app/queries/workarea/search/admin_fulfillment_skus.rb +20 -0
  161. data/app/queries/workarea/search/admin_releases.rb +1 -4
  162. data/app/queries/workarea/search/admin_search.rb +0 -4
  163. data/app/queries/workarea/search/admin_segments.rb +16 -0
  164. data/app/queries/workarea/search/admin_sorting.rb +1 -1
  165. data/app/queries/workarea/search/pagination.rb +1 -4
  166. data/app/queries/workarea/search/product_display_rules.rb +40 -8
  167. data/app/queries/workarea/search/product_entries.rb +15 -1
  168. data/app/queries/workarea/search/product_search.rb +4 -1
  169. data/app/queries/workarea/search/storefront_search/exact_matches.rb +1 -1
  170. data/app/queries/workarea/search/storefront_search/response.rb +6 -4
  171. data/app/queries/workarea/search/storefront_search.rb +5 -4
  172. data/app/seeds/workarea/assets_seeds.rb +2 -1
  173. data/app/seeds/workarea/insights_seeds.rb +0 -23
  174. data/app/seeds/workarea/orders_seeds.rb +7 -6
  175. data/app/seeds/workarea/products_seeds.rb +2 -0
  176. data/app/seeds/workarea/segments_seeds.rb +8 -0
  177. data/app/seeds/workarea/tax_seeds.rb +3 -3
  178. data/app/services/workarea/cancel_order.rb +8 -3
  179. data/app/services/workarea/copy_order.rb +6 -0
  180. data/app/services/workarea/create_fulfillment.rb +5 -3
  181. data/app/services/workarea/direct_upload.rb +11 -19
  182. data/app/services/workarea/hash_update.rb +1 -18
  183. data/app/services/workarea/packaging.rb +1 -1
  184. data/app/services/workarea/save_publishing.rb +5 -1
  185. data/app/workers/sidekiq/callbacks.rb +4 -52
  186. data/app/workers/sidekiq/callbacks_worker.rb +4 -6
  187. data/app/workers/workarea/bulk_index_admin.rb +2 -2
  188. data/app/workers/workarea/bulk_index_products.rb +6 -5
  189. data/app/workers/workarea/bulk_index_searches.rb +5 -5
  190. data/app/workers/workarea/deactivate_stale_discounts.rb +3 -3
  191. data/app/workers/workarea/generate_insights.rb +1 -1
  192. data/app/workers/workarea/index_admin_search.rb +1 -0
  193. data/app/workers/workarea/index_categorization.rb +4 -7
  194. data/app/workers/workarea/index_category.rb +2 -1
  195. data/app/workers/workarea/index_category_changes.rb +11 -6
  196. data/app/workers/workarea/index_fulfillment_changes.rb +2 -1
  197. data/app/workers/workarea/index_help.rb +2 -1
  198. data/app/workers/workarea/index_page.rb +2 -1
  199. data/app/workers/workarea/index_payment_transactions.rb +2 -1
  200. data/app/workers/workarea/index_product.rb +4 -4
  201. data/app/workers/workarea/index_product_children.rb +4 -3
  202. data/app/workers/workarea/index_product_rule.rb +3 -2
  203. data/app/workers/workarea/index_search_customizations.rb +3 -2
  204. data/app/workers/workarea/index_skus.rb +5 -6
  205. data/app/workers/workarea/keep_product_index_fresh.rb +3 -5
  206. data/app/workers/workarea/process_import.rb +3 -3
  207. data/app/workers/workarea/process_search_recommendations.rb +5 -9
  208. data/app/workers/workarea/publish_release.rb +1 -0
  209. data/app/workers/workarea/save_metrics.rb +116 -0
  210. data/app/workers/workarea/save_order_cancellation_metrics.rb +35 -0
  211. data/app/workers/workarea/save_order_metrics.rb +10 -74
  212. data/app/workers/workarea/status_reporter.rb +2 -4
  213. data/app/workers/workarea/synchronize_user_metrics.rb +17 -0
  214. data/app/workers/workarea/update_payment_profile_email.rb +22 -0
  215. data/app/workers/workarea/verify_scheduled_releases.rb +4 -11
  216. data/config/initializers/00_configuration.rb +185 -0
  217. data/config/initializers/02_assets.rb +1 -0
  218. data/config/initializers/05_scheduled_jobs.rb +0 -7
  219. data/config/initializers/10_rack_middleware.rb +2 -5
  220. data/config/initializers/11_payment.rb +4 -0
  221. data/config/initializers/13_rack_attack.rb +2 -4
  222. data/config/initializers/14_content_block_types.rb +16 -16
  223. data/config/initializers/15_endpoint_monitoring.rb +3 -6
  224. data/config/initializers/{21_premailer.rb → 24_premailer.rb} +1 -0
  225. data/config/initializers/{20_i18n_js.rb → 25_i18n_js.rb} +0 -0
  226. data/config/locales/en.yml +13 -24
  227. data/lib/generators/workarea/content_block_type/templates/initializer.rb +1 -1
  228. data/lib/generators/workarea/install/USAGE +0 -1
  229. data/lib/generators/workarea/install/install_generator.rb +0 -21
  230. data/lib/generators/workarea/install/templates/initializer.rb.erb +1 -1
  231. data/lib/tasks/insights.rake +1 -0
  232. data/lib/tasks/migrate.rake +106 -0
  233. data/lib/tasks/search.rake +17 -19
  234. data/lib/tasks/tests.rake +1 -1
  235. data/lib/workarea/cache.rb +6 -35
  236. data/lib/workarea/changelog.rake +2 -2
  237. data/lib/workarea/configuration/administrable/definition.rb +42 -0
  238. data/lib/workarea/configuration/administrable/field.rb +79 -0
  239. data/lib/workarea/configuration/administrable/fieldset.rb +44 -0
  240. data/lib/workarea/configuration/administrable_options.rb +24 -0
  241. data/lib/workarea/configuration/cache_store.rb +3 -1
  242. data/lib/workarea/configuration/content_blocks.rb +17 -0
  243. data/lib/workarea/configuration/mongoid.rb +1 -4
  244. data/lib/workarea/configuration/session.rb +36 -0
  245. data/lib/workarea/configuration/sidekiq.rb +3 -1
  246. data/lib/workarea/configuration.rb +121 -148
  247. data/lib/workarea/core/engine.rb +3 -58
  248. data/lib/workarea/core.rb +15 -11
  249. data/lib/workarea/elasticsearch/document.rb +8 -15
  250. data/lib/workarea/ext/freedom_patches/action_view_cache_helper.rb +4 -6
  251. data/lib/workarea/ext/freedom_patches/active_support_duration.rb +14 -0
  252. data/lib/workarea/ext/freedom_patches/money.rb +11 -0
  253. data/lib/workarea/ext/freedom_patches/premailer.rb +6 -0
  254. data/lib/workarea/ext/freedom_patches/string.rb +19 -4
  255. data/lib/workarea/ext/mongoid/find_ordered.rb +2 -2
  256. data/lib/workarea/ext/mongoid/lookup_hash.rb +30 -0
  257. data/lib/workarea/geolocation.rb +24 -6
  258. data/lib/workarea/routes_constraints/super_admin.rb +1 -1
  259. data/lib/workarea/version.rb +3 -4
  260. data/lib/workarea/visit.rb +92 -0
  261. data/lib/workarea/warnings.rb +95 -0
  262. data/lib/workarea.rb +102 -3
  263. data/test/dummy/config/initializers/session_store.rb +1 -1
  264. data/test/fixtures/tax_rates.csv +3 -3
  265. data/test/generators/workarea/content_block_type_generator_test.rb +1 -1
  266. data/test/generators/workarea/install_generator_test.rb +0 -13
  267. data/test/integration/workarea/authentication_test.rb +27 -4
  268. data/test/integration/workarea/authorization_test.rb +5 -0
  269. data/test/integration/workarea/cache_varies_integration_test.rb +21 -23
  270. data/test/integration/workarea/monitoring_integration_test.rb +5 -10
  271. data/test/javascripts/duplicate_id_spec.js +1 -7
  272. data/test/javascripts/fixtures/{duplicate_id_fail.html.haml → duplicate_id.html.haml} +0 -1
  273. data/test/javascripts/form_submitting_controls_spec.js +0 -1
  274. data/test/lib/workarea/asset_endpoints/favicons_test.rb +11 -13
  275. data/test/lib/workarea/configuration/administrable/definition_test.rb +71 -0
  276. data/test/lib/workarea/configuration/administrable/field_test.rb +128 -0
  277. data/test/lib/workarea/configuration/administrable/fieldset_test.rb +78 -0
  278. data/test/lib/workarea/configuration/configuration_test.rb +20 -0
  279. data/test/lib/workarea/elasticsearch/document_test.rb +0 -20
  280. data/test/lib/workarea/elasticsearch/query_cache_test.rb +7 -7
  281. data/test/lib/workarea/ext/freedom_patches/money_test.rb +11 -0
  282. data/test/lib/workarea/ext/mongoid/lookup_hash_test.rb +86 -0
  283. data/test/lib/workarea/geolocation_test.rb +20 -0
  284. data/test/mailers/workarea/application_mailer_test.rb +1 -11
  285. data/test/middleware/workarea/query_cache_middleware_test.rb +55 -0
  286. data/test/models/workarea/checkout/collect_payment_test.rb +14 -6
  287. data/test/models/workarea/checkout_test.rb +31 -57
  288. data/test/models/workarea/configuration/params_test.rb +37 -0
  289. data/test/models/workarea/data_file/csv_test.rb +8 -73
  290. data/test/models/workarea/data_file/import_test.rb +31 -0
  291. data/test/models/workarea/data_file/json_test.rb +6 -48
  292. data/test/models/workarea/data_file/tax_test.rb +3 -0
  293. data/test/models/workarea/fulfillment/policies/download_test.rb +39 -0
  294. data/test/models/workarea/fulfillment/sku_test.rb +54 -0
  295. data/test/models/workarea/insights/best_customers_test.rb +5 -2
  296. data/test/models/workarea/insights/best_full_price_customers_test.rb +5 -2
  297. data/test/models/workarea/insights/customer_acquisition_test.rb +1 -1
  298. data/test/models/workarea/insights/customers_at_risk_test.rb +1 -1
  299. data/test/models/workarea/insights/low_aov_customers_test.rb +1 -1
  300. data/test/models/workarea/inventory/collection_test.rb +75 -0
  301. data/test/models/workarea/metrics/affinity_test.rb +85 -0
  302. data/test/models/workarea/metrics/product_by_week_test.rb +27 -31
  303. data/test/models/workarea/metrics/search_by_day_test.rb +3 -0
  304. data/test/models/workarea/metrics/search_by_week_test.rb +27 -31
  305. data/test/models/workarea/metrics/update_user_aggregations_test.rb +62 -0
  306. data/test/models/workarea/metrics/user_test.rb +146 -63
  307. data/test/models/workarea/navigation/breadcrumbs_test.rb +12 -2
  308. data/test/models/workarea/order/queries_test.rb +2 -0
  309. data/test/models/workarea/order_test.rb +44 -0
  310. data/test/models/workarea/payment/address_test.rb +109 -111
  311. data/test/models/workarea/payment/credit_card_integration_test.rb +0 -52
  312. data/test/models/workarea/payment/credit_card_test.rb +5 -0
  313. data/test/models/workarea/pricing/cache_key_test.rb +7 -1
  314. data/test/models/workarea/pricing/calculators/tax_calculator_test.rb +41 -1
  315. data/test/models/workarea/pricing/discount/collection_test.rb +27 -29
  316. data/test/models/workarea/pricing/discount_test.rb +22 -24
  317. data/test/models/workarea/pricing/item_tax_applier_test.rb +83 -0
  318. data/test/models/workarea/pricing/price_test.rb +9 -0
  319. data/test/models/workarea/pricing/request_test.rb +13 -0
  320. data/test/models/workarea/pricing/sku_test.rb +9 -0
  321. data/test/models/workarea/releasable_active_test.rb +113 -0
  322. data/test/models/workarea/releasable_test.rb +69 -120
  323. data/test/models/workarea/release/changeset_test.rb +41 -0
  324. data/test/models/workarea/release_jobs_test.rb +52 -0
  325. data/test/models/workarea/release_previewing_test.rb +43 -0
  326. data/test/models/workarea/release_test.rb +59 -153
  327. data/test/models/workarea/search/admin/content_asset_test.rb +26 -28
  328. data/test/models/workarea/search/order_text_test.rb +35 -0
  329. data/test/models/workarea/search/storefront/category_query_test.rb +81 -0
  330. data/test/models/workarea/search/storefront/product/categories_test.rb +10 -70
  331. data/test/models/workarea/search/storefront/product_test.rb +10 -11
  332. data/test/models/workarea/search/storefront_test.rb +1 -13
  333. data/test/models/workarea/segment/life_cycle_test.rb +24 -0
  334. data/test/models/workarea/segment/rules/browser_info_test.rb +33 -0
  335. data/test/models/workarea/segment/rules/geolocation_option_test.rb +16 -0
  336. data/test/models/workarea/segment/rules/geolocation_test.rb +49 -0
  337. data/test/models/workarea/segment/rules/last_order_test.rb +32 -0
  338. data/test/models/workarea/segment/rules/logged_in_test.rb +25 -0
  339. data/test/models/workarea/segment/rules/orders_test.rb +59 -0
  340. data/test/models/workarea/segment/rules/revenue_test.rb +59 -0
  341. data/test/models/workarea/segment/rules/sessions_test.rb +26 -0
  342. data/test/models/workarea/segment/rules/tags_test.rb +27 -0
  343. data/test/models/workarea/segment/rules/traffic_referrer_test.rb +31 -0
  344. data/test/models/workarea/segment_test.rb +80 -0
  345. data/test/models/workarea/segmentable_test.rb +74 -0
  346. data/test/models/workarea/shipping/address_test.rb +109 -111
  347. data/test/models/workarea/shipping/sku_test.rb +8 -12
  348. data/test/models/workarea/user/password_reset_test.rb +4 -12
  349. data/test/models/workarea/user_test.rb +13 -10
  350. data/test/queries/workarea/admin_search_query_wrapper_test.rb +21 -25
  351. data/test/queries/workarea/categorization_test.rb +49 -0
  352. data/test/queries/workarea/featured_categorization_test.rb +66 -0
  353. data/test/queries/workarea/order_cancellation_metrics_test.rb +203 -0
  354. data/test/queries/workarea/order_item_details_test.rb +17 -0
  355. data/test/queries/workarea/order_metrics_test.rb +12 -0
  356. data/test/queries/workarea/product_releases_test.rb +56 -0
  357. data/test/queries/workarea/recommendation/searches_test.rb +3 -3
  358. data/test/queries/workarea/recommendation/user_activity_based_test.rb +4 -4
  359. data/test/queries/workarea/reports/customers_test.rb +12 -8
  360. data/test/queries/workarea/reports/report_test.rb +4 -6
  361. data/test/queries/workarea/reports/sales_by_country_test.rb +18 -3
  362. data/test/queries/workarea/reports/sales_by_product_test.rb +28 -8
  363. data/test/queries/workarea/reports/sales_by_sku_test.rb +28 -8
  364. data/test/queries/workarea/reports/sales_by_tender_test.rb +100 -0
  365. data/test/queries/workarea/reports/sales_by_traffic_referrer_test.rb +6 -6
  366. data/test/queries/workarea/search/admin_releases_test.rb +1 -26
  367. data/test/queries/workarea/search/admin_search_test.rb +0 -10
  368. data/test/queries/workarea/search/admin_users_test.rb +2 -2
  369. data/test/queries/workarea/search/category_browse_test.rb +53 -0
  370. data/test/queries/workarea/search/facet_sorting/size_test.rb +11 -13
  371. data/test/queries/workarea/search/pagination_test.rb +9 -26
  372. data/test/queries/workarea/search/product_entries_test.rb +27 -0
  373. data/test/queries/workarea/search/product_search_test.rb +0 -16
  374. data/test/queries/workarea/search/storefront_search/response_test.rb +26 -0
  375. data/test/queries/workarea/search/storefront_search_test.rb +34 -36
  376. data/test/services/workarea/cancel_order_test.rb +27 -13
  377. data/test/services/workarea/copy_order_test.rb +17 -0
  378. data/test/services/workarea/direct_upload_test.rb +1 -46
  379. data/test/services/workarea/export_report_test.rb +41 -45
  380. data/test/services/workarea/hash_update_test.rb +12 -12
  381. data/test/services/workarea/packaging_test.rb +9 -11
  382. data/test/services/workarea/save_publishing_test.rb +23 -0
  383. data/test/workers/sidekiq/callbacks_test.rb +0 -22
  384. data/test/workers/workarea/deactivate_stale_discounts_test.rb +2 -2
  385. data/test/workers/workarea/index_category_changes_test.rb +5 -7
  386. data/test/workers/workarea/keep_product_index_fresh_test.rb +1 -1
  387. data/test/workers/workarea/process_import_test.rb +0 -6
  388. data/test/workers/workarea/process_search_recommendations_test.rb +4 -5
  389. data/test/workers/workarea/status_reporter_test.rb +2 -1
  390. data/test/workers/workarea/synchronize_user_metrics_test.rb +57 -0
  391. data/test/workers/workarea/update_payment_profile_email_test.rb +27 -0
  392. data/test/workers/workarea/verify_scheduled_releases_test.rb +0 -26
  393. data/workarea-core.gemspec +13 -11
  394. metadata +196 -92
  395. data/app/assets/javascripts/workarea/core/templates/ui_menu_item.jst.ejs +0 -10
  396. data/app/controllers/workarea/admin_guest_browsing.rb +0 -54
  397. data/app/middleware/workarea/rack_cache_config_middleware.rb +0 -19
  398. data/app/queries/workarea/search/search_suggestions.rb +0 -43
  399. data/app/workers/workarea/bust_discount_cache.rb +0 -16
  400. data/app/workers/workarea/clean_user_activity.rb +0 -11
  401. data/app/workers/workarea/undo_release.rb +0 -15
  402. data/app/workers/workarea/update_email.rb +0 -33
  403. data/lib/generators/workarea/docker/USAGE +0 -16
  404. data/lib/generators/workarea/docker/docker_generator.rb +0 -61
  405. data/lib/generators/workarea/docker/templates/.env.erb +0 -6
  406. data/lib/generators/workarea/docker/templates/Dockerfile.erb +0 -48
  407. data/lib/generators/workarea/docker/templates/docker-compose.yml.erb +0 -72
  408. data/lib/generators/workarea/docker/templates/docker-entrypoint.sh.erb +0 -6
  409. data/lib/generators/workarea/docker/templates/docker-sync.yml.erb +0 -8
  410. data/lib/generators/workarea/docker/templates/docker-wait.sh +0 -11
  411. data/lib/generators/workarea/docker/templates/docker.env +0 -16
  412. data/lib/generators/workarea/docker/templates/docker_init.rb.erb +0 -29
  413. data/lib/workarea/configuration/headless_chrome.rb +0 -42
  414. data/lib/workarea/configuration/puma.rb +0 -37
  415. data/lib/workarea/ext/freedom_patches/i18n_js.rb +0 -27
  416. data/lib/workarea/ext/freedom_patches/mongoid_localized_defaults.rb +0 -25
  417. data/lib/workarea/ext/jbuilder/jbuilder_cache.rb +0 -29
  418. data/lib/workarea/ext/referer_parser/parser.decorator +0 -43
  419. data/lib/workarea/queues_pauser.rb +0 -26
  420. data/lib/workarea/robots.rb +0 -9
  421. data/test/generators/workarea/docker_generator_test.rb +0 -156
  422. data/test/javascripts/fixtures/duplicate_id_pass.html.haml +0 -2
  423. data/test/lib/workarea/ext/freedom_patches/mongoid_localized_defaults_test.rb +0 -25
  424. data/test/lib/workarea/ext/referer_parser/parser_test.rb +0 -20
  425. data/test/queries/workarea/search/search_suggestions_test.rb +0 -29
  426. data/test/workers/workarea/undo_release_test.rb +0 -23
  427. data/test/workers/workarea/update_email_test.rb +0 -39
@@ -5,49 +5,27 @@ module Workarea
5
5
 
6
6
  def self.jump_to(params, size = Workarea.config.default_admin_jump_to_result_count)
7
7
  query = {
8
- aggs: {
9
- grouped_by_type: {
10
- filter: {
11
- match_phrase_prefix: {
12
- jump_to_search_text: {
13
- query: params[:q],
14
- max_expansions: 10
15
- }
16
- }
17
- },
18
- aggs: {
19
- type: {
20
- terms: { field: 'facets.type', size: Workarea.config.jump_to_type_limit },
21
- aggs: {
22
- top: {
23
- top_hits: {
24
- size: Workarea.config.jump_to_results_per_type,
25
- sort: [
26
- { _score: { order: 'desc' } },
27
- { updated_at: { order: 'desc' } }
28
- ]
29
- }
30
- }
31
- }
32
- }
8
+ query: {
9
+ match_phrase_prefix: {
10
+ jump_to_search_text: {
11
+ query: params[:q],
12
+ max_expansions: 10
33
13
  }
34
14
  }
35
- }
15
+ },
16
+ size: size,
17
+ sort: [{ jump_to_position: :asc }]
36
18
  }
37
19
 
38
- aggregation = search(query)['aggregations']['grouped_by_type']['type']
39
- aggregation['buckets']
40
- .reduce([]) { |m, b| m + b['top']['hits']['hits'] }
41
- .sort_by { |r| [r['_source']['jump_to_position'] || 999, r['_score'] || 0] }
42
- .map do |result|
43
- {
44
- label: result['_source']['jump_to_text'],
45
- type: result['_source']['facets']['type'],
46
- model_class: result['_source']['model_class'],
47
- route_helper: result['_source']['jump_to_route_helper'],
48
- to_param: result['_source']['jump_to_param']
49
- }
50
- end
20
+ search(query)['hits']['hits'].map do |result|
21
+ {
22
+ label: result['_source']['jump_to_text'],
23
+ type: result['_source']['facets']['type'],
24
+ model_class: result['_source']['model_class'],
25
+ route_helper: result['_source']['jump_to_route_helper'],
26
+ to_param: result['_source']['jump_to_param']
27
+ }
28
+ end
51
29
  end
52
30
 
53
31
  def self.for(model)
@@ -28,13 +28,11 @@ module Workarea
28
28
  payment.try(:credit_card).try(:issuer)
29
29
  end
30
30
 
31
+ # Space-separated IDs for item-level and shipping-level discounts.
32
+ #
33
+ # @return [String]
31
34
  def discount_text
32
- @order
33
- .price_adjustments
34
- .map { |pa| pa.data['discount_id'] }
35
- .compact
36
- .uniq
37
- .join(' ')
35
+ (order_discounts + shipping_discounts).join(' ')
38
36
  end
39
37
 
40
38
  def shipping_text
@@ -70,6 +68,25 @@ module Workarea
70
68
  def addresses
71
69
  (shippings.map(&:address) + [payment.try(:address)]).reject(&:blank?)
72
70
  end
71
+
72
+ private
73
+
74
+ def order_discounts
75
+ @order
76
+ .price_adjustments
77
+ .map { |pa| pa.data['discount_id'] }
78
+ .compact
79
+ .uniq
80
+ end
81
+
82
+ def shipping_discounts
83
+ shippings
84
+ .map(&:price_adjustments)
85
+ .flatten
86
+ .map { |pa| pa.data['discount_id'] }
87
+ .compact
88
+ .uniq
89
+ end
73
90
  end
74
91
  end
75
92
  end
@@ -0,0 +1,152 @@
1
+ # For some reason, loading Search::Storefront::Product doesn't get triggered
2
+ require_dependency 'workarea/search/storefront/product'
3
+
4
+ module Workarea
5
+ module Search
6
+ class Storefront
7
+ class CategoryQuery
8
+ attr_reader :category
9
+
10
+ class << self
11
+ def find_by_product(product)
12
+ product = product.in_release(Release.current) if product.persisted?
13
+ search_model = Product.new(product, skip_categorization: true)
14
+
15
+ begin
16
+ find!(id: search_model.id)
17
+ rescue
18
+ begin
19
+ find!(document: search_model.as_document)
20
+ rescue ::Elasticsearch::Transport::Transport::ServerError
21
+ []
22
+ end
23
+ end
24
+ end
25
+
26
+ def find!(options)
27
+ results = Storefront.current_index.search(
28
+ size: Workarea.config.product_categories_by_rules_max_count,
29
+ query: {
30
+ percolate: options.merge(
31
+ field: 'query',
32
+ index: Storefront.current_index.name,
33
+ type: Storefront.type,
34
+ document_type: 'category'
35
+ )
36
+ },
37
+ post_filter: if Release.current.blank?
38
+ {
39
+ bool: {
40
+ minimum_should_match: 1,
41
+ should: [
42
+ { term: { release_id: 'live' } },
43
+ { bool: { must_not: { exists: { field: 'release_id' } } } } # for upgrade compatiblity
44
+ ]
45
+ }
46
+ }
47
+ else
48
+ {
49
+ bool: {
50
+ minimum_should_match: 1,
51
+ should: [
52
+ { term: { release_id: Release.current.id } },
53
+ {
54
+ bool: {
55
+ must_not: [{ term: { changeset_release_ids: Release.current.id } }],
56
+ must: [
57
+ {
58
+ bool: {
59
+ minimum_should_match: 1,
60
+ should: [
61
+ { term: { release_id: 'live' } },
62
+ { bool: { must_not: { exists: { field: 'release_id' } } } } # for upgrade compatiblity
63
+ ]
64
+ }
65
+ }
66
+ ]
67
+ }
68
+ }
69
+ ]
70
+ }
71
+ }
72
+ end
73
+ )
74
+
75
+ results['hits']['hits'].map { |h| h['_id'].split('-').first }
76
+ end
77
+ end
78
+
79
+ def initialize(category)
80
+ @category = category
81
+ end
82
+
83
+ def update
84
+ delete
85
+ create unless category.product_rules.blank?
86
+ end
87
+
88
+ def create
89
+ I18n.for_each_locale do
90
+ create_live
91
+ create_releases
92
+ end
93
+ end
94
+
95
+ def delete
96
+ I18n.for_each_locale do
97
+ begin
98
+ Storefront.current_index.delete(category.id, type: 'category')
99
+ rescue ::Elasticsearch::Transport::Transport::Errors::NotFound
100
+ # doesn't matter we want it deleted
101
+ end
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ def create_live
108
+ Release.without_current do
109
+ category.reload
110
+
111
+ if category.product_rules.present?
112
+ document = {
113
+ id: category.id,
114
+ release_id: 'live',
115
+ changeset_release_ids: changesets.map(&:release_id).uniq,
116
+ query: Categorization.new(rules: category.product_rules).query
117
+ }
118
+
119
+ Storefront.current_index.save(document, type: 'category')
120
+ end
121
+ end
122
+ end
123
+
124
+ def create_releases
125
+ changesets.each do |changeset|
126
+ changeset.release.as_current do
127
+ category.reload
128
+
129
+ if category.product_rules.present?
130
+ document = {
131
+ id: "#{category.id}-#{changeset.release_id}",
132
+ release_id: changeset.release_id,
133
+ query: Categorization.new(rules: category.product_rules).query
134
+ }
135
+
136
+ Storefront.current_index.save(document, type: 'category')
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ def changesets
143
+ @changesets ||= Release::Changeset
144
+ .where(releasable_type: ProductRule.name)
145
+ .any_in(releasable_id: category.product_rules.map(&:id))
146
+ .includes(:release)
147
+ .to_a
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -5,59 +5,25 @@ module Workarea
5
5
  module Categories
6
6
  extend ActiveSupport::Concern
7
7
 
8
+ # TODO remove these in v3.6
8
9
  class_methods do
9
- def add_category(category)
10
- I18n.for_each_locale do
11
- category.reload
12
-
13
- if category.product_rules.present?
14
- document = {
15
- id: category.id,
16
- query: Workarea::Search::Categorization.new(rules: category.product_rules).query
17
- }
10
+ Workarea.deprecation.deprecate_methods(
11
+ self,
12
+ add_category: 'Use `Search::Storefront::CategoryQuery.new(category).create` instead',
13
+ delete_category: 'Use `Search::Storefront::CategoryQuery.new(category).delete` instead',
14
+ find_categories: 'Use `Workarea::Search::Storefront::CategoryQuery.find_by_product` instead'
15
+ )
18
16
 
19
- Storefront.current_index.save(document, type: 'category')
20
- end
21
- end
17
+ def add_category(category)
18
+ CategoryQuery.new(category).create
22
19
  end
23
20
 
24
21
  def delete_category(category_id)
25
- I18n.for_each_locale do
26
- current_index.delete(category_id, type: 'category')
27
- end
28
-
29
- rescue ::Elasticsearch::Transport::Transport::Errors::NotFound
30
- # doesn't matter we want it deleted
22
+ CategoryQuery.new(category_id).delete
31
23
  end
32
24
 
33
25
  def find_categories(product)
34
- search_model = Product.new(product, skip_categorization: true)
35
-
36
- begin
37
- find_categories!(id: search_model.id)
38
- rescue
39
- begin
40
- find_categories!(document: search_model.as_document)
41
- rescue ::Elasticsearch::Transport::Transport::ServerError
42
- []
43
- end
44
- end
45
- end
46
-
47
- def find_categories!(options)
48
- results = current_index.search(
49
- size: Workarea.config.product_categories_by_rules_max_count,
50
- query: {
51
- percolate: options.merge(
52
- field: 'query',
53
- index: current_index.name,
54
- type: Storefront.type,
55
- document_type: 'category'
56
- )
57
- }
58
- )
59
-
60
- results['hits']['hits'].map { |h| h['_id'] }
26
+ CategoryQuery.find_by_product(product)
61
27
  end
62
28
  end
63
29
 
@@ -21,7 +21,8 @@ module Workarea
21
21
  end
22
22
 
23
23
  def id
24
- CGI.escape("#{type}-#{model.id}")
24
+ pieces = [type, model.id, model.try(:release_id)].reject(&:blank?)
25
+ CGI.escape(pieces.join('-'))
25
26
  end
26
27
 
27
28
  def type
@@ -32,23 +33,21 @@ module Workarea
32
33
  model.slug
33
34
  end
34
35
 
35
- # Whether the product is active for a given release state. Storing
36
- # active per-release allows accurate previewing of products in releases
37
- # on the storefront.
36
+ def release_id
37
+ model.try(:release_id).presence || 'live'
38
+ end
39
+
40
+ # Whether the product is active. Stored as `:now` way for upgrade support
41
+ # to v3.5 without requiring reindexing.
38
42
  #
39
43
  # return [Hash]
40
44
  #
41
45
  def active
42
- model.changesets.inject(now: model.active?) do |memo, changeset|
43
- active = if changeset.changeset['active'].respond_to?(:[])
44
- changeset.changeset['active'][I18n.locale]
45
- else
46
- !!changeset.changeset['active']
47
- end
48
-
49
- memo[changeset.release_id.to_s] = active
50
- memo
51
- end
46
+ { now: model.active? }
47
+ end
48
+
49
+ def active_segment_ids
50
+ model.try(:active_segment_ids)
52
51
  end
53
52
 
54
53
  def facets
@@ -80,21 +79,26 @@ module Workarea
80
79
  end
81
80
 
82
81
  def as_document
83
- {
84
- id: id,
85
- type: type,
86
- slug: slug,
87
- active: active,
88
- suggestion_content: suggestion_content,
89
- created_at: model.created_at,
90
- updated_at: model.updated_at,
91
- facets: facets,
92
- numeric: numeric,
93
- keywords: keywords,
94
- sorts: sorts,
95
- content: content,
96
- cache: cache
97
- }
82
+ Release.with_current(release_id) do
83
+ {
84
+ id: id,
85
+ type: type,
86
+ slug: slug,
87
+ active: active,
88
+ active_segment_ids: active_segment_ids,
89
+ release_id: release_id,
90
+ changeset_release_ids: Array.wrap(model.try(:changesets)).map(&:release_id),
91
+ suggestion_content: suggestion_content,
92
+ created_at: model.created_at,
93
+ updated_at: model.updated_at,
94
+ facets: facets,
95
+ numeric: numeric,
96
+ keywords: keywords,
97
+ sorts: sorts,
98
+ content: content,
99
+ cache: cache
100
+ }
101
+ end
98
102
  end
99
103
  end
100
104
  end
@@ -0,0 +1,8 @@
1
+ module Workarea
2
+ class Segment
3
+ class FirstTimeCustomer < Segment
4
+ include LifeCycle
5
+ self.default_rules = [Rules::Orders.new(minimum: 1, maximum: 1)]
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ module Workarea
2
+ class Segment
3
+ class FirstTimeVisitor < Segment
4
+ include LifeCycle
5
+
6
+ self.default_rules = [
7
+ Rules::Sessions.new(minimum: 0, maximum: 1),
8
+ Rules::Orders.new(maximum: 0)
9
+ ]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,44 @@
1
+ module Workarea
2
+ class Segment
3
+ module LifeCycle
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ field :_id, type: String, default: -> { self.class.instance_id }
8
+ cattr_accessor :default_rules
9
+ end
10
+
11
+ class_methods do
12
+ def model_name
13
+ Segment.model_name
14
+ end
15
+
16
+ def instance
17
+ first || create!(rules: default_rules)
18
+ end
19
+
20
+ def instance_id
21
+ name.demodulize.underscore.to_sym
22
+ end
23
+ end
24
+
25
+ def self.create!
26
+ [
27
+ FirstTimeVisitor,
28
+ ReturningVisitor,
29
+ FirstTimeCustomer,
30
+ ReturningCustomer,
31
+ LoyalCustomer
32
+ ].each(&:instance)
33
+ end
34
+
35
+ def name
36
+ self.class.name.demodulize.underscore.titleize
37
+ end
38
+
39
+ def destroy
40
+ false
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ module Workarea
2
+ class Segment
3
+ class LoyalCustomer < Segment
4
+ include LifeCycle
5
+
6
+ self.default_rules = [
7
+ Rules::Orders.new(minimum: Workarea.config.loyal_customers_min_orders),
8
+ Rules::LastOrder.new(days: Workarea.config.loyal_customers_last_order_days_ago)
9
+ ]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module Workarea
2
+ class Segment
3
+ class ReturningCustomer < Segment
4
+ include LifeCycle
5
+
6
+ self.default_rules = [
7
+ Rules::Orders.new(
8
+ minimum: 2,
9
+ maximum: Workarea.config.loyal_customers_min_orders - 1
10
+ )
11
+ ]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ module Workarea
2
+ class Segment
3
+ class ReturningVisitor < Segment
4
+ include LifeCycle
5
+
6
+ self.default_rules = [
7
+ Rules::Sessions.new(minimum: 2),
8
+ Rules::Orders.new(maximum: 0)
9
+ ]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module Workarea
2
+ class Segment
3
+ module Rules
4
+ class Base
5
+ include ApplicationDocument
6
+
7
+ embedded_in :segment, class_name: 'Workarea::Segment', inverse_of: :rules
8
+ delegate :slug, to: :class
9
+
10
+ def self.slug
11
+ name.demodulize.underscore.to_sym
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,29 @@
1
+ module Workarea
2
+ class Segment
3
+ module Rules
4
+ class BrowserInfo < Base
5
+ field :general, type: Array, default: []
6
+ field :device, type: Array, default: []
7
+ field :platform, type: Array, default: []
8
+
9
+ def qualifies?(visit)
10
+ general_match?(visit.browser) ||
11
+ device_match?(visit.browser) ||
12
+ platform_match?(visit.browser)
13
+ end
14
+
15
+ def general_match?(browser)
16
+ general.any? { |g| browser.try("#{g}?") }
17
+ end
18
+
19
+ def device_match?(browser)
20
+ device.any? { |d| browser.device.try("#{d}?") }
21
+ end
22
+
23
+ def platform_match?(browser)
24
+ platform.any? { |p| browser.platform.try("#{p}?") }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module Workarea
2
+ class Segment
3
+ module Rules
4
+ class Geolocation < Base
5
+ field :locations, type: Array, default: []
6
+
7
+ def qualifies?(visit)
8
+ return false if locations.blank?
9
+
10
+ visit_locations = matchable_locations_for(visit)
11
+ return false if visit_locations.blank?
12
+
13
+ matchable_locations = locations.map(&:downcase)
14
+ visit_locations.any? { |l| l.in?(matchable_locations) }
15
+ end
16
+
17
+ def matchable_locations_for(visit)
18
+ option_ids = [
19
+ visit.postal_code,
20
+ GeolocationOption.from_subdivision(visit.country, visit.subdivision)&.id,
21
+ GeolocationOption.from_country(visit.country)&.id
22
+ ]
23
+
24
+ (visit.location_names + option_ids).reject(&:blank?).map(&:downcase)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,53 @@
1
+ module Workarea
2
+ class Segment
3
+ module Rules
4
+ class GeolocationOption
5
+ class << self
6
+ delegate :[], to: :all
7
+
8
+ def all
9
+ @all ||= Country.all.reduce({}) do |memo, country|
10
+ memo[country.alpha2] = from_country(country)
11
+
12
+ country.subdivisions.each do |id, subdivision|
13
+ if subdivision.name.present?
14
+ location = from_subdivision(country, subdivision)
15
+ memo[location.id] = location
16
+ end
17
+ end
18
+
19
+ memo
20
+ end
21
+ end
22
+
23
+ def from_country(country)
24
+ return if country.blank?
25
+ new(id: country.alpha2, name: country.name, model: country)
26
+ end
27
+
28
+ def from_subdivision(country, subdivision)
29
+ return if country.blank? || subdivision.blank?
30
+
31
+ id = "#{country.alpha2}-#{country.subdivisions.invert[subdivision]}"
32
+ name = "#{subdivision.name}, #{country.alpha2}"
33
+ new(id: id, name: name, model: subdivision)
34
+ end
35
+
36
+ def search(query)
37
+ return [] if query.blank?
38
+ regex = /^#{::Regexp.quote(query.to_s)}/i
39
+ all.values.select { |r| r.name =~ regex }
40
+ end
41
+ end
42
+
43
+ attr_reader :id, :name, :model
44
+
45
+ def initialize(options)
46
+ @id = options[:id]
47
+ @name = options[:name]
48
+ @model = options[:model]
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,14 @@
1
+ module Workarea
2
+ class Segment
3
+ module Rules
4
+ class LastOrder < Base
5
+ field :days, type: Integer
6
+
7
+ def qualifies?(visit)
8
+ return false if visit.metrics.last_order_at.blank? || days.blank?
9
+ visit.metrics.last_order_at >= days.days.ago
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end