solidus_importer 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Import products CSV from the backend](.readme/import-products-1.gif)
|
26
|
+
|
27
|
+
![Look at the newly imported products](.readme/import-products-2.gif)
|
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
|