workarea-core 3.5.16 → 3.5.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/workarea/current_tracking.rb +4 -2
  3. data/app/controllers/workarea/impersonation.rb +4 -2
  4. data/app/mailers/workarea/application_mailer.rb +4 -1
  5. data/app/middleware/workarea/application_middleware.rb +5 -2
  6. data/app/models/workarea/checkout.rb +6 -2
  7. data/app/models/workarea/data_file/csv.rb +9 -1
  8. data/app/models/workarea/inquiry.rb +2 -1
  9. data/app/models/workarea/order/item.rb +4 -4
  10. data/app/models/workarea/search/admin/inventory_sku.rb +5 -1
  11. data/app/models/workarea/shipping/service.rb +12 -5
  12. data/app/models/workarea/tax/rate.rb +3 -1
  13. data/app/queries/workarea/search/admin_search.rb +4 -0
  14. data/app/queries/workarea/search/admin_sorting.rb +1 -1
  15. data/app/queries/workarea/search/product_entries.rb +12 -6
  16. data/config/initializers/00_configuration.rb +23 -8
  17. data/config/initializers/05_scheduled_jobs.rb +1 -1
  18. data/config/initializers/22_session_store.rb +1 -1
  19. data/config/locales/en.yml +3 -0
  20. data/lib/generators/workarea/install/install_generator.rb +13 -0
  21. data/lib/generators/workarea/install/templates/initializer.rb.erb +1 -13
  22. data/lib/tasks/cache.rake +3 -33
  23. data/lib/tasks/help.rake +4 -43
  24. data/lib/tasks/insights.rake +3 -35
  25. data/lib/tasks/migrate.rake +3 -96
  26. data/lib/tasks/search.rake +6 -68
  27. data/lib/tasks/services.rake +4 -54
  28. data/lib/workarea.rb +10 -0
  29. data/lib/workarea/configuration.rb +4 -3
  30. data/lib/workarea/configuration/administrable_options.rb +2 -1
  31. data/lib/workarea/core/engine.rb +4 -0
  32. data/lib/workarea/scheduled_jobs.rb +1 -1
  33. data/lib/workarea/tasks/cache.rb +43 -0
  34. data/lib/workarea/tasks/help.rb +55 -0
  35. data/lib/workarea/tasks/insights.rb +47 -0
  36. data/lib/workarea/tasks/migrate.rb +106 -0
  37. data/lib/workarea/tasks/search.rb +105 -0
  38. data/lib/workarea/tasks/services.rb +71 -0
  39. data/lib/workarea/version.rb +1 -1
  40. data/lib/workarea/visit.rb +8 -1
  41. data/lib/workarea/warnings.rb +1 -1
  42. data/test/generators/workarea/install_generator_test.rb +6 -2
  43. data/test/integration/workarea/authentication_test.rb +2 -1
  44. data/test/lib/workarea/scheduled_jobs_test.rb +1 -5
  45. data/test/mailers/workarea/application_mailer_test.rb +10 -0
  46. data/test/models/workarea/checkout_test.rb +57 -0
  47. data/test/models/workarea/data_file/csv_test.rb +2 -1
  48. data/test/models/workarea/data_file/import_test.rb +40 -0
  49. data/test/models/workarea/order/item_test.rb +9 -0
  50. data/test/models/workarea/shipping/service_test.rb +26 -0
  51. data/test/queries/workarea/search/admin_search_test.rb +10 -0
  52. data/test/workers/workarea/status_reporter_test.rb +3 -1
  53. data/workarea-core.gemspec +1 -1
  54. metadata +10 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 569f33ae16d992354fca8761ef67ed5473a12755dda710cfff057ebd2d7aa2b4
4
- data.tar.gz: 24b32513e4215b2aa1c67a5f1d930ae5f8a81ee098be08234eab95f69a6bc574
3
+ metadata.gz: 95264a0fb5c9e405852a21f6c8b590f54c97a64b9fb72bc97156dbc57ec36c01
4
+ data.tar.gz: 6811a1d019494db24af2cbc60ec987f71d58c96b27677bc5260780c939619be0
5
5
  SHA512:
