workarea-core 3.4.44 → 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.rb +18 -6
  26. data/app/models/workarea/checkout/collect_payment.rb +10 -1
  27. data/app/models/workarea/checkout/fraud/analyzer.rb +44 -0
  28. data/app/models/workarea/checkout/fraud/no_decision_analyzer.rb +14 -0
  29. data/app/models/workarea/checkout/fraud/test_analyzer.rb +26 -0
  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.rb +12 -82
  33. data/app/models/workarea/content/asset.rb +5 -18
  34. data/app/models/workarea/content/block.rb +8 -2
  35. data/app/models/workarea/content/block_draft.rb +7 -2
  36. data/app/models/workarea/content/block_type.rb +2 -2
  37. data/app/models/workarea/content/block_type_definition.rb +4 -0
  38. data/app/models/workarea/content/preset.rb +1 -1
  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.rb +47 -4
  72. data/app/models/workarea/order/fraud_decision.rb +18 -0
  73. data/app/models/workarea/order/item.rb +5 -0
  74. data/app/models/workarea/order/queries.rb +1 -0
  75. data/app/models/workarea/order/status.rb +10 -1
  76. data/app/models/workarea/payment.rb +7 -2
  77. data/app/models/workarea/payment/credit_card.rb +12 -0
  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.rb +2 -3
  82. data/app/models/workarea/pricing/discount/application_group.rb +8 -3
  83. data/app/models/workarea/pricing/discount/free_gift.rb +2 -1
  84. data/app/models/workarea/pricing/discount/redemption.rb +1 -1
  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.rb +72 -99
  95. data/app/models/workarea/release/activation.rb +45 -0
  96. data/app/models/workarea/release/changes.rb +45 -0
  97. data/app/models/workarea/release/changeset.rb +42 -52
  98. data/app/models/workarea/release/preview.rb +28 -0
  99. data/app/models/workarea/release/status.rb +2 -24
  100. data/app/models/workarea/release_session.rb +11 -11
  101. data/app/models/workarea/search/admin.rb +17 -39
  102. data/app/models/workarea/search/admin/catalog_product.rb +1 -2
  103. data/app/models/workarea/search/admin/fulfillment_sku.rb +27 -0
  104. data/app/models/workarea/search/admin/order.rb +1 -1
  105. data/app/models/workarea/search/admin/pricing_discount.rb +1 -1
  106. data/app/models/workarea/search/admin/release.rb +1 -4
  107. data/app/models/workarea/search/admin/segment.rb +19 -0
  108. data/app/models/workarea/search/order_text.rb +23 -6
  109. data/app/models/workarea/search/storefront.rb +33 -29
  110. data/app/models/workarea/search/storefront/category_query.rb +152 -0
  111. data/app/models/workarea/search/storefront/product/categories.rb +11 -45
  112. data/app/models/workarea/segment.rb +54 -0
  113. data/app/models/workarea/segment/first_time_customer.rb +8 -0
  114. data/app/models/workarea/segment/first_time_visitor.rb +12 -0
  115. data/app/models/workarea/segment/life_cycle.rb +44 -0
  116. data/app/models/workarea/segment/loyal_customer.rb +12 -0
  117. data/app/models/workarea/segment/returning_customer.rb +14 -0
  118. data/app/models/workarea/segment/returning_visitor.rb +12 -0
  119. data/app/models/workarea/segment/rules/base.rb +16 -0
  120. data/app/models/workarea/segment/rules/browser_info.rb +29 -0
  121. data/app/models/workarea/segment/rules/geolocation.rb +29 -0
  122. data/app/models/workarea/segment/rules/geolocation_option.rb +53 -0
  123. data/app/models/workarea/segment/rules/last_order.rb +14 -0
  124. data/app/models/workarea/segment/rules/logged_in.rb +13 -0
  125. data/app/models/workarea/segment/rules/orders.rb +17 -0
  126. data/app/models/workarea/segment/rules/revenue.rb +17 -0
  127. data/app/models/workarea/segment/rules/sessions.rb +17 -0
  128. data/app/models/workarea/segment/rules/tags.rb +14 -0
  129. data/app/models/workarea/segment/rules/traffic_referrer.rb +18 -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.rb +5 -4
  170. data/app/queries/workarea/search/storefront_search/exact_matches.rb +1 -1
  171. data/app/queries/workarea/search/storefront_search/response.rb +6 -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.rb +102 -3
  236. data/lib/workarea/cache.rb +6 -35
  237. data/lib/workarea/changelog.rake +2 -2
  238. data/lib/workarea/configuration.rb +121 -148
  239. data/lib/workarea/configuration/administrable/definition.rb +42 -0
  240. data/lib/workarea/configuration/administrable/field.rb +79 -0
  241. data/lib/workarea/configuration/administrable/fieldset.rb +44 -0
  242. data/lib/workarea/configuration/administrable_options.rb +24 -0
  243. data/lib/workarea/configuration/cache_store.rb +3 -1
  244. data/lib/workarea/configuration/content_blocks.rb +17 -0
  245. data/lib/workarea/configuration/mongoid.rb +1 -4
  246. data/lib/workarea/configuration/session.rb +36 -0
  247. data/lib/workarea/configuration/sidekiq.rb +3 -1
  248. data/lib/workarea/core.rb +15 -11
  249. data/lib/workarea/core/engine.rb +3 -58
  250. data/lib/workarea/elasticsearch/document.rb +8 -15
  251. data/lib/workarea/ext/freedom_patches/action_view_cache_helper.rb +4 -6
  252. data/lib/workarea/ext/freedom_patches/active_support_duration.rb +14 -0
  253. data/lib/workarea/ext/freedom_patches/money.rb +11 -0
  254. data/lib/workarea/ext/freedom_patches/premailer.rb +6 -0
  255. data/lib/workarea/ext/freedom_patches/string.rb +19 -4
  256. data/lib/workarea/ext/mongoid/find_ordered.rb +2 -2
  257. data/lib/workarea/ext/mongoid/lookup_hash.rb +30 -0
  258. data/lib/workarea/geolocation.rb +24 -6
  259. data/lib/workarea/routes_constraints/super_admin.rb +1 -1
  260. data/lib/workarea/version.rb +3 -4
  261. data/lib/workarea/visit.rb +92 -0
  262. data/lib/workarea/warnings.rb +95 -0
  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 +12 -10
  394. metadata +188 -78
  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
