huginn_bigcommerce_product_agent 1.9.0 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8bbd3a133d224fea3726d9b0f8b86803957e919e225e983be8ac3a1c7c027312
4
- data.tar.gz: 202e2165d11044d4f44e722aa39e61224b819b344304e73e3c9e888c0a20153a
3
+ metadata.gz: 75db6b836de4031aa33df47c393b0db0b59f99813fa96e98ee15ec10b39b8f91
4
+ data.tar.gz: d5c2bcc87f0fd3f95e489aa651f06297b07225398edfb6dfc8869c976f63caf7
5
5
  SHA512:
6
- metadata.gz: 87204345ec8502a89ac61b5de9bf21f8daf8ab34b3b22cb2563e6a55b90da6239e2dc92d096caf4e9b8e08b2591286ac3f4b8b36a6ba07efecafdbafdf3d93bb
7
- data.tar.gz: abe689235f182acf5c2eb977d9179af9b7431a1537435093a140e54e16b1981bb901fa02ec2bdc8e3469c96fab3d314474f63714caa835d878edcb375223f781
6
+ metadata.gz: 96cc182ce0413cbb9843e4ab435d47ca85540e0025a9cb60579a200f37a3c9c299df0583b20454a83adf23f516e917f75e75942b555245761e21918af79d76b4
7
+ data.tar.gz: 463354a063899f6cb03c253cf65b692ecb2258525e04ce107320f41e03ea6ee4251eda5c8bd4147596a9d6a2fdb90e1e85837b13aa7a05936e7f026d763e22fd
@@ -42,7 +42,11 @@ module BigcommerceProductAgent
42
42
  end
43
43
 
44
44
  def delete(product_id, meta_field_id)
45
- client.delete(uri(product_id: product_id, meta_field_id: meta_field_id))
45
+ begin
46
+ client.delete(uri(product_id: product_id, meta_field_id: meta_field_id))
47
+ rescue Faraday::Error::ClientError => e
48
+ raise e, "\n#{e.message}\nFailed to delete meta_field with id = #{meta_field_id}\nfor product with id = #{product_id}\n", e.backtrace
49
+ end
46
50
  end
47
51
  end
48
52
  end
@@ -55,6 +55,13 @@ module BigcommerceProductAgent
55
55
  map
56
56
  end
57
57
 
58
+ def disable(productId)
59
+ upsert({ id: productId, is_visible: false })
60
+ end
61
+
62
+ def enable(productId)
63
+ upsert({ id: productId, is_visible: true })
64
+ end
58
65
  end
59
66
  end
60
67
  end
@@ -33,8 +33,12 @@ module BigcommerceProductAgent
33
33
  end
34
34
 
35
35
  def create(product_id, option)
36
- response = client.post(uri(product_id: product_id), option.to_json)
37
- return response.body['data']
36
+ begin
37
+ response = client.post(uri(product_id: product_id), option.to_json)
38
+ return response.body['data']
39
+ rescue Faraday::Error::ClientError => e
40
+ raise e, "\n#{e.message}\nFailed to upsert product_option = #{option.to_json}\nfor product with id = #{product_id}\n", e.backtrace
41
+ end
38
42
  end
39
43
  end
40
44
  end
@@ -100,36 +100,48 @@ module Agents
100
100
  end
101
101
  end
102
102
 
103
- # 1. upsert product
104
- # 2. upsert option & option_values
105
- # 3. delete old option_values
103
+ # 1. Upsert the core product (Wrapper Product)
104
+ # - NOTE: This initial upsert intentionally disables the product.
105
+ # Doing so prevents content hiccups with variants particularly
106
+ # for products going off sale.
107
+ # 2. Upsert option & option_values (BigCommerce Variants)
108
+ # 3. Delete old option_values
106
109
  # - NOTE: deleting an option_value also deletes the variant
107
110
  # associated with the option_value
108
- # 4. upsert variants
111
+ # 4. Upsert variants
109
112
  # - NOTE: because deleting option values deletes variants
110
113
  # we need to fetch the variants AFTER deletion has occurred.
111
114
  # - NOTE: by deleting variants in #3 if option_values on an
112
115
  # existing variant changes over time, we're effectively deleting
113
116
  # and then re-adding the variant. Could get weird.
117
+ # 5. Re-enable the updated product
118
+ # - NOTE: If the product no longer has any variants, it will remain
119
+ # disabled as it can no longer be purchased.
114
120
  def handle_variants(event)