6
- metadata.gz: 11b65e3f2de7268ec497fecb2019a10133e2a477cc5bf06cd64390976098baa97277a75a295633a4fd6e43b221d31453c6a4da99b1c05012534e19621a6c7d18
7
- data.tar.gz: 614ed5b1d7970082ba8eb48b8448434737d194ed1fed7158537774b9e46dce862ab0cae2faafca35f40378a061e3a91c02b9ab4d36875201896ea33ff6b60991
6
+ metadata.gz: 64268e7f2384a9812fd40613e5b0466a5e28945ad7b7f68a3cf33392ff27fb2b1030ce802b69f4d569ee08e5a8efac51cdb0c75359bec5376825018458eef126
7
+ data.tar.gz: 6aa1f3a86cc08c3d2f1ab9dab6bf0087c01533d53b3fc38f157e92a834cbc6cddfc9838a8351a0d4cd1e2d754cc274081e83dfe9b62aa9b6437c47e958762f23
@@ -1,7 +1,6 @@
1
1
  module Workarea
2
2
  module CurrentTracking
3
3
  extend ActiveSupport::Concern
4
- include HttpCaching
5
4
 
6
5
  included do
7
6
  before_action :ensure_current_metrics
@@ -27,7 +26,10 @@ module Workarea
27
26
  if email.blank?
28
27
  cookies.delete(:email)
29
28
  elsif email != cookies.signed[:email]
30
- Metrics::User.find_or_initialize_by(id: email).merge!(current_visit&.metrics)
29
+ unless impersonating?
30
+ Metrics::User.find_or_initialize_by(id: email).merge!(current_visit&.metrics)
31
+ end
32
+
31
33
  cookies.permanent.signed[:email] = email
32
34
  end
33
35
 
@@ -15,10 +15,11 @@ module Workarea
15
15
  session[:user_id] = user.id.to_s
16
16
 
17
17
  user.mark_impersonated_by!(current_user)
18
- @current_user = user
18
+ update_tracking!(email: user.email)
19
19
  end
20
20
 
21
21
  def stop_impersonation
22
+ update_tracking!(email: current_admin.email)
22
23
  session[:user_id] = current_admin.id.to_s
23
24
  session.delete(:admin_id)
24
25
  end
@@ -39,7 +40,8 @@ module Workarea
39
40
  end
40
41
 
41
42
  def current_impersonation
42
- @current_impersonation ||= User.find(session[:user_id])
43
+ return @current_impersonation if defined?(@current_impersonation)
44
+ @current_impersonation = User.find(session[:user_id]) rescue nil
43
45
  end
44
46
 
45
47
  def admin_browse_as_guest
@@ -8,7 +8,10 @@ module Workarea
8
8
  default from: -> (*) { Workarea.config.email_from }
9
9
 
10
10
  def default_url_options(options = {})
11
- super.merge(host: Workarea.config.host)
11
+ # super isn't returning the configured options, so manually merge them in
12
+ super
13
+ .merge(Rails.application.config.action_mailer.default_url_options.to_h)
14
+ .merge(host: Workarea.config.host)
12
15
  end
13
16
  end
14
17
  end
@@ -1,12 +1,15 @@
1
1
  module Workarea
2
2
  class ApplicationMiddleware
3
+ ASSET_REGEX = /(jpe?g|png|ico|gif|bmp|webp|tif?f|css|js|svg|otf|ttf|woff|woff2)$/
4
+
3
5
  def initialize(app)
4
6
  @app = app
5
7
  end
6
8
 
7
9
  def call(env)
8
10
  request = Rack::Request.new(env)
9
- return @app.call(env) if request.path =~ /(jpe?g|png|ico|gif|css|js|svg)$/
11
+ env['workarea.asset_request'] = request.path =~ ASSET_REGEX
12
+ return @app.call(env) if env['workarea.asset_request']
10
13
 
11
14
  set_locale(env, request)
12
15
  setup_environment(env, request)
@@ -25,7 +28,7 @@ module Workarea
25
28
  env['workarea.visit'] = Visit.new(env)
26
29
  env['workarea.cache_varies'] = Cache::Varies.new(env['workarea.visit']).to_s
27
30
  env['rack-cache.cache_key'] = Cache::RackCacheKey
28
- env['rack-cache.force-pass'] = env['workarea.visit'].admin?
31
+ env['rack-cache.force-pass'] = env['workarea.visit'].admin? && !env['workarea.asset_request']
29
32
  end
30
33
 
31
34
  def set_segment_request_headers(env)
@@ -154,10 +154,14 @@ module Workarea
154
154
  # Used in auto completing an order for a logged in user.
155
155
  #
156
156
  # @param [Hash] parameters for updating
157
- # @return [self]
157
+ # @return [Boolean] whether the update was successful.
158
158
  #
