spree_fosdick_integration 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +28 -0
  3. data/Gemfile +21 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +76 -0
  6. data/Rakefile +15 -0
  7. data/app/controllers/spree/admin/fosdick_shipments_controller.rb +18 -0
  8. data/app/mailers/spree/fosdick_shipment_mailer.rb +13 -0
  9. data/app/models/spree/fosdick_exception.rb +15 -0
  10. data/app/models/spree/fosdick_shipment.rb +8 -0
  11. data/app/models/spree/shipment_decorator.rb +17 -0
  12. data/app/overrides/add_fosdick_panel.rb +8 -0
  13. data/app/serializers/address_serializer.rb +18 -0
  14. data/app/serializers/inventory_unit_serializer.rb +21 -0
  15. data/app/serializers/shipment_serializer.rb +104 -0
  16. data/app/services/exception_logger.rb +21 -0
  17. data/app/views/spree/admin/fosdick_shipments/_filters_bar.html.haml +36 -0
  18. data/app/views/spree/admin/fosdick_shipments/_fosdick_shipments_list.html.haml +28 -0
  19. data/app/views/spree/admin/fosdick_shipments/_fosdick_shipments_list_respond.js.erb +1 -0
  20. data/app/views/spree/admin/fosdick_shipments/_titles.html.haml +5 -0
  21. data/app/views/spree/admin/fosdick_shipments/index.html.haml +3 -0
  22. data/app/views/spree/admin/fosdick_shipments/index.js.haml +1 -0
  23. data/app/views/spree/fosdick_shipment_mailer/order_shipped.html.erb +135 -0
  24. data/config/locales/en.yml +14 -0
  25. data/config/locales/ru.yml +14 -0
  26. data/config/routes.rb +10 -0
  27. data/db/migrate/20151013153126_create_spree_fosdick_shipment.rb +17 -0
  28. data/db/migrate/20151015122038_create_fosdick_exceptions.rb +15 -0
  29. data/db/migrate/20151028105216_add_fosdick_atempt_and_state_spree_shipments.rb +6 -0
  30. data/lib/fosdick/documents/shipment.rb +89 -0
  31. data/lib/fosdick/processor.rb +64 -0
  32. data/lib/fosdick/receiver.rb +49 -0
  33. data/lib/fosdick/sender.rb +39 -0
  34. data/lib/generators/spree_fosdick_integration/install/install_generator.rb +32 -0
  35. data/lib/generators/spree_fosdick_integration/install/templates/fosdick.rb +6 -0
  36. data/lib/generators/spree_fosdick_integration/install/templates/fosdick.yml.template +18 -0
  37. data/lib/spree_fosdick_integration.rb +8 -0
  38. data/lib/spree_fosdick_integration/engine.rb +22 -0
  39. data/lib/spree_fosdick_integration/factories.rb +31 -0
  40. data/lib/spree_fosdick_integration/version.rb +3 -0
  41. data/script/rails +7 -0
  42. data/spec/controllers/admin/fosdick_shipments_controller_spec.rb +16 -0
  43. data/spec/models/spree/fosdick_exception_spec.rb +21 -0
  44. data/spec/models/spree/fosdick_shipment_spec.rb +9 -0
  45. data/spec/models/spree/shipment_spec.rb +50 -0
  46. data/spec/services/exception_logger_spec.rb +11 -0
  47. data/spec/services/fosdick/processor_spec.rb +94 -0
  48. data/spec/services/fosdick/receiver_spec.rb +43 -0
  49. data/spec/services/fosdick/sender_spec.rb +34 -0
  50. data/spec/spec_helper.rb +99 -0
  51. data/spree_fosdick_integration.gemspec +37 -0
  52. metadata +286 -0
