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 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