159
159
  def update(params = {})
160
- steps.each { |s| s.new(self).update(params) }
160
+ return true if params.blank?
161
+
162
+ steps.reduce(true) do |result, step|
163
+ result &= step.new(self).update(params)
164
+ end
161
165
  end
162
166
 
163
167
  # Whether this checkout needs any further information
@@ -17,7 +17,15 @@ module Workarea
17
17
  assign_attributes(root, attrs)
18
18
  assign_embedded_attributes(root, attrs)
19
19
 
20
- if root.save || failed_new_record_ids.exclude?(id)
20
+ possibly_affected_models = root.embedded_children + [root]
21
+ was_successful = true
22
+
23
+ possibly_affected_models.each do |model|
24
+ meaningful_changes = model.changes.except('updated_at')
25
+ was_successful &= model.save if model.changed? && meaningful_changes.present?
26
+ end
27
+
28
+ if was_successful || failed_new_record_ids.exclude?(id)
21
29
  log(index, root)
22
30
  else
23
31
  operation.total += 1 # ensure line numbers remain consistent
@@ -15,7 +15,8 @@ module Workarea
15
15
  validate :subject_exists
16
16
 
17
17
  def full_subject
18
- Workarea.config.inquiry_subjects[subject]
18
+ I18n.t('workarea.inquiry.subjects')[subject.optionize.to_sym].presence ||
19
+ Workarea.config.inquiry_subjects[subject]
19
20
  end
20
21
 
21
22
  private
@@ -33,17 +33,17 @@ module Workarea
33
33
  # To allow for custom policies defining their own methods here
34
34
  Workarea.config.fulfillment_policies.each do |class_name|
35
35
  define_method "#{class_name.demodulize.underscore}?" do
36
- fulfillment == class_name.demodulize.underscore
36
+ fulfilled_by?(class_name.demodulize.underscore)
37
37
  end
38
38
  end
39
39
 
40
40
  # These methods exist for findability
41
41
  def shipping?
42
- fulfillment == 'shipping'
42
+ fulfilled_by?('shipping')
43
43
  end
44
44
 
45
45
  def download?
46
- fulfillment == 'download'
46
+ fulfilled_by?('download')
47
47
  end
48
48
 
49
49
  # Whether this order has any items that need to be fulfilled by a particular
@@ -53,7 +53,7 @@ module Workarea
53
53
  # @return [Boolean]
54
54
  #
55
55
  def fulfilled_by?(*types)
56
- types.any? { |t| send("#{t}?") }
56
+ types.map(&:to_s).include?(fulfillment)
57
57
  end
58
58
 
59
59
  # Whether this item is a digital (not-shipped) type of item.
@@ -11,7 +11,11 @@ module Workarea
11
11
  end
12
12
 
13
13
  def jump_to_text
14
- "#{model.id} (#{model.available} available)"
14
+ I18n.t(
15
+ 'workarea.inventory_sku.jump_to_text',
16
+ id: model.id,
17
+ count: model.available_to_sell
18
+ )
15
19
  end
16
20
 
17
21
  def jump_to_position
@@ -37,15 +37,22 @@ module Workarea
37
37
  end
38
38
 
39
39
  def self.by_price(price)
40
- cache.select do |method|
41
- (method.subtotal_min.nil? || method.subtotal_min <= price) &&
42
- (method.subtotal_max.nil? || method.subtotal_max >= price)
40
+ cache.select do |service|
41
+ (service.subtotal_min.nil? || service.subtotal_min <= price) &&
42
+ (service.subtotal_max.nil? || service.subtotal_max >= price)
43
43
  end
44
44
  end
45
45
 
46
46
  def self.find_tax_code(carrier, name)
47
- method = find_by(carrier: carrier, name: name) rescue nil
48
- method.try(:tax_code)
47
+ service = find_by(carrier: carrier, name: name) rescue nil
48
+ service.present? ? service.tax_code : default_tax_code(carrier, name)
49
+ end
50
+
51
+ def self.default_tax_code(carrier, name)
52
+ default = Workarea.config.default_shipping_service_tax_code
53
+ return default unless default.respond_to?(:call)
54
+
55
+ default.call(carrier, name)
49
56
  end
50
57
 
51
58
  def find_rate(price = 0.to_m)
@@ -57,7 +57,9 @@ module Workarea
57
57
  percentage_field = super
58
58
  return percentage_field unless percentage_field.zero?
59
59
 
