mws-connect 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.
- data/.gitignore +19 -0
- data/.sublime-project +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +111 -0
- data/Rakefile +1 -0
- data/lib/mws.rb +34 -0
- data/lib/mws/apis.rb +6 -0
- data/lib/mws/apis/feeds.rb +20 -0
- data/lib/mws/apis/feeds/api.rb +103 -0
- data/lib/mws/apis/feeds/distance.rb +23 -0
- data/lib/mws/apis/feeds/feed.rb +114 -0
- data/lib/mws/apis/feeds/image_listing.rb +44 -0
- data/lib/mws/apis/feeds/inventory.rb +77 -0
- data/lib/mws/apis/feeds/measurement.rb +32 -0
- data/lib/mws/apis/feeds/money.rb +31 -0
- data/lib/mws/apis/feeds/price_listing.rb +48 -0
- data/lib/mws/apis/feeds/product.rb +173 -0
- data/lib/mws/apis/feeds/sale_price.rb +31 -0
- data/lib/mws/apis/feeds/shipping.rb +160 -0
- data/lib/mws/apis/feeds/submission_info.rb +45 -0
- data/lib/mws/apis/feeds/submission_result.rb +87 -0
- data/lib/mws/apis/feeds/transaction.rb +37 -0
- data/lib/mws/apis/feeds/weight.rb +19 -0
- data/lib/mws/apis/orders.rb +23 -0
- data/lib/mws/connection.rb +84 -0
- data/lib/mws/enum.rb +81 -0
- data/lib/mws/errors.rb +32 -0
- data/lib/mws/query.rb +45 -0
- data/lib/mws/serializer.rb +81 -0
- data/lib/mws/signer.rb +20 -0
- data/lib/mws/utils.rb +50 -0
- data/mws.gemspec +25 -0
- data/scripts/catalog-workflow +136 -0
- data/spec/mws/apis/feeds/api_spec.rb +229 -0
- data/spec/mws/apis/feeds/distance_spec.rb +43 -0
- data/spec/mws/apis/feeds/feed_spec.rb +92 -0
- data/spec/mws/apis/feeds/image_listing_spec.rb +109 -0
- data/spec/mws/apis/feeds/inventory_spec.rb +135 -0
- data/spec/mws/apis/feeds/measurement_spec.rb +84 -0
- data/spec/mws/apis/feeds/money_spec.rb +43 -0
- data/spec/mws/apis/feeds/price_listing_spec.rb +90 -0
- data/spec/mws/apis/feeds/product_spec.rb +264 -0
- data/spec/mws/apis/feeds/shipping_spec.rb +78 -0
- data/spec/mws/apis/feeds/submission_info_spec.rb +111 -0
- data/spec/mws/apis/feeds/submission_result_spec.rb +157 -0
- data/spec/mws/apis/feeds/transaction_spec.rb +64 -0
- data/spec/mws/apis/feeds/weight_spec.rb +43 -0
- data/spec/mws/apis/orders_spec.rb +9 -0
- data/spec/mws/connection_spec.rb +331 -0
- data/spec/mws/enum_spec.rb +166 -0
- data/spec/mws/query_spec.rb +104 -0
- data/spec/mws/serializer_spec.rb +187 -0
- data/spec/mws/signer_spec.rb +67 -0
- data/spec/mws/utils_spec.rb +147 -0
- data/spec/spec_helper.rb +10 -0
- metadata +220 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module Mws::Apis::Feeds
|
4
|
+
|
5
|
+
class ImageListing
|
6
|
+
|
7
|
+
Type = Mws::Enum.for(
|
8
|
+
main: 'Main',
|
9
|
+
alt1: 'PT1',
|
10
|
+
alt2: 'PT2',
|
11
|
+
alt3: 'PT3',
|
12
|
+
alt4: 'PT4',
|
13
|
+
alt5: 'PT5'
|
14
|
+
)
|
15
|
+
|
16
|
+
attr_reader :sku, :url
|
17
|
+
|
18
|
+
Mws::Enum.sym_reader self, :type
|
19
|
+
|
20
|
+
def initialize(sku, url, type=nil)
|
21
|
+
raise Mws::Errors::ValidationError, 'SKU is required.' if sku.nil? or sku.strip.empty?
|
22
|
+
@sku = sku
|
23
|
+
raise Mws::Errors::ValidationError, 'URL must be an unsecured http address.' unless url =~ URI::regexp('http')
|
24
|
+
@url = url
|
25
|
+
@type = Type.for(type) || Type.MAIN
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(other)
|
29
|
+
return true if equal? other
|
30
|
+
return false unless other.class == self.class
|
31
|
+
sku == other.sku and url == other.url and type == other.type
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_xml(name='ProductImage', parent=nil)
|
35
|
+
Mws::Serializer.tree name, parent do | xml |
|
36
|
+
xml.SKU @sku
|
37
|
+
xml.ImageType @type.val
|
38
|
+
xml.ImageLocation @url
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Mws::Apis::Feeds
|
2
|
+
|
3
|
+
class Inventory
|
4
|
+
|
5
|
+
attr_reader :sku, :available, :quantity, :lookup, :fulfillment, :restock
|
6
|
+
|
7
|
+
def initialize(sku, options)
|
8
|
+
@sku = sku
|
9
|
+
@available = options[:available]
|
10
|
+
@quantity = options[:quantity]
|
11
|
+
@lookup = options[:lookup]
|
12
|
+
@fulfillment = Fulfillment.new(
|
13
|
+
options[:fulfillment_center],
|
14
|
+
options[:fulfillment_latency],
|
15
|
+
options[:fulfillment_type]
|
16
|
+
)
|
17
|
+
@restock = options[:restock]
|
18
|
+
validate
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_xml(name='Inventory', parent=nil)
|
22
|
+
Mws::Serializer.tree name, parent do |xml|
|
23
|
+
xml.SKU @sku
|
24
|
+
xml.FulfillmentCenterID @fulfillment.center unless @fulfillment.center.nil?
|
25
|
+
xml.Available @available unless @available.nil?
|
26
|
+
xml.Quantity @quantity unless @quantity.nil?
|
27
|
+
xml.Lookup @lookup unless @lookup.nil?
|
28
|
+
xml.RestockDate @restock.iso8601 unless @restock.nil?
|
29
|
+
xml.FulfillmentLatency @fulfillment.latency unless @fulfillment.latency.nil?
|
30
|
+
xml.SwitchFulfillmentTo Fulfillment::Type.for(@fulfillment.type).val unless @fulfillment.type.nil?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Fulfillment
|
35
|
+
|
36
|
+
Type = Mws::Enum.for afn: 'AFN', mfn: 'MFN'
|
37
|
+
|
38
|
+
attr_reader :center, :latency
|
39
|
+
|
40
|
+
Mws::Enum.sym_reader self, :type
|
41
|
+
|
42
|
+
def initialize(center, latency, type)
|
43
|
+
@center = center
|
44
|
+
@latency = latency
|
45
|
+
unless @latency.nil? or (@latency.to_i == @latency and @latency > 0)
|
46
|
+
raise Mws::Errors::ValidationError.new('Fulfillment latency must be a whole number greater than zero.')
|
47
|
+
end
|
48
|
+
@type = Type.for(type)
|
49
|
+
raise Mws::Errors::ValidationError.new("Fulfillment type must be either 'AFN' or 'MFN'.") if type and @type.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def validate
|
57
|
+
raise Mws::Errors::ValidationError.new('SKU is required.') if @sku.nil? or @sku.to_s.strip.empty?
|
58
|
+
unless [ @available, @quantity, @lookup ].compact.size == 1
|
59
|
+
raise Mws::Errors::ValidationError.new("One and only one of 'available', 'quantity' or 'lookup' must be specified.")
|
60
|
+
end
|
61
|
+
unless @available.nil? or [ true, false ].include? @available
|
62
|
+
raise Mws::Errors::ValidationError.new('Available must be either true or false.')
|
63
|
+
end
|
64
|
+
unless @quantity.nil? or (@quantity.to_i == @quantity and @quantity >= 0)
|
65
|
+
raise Mws::Errors::ValidationError.new('Quantity must be a whole number greater than or equal to zero.')
|
66
|
+
end
|
67
|
+
unless @lookup.nil? or [ true, false ].include? @lookup
|
68
|
+
raise Mws::Errors::ValidationError.new('Lookup must be either true or false.')
|
69
|
+
end
|
70
|
+
unless @restock.nil? or (@restock.respond_to? :iso8601 and Time.now < @restock)
|
71
|
+
raise Mws::Errors::ValidationError.new('Restock date must be in the future.')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Mws::Apis::Feeds
|
2
|
+
|
3
|
+
class Measurement
|
4
|
+
|
5
|
+
attr_reader :amount
|
6
|
+
|
7
|
+
Mws::Enum.sym_reader self, :unit
|
8
|
+
|
9
|
+
def initialize(amount, unit)
|
10
|
+
@amount = amount
|
11
|
+
@unit = self.class.const_get(:Unit).for(unit)
|
12
|
+
raise Mws::Errors::ValidationError, "Invalid unit of measure '#{unit}'" if @unit.nil?
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
return true if equal? other
|
18
|
+
return false unless other.class == self.class
|
19
|
+
amount == other.amount and unit == other.unit
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_xml(name=nil, parent=nil)
|
23
|
+
name ||= self.class.name.split('::').last
|
24
|
+
amount = @amount
|
25
|
+
amount = '%.2f' % amount if amount.to_s =~ /\d*\.\d\d+/
|
26
|
+
Mws::Serializer.leaf name, parent, amount, unitOfMeasure: @unit.val
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Mws::Apis::Feeds
|
2
|
+
|
3
|
+
class Money < Measurement
|
4
|
+
|
5
|
+
Currency = Mws::Enum.for(
|
6
|
+
usd: 'USD',
|
7
|
+
gbp: 'GBP',
|
8
|
+
eur: 'EUR',
|
9
|
+
jpy: 'JPY',
|
10
|
+
cad: 'CAD',
|
11
|
+
default: 'DEFAULT'
|
12
|
+
)
|
13
|
+
|
14
|
+
Unit = Currency
|
15
|
+
|
16
|
+
def initialize(amount, currency=nil)
|
17
|
+
raise Mws::Errors::ValidationError, "Invalid currency '#{currency}'" if currency and Currency.for(currency).nil?
|
18
|
+
super amount, currency || :usd
|
19
|
+
end
|
20
|
+
|
21
|
+
def currency
|
22
|
+
unit
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_xml(name='Price', parent=nil)
|
26
|
+
Mws::Serializer.leaf name, parent, '%.2f' % @amount, currency: @unit.val
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Mws::Apis::Feeds
|
4
|
+
|
5
|
+
class PriceListing
|
6
|
+
|
7
|
+
attr_reader :sku, :currency, :base, :sale, :min
|
8
|
+
|
9
|
+
def initialize(sku, base, options={})
|
10
|
+
@sku = sku
|
11
|
+
@base = Money.new(base, options[:currency])
|
12
|
+
@currency = @base.currency
|
13
|
+
@min = Money.new(options[:min], @currency) if options.include? :min
|
14
|
+
on_sale(options[:sale][:amount], options[:sale][:from], options[:sale][:to]) if options.include? :sale
|
15
|
+
validate
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_sale(amount, from, to)
|
19
|
+
@sale = SalePrice.new Money.new(amount, @currency), from, to
|
20
|
+
validate
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_xml(name='Price', parent=nil)
|
25
|
+
Mws::Serializer.tree name, parent do |xml|
|
26
|
+
xml.SKU @sku
|
27
|
+
@base.to_xml 'StandardPrice', xml
|
28
|
+
@min.to_xml 'MAP', xml if @min
|
29
|
+
@sale.to_xml 'Sale', xml if @sale
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def validate
|
36
|
+
if @min
|
37
|
+
unless @min.amount < @base.amount
|
38
|
+
raise Mws::Errors::ValidationError, "'Base Price' must be greater than 'Minimum Advertised Price'."
|
39
|
+
end
|
40
|
+
if @sale and @sale.price.amount <= @min.amount
|
41
|
+
raise Mws::Errors::ValidationError, "'Sale Price' must be greater than 'Minimum Advertised Price'."
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
module Mws::Apis::Feeds
|
2
|
+
|
3
|
+
class Product
|
4
|
+
|
5
|
+
CategorySerializer = Mws::Serializer.new ce: 'CE', fba: 'FBA', eu_compliance: 'EUCompliance'
|
6
|
+
|
7
|
+
attr_reader :sku, :description
|
8
|
+
|
9
|
+
attr_accessor :upc, :tax_code, :msrp, :brand, :manufacturer, :name, :description, :bullet_points
|
10
|
+
attr_accessor :item_dimensions, :package_dimensions, :package_weight, :shipping_weight
|
11
|
+
attr_accessor :category, :details
|
12
|
+
|
13
|
+
def initialize(sku, &block)
|
14
|
+
@sku = sku
|
15
|
+
@bullet_points = []
|
16
|
+
ProductBuilder.new(self).instance_eval &block if block_given?
|
17
|
+
raise Mws::Errors::ValidationError, 'Product must have a category when details are specified.' if @details and @category.nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_xml(name='Product', parent=nil)
|
21
|
+
Mws::Serializer.tree name, parent do |xml|
|
22
|
+
xml.SKU @sku
|
23
|
+
xml.StandardProductID {
|
24
|
+
xml.Type 'UPC'
|
25
|
+
xml.Value @upc
|
26
|
+
} unless @upc.nil?
|
27
|
+
xml.ProductTaxCode @tax_code unless @upc.nil?
|
28
|
+
xml.DescriptionData {
|
29
|
+
xml.Title @name unless @name.nil?
|
30
|
+
xml.Brand @brand unless @brand.nil?
|
31
|
+
xml.Description @description unless @description.nil?
|
32
|
+
bullet_points.each do | bullet_point |
|
33
|
+
xml.BulletPoint bullet_point
|
34
|
+
end
|
35
|
+
@item_dimensions.to_xml('ItemDimensions', xml) unless @item_dimensions.nil?
|
36
|
+
@package_dimensions.to_xml('PackageDimensions', xml) unless @item_dimensions.nil?
|
37
|
+
|
38
|
+
@package_weight.to_xml('PackageWeight', xml) unless @package_weight.nil?
|
39
|
+
@shipping_weight.to_xml('ShippingWeight', xml) unless @shipping_weight.nil?
|
40
|
+
|
41
|
+
@msrp.to_xml 'MSRP', xml unless @msrp.nil?
|
42
|
+
|
43
|
+
xml.Manufacturer @manufacturer unless @manufacturer.nil?
|
44
|
+
}
|
45
|
+
|
46
|
+
unless @details.nil?
|
47
|
+
xml.ProductData {
|
48
|
+
CategorySerializer.xml_for @category, {product_type: @details}, xml
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class DelegatingBuilder
|
55
|
+
|
56
|
+
def initialize(delegate)
|
57
|
+
@delegate = delegate
|
58
|
+
end
|
59
|
+
|
60
|
+
def method_missing(method, *args, &block)
|
61
|
+
@delegate.send("#{method}=", *args, &block) if @delegate.respond_to? "#{method}="
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class ProductBuilder < DelegatingBuilder
|
66
|
+
|
67
|
+
def initialize(product)
|
68
|
+
super product
|
69
|
+
@product = product
|
70
|
+
end
|
71
|
+
|
72
|
+
def msrp(amount, currency)
|
73
|
+
@product.msrp = Money.new amount, currency
|
74
|
+
end
|
75
|
+
|
76
|
+
def item_dimensions(&block)
|
77
|
+
@product.item_dimensions = Dimensions.new
|
78
|
+
DimensionsBuilder.new(@product.item_dimensions).instance_eval &block if block_given?
|
79
|
+
end
|
80
|
+
|
81
|
+
def package_dimensions(&block)
|
82
|
+
@product.package_dimensions = Dimensions.new
|
83
|
+
DimensionsBuilder.new(@product.package_dimensions).instance_eval &block if block_given?
|
84
|
+
end
|
85
|
+
|
86
|
+
def package_weight(value, unit=nil)
|
87
|
+
@product.package_weight = Weight.new(value, unit)
|
88
|
+
end
|
89
|
+
|
90
|
+
def shipping_weight(value, unit=nil)
|
91
|
+
@product.shipping_weight = Weight.new(value, unit)
|
92
|
+
end
|
93
|
+
|
94
|
+
def bullet_point(bullet_point)
|
95
|
+
@product.bullet_points << bullet_point
|
96
|
+
end
|
97
|
+
|
98
|
+
def details(details=nil, &block)
|
99
|
+
@product.details = details || {}
|
100
|
+
DetailBuilder.new(@product.details).instance_eval &block if block_given?
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
class Dimensions
|
106
|
+
|
107
|
+
attr_accessor :length, :width, :height, :weight
|
108
|
+
|
109
|
+
def to_xml(name='Dimensions', parent=nil)
|
110
|
+
Mws::Serializer.tree name, parent do |xml|
|
111
|
+
@length.to_xml 'Length', xml unless @length.nil?
|
112
|
+
@width.to_xml 'Width', xml unless @width.nil?
|
113
|
+
@height.to_xml 'Height', xml unless @height.nil?
|
114
|
+
@weight.to_xml 'Weight', xml unless @weight.nil?
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
class DimensionsBuilder
|
121
|
+
|
122
|
+
def initialize(dimensions)
|
123
|
+
@dimensions = dimensions
|
124
|
+
end
|
125
|
+
|
126
|
+
def length(value, unit=nil)
|
127
|
+
@dimensions.length = Distance.new(value, unit)
|
128
|
+
end
|
129
|
+
|
130
|
+
def width(value, unit=nil)
|
131
|
+
@dimensions.width = Distance.new(value, unit)
|
132
|
+
end
|
133
|
+
|
134
|
+
def height(value, unit=nil)
|
135
|
+
@dimensions.height = Distance.new(value, unit)
|
136
|
+
end
|
137
|
+
|
138
|
+
def weight(value, unit=nil)
|
139
|
+
@dimensions.weight = Weight.new(value, unit)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class DetailBuilder
|
144
|
+
|
145
|
+
def initialize(details)
|
146
|
+
@details = details
|
147
|
+
end
|
148
|
+
|
149
|
+
def as_distance(amount, unit=nil)
|
150
|
+
Distance.new amount, unit
|
151
|
+
end
|
152
|
+
|
153
|
+
def as_weight(amount, unit=nil)
|
154
|
+
Weight.new amount, unit
|
155
|
+
end
|
156
|
+
|
157
|
+
def as_money(amount, currency=nil)
|
158
|
+
Money.new amount, currency
|
159
|
+
end
|
160
|
+
|
161
|
+
def method_missing(method, *args, &block)
|
162
|
+
if block_given?
|
163
|
+
@details[method] = {}
|
164
|
+
DetailBuilder.new(@details[method]).instance_eval(&block)
|
165
|
+
elsif args.length > 0
|
166
|
+
@details[method] = args[0]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Mws::Apis::Feeds
|
4
|
+
|
5
|
+
class SalePrice
|
6
|
+
|
7
|
+
attr_reader :price, :from, :to
|
8
|
+
|
9
|
+
def initialize(price, from, to)
|
10
|
+
@price = price
|
11
|
+
@from = from
|
12
|
+
@to = to
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
return true if equal? other
|
17
|
+
return false unless other.class == self.class
|
18
|
+
@price == other.price and @from == other.from and @to == other.to
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_xml(name='Sale', parent=nil)
|
22
|
+
Mws::Serializer.tree name, parent do |xml|
|
23
|
+
xml.StartDate @from.iso8601
|
24
|
+
xml.EndDate @to.iso8601
|
25
|
+
price.to_xml 'SalePrice', xml
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Mws::Apis::Feeds
|
4
|
+
|
5
|
+
class Shipping
|
6
|
+
|
7
|
+
Region = Mws::Enum.for(
|
8
|
+
continental_us: 'Cont US',
|
9
|
+
us_protectorates: 'US Prot',
|
10
|
+
alaska_hawaii: 'Alaska Hawaii',
|
11
|
+
apo_fpo: 'APO/FPO',
|
12
|
+
canada: 'Canada',
|
13
|
+
europe: 'Europe',
|
14
|
+
asia: 'Asia',
|
15
|
+
other: 'Outside US, EU, CA, Asia'
|
16
|
+
)
|
17
|
+
|
18
|
+
Variant = Mws::Enum.for(
|
19
|
+
street: 'Street Addr',
|
20
|
+
po_box: 'PO Box'
|
21
|
+
)
|
22
|
+
|
23
|
+
Speed = Mws::Enum.for(
|
24
|
+
standard: 'Std',
|
25
|
+
expedited: 'Exp',
|
26
|
+
two_day: 'Second',
|
27
|
+
one_day: 'Next'
|
28
|
+
)
|
29
|
+
|
30
|
+
attr_reader :sku
|
31
|
+
|
32
|
+
def initialize(sku, &block)
|
33
|
+
raise Mws::Errors::ValidationError.new('SKU is required.') if sku.nil? or sku.to_s.strip.empty?
|
34
|
+
@sku = sku
|
35
|
+
@options = []
|
36
|
+
Builder.new(self).instance_eval &block if block_given?
|
37
|
+
end
|
38
|
+
|
39
|
+
def options
|
40
|
+
@options.dup
|
41
|
+
end
|
42
|
+
|
43
|
+
def <<(option)
|
44
|
+
@options << option
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_xml(name='Override', parent=nil)
|
48
|
+
Mws::Serializer.tree name, parent do |xml|
|
49
|
+
xml.SKU @sku
|
50
|
+
@options.each { |option| option.to_xml 'ShippingOverride', xml }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Option
|
55
|
+
|
56
|
+
Mws::Enum.sym_reader self, :region, :speed, :variant
|
57
|
+
|
58
|
+
def initialize(region, speed=Speed.STANDARD, variant=nil)
|
59
|
+
@region = Region.for(region)
|
60
|
+
@speed = Speed.for(speed)
|
61
|
+
@variant = nil
|
62
|
+
if supports_variant?
|
63
|
+
@variant = Variant.for(variant) || Variant.STREET
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def supports_variant?
|
68
|
+
[ Region.CONTINENTAL_US, Region.US_PROTECTORATES, Region.ALASKA_HAWAII, Region.APO_FPO ].include? @region
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_s
|
72
|
+
return @speed.val if [ Speed.TWO_DAY, Speed.ONE_DAY ].include? @speed
|
73
|
+
[ @speed, @region, @variant ].compact.map { |it| it.val }.join ' '
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
class Restriction
|
79
|
+
|
80
|
+
attr_reader :option, :restricted
|
81
|
+
|
82
|
+
def initialize(option, restricted=true)
|
83
|
+
@option = option
|
84
|
+
@restricted = restricted
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_xml(name='ShippingOverride', parent=nil)
|
88
|
+
Mws::Serializer.tree name, parent do |xml|
|
89
|
+
xml.ShipOption @option
|
90
|
+
xml.IsShippingRestricted @restricted
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
class Override
|
97
|
+
|
98
|
+
Type = Mws::Enum.for(
|
99
|
+
adjust: 'Additive',
|
100
|
+
replace: 'Exclusive'
|
101
|
+
)
|
102
|
+
|
103
|
+
attr_reader :option, :amount
|
104
|
+
|
105
|
+
Mws::Enum.sym_reader self, :type
|
106
|
+
|
107
|
+
def initialize(option, type, amount)
|
108
|
+
@option = option
|
109
|
+
@type = Type.for(type)
|
110
|
+
@amount = amount
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_xml(name='ShippingOverride', parent=nil)
|
114
|
+
Mws::Serializer.tree name, parent do |xml|
|
115
|
+
xml.ShipOption @option
|
116
|
+
xml.Type @type.val
|
117
|
+
@amount.to_xml 'ShipAmount', xml
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
class Builder
|
124
|
+
|
125
|
+
@target
|
126
|
+
|
127
|
+
def initialize(target)
|
128
|
+
@target = target
|
129
|
+
end
|
130
|
+
|
131
|
+
def restriction(restricted, region, speed, variant)
|
132
|
+
@target << Restriction.new(Option.new(region, speed, variant), restricted)
|
133
|
+
end
|
134
|
+
|
135
|
+
def restricted(region, speed, variant=nil)
|
136
|
+
restriction true, region, speed, variant
|
137
|
+
end
|
138
|
+
|
139
|
+
def unrestricted(region, speed, variant=nil)
|
140
|
+
restriction false, region, speed, variant
|
141
|
+
end
|
142
|
+
|
143
|
+
def override(type, amount, currency, region, speed, variant)
|
144
|
+
@target << Override.new(Option.new(region, speed, variant), type,
|
145
|
+
Money.new(amount, currency))
|
146
|
+
end
|
147
|
+
|
148
|
+
def adjust(amount, currency, region, speed, variant=nil)
|
149
|
+
override :adjust, amount, currency, region, speed, variant
|
150
|
+
end
|
151
|
+
|
152
|
+
def replace(amount, currency, region, speed, variant=nil)
|
153
|
+
override :replace, amount, currency, region, speed, variant
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|