@@ -3,7 +3,10 @@ module Workarea
3
3
  class Rate
4
4
  include ApplicationDocument
5
5
 
6
- field :percentage, type: Float, default: 0
6
+
7
+ field :country_percentage, type: Float, default: 0
8
+ field :region_percentage, type: Float, default: 0
9
+ field :postal_code_percentage, type: Float, default: 0
7
10
  field :country, type: Country
8
11
  field :region, type: String
9
12
  field :postal_code, type: String
@@ -11,13 +14,37 @@ module Workarea
11
14
  field :tier_min, type: Money
12
15
  field :tier_max, type: Money
13
16
 
17
+ # This field is deprecated in favor of the more specific percentage fields
18
+ # TODO: Remove in v3.6,
19
+ field :percentage, type: Float, default: 0
20
+ alias_method :percentage=, :postal_code_percentage=
21
+
14
22
  index({ category_id: 1, country: 1, region: 1, postal_code: 1 })
23
+ index({ country: 1 })
24
+ index({ region: 1 })
25
+ index({ postal_code: 1 })
15
26
 
16
27
  belongs_to :category,
17
28
  class_name: 'Workarea::Tax::Category',
18
29
  inverse_of: :rates,
19
30
  index: true
20
31
 
32
+ def self.search(query)
33
+ return all unless query.present?
34
+
35
+ regex = /^#{::Regexp.quote(query)}/i
36
+ country = Country[query]
37
+
38
+ clauses = [{ region: regex }, { postal_code: regex }]
39
+ clauses << { country: country } if country.present?
40
+
41
+ any_of(clauses)
42
+ end
43
+
44
+ def self.sorts
45
+ [Sort.country, Sort.region, Sort.postal_code, Sort.newest, Sort.modified]
46
+ end
47
+
21
48
  def valid?(*)