60
- country_percentage + region_percentage + postal_code_percentage
60
+ [country_percentage, region_percentage, postal_code_percentage]
61
+ .compact
62
+ .sum
61
63
  end
62
64
  end
63
65
  end
@@ -11,6 +11,10 @@ module Workarea
11
11
  def self.available_sorts
12
12
  AdminSorting.available_sorts
13
13
  end
14
+
15
+ def default_admin_sort
16
+ [{ _score: :desc }, { updated_at: :desc }]
17
+ end
14
18
  end
15
19
  end
16
20
  end
@@ -16,7 +16,7 @@ module Workarea
16
16
  end
17
17
 
18
18
  def default_admin_sort
19
- [{ _score: :desc }, { updated_at: :desc }]
19
+ [{ updated_at: :desc }, { _score: :desc }]
20
20
  end
21
21
 
22
22
  def user_selected_sort
@@ -18,17 +18,23 @@ module Workarea
18
18
 
19
19
  def live_entries
20
20
  @live_entries ||= @products.reduce([]) do |memo, product|
21
- memo + Array.wrap(index_entries_for(product.without_release))
21
+ memo + live_entries_for(product)
22
22
  end
23
23
  end
24
24
 
25
25
  def release_entries
26
- @release_entries ||= @products.reduce([]) do |results, product|
27
- releases = ProductReleases.new(product).releases
26
+ @release_entries ||= @products.reduce([]) do |memo, product|
27
+ memo + release_entries_for(product)
28
+ end
29
+ end
30
+
31
+ def live_entries_for(product)
32
+ Array.wrap(index_entries_for(product.without_release))
33
+ end
28
34
 
29
- results + releases.reduce([]) do |memo, release|
30
- memo + Array.wrap(index_entries_for(product.in_release(release)))
31
- end
35
+ def release_entries_for(product)
36
+ ProductReleases.new(product).releases.reduce([]) do |memo, release|
37
+ memo + Array.wrap(index_entries_for(product.in_release(release)))
32
38
  end
33
39
  end
34
40
 
@@ -76,6 +76,17 @@ Workarea::Configuration.define_fields do
76
76
  zip: '19106'
77
77
  },
78
78
  description: 'Origin location for calculating shipping costs'
79
+
80
+ # This can be overwritten within the app to use a proc for more complex
81
+ # scenarios.
82
+ field 'Default Shipping Service Tax Code',
83
+ type: String,
84
+ allow_blank: true,
85
+ description: %(
86
+ Tax code assigned to shipping options when an existing service does
87
+ not exist. This is useful for third-party gateways to assign tax codes
88
+ to dynamically generated options.
89
+ ).squish
79
90
  end
80
91
 
81
92
  fieldset 'Payment', namespaced: false do
@@ -116,11 +127,6 @@ Workarea::Configuration.define_fields do
116
127
  end
117
128
 
118
129
  fieldset 'Search', namespaced: false do
119
- field 'Default Search Facet Result Sizes',
120
- type: :integer,
121
- default: 10,
122
- description: 'The number of filter results returned for each filter type.'
123
-
124
130
  field 'Search Facet Result Sizes',
125
131
  type: :hash,
126
132
  values_type: :integer,
@@ -128,7 +134,15 @@ Workarea::Configuration.define_fields do
128
134
  description: %(
129
135
  The number of filter results returned for any specified filter type. If no
130
136
  size is defined for a filter type, the default will be what is specified
131
- in the default config above.
137
+ in the default config below.
138
+ ).squish
139
+
140
+ field 'Default Search Facet Result Sizes',
141
+ type: :integer,
142
+ default: 10,
143
+ description: %(
144
+ The number of filter results returned for each filter type when not
145
+ specified above.
132
146
  ).squish
133
147
 
134
148
  field 'Search Size Facet Sort',
@@ -156,14 +170,15 @@ Workarea::Configuration.define_fields do
156
170
  end
157
171
 
158
172
  fieldset 'Communication', namespaced: false do
173
+
159
174
  field 'Email From',
160
175
  type: :string,
161
- default: 'noreply@example.com',
176
+ default: -> { "#{Workarea.config.site_name} <noreply@#{Workarea.config.host}>" },
162
177
  description: 'The email address used as the sender of system emails'
163
178
 
164
179
  field 'Email To',
165
180
  type: :string,