115
121
  product = event.payload
116
122
 
117
123
  split = get_mapper(:ProductMapper).split_digital_and_physical(
118
- product,
119
- interpolated['custom_fields_map']
124
+ product,
125
+ interpolated['custom_fields_map']
120
126
  )
121
127
  physical = split[:physical]
122
128
  digital = split[:digital]
123
129
 
130
+ base_sku = product['additionalProperty'].find { |p|
131
+ p['propertyID'] == 'baseSku'
132
+ }['value']
133
+
124
134
  wrapper_skus = {
125
- physical: get_mapper(:ProductMapper).get_wrapper_sku(physical),
126
- digital: get_mapper(:ProductMapper).get_wrapper_sku(digital),
135
+ # Use the provided base_sku if it exists -- otherwise, infer the SKU from the variant list
136
+ physical: base_sku ? "#{base_sku}-P" : get_mapper(:ProductMapper).get_wrapper_sku(physical),
137
+ digital: base_sku ? "#{base_sku}-D" : get_mapper(:ProductMapper).get_wrapper_sku(digital),
127
138
  }
128
139
 
129
140
  bc_products = @product.get_by_skus(
130
141
  wrapper_skus.map {|k,v| v},
131
142
  %w[custom_fields options]
132
143
  )
144
+
133
145
  #save skus
134
146
  digital_skus = []
135
147
  physical_skus = []
@@ -149,15 +161,13 @@ module Agents
149
161
  product['name'] = "#{product['name']} (Digital)"
150
162
  end
151
163
 
152
- # Ignatius Press -- some products have the same name and must be disambiguated.
153
- # ...by adding a list of the product types (hardback, paperback, etc.) to their names
164
+ # BigCommerce requires that product names be unique. In some cases, (like book titles from multiple sources),
165
+ # this may be hard to enforce. In those cases, the product SKUs should still be unique, so we append the SKU
166
+ # to the product title with a `|~` separator. We then set the `page_title` to the original product name so
167
+ # users don't see system values.
154
168
  if boolify(options['should_disambiguate'])
155
169
  product['page_title'] = product['name']
156
- product['name'] += " |~ " + product['model'].map { |m|
157
- m['additionalProperty'].find { |p|
158
- p['propertyID'] == 'option'
159
- }['value']
160
- }.join(", ")
170
+ product['name'] += " |~ " + (is_digital ? wrapper_skus[:digital] : wrapper_skus[:physical])
161
171
  end
162
172
 
163
173
  wrapper_sku = wrapper_skus[type]
@@ -166,6 +176,8 @@ module Agents
166
176
  bc_option = !bc_product.nil? ? bc_product['options'].select {|opt| opt['display_name'] === variant_option_name}.first : nil
167
177
 
168
178
  search_skus = is_digital ? physical_skus : digital_skus
179
+
180
+
169
181
  # ##############################
170
182
  # 1. update wrapper product
171
183
  # ##############################
@@ -204,7 +216,7 @@ module Agents
204
216
  # ##############################
205
217
  variant_skus = get_mapper(:ProductMapper).get_product_skus(product)
206
218
  bc_variants = @product_variant.index(product_id)
207
- mapped_variants = product['model'].map do |variant|
219
+ mapped_variants = product['model'].select { |m| m['isAvailableForPurchase'] }.map do |variant|
208
220
  bc_variant = bc_variants.select {|v| v['sku'] === variant['sku']}.first
209
221
  opt = get_mapper(:ProductMapper).get_option(variant)
210
222
  bc_option_value = bc_option['option_values'].select {|ov| ov['label'] == opt}.first
@@ -221,7 +233,15 @@ module Agents
221
233
  )
222
234
  end
223
235
 
224
- bc_product['variants'] = @variant.upsert(mapped_variants)
236
+
237
+ unless (mapped_variants.blank?)
238
+ bc_product['variants'] = @variant.upsert(mapped_variants)
239
+
240
+ # ##############################
241
+ # 5. Re-enable the updated product
242
+ # ##############################
243
+ @product.enable(product_id)
244
+ end
225
245
  end
226
246
 
227
247
  bc_physical = bc_products[wrapper_skus[:physical]]
@@ -329,6 +349,11 @@ module Agents
329
349
  clean_up_modifier_values(modifier_updates[:delete])
330
350
  meta_fields = update_meta_fields(meta_fields_upsert, meta_fields_delete)
331
351
 
