sports_south 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/sports_south/base.rb +67 -0
- data/lib/sports_south/brand.rb +33 -0
- data/lib/sports_south/category.rb +52 -0
- data/lib/sports_south/image.rb +32 -0
- data/lib/sports_south/inventory.rb +171 -0
- data/lib/sports_south/invoice.rb +51 -0
- data/lib/sports_south/order.rb +200 -0
- data/lib/sports_south/user.rb +44 -0
- data/lib/sports_south/version.rb +3 -0
- data/lib/sports_south.rb +17 -0
- data/sports_south.gemspec +26 -0
- metadata +122 -0
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
data/.rspec
ADDED
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
data/Gemfile
ADDED
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
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,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('<', '<').gsub('>', '>')
|
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
|
data/lib/sports_south.rb
ADDED
@@ -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: []
|