@@ -0,0 +1,39 @@
1
+ module Fosdick
2
+ class Sender
3
+ include HTTParty
4
+ base_uri FOSDICK_CONFIG['ipost_uri']
5
+ format :xml
6
+
7
+ def self.send_doc(doc, config)
8
+ client = config['client_name']
9
+
10
+ begin
11
+ # required sleep 2 sec for Fosdick Shopping Cart iPost interface
12
+ sleep 2
13
+ res = post("/#{client}/cart/ipost.asp", body: doc)
14
+ rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
15
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError
16
+
17
+ raise Fosdick::SendError
18
+ end
19
+ return validate_and_return(res)
20
+ end
21
+
22
+ private
23
+
24
+ def self.validate_and_return(response)
25
+ order_response = response['UnitycartOrderResponse']['OrderResponse']
26
+
27
+ if order_response['SuccessCode'] == 'True'
28
+ order_response['OrderNumber']
29
+ else
30
+ {
31
+ code: order_response['ErrorCode'],
32
+ errors: order_response['Errors']
33
+ }
34
+ end
35
+ end
36
+ end
37
+
38
+ class SendError < StandardError; end
39
+ end
@@ -0,0 +1,32 @@
1
+ module SpreeFosdickIntegration
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ class_option :auto_run_migrations, type: :boolean, default: false
6
+
7
+ def self.source_paths
8
+ paths = self.superclass.source_paths
9
+ paths << File.expand_path('../templates', __FILE__)
10
+ paths.flatten
11
+ end
12
+
13
+ def add_migrations
14
+ run 'bundle exec rake railties:install:migrations FROM=spree_fosdick_integration'
15
+ end
16
+
17
+ def run_migrations
18
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask 'Would you like to run the migrations now? [Y/n]')
19
+ if run_migrations
20
+ run 'bundle exec rake db:migrate'
21
+ else
22
+ puts 'Skipping rake db:migrate, don\'t forget to run it!'
23
+ end
24
+ end
25
+
26
+ def copy_initializer
27
+ copy_file 'fosdick.yml.template', 'config/fosdick.yml'
28
+ copy_file 'fosdick.rb', 'config/initializers/fosdick.rb'
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,6 @@
1
+ FOSDICK_CONFIG = YAML.load_file("#{Rails.root}/config/fosdick.yml")[Rails.env]
2
+ FOSDICK_STATES_EXCEPTIONS = {
3
+ 'U.S. Armed Forces – Americas' => 'AA',
4
+ 'U.S. Armed Forces – Europe' => 'AE',
5
+ 'U.S. Armed Forces – Pacific' => 'AP'
6
+ }
@@ -0,0 +1,18 @@
1
+ defaults: &defaults
2
+ test: true
3
+ ipost_uri: 'https://www.unitycart.com'
4
+ client_name: test
5
+ adcode: TEST
6
+ client_code: ad54LIADFJ2754
7
+ basic_auth:
8
+ login: CLIENTTEST
9
+ password: TESTPW22415
10
+
11
+ test:
12
+ <<: *defaults
13
+ development:
14
+ <<: *defaults
15
+ staging:
16
+ <<: *defaults
17
+ production:
18
+ <<: *defaults
@@ -0,0 +1,8 @@
1
+ require 'spree_core'
2
+ require 'spree_fosdick_integration/engine'
3
+
4
+ require 'spree_fosdick_integration/version'
5
+
6
+ module SpreeFosdickIntegration
7
+ # Your code goes here...
8
+ end
@@ -0,0 +1,22 @@
1
+ module SpreeFosdickIntegration
2
+ class Engine < Rails::Engine
3
+ require 'spree/core'
4
+ isolate_namespace Spree
5
+ engine_name 'spree_fosdick_integration'
6
+
7
+ config.autoload_paths += %W(#{config.root}/lib)
8
+
9
+ # use rspec for tests
10
+ config.generators do |g|
11
+ g.test_framework :rspec
12
+ end
13
+
14
+ def self.activate
15
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
16
+ Rails.configuration.cache_classes ? require(c) : load(c)
17
+ end
18
+ end
19
+
20
+ config.to_prepare &method(:activate).to_proc
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ FactoryGirl.define do
2
+ # Example adding this to your spec_helper will load these Factories for use:
3
+ # require 'spree_fosdick_integration/factories'
4
+
5
+ factory :fosdick_shipment, class: Spree::FosdickShipment do
6
+ shipment
7
+ fosdick_order_num '5177595188'
8
+ external_order_num 'H28547338764'
9
+ tracking_number nil
10
+ state 'sent'
11
+ confirmation_sent false
12
+ ship_date nil
13
+
14
+ factory :fosdick_shipment_shipped do
15
+ fosdick_order_num '00501201556352008'
16
+ tracking_number ['9261295598944533641919']
17
+ state 'shipped'
18
+ confirmation_sent true
19
+ ship_date {Time.zone.now}
20
+ end
21
+ end
22
+
23
+ factory :fosdick_exception, class: Spree::FosdickException do
24
+ fosdick_shipment
25
+
26
+ error_code 'Error'
27
+ message 'Error - Unknown Shipping Method: 0099DEX'
28
+ state 'error'
29
+ happened_at {Time.zone.now}
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module SpreeFosdickIntegration
2
+ VERSION = '0.0.3'
3
+ end
@@ -0,0 +1,7 @@
1
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
2
+
3
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
4
+ ENGINE_PATH = File.expand_path('../../lib/spree_abandoned_cart/engine', __FILE__)
5
+
6
+ require 'rails/all'
7
+ require 'rails/engine/commands'
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Spree::Admin::FosdickShipmentsController, type: :controller do
4
+ describe '#index' do
5
+ let!(:fosdick_shipment) { create :fosdick_shipment }
6
+ let!(:admin) { create :admin_user }
7
+ before { sign_in(admin) }
8
+
9
+ it 'should have 200 response' do
10
+ get :index, { use_route: SpreeFosdickIntegration }
11
+
12
+ expect(response.status).to eq(200)
13
+ expect(assigns[:fosdick_shipments]).to eq [fosdick_shipment]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Spree::FosdickException, type: :model do
4
+ describe 'relationships with other entities' do
5
+ it { should belong_to(:fosdick_shipment).class_name('Spree::FosdickShipment')}
6
+ end
7
+
8
+ describe 'instance methods' do
9
+ let(:shipment) {create :shipment}
10
+ let(:fosdick_shipment) {create :fosdick_shipment, shipment: shipment}
11
+ let(:fosdick_exception) {create :fosdick_exception, fosdick_shipment: fosdick_shipment}
12
+
13
+ it 'should update fosdick state of a shipment after creation' do
14
+ expect(fosdick_exception.shipment.fosdick_state).to eq 'exception'
15
+ end
16
+
17
+ it 'should return shipment for current fosdick exception' do
18
+ expect(fosdick_exception.shipment).to eq shipment
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Spree::FosdickShipment, type: :model do
4
+ describe 'relationships with other entities' do
5
+ it { should belong_to(:shipment).class_name('Spree::Shipment')}
6
+ it { should have_many(:fosdick_exceptions)}
7
+ it { should serialize(:tracking_number) }
8
+ end
9
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Spree::Shipment, type: :model do
4
+ describe 'relationships with other entities' do
5
+ it { should have_one(:fosdick_shipment)}
6
+ end
7
+
8
+ describe 'class methods' do
9
+ let!(:shipment) { create :shipment, shipped_at: nil, state: 'ready', send_atempt: 0 }
10
+ let!(:result) { JSON.parse(
11
+ { 'id'=>'H58716774464',
12
+ 'order_id'=>'R547700419',
13
+ 'email'=>'georgiana@armstrong.co.uk',
14
+ 'cost'=>100.0,
15
+ 'status'=>'ready',
16
+ 'stock_location'=>'NY Warehouse',
17
+ 'shipping_method'=>nil,
18
+ 'tracking'=>'U10000',
19
+ 'placed_on'=>'',
20
+ 'shipped_at'=>nil,
21
+ 'totals'=>
22
+ { 'item'=>'$0.00',
23
+ 'adjustment'=>'$0.00',
24
+ 'tax'=>'$0.00',
25
+ 'shipping'=>'$0.00',
26
+ 'payment'=>'0.0',
27
+ 'order'=>'$0.00'},
28
+ 'updated_at'=>'2015-11-13T13:18:31Z',
29
+ 'channel'=>'spree',
30
+ 'items'=>[],
31
+ 'shipping_method_code'=>'UPS_GROUND',
32
+ 'billing_address'=>
33
+ { 'firstname'=>'John',
34
+ 'lastname'=>'Doe',
35
+ 'address1'=>'10 Lovely Street',
36
+ 'address2'=>'Northwest',
37
+ 'zipcode'=>'35005',
38
+ 'city'=>'Herndon',
39
+ 'state'=>'AL',
40
+ 'country'=>'US',
41
+ 'phone'=>'555-555-0199'},
42
+ 'shipping_address'=>nil}.to_json) }
43
+
44
+ before { allow(ActiveModel::ArraySerializer).to receive(:new).and_return([result]) }
45
+
46
+ it 'should perform shipments to fosdick' do
47
+ expect(Spree::Shipment.perform_fosdick_shipments).to eq [result]
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe ExceptionLogger, type: :services do
4
+ let!(:fosdick_shipment) { create :fosdick_shipment }
5
+
6
+ it 'should log exceptions' do
7
+ ExceptionLogger.new.log('Error', 'Error - Unknown Shipping Method: 0099DEX', fosdick_shipment.id)
8
+
9
+ expect(Spree::FosdickException.last.fosdick_shipment).to eq fosdick_shipment
10
+ end
11
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Fosdick::Processor, type: :services do
4
+ before { stub_const 'FOSDICK_CONFIG', {
5
+ 'test' =>true,
6
+ 'ipost_uri' =>'https://www.unitycart.com',
7
+ 'client_name' =>'test',
8
+ 'adcode' =>'TEST',
9
+ 'client_code' =>'TEST_CODE',
10
+ 'basic_auth' =>{
11
+ 'login' =>'TESTLOG',
12
+ 'password'=>'password'}
13
+ }}
14
+
15
+ let(:config) { FOSDICK_CONFIG }
16
+ let!(:order_ready_to_ship) {create :order_ready_to_ship}
17
+
18
+ describe 'sender service logic' do
19
+ let(:doc) {Fosdick::Documents::Shipment.new(Spree::Shipment.perform_fosdick_shipments.first, config).to_xml}
20
+ let(:response) { '<UnitycartOrderResponse xml:lang="en-US">
21
+ <OrderResponse ExternalID="YourID">
22
+ <SuccessCode>True</SuccessCode>
23
+ <OrderNumber>2679987865</OrderNumber>
24
+ </OrderResponse>
25
+ </UnitycartOrderResponse>'}
26
+
27
+ before {
28
+ stub_request(:post, "https://www.unitycart.com/test/cart/ipost.asp").
29
+ with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
30
+ to_return(:status => 200, :body => response, :headers => {})
31
+ }
32
+
33
+ it 'should send new shipment to fosdick' do
34
+ eligible_shipment = Spree::Shipment.perform_fosdick_shipments.last
35
+
36
+ expect{Fosdick::Processor.send_shipment(eligible_shipment, config)}.to change{Spree::FosdickShipment.count}.from(0).to(1)
37
+ expect(Spree::FosdickShipment.last.external_order_num).to eq order_ready_to_ship.shipments.first.number
38
+ expect(Spree::FosdickShipment.last.fosdick_order_num).to eq '2679987865'
39
+ expect(Spree::FosdickShipment.last.state).to eq 'sent'
40
+ expect(Spree::FosdickShipment.last.tracking_number).to eq nil
41
+ expect(Spree::FosdickShipment.last.ship_date).to eq nil
42
+
43
+ expect(Spree::Shipment.last.state).to eq 'shipped'
44
+ expect(Spree::Shipment.last.fosdick_state).to eq 'success'
45
+ end
46
+ end
47
+
48
+ describe 'receiver service logic' do
49
+ let!(:fosdick_shipment) {create :fosdick_shipment, external_order_num: 'H0011001',
50
+ spree_shipment_id: order_ready_to_ship.shipments.last.id,
51
+ tracking_number: nil}
52
+ let(:response) { '[
53
+ {
54
+ "fosdick_order_num": "00101201506460001",
55
+ "external_order_num": "H0011001",
56
+ "ship_date": "2015-02-06",
57
+ "trackings": [
58
+ {
59
+ "tracking_num": "9274899998944522337",
60
+ "carrier_code": "92",
61
+ "carrier_name": "FEDEX SMART POST"
62
+ },
63
+ {
64
+ "tracking_num": "9274899998944599999",
65
+ "carrier_code": "92",
66
+ "carrier_name": "FEDEX SMART POST"
67
+ }
68
+ ]
69
+ }]'}
70
+
71
+ before {
72
+ stub_request(:get, "https://www.customerstatus.com/fosdickapi/shipmentdetail.json?external_order_num=#{fosdick_shipment.external_order_num}").
73
+ with(:headers => {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
74
+ to_return(:status => 200, :body => response, :headers => {})
75
+ }
76
+
77
+ before { Spree::Shipment.last.update_column(:number, 'H0011001')}
78
+ before { ActionMailer::Base.deliveries.clear }
79
+
80
+ it 'should receive shipment details from fosdick' do
81
+ Fosdick::Processor.receive_shipment({external_order_num: 'H0011001'}, 'shipmentdetail.json' )
82
+
83
+ expect(Spree::FosdickShipment.last.tracking_number).to eq (['9274899998944522337', '9274899998944599999'])
84
+ expect(Spree::FosdickShipment.last.confirmation_sent).to eq true
85
+ expect(Spree::FosdickShipment.last.ship_date).to eq '2015-02-06 00:00:00'
86
+ expect(Spree::FosdickShipment.last.fosdick_order_num).to eq '00101201506460001'
87
+ end
88
+
89
+ it 'should notify user by email that order shipped' do
90
+
91
+ expect{Fosdick::Processor.receive_shipment({external_order_num: 'H0011001'}, 'shipmentdetail.json' )}.to change{ActionMailer::Base.deliveries.count}.from(0).to(1)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+
4
+ RSpec.describe Fosdick::Receiver, type: :services do
5
+ before { stub_const 'FOSDICK_CONFIG', {
6
+ 'test' =>true,
7
+ 'ipost_uri' =>'https://www.unitycart.com',
8
+ 'client_name' =>'test',
9
+ 'adcode' =>'TEST',
10
+ 'client_code' =>'TEST_CODE',
11
+ 'basic_auth' =>{
12
+ 'login' =>'TESTLOG',
13
+ 'password'=>'password'}
14
+ }}
15
+
16
+ let(:fosdick_shipment) {create :fosdick_shipment}
17
+ let(:config) { FOSDICK_CONFIG }
18
+ let(:response) { '[
19
+ {
20
+ "fosdick_order_num": "00101201506460001",
21
+ "external_order_num": "0011001",
22
+ "ship_date": "2015-02-06",
23
+ "trackings": [
24
+ {
25
+ "tracking_num": "9274899998944522337",
26
+ "carrier_code": "92",
27
+ "carrier_name": "FEDEX SMART POST"
28
+ },
29
+ {
30
+ "tracking_num": "9274899998944599999",
31
+ "carrier_code": "92",
32
+ "carrier_name": "FEDEX SMART POST"
33
+ }
34
+ ]
35
+ }]'}
36
+
37
+ before {
38
+ stub_request(:get, "https://www.customerstatus.com/fosdickapi/shipmentdetail.json?external_order_num=#{fosdick_shipment.external_order_num}").
39
+ with(:headers => {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
40
+ to_return(:status => 200, :body => response, :headers => {})
41
+ }
42
+ it {expect(Fosdick::Receiver.new('shipmentdetail.json', {external_order_num: fosdick_shipment.external_order_num}).call_api(config)).to eq JSON.parse(response)}
43
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Fosdick::Sender, type: :services do
4
+ before { stub_const 'FOSDICK_CONFIG', {
5
+ 'test' =>true,
6
+ 'ipost_uri' =>'https://www.unitycart.com',
7
+ 'client_name' =>'test',
8
+ 'adcode' =>'TEST',
9
+ 'client_code' =>'TEST_CODE',
10
+ 'basic_auth' =>{
11
+ 'login' =>'TESTLOG',
12
+ 'password'=>'password'}
13
+ }}
14
+
15
+ let!(:order_ready_to_ship) {create :order_ready_to_ship}
16
+
17
+ let(:config) { FOSDICK_CONFIG }
18
+ let(:doc) {Fosdick::Documents::Shipment.new(Spree::Shipment.perform_fosdick_shipments.first, config).to_xml}
19
+ let(:response) { '<UnitycartOrderResponse xml:lang="en-US">
20
+ <OrderResponse ExternalID="YourID">
21
+ <SuccessCode>True</SuccessCode>
22
+ <OrderNumber>2679987865</OrderNumber>
23
+ </OrderResponse>
24
+ </UnitycartOrderResponse>'}
25
+
26
+ before {
27
+ stub_request(:post, "https://www.unitycart.com/test/cart/ipost.asp").
28
+ with(:body => doc,
29
+ :headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
30
+ to_return(:status => 200, :body => response, :headers => {})
31
+ }
32
+
33
+ it {expect(Fosdick::Sender.send_doc(doc, config)).to eq '2679987865'}
34
+ end