sports_south 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3ad70665245178cc174e50a43bc3625f0e8aa15e
4
+ data.tar.gz: 91cbd940f6eea3b0e42353e213aecbeca5bf6d57
5
+ SHA512:
6
+ metadata.gz: 7004bfbf4ed15679d6639c0dcbd02f92144c41cd0a0f3d1bc41c430b81a7f1930f13a9c68313f86ba379ecd7c75653a2de71e9f1a6424bff9a587f1f645108ab
7
+ data.tar.gz: affda4a5bbd8c6e2141dae605290c8742f8b4ecc4d0d2d845f9cf07c559dac09696eb0d587ccd0b3f1f8a7c72db24a3a26ff0bb21c266139c47ed25cea09f85d
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ sports_south
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.0
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.0
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sports_south.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Dale Campbell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # SportsSouth
2
+
3
+ Sports South API Ruby library.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'sports_south'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install sports_south
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Development
26
+
27
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
28
+
29
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
30
+
31
+ ## Contributing
32
+
33
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ammoready/sports_south.
34
+
35
+
36
+ ## License
37
+
38
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
39
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "sports_south"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,67 @@
1
+ module SportsSouth
2
+ # Holds methods common to all classes.
3
+ class Base
4
+
5
+ protected
6
+
7
+ # Wrapper to `self.requires!` that can be used as an instance method.
8
+ def requires!(*args)
9
+ self.class.requires!(*args)
10
+ end
11
+
12
+ def self.requires!(hash, *params)
13
+ params.each do |param|
14
+ if param.is_a?(Array)
15
+ raise ArgumentError.new("Missing required parameter: #{param.first}") unless hash.has_key?(param.first)
16
+
17
+ valid_options = param[1..-1]
18
+ raise ArgumentError.new("Parameter: #{param.first} must be one of: #{valid_options.join(', ')}") unless valid_options.include?(hash[param.first])
19
+ else
20
+ raise ArgumentError.new("Missing required parameter: #{param}") unless hash.has_key?(param)
21
+ end
22
+ end
23
+ end
24
+
25
+ # Returns a hash of common form params.
26
+ def self.form_params(options = {})
27
+ {
28
+ UserName: options[:username],
29
+ Password: options[:password],
30
+ CustomerNumber: options[:customer_number],
31
+ Source: options[:source],
32
+ }
33
+ end
34
+ def form_params(*args); self.class.form_params(*args); end
35
+
36
+ # Returns the Net::HTTP and Net::HTTP::Post objects.
37
+ #
38
+ # http, request = get_http_and_request(<api_url>, <endpoint>)
39
+ def self.get_http_and_request(api_url, endpoint)
40
+ uri = URI([api_url, endpoint].join)
41
+ http = Net::HTTP.new(uri.host, uri.port)
42
+ request = Net::HTTP::Post.new(uri.request_uri)
43
+
44
+ return http, request
45
+ end
46
+ def get_http_and_request(*args); self.class.get_http_and_request(*args); end
47
+
48
+ def self.content_for(xml_doc, field)
49
+ node = xml_doc.css(field).first
50
+ node.nil? ? nil : node.content.strip
51
+ end
52
+ def content_for(*args); self.class.content_for(*args); end
53
+
54
+ def self.not_authenticated?(xml_doc)
55
+ msg = content_for(xml_doc, 'ERROR')
56
+ (msg =~ /Authentication Failed/i) || (msg =~ /NOT AUTHENTICATED/i)
57
+ end
58
+ def not_authenticated?(*args); self.class.not_authenticated?(*args); end
59
+
60
+ # HACK: We have to fix the malformed XML response SS is currently returning.
61
+ def self.sanitize_response(response)
62
+ response.body.gsub('&lt;', '<').gsub('&gt;', '>')
63
+ end
64
+ def sanitize_response(*args); self.class.sanitize_response(*args); end
65
+
66
+ end
67
+ end
@@ -0,0 +1,33 @@
1
+ module SportsSouth
2
+ class Brand < Base
3
+
4
+ API_URL = 'http://webservices.theshootingwarehouse.com/smart/inventory.asmx'
5
+
6
+ def self.all(options = {})
7
+ requires!(options, :username, :password, :source, :customer_number)
8
+
9
+ http, request = get_http_and_request(API_URL, '/BrandUpdate')
10
+
11
+ request.set_form_data(form_params(options))
12
+ response = http.request(request)
13
+ body = sanitize_response(response)
14
+ xml_doc = Nokogiri::XML(body)
15
+
16
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
17
+
18
+ brands = []
19
+
20
+ xml_doc.css('Table').each do |brand|
21
+ brands << {
22
+ id: content_for(brand, 'BRDNO'),
23
+ name: content_for(brand, 'BRDNM'),
24
+ url: content_for(brand, 'BRDURL'),
25
+ item_count: content_for(brand, 'ITCOUNT'),
26
+ }
27
+ end
28
+
29
+ brands
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,52 @@
1
+ module SportsSouth
2
+ class Category < Base
3
+ API_URL = 'http://webservices.theshootingwarehouse.com/smart/inventory.asmx'
4
+
5
+ def self.all(options = {})
6
+ requires!(options, :username, :password, :source, :customer_number)
7
+
8
+ http, request = get_http_and_request(API_URL, '/CategoryUpdate')
9
+
10
+ request.set_form_data(form_params(options))
11
+
12
+ response = http.request(request)
13
+ body = sanitize_response(response)
14
+ xml_doc = Nokogiri::XML(body)
15
+
16
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
17
+
18
+ categories = []
19
+
20
+ xml_doc.css('Table').each do |category|
21
+ categories << {
22
+ category_id: content_for(category, 'CATID'),
23
+ description: content_for(category, 'CATDES'),
24
+ department_id: content_for(category, 'DEPID'),
25
+ department_description: content_for(category, 'DEP'),
26
+ attribute_1: content_for(category, 'ATTR1'),
27
+ attribute_2: content_for(category, 'ATTR2'),
28
+ attribute_3: content_for(category, 'ATTR3'),
29
+ attribute_4: content_for(category, 'ATTR4'),
30
+ attribute_5: content_for(category, 'ATTR5'),
31
+ attribute_6: content_for(category, 'ATTR6'),
32
+ attribute_7: content_for(category, 'ATTR7'),
33
+ attribute_8: content_for(category, 'ATTR8'),
34
+ attribute_9: content_for(category, 'ATTR9'),
35
+ attribute_10: content_for(category, 'ATTR0'),
36
+ attribute_11: content_for(category, 'ATTR11'),
37
+ attribute_12: content_for(category, 'ATTR12'),
38
+ attribute_13: content_for(category, 'ATTR13'),
39
+ attribute_14: content_for(category, 'ATTR14'),
40
+ attribute_15: content_for(category, 'ATTR15'),
41
+ attribute_16: content_for(category, 'ATTR16'),
42
+ attribute_17: content_for(category, 'ATTR17'),
43
+ attribute_18: content_for(category, 'ATTR18'),
44
+ attribute_19: content_for(category, 'ATTR19'),
45
+ attribute_20: content_for(category, 'ATTR20'),
46
+ }
47
+ end
48
+
49
+ categories
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,32 @@
1
+ module SportsSouth
2
+ class Image < Base
3
+
4
+ API_URL = 'http://webservices.theshootingwarehouse.com/smart/images.asmx'
5
+
6
+ def self.urls(item_number, options = {})
7
+ requires!(options, :username, :password, :source, :customer_number)
8
+
9
+ http, request = get_http_and_request(API_URL, '/GetPictureURLs')
10
+
11
+ request.set_form_data(form_params(options).merge({
12
+ ItemNumber: item_number
13
+ }))
14
+
15
+ response = http.request(request)
16
+ body = sanitize_response(response)
17
+ xml_doc = Nokogiri::XML(body)
18
+
19
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
20
+
21
+ images = {}
22
+
23
+ xml_doc.css('Table').each do |image|
24
+ size = content_for(image, 'ImageSize').to_sym
25
+ images[size] = content_for(image, 'Link')
26
+ end
27
+
28
+ images
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,171 @@
1
+ module SportsSouth
2
+ class Inventory < Base
3
+
4
+ API_URL = 'http://webservices.theshootingwarehouse.com/smart/inventory.asmx'
5
+
6
+ CATALOG_CODES = {
7
+ 'S' => :special,
8
+ 'C' => :closeout,
9
+ 'F' => :flyer,
10
+ 'B' => :buyers_special,
11
+ 'N' => :net_price,
12
+ }
13
+
14
+ ITEM_TYPES = {
15
+ '1' => :handgun,
16
+ '2' => :long_gun,
17
+ '3' => :accessory,
18
+ '4' => :ammunition,
19
+ '5' => :optics,
20
+ '6' => :archery,
21
+ '7' => :reloading,
22
+ }
23
+
24
+ def self.all(options = {})
25
+ requires!(options, :username, :password, :source, :customer_number)
26
+
27
+ options[:last_update] ||= '1/1/1990' # Return full catalog.
28
+ options[:last_item] ||= '-1' # Return all items.
29
+
30
+ http, request = get_http_and_request(API_URL, '/DailyItemUpdate')
31
+
32
+ request.set_form_data(form_params(options).merge({
33
+ LastUpdate: options[:last_update],
34
+ LastItem: options[:last_item],
35
+ }))
36
+
37
+ response = http.request(request)
38
+ body = sanitize_response(response)
39
+ xml_doc = Nokogiri::XML(body)
40
+
41
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
42
+
43
+ items = []
44
+
45
+ xml_doc.css('Table').each do |item|
46
+ items << {
47
+ item_number: content_for(item, 'ITEMNO'),
48
+ description: content_for(item, 'IDESC'),
49
+ manufacturer_sequence: content_for(item, 'IMFSEQ'),
50
+ manufacturer_number: content_for(item, 'IMFGNO'),
51
+ catalog_sequence: content_for(item, 'CSEQ'),
52
+ item_type: ITEM_TYPES[content_for(item, 'ITYPE')],
53
+ short_description: content_for(item, 'SHDESC'),
54
+ unit_of_measure: content_for(item, 'UOM'),
55
+ catalog_price: content_for(item, 'PRC1'),
56
+ customer_price: content_for(item, 'CPRC'),
57
+ quantity_on_hand: content_for(item, 'QTYOH'),
58
+ weight_per_box: content_for(item, 'WTPBX'),
59
+ upc: content_for(item, 'ITUPC'),
60
+ manufacturer_item_number: content_for(item, 'MFGINO'),
61
+ scan_name_1: content_for(item, 'SCNAM1'),
62
+ scan_name_2: content_for(item, 'SCNAM2'),
63
+ catalog_code: CATALOG_CODES[content_for(item, 'CATCD')],
64
+ mapp_price_code: content_for(item, 'MFPRTYP'),
65
+ mapp_price: content_for(item, 'MFPRC'),
66
+ category_id: content_for(item, 'CATID'),
67
+ text_reference_number: content_for(item, 'TXTREF'),
68
+ picture_reference_number: content_for(item, 'PICREF'),
69
+ brand_id: content_for(item, 'ITBRDNO'),
70
+ item_model_number: content_for(item, 'IMODEL'),
71
+ item_purpose: content_for(item, 'IPURPOSE'),
72
+ series_description: content_for(item, 'SERIES'),
73
+ item_length: content_for(item, 'LENGTH'),
74
+ item_height: content_for(item, 'HEIGHT'),
75
+ item_width: content_for(item, 'WIDTH'),
76
+ item_ships_hazmat_air: content_for(item, 'HAZAIR'),
77
+ item_ships_hazmat_ground: content_for(item, 'HAZGRND'),
78
+ date_of_last_change: content_for(item, 'CHGDTE'),
79
+ date_added: content_for(item, 'CHGDTE'),
80
+ attribute_1: content_for(item, 'ITATR1'),
81
+ attribute_2: content_for(item, 'ITATR2'),
82
+ attribute_3: content_for(item, 'ITATR3'),
83
+ attribute_4: content_for(item, 'ITATR4'),
84
+ attribute_5: content_for(item, 'ITATR5'),
85
+ attribute_6: content_for(item, 'ITATR6'),
86
+ attribute_7: content_for(item, 'ITATR7'),
87
+ attribute_8: content_for(item, 'ITATR8'),
88
+ attribute_9: content_for(item, 'ITATR9'),
89
+ attribute_10: content_for(item, 'ITATR0'),
90
+ attribute_11: content_for(item, 'ITATR11'),
91
+ attribute_12: content_for(item, 'ITATR12'),
92
+ attribute_13: content_for(item, 'ITATR13'),
93
+ attribute_14: content_for(item, 'ITATR14'),
94
+ attribute_15: content_for(item, 'ITATR15'),
95
+ attribute_16: content_for(item, 'ITATR16'),
96
+ attribute_17: content_for(item, 'ITATR17'),
97
+ attribute_18: content_for(item, 'ITATR18'),
98
+ attribute_19: content_for(item, 'ITATR19'),
99
+ attribute_20: content_for(item, 'ITATR20'),
100
+ }
101
+ end
102
+
103
+ items
104
+ end
105
+
106
+ def self.get_text(item_number, options = {})
107
+ requires!(options, :username, :password, :source, :customer_number)
108
+
109
+ http, request = get_http_and_request(API_URL, '/GetText')
110
+
111
+ request.set_form_data(form_params(options).merge({ ItemNumber: item_number }))
112
+
113
+ response = http.request(request)
114
+ body = sanitize_response(response)
115
+ xml_doc = Nokogiri::XML(body)
116
+
117
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
118
+
119
+ { catalog_text: content_for(xml_doc, 'CATALOGTEXT') }
120
+ end
121
+
122
+ def self.inquiry(item_number, options = {})
123
+ requires!(options, :username, :password, :source, :customer_number)
124
+
125
+ http, request = get_http_and_request(API_URL, '/OnhandInquiry')
126
+
127
+ request.set_form_data(form_params(options).merge({ ItemNumber: item_number }))
128
+
129
+ response = http.request(request)
130
+ body = sanitize_response(response)
131
+ xml_doc = Nokogiri::XML(body)
132
+
133
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
134
+
135
+ {
136
+ item_number: content_for(xml_doc, 'I'),
137
+ quantity_on_hand: content_for(xml_doc, 'Q').to_i,
138
+ catalog_price: content_for(xml_doc, 'P'),
139
+ customer_price: content_for(xml_doc, 'C'),
140
+ }
141
+ end
142
+
143
+ # This method accepts an Array of +item_numbers+.
144
+ def self.onhand_update_by_csv(item_numbers, options = {})
145
+ requires!(options, :username, :password, :source, :customer_number)
146
+
147
+ http, request = get_http_and_request(API_URL, '/OnhandUpdatebyCSV')
148
+
149
+ request.set_form_data(form_params(options).merge({ CSVItems: item_numbers.join(',') }))
150
+
151
+ response = http.request(request)
152
+ body = sanitize_response(response)
153
+ xml_doc = Nokogiri::XML(body)
154
+
155
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
156
+
157
+ items = []
158
+
159
+ xml_doc.css('Table').each do |item|
160
+ items << {
161
+ item_number: content_for(item, 'I'),
162
+ quantity: content_for(item, 'Q'),
163
+ price: content_for(item, 'P'),
164
+ }
165
+ end
166
+
167
+ items
168
+ end
169
+
170
+ end
171
+ end
@@ -0,0 +1,51 @@
1
+ module SportsSouth
2
+ class Invoice < Base
3
+
4
+ API_URL = 'http://webservices.theshootingwarehouse.com/smart/invoices.asmx'
5
+
6
+ attr_reader :response_body
7
+ attr_reader :po_number
8
+
9
+ def self.find_by_po_number(po_number, options = {})
10
+ requires!(options, :username, :password, :source, :customer_number)
11
+ new(options.merge({po_number: po_number}))
12
+ end
13
+
14
+ def initialize(options = {})
15
+ requires!(options, :username, :password, :source, :customer_number)
16
+ @options = options
17
+ @po_number = options[:po_number]
18
+ end
19
+
20
+ def tracking
21
+ raise StandardError.new("No @po_number present.") if @po_number.nil?
22
+
23
+ http, request = get_http_and_request(API_URL, '/GetTrackingByPo')
24
+
25
+ request.set_form_data(form_params(@options).merge({
26
+ PONumber: @po_number,
27
+ }))
28
+
29
+ response = http.request(request)
30
+ body = sanitize_response(response)
31
+ xml_doc = Nokogiri::XML(body)
32
+
33
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
34
+
35
+ @response_body = body
36
+
37
+ @tracking = {
38
+ invoice_number: content_for(xml_doc, 'INVNO'),
39
+ customer_number: content_for(xml_doc, 'CUSNO'),
40
+ po_number: content_for(xml_doc, 'PONBR'),
41
+ ship_date: content_for(xml_doc, 'SHPDTE'),
42
+ tracking_number: content_for(xml_doc, 'TRACKNO'),
43
+ package_weight: content_for(xml_doc, 'PKGWT'),
44
+ cod_amount: content_for(xml_doc, 'CODAMT'),
45
+ hazmat: content_for(xml_doc, 'HAZMAT'),
46
+ ship_amount: content_for(xml_doc, 'SHPAMT'),
47
+ ship_service: content_for(xml_doc, 'SERVICE'),
48
+ }
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,200 @@
1
+ module SportsSouth
2
+ class Order < Base
3
+
4
+ API_URL = 'http://webservices.theshootingwarehouse.com/smart/orders.asmx'
5
+
6
+ SHIP_VIA = {
7
+ ground: '',
8
+ next_day: 'N',
9
+ two_day: '2',
10
+ three_day: '3',
11
+ saturday: 'S',
12
+ }
13
+
14
+ # D=Placed, E=Error placing Order, R=Placed-Verifying, W=Open
15
+ STATUS = {
16
+ 'D' => :placed,
17
+ 'E' => :error_placing_order,
18
+ 'R' => :placed_verifying,
19
+ 'W' => :open,
20
+ }
21
+
22
+ attr_reader :response_body
23
+ attr_reader :order_number
24
+
25
+ def self.find(order_number, options = {})
26
+ requires!(options, :username, :password, :source, :customer_number)
27
+ new(options.merge({order_number: order_number}))
28
+ end
29
+
30
+ def initialize(options = {})
31
+ requires!(options, :username, :password, :source, :customer_number)
32
+ @options = options
33
+ @order_number = options[:order_number]
34
+ end
35
+
36
+ def add_header(header = {})
37
+ requires!(header, :purchase_order, :sales_message, :shipping)
38
+ header[:customer_order_number] = header[:purchase_order] unless header.has_key?(:customer_order_number)
39
+ header[:adult_signature] = false unless header.has_key?(:adult_signature)
40
+ header[:signature] = false unless header.has_key?(:signature)
41
+ header[:insurance] = false unless header.has_key?(:insurance)
42
+
43
+ requires!(header[:shipping], :name, :address_one, :city, :state, :zip, :phone)
44
+ header[:shipping][:attn] = header[:shipping][:name] unless header.has_key?(:attn)
45
+ header[:shipping][:via] = SHIP_VIA[:ground] unless header.has_key?(:ship_via)
46
+ header[:shipping][:address_two] = '' unless header[:shipping].has_key?(:address_two)
47
+
48
+ http, request = get_http_and_request(API_URL, '/AddHeader')
49
+
50
+ request.set_form_data(form_params(@options).merge({
51
+ PO: header[:purchase_order],
52
+ CustomerOrderNumber: header[:customer_order_number],
53
+ SalesMessage: header[:sales_message],
54
+
55
+ ShipVia: header[:shipping][:via],
56
+ ShipToName: header[:shipping][:name],
57
+ ShipToAttn: header[:shipping][:attn],
58
+ ShipToAddr1: header[:shipping][:address_one],
59
+ ShipToAddr2: header[:shipping][:address_two],
60
+ ShipToCity: header[:shipping][:city],
61
+ ShipToState: header[:shipping][:state],
62
+ ShipToZip: header[:shipping][:zip],
63
+ ShipToPhone: header[:shipping][:phone],
64
+
65
+ AdultSignature: header[:adult_signature],
66
+ Signature: header[:signature],
67
+ Insurance: header[:insurance],
68
+ }))
69
+
70
+ response = http.request(request)
71
+ xml_doc = Nokogiri::XML(response.body)
72
+
73
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
74
+
75
+ @response_body = response.body
76
+ @order_number = xml_doc.content
77
+ end
78
+
79
+ def add_detail(detail = {})
80
+ raise StandardError.new("No @order_number present.") if @order_number.nil?
81
+
82
+ requires!(detail, :ss_item_number, :price)
83
+ detail[:quantity] = 1 unless detail.has_key?(:quantity)
84
+ detail[:item_number] = '' unless detail.has_key?(:item_number)
85
+ detail[:item_description] = '' unless detail.has_key?(:item_description)
86
+
87
+ http, request = get_http_and_request(API_URL, '/AddDetail')
88
+
89
+ request.set_form_data(form_params(@options).merge({
90
+ OrderNumber: @order_number,
91
+ SSItemNumber: detail[:ss_item_number],
92
+ Quantity: detail[:quantity],
93
+ OrderPrice: detail[:price],
94
+ CustomerItemNumber: detail[:item_number],
95
+ CustomerItemDescription: detail[:item_description],
96
+ }))
97
+
98
+ response = http.request(request)
99
+ xml_doc = Nokogiri::XML(response.body)
100
+
101
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
102
+
103
+ @response_body = response.body
104
+
105
+ xml_doc.content == 'true'
106
+ end
107
+
108
+ def submit!
109
+ raise StandardError.new("No @order_number present.") if @order_number.nil?
110
+
111
+ http, request = get_http_and_request(API_URL, '/Submit')
112
+
113
+ request.set_form_data(form_params(@options).merge({
114
+ OrderNumber: @order_number,
115
+ }))
116
+
117
+ response = http.request(request)
118
+ xml_doc = Nokogiri::XML(response.body)
119
+
120
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
121
+
122
+ @response_body = response.body
123
+
124
+ xml_doc.content == 'true'
125
+ end
126
+
127
+ def header
128
+ raise StandardError.new("No @order_number present.") if @order_number.nil?
129
+
130
+ http, request = get_http_and_request(API_URL, '/GetHeader')
131
+
132
+ request.set_form_data(form_params(@options).merge({
133
+ CustomerOrderNumber: @order_number,
134
+ OrderNumber: @order_number
135
+ }))
136
+
137
+ response = http.request(request)
138
+ body = sanitize_response(response)
139
+ xml_doc = Nokogiri::XML(body)
140
+
141
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
142
+
143
+ @response_body = body
144
+
145
+ @header = {
146
+ system_order_number: content_for(xml_doc, 'ORDNO'),
147
+ customer_number: content_for(xml_doc, 'ORCUST'),
148
+ order_po_number: content_for(xml_doc, 'ORPO'),
149
+ customer_order_number: content_for(xml_doc, 'ORCONO'),
150
+ order_date: content_for(xml_doc, 'ORDATE'),
151
+ message: content_for(xml_doc, 'MSG'),
152
+ air_code: content_for(xml_doc, 'ORAIR'),
153
+ order_source: content_for(xml_doc, 'ORSRC'),
154
+ status: STATUS[content_for(xml_doc, 'STATUS')],
155
+ }
156
+ end
157
+
158
+ def details
159
+ raise StandardError.new("No @order_number present.") if @order_number.nil?
160
+
161
+ http, request = get_http_and_request(API_URL, '/GetDetail')
162
+
163
+ request.set_form_data(form_params(@options).merge({
164
+ CustomerOrderNumber: @order_number,
165
+ OrderNumber: @order_number,
166
+ }))
167
+
168
+ response = http.request(request)
169
+ body = sanitize_response(response)
170
+ xml_doc = Nokogiri::XML(body)
171
+
172
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
173
+
174
+ @response_body = body
175
+ @details = []
176
+
177
+ xml_doc.css('Table').each do |table|
178
+ @details << {
179
+ system_order_number: content_for(table, 'ORDNO'),
180
+ order_line_number: content_for(table, 'ORLINE'),
181
+ customer_number: content_for(table, 'ORCUST'),
182
+ order_item_number: content_for(table, 'ORITEM'),
183
+ order_quantity: content_for(table, 'ORQTY'),
184
+ order_price: content_for(table, 'ORPRC'),
185
+ ship_quantity: content_for(table, 'ORQTYF'),
186
+ ship_price: content_for(table, 'ORPRCF'),
187
+ customer_item_number: content_for(table, 'ORCUSI'),
188
+ customer_description: content_for(table, 'ORCUSD'),
189
+ item_description: content_for(table, 'IDESC'),
190
+ quantity_on_hand: content_for(table, 'QTYOH'),
191
+ line_detail_comment: content_for(table, 'ORDCMT'),
192
+ line_detail_po_number: content_for(table, 'ORPO2'),
193
+ }
194
+ end
195
+
196
+ @details
197
+ end
198
+
199
+ end
200
+ end
@@ -0,0 +1,44 @@
1
+ module SportsSouth
2
+ class User < Base
3
+
4
+ API_URL = 'http://webservices.theshootingwarehouse.com/smart/users.asmx'
5
+
6
+ attr_reader :response_body
7
+
8
+ def initialize(options = {})
9
+ requires!(options, :username, :password, :source, :customer_number)
10
+ @options = options
11
+ end
12
+
13
+ def authenticated?
14
+ # Use #email_preferences as a check, since there's no official way of just testing credentials.
15
+ email_preferences
16
+ true
17
+ rescue SportsSouth::NotAuthenticated
18
+ false
19
+ end
20
+
21
+ def email_preferences
22
+ http, request = get_http_and_request(API_URL, '/GetEmailPrefs')
23
+
24
+ request.set_form_data(form_params(@options))
25
+
26
+ response = http.request(request)
27
+ body = sanitize_response(response)
28
+ xml_doc = Nokogiri::XML(body)
29
+
30
+ raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
31
+
32
+ @response_body = body
33
+
34
+ @email_preferences = {
35
+ default_email: content_for(xml_doc, 'CUEML'),
36
+ statement_email: content_for(xml_doc, 'STMNTS'),
37
+ marketing_email: content_for(xml_doc, 'MKTG'),
38
+ email_statements: (content_for(xml_doc, 'EMLSTM') == 'E'),
39
+ email_invoices: (content_for(xml_doc, 'EMLINV') == 'E'),
40
+ }
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ module SportsSouth
2
+ VERSION = "0.10.0"
3
+ end
@@ -0,0 +1,17 @@
1
+ require 'sports_south/version'
2
+
3
+ require 'net/http'
4
+ require 'nokogiri'
5
+
6
+ require 'sports_south/base'
7
+ require 'sports_south/brand'
8
+ require 'sports_south/category'
9
+ require 'sports_south/image'
10
+ require 'sports_south/inventory'
11
+ require 'sports_south/invoice'
12
+ require 'sports_south/order'
13
+ require 'sports_south/user'
14
+
15
+ module SportsSouth
16
+ class NotAuthenticated < StandardError; end
17
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sports_south/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sports_south"
8
+ spec.version = SportsSouth::VERSION
9
+ spec.authors = ["Dale Campbell"]
10
+ spec.email = ["oshuma@gmail.com"]
11
+ spec.summary = %q{Sports South API Ruby library.}
12
+ spec.description = %q{}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "nokogiri", "~> 1.6"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.10"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec", "~> 3.3"
26
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sports_south
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.10.0
5
+ platform: ruby
6
+ authors:
7
+ - Dale Campbell
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-08-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.3'
69
+ description: ''
70
+ email:
71
+ - oshuma@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".ruby-gemset"
79
+ - ".ruby-version"
80
+ - ".travis.yml"
81
+ - Gemfile
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - bin/console
86
+ - bin/setup
87
+ - lib/sports_south.rb
88
+ - lib/sports_south/base.rb
89
+ - lib/sports_south/brand.rb
90
+ - lib/sports_south/category.rb
91
+ - lib/sports_south/image.rb
92
+ - lib/sports_south/inventory.rb
93
+ - lib/sports_south/invoice.rb
94
+ - lib/sports_south/order.rb
95
+ - lib/sports_south/user.rb
96
+ - lib/sports_south/version.rb
97
+ - sports_south.gemspec
98
+ homepage: ''
99
+ licenses:
100
+ - MIT
101
+ metadata: {}
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.4.5
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Sports South API Ruby library.
122
+ test_files: []