352
+ if product['is_visible']
353
+ # If the product should be enabled, re-enable it
354
+ @product.enable(result[:product]['id'])
355
+ end
356
+
332
357
  product['meta_fields'] = meta_fields
333
358
  product['modifiers'] = modifier_updates[:upsert]
334
359
  create_event payload: {
@@ -385,6 +410,12 @@ module Agents
385
410
  is_digital,
386
411
  )
387
412
 
413
+ payload[:is_visible] = false
414
+ # NOTE: Products are intentionally upserted as disabled so that users
415
+ # don't see an incomplete listing (particularly when leveraging variants)
416
+ # The variant and option_list methods will re-enable products after the
417
+ # upsert is complete.
418
+
388
419
  bc_product = @product.upsert(payload, {
389
420
  include: %w[custom_fields variants options].join(',')
390
421
  })
@@ -427,7 +458,9 @@ module Agents
427
458
  end
428
459
 
429
460
  delete_fields.each do |field|
430
- @meta_field.delete(field[:resource_id], field[:id])
461
+ if field[:resource_id] && field[:id]
462
+ @meta_field.delete(field[:resource_id], field[:id])
463
+ end
431
464
  end
432
465
 
433
466
  meta_fields
@@ -63,7 +63,7 @@ module BigcommerceProductAgent
63
63
  variant = self.get_variant_by_sku(sku, product)
64
64
  payload = self.map(product, variant, additional_data, is_digital, sku)
65
65
  payload[:id] = product_id unless product_id.nil?
66
- payload[:sku] = self.get_wrapper_sku(product)
66
+ payload[:sku] = sku
67
67
 
68
68
  return payload
69
69
  end
@@ -76,7 +76,9 @@ module BigcommerceProductAgent
76
76
  map = {}
77
77
 
78
78
  product['model'].each do |model|
79
- map[model['sku']] = self.get_option(model)
79
+ if model['isAvailableForPurchase']
80
+ map[model['sku']] = self.get_option(model)
81
+ end
80
82
  end
81
83
 
82
84
  return map
@@ -109,6 +111,7 @@ module BigcommerceProductAgent
109
111
  clone = Marshal.load(Marshal.dump(product))
110
112
  clone['model'] = digitals
111
113
  clone['sku'] = clone['model'].select {|m| m['isDefault'] = true}.first['sku']
114
+ clone['categories'] = self.get_categories_after_split(clone, clone['categories'])
112
115
  self.merge_additional_properties(clone, field_map)
113
116
  result[:digital] = clone
114
117
  end
@@ -119,6 +122,7 @@ module BigcommerceProductAgent
119
122
  clone = Marshal.load(Marshal.dump(product))
120
123
  clone['model'] = physicals
121
124
  clone['sku'] = clone['model'].select {|m| m['isDefault'] = true}.first['sku']
125
+ clone['categories'] = self.get_categories_after_split(clone, clone['categories'])
122
126
  self.merge_additional_properties(clone, field_map)
123
127
  result[:physical] = clone
124
128
  end
@@ -140,6 +144,20 @@ module BigcommerceProductAgent
140
144
  return categories
141
145
  end
142
146
 
147
+ def self.get_categories_after_split(product, categories)
148
+ # categories equal shared categories between digital and physical products
149
+
150
+ if product['model'].select { |c| c['categories']}
151
+ product['model'].each do |variant|
152
+ variant['categories'].map do |category|
153
+ categories.push(category)
154
+ end
155
+ end
156
+ end
157
+
158
+ return categories
159
+ end
160
+
143
161
  def self.get_option(variant)
144
162
  if variant['encodingFormat']
145
163
  return variant['encodingFormat']
@@ -9,7 +9,7 @@ module BigcommerceProductAgent
9
9
  price: variant['offers'] && variant['offers'][0] ? variant['offers'][0]['price'] : '0',
10
10
  cost_price: nil,
11
11
  sale_price: nil,
12
- retail_price: nil,
12
+ retail_price: variant['offers'] && variant['offers'][0] ? variant['offers'][0]['price'] : nil,
13
13
  weight: variant['weight'] ? variant['weight']['value'] : '0',
14
14
  width: variant['width'] ? variant['width']['value'] : '0',
15
15
  depth: variant['depth'] ? variant['depth']['value'] : '0',
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: huginn_bigcommerce_product_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.0
4
+ version: 1.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacob Spizziri
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-15 00:00:00.000000000 Z
11
+ date: 2020-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler