shopify_transporter 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +23 -0
- data/LICENSE +20 -0
- data/README.md +290 -0
- data/RELEASING +16 -0
- data/Rakefile +11 -0
- data/exe/shopify_transporter +47 -0
- data/lib/shopify_transporter/generators/base_group.rb +25 -0
- data/lib/shopify_transporter/generators/generate.rb +64 -0
- data/lib/shopify_transporter/generators/new.rb +23 -0
- data/lib/shopify_transporter/generators.rb +3 -0
- data/lib/shopify_transporter/pipeline/all_platforms/metafields.rb +61 -0
- data/lib/shopify_transporter/pipeline/magento/customer/addresses_attribute.rb +60 -0
- data/lib/shopify_transporter/pipeline/magento/customer/top_level_attributes.rb +47 -0
- data/lib/shopify_transporter/pipeline/magento/order/addresses_attribute.rb +46 -0
- data/lib/shopify_transporter/pipeline/magento/order/line_items.rb +55 -0
- data/lib/shopify_transporter/pipeline/magento/order/top_level_attributes.rb +39 -0
- data/lib/shopify_transporter/pipeline/magento/product/top_level_attributes.rb +36 -0
- data/lib/shopify_transporter/pipeline/stage.rb +16 -0
- data/lib/shopify_transporter/pipeline.rb +4 -0
- data/lib/shopify_transporter/record_builder.rb +51 -0
- data/lib/shopify_transporter/shopify/attributes_accumulator.rb +43 -0
- data/lib/shopify_transporter/shopify/attributes_helpers.rb +53 -0
- data/lib/shopify_transporter/shopify/customer.rb +79 -0
- data/lib/shopify_transporter/shopify/order.rb +107 -0
- data/lib/shopify_transporter/shopify/product.rb +118 -0
- data/lib/shopify_transporter/shopify/record.rb +60 -0
- data/lib/shopify_transporter/shopify.rb +7 -0
- data/lib/shopify_transporter/version.rb +4 -0
- data/lib/shopify_transporter.rb +247 -0
- data/lib/tasks/factory_bot.rake +9 -0
- data/lib/templates/custom_stage.tt +31 -0
- data/lib/templates/gemfile.tt +5 -0
- data/lib/templates/magento/config.tt +23 -0
- data/shopify_transporter.gemspec +36 -0
- metadata +152 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'shopify_transporter/pipeline/stage'
|
3
|
+
require 'shopify_transporter/shopify'
|
4
|
+
|
5
|
+
module ShopifyTransporter
|
6
|
+
module Pipeline
|
7
|
+
module Magento
|
8
|
+
module Customer
|
9
|
+
class TopLevelAttributes < Pipeline::Stage
|
10
|
+
def convert(hash, record)
|
11
|
+
accumulator = TopLevelAttributesAccumulator.new(record)
|
12
|
+
accumulator.accumulate(hash)
|
13
|
+
record.merge!(accumulator.output)
|
14
|
+
end
|
15
|
+
|
16
|
+
class TopLevelAttributesAccumulator < Shopify::AttributesAccumulator
|
17
|
+
COLUMN_MAPPING = {
|
18
|
+
'firstname' => 'first_name',
|
19
|
+
'lastname' => 'last_name',
|
20
|
+
'email' => 'email',
|
21
|
+
}
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def input_applies?(_input)
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def attributes_from(input)
|
30
|
+
attributes = COLUMN_MAPPING.each_with_object({}) do |(key, value), obj|
|
31
|
+
obj[value] = input[key] if input[key]
|
32
|
+
end
|
33
|
+
attributes['accepts_marketing'] = accepts_marketing(input)
|
34
|
+
attributes
|
35
|
+
end
|
36
|
+
|
37
|
+
def accepts_marketing(input)
|
38
|
+
value = input['is_subscribed']
|
39
|
+
return unless value.present?
|
40
|
+
(value == '1').to_s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'shopify_transporter/pipeline/stage'
|
3
|
+
require 'shopify_transporter/shopify'
|
4
|
+
module ShopifyTransporter
|
5
|
+
module Pipeline
|
6
|
+
module Magento
|
7
|
+
module Order
|
8
|
+
class AddressesAttribute < Pipeline::Stage
|
9
|
+
BILLING_PREFIX = 'billing_'
|
10
|
+
SHIPPING_PREFIX = 'shipping_'
|
11
|
+
|
12
|
+
def convert(input, record)
|
13
|
+
record['billing_address'] = address_attributes(input, BILLING_PREFIX)
|
14
|
+
record['shipping_address'] = address_attributes(input, SHIPPING_PREFIX)
|
15
|
+
record
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def get_nested_address(input, prefix)
|
21
|
+
items = input['items']
|
22
|
+
result = items && items['result']
|
23
|
+
address = result && result["#{prefix}address"]
|
24
|
+
address || []
|
25
|
+
end
|
26
|
+
|
27
|
+
def address_attributes(input, prefix)
|
28
|
+
address_attrs = get_nested_address(input, prefix)
|
29
|
+
return address_attrs unless address_attrs.present?
|
30
|
+
|
31
|
+
{
|
32
|
+
first_name: address_attrs['firstname'],
|
33
|
+
last_name: address_attrs['lastname'],
|
34
|
+
phone: address_attrs['telephone'],
|
35
|
+
address1: address_attrs['street'],
|
36
|
+
city: address_attrs['city'],
|
37
|
+
province_code: address_attrs['region'],
|
38
|
+
zip: address_attrs['postcode'],
|
39
|
+
country_code: address_attrs['country_id'],
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'shopify_transporter/pipeline/stage'
|
3
|
+
require 'shopify_transporter/shopify'
|
4
|
+
|
5
|
+
module ShopifyTransporter
|
6
|
+
module Pipeline
|
7
|
+
module Magento
|
8
|
+
module Order
|
9
|
+
class LineItems < Pipeline::Stage
|
10
|
+
def convert(input, record)
|
11
|
+
record.merge!(
|
12
|
+
{
|
13
|
+
line_items: line_items(input),
|
14
|
+
}.stringify_keys
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def line_items(input)
|
21
|
+
line_items = line_items_array(input)
|
22
|
+
line_items.map { |item| line_item(item) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def line_items_array(input)
|
26
|
+
items_1 = input['items']
|
27
|
+
result = items_1 && items_1['result']
|
28
|
+
items_2 = result && result['items']
|
29
|
+
item = items_2 && items_2['item']
|
30
|
+
return [] unless item
|
31
|
+
item.is_a?(Array) ? item : [item]
|
32
|
+
end
|
33
|
+
|
34
|
+
def line_item(item)
|
35
|
+
{
|
36
|
+
quantity: item['qty_ordered'],
|
37
|
+
sku: item['sku'],
|
38
|
+
name: item['name'],
|
39
|
+
price: item['price'],
|
40
|
+
tax_lines: tax_lines(item),
|
41
|
+
}.stringify_keys
|
42
|
+
end
|
43
|
+
|
44
|
+
def tax_lines(item)
|
45
|
+
{
|
46
|
+
title: 'Tax',
|
47
|
+
price: item['tax_amount'],
|
48
|
+
rate: item['tax_percent'],
|
49
|
+
}.stringify_keys
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'shopify_transporter/pipeline/stage'
|
3
|
+
require 'shopify_transporter/shopify'
|
4
|
+
|
5
|
+
module ShopifyTransporter
|
6
|
+
module Pipeline
|
7
|
+
module Magento
|
8
|
+
module Order
|
9
|
+
class TopLevelAttributes < Pipeline::Stage
|
10
|
+
def convert(hash, record)
|
11
|
+
record.merge!(
|
12
|
+
{
|
13
|
+
name: hash['increment_id'],
|
14
|
+
email: hash['customer_email'],
|
15
|
+
processed_at: hash['created_at'],
|
16
|
+
subtotal_price: hash['subtotal'],
|
17
|
+
total_tax: hash['tax_amount'],
|
18
|
+
total_price: hash['grand_total'],
|
19
|
+
}.stringify_keys
|
20
|
+
)
|
21
|
+
customer = build_customer(hash)
|
22
|
+
record['customer'] = customer unless customer.empty?
|
23
|
+
record
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def build_customer(hash)
|
29
|
+
{
|
30
|
+
email: hash['customer_email'],
|
31
|
+
first_name: hash['customer_firstname'],
|
32
|
+
last_name: hash['customer_lastname'],
|
33
|
+
}.stringify_keys
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'shopify_transporter/pipeline/stage'
|
3
|
+
require 'shopify_transporter/shopify'
|
4
|
+
|
5
|
+
module ShopifyTransporter
|
6
|
+
module Pipeline
|
7
|
+
module Magento
|
8
|
+
module Product
|
9
|
+
class TopLevelAttributes < Pipeline::Stage
|
10
|
+
def convert(hash, record)
|
11
|
+
accumulator = TopLevelAttributesAccumulator.new(record)
|
12
|
+
accumulator.accumulate(hash)
|
13
|
+
end
|
14
|
+
|
15
|
+
class TopLevelAttributesAccumulator < Shopify::AttributesAccumulator
|
16
|
+
COLUMN_MAPPING = {
|
17
|
+
'sku' => 'sku',
|
18
|
+
'name' => 'title',
|
19
|
+
'description' => 'body_html',
|
20
|
+
}
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def input_applies?(_input)
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def attributes_from(input)
|
29
|
+
map_from_key_to_val(COLUMN_MAPPING, input)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ShopifyTransporter
|
3
|
+
module Pipeline
|
4
|
+
class Stage
|
5
|
+
attr_reader :params
|
6
|
+
|
7
|
+
def initialize(params = nil)
|
8
|
+
@params = params
|
9
|
+
end
|
10
|
+
|
11
|
+
def convert(_hash, _record)
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyTransporter
|
4
|
+
class KeyMissing < StandardError
|
5
|
+
def initialize(key_name)
|
6
|
+
super("cannot process entry. Required field not found: '#{key_name}'")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class RequiredKeyMissing < KeyMissing; end
|
11
|
+
|
12
|
+
class MissingParentObject < KeyMissing; end
|
13
|
+
|
14
|
+
class RecordBuilder
|
15
|
+
attr_accessor :instances
|
16
|
+
|
17
|
+
def initialize(key_name, key_required)
|
18
|
+
@instances = {}
|
19
|
+
@key_name = key_name
|
20
|
+
@key_required = key_required
|
21
|
+
end
|
22
|
+
|
23
|
+
def build(input)
|
24
|
+
validate_related(input)
|
25
|
+
|
26
|
+
if key_of(input).nil?
|
27
|
+
yield @last_record
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
yield record_from(input)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def validate_related(input)
|
37
|
+
raise MissingParentObject, @key_name if key_of(input).nil? && @last_record.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
def record_from(input)
|
41
|
+
record = @instances[key_of(input)] ||= {}
|
42
|
+
@last_record = record
|
43
|
+
end
|
44
|
+
|
45
|
+
def key_of(input)
|
46
|
+
record_key = input[@key_name]
|
47
|
+
raise RequiredKeyMissing, @key_name if @key_required && record_key.nil?
|
48
|
+
record_key
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'attributes_helpers'
|
3
|
+
|
4
|
+
module ShopifyTransporter
|
5
|
+
module Shopify
|
6
|
+
class AttributesAccumulator
|
7
|
+
include AttributesHelpers
|
8
|
+
|
9
|
+
attr_reader :output
|
10
|
+
|
11
|
+
def initialize(initial_value)
|
12
|
+
@output = initial_value
|
13
|
+
end
|
14
|
+
|
15
|
+
def accumulate(input)
|
16
|
+
return @output unless input_applies?(input)
|
17
|
+
attributes = attributes_from(input)
|
18
|
+
accumulate_attributes(attributes)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def input_applies?(_input)
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
|
27
|
+
def attributes_from(_input)
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
def accumulate_attributes(attributes)
|
32
|
+
case @output
|
33
|
+
when Array
|
34
|
+
@output << attributes
|
35
|
+
when Hash
|
36
|
+
@output.merge!(attributes)
|
37
|
+
else
|
38
|
+
raise 'Unexpected initial value. Initial value must be an array or a hash.'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "active_support/core_ext/string"
|
3
|
+
module ShopifyTransporter
|
4
|
+
module Shopify
|
5
|
+
module AttributesHelpers
|
6
|
+
def attributes_present?(row, *attributes)
|
7
|
+
row.slice(*attributes).any? { |_, v| v.present? }
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_prefix(prefix, *attributes)
|
11
|
+
[*attributes].map { |attribute| "#{prefix}#{attribute}".to_sym }
|
12
|
+
end
|
13
|
+
|
14
|
+
def drop_prefix(row, prefix)
|
15
|
+
row.transform_keys { |key| key.to_s.remove(prefix).to_sym }
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete_empty_attributes(row)
|
19
|
+
row.delete_if { |_, v| v.respond_to?(:empty?) && v.empty? }
|
20
|
+
end
|
21
|
+
|
22
|
+
def normalize_keys(row)
|
23
|
+
row.compact.transform_keys { |key| normalize_string(key).to_sym }
|
24
|
+
end
|
25
|
+
|
26
|
+
def normalize_string(str)
|
27
|
+
str.to_s.parameterize.underscore
|
28
|
+
end
|
29
|
+
|
30
|
+
def rename_fields(row, field_map)
|
31
|
+
return row unless field_map.present?
|
32
|
+
row = row.dup
|
33
|
+
field_map.each { |from, to| row[to] = row.delete from if row[from].present? }
|
34
|
+
row
|
35
|
+
end
|
36
|
+
|
37
|
+
def map_from_key_to_val(mapper, input)
|
38
|
+
mapper.each_with_object({}) do |(key, value), attributes|
|
39
|
+
attributes[value] = input[key] if input[key]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def shopify_metafield_hash(key:, value:, value_type: 'string', namespace:)
|
44
|
+
{
|
45
|
+
'key' => key,
|
46
|
+
'value' => value,
|
47
|
+
'value_type' => value_type,
|
48
|
+
'namespace' => namespace,
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'record'
|
3
|
+
require 'active_support/inflector'
|
4
|
+
require 'csv'
|
5
|
+
|
6
|
+
module ShopifyTransporter
|
7
|
+
module Shopify
|
8
|
+
class Customer < Record
|
9
|
+
class << self
|
10
|
+
def header
|
11
|
+
columns.map(&:titleize).to_csv
|
12
|
+
end
|
13
|
+
|
14
|
+
def keys
|
15
|
+
%w(email phone).freeze
|
16
|
+
end
|
17
|
+
|
18
|
+
def columns
|
19
|
+
%w(
|
20
|
+
first_name last_name email phone accepts_marketing tags
|
21
|
+
note tax_exempt company address1 address2 city province
|
22
|
+
province_code zip country country_code metafield_namespace
|
23
|
+
metafield_key metafield_value metafield_value_type
|
24
|
+
).freeze
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_csv
|
29
|
+
CSV.generate do |csv|
|
30
|
+
csv << top_level_row_values
|
31
|
+
address_row_values.each { |row| csv << row }
|
32
|
+
metafield_row_values.each { |row| csv << row }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
TOP_LEVEL_ATTRIBUTES = %w(
|
39
|
+
first_name last_name email phone
|
40
|
+
accepts_marketing
|
41
|
+
tags
|
42
|
+
note
|
43
|
+
tax_exempt
|
44
|
+
).freeze
|
45
|
+
|
46
|
+
ADDRESS_ATTRIBUTES = %w(
|
47
|
+
first_name last_name phone
|
48
|
+
company
|
49
|
+
address1 address2
|
50
|
+
city
|
51
|
+
province province_code zip
|
52
|
+
country country_code
|
53
|
+
).freeze
|
54
|
+
|
55
|
+
SHARED_ADDRESS_ATTRIBUTES = %w(
|
56
|
+
first_name last_name phone
|
57
|
+
).freeze
|
58
|
+
|
59
|
+
def top_level_row_values
|
60
|
+
base_hash.merge(record_hash.slice(*TOP_LEVEL_ATTRIBUTES)).values
|
61
|
+
end
|
62
|
+
|
63
|
+
def address_row_values
|
64
|
+
return [] unless record_hash['addresses']
|
65
|
+
record_hash['addresses'].map do |address_hash|
|
66
|
+
address = address_hash.slice(*ADDRESS_ATTRIBUTES)
|
67
|
+
populate_missing_address_attributes!(address)
|
68
|
+
row_values_from(address) if self.class.has_values?(address)
|
69
|
+
end.compact
|
70
|
+
end
|
71
|
+
|
72
|
+
def populate_missing_address_attributes!(address)
|
73
|
+
SHARED_ADDRESS_ATTRIBUTES.each do |shared_attribute|
|
74
|
+
address[shared_attribute] = record_hash[shared_attribute] if address[shared_attribute].nil?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_support/inflector'
|
3
|
+
require 'csv'
|
4
|
+
require_relative 'attributes_helpers'
|
5
|
+
require_relative 'record'
|
6
|
+
|
7
|
+
module ShopifyTransporter
|
8
|
+
module Shopify
|
9
|
+
class Order < Record
|
10
|
+
class << self
|
11
|
+
include AttributesHelpers
|
12
|
+
|
13
|
+
def header
|
14
|
+
[
|
15
|
+
'Name', 'Email', 'Financial Status', 'Fulfillment Status', 'Currency',
|
16
|
+
'Buyer Accepts Marketing', 'Cancel Reason', 'Cancelled At', 'Closed At', 'Tags', 'Note',
|
17
|
+
'Phone', 'Referring Site', 'Processed At', 'Source name', 'Total discounts', 'Total weight',
|
18
|
+
'Total Tax', 'Shipping Company', 'Shipping Name', 'Shipping Phone', 'Shipping First Name',
|
19
|
+
'Shipping Last Name', 'Shipping Address1', 'Shipping Address2', 'Shipping City',
|
20
|
+
'Shipping Province', 'Shipping Province Code', 'Shipping Zip', 'Shipping Country',
|
21
|
+
'Shipping Country Code', 'Billing Company', 'Billing Name', 'Billing Phone',
|
22
|
+
'Billing First Name', 'Billing Last Name', 'Billing Address1', 'Billing Address2',
|
23
|
+
'Billing City', 'Billing Province', 'Billing Province Code', 'Billing Zip',
|
24
|
+
'Billing Country', 'Billing Country Code', 'Lineitem name', 'Lineitem quantity',
|
25
|
+
'Lineitem price', 'Lineitem discount', 'Lineitem compare at price', 'Lineitem sku',
|
26
|
+
'Lineitem requires shipping', 'Lineitem taxable', 'Lineitem fulfillment status',
|
27
|
+
'Tax 1 Title', 'Tax 1 Price', 'Tax 1 Rate', 'Tax 2 Title', 'Tax 2 Price', 'Tax 2 Rate',
|
28
|
+
'Tax 3 Title', 'Tax 3 Price', 'Tax 3 Rate', 'Metafield Namespace', 'Metafield Key',
|
29
|
+
'Metafield Value', 'Metafield Value Type'
|
30
|
+
].to_csv
|
31
|
+
end
|
32
|
+
|
33
|
+
def keys
|
34
|
+
%w(name).freeze
|
35
|
+
end
|
36
|
+
|
37
|
+
def columns
|
38
|
+
@columns ||= header.split(',').map do |header_column|
|
39
|
+
normalize_string(header_column)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_csv
|
45
|
+
CSV.generate do |csv|
|
46
|
+
csv << top_level_row_values
|
47
|
+
line_item_row_values.each { |row| csv << row }
|
48
|
+
metafield_row_values.each { |row| csv << row }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
TOP_LEVEL_ATTRIBUTES = %w(
|
55
|
+
name email financial_status fulfillment_status currency
|
56
|
+
buyer_accepts_marketing cancel_reason cancelled_at closed_at tags note
|
57
|
+
phone referring_site processed_at source_name total_discounts total_weight total_tax
|
58
|
+
)
|
59
|
+
|
60
|
+
ADDRESS_ATTRIBUTES = %w(
|
61
|
+
company name phone first_name last_name address1 address2 city province province_code zip country country_code
|
62
|
+
)
|
63
|
+
|
64
|
+
LINE_ITEM_PREFIX = "lineitem_"
|
65
|
+
|
66
|
+
LINE_ITEM_ATTRIBUTES = %w(
|
67
|
+
name quantity price discount compare_at_price sku requires_shipping taxable fulfillment_status
|
68
|
+
)
|
69
|
+
|
70
|
+
def address_hash_for(address_hash, prefix)
|
71
|
+
return {} if address_hash.blank?
|
72
|
+
|
73
|
+
address_hash.transform_keys { |k| "#{prefix}#{k}" }
|
74
|
+
end
|
75
|
+
|
76
|
+
def top_level_row_values
|
77
|
+
top_level_hash = record_hash
|
78
|
+
.slice(*TOP_LEVEL_ATTRIBUTES)
|
79
|
+
.merge(address_hash_for(record_hash['billing_address'], 'billing_'))
|
80
|
+
.merge(address_hash_for(record_hash['shipping_address'], 'shipping_'))
|
81
|
+
self.class.has_values?(top_level_hash) ? row_values_from(top_level_hash) : nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def tax_line_hash(line_item_hash)
|
85
|
+
return {} if line_item_hash['tax_lines'].blank?
|
86
|
+
|
87
|
+
line_item_hash['tax_lines'].each_with_index.map do |tax_line, index|
|
88
|
+
tax_line.each_with_object({}) do |(key, val), hash|
|
89
|
+
hash["tax_#{index + 1}_#{key}"] = val
|
90
|
+
end
|
91
|
+
end.reduce({}, :merge)
|
92
|
+
end
|
93
|
+
|
94
|
+
def line_item_row_values
|
95
|
+
return [] unless record_hash['line_items']
|
96
|
+
|
97
|
+
record_hash['line_items'].map do |line_item_hash|
|
98
|
+
line_item = line_item_hash.slice(*LINE_ITEM_ATTRIBUTES)
|
99
|
+
.transform_keys! { |k| "#{LINE_ITEM_PREFIX}#{k}" }
|
100
|
+
.merge(tax_line_hash(line_item_hash))
|
101
|
+
|
102
|
+
row_values_from(line_item) if self.class.has_values?(line_item)
|
103
|
+
end.compact
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|