solidus_importer 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.readme/import-products-1.gif +0 -0
- data/.readme/import-products-2.gif +0 -0
- data/README.md +9 -1
- data/Rakefile +4 -0
- data/app/models/solidus_importer/order_updater.rb +6 -0
- data/app/models/solidus_importer/spree_core_importer_order.rb +50 -0
- data/bin/rails-sandbox +1 -1
- data/bin/sandbox +2 -0
- data/lib/solidus_importer.rb +1 -0
- data/lib/solidus_importer/base_importer.rb +9 -2
- data/lib/solidus_importer/configuration.rb +7 -2
- data/lib/solidus_importer/order_importer.rb +39 -0
- data/lib/solidus_importer/process_import.rb +5 -1
- data/lib/solidus_importer/process_row.rb +3 -1
- data/lib/solidus_importer/processors/bill_address.rb +52 -0
- data/lib/solidus_importer/processors/customer.rb +10 -7
- data/lib/solidus_importer/processors/customer_address.rb +44 -0
- data/lib/solidus_importer/processors/line_item.rb +33 -0
- data/lib/solidus_importer/processors/order.rb +41 -12
- data/lib/solidus_importer/processors/payment.rb +56 -0
- data/lib/solidus_importer/processors/ship_address.rb +52 -0
- data/lib/solidus_importer/processors/shipment.rb +86 -0
- data/lib/solidus_importer/version.rb +1 -1
- data/spec/features/solidus_importer/import_spec.rb +68 -4
- data/spec/features/solidus_importer/processors_spec.rb +14 -11
- data/spec/fixtures/solidus_importer/customers.csv +5 -3
- data/spec/fixtures/solidus_importer/orders.csv +2 -2
- data/spec/lib/solidus_importer/base_importer_spec.rb +10 -0
- data/spec/lib/solidus_importer/order_importer_spec.rb +63 -0
- data/spec/lib/solidus_importer/processors/bill_address_spec.rb +33 -0
- data/spec/lib/solidus_importer/processors/customer_address_spec.rb +67 -0
- data/spec/lib/solidus_importer/processors/customer_spec.rb +14 -1
- data/spec/lib/solidus_importer/processors/line_item_spec.rb +22 -0
- data/spec/lib/solidus_importer/processors/order_spec.rb +2 -23
- data/spec/lib/solidus_importer/processors/payment_spec.rb +31 -0
- data/spec/lib/solidus_importer/processors/ship_address_spec.rb +33 -0
- data/spec/lib/solidus_importer/processors/shipment_spec.rb +22 -0
- metadata +31 -9
- data/lib/solidus_importer/processors/address.rb +0 -47
- data/spec/lib/solidus_importer/processors/address_spec.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06ff1cdd3f709eebef5325d77c7c4b43d4ea41de11c11365e0490f6f52214d2f
|
4
|
+
data.tar.gz: a5f0868efe1a6c62b7f048eba8aa81af91dd98364af367b8a44811acbed9440f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a344000551b4f69f1795d7b9c114621a523a40b50a0fa5ca2ec0a3122b61fd69642f5ee6aa3a716f6e6cd2a7198e1a426f58012ab87f99118f843bb7c1c839e3
|
7
|
+
data.tar.gz: a181da584ecc1fc043a9ad5c877fe8ca6e7369a5c3a9f5cfd28e8fb71d70a0c4a9702f00b2172c92f2b6c1630104af1943c864ae21747b3607e752192c284727
|
Binary file
|
Binary file
|
data/README.md
CHANGED
@@ -20,6 +20,14 @@ bundle exec rails g solidus_importer:install
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
+
The imports can be fully managed from the backend UI, following progress (image processing can take a few seconds for each image).
|
24
|
+
|
25
|
+

|
26
|
+
|
27
|
+

