solidus_backtracs 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.bundle/config +2 -0
- data/.circleci/config.yml +41 -0
- data/.gem_release.yml +5 -0
- data/.github/stale.yml +17 -0
- data/.github_changelog_generator +2 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.rubocop.yml +14 -0
- data/.rubocop_todo.yml +40 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile +33 -0
- data/LICENSE +26 -0
- data/README.md +208 -0
- data/Rakefile +6 -0
- data/app/assets/javascripts/spree/backend/solidus_backtracs.js +2 -0
- data/app/assets/javascripts/spree/frontend/solidus_backtracs.js +2 -0
- data/app/assets/stylesheets/spree/backend/solidus_backtracs.css +4 -0
- data/app/assets/stylesheets/spree/frontend/solidus_backtracs.css +4 -0
- data/app/controllers/spree/backtracs_controller.rb +46 -0
- data/app/decorators/models/solidus_backtracs/spree/shipment_decorator.rb +33 -0
- data/app/helpers/solidus_backtracs/export_helper.rb +52 -0
- data/app/jobs/solidus_backtracs/api/schedule_shipment_syncs_job.rb +28 -0
- data/app/jobs/solidus_backtracs/api/sync_shipment_job.rb +17 -0
- data/app/jobs/solidus_backtracs/api/sync_shipments_job.rb +41 -0
- data/app/queries/solidus_backtracs/shipment/between_query.rb +14 -0
- data/app/queries/solidus_backtracs/shipment/exportable_query.rb +24 -0
- data/app/queries/solidus_backtracs/shipment/pending_api_sync_query.rb +51 -0
- data/app/views/spree/backtracs/export.xml.builder +58 -0
- data/bin/console +17 -0
- data/bin/rails +7 -0
- data/bin/rails-engine +13 -0
- data/bin/rails-sandbox +16 -0
- data/bin/rake +7 -0
- data/bin/sandbox +86 -0
- data/bin/setup +8 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +6 -0
- data/db/migrate/20210220093010_add_backtracs_api_sync_fields.rb +8 -0
- data/lib/generators/solidus_backtracs/install/install_generator.rb +27 -0
- data/lib/generators/solidus_backtracs/install/templates/initializer.rb +91 -0
- data/lib/solidus_backtracs/api/batch_syncer.rb +45 -0
- data/lib/solidus_backtracs/api/client.rb +36 -0
- data/lib/solidus_backtracs/api/rate_limited_error.rb +23 -0
- data/lib/solidus_backtracs/api/request_error.rb +33 -0
- data/lib/solidus_backtracs/api/request_runner.rb +87 -0
- data/lib/solidus_backtracs/api/shipment_serializer.rb +103 -0
- data/lib/solidus_backtracs/api/threshold_verifier.rb +28 -0
- data/lib/solidus_backtracs/configuration.rb +62 -0
- data/lib/solidus_backtracs/engine.rb +19 -0
- data/lib/solidus_backtracs/errors.rb +23 -0
- data/lib/solidus_backtracs/shipment_notice.rb +58 -0
- data/lib/solidus_backtracs/testing_support/factories.rb +4 -0
- data/lib/solidus_backtracs/version.rb +5 -0
- data/lib/solidus_backtracs.rb +16 -0
- data/solidus_shipstation.gemspec +39 -0
- data/spec/controllers/spree/backtracs_controller_spec.rb +103 -0
- data/spec/fixtures/backtracs_xml_schema.xsd +171 -0
- data/spec/jobs/solidus_backtracs/api/schedule_shipment_syncs_job_spec.rb +32 -0
- data/spec/jobs/solidus_backtracs/api/sync_shipments_job_spec.rb +102 -0
- data/spec/lib/solidus_backtracs/api/batch_syncer_spec.rb +228 -0
- data/spec/lib/solidus_backtracs/api/client_spec.rb +120 -0
- data/spec/lib/solidus_backtracs/api/rate_limited_error_spec.rb +21 -0
- data/spec/lib/solidus_backtracs/api/request_error_spec.rb +20 -0
- data/spec/lib/solidus_backtracs/api/request_runner_spec.rb +65 -0
- data/spec/lib/solidus_backtracs/api/shipment_serializer_spec.rb +25 -0
- data/spec/lib/solidus_backtracs/api/threshold_verifier_spec.rb +61 -0
- data/spec/lib/solidus_backtracs/shipment_notice_spec.rb +111 -0
- data/spec/lib/solidus_backtracs_spec.rb +9 -0
- data/spec/models/spree/shipment_spec.rb +49 -0
- data/spec/queries/solidus_backtracs/shipment/between_query_spec.rb +53 -0
- data/spec/queries/solidus_backtracs/shipment/exportable_query_spec.rb +53 -0
- data/spec/queries/solidus_backtracs/shipment/pending_api_sync_query_spec.rb +37 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/configuration_helper.rb +13 -0
- data/spec/support/controllers.rb +1 -0
- data/spec/support/webmock.rb +3 -0
- data/spec/support/xsd.rb +5 -0
- metadata +248 -0
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "uri"
|
3
|
+
require "net/http"
|
4
|
+
|
5
|
+
module SolidusBacktracs
|
6
|
+
module Api
|
7
|
+
class RequestRunner
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@username = SolidusBacktracs.configuration.authentication_username
|
11
|
+
@password = SolidusBacktracs.configuration.authentication_password
|
12
|
+
@api_base = SolidusBacktracs.configuration.api_base
|
13
|
+
end
|
14
|
+
|
15
|
+
def authenticated_call(method: nil, path: nil, serializer: nil, shipment: nil)
|
16
|
+
unless @username.present? || @password.present? || @api_base.present?
|
17
|
+
raise "Credentials not defined for Authentication"
|
18
|
+
end
|
19
|
+
|
20
|
+
Rails.cache.fetch("backtracks_cache_key", expires_in: 1.hour) do
|
21
|
+
@response = self.call(method: :get, path: "/webservices/user/Authentication.asmx/Login?sUserName=#{@username}&sPassword=#{@password}")
|
22
|
+
end
|
23
|
+
|
24
|
+
authenticted = parse_authentication_response(@response, "Result")
|
25
|
+
|
26
|
+
if authenticted == 'false'
|
27
|
+
raise RequestError.from_response(@response)
|
28
|
+
else
|
29
|
+
sguid = parse_authentication_response(@response, "Message")
|
30
|
+
params = serializer.call(shipment, sguid)
|
31
|
+
|
32
|
+
rma_response = call(method: :post, path: path, params: params)
|
33
|
+
sync_shipment(shipment, rma_response)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def call(method: nil, path: nil, params: {})
|
38
|
+
doc = {}
|
39
|
+
if params.present?
|
40
|
+
doc = Nokogiri::XML(params.to_s)
|
41
|
+
end
|
42
|
+
response = HTTParty.send(
|
43
|
+
method,
|
44
|
+
URI.join(@api_base, path),
|
45
|
+
body: doc.to_xml,
|
46
|
+
http_proxyaddr: SolidusBacktracs.configuration.proxy_address,
|
47
|
+
http_proxyport: SolidusBacktracs.configuration.proxy_port,
|
48
|
+
http_proxyuser: SolidusBacktracs.configuration.proxy_username,
|
49
|
+
http_proxypass: SolidusBacktracs.configuration.proxy_password,
|
50
|
+
headers: {
|
51
|
+
'Content-Type' => 'text/xml',
|
52
|
+
},
|
53
|
+
)
|
54
|
+
|
55
|
+
case response.code.to_s
|
56
|
+
when /2\d{2}/
|
57
|
+
response
|
58
|
+
when '429'
|
59
|
+
raise RateLimitedError.from_response(response)
|
60
|
+
else
|
61
|
+
raise RequestError.from_response(response)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_authentication_response(response, type)
|
66
|
+
response.dig("AuthenticationResponse", type)
|
67
|
+
end
|
68
|
+
|
69
|
+
def sync_shipment(shipment, response)
|
70
|
+
result = response.dig("Envelope", "Body", "CreateNewResponse", "CreateNewResult", "Result")
|
71
|
+
if result == 'true'
|
72
|
+
shipment.update_column(:backtracs_synced_at, Time.zone.now)
|
73
|
+
|
74
|
+
::Spree::Event.fire(
|
75
|
+
'solidus_backtracs.api.sync_completed',
|
76
|
+
shipment: shipment
|
77
|
+
)
|
78
|
+
else
|
79
|
+
::Spree::Event.fire(
|
80
|
+
'solidus_backtracs.api.sync_failed',
|
81
|
+
shipment: shipment
|
82
|
+
)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBacktracs
|
4
|
+
module Api
|
5
|
+
class ShipmentSerializer
|
6
|
+
|
7
|
+
def initialize(shipment:)
|
8
|
+
@shipment = shipment
|
9
|
+
@config = SolidusBacktracs.config
|
10
|
+
@property_id = ::Spree::Property.find_by(name: SolidusBacktracs.config.default_property_name)&.id
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(sguid: nil)
|
14
|
+
order = @shipment.order
|
15
|
+
user = @shipment.user
|
16
|
+
|
17
|
+
xml = Builder::XmlMarkup.new
|
18
|
+
xml.instruct!(:xml, :encoding => "UTF-8")
|
19
|
+
|
20
|
+
xml.soap(:Envelope, {"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", "xmlns:xsd" => "http://www.w3.org/2001/XMLSchema", "xmlns:soap" => "http://schemas.xmlsoap.org/soap/envelope/"}) do
|
21
|
+
xml.soap :Body do
|
22
|
+
xml.CreateNew({"xmlns" => "http://bactracs.andlor.com/rmaservice"}) do
|
23
|
+
xml.sGuid sguid
|
24
|
+
xml.NewRMA {
|
25
|
+
xml.RMANumber @shipment.number
|
26
|
+
xml.RMATypeName @config.default_rma_type
|
27
|
+
xml.RMASubTypeName
|
28
|
+
xml.CustomerRef
|
29
|
+
xml.InboundShippingPriority
|
30
|
+
xml.InboundTrackingNumber @shipment.tracking
|
31
|
+
|
32
|
+
xml.Ship {
|
33
|
+
xml.Carrier @config.default_carrier
|
34
|
+
xml.ShipMethod @config.default_ship_method
|
35
|
+
xml.ShipDate @shipment.created_at.strftime(SolidusBacktracs::ExportHelper::BACTRACS_DATE_FORMAT)
|
36
|
+
xml.TrackingNumber @shipment.tracking
|
37
|
+
xml.SerialNumber @shipment.number
|
38
|
+
xml.Ud1
|
39
|
+
}
|
40
|
+
xml.Customer {
|
41
|
+
SolidusBacktracs::ExportHelper.backtracs_address(xml, order, :ship)
|
42
|
+
SolidusBacktracs::ExportHelper.backtracs_address(xml, order, :bill)
|
43
|
+
}
|
44
|
+
xml.Rep {
|
45
|
+
xml.Code
|
46
|
+
xml.Name user.full_name
|
47
|
+
xml.Email user.email
|
48
|
+
}
|
49
|
+
|
50
|
+
xml.RMALines {
|
51
|
+
@shipment.line_items.each do |line|
|
52
|
+
product = line.product
|
53
|
+
if product.respond_to?(:assembly?) && product.assembly?
|
54
|
+
product.parts.each do |part|
|
55
|
+
next unless part.product.product_properties.where(property_id: @property_id).present?
|
56
|
+
line_items_xml(xml: xml, line_item: line, variant: part, order: order)
|
57
|
+
end
|
58
|
+
else
|
59
|
+
line_items_xml(xml: xml, line_item: line, variant: line.variant, order: order)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
}
|
63
|
+
xml.OrderDate order.completed_at.strftime(SolidusBacktracs::ExportHelper::BACTRACS_DATE_FORMAT)
|
64
|
+
xml.CreateDate @shipment.created_at.strftime(SolidusBacktracs::ExportHelper::BACTRACS_DATE_FORMAT)
|
65
|
+
xml.Status @config.default_status
|
66
|
+
xml.RMAId @shipment.id
|
67
|
+
xml.ClientGuid
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
xml
|
73
|
+
end
|
74
|
+
|
75
|
+
def line_items_xml(xml: nil, line_item: nil, variant: nil, order: nil)
|
76
|
+
shipment_notice = @shipment.shipment_notice
|
77
|
+
xml.RMALine {
|
78
|
+
xml.DFItem find_sku_variant(variant)
|
79
|
+
xml.DFModelNum find_sku_variant(variant)
|
80
|
+
xml.DFCategory
|
81
|
+
xml.DFCategoryDescription
|
82
|
+
xml.DFQuantity line_item.quantity
|
83
|
+
xml.DFUnitPrice line_item.price
|
84
|
+
xml.DFSerialNumbers
|
85
|
+
xml.Ud1s
|
86
|
+
xml.CurrentWarranties
|
87
|
+
xml.DFComments
|
88
|
+
xml.DFStatus @shipment.state
|
89
|
+
xml.PurchaseDate order.completed_at.strftime(SolidusBacktracs::ExportHelper::BACTRACS_DATE_FORMAT)
|
90
|
+
xml.ServiceProvider shipment_notice&.service
|
91
|
+
xml.WarrantyRepair
|
92
|
+
xml.RMALineTest
|
93
|
+
xml.InboundShipWeight variant.weight.to_f
|
94
|
+
xml.RPLocation @config.default_rp_location
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
def find_sku_variant(variant)
|
99
|
+
@config.sku_map[variant.sku].present? ? @config.sku_map[variant.sku] : variant.sku
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBacktracs
|
4
|
+
module Api
|
5
|
+
class ThresholdVerifier
|
6
|
+
class << self
|
7
|
+
def call(shipment)
|
8
|
+
return false unless shipment.order.completed?
|
9
|
+
|
10
|
+
!!(shipment_requires_creation?(shipment) || shipment_requires_update?(shipment))
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def shipment_requires_creation?(shipment)
|
16
|
+
shipment.backtracs_synced_at.nil? &&
|
17
|
+
Time.zone.now - shipment.order.updated_at < SolidusBacktracs.config.api_sync_threshold
|
18
|
+
end
|
19
|
+
|
20
|
+
def shipment_requires_update?(shipment)
|
21
|
+
shipment.backtracs_synced_at &&
|
22
|
+
shipment.backtracs_synced_at < shipment.order.updated_at &&
|
23
|
+
Time.zone.now - shipment.order.updated_at < SolidusBacktracs.config.api_sync_threshold
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBacktracs
|
4
|
+
class Configuration
|
5
|
+
attr_accessor(
|
6
|
+
:webhook_username,
|
7
|
+
:webhook_password,
|
8
|
+
:weight_units,
|
9
|
+
:ssl_encrypted,
|
10
|
+
:capture_at_notification,
|
11
|
+
:export_canceled_shipments,
|
12
|
+
:api_batch_size,
|
13
|
+
:api_sync_threshold,
|
14
|
+
:api_shipment_serializer,
|
15
|
+
:api_key,
|
16
|
+
:api_secret,
|
17
|
+
:api_shipment_matcher,
|
18
|
+
:error_handler,
|
19
|
+
:shipment_notice_class,
|
20
|
+
:authentication_username,
|
21
|
+
:authentication_password,
|
22
|
+
:api_base,
|
23
|
+
:proxy_address,
|
24
|
+
:proxy_port,
|
25
|
+
:proxy_username,
|
26
|
+
:proxy_password,
|
27
|
+
:default_carrier,
|
28
|
+
:default_ship_method,
|
29
|
+
:default_rp_location,
|
30
|
+
:default_status,
|
31
|
+
:default_property_name,
|
32
|
+
:sku_map,
|
33
|
+
:default_rma_type,
|
34
|
+
:shippable_skus
|
35
|
+
)
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
@api_batch_size = 100
|
39
|
+
@api_sync_threshold = 7.days
|
40
|
+
@error_handler = ->(_error, _extra = {}) {
|
41
|
+
Rails.logger.error "#{error.inspect} (#{extra.inspect})"
|
42
|
+
}
|
43
|
+
@api_shipment_matcher = proc do |backtracs_order, shipments|
|
44
|
+
shipments.find { |shipment| shipment.number == backtracs_order['orderNumber'] }
|
45
|
+
end
|
46
|
+
|
47
|
+
@shipment_notice_class = 'SolidusBacktracs::ShipmentNotice'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class << self
|
52
|
+
def configuration
|
53
|
+
@configuration ||= Configuration.new
|
54
|
+
end
|
55
|
+
|
56
|
+
alias config configuration
|
57
|
+
|
58
|
+
def configure
|
59
|
+
yield configuration
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'solidus_core'
|
4
|
+
require 'solidus_support'
|
5
|
+
|
6
|
+
module SolidusBacktracs
|
7
|
+
class Engine < Rails::Engine
|
8
|
+
include SolidusSupport::EngineExtensions
|
9
|
+
|
10
|
+
isolate_namespace ::Spree
|
11
|
+
|
12
|
+
engine_name 'solidus_backtracs'
|
13
|
+
|
14
|
+
# use rspec for tests
|
15
|
+
config.generators do |g|
|
16
|
+
g.test_framework :rspec
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBacktracs
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
class OrderNotPaidError < Error
|
7
|
+
def initialize(order, *args)
|
8
|
+
super("Order #{order.number} is not paid and capture_at_notification is false", *args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ShipmentNotFoundError < Error
|
13
|
+
def initialize(shipment_number, *args)
|
14
|
+
super("Could not find shipment with number #{shipment_number}", *args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class PaymentError < Error
|
19
|
+
def initialize(payment, *args)
|
20
|
+
super("Could not process payment #{payment.id}", *args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBacktracs
|
4
|
+
class ShipmentNotice
|
5
|
+
attr_reader :shipment_number, :shipment_tracking
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def from_payload(params)
|
9
|
+
new(
|
10
|
+
shipment_number: params[:order_number],
|
11
|
+
shipment_tracking: params[:tracking_number],
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(shipment_number:, shipment_tracking:)
|
17
|
+
@shipment_number = shipment_number
|
18
|
+
@shipment_tracking = shipment_tracking
|
19
|
+
end
|
20
|
+
|
21
|
+
def apply
|
22
|
+
unless shipment
|
23
|
+
raise ShipmentNotFoundError, shipment
|
24
|
+
end
|
25
|
+
|
26
|
+
process_payment
|
27
|
+
ship_shipment
|
28
|
+
|
29
|
+
shipment
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def shipment
|
35
|
+
@shipment ||= ::Spree::Shipment.find_by(number: shipment_number)
|
36
|
+
end
|
37
|
+
|
38
|
+
def process_payment
|
39
|
+
return if shipment.order.paid?
|
40
|
+
|
41
|
+
unless SolidusBacktracs.configuration.capture_at_notification
|
42
|
+
raise OrderNotPaidError, shipment.order
|
43
|
+
end
|
44
|
+
|
45
|
+
shipment.order.payments.pending.each do |payment|
|
46
|
+
payment.capture!
|
47
|
+
rescue ::Spree::Core::GatewayError
|
48
|
+
raise PaymentError, payment
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def ship_shipment
|
53
|
+
shipment.update!(tracking: shipment_tracking)
|
54
|
+
shipment.ship! if shipment.can_ship?
|
55
|
+
shipment.order.recalculate
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'httparty'
|
4
|
+
|
5
|
+
require 'solidus_backtracs/api/batch_syncer'
|
6
|
+
require 'solidus_backtracs/api/request_runner'
|
7
|
+
require 'solidus_backtracs/api/client'
|
8
|
+
require 'solidus_backtracs/api/request_error'
|
9
|
+
require 'solidus_backtracs/api/rate_limited_error'
|
10
|
+
require 'solidus_backtracs/api/shipment_serializer'
|
11
|
+
require 'solidus_backtracs/api/threshold_verifier'
|
12
|
+
require 'solidus_backtracs/configuration'
|
13
|
+
require 'solidus_backtracs/errors'
|
14
|
+
require 'solidus_backtracs/shipment_notice'
|
15
|
+
require 'solidus_backtracs/version'
|
16
|
+
require 'solidus_backtracs/engine'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/solidus_backtracs/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'solidus_backtracs'
|
7
|
+
spec.version = SolidusBacktracs::VERSION
|
8
|
+
spec.authors = ['Stephen Puiszis']
|
9
|
+
spec.email = 'steve@tablexi.com'
|
10
|
+
|
11
|
+
spec.summary = 'A Solidus extension for integrating the Backtracs API.'
|
12
|
+
spec.homepage = 'https://github.com/solidusio-contrib/solidus_backtracs'
|
13
|
+
spec.license = 'BSD-3-Clause'
|
14
|
+
|
15
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
16
|
+
spec.metadata['source_code_uri'] = 'https://github.com/solidusio-contrib/solidus_backtracs'
|
17
|
+
spec.metadata['changelog_uri'] = 'https://github.com/solidusio-contrib/solidus_backtracs/blob/master/CHANGELOG.md'
|
18
|
+
|
19
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.5')
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
|
24
|
+
|
25
|
+
spec.files = files.grep_v(%r{^(test|spec|features)/})
|
26
|
+
spec.test_files = files.grep(%r{^(test|spec|features)/})
|
27
|
+
spec.bindir = "exe"
|
28
|
+
spec.executables = files.grep(%r{^exe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ["lib"]
|
30
|
+
|
31
|
+
spec.add_dependency 'httparty', '~> 0.18'
|
32
|
+
spec.add_dependency 'solidus_core', ['>= 2.0.0', '< 4']
|
33
|
+
spec.add_dependency 'solidus_support', '~> 0.5'
|
34
|
+
|
35
|
+
spec.add_development_dependency 'rails-controller-testing'
|
36
|
+
spec.add_development_dependency 'rspec-xsd'
|
37
|
+
spec.add_development_dependency 'solidus_dev_support', '~> 2.5'
|
38
|
+
spec.add_development_dependency 'webmock'
|
39
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Spree::BacktracsController do
|
4
|
+
render_views
|
5
|
+
|
6
|
+
describe '#export' do
|
7
|
+
context 'when the authentication is invalid' do
|
8
|
+
it 'returns an error error' do
|
9
|
+
get :export, params: { format: 'xml' }
|
10
|
+
|
11
|
+
expect(response.status).to eq(401)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'when the authentication is valid' do
|
16
|
+
it 'responds with 200 OK' do
|
17
|
+
stub_backtracs_auth
|
18
|
+
create(:order_ready_to_ship)
|
19
|
+
|
20
|
+
get :export,
|
21
|
+
params: {
|
22
|
+
start_date: 1.day.ago.strftime('%m/%d/%Y %H:%M'),
|
23
|
+
end_date: 1.day.from_now.strftime('%m/%d/%Y %H:%M'),
|
24
|
+
format: 'xml'
|
25
|
+
}
|
26
|
+
|
27
|
+
expect(response.status).to eq(200)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'generates Backtracs-compliant XML' do
|
31
|
+
stub_backtracs_auth
|
32
|
+
create(:order_ready_to_ship)
|
33
|
+
|
34
|
+
get :export, params: {
|
35
|
+
start_date: 1.day.ago.strftime('%m/%d/%Y %H:%M'),
|
36
|
+
end_date: 1.day.from_now.strftime('%m/%d/%Y %H:%M'),
|
37
|
+
format: 'xml'
|
38
|
+
}
|
39
|
+
|
40
|
+
expect(response.body).to pass_validation('spec/fixtures/backtracs_xml_schema.xsd')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#shipnotify' do
|
46
|
+
context 'when the authentication is valid' do
|
47
|
+
context 'when the shipment can be found' do
|
48
|
+
it 'responds with 200 OK' do
|
49
|
+
stub_backtracs_auth
|
50
|
+
shipment = create(:order_ready_to_ship).shipments.first
|
51
|
+
|
52
|
+
post :shipnotify, params: {
|
53
|
+
order_number: shipment.number,
|
54
|
+
tracking_number: '123456',
|
55
|
+
format: 'xml',
|
56
|
+
}
|
57
|
+
shipment.reload
|
58
|
+
|
59
|
+
expect(response.status).to eq(200)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'updates the shipment' do
|
63
|
+
stub_backtracs_auth
|
64
|
+
shipment = create(:order_ready_to_ship).shipments.first
|
65
|
+
|
66
|
+
post :shipnotify, params: {
|
67
|
+
order_number: shipment.number,
|
68
|
+
tracking_number: '123456',
|
69
|
+
format: 'xml',
|
70
|
+
}
|
71
|
+
shipment.reload
|
72
|
+
|
73
|
+
expect(shipment).to have_attributes(
|
74
|
+
tracking: '123456',
|
75
|
+
state: 'shipped',
|
76
|
+
shipped_at: an_instance_of(ActiveSupport::TimeWithZone),
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'when the shipment cannot be found' do
|
82
|
+
it 'responds with 400 Bad Request' do
|
83
|
+
stub_backtracs_auth
|
84
|
+
shipment = create(:order_ready_to_ship).shipments.first
|
85
|
+
|
86
|
+
post :shipnotify, params: {
|
87
|
+
order_number: 'ABC123',
|
88
|
+
tracking_number: '123456',
|
89
|
+
format: 'xml',
|
90
|
+
}
|
91
|
+
shipment.reload
|
92
|
+
|
93
|
+
expect(response.status).to eq(400)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def stub_backtracs_auth(username = 'mario', password = 'lemieux')
|
100
|
+
stub_configuration(username: username, password: password)
|
101
|
+
request.headers['Authorization'] = ActionController::HttpAuthentication::Basic.encode_credentials(username, password)
|
102
|
+
end
|
103
|
+
end
|