166
- default: 'customerservice@example.com',
181
+ default: -> { "#{Workarea.config.site_name} <customerservice@#{Workarea.config.host}>" },
167
182
  description: 'The email address that receives user generated emails, e.g. contact us inquiries'
168
183
 
169
184
  field 'Inquiry Subjects',
@@ -1,4 +1,4 @@
1
- unless Workarea.config.skip_service_connections
1
+ unless Workarea.skip_services?
2
2
  Sidekiq::Cron::Job.create(
3
3
  name: 'Workarea::CleanInventoryTransactions',
4
4
  klass: 'Workarea::CleanInventoryTransactions',
@@ -7,5 +7,5 @@ env_expire_after = ENV['WORKAREA_SESSION_STORE_EXPIRE_AFTER']
7
7
  Rails.application.config.session_store(
8
8
  :cookie_store,
9
9
  key: "_#{Rails.application.class.name.deconstantize.underscore}_session",
10
- expire_after: env_expire_after.present? ? env_expire_after.to_i : 2.weeks
10
+ expire_after: env_expire_after.present? ? env_expire_after.to_i : 30.minutes
11
11
  )
@@ -102,6 +102,9 @@ en:
102
102
  name: "Fulfillment SKU %{id}"
103
103
  inventory_sku:
104
104
  name: "Inventory %{id}"
105
+ jump_to_text: "%{id} (%{count} sellable)"
106
+ inquiry:
107
+ subjects: {}
105
108
  order:
106
109
  name: "Order %{id}"
107
110
  traffic_referrer:
@@ -55,6 +55,19 @@ module Workarea
55
55
  remove_file 'public/favicon.ico'
56
56
  end
57
57
 
58
+ def add_development_mailer_port
59
+ development_port = <<-CODE
60
+
61
+ config.action_mailer.default_url_options = { port: 3000 }
62
+ CODE
63
+
64
+ inject_into_file(
65
+ 'config/environments/development.rb',
66
+ development_port,
67
+ before: /^end/
68
+ )
69
+ end
70
+
58
71
  private
59
72
 
60
73
  def app_name
@@ -4,19 +4,7 @@ Workarea.configure do |config|
4
4
 
5
5
  config.host = {
6
6
  'test' => 'www.example.com',
7
- 'development' => 'localhost:3000',
7
+ 'development' => 'localhost',
8
8
  'production' => 'www.<%= app_name.dasherize %>.com' # TODO
9
9
  }[Rails.env]
10
-
11
- config.email_to = {
12
- 'test' => "#{config.site_name} <customerservice@example.com>",
13
- 'development' => "#{config.site_name} <customerservice@<%= app_name %>.test>",
14
- 'production' => "#{config.site_name} <customerservice@<%= app_name.dasherize %>.com>" # TODO
15
- }[Rails.env]
16
-
17
- config.email_from = {
18
- 'test' => "#{config.site_name} <noreply@example.com>",
19
- 'development' => "#{config.site_name} <noreply@<%= app_name %>.test",
20
- 'production' => "#{config.site_name} <noreply@<%= app_name.dasherize %>.com>" # TODO
21
- }[Rails.env]
22
10
  end
@@ -1,40 +1,10 @@
1
+ require 'workarea/tasks/cache'
2
+
1
3
  namespace :workarea do
2
4
  namespace :cache do
3
5
  desc 'Prime images cache'
4
6
  task prime_images: :environment do
5
- include Rails.application.routes.url_helpers
6
- include Workarea::Storefront::ProductsHelper
7
- include Workarea::Core::Engine.routes.url_helpers
8
-
9
- built_in_jobs = [:thumb, :gif, :jpg, :png, :strip, :convert, :optimized]
10
-
11
- jobs = Dragonfly.app(:workarea).processor_methods.reject do |job|
12
- built_in_jobs.include?(job)
13
- end
14
-
15
- Workarea::Catalog::Product.all.each_by(50) do |product|
16
- product.images.each do |image|
17
- jobs.each do |job|
18
- url = URI.join(
19
- "https://#{Workarea.config.host}",
20
- dynamic_product_image_url(
21
- image.product.slug,
22
- image.option,
23
- image.id,
24
- job,
25
- only_path: true
26
- )
27
- ).to_s
28
-
29
- begin
30
- `curl #{url}`
31
- puts "Downloaded image #{url}"
32
- rescue StandardError => e
33
- puts e.inspect
34
- end
35
- end
36
- end
37
- end
7
+ Workarea::Tasks::Cache.prime_images
38
8
  end
39
9
  end
40
10
  end