22
49
  super.tap do
23
50
  self.region = nil if region.blank?
@@ -25,6 +52,13 @@ module Workarea
25
52
  self.percentage ||= 0
26
53
  end
27
54
  end
55
+
56
+ def percentage
57
+ percentage_field = super
58
+ return percentage_field unless percentage_field.zero?
59
+
60
+ country_percentage + region_percentage + postal_code_percentage
61
+ end
28
62
  end
29
63
  end
30
64
  end
@@ -18,6 +18,7 @@ module Workarea
18
18
  field :marketing_access, type: Boolean, default: false
19
19
  field :help_admin, type: Boolean, default: false
20
20
  field :permissions_manager, type: Boolean, default: false
21
+ field :orders_manager, type: Boolean, default: false
21
22
  field :can_publish_now, type: Boolean
22
23
  field :can_restore, type: Boolean
23
24
  field :status_email_recipient, type: Boolean, default: false
@@ -26,7 +27,8 @@ module Workarea
26
27
 
27
28
  before_validation :set_all_permissions, if: :super_admin?
28
29
  before_validation :set_default_admin_permissions, if: :admin?
29
- before_save :reset_permissions_if_no_longer_admin
30
+ before_validation :unset_all_permissions, if: :no_longer_admin?
31
+ before_save :reset_token, if: :no_longer_admin?
30
32
 
31
33
  scope :admins, -> { where(admin: true) }
32
34
  index({ admin: 1 })
@@ -34,7 +36,7 @@ module Workarea
34
36
 
35
37
  class_methods do
36
38
  def status_email_recipients
37
- admins.where(status_email_recipient: true).pluck(:email)
39
+ admins.where(status_email_recipient: true)
38
40
  end
39
41
  end
40
42
 
@@ -45,12 +47,22 @@ module Workarea
45
47
  )
46
48
  end
47
49
 
50
+ def super_admin=(value)
51
+ super.tap { super_admin ? set_all_permissions : unset_all_permissions }
52
+ end
53
+
54
+ def no_longer_admin?
55
+ !admin? && !super_admin && (admin_was || super_admin_was)
56
+ end
57
+
48
58
  private
49
59
 
50
60
  def set_all_permissions
51
- Workarea.config.permissions_fields.each do |field|
52
- send("#{field}=", true)
53
- end
61
+ Workarea.config.permissions_fields.each { |field| send("#{field}=", true) }
62
+ end
63
+
64
+ def unset_all_permissions
65
+ Workarea.config.permissions_fields.each { |field| send("#{field}=", false) }
54
66
  end
55
67
 
56
68
  def set_default_admin_permissions
@@ -58,14 +70,8 @@ module Workarea
58
70
  self.can_restore = true if can_restore.nil?
59
71
  end
60
72
 
61
- def reset_permissions_if_no_longer_admin
62
- if admin_was && !admin
63
- Workarea.config.permissions_fields.each do |field|
64
- send("#{field}=", false)
65
- end
66
-
67
- self.token = self.class.generate_unique_secure_token
68
- end
73
+ def reset_token
74
+ self.token = self.class.generate_unique_secure_token
69
75
  end
70
76
  end
71
77
  end
@@ -50,6 +50,10 @@ module Workarea
50
50
  last_login_attempt_at >= Workarea.config.lockout_period.ago
51
51
  end
52
52
 
53
+ def unlock_login!
54
+ update!(failed_login_count: 0)
55
+ end
56
+
53
57
  def update_login!(request)