|
28
|
+
|
29
|
+
### From the console
|
30
|
+
|
23
31
|
Sample code to import some products:
|
24
32
|
|
25
33
|
```ruby
|
@@ -84,7 +92,7 @@ SolidusImporter::Config.solidus_importer[:customers][:processors].map! do |proce
|
|
84
92
|
if processor == 'SolidusImporter::Processors::Log'
|
85
93
|
'CustomLoggerProcessor'
|
86
94
|
else
|
87
|
-
|
95
|
+
processor
|
88
96
|
end
|
89
97
|
end
|
90
98
|
```
|
data/Rakefile
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
module SolidusImporter
|
2
|
+
class SpreeCoreImporterOrder < Spree::Core::Importer::Order
|
3
|
+
def self.import(user, params)
|
4
|
+
params = params.to_h
|
5
|
+
ActiveRecord::Base.transaction do
|
6
|
+
ensure_country_id_from_params params[:ship_address_attributes]
|
7
|
+
ensure_state_id_from_params params[:ship_address_attributes]
|
8
|
+
ensure_country_id_from_params params[:bill_address_attributes]
|
9
|
+
ensure_state_id_from_params params[:bill_address_attributes]
|
10
|
+
|
11
|
+
create_params = params.slice :currency
|
12
|
+
order = Spree::Order.create! create_params
|
13
|
+
order.store ||= Spree::Store.default
|
14
|
+
order.associate_user!(user)
|
15
|
+
order.save!
|
16
|
+
|
17
|
+
shipments_attrs = params.delete(:shipments_attributes)
|
18
|
+
|
19
|
+
create_line_items_from_params(params.delete(:line_items_attributes), order)
|
20
|
+
create_shipments_from_params(shipments_attrs, order)
|
21
|
+
create_adjustments_from_params(params.delete(:adjustments_attributes), order)
|
22
|
+
create_payments_from_params(params.delete(:payments_attributes), order)
|
23
|
+
|
24
|
+
params.delete(:user_id) unless user.try(:has_spree_role?, "admin") && params.key?(:user_id)
|
25
|
+
|
26
|
+
completed_at = params.delete(:completed_at)
|
27
|
+
|
28
|
+
order.update!(params)
|
29
|
+
|
30
|
+
order.create_proposed_shipments unless shipments_attrs.present?
|
31
|
+
|
32
|
+
if completed_at
|
33
|
+
order.completed_at = completed_at
|
34
|
+
order.state = 'complete'
|
35
|
+
order.save!
|
36
|
+
end
|
37
|
+
|
38
|
+
# Really ensure that the order totals & states are correct
|
39
|
+
updater = SolidusImporter::OrderUpdater.new(order)
|
40
|
+
updater.update
|
41
|
+
if shipments_attrs.present?
|
42
|
+
order.shipments.each_with_index do |shipment, index|
|
43
|
+
shipment.update_columns(cost: shipments_attrs[index][:cost].to_f) if shipments_attrs[index][:cost].present?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
order.reload
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/bin/rails-sandbox
CHANGED
@@ -5,7 +5,7 @@ app_root = 'sandbox'
|
|
5
5
|
unless File.exist? "#{app_root}/bin/rails"
|
6
6
|
warn 'Creating the sandbox app...'
|
7
7
|
Dir.chdir "#{__dir__}/.." do
|
8
|
-
system "#{__dir__}/sandbox" or begin
|
8
|
+
system "#{__dir__}/sandbox" or begin
|
9
9
|
warn 'Automatic creation of the sandbox app failed'
|
10
10
|
exit 1
|
11
11
|
end
|
data/bin/sandbox
CHANGED
@@ -72,9 +72,11 @@ unbundled bundle exec rails generate spree:install \
|
|
72
72
|
--user_class=Spree::User \
|
73
73
|
--enforce_available_locales=true \
|
74
74
|
--with-authentication=false \
|
75
|
+
--payment-method=none
|
75
76
|
$@
|
76
77
|
|
77
78
|
unbundled bundle exec rails generate solidus:auth:install
|
79
|
+
unbundled bundle exec rails generate ${extension_name}:install
|
78
80
|
|
79
81
|
echo
|
80
82
|
echo "🚀 Sandbox app successfully created for $extension_name!"
|
data/lib/solidus_importer.rb
CHANGED
@@ -6,6 +6,7 @@ require 'solidus_support'
|
|
6
6
|
require 'solidus_importer/version'
|
7
7
|
require 'solidus_importer/exception'
|
8
8
|
require 'solidus_importer/base_importer'
|
9
|
+
require 'solidus_importer/order_importer'
|
9
10
|
|
10
11
|
require 'solidus_importer/processors/base'
|
11
12
|
processors = File.join(__dir__, 'solidus_importer/processors/*.rb')
|
@@ -20,7 +20,14 @@ module SolidusImporter
|
|
20
20
|
end
|
21
21
|
|
22
22
|
##
|
23
|
-
# Defines a method called after the import process is
|
24
|
-
def after_import(
|
23
|
+
# Defines a method called after the import process is finished
|
24
|
+
def after_import(context)
|
25
|
+
context
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Defines a method called after the import of each row
|
30
|
+
def handle_row_import(_ending_row_context)
|
31
|
+
end
|
25
32
|
end
|
26
33
|
end
|
@@ -6,15 +6,20 @@ module SolidusImporter
|
|
6
6
|
customers: {
|
7
7
|
importer: SolidusImporter::BaseImporter,
|
8
8
|
processors: [
|
9
|
-
SolidusImporter::Processors::Address,
|
10
9
|
SolidusImporter::Processors::Customer,
|
10
|
+
SolidusImporter::Processors::CustomerAddress,
|
11
11
|
SolidusImporter::Processors::Log
|
12
12
|
]
|
13
13
|
},
|
14
14
|
orders: {
|
15
|
-
importer: SolidusImporter::
|
15
|
+
importer: SolidusImporter::OrderImporter,
|
16
16
|
processors: [
|
17
17
|
SolidusImporter::Processors::Order,
|
18
|
+
SolidusImporter::Processors::BillAddress,
|
19
|
+
SolidusImporter::Processors::ShipAddress,
|
20
|
+
SolidusImporter::Processors::LineItem,
|
21
|
+
SolidusImporter::Processors::Shipment,
|
22
|
+
SolidusImporter::Processors::Payment,
|
18
23
|
SolidusImporter::Processors::Log
|
19
24
|
]
|
20
25
|
},
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module SolidusImporter
|
6
|
+
class OrderImporter < BaseImporter
|
7
|
+
attr_accessor :orders
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
super
|
11
|
+
self.orders = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle_row_import(context)
|
15
|
+
number = context.dig(:order, :number)
|
16
|
+
|
17
|
+
return unless number
|
18
|
+
|
19
|
+
orders[number] ||= {}
|
20
|
+
|
21
|
+
order_params = context[:order].to_h.reject { |_k, v| v.blank? }
|
22
|
+
payments_attributes = order_params[:payments_attributes]
|
23
|
+
orders[number][:payments_attributes] ||= []
|
24
|
+
orders[number][:payments_attributes] << payments_attributes if payments_attributes.present?
|
25
|
+
orders[number].merge!(order_params)
|
26
|
+
end
|
27
|
+
|
28
|
+
def after_import(context)
|
29
|
+
orders.each do |_, params|
|
30
|
+
user = params.delete(:user)
|
31
|
+
SolidusImporter::SpreeCoreImporterOrder.import(user, params)
|
32
|
+
rescue StandardError
|
33
|
+
context[:success] = false
|
34
|
+
end
|
35
|
+
|
36
|
+
context
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -26,7 +26,11 @@ module SolidusImporter
|
|
26
26
|
initial_context = @importer.before_import(initial_context)
|
27
27
|
unless @import.failed?
|
28
28
|
rows = process_rows(initial_context)
|
29
|
-
@
|
29
|
+
ending_context = @importer.after_import(initial_context)
|
30
|
+
state = @import.state
|
31
|
+
state = :completed if rows.zero?
|
32
|
+
state = :failed if ending_context[:success] == false
|
33
|
+
@import.update(state: state)
|
30
34
|
end
|
31
35
|
@import
|
32
36
|
end
|
@@ -21,6 +21,9 @@ module SolidusImporter
|
|
21
21
|
break
|
22
22
|
end
|
23
23
|
end
|
24
|
+
|
25
|
+
@importer.handle_row_import(context)
|
26
|
+
|
24
27
|
@row.update!(
|
25
28
|
state: context[:success] ? :completed : :failed,
|
26
29
|
messages: context[:messages]
|
@@ -34,7 +37,6 @@ module SolidusImporter
|
|
34
37
|
def check_import_finished(context)
|
35
38
|
return unless @row.import.finished?
|
36
39
|
|
37
|
-
@importer.after_import(context)
|
38
40
|
@row.import.update!(state: (@row.import.rows.failed.any? ? :failed : :completed))
|
39
41
|
end
|
40
42
|
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusImporter
|
4
|
+
module Processors
|
5
|
+
class BillAddress < Base
|
6
|
+
def call(context)
|
7
|
+
@data = context[:data]
|
8
|
+
|
9
|
+
return if @data['Billing Address1'].blank?
|
10
|
+
|
11
|
+
order = context.fetch(:order, {})
|
12
|
+
|
13
|
+
order[:bill_address_attributes] = bill_address_attributes
|
14
|
+
|
15
|
+
context.merge!(order: order)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def country_code
|
21
|
+
@data['Billing Country Code']
|
22
|
+
end
|
23
|
+
|
24
|
+
def province_code
|
25
|
+
@data['Billing Province Code']
|
26
|
+
end
|
27
|
+
|
28
|
+
def bill_address_attributes
|
29
|
+
{
|
30
|
+
firstname: @data['Billing First Name'],
|
31
|
+
lastname: @data['Billing Last Name'],
|
32
|
+
address1: @data['Billing Address1'],
|
33
|
+
address2: @data['Billing Address2'],
|
34
|
+
city: @data['Billing City'],
|
35
|
+
company: @data['Billing Company'],
|
36
|
+
zipcode: @data['Billing Zip'],
|
37
|
+
phone: @data['Billing Phone'],
|
38
|
+
country: country,
|
39
|
+
state: state
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def country
|
44
|
+
@country ||= Spree::Country.find_by(iso: country_code) if country_code
|
45
|
+
end
|
46
|
+
|
47
|
+
def state
|
48
|
+
@state ||= country&.states&.find_by(abbr: province_code) if province_code
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -3,15 +3,11 @@
|
|
3
3
|
module SolidusImporter
|
4
4
|
module Processors
|
5
5
|
class Customer < Base
|
6
|
-
attr_accessor :address
|
7
|
-
|
8
6
|
def call(context)
|
9
7
|
@data = context.fetch(:data)
|
10
8
|
check_data
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
context.merge!(user: process_user)
|
10
|
+
context.merge!(user: process_user && persist_user)
|
15
11
|
end
|
16
12
|
|
17
13
|
def options
|
@@ -26,15 +22,22 @@ module SolidusImporter
|
|
26
22
|
raise SolidusImporter::Exception, 'Missing required key: "Email"' if @data['Email'].blank?
|
27
23
|
end
|
28
24
|
|
25
|
+
def user
|
26
|
+
@user ||= prepare_user
|
27
|
+
end
|
28
|
+
|
29
29
|
def prepare_user
|
30
30
|
Spree::User.find_or_initialize_by(email: @data['Email']) do |u|
|
31
31
|
u.password = options[:password_method].call(u)
|
32
|
-
u.bill_address = address if address.present?
|
33
32
|
end
|
34
33
|
end
|
35
34
|
|
36
35
|
def process_user
|
37
|
-
|
36
|
+
user
|
37
|
+
end
|
38
|
+
|
39
|
+
def persist_user
|
40
|
+
user.tap(&:save!)
|
38
41
|
end
|
39
42
|
end
|
40
43
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusImporter
|
4
|
+
module Processors
|
5
|
+
class CustomerAddress < Base
|
6
|
+
def call(context)
|
7
|
+
@data = context.fetch(:data)
|
8
|
+
|
9
|
+
address = Spree::Address.find_or_create_by(address_attributes)
|
10
|
+
return unless address.valid?
|
11
|
+
|
12
|
+
user = context.fetch(:user)
|
13
|
+
user.addresses << address
|
14
|
+
user.bill_address ||= address
|
15
|
+
user.ship_address ||= address
|
16
|
+
user.save!
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def country
|
22
|
+
@country ||= Spree::Country.find_by(iso: @data['Country Code']) if @data['Country Code']
|
23
|
+
end
|
24
|
+
|
25
|
+
def state
|
26
|
+
@state ||= country&.states&.find_by(abbr: @data['Province Code']) if @data['Province Code']
|
27
|
+
end
|
28
|
+
|
29
|
+
def address_attributes
|
30
|
+
@address_attributes ||= {
|
31
|
+
firstname: @data['First Name'],
|
32
|
+
lastname: @data['Last Name'],
|
33
|
+
address1: @data['Address1'],
|
34
|
+
address2: @data['Address2'],
|
35
|
+
city: @data['City'],
|
36
|
+
zipcode: @data['Zip'],
|
37
|
+
phone: @data['Phone'],
|
38
|
+
country: country,
|
39
|
+
state: state,
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusImporter
|
4
|
+
module Processors
|
5
|
+
class LineItem < Base
|
6
|
+
def call(context)
|
7
|
+
@data = context[:data]
|
8
|
+
|
9
|
+
return if @data['Lineitem sku'].blank?
|
10
|
+
|
11
|
+
order = context.fetch(:order, {})
|
12
|
+
|
13
|
+
order[:line_items_attributes] ||= {}
|
14
|
+
|
15
|
+
index = order[:line_items_attributes].size
|
16
|
+
|
17
|
+
order[:line_items_attributes][index] = line_items_attributes
|
18
|
+
|
19
|
+
context.merge!(order: order)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def line_items_attributes
|
25
|
+
{
|
26
|
+
sku: @data['Lineitem sku'],
|
27
|
+
quantity: @data['Lineitem quantity'],
|
28
|
+
price: @data['Lineitem price']
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -6,7 +6,7 @@ module SolidusImporter
|
|
6
6
|
def call(context)
|
7
7
|
@data = context.fetch(:data)
|
8
8
|
check_data
|
9
|
-
context.merge!(order:
|
9
|
+
context.merge!(order: order_attributes)
|
10
10
|
end
|
11
11
|
|
12
12
|
def options
|
@@ -21,19 +21,48 @@ module SolidusImporter
|
|
21
21
|
raise SolidusImporter::Exception, 'Missing required key: "Name"' if @data['Name'].blank?
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
order.currency = @data['Currency'] unless @data['Currency'].nil?
|
30
|
-
order.email = @data['Email'] unless @data['Email'].nil?
|
31
|
-
order.special_instructions = @data['Note'] unless @data['Note'].nil?
|
32
|
-
end
|
24
|
+
def completed_at
|
25
|
+
processed_at = @data['Processed At']
|
26
|
+
processed_at ? Time.parse(processed_at).in_time_zone : Time.current
|
27
|
+
rescue ArgumentError
|
28
|
+
Time.current
|
33
29
|
end
|
34
30
|
|
35
|
-
def
|
36
|
-
|
31
|
+
def currency
|
32
|
+
@data['Currency']
|
33
|
+
end
|
34
|
+
|
35
|
+
def email
|
36
|
+
@data['Email']
|
37
|
+
end
|
38
|
+
|
39
|
+
def order_attributes
|
40
|
+
{
|
41
|
+
number: number,
|
42
|
+
completed_at: completed_at,
|
43
|
+
store: options[:store],
|
44
|
+
currency: currency,
|
45
|
+
email: email,
|
46
|
+
user: user,
|
47
|
+
special_instructions: special_instruction,
|
48
|
+
line_items_attributes: {},
|
49
|
+
bill_address_attributes: {},
|
50
|
+
ship_address_attributes: {},
|
51
|
+
shipments_attributes: [],
|
52
|
+
payments_attributes: []
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def number
|
57
|
+
@data['Name']
|
58
|
+
end
|
59
|
+
|
60
|
+
def special_instruction
|
61
|
+
@data['Note']
|
62
|
+
end
|
63
|
+
|
64
|
+
def user
|
65
|
+
@user ||= Spree::User.find_by(email: email)
|
37
66
|
end
|
38
67
|
end
|
39
68
|
end
|