omniorder 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +40 -0
- data/Rakefile +9 -0
- data/lib/omniorder/customer.rb +7 -0
- data/lib/omniorder/customerable.rb +42 -0
- data/lib/omniorder/entity.rb +21 -0
- data/lib/omniorder/import.rb +11 -0
- data/lib/omniorder/import_strategy/base.rb +50 -0
- data/lib/omniorder/import_strategy/groupon.rb +93 -0
- data/lib/omniorder/order.rb +12 -0
- data/lib/omniorder/order_product.rb +7 -0
- data/lib/omniorder/order_productable.rb +11 -0
- data/lib/omniorder/orderable.rb +35 -0
- data/lib/omniorder/product.rb +12 -0
- data/lib/omniorder/purchasable.rb +7 -0
- data/lib/omniorder/version.rb +3 -0
- data/lib/omniorder.rb +55 -0
- data/omniorder.gemspec +27 -0
- data/spec/assets/imports/groupon/base_spec.rb +17 -0
- data/spec/assets/imports/groupon/get_orders.json +82 -0
- data/spec/customerable_spec.rb +37 -0
- data/spec/import_strategy/groupon_spec.rb +88 -0
- data/spec/orderable_spec.rb +55 -0
- data/spec/spec_helper.rb +27 -0
- metadata +148 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c03ee4b2df4c0589a2a01e7787556544cf5bc382
|
4
|
+
data.tar.gz: e229b29ba52d8c82adc3b083933c6112f8c20011
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ce3a0db160edea671d7c571be7251616f7832cf85d842cd0f54f6ec2f2e389540744ca6c6b023042ad87c53fd56293fad8c5ae14b72d2a3976d4b4da913b4d9d
|
7
|
+
data.tar.gz: 2b70815df33f45e01f465aefec85797e292ef9e33f9caefafba0680f3407194d9acbdc8c4ad4c8ec168daa3eae09986be793ce8be8d8f51baad77e60040bf4f3
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Howard Wilson
|
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,40 @@
|
|
1
|
+
# Omniorder
|
2
|
+
|
3
|
+
[](https://travis-ci.org/watsonbox/omniorder)
|
4
|
+
|
5
|
+
Import online orders from various channels for fulfillment.
|
6
|
+
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'omniorder'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
```bash
|
19
|
+
$ bundle
|
20
|
+
```
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
```bash
|
25
|
+
$ gem install omniorder
|
26
|
+
```
|
27
|
+
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
TODO: Write usage instructions here
|
32
|
+
|
33
|
+
|
34
|
+
## Contributing
|
35
|
+
|
36
|
+
1. Fork it ( https://github.com/watsonbox/omniorder/fork )
|
37
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
38
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
39
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
40
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Omniorder
|
2
|
+
module Customerable
|
3
|
+
def username_is_email?
|
4
|
+
username == email
|
5
|
+
end
|
6
|
+
|
7
|
+
def first_names
|
8
|
+
name.split[0...-1]
|
9
|
+
end
|
10
|
+
|
11
|
+
def first_name
|
12
|
+
first_names.first
|
13
|
+
end
|
14
|
+
|
15
|
+
def last_name
|
16
|
+
name.split.last
|
17
|
+
end
|
18
|
+
|
19
|
+
def full_address
|
20
|
+
fields = [address1, address2, address3, address4, postcode, country]
|
21
|
+
fields.reject { |f| f.nil? || f.empty? }.join("\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.get_or_new_from_email(email, attributes = {})
|
25
|
+
customer = nil
|
26
|
+
|
27
|
+
if Omniorder.customer_type.respond_to?(:find_by_email)
|
28
|
+
customer = Omniorder.customer_type.find_by_email(email)
|
29
|
+
end
|
30
|
+
|
31
|
+
if customer
|
32
|
+
attributes.each do |name, value|
|
33
|
+
customer.send("#{name}=", value)
|
34
|
+
end
|
35
|
+
else
|
36
|
+
Omniorder.customer_type.new(attributes)
|
37
|
+
end
|
38
|
+
|
39
|
+
customer
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Omniorder
|
2
|
+
class Entity
|
3
|
+
def self.attributes(*attributes)
|
4
|
+
if attributes.empty?
|
5
|
+
instance_variable_get('@attributes')
|
6
|
+
else
|
7
|
+
instance_variable_set('@attributes', attributes)
|
8
|
+
attr_accessor *attributes
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(attributes = {})
|
13
|
+
# Initialize known attributes
|
14
|
+
attributes.each do |attribute, value|
|
15
|
+
if self.class.attributes.include?(attribute.to_sym)
|
16
|
+
send("#{attribute}=", value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Omniorder
|
2
|
+
class Import
|
3
|
+
# Allows the import to control how orders are created
|
4
|
+
# e.g. an account could be assigned if system is multi-tennant
|
5
|
+
# Use generate not build so as not to conflict with ActiveRecord
|
6
|
+
# TODO: Move to Importable?
|
7
|
+
def generate_order(attributes = {})
|
8
|
+
Omniorder.order_type.new({ import: self }.merge(attributes))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Omniorder
|
2
|
+
module ImportStrategy
|
3
|
+
class Base
|
4
|
+
attr_accessor :import
|
5
|
+
attr_writer :options
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def options(options = nil)
|
9
|
+
if options
|
10
|
+
@options = options
|
11
|
+
else
|
12
|
+
@options || {}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def clear_options
|
17
|
+
@options = nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(import, options = {})
|
22
|
+
self.import = import
|
23
|
+
self.options = options
|
24
|
+
end
|
25
|
+
|
26
|
+
# Combines global and local options
|
27
|
+
def options
|
28
|
+
self.class.options.merge(@options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def before_import
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def after_import
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def customer_country_or_default(country)
|
42
|
+
if country.nil? || country.empty?
|
43
|
+
Omniorder.default_customer_country
|
44
|
+
else
|
45
|
+
country
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Omniorder
|
2
|
+
module ImportStrategy
|
3
|
+
# Groupon Import Strategy
|
4
|
+
# See: https://scm.commerceinterface.com/api-doc/v2/
|
5
|
+
class Groupon < Base
|
6
|
+
API_URL = "https://scm.commerceinterface.com/api/v2/"
|
7
|
+
|
8
|
+
attr_accessor :supplier_id, :access_token
|
9
|
+
|
10
|
+
def initialize(import, options = {})
|
11
|
+
super
|
12
|
+
|
13
|
+
unless self.supplier_id = options[:supplier_id] and !supplier_id.to_s.empty?
|
14
|
+
raise "Omniorder::ImportStrategy::Groupon requires a supplier_id"
|
15
|
+
end
|
16
|
+
|
17
|
+
unless self.access_token = options[:access_token] and !access_token.to_s.empty?
|
18
|
+
raise "Omniorder::ImportStrategy::Groupon requires an access_token"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def import_orders
|
23
|
+
get_order_info['data'].to_a.each do |order_info|
|
24
|
+
success = yield create_order(order_info)
|
25
|
+
|
26
|
+
if success && options[:mark_exported]
|
27
|
+
result = Crack::JSON.parse do_request(mark_exported_url(order_info), :post)
|
28
|
+
|
29
|
+
unless result['success']
|
30
|
+
raise "Failed to mark Groupon order ##{order_info['orderid']} as exported"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_orders_url
|
37
|
+
URI.join(API_URL, "get_orders?supplier_id=#{supplier_id}&token=#{access_token}")
|
38
|
+
end
|
39
|
+
|
40
|
+
def mark_exported_url(order_info)
|
41
|
+
lids = order_info['line_items'].map { |li| li["ci_lineitemid"] }
|
42
|
+
URI.join(API_URL, "mark_exported?supplier_id=#{supplier_id}&token=#{access_token}&ci_lineitem_ids=[#{lids.join(',')}]")
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def create_order(order_info)
|
48
|
+
order = import.generate_order(
|
49
|
+
:order_number => order_info['orderid'],
|
50
|
+
:total_price => order_info['amount']['total'].to_f,
|
51
|
+
:date => DateTime.strptime(order_info['date'], '%m/%d/%Y %I:%M%p UTC')
|
52
|
+
)
|
53
|
+
|
54
|
+
order.customer = create_customer(order, order_info['customer'])
|
55
|
+
|
56
|
+
order_info['line_items'].each do |line_item_info|
|
57
|
+
order.add_product_by_code(line_item_info['sku'].to_s, line_item_info['quantity'].to_i)
|
58
|
+
end
|
59
|
+
|
60
|
+
order
|
61
|
+
end
|
62
|
+
|
63
|
+
def create_customer(order, customer_info)
|
64
|
+
# NOTE: Can't find existing customer as no username or email given
|
65
|
+
order.generate_customer(
|
66
|
+
:name => customer_info['name'],
|
67
|
+
:phone => customer_info['phone'],
|
68
|
+
:address1 => customer_info['address1'],
|
69
|
+
:address2 => customer_info['address2'],
|
70
|
+
:address3 => customer_info['city'],
|
71
|
+
:address4 => customer_info['state'],
|
72
|
+
:postcode => customer_info['zip'].to_s.squeeze(' ').upcase,
|
73
|
+
:country => customer_country_or_default(customer_info['country'])
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
# NOTE: We don't appear to get an email address for customers
|
78
|
+
def get_order_info
|
79
|
+
Crack::JSON.parse do_request(get_orders_url)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def do_request(url, type = :get)
|
85
|
+
uri = URI(url)
|
86
|
+
http = Net::HTTP.new(uri.host, 443)
|
87
|
+
http.use_ssl = true
|
88
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
89
|
+
http.request((type == :post ? Net::HTTP::Post : Net::HTTP::Get).new(uri.request_uri)).body
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Omniorder
|
2
|
+
# Represents common order behavior
|
3
|
+
# Include Omniorder::Orderable in your Order class to make it Omniorder compatible.
|
4
|
+
module Orderable
|
5
|
+
# Use generate not build so as not to conflict with ActiveRecord
|
6
|
+
def generate_customer(attributes = {})
|
7
|
+
Omniorder.customer_type.new(attributes)
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_product(product, quantity = 1)
|
11
|
+
order_product = order_products.to_a.find { |op| op.product == product }
|
12
|
+
|
13
|
+
if order_product.nil?
|
14
|
+
order_products << Omniorder.order_product_type.new(:product => product, :quantity => quantity)
|
15
|
+
else
|
16
|
+
order_product.quantity += quantity
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_product_by_code(code, quantity = 1)
|
21
|
+
Omniorder.product_type.find_by_code(code).tap do |product|
|
22
|
+
add_product product, quantity unless product.nil?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def product_count
|
27
|
+
order_products.inject(0) { |sum, op| sum + op.quantity }
|
28
|
+
end
|
29
|
+
|
30
|
+
# Human-readable string representing order products
|
31
|
+
def product_list
|
32
|
+
order_products.sort.map(&:to_s).join('/')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/omniorder.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "net/https"
|
3
|
+
require "crack"
|
4
|
+
|
5
|
+
require "omniorder/version"
|
6
|
+
require "omniorder/entity"
|
7
|
+
require "omniorder/customerable"
|
8
|
+
require "omniorder/customer"
|
9
|
+
require "omniorder/purchasable"
|
10
|
+
require "omniorder/product"
|
11
|
+
require "omniorder/orderable"
|
12
|
+
require "omniorder/order"
|
13
|
+
require "omniorder/order_productable"
|
14
|
+
require "omniorder/order_product"
|
15
|
+
require "omniorder/import"
|
16
|
+
require "omniorder/import_strategy/base"
|
17
|
+
require "omniorder/import_strategy/groupon"
|
18
|
+
|
19
|
+
module Omniorder
|
20
|
+
class << self
|
21
|
+
attr_writer :order_type
|
22
|
+
attr_writer :order_product_type
|
23
|
+
attr_writer :customer_type
|
24
|
+
attr_writer :product_type
|
25
|
+
attr_writer :default_customer_country
|
26
|
+
|
27
|
+
def order_type
|
28
|
+
@order_type || Order
|
29
|
+
rescue NameError
|
30
|
+
raise "Please set Omniorder#order_type"
|
31
|
+
end
|
32
|
+
|
33
|
+
def customer_type
|
34
|
+
@customer_type || Customer
|
35
|
+
rescue NameError
|
36
|
+
raise "Please set Omniorder#customer_type"
|
37
|
+
end
|
38
|
+
|
39
|
+
def product_type
|
40
|
+
@product_type || Product
|
41
|
+
rescue NameError
|
42
|
+
raise "Please set Omniorder#product_type"
|
43
|
+
end
|
44
|
+
|
45
|
+
def order_product_type
|
46
|
+
@order_product_type || OrderProduct
|
47
|
+
rescue NameError
|
48
|
+
raise "Please set Omniorder#order_product_type"
|
49
|
+
end
|
50
|
+
|
51
|
+
def default_customer_country
|
52
|
+
@default_customer_country || 'United Kingdom'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/omniorder.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'omniorder/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "omniorder"
|
8
|
+
spec.version = Omniorder::VERSION
|
9
|
+
spec.authors = ["Howard Wilson"]
|
10
|
+
spec.email = ["howard@watsonbox.net"]
|
11
|
+
spec.summary = %q{Import online orders from various channels for fulfillment.}
|
12
|
+
spec.description = %q{Import online orders from various channels for fulfillment.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "crack", "~> 0.4.2"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.1.0"
|
26
|
+
spec.add_development_dependency "webmock", "~> 1.20.4"
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Omniorder::ImportStrategy::Base do
|
4
|
+
let(:strategy_class) { Class.new(Omniorder::ImportStrategy::Base) }
|
5
|
+
|
6
|
+
context 'with a global option' do
|
7
|
+
around do |example|
|
8
|
+
strategy_class.options global_option: true
|
9
|
+
example.run
|
10
|
+
strategy_class.clear_options
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'includes the global options' do
|
14
|
+
expect(strategy_class.new(nil, local_option: true).options).to eq(global_option: true, local_option: true)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
{
|
2
|
+
"meta": {
|
3
|
+
"no_of_pages": 1,
|
4
|
+
"current_page": 1,
|
5
|
+
"max_items_per_page": 250,
|
6
|
+
"no_of_items": 2
|
7
|
+
},
|
8
|
+
"data": [{
|
9
|
+
"orderid": "FFB7A681BE",
|
10
|
+
"customer": {
|
11
|
+
"city": "BRADFORD",
|
12
|
+
"state": "KENT",
|
13
|
+
"name": "SOME BODY HERE",
|
14
|
+
"zip": "SOME ZIP",
|
15
|
+
"country": "UK",
|
16
|
+
"address1": "901",
|
17
|
+
"address2": "GREENFIELDS LANE",
|
18
|
+
"phone": "01234 982103"
|
19
|
+
},
|
20
|
+
"line_items": [{
|
21
|
+
"sku": "03658246",
|
22
|
+
"name": "SOME PACK OF 6",
|
23
|
+
"weight": 0.0,
|
24
|
+
"bom_sku": null,
|
25
|
+
"unit_price": 10.99,
|
26
|
+
"ci_lineitemid": 54553918,
|
27
|
+
"quantity": 3
|
28
|
+
}],
|
29
|
+
"shipping": {
|
30
|
+
"carrier": null,
|
31
|
+
"method": "BEST"
|
32
|
+
},
|
33
|
+
"date": "05/16/2013 08:10AM UTC",
|
34
|
+
"amount": {
|
35
|
+
"total": 10.99,
|
36
|
+
"shipping": 0
|
37
|
+
},
|
38
|
+
"supplier": "SUPPLIER NAME"
|
39
|
+
},
|
40
|
+
{
|
41
|
+
"orderid": "FFB7A68990",
|
42
|
+
"customer": {
|
43
|
+
"city": "BRADFORD",
|
44
|
+
"state": "KENT",
|
45
|
+
"name": "SOME BODY HERE",
|
46
|
+
"zip": "SOME ZIP",
|
47
|
+
"country": "UK",
|
48
|
+
"address1": "901",
|
49
|
+
"address2": "GREENFIELDS LANE",
|
50
|
+
"phone": "01234 982103"
|
51
|
+
},
|
52
|
+
"line_items": [{
|
53
|
+
"sku": "03658246",
|
54
|
+
"name": "SOME PACK OF 6",
|
55
|
+
"weight": 0.0,
|
56
|
+
"bom_sku": null,
|
57
|
+
"unit_price": 10.99,
|
58
|
+
"ci_lineitemid": 54553920,
|
59
|
+
"quantity": 3
|
60
|
+
},
|
61
|
+
{
|
62
|
+
"sku": "03658248",
|
63
|
+
"name": "SOME PACK OF 6",
|
64
|
+
"weight": 0.0,
|
65
|
+
"bom_sku": null,
|
66
|
+
"unit_price": 10.99,
|
67
|
+
"ci_lineitemid": 54553921,
|
68
|
+
"quantity": 2
|
69
|
+
}],
|
70
|
+
"shipping": {
|
71
|
+
"carrier": null,
|
72
|
+
"method": "BEST"
|
73
|
+
},
|
74
|
+
"date": "05/16/2013 10:11AM UTC",
|
75
|
+
"amount": {
|
76
|
+
"total": 10.99,
|
77
|
+
"shipping": 0
|
78
|
+
},
|
79
|
+
"supplier": "SUPPLIER NAME"
|
80
|
+
}],
|
81
|
+
"success": true
|
82
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Omniorder::Customerable do
|
4
|
+
let(:customer) { Omniorder::Customer.new }
|
5
|
+
|
6
|
+
describe '#first_name' do
|
7
|
+
it 'is the first name' do
|
8
|
+
customer.name = "Alan Gordon Partridge"
|
9
|
+
expect(customer.first_name).to eq("Alan")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#first_names' do
|
14
|
+
it 'is the first names' do
|
15
|
+
customer.name = "Alan Gordon Partridge"
|
16
|
+
expect(customer.first_names).to eq(["Alan", "Gordon"])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#last_name' do
|
21
|
+
it 'is the last names' do
|
22
|
+
customer.name = "Alan Gordon Partridge"
|
23
|
+
expect(customer.last_name).to eq("Partridge")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#full_address' do
|
28
|
+
it 'should ignore blank address components' do
|
29
|
+
customer.address1 = "29, Sycamore Lane"
|
30
|
+
customer.address3 = "Birmingham"
|
31
|
+
customer.postcode = "B13 8JU"
|
32
|
+
customer.country = "United Kingdom"
|
33
|
+
|
34
|
+
expect(customer.full_address).to eq("29, Sycamore Lane\nBirmingham\nB13 8JU\nUnited Kingdom")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Omniorder::ImportStrategy::Groupon do
|
4
|
+
let(:import) { Omniorder::Import.new }
|
5
|
+
let(:strategy) { Omniorder::ImportStrategy::Groupon.new(import, strategy_options) }
|
6
|
+
let(:strategy_options) { { supplier_id: '1', access_token: 'xYRPKcoakMoiRzWgKLV5TqPSdNAaZQT' } }
|
7
|
+
|
8
|
+
before do
|
9
|
+
stub_request(
|
10
|
+
:get,
|
11
|
+
"https://scm.commerceinterface.com/api/v2/get_orders?supplier_id=1&token=xYRPKcoakMoiRzWgKLV5TqPSdNAaZQT"
|
12
|
+
).to_return(
|
13
|
+
body: File.new('spec/assets/imports/groupon/get_orders.json'),
|
14
|
+
status: 200
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'no supplier_id is supplied' do
|
19
|
+
let(:strategy_options) { { access_token: 'xYRPKcoakMoiRzWgKLV5TqPSdNAaZQT' } }
|
20
|
+
|
21
|
+
it 'raises an exception' do
|
22
|
+
expect { strategy }.to raise_exception "Omniorder::ImportStrategy::Groupon requires a supplier_id"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'no access_token is supplied' do
|
27
|
+
let(:strategy_options) { { supplier_id: '1' } }
|
28
|
+
|
29
|
+
it 'raises an exception' do
|
30
|
+
expect { strategy }.to raise_exception "Omniorder::ImportStrategy::Groupon requires an access_token"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'imports orders with products and a customer' do
|
35
|
+
orders = []
|
36
|
+
strategy.import_orders { |o| orders << o }
|
37
|
+
|
38
|
+
order = orders.first
|
39
|
+
expect(order.order_number).to eq("FFB7A681BE")
|
40
|
+
expect(order.total_price).to eq(10.99)
|
41
|
+
expect(order.date.to_s).to eq("2013-05-16T08:10:00+00:00")
|
42
|
+
expect(order.order_products.count).to eq(1)
|
43
|
+
|
44
|
+
order_product = order.order_products.first
|
45
|
+
expect(order_product.quantity).to eq(3)
|
46
|
+
expect(order_product.product.code).to eq('03658246')
|
47
|
+
|
48
|
+
customer = order.customer
|
49
|
+
expect(customer.name).to eq("SOME BODY HERE")
|
50
|
+
expect(customer.phone).to eq("01234 982103")
|
51
|
+
expect(customer.address1).to eq("901")
|
52
|
+
expect(customer.address2).to eq("GREENFIELDS LANE")
|
53
|
+
expect(customer.address3).to eq("BRADFORD")
|
54
|
+
expect(customer.address4).to eq("KENT")
|
55
|
+
expect(customer.postcode).to eq("SOME ZIP")
|
56
|
+
expect(customer.country).to eq("UK")
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'the mark_exported option is set' do
|
60
|
+
let(:strategy_options) { { supplier_id: '1', access_token: 'xYRPKcoakMoiRzWgKLV5TqPSdNAaZQT', mark_exported: true } }
|
61
|
+
|
62
|
+
let(:mark_exported_result) { '{ "success": true }' }
|
63
|
+
let!(:mark_exported_stub) do
|
64
|
+
stub_request(
|
65
|
+
:post,
|
66
|
+
"https://scm.commerceinterface.com/api/v2/mark_exported?supplier_id=1&token=xYRPKcoakMoiRzWgKLV5TqPSdNAaZQT&ci_lineitem_ids=[54553920,54553921]"
|
67
|
+
).to_return(
|
68
|
+
body: mark_exported_result,
|
69
|
+
status: 200
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'marks orders as exported when the order handler is truthy' do
|
74
|
+
strategy.import_orders { |o| true if o.order_number == "FFB7A68990" }
|
75
|
+
expect(mark_exported_stub).to have_been_requested.once
|
76
|
+
end
|
77
|
+
|
78
|
+
context "mark exported API call fails" do
|
79
|
+
let(:mark_exported_result) { '{ "success": false }' }
|
80
|
+
|
81
|
+
it 'raises an exception' do
|
82
|
+
expect {
|
83
|
+
strategy.import_orders { |o| true if o.order_number == "FFB7A68990" }
|
84
|
+
}.to raise_exception "Failed to mark Groupon order #FFB7A68990 as exported"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Omniorder::Orderable do
|
4
|
+
let(:product1) { Omniorder::Product.new(:code => 'CODE1') }
|
5
|
+
let(:product2) { Omniorder::Product.new(:code => 'CODE2') }
|
6
|
+
let(:order) { Omniorder::Order.new }
|
7
|
+
|
8
|
+
before do
|
9
|
+
allow(Omniorder::Product).to receive(:find_by_code)
|
10
|
+
.with('CODE1')
|
11
|
+
.and_return(product1)
|
12
|
+
|
13
|
+
allow(Omniorder::Product).to receive(:find_by_code)
|
14
|
+
.with('CODE2')
|
15
|
+
.and_return(product2)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#add_product_by_code' do
|
19
|
+
it 'adds a product to the order when one is found' do
|
20
|
+
order.add_product_by_code('CODE1')
|
21
|
+
|
22
|
+
order_product = order.order_products.first
|
23
|
+
expect(order_product.quantity).to eq(1)
|
24
|
+
expect(order_product.product).to eq(product1)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'does nothing when no matching product is found' do
|
28
|
+
expect(Omniorder::Product).to receive(:find_by_code)
|
29
|
+
.with('CODE1')
|
30
|
+
.and_return(nil)
|
31
|
+
|
32
|
+
order.add_product_by_code('CODE1')
|
33
|
+
|
34
|
+
expect(order.order_products.count).to eq(0)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#product_count' do
|
39
|
+
it 'is the sum of all attached product quantities' do
|
40
|
+
order.add_product_by_code('CODE1', 10)
|
41
|
+
order.add_product_by_code('CODE2', 6)
|
42
|
+
|
43
|
+
expect(order.product_count).to eq(16)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#product_list' do
|
48
|
+
it 'is a human-readable string representing order products' do
|
49
|
+
order.add_product_by_code('CODE1', 10)
|
50
|
+
order.add_product_by_code('CODE2', 6)
|
51
|
+
|
52
|
+
expect(order.product_list).to eq('10xCODE1/6xCODE2')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'webmock/rspec'
|
2
|
+
require 'omniorder'
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
# rspec-expectations config goes here. You can use an alternate
|
6
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
7
|
+
# assertions if you prefer.
|
8
|
+
config.expect_with :rspec do |expectations|
|
9
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
10
|
+
# and `failure_message` of custom matchers include text for helper methods
|
11
|
+
# defined using `chain`, e.g.:
|
12
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
13
|
+
# # => "be bigger than 2 and smaller than 4"
|
14
|
+
# ...rather than:
|
15
|
+
# # => "be bigger than 2"
|
16
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
17
|
+
end
|
18
|
+
|
19
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
20
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
21
|
+
config.mock_with :rspec do |mocks|
|
22
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
23
|
+
# a real object. This is generally recommended, and will default to
|
24
|
+
# `true` in RSpec 4.
|
25
|
+
mocks.verify_partial_doubles = true
|
26
|
+
end
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: omniorder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Howard Wilson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: crack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.4.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.4.2
|
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.6'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '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.1.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.1.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: webmock
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.20.4
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.20.4
|
83
|
+
description: Import online orders from various channels for fulfillment.
|
84
|
+
email:
|
85
|
+
- howard@watsonbox.net
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".rspec"
|
92
|
+
- ".travis.yml"
|
93
|
+
- Gemfile
|
94
|
+
- LICENSE.txt
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- lib/omniorder.rb
|
98
|
+
- lib/omniorder/customer.rb
|
99
|
+
- lib/omniorder/customerable.rb
|
100
|
+
- lib/omniorder/entity.rb
|
101
|
+
- lib/omniorder/import.rb
|
102
|
+
- lib/omniorder/import_strategy/base.rb
|
103
|
+
- lib/omniorder/import_strategy/groupon.rb
|
104
|
+
- lib/omniorder/order.rb
|
105
|
+
- lib/omniorder/order_product.rb
|
106
|
+
- lib/omniorder/order_productable.rb
|
107
|
+
- lib/omniorder/orderable.rb
|
108
|
+
- lib/omniorder/product.rb
|
109
|
+
- lib/omniorder/purchasable.rb
|
110
|
+
- lib/omniorder/version.rb
|
111
|
+
- omniorder.gemspec
|
112
|
+
- spec/assets/imports/groupon/base_spec.rb
|
113
|
+
- spec/assets/imports/groupon/get_orders.json
|
114
|
+
- spec/customerable_spec.rb
|
115
|
+
- spec/import_strategy/groupon_spec.rb
|
116
|
+
- spec/orderable_spec.rb
|
117
|
+
- spec/spec_helper.rb
|
118
|
+
homepage: ''
|
119
|
+
licenses:
|
120
|
+
- MIT
|
121
|
+
metadata: {}
|
122
|
+
post_install_message:
|
123
|
+
rdoc_options: []
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
requirements: []
|
137
|
+
rubyforge_project:
|
138
|
+
rubygems_version: 2.2.2
|
139
|
+
signing_key:
|
140
|
+
specification_version: 4
|
141
|
+
summary: Import online orders from various channels for fulfillment.
|
142
|
+
test_files:
|
143
|
+
- spec/assets/imports/groupon/base_spec.rb
|
144
|
+
- spec/assets/imports/groupon/get_orders.json
|
145
|
+
- spec/customerable_spec.rb
|
146
|
+
- spec/import_strategy/groupon_spec.rb
|
147
|
+
- spec/orderable_spec.rb
|
148
|
+
- spec/spec_helper.rb
|