seko 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +23 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +210 -0
- data/Rakefile +1 -0
- data/lib/seko/client.rb +236 -0
- data/lib/seko/company.rb +33 -0
- data/lib/seko/dispatch.rb +22 -0
- data/lib/seko/filter.rb +9 -0
- data/lib/seko/order.rb +77 -0
- data/lib/seko/product.rb +27 -0
- data/lib/seko/receipt.rb +36 -0
- data/lib/seko/response.rb +33 -0
- data/lib/seko/shipping.rb +26 -0
- data/lib/seko/stock.rb +9 -0
- data/lib/seko/tracking.rb +22 -0
- data/lib/seko/version.rb +3 -0
- data/lib/seko.rb +31 -0
- data/seko.gemspec +27 -0
- data/spec/fixtures/company_submit.json +21 -0
- data/spec/fixtures/dispatch_statuses.json +25 -0
- data/spec/fixtures/grn.json +45 -0
- data/spec/fixtures/order_status.json +11 -0
- data/spec/fixtures/order_submit.json +33 -0
- data/spec/fixtures/order_tracking.json +12 -0
- data/spec/fixtures/order_websubmit.json +29 -0
- data/spec/fixtures/product_submit.json +15 -0
- data/spec/fixtures/receipt_submit.json +26 -0
- data/spec/fixtures/stock.json +29 -0
- data/spec/fixtures/stock_adjustment.json +7 -0
- data/spec/fixtures/stock_movement.json +7 -0
- data/spec/lib/client_spec.rb +364 -0
- data/spec/lib/company_spec.rb +22 -0
- data/spec/lib/dispatch_spec.rb +17 -0
- data/spec/lib/filter_spec.rb +12 -0
- data/spec/lib/order_spec.rb +60 -0
- data/spec/lib/product_spec.rb +23 -0
- data/spec/lib/receipt_spec.rb +36 -0
- data/spec/lib/response_spec.rb +53 -0
- data/spec/lib/stock_spec.rb +12 -0
- data/spec/logs/spec.log +0 -0
- data/spec/spec_helper.rb +176 -0
- metadata +196 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7ccf252a5f73196d16afac7f326d8fca9163f7dc
|
4
|
+
data.tar.gz: 2fb7c168442260f3b587d28e7823fb497bc98254
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1bd565235abd405f4f44d2bf98a1942770ca3b4252ddffd0d83affd78db0d0a1680de9661f0df2f6a2e19d7c5d820e2b9f25f062d9fa5fc9b1cc2f90b3146d3b
|
7
|
+
data.tar.gz: eafb4f39d5629a62b5554b09cbdf8678a810c1f65129693e0a95d1ec4f0312394e6cc9e42e9a617f814b8629651404c69c91f1654f0ed6338077385a28eaa336
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.0.0
|
4
|
+
|
5
|
+
matrix:
|
6
|
+
allow_failures:
|
7
|
+
- rvm: rbx-19mode
|
8
|
+
|
9
|
+
script:
|
10
|
+
- CODECLIMATE_REPO_TOKEN=dd4351c1e437fe6bb682de8e30eae582e627b4e0959ed3202839ad057af5d4d1 bundle exec rspec
|
11
|
+
|
12
|
+
# addons:
|
13
|
+
# code_climate:
|
14
|
+
# repo_token: dd4351c1e437fe6bb682de8e30eae582e627b4e0959ed3202839ad057af5d4d1
|
15
|
+
|
16
|
+
notifications:
|
17
|
+
email:
|
18
|
+
- justin@jgrubbs.net
|
19
|
+
|
20
|
+
gemfile:
|
21
|
+
- Gemfile
|
22
|
+
|
23
|
+
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Justin Grubbs
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
# Seko
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/jGRUBBS/seko-ruby-api.svg?branch=master)](https://travis-ci.org/jGRUBBS/seko-ruby-api.svg?branch=master)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/jGRUBBS/seko-ruby-api/badges/gpa.svg)](https://codeclimate.com/github/jGRUBBS/seko-ruby-api)
|
5
|
+
[![Test Coverage](https://codeclimate.com/github/jGRUBBS/seko-ruby-api/badges/coverage.svg)](https://codeclimate.com/github/jGRUBBS/seko-ruby-api)
|
6
|
+
|
7
|
+
Ruby wrapper for Seko Logistics' SupplyStream iHub REST API v1
|
8
|
+
|
9
|
+
[SupplyStream REST API Documentation](https://wiki.supplystream.com/GetFile.aspx?Page=MANUAL.Integration-Hub-Rest-APIs&File=integration-ihub-rest-apis-v1.4.pdf)
|
10
|
+
|
11
|
+
## Integrations
|
12
|
+
|
13
|
+
1. **Inbound Product Master Upload and method**
|
14
|
+
2. **Inbound Companies Upload and method**
|
15
|
+
3. **Inbound Advanced Shipment Notification**
|
16
|
+
4. **Inbound Sales Order / Cancel Orders**
|
17
|
+
5. **Retrieve GRN’s**
|
18
|
+
6. **Retrieve Stock Quantity**
|
19
|
+
7. **Retrieve Tracking Details**
|
20
|
+
8. **Retrieve Sales Order Status**
|
21
|
+
9. **Retrieve Stock Adjustments**
|
22
|
+
10. **Retrieve Stock Movements**
|
23
|
+
|
24
|
+
## Installation
|
25
|
+
|
26
|
+
Add this line to your application's Gemfile:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem 'seko'
|
30
|
+
```
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
|
34
|
+
$ bundle
|
35
|
+
|
36
|
+
Or install it yourself as:
|
37
|
+
|
38
|
+
$ gem install seko
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
#### Configuration
|
42
|
+
config/initializers/seko.rb
|
43
|
+
```ruby
|
44
|
+
Seko.configure(
|
45
|
+
token: 'SekoAPIToKeN'
|
46
|
+
supplier_code: 'DEFSUPLJLTD001',
|
47
|
+
supplier_description: 'Default Supplier LARSSON & JENNINGS LTD',
|
48
|
+
supplier_uom: 1,
|
49
|
+
warehouses: {
|
50
|
+
us: 'US123',
|
51
|
+
uk: 'UK123'
|
52
|
+
}
|
53
|
+
)
|
54
|
+
```
|
55
|
+
|
56
|
+
#### Submit Product
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
client = Seko::Client.new(Seko.config[:token])
|
60
|
+
response = client.submit_product(upc: "123456", description: 'A test product')
|
61
|
+
```
|
62
|
+
|
63
|
+
#### Submit Receipt
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
line_items = [ { upc: "123456", quantity: 10 } ]
|
67
|
+
warehouse = Seko.config[:warehouses][:us]
|
68
|
+
client = Seko::Client.new(Seko.config[:token])
|
69
|
+
response = client.submit_receipt(line_item_array, warehouse)
|
70
|
+
```
|
71
|
+
|
72
|
+
#### Submit Company
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
company_hash = {
|
76
|
+
code: 'IND001',
|
77
|
+
description: 'Indigina'
|
78
|
+
}
|
79
|
+
client = Seko::Client.new(Seko.config[:token])
|
80
|
+
response = client.submit_company(company_hash)
|
81
|
+
```
|
82
|
+
|
83
|
+
#### Get Stock
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
client = Seko::Client.new(Seko.config[:token])
|
87
|
+
response = client.get_inventory
|
88
|
+
```
|
89
|
+
|
90
|
+
#### Check GRN
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
client = Seko::Client.new(Seko.config[:token])
|
94
|
+
response = client.check_grn('5b2dcd8e-52c3-4e27-a712-eaacda2dd8fe')
|
95
|
+
```
|
96
|
+
|
97
|
+
#### Order Status
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
client = Seko::Client.new(Seko.config[:token])
|
101
|
+
response = client.order_status('5b2dcd8e-52c3-4e27-a712-eaacda2dd8fe')
|
102
|
+
```
|
103
|
+
|
104
|
+
#### Order Tracking
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
client = Seko::Client.new(Seko.config[:token])
|
108
|
+
response = client.order_tracking('5b2dcd8e-52c3-4e27-a712-eaacda2dd8fe')
|
109
|
+
```
|
110
|
+
|
111
|
+
#### Cancel Order
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
Seko::Order::CANCEL_CODES
|
115
|
+
# => {
|
116
|
+
# "001" => "Customer Request",
|
117
|
+
# "002" => "Order Delayed",
|
118
|
+
# "003" => "Duplicate",
|
119
|
+
# "004" => "Item not available",
|
120
|
+
# "005" => "Cannot ship to address",
|
121
|
+
# "006" => "Other"
|
122
|
+
# }
|
123
|
+
|
124
|
+
client = Seko::Client.new(Seko.config[:token])
|
125
|
+
response = client.cancel_order('5b2dcd8e-52c3-4e27-a712-eaacda2dd8fe', '001')
|
126
|
+
```
|
127
|
+
|
128
|
+
#### Stock Movements
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
client = Seko::Client.new(Seko.config[:token])
|
132
|
+
warehouse = Seko.config[:warehouses][:us]
|
133
|
+
from = 3.days.ago
|
134
|
+
to = Time.now
|
135
|
+
response = client.stock_movements(from, to, warehouse)
|
136
|
+
```
|
137
|
+
|
138
|
+
#### Stock Adjustments
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
client = Seko::Client.new(Seko.config[:token])
|
142
|
+
warehouse = Seko.config[:warehouses][:us]
|
143
|
+
from = 3.days.ago
|
144
|
+
to = Time.now
|
145
|
+
response = client.stock_adjustments(from, to, warehouse)
|
146
|
+
```
|
147
|
+
|
148
|
+
#### Dispatch Statuses
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
client = Seko::Client.new(Seko.config[:token])
|
152
|
+
warehouse = Seko.config[:warehouses][:us] # warehouse is optional
|
153
|
+
from = 3.days.ago
|
154
|
+
to = Time.now
|
155
|
+
response = client.dispatch_statuses(from, to, warehouse)
|
156
|
+
|
157
|
+
# get collection of GUIDs dispatched
|
158
|
+
guid_array = Seko::Dispatch.parse(response)
|
159
|
+
# returns something like this
|
160
|
+
# => ["2b5e52cc-fb6f-4ea4-b8cf-cf64e3a2b8db", "93e92ca2-725a-46f8-90dd-43a16105f78d"]
|
161
|
+
```
|
162
|
+
|
163
|
+
#### Submit Order
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
order = {
|
167
|
+
shipping_address: {
|
168
|
+
first_name: "John",
|
169
|
+
last_name: "Smith",
|
170
|
+
address1: "123 Here Now",
|
171
|
+
address2: "2nd Floor",
|
172
|
+
city: "New York",
|
173
|
+
state: "New York",
|
174
|
+
country: "US",
|
175
|
+
zipcode: "10012",
|
176
|
+
phone: "123-123-1234"
|
177
|
+
},
|
178
|
+
email: "someone@somehwere.com",
|
179
|
+
number: "R123123123",
|
180
|
+
warehouse: "DC123",
|
181
|
+
date: "2013-12-12",
|
182
|
+
line_items: [
|
183
|
+
{
|
184
|
+
quantity: "1",
|
185
|
+
sku: "123332211"
|
186
|
+
}
|
187
|
+
]
|
188
|
+
}
|
189
|
+
|
190
|
+
client = Seko::Client.new(Seko.config[:token])
|
191
|
+
response = client.send_order_request(order)
|
192
|
+
|
193
|
+
### NOTE you may want to store the GUID from the response
|
194
|
+
### you need the guid for status updates, tracking, and canceling orders
|
195
|
+
response.guid
|
196
|
+
|
197
|
+
if response.success?
|
198
|
+
# DO SOMETHING
|
199
|
+
else
|
200
|
+
# QUEUE REQUEST, STORE AND RAISE ERRORS
|
201
|
+
end
|
202
|
+
```
|
203
|
+
|
204
|
+
## Contributing
|
205
|
+
|
206
|
+
1. Fork it ( https://github.com/[my-github-username]/seko/fork )
|
207
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
208
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
209
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
210
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/lib/seko/client.rb
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Seko
|
5
|
+
class Client
|
6
|
+
|
7
|
+
PORT = 443
|
8
|
+
TEST_HOST = 'hubuat1.supplystream.com'
|
9
|
+
# api.supplystream.com
|
10
|
+
LIVE_HOST = 'hub.supplystream.com'
|
11
|
+
API_PATH = '/hub/api/'
|
12
|
+
API_VERSION = 'v1'
|
13
|
+
CONTENT_TYPE = 'application/json'
|
14
|
+
KEYS_MAP = {
|
15
|
+
"FreeQuantity" => "qty",
|
16
|
+
"ProductCode" => "upc"
|
17
|
+
}
|
18
|
+
|
19
|
+
attr_accessor :token, :response, :type, :request_uri, :path, :service, :endpoint, :options
|
20
|
+
|
21
|
+
def initialize(token, options = {})
|
22
|
+
raise "Token is required" unless token
|
23
|
+
|
24
|
+
@token = token
|
25
|
+
@options = default_options.merge(options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def send_order_request(order_hash)
|
29
|
+
@service = 'salesorders'
|
30
|
+
@endpoint = 'websubmit'
|
31
|
+
post(Order.websubmit(order_hash))
|
32
|
+
end
|
33
|
+
|
34
|
+
def order_request(order_hash)
|
35
|
+
Order.websubmit(order_hash).to_json
|
36
|
+
end
|
37
|
+
|
38
|
+
def send_wholesale_request(order_hash)
|
39
|
+
@service = 'salesorders'
|
40
|
+
@endpoint = 'submit'
|
41
|
+
post(Order.submit(order_hash))
|
42
|
+
end
|
43
|
+
|
44
|
+
def wholesale_request(order_hash)
|
45
|
+
Order.submit(order_hash).to_json
|
46
|
+
end
|
47
|
+
|
48
|
+
# FIXME: use this method once SS fixes their API
|
49
|
+
# for now we are manually sorting the distribution centre
|
50
|
+
# see #inventory_response below
|
51
|
+
#
|
52
|
+
# def get_inventory(warehouse = nil)
|
53
|
+
# @service = 'stock'
|
54
|
+
# @endpoint = if warehouse.nil?
|
55
|
+
# "all"
|
56
|
+
# else
|
57
|
+
# "dc/#{warehouse}"
|
58
|
+
# end
|
59
|
+
# inventory_response
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# def inventory_response
|
63
|
+
# response = get
|
64
|
+
# response.parsed = map_results(Stock.parse(response))
|
65
|
+
# response
|
66
|
+
# end
|
67
|
+
|
68
|
+
def get_inventory(warehouse = nil)
|
69
|
+
@service = 'stock'
|
70
|
+
@endpoint = 'all'
|
71
|
+
inventory_response(warehouse)
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def inventory_response(warehouse = nil)
|
76
|
+
response = get
|
77
|
+
response.parsed = map_results(Stock.parse(response)).select do |stock|
|
78
|
+
warehouse.nil? ? true : stock["DCCode"] == warehouse
|
79
|
+
end
|
80
|
+
response
|
81
|
+
end
|
82
|
+
|
83
|
+
def upcs(inventory)
|
84
|
+
inventory.collect { |s| s["upc"] }
|
85
|
+
end
|
86
|
+
|
87
|
+
def mapped_inventory(upcs, inventory)
|
88
|
+
inventory.collect do |stock|
|
89
|
+
if upcs.include?(stock["upc"])
|
90
|
+
{ quantity: stock["qty"].to_i }
|
91
|
+
end
|
92
|
+
end.compact
|
93
|
+
end
|
94
|
+
|
95
|
+
def map_results(results)
|
96
|
+
results.map { |h| h.inject({ }) { |x, (k,v)| x[map_keys(k)] = v; x } }
|
97
|
+
end
|
98
|
+
|
99
|
+
def map_keys(key)
|
100
|
+
KEYS_MAP[key] || key
|
101
|
+
end
|
102
|
+
|
103
|
+
def submit_product(product_hash)
|
104
|
+
@service = 'products'
|
105
|
+
@endpoint = 'submit'
|
106
|
+
post(Product.format(product_hash))
|
107
|
+
end
|
108
|
+
|
109
|
+
def submit_receipt(line_item_array, warehouse)
|
110
|
+
@service = 'receipts'
|
111
|
+
@endpoint = 'submit'
|
112
|
+
post(Receipt.format(line_item_array, warehouse))
|
113
|
+
end
|
114
|
+
|
115
|
+
def submit_company(company_hash)
|
116
|
+
@service = 'companies'
|
117
|
+
@endpoint = 'submit'
|
118
|
+
post(Company.format(company_hash))
|
119
|
+
end
|
120
|
+
|
121
|
+
def check_grn(guid)
|
122
|
+
@service = 'grns'
|
123
|
+
@endpoint = guid
|
124
|
+
get
|
125
|
+
end
|
126
|
+
|
127
|
+
def order_status(guid)
|
128
|
+
@service = 'salesorders'
|
129
|
+
@endpoint = "#{guid}/status"
|
130
|
+
get
|
131
|
+
end
|
132
|
+
|
133
|
+
def order_tracking(guid)
|
134
|
+
@service = 'salesorders'
|
135
|
+
@endpoint = "#{guid}/tracking"
|
136
|
+
get
|
137
|
+
end
|
138
|
+
|
139
|
+
def cancel_order(guid, reason_code)
|
140
|
+
@service = 'salesorders'
|
141
|
+
@endpoint = "#{guid}/cancel/reasoncode/#{reason_code}"
|
142
|
+
post({'Cancel' => 'Order'})
|
143
|
+
end
|
144
|
+
|
145
|
+
def stock_adjustments(from, to, warehouse)
|
146
|
+
@service = 'stock'
|
147
|
+
@endpoint = "adjustment/#{format_from_to(from, to)}"
|
148
|
+
get("#{request_uri}&dc=#{warehouse}")
|
149
|
+
end
|
150
|
+
|
151
|
+
def stock_movements(from, to, warehouse)
|
152
|
+
@service = 'stock'
|
153
|
+
@endpoint = "movement/#{format_from_to(from, to)}"
|
154
|
+
get("#{request_uri}&dc=#{warehouse}")
|
155
|
+
end
|
156
|
+
|
157
|
+
def dispatch_statuses(from, to, warehouse = nil)
|
158
|
+
@service = 'dispatches'
|
159
|
+
@endpoint = "status/#{format_from_to(from, to)}"
|
160
|
+
request_url = "#{request_uri}&status=Dispatched"
|
161
|
+
request_url += "&dc=#{warehouse}" unless warehouse.nil?
|
162
|
+
get(request_url)
|
163
|
+
end
|
164
|
+
|
165
|
+
def request_uri
|
166
|
+
"https://#{host}#{path}?token=#{token}"
|
167
|
+
end
|
168
|
+
|
169
|
+
def path
|
170
|
+
"#{API_PATH}#{service}/#{API_VERSION}/#{endpoint}.json"
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
def default_options
|
175
|
+
{
|
176
|
+
verbose: false,
|
177
|
+
test_mode: true
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
181
|
+
def testing?
|
182
|
+
@options[:test_mode]
|
183
|
+
end
|
184
|
+
|
185
|
+
def verbose?
|
186
|
+
@options[:verbose]
|
187
|
+
end
|
188
|
+
|
189
|
+
def host
|
190
|
+
testing? ? TEST_HOST : LIVE_HOST
|
191
|
+
end
|
192
|
+
|
193
|
+
def log(message)
|
194
|
+
return unless verbose?
|
195
|
+
puts message
|
196
|
+
end
|
197
|
+
|
198
|
+
def http
|
199
|
+
@http ||= Net::HTTP.new(host, PORT)
|
200
|
+
end
|
201
|
+
|
202
|
+
def build_request(type, url = request_uri)
|
203
|
+
request = Net::HTTP.const_get(type).new(url)
|
204
|
+
request.content_type = CONTENT_TYPE
|
205
|
+
request
|
206
|
+
end
|
207
|
+
|
208
|
+
def request(request, json_request = nil)
|
209
|
+
http.use_ssl = true
|
210
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
211
|
+
response = http.request(request)
|
212
|
+
parse_response(response.body)
|
213
|
+
end
|
214
|
+
|
215
|
+
def get(url = request_uri)
|
216
|
+
request = build_request('Get', url)
|
217
|
+
request(request)
|
218
|
+
end
|
219
|
+
|
220
|
+
def post(json_request, url = request_uri)
|
221
|
+
request = build_request('Post', url)
|
222
|
+
request.body = json_request.to_json
|
223
|
+
request(request)
|
224
|
+
end
|
225
|
+
|
226
|
+
def parse_response(json_response)
|
227
|
+
log(json_response)
|
228
|
+
@response = Response.new(json_response)
|
229
|
+
end
|
230
|
+
|
231
|
+
def format_from_to(from, to)
|
232
|
+
"#{from.strftime('%F')}T00:00:00Z/#{to.strftime('%F')}T00:00:00Z"
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
end
|
data/lib/seko/company.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Seko
|
2
|
+
class Company
|
3
|
+
|
4
|
+
def self.format(company)
|
5
|
+
{
|
6
|
+
"Request" => {
|
7
|
+
"Company" => {
|
8
|
+
"CompanyCode" => company[:code],
|
9
|
+
"CompanyDescription" => company[:description],
|
10
|
+
"CompanyType" => "Ship To"
|
11
|
+
},
|
12
|
+
"List" => {
|
13
|
+
"Address" => address(company[:address])
|
14
|
+
}
|
15
|
+
}
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.address(address)
|
20
|
+
{
|
21
|
+
"AddressLine1" => address[:address1],
|
22
|
+
"AddressLine2" => address[:address2],
|
23
|
+
"AddressLine3" => address[:address3],
|
24
|
+
"AddressType" => "Delivery",
|
25
|
+
"City" => address[:city],
|
26
|
+
"CountryCode" => address[:country],
|
27
|
+
"County" => address[:country_name],
|
28
|
+
"PostcodeZip" => address[:zipcode]
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Seko
|
2
|
+
class Dispatch
|
3
|
+
|
4
|
+
def self.line_items(response)
|
5
|
+
response.root_response["List"]["DispatchLineItem"]
|
6
|
+
end
|
7
|
+
|
8
|
+
# returns array of GUIDs
|
9
|
+
def self.parse(response)
|
10
|
+
dispatches = line_items(response)
|
11
|
+
# API is flawed and returns an array if multiple dispatches
|
12
|
+
if dispatches.is_a?(Array)
|
13
|
+
dispatches.collect { |h| h["GUID"] }
|
14
|
+
else
|
15
|
+
# and a singular object otherwise
|
16
|
+
# so we wrap it as an array
|
17
|
+
Array(dispatches["GUID"])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
data/lib/seko/filter.rb
ADDED
data/lib/seko/order.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
module Seko
|
2
|
+
class Order
|
3
|
+
|
4
|
+
CANCEL_CODES = {
|
5
|
+
'001' => 'Customer Request',
|
6
|
+
'002' => 'Order Delayed',
|
7
|
+
'003' => 'Duplicate',
|
8
|
+
'004' => 'Item not available',
|
9
|
+
'005' => 'Cannot ship to address',
|
10
|
+
'006' => 'Other'
|
11
|
+
}
|
12
|
+
|
13
|
+
def self.websubmit(attributes)
|
14
|
+
build_request(attributes, "Web")
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.submit(attributes)
|
18
|
+
build_request(attributes)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.build_request(attributes, type = nil)
|
22
|
+
formatted = format(attributes, type)
|
23
|
+
formatted["Request"].merge!(company(attributes[:company])) if attributes[:company]
|
24
|
+
formatted
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.format(order, order_prefix = nil)
|
28
|
+
{
|
29
|
+
"Request" => {
|
30
|
+
"DeliveryDetails" => address(order[:shipping_address], order[:email]),
|
31
|
+
"List" => {
|
32
|
+
"SalesOrderLineItem" => line_items(order[:line_items])
|
33
|
+
},
|
34
|
+
"SalesOrderHeader" => { "DCCode" => order[:warehouse] },
|
35
|
+
"#{order_prefix}SalesOrder" => {
|
36
|
+
"SalesOrderDate" => order[:date],
|
37
|
+
"SalesOrderNumber" => order[:number]
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.address(address, email)
|
44
|
+
{
|
45
|
+
"City" => address[:city],
|
46
|
+
"CountryCode" => address[:country],
|
47
|
+
"EmailAddress" => email,
|
48
|
+
"FirstName" => address[:first_name],
|
49
|
+
"LastName" => address[:last_name],
|
50
|
+
"Line1" => address[:address1],
|
51
|
+
"Line2" => address[:address2],
|
52
|
+
"PhoneNumber" => address[:phone],
|
53
|
+
"PostcodeZip" => address[:zipcode]
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.line_items(items)
|
58
|
+
items.collect.with_index do |line_item, index|
|
59
|
+
{
|
60
|
+
"LineNumber" => index + 1,
|
61
|
+
"ProductCode" => line_item[:sku],
|
62
|
+
"Quantity" => line_item[:quantity]
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.company(company)
|
68
|
+
{
|
69
|
+
"ShipToCompany" => {
|
70
|
+
"CompanyCode" => company[:code],
|
71
|
+
"CompanyDescription" => company[:description]
|
72
|
+
}
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
data/lib/seko/product.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Seko
|
2
|
+
class Product
|
3
|
+
|
4
|
+
def self.supplier
|
5
|
+
{
|
6
|
+
"SupplierCode" => Seko.config[:supplier_code],
|
7
|
+
"SupplierDescription" => Seko.config[:supplier_description],
|
8
|
+
"UOM" => Seko.config[:supplier_uom]
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.format(attributes)
|
13
|
+
{
|
14
|
+
"Request" => {
|
15
|
+
"List" => {
|
16
|
+
"SupplierMapping" => supplier
|
17
|
+
},
|
18
|
+
"ProductMaster" => {
|
19
|
+
"ProductCode" => attributes[:upc],
|
20
|
+
"ProductDescription" => Filter.parse(attributes[:description])
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|