54
58
  update_attributes!(
55
59
  ip_address: request.ip,
@@ -4,7 +4,7 @@ module Workarea
4
4
  include ApplicationDocument
5
5
  include UrlToken
6
6
 
7
- belongs_to :user, class_name: 'Workarea::User', index: true
7
+ belongs_to :user, class_name: 'Workarea::User'
8
8
 
9
9
  index(
10
10
  { created_at: 1 },
@@ -14,8 +14,6 @@ module Workarea
14
14
  def self.setup!(email)
15
15
  user = User.find_by_email(email)
16
16
  return nil unless user
17
-
18
- where(user_id: user.id).destroy_all
19
17
  create!(user: user)
20
18
  end
21
19
 
@@ -12,7 +12,7 @@ module Workarea
12
12
  end
13
13
 
14
14
  def default_model
15
- key = "#{@product.cache_key}/default_category"
15
+ key = [@product.cache_key, 'default_category', Release.current&.id].compact.join('/')
16
16
 
17
17
  @default_model ||= Rails.cache.fetch(key, expires_in: Workarea.config.cache_expirations.products_default_category) do
18
18
  manual_default_model ||
@@ -30,7 +30,7 @@ module Workarea
30
30
 
31
31
  def queries
32
32
  return [] if @product.blank?
33
- @queries ||= Search::Storefront::Product.find_categories(@product)
33
+ @queries ||= Search::Storefront::CategoryQuery.find_by_product(@product)
34
34
  end
35
35
 
36
36
  def to_models
@@ -57,7 +57,7 @@ module Workarea
57
57
 
58
58
  def manual_models
59
59
  return [] if @product.blank?
60
- @manual_models ||= Catalog::Category.by_product(@product.id).to_a
60
+ @manual_models ||= FeaturedCategorization.new(@product.id).to_a
61
61
  end
62
62
  end
63
63
  end
@@ -0,0 +1,53 @@
1
+ module Workarea
2
+ class FeaturedCategorization
3
+ include Enumerable
4
+ delegate :blank?, :present?, :size, :length, :to_a, to: :all
5
+
6
+ def initialize(*product_ids)
7
+ @product_ids = product_ids
8
+ end
9
+
10
+ def all
11
+ @all ||= begin
12
+ result = live
13
+
14
+ categories_affected_by_current_release.each do |category|
15
+ if product_featured_in_category?(category)
16
+ result += [category] unless result.include?(category)
17
+ else
18
+ result -= [category]
19
+ end
20
+ end
21
+
22
+ result
23
+ end
24
+ end
25
+
26
+ def live
27
+ @live ||= Catalog::Category.by_product(@product_ids).to_a
28
+ end
29
+
30
+ def categories_affected_by_current_release
31
+ return [] if Release.current.blank?
32
+
33
+ Catalog::Category.in(
34
+ id: category_changesets_affecting_current_release.map(&:releasable_id)
35
+ )
36
+ end
37
+
38
+ def category_changesets_affecting_current_release
39
+ return [] if Release.current.blank?
40
+
41
+ FeaturedProducts
42
+ .changesets(*@product_ids)
43
+ .where(releasable_type: Catalog::Category.name)
44
+ .in(release_id: Release.current.preview.releases.map(&:id))
45
+ end
46
+
47
+ private
48
+
49
+ def product_featured_in_category?(category)
50
+ @product_ids.any? { |id| category.featured_product?(id) }
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,155 @@
1
+ module Workarea
2
+ class OrderCancellationMetrics
3
+ attr_reader :order, :occured_at
4
+ delegate_missing_to :order
5
+
6
+ def initialize(order, item_values: {}, shipping_value: nil, occured_at: Time.current)
7
+ @order = order
8
+ @item_values = item_values
9
+ @shipping_value = shipping_value
10
+ @occured_at = occured_at&.to_time
11
+ end
12
+
13
+ def sales_data
14
+ @sales_data ||= begin
15
+ based_on_items = calculate_based_on_items(items)
16
+ refund = based_on_items[:refund] + shipping_refund_total
17
+ {
18
+ cancellations: 1,
19
+ units_canceled: based_on_items[:units_canceled],
20
+ refund: refund,
21
+ revenue: refund
22
+ }
23
+ end
24
+ end
25
+
26
+ def user_data
27
+ {
28
+ email: email,
29
+ refund: sales_data[:refund]
30
+ }
31
+ end
32
+
33
+ def products
34
+ @products ||= order.items
35
+ .group_by(&:product_id)
36
+ .transform_values { |items| calculate_based_on_items(items) }
37
+ .reject { |_, v| v.blank? }
38
+ end
39
+
40
+ def categories
41
+ @categories ||= items_by_via
42
+ .select { |gid| gid.model_class.name == 'Workarea::Catalog::Category' }
43
+ .transform_keys(&:model_id)
44
+ end
45
+
46
+ def searches
47
+ @searches ||= items_by_via
48
+ .select { |gid| gid.model_class.name == 'Workarea::Navigation::SearchResults' }
49
+ .each_with_object({}) do |(gid, data), memo|
50
+ query_id = gid.find.query_string.id
51
+
52
+ memo[query_id] ||= Hash.new(0)
53
+ memo[query_id].merge!(data) { |_, total, current| total + current }
54
+ end
55
+ end
56
+
57
+ def skus
58
+ @skus ||= order.items
59
+ .group_by(&:sku)
60
+ .transform_values { |items| calculate_based_on_items(items) }
61
+ .reject { |_, v| v.blank? }
62
+ end
63
+
64
+ def menus
65
+ @menus ||= categories.each_with_object({}) do |(category_id, data), memo|
66
+ category = category_models[category_id]
67
+ taxon_ids = [category&.taxon&.id, *category&.taxon&.parent_ids].compact
68
+
69
+ Navigation::Menu.any_in(taxon_id: taxon_ids).pluck(:id).map(&:to_s).each do |menu_id|
70
+ memo[menu_id] ||= Hash.new(0)
71
+ memo[menu_id].merge!(data) { |_, total, current| total + current }
72
+ end
73
+
74
+ memo
75
+ end.reject { |_, v| v.blank? }
76
+ end
77
+
78
+ def country
79
+ payment.address&.country&.alpha2
80
+ end
81
+
82
+ def shipping_refund_total
83
+ (@shipping_value || shipping_total || 0) * -1
84
+ end
85
+
86
+ def segments
87
+ @segments ||= order.segment_ids.each_with_object({}) do |segment_id, data|
88
+ data[segment_id] = sales_data
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def payment
95
+ @payment ||= Payment.find_or_initialize_by(id: order.id)
96
+ end
97
+
98
+ def shippings
99
+ @shippings ||= Shipping.by_order(order.id)
100
+ end
101
+
102
+ def all_price_adjustments
103
+ @all_price_adjustments ||= order.price_adjustments +
104
+ shippings.map(&:price_adjustments).flatten
105
+ end
106
+
107
+ def calculate_based_on_items(items)
108
+ Array.wrap(items).each_with_object(Hash.new(0)) do |item, memo|
109
+ data = item_data[item.id.to_s]&.with_indifferent_access
110
+ next unless data.present?
111
+
112
+ memo[:units_canceled] += data[:quantity]
113
+ memo[:refund] += data[:amount] * -1
114
+ memo[:revenue] += data[:amount] * -1
115
+ end
116
+ end
117
+
118
+ def item_data
119
+ return @item_values if @item_values.present?
120
+
121
+ @item_data ||= items.each_with_object({}) do |item, data|
122
+ adjustments = PriceAdjustmentSet.new(item.price_adjustments)
123
+ before_tax = adjustments.sum - adjustments.adjusting('tax').sum
124
+
125
+ data[item.id.to_s] = {
126
+ quantity: item.quantity,
127
+ amount: before_tax + tax_total_for(item.id)
128
+ }
129
+ end
130
+ end
131
+
132
+ def items_by_via
133
+ @items_by_via ||= order.items.each_with_object({}) do |item, memo|
134
+ global_id = GlobalID.parse(item.via)
135
+ next unless global_id.present?
136
+
137
+ memo[global_id] ||= Hash.new(0)
138
+ values = calculate_based_on_items(item)
139
+
140
+ memo[global_id].merge!(values) { |_, sum, item| sum + item }
141
+ end.reject { |_, v| v.blank? }
142
+ end
143
+
144
+ def tax_total_for(item_id)
145
+ all_price_adjustments
146
+ .adjusting('tax')
147
+ .select { |a| a.data['order_item_id'].to_s == item_id.to_s }
148
+ .sum
149
+ end
150
+
151
+ def category_models
152
+ @category_models ||= Catalog::Category.any_in(id: categories.keys).to_lookup_hash
153
+ end
154
+ end
155
+ end
@@ -4,8 +4,6 @@ module Workarea
4
4
 
5
5
  attr_reader :product, :sku
6
6
 
7
- delegate :digital?, to: :product
8
-
9
7
  def self.find!(sku, product_id: nil)
10
8
  product =
11
9
  if product_id.present?
@@ -43,12 +41,27 @@ module Workarea
43
41
  @pricing ||= Pricing::Sku.find(sku)
44
42
  end
45
43
 
44
+ def fulfillment
45
+ @fulfillment ||= Fulfillment::Sku.find_or_initialize_by(id: sku)
46
+ end
47
+
48
+ # This is a stop-gap for transitioning away from a digital flag for
49
+ # determining checkout behavior for items not requiring
50
+ # physical fulfillment.
51
+ #
52
+ # TODO: remove #digital? usage in v3.6
53
+ #
54
+ def requires_shipping?
55
+ !product.digital? && fulfillment.requires_shipping?
56
+ end
57
+
46
58
  def to_h
47
59
  {
48
60
  product_id: product.id,
49
61
  product_attributes: product.as_document,
50
62
  category_ids: category_ids,
51
- discountable: pricing.discountable?
63
+ discountable: pricing.discountable?,
64
+ requires_shipping: requires_shipping?
52
65
  }
53
66
  end
54
67
  end
@@ -7,6 +7,10 @@ module Workarea
7
7
  @order = order
8
8
  end
9
9
 
10
+ def occured_at
11
+ order.placed_at
12
+ end
13
+
10
14
  def user
11
15
  @user ||= Metrics::User.find_or_initialize_by(id: email)
12
16
  end
@@ -38,6 +42,14 @@ module Workarea
38
42
  end
39
43
  end
40
44
 
45
+ def user_data
46
+ {
47
+ email: email,
48
+ revenue: total_price,
49
+ discounts: sales_data[:discounts],
50
+ }
51
+ end
52
+
41
53
  def shipping_before_discounts
42
54
  all_price_adjustments
43
55
  .select { |pa| pa.price == 'shipping' }
@@ -85,7 +97,7 @@ module Workarea
85
97
 
86
98
  def menus
87
99
  @menus ||= categories.reduce({}) do |memo, (category_id, data)|
88
- category = category_models.detect { |c| c.id.to_s == category_id.to_s }
100
+ category = category_models[category_id]
89
101
  taxon_ids = [category&.taxon&.id, category&.taxon&.parent_ids].flatten.reject(&:blank?)
90
102
 
91
103
  Navigation::Menu.any_in(taxon_id: taxon_ids).pluck(:id).map(&:to_s).each do |menu_id|
@@ -104,8 +116,12 @@ module Workarea
104
116
  end
105
117
  end
106
118
 
119
+ def payment
120
+ @payment ||= Payment.find_or_initialize_by(id: order.id)
121
+ end
122
+
107
123
  def country
108
- Payment.find(order.id).address.country.alpha2 rescue nil
124
+ payment.address&.country&.alpha2
109
125
  end
110
126
 
111
127
  def shippings
@@ -138,6 +154,19 @@ module Workarea
138
154
  end
139
155
  end
140
156
 
157
+ def tenders
158
+ @tenders ||= payment.tenders.each_with_object({}) do |tender, data|
159
+ data[tender.slug] ||= { orders: 1, revenue: 0 }
160
+ data[tender.slug][:revenue] += tender.amount
161
+ end
162
+ end
163
+
164
+ def segments
165
+ @segments ||= order.segment_ids.each_with_object({}) do |segment_id, data|
166
+ data[segment_id] = sales_data
167
+ end
168
+ end
169
+
141
170
  private
142
171
 
143
172
  def calculate_based_on_items(items)
@@ -197,7 +226,7 @@ module Workarea
197
226
  end
198
227
 
199
228
  def category_models
200
- @category_models ||= Catalog::Category.any_in(id: categories.keys).to_a
229
+ @category_models ||= Catalog::Category.any_in(id: categories.keys).to_lookup_hash
201
230
  end
202
231
  end
203
232
  end