omniorder 0.0.1

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: 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
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.2
4
+ - 2.0.0
5
+ - 1.9.3
6
+ - jruby-1.7.16
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in omniorder.gemspec
4
+ gemspec
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
+ [![Build Status](http://img.shields.io/travis/watsonbox/omniorder.svg?style=flat)](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,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/clean'
3
+
4
+ begin
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ task :default => [:spec]
8
+ rescue LoadError
9
+ end
@@ -0,0 +1,7 @@
1
+ module Omniorder
2
+ class Customer < Entity
3
+ include Customerable
4
+
5
+ attributes :username, :name, :phone, :email, :address1, :address2, :address3, :address4, :postcode, :country
6
+ end
7
+ end
@@ -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,12 @@
1
+ module Omniorder
2
+ class Order < Entity
3
+ include Orderable
4
+
5
+ attributes :customer, :order_products, :order_number, :total_price, :date
6
+
7
+ def initialize(attributes = {})
8
+ super
9
+ self.order_products ||= []
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module Omniorder
2
+ class OrderProduct < Entity
3
+ include OrderProductable
4
+
5
+ attributes :product, :quantity
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ module Omniorder
2
+ module OrderProductable
3
+ def <=>(other)
4
+ product <=> other.product
5
+ end
6
+
7
+ def to_s
8
+ quantity.to_s + 'x' + product.code
9
+ end
10
+ end
11
+ 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
@@ -0,0 +1,12 @@
1
+ module Omniorder
2
+ class Product < Entity
3
+ include Purchasable
4
+
5
+ attributes :code
6
+
7
+ # This implementation assumes the product to exist
8
+ def self.find_by_code(code)
9
+ new(:code => code)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module Omniorder
2
+ module Purchasable
3
+ def <=>(other)
4
+ code <=> other.code
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module Omniorder
2
+ VERSION = "0.0.1"
3
+ 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
@@ -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