solidus_shipstation 1.0.0
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/.bundle/config +2 -0
- data/.circleci/config.yml +41 -0
- data/.gem_release.yml +5 -0
- data/.github/stale.yml +17 -0
- data/.github_changelog_generator +2 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.rubocop.yml +13 -0
- data/.rubocop_todo.yml +40 -0
- data/CHANGELOG.md +36 -0
- data/Gemfile +33 -0
- data/LICENSE +26 -0
- data/README.md +208 -0
- data/Rakefile +6 -0
- data/app/assets/javascripts/spree/backend/solidus_shipstation.js +2 -0
- data/app/assets/javascripts/spree/frontend/solidus_shipstation.js +2 -0
- data/app/assets/stylesheets/spree/backend/solidus_shipstation.css +4 -0
- data/app/assets/stylesheets/spree/frontend/solidus_shipstation.css +4 -0
- data/app/controllers/spree/shipstation_controller.rb +45 -0
- data/app/decorators/models/solidus_shipstation/spree/shipment_decorator.rb +33 -0
- data/app/helpers/solidus_shipstation/export_helper.rb +32 -0
- data/app/jobs/solidus_shipstation/api/schedule_shipment_syncs_job.rb +19 -0
- data/app/jobs/solidus_shipstation/api/sync_shipments_job.rb +41 -0
- data/app/queries/solidus_shipstation/shipment/between_query.rb +14 -0
- data/app/queries/solidus_shipstation/shipment/exportable_query.rb +24 -0
- data/app/queries/solidus_shipstation/shipment/pending_api_sync_query.rb +63 -0
- data/app/views/spree/shipstation/export.xml.builder +58 -0
- data/bin/console +17 -0
- data/bin/rails +7 -0
- data/bin/rails-engine +13 -0
- data/bin/rails-sandbox +16 -0
- data/bin/rake +7 -0
- data/bin/sandbox +86 -0
- data/bin/setup +8 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +6 -0
- data/db/migrate/20210220093010_add_shipstation_api_sync_fields.rb +9 -0
- data/lib/generators/solidus_shipstation/install/install_generator.rb +27 -0
- data/lib/generators/solidus_shipstation/install/templates/initializer.rb +62 -0
- data/lib/solidus_shipstation.rb +16 -0
- data/lib/solidus_shipstation/api/batch_syncer.rb +70 -0
- data/lib/solidus_shipstation/api/client.rb +38 -0
- data/lib/solidus_shipstation/api/rate_limited_error.rb +23 -0
- data/lib/solidus_shipstation/api/request_error.rb +33 -0
- data/lib/solidus_shipstation/api/request_runner.rb +50 -0
- data/lib/solidus_shipstation/api/shipment_serializer.rb +84 -0
- data/lib/solidus_shipstation/api/threshold_verifier.rb +28 -0
- data/lib/solidus_shipstation/configuration.rb +44 -0
- data/lib/solidus_shipstation/engine.rb +19 -0
- data/lib/solidus_shipstation/errors.rb +23 -0
- data/lib/solidus_shipstation/shipment_notice.rb +58 -0
- data/lib/solidus_shipstation/testing_support/factories.rb +4 -0
- data/lib/solidus_shipstation/version.rb +5 -0
- data/solidus_shipstation.gemspec +40 -0
- data/spec/controllers/spree/shipstation_controller_spec.rb +103 -0
- data/spec/fixtures/shipstation_xml_schema.xsd +171 -0
- data/spec/jobs/solidus_shipstation/api/schedule_shipment_syncs_job_spec.rb +32 -0
- data/spec/jobs/solidus_shipstation/api/sync_shipments_job_spec.rb +102 -0
- data/spec/lib/solidus_shipstation/api/batch_syncer_spec.rb +229 -0
- data/spec/lib/solidus_shipstation/api/client_spec.rb +120 -0
- data/spec/lib/solidus_shipstation/api/rate_limited_error_spec.rb +21 -0
- data/spec/lib/solidus_shipstation/api/request_error_spec.rb +20 -0
- data/spec/lib/solidus_shipstation/api/request_runner_spec.rb +64 -0
- data/spec/lib/solidus_shipstation/api/shipment_serializer_spec.rb +12 -0
- data/spec/lib/solidus_shipstation/api/threshold_verifier_spec.rb +61 -0
- data/spec/lib/solidus_shipstation/shipment_notice_spec.rb +111 -0
- data/spec/lib/solidus_shipstation_spec.rb +9 -0
- data/spec/models/spree/shipment_spec.rb +49 -0
- data/spec/queries/solidus_shipstation/shipment/between_query_spec.rb +53 -0
- data/spec/queries/solidus_shipstation/shipment/exportable_query_spec.rb +53 -0
- data/spec/queries/solidus_shipstation/shipment/pending_api_sync_query_spec.rb +37 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/configuration_helper.rb +13 -0
- data/spec/support/controllers.rb +1 -0
- data/spec/support/webmock.rb +3 -0
- data/spec/support/xsd.rb +5 -0
- metadata +248 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusShipstation
|
4
|
+
module Api
|
5
|
+
class ThresholdVerifier
|
6
|
+
class << self
|
7
|
+
def call(shipment)
|
8
|
+
return false unless shipment.order.completed?
|
9
|
+
|
10
|
+
!!(shipment_requires_creation?(shipment) || shipment_requires_update?(shipment))
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def shipment_requires_creation?(shipment)
|
16
|
+
shipment.shipstation_synced_at.nil? &&
|
17
|
+
Time.zone.now - shipment.order.updated_at < SolidusShipstation.config.api_sync_threshold
|
18
|
+
end
|
19
|
+
|
20
|
+
def shipment_requires_update?(shipment)
|
21
|
+
shipment.shipstation_synced_at &&
|
22
|
+
shipment.shipstation_synced_at < shipment.order.updated_at &&
|
23
|
+
Time.zone.now - shipment.order.updated_at < SolidusShipstation.config.api_sync_threshold
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusShipstation
|
4
|
+
class Configuration
|
5
|
+
attr_accessor(
|
6
|
+
:username,
|
7
|
+
:password,
|
8
|
+
:weight_units,
|
9
|
+
:ssl_encrypted,
|
10
|
+
:capture_at_notification,
|
11
|
+
:export_canceled_shipments,
|
12
|
+
:api_batch_size,
|
13
|
+
:api_sync_threshold,
|
14
|
+
:api_shipment_serializer,
|
15
|
+
:api_key,
|
16
|
+
:api_secret,
|
17
|
+
:api_shipment_matcher,
|
18
|
+
:error_handler,
|
19
|
+
)
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@api_batch_size = 100
|
23
|
+
@api_sync_threshold = 7.days
|
24
|
+
@error_handler = ->(_error, _extra = {}) {
|
25
|
+
Rails.logger.error "#{error.inspect} (#{extra.inspect})"
|
26
|
+
}
|
27
|
+
@api_shipment_matcher = proc do |shipstation_order, shipments|
|
28
|
+
shipments.find { |shipment| shipment.number == shipstation_order['orderNumber'] }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
def configuration
|
35
|
+
@configuration ||= Configuration.new
|
36
|
+
end
|
37
|
+
|
38
|
+
alias config configuration
|
39
|
+
|
40
|
+
def configure
|
41
|
+
yield configuration
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'solidus_core'
|
4
|
+
require 'solidus_support'
|
5
|
+
|
6
|
+
module SolidusShipstation
|
7
|
+
class Engine < Rails::Engine
|
8
|
+
include SolidusSupport::EngineExtensions
|
9
|
+
|
10
|
+
isolate_namespace ::Spree
|
11
|
+
|
12
|
+
engine_name 'solidus_shipstation'
|
13
|
+
|
14
|
+
# use rspec for tests
|
15
|
+
config.generators do |g|
|
16
|
+
g.test_framework :rspec
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusShipstation
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
class OrderNotPaidError < Error
|
7
|
+
def initialize(order, *args)
|
8
|
+
super("Order #{order.number} is not paid and capture_at_notification is false", *args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ShipmentNotFoundError < Error
|
13
|
+
def initialize(shipment_number, *args)
|
14
|
+
super("Could not find shipment with number #{shipment_number}", *args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class PaymentError < Error
|
19
|
+
def initialize(payment, *args)
|
20
|
+
super("Could not process payment #{payment.id}", *args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusShipstation
|
4
|
+
class ShipmentNotice
|
5
|
+
attr_reader :shipment_number, :shipment_tracking
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def from_payload(params)
|
9
|
+
new(
|
10
|
+
shipment_number: params[:order_number],
|
11
|
+
shipment_tracking: params[:tracking_number],
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(shipment_number:, shipment_tracking:)
|
17
|
+
@shipment_number = shipment_number
|
18
|
+
@shipment_tracking = shipment_tracking
|
19
|
+
end
|
20
|
+
|
21
|
+
def apply
|
22
|
+
unless shipment
|
23
|
+
raise ShipmentNotFoundError, shipment
|
24
|
+
end
|
25
|
+
|
26
|
+
process_payment
|
27
|
+
ship_shipment
|
28
|
+
|
29
|
+
shipment
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def shipment
|
35
|
+
@shipment ||= ::Spree::Shipment.find_by(number: shipment_number)
|
36
|
+
end
|
37
|
+
|
38
|
+
def process_payment
|
39
|
+
return if shipment.order.paid?
|
40
|
+
|
41
|
+
unless SolidusShipstation.configuration.capture_at_notification
|
42
|
+
raise OrderNotPaidError, shipment.order
|
43
|
+
end
|
44
|
+
|
45
|
+
shipment.order.payments.pending.each do |payment|
|
46
|
+
payment.capture!
|
47
|
+
rescue ::Spree::Core::GatewayError
|
48
|
+
raise PaymentError, payment
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def ship_shipment
|
53
|
+
shipment.update!(tracking: shipment_tracking)
|
54
|
+
shipment.ship! if shipment.can_ship?
|
55
|
+
shipment.order.recalculate
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/solidus_shipstation/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'solidus_shipstation'
|
7
|
+
spec.version = SolidusShipstation::VERSION
|
8
|
+
spec.authors = ['Stephen Puiszis']
|
9
|
+
spec.email = 'steve@tablexi.com'
|
10
|
+
|
11
|
+
spec.summary = 'Solidus/ShipStation Integration'
|
12
|
+
spec.description = 'Integrates ShipStation API with Solidus. Supports exporting shipments and importing tracking numbers'
|
13
|
+
spec.homepage = 'https://github.com/solidusio-contrib/solidus_shipstation'
|
14
|
+
spec.license = 'BSD-3-Clause'
|
15
|
+
|
16
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
17
|
+
spec.metadata['source_code_uri'] = 'https://github.com/solidusio-contrib/solidus_shipstation'
|
18
|
+
spec.metadata['changelog_uri'] = 'https://github.com/solidusio-contrib/solidus_shipstation/blob/master/CHANGELOG.md'
|
19
|
+
|
20
|
+
spec.required_ruby_version = Gem::Requirement.new('~> 2.5')
|
21
|
+
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
+
files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
|
25
|
+
|
26
|
+
spec.files = files.grep_v(%r{^(test|spec|features)/})
|
27
|
+
spec.test_files = files.grep(%r{^(test|spec|features)/})
|
28
|
+
spec.bindir = "exe"
|
29
|
+
spec.executables = files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
spec.add_dependency 'httparty', '~> 0.18'
|
33
|
+
spec.add_dependency 'solidus_core', ['>= 2.0.0', '< 4']
|
34
|
+
spec.add_dependency 'solidus_support', '~> 0.5'
|
35
|
+
|
36
|
+
spec.add_development_dependency 'rails-controller-testing'
|
37
|
+
spec.add_development_dependency 'rspec-xsd'
|
38
|
+
spec.add_development_dependency 'solidus_dev_support', '~> 2.5'
|
39
|
+
spec.add_development_dependency 'webmock'
|
40
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Spree::ShipstationController do
|
4
|
+
render_views
|
5
|
+
|
6
|
+
describe '#export' do
|
7
|
+
context 'when the authentication is invalid' do
|
8
|
+
it 'returns an error error' do
|
9
|
+
get :export, params: { format: 'xml' }
|
10
|
+
|
11
|
+
expect(response.status).to eq(401)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'when the authentication is valid' do
|
16
|
+
it 'responds with 200 OK' do
|
17
|
+
stub_shipstation_auth
|
18
|
+
create(:order_ready_to_ship)
|
19
|
+
|
20
|
+
get :export,
|
21
|
+
params: {
|
22
|
+
start_date: 1.day.ago.strftime('%m/%d/%Y %H:%M'),
|
23
|
+
end_date: 1.day.from_now.strftime('%m/%d/%Y %H:%M'),
|
24
|
+
format: 'xml'
|
25
|
+
}
|
26
|
+
|
27
|
+
expect(response.status).to eq(200)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'generates ShipStation-compliant XML' do
|
31
|
+
stub_shipstation_auth
|
32
|
+
create(:order_ready_to_ship)
|
33
|
+
|
34
|
+
get :export, params: {
|
35
|
+
start_date: 1.day.ago.strftime('%m/%d/%Y %H:%M'),
|
36
|
+
end_date: 1.day.from_now.strftime('%m/%d/%Y %H:%M'),
|
37
|
+
format: 'xml'
|
38
|
+
}
|
39
|
+
|
40
|
+
expect(response.body).to pass_validation('spec/fixtures/shipstation_xml_schema.xsd')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#shipnotify' do
|
46
|
+
context 'when the authentication is valid' do
|
47
|
+
context 'when the shipment can be found' do
|
48
|
+
it 'responds with 200 OK' do
|
49
|
+
stub_shipstation_auth
|
50
|
+
shipment = create(:order_ready_to_ship).shipments.first
|
51
|
+
|
52
|
+
post :shipnotify, params: {
|
53
|
+
order_number: shipment.number,
|
54
|
+
tracking_number: '123456',
|
55
|
+
format: 'xml',
|
56
|
+
}
|
57
|
+
shipment.reload
|
58
|
+
|
59
|
+
expect(response.status).to eq(200)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'updates the shipment' do
|
63
|
+
stub_shipstation_auth
|
64
|
+
shipment = create(:order_ready_to_ship).shipments.first
|
65
|
+
|
66
|
+
post :shipnotify, params: {
|
67
|
+
order_number: shipment.number,
|
68
|
+
tracking_number: '123456',
|
69
|
+
format: 'xml',
|
70
|
+
}
|
71
|
+
shipment.reload
|
72
|
+
|
73
|
+
expect(shipment).to have_attributes(
|
74
|
+
tracking: '123456',
|
75
|
+
state: 'shipped',
|
76
|
+
shipped_at: an_instance_of(ActiveSupport::TimeWithZone),
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'when the shipment cannot be found' do
|
82
|
+
it 'responds with 400 Bad Request' do
|
83
|
+
stub_shipstation_auth
|
84
|
+
shipment = create(:order_ready_to_ship).shipments.first
|
85
|
+
|
86
|
+
post :shipnotify, params: {
|
87
|
+
order_number: 'ABC123',
|
88
|
+
tracking_number: '123456',
|
89
|
+
format: 'xml',
|
90
|
+
}
|
91
|
+
shipment.reload
|
92
|
+
|
93
|
+
expect(response.status).to eq(400)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def stub_shipstation_auth(username = 'mario', password = 'lemieux')
|
100
|
+
stub_configuration(username: username, password: password)
|
101
|
+
request.headers['Authorization'] = ActionController::HttpAuthentication::Basic.encode_credentials(username, password)
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
2
|
+
<xs:element name="Orders">
|
3
|
+
<xs:complexType>
|
4
|
+
<xs:sequence>
|
5
|
+
<xs:element name="Order" maxOccurs="unbounded" minOccurs="0">
|
6
|
+
<xs:complexType>
|
7
|
+
<xs:all>
|
8
|
+
<xs:element type="String50" name="OrderID" minOccurs="0"/>
|
9
|
+
<xs:element type="String50" name="OrderNumber"/>
|
10
|
+
<xs:element type="DateTime" name="OrderDate"/>
|
11
|
+
<xs:element type="String50" name="OrderStatus"/>
|
12
|
+
<xs:element type="DateTime" name="LastModified"/>
|
13
|
+
<xs:element type="String100" name="ShippingMethod" minOccurs="0"/>
|
14
|
+
<xs:element type="String50" name="PaymentMethod" minOccurs="0"/>
|
15
|
+
<xs:element type="xs:float" name="OrderTotal"/>
|
16
|
+
<xs:element type="xs:float" name="TaxAmount" minOccurs="0"/>
|
17
|
+
<xs:element type="xs:float" name="ShippingAmount" minOccurs="0"/>
|
18
|
+
<xs:element type="String1000" name="CustomerNotes" minOccurs="0"/>
|
19
|
+
<xs:element type="String1000" name="InternalNotes" minOccurs="0"/>
|
20
|
+
<xs:element type="xs:boolean" name="Gift" minOccurs="0"/>
|
21
|
+
<xs:element type="String1000" name="GiftMessage" minOccurs="0"/>
|
22
|
+
<xs:element type="String100" name="CustomField1" minOccurs="0"/>
|
23
|
+
<xs:element type="String100" name="CustomField2" minOccurs="0"/>
|
24
|
+
<xs:element type="String100" name="CustomField3" minOccurs="0"/>
|
25
|
+
<xs:element type="String100" name="RequestedWarehouse" minOccurs="0"/>
|
26
|
+
<xs:element type="String50" name="Source" minOccurs="0" />
|
27
|
+
<xs:element name="Customer">
|
28
|
+
<xs:complexType>
|
29
|
+
<xs:all>
|
30
|
+
<xs:element type="String100" name="CustomerCode"/>
|
31
|
+
<xs:element name="BillTo">
|
32
|
+
<xs:complexType>
|
33
|
+
<xs:all>
|
34
|
+
<xs:element type="String100" name="Name"/>
|
35
|
+
<xs:element type="String100" name="Company" minOccurs="0"/>
|
36
|
+
<xs:element type="String50" name="Phone" minOccurs="0"/>
|
37
|
+
<xs:element type="Email" name="Email" minOccurs="0"/>
|
38
|
+
<xs:element type="String200" name="Address1" minOccurs="0"/>
|
39
|
+
<xs:element type="String200" name="Address2" minOccurs="0"/>
|
40
|
+
<xs:element type="String100" name="City" minOccurs="0"/>
|
41
|
+
<xs:element type="String100" name="State" minOccurs="0"/>
|
42
|
+
<xs:element type="String50" name="PostalCode" minOccurs="0"/>
|
43
|
+
<xs:element type="StringExactly2" name="Country" minOccurs="0"/>
|
44
|
+
</xs:all>
|
45
|
+
</xs:complexType>
|
46
|
+
</xs:element>
|
47
|
+
<xs:element name="ShipTo">
|
48
|
+
<xs:complexType>
|
49
|
+
<xs:all>
|
50
|
+
<xs:element type="String100" name="Name"/>
|
51
|
+
<xs:element type="String100" name="Company" minOccurs="0"/>
|
52
|
+
<xs:element type="String200" name="Address1"/>
|
53
|
+
<xs:element type="String200" name="Address2" minOccurs="0"/>
|
54
|
+
<xs:element type="String100" name="City"/>
|
55
|
+
<xs:element type="String100" name="State" minOccurs="0"/>
|
56
|
+
<xs:element type="String50" name="PostalCode" minOccurs="1"/>
|
57
|
+
<xs:element type="StringExactly2" name="Country"/>
|
58
|
+
<xs:element type="String50" name="Phone" minOccurs="0"/>
|
59
|
+
</xs:all>
|
60
|
+
</xs:complexType>
|
61
|
+
</xs:element>
|
62
|
+
</xs:all>
|
63
|
+
</xs:complexType>
|
64
|
+
</xs:element>
|
65
|
+
<xs:element name="Items">
|
66
|
+
<xs:complexType>
|
67
|
+
<xs:sequence>
|
68
|
+
<xs:element name="Item" maxOccurs="unbounded" minOccurs="0">
|
69
|
+
<xs:complexType>
|
70
|
+
<xs:all>
|
71
|
+
<xs:element type="String50" name="LineItemID" minOccurs="0"/>
|
72
|
+
<xs:element type="String100" name="SKU"/>
|
73
|
+
<xs:element type="String200" name="Name"/>
|
74
|
+
<xs:element type="xs:boolean" name="Adjustment" minOccurs="0"/>
|
75
|
+
<xs:element type="xs:anyURI" name="ImageUrl" minOccurs="0"/>
|
76
|
+
<xs:element type="xs:float" name="Weight" minOccurs="0"/>
|
77
|
+
<xs:element name="WeightUnits" minOccurs="0">
|
78
|
+
<xs:simpleType>
|
79
|
+
<xs:restriction base="xs:string">
|
80
|
+
<xs:pattern value="|pound|pounds|lb|lbs|gram|grams|gm|oz|ounces|Pound|Pounds|Lb|Lbs|Gram|Grams|Gm|Oz|Ounces|POUND|POUNDS|LB|LBS|GRAM|GRAMS|GM|OZ|OUNCES"/>
|
81
|
+
</xs:restriction>
|
82
|
+
</xs:simpleType>
|
83
|
+
</xs:element>
|
84
|
+
<xs:element name="Dimensions" minOccurs="0">
|
85
|
+
<xs:complexType>
|
86
|
+
<xs:all>
|
87
|
+
<xs:element name="DimensionUnits" minOccurs="0">
|
88
|
+
<xs:simpleType>
|
89
|
+
<xs:restriction base="xs:string">
|
90
|
+
<xs:pattern value="|inch|inches|in|centimeter|centimeters|cm|INCH|INCHES|IN|CENTIMETER|CENTIMETERS|CM"/>
|
91
|
+
</xs:restriction>
|
92
|
+
</xs:simpleType>
|
93
|
+
</xs:element>
|
94
|
+
<xs:element type="xs:float" name="Length"/>
|
95
|
+
<xs:element type="xs:float" name="Width"/>
|
96
|
+
<xs:element type="xs:float" name="Height"/>
|
97
|
+
</xs:all>
|
98
|
+
</xs:complexType>
|
99
|
+
</xs:element>
|
100
|
+
<xs:element type="xs:int" name="Quantity"/>
|
101
|
+
<xs:element type="xs:float" name="UnitPrice"/>
|
102
|
+
<xs:element type="String100" name="Location" minOccurs="0"/>
|
103
|
+
<xs:element name="Options" minOccurs="0">
|
104
|
+
<xs:complexType>
|
105
|
+
<xs:sequence>
|
106
|
+
<xs:element name="Option" maxOccurs="100" minOccurs="0">
|
107
|
+
<xs:complexType>
|
108
|
+
<xs:all>
|
109
|
+
<xs:element type="String100" name="Name"/>
|
110
|
+
<xs:element type="String100" name="Value"/>
|
111
|
+
<xs:element type="xs:float" name="Weight" minOccurs="0"/>
|
112
|
+
</xs:all>
|
113
|
+
</xs:complexType>
|
114
|
+
</xs:element>
|
115
|
+
</xs:sequence>
|
116
|
+
</xs:complexType>
|
117
|
+
</xs:element>
|
118
|
+
</xs:all>
|
119
|
+
</xs:complexType>
|
120
|
+
</xs:element>
|
121
|
+
</xs:sequence>
|
122
|
+
</xs:complexType>
|
123
|
+
</xs:element>
|
124
|
+
</xs:all>
|
125
|
+
</xs:complexType>
|
126
|
+
</xs:element>
|
127
|
+
</xs:sequence>
|
128
|
+
<xs:attribute type="xs:short" name="pages"/>
|
129
|
+
</xs:complexType>
|
130
|
+
</xs:element>
|
131
|
+
<xs:simpleType name="DateTime">
|
132
|
+
<xs:restriction base="xs:string">
|
133
|
+
<xs:pattern value="[0-9][0-9]?/[0-9][0-9]?/[0-9][0-9][0-9]?[0-9]? [0-9][0-9]?:[0-9][0-9]?:?[0-9]?[0-9]?. ?[aApP]?[mM]?"/>
|
134
|
+
</xs:restriction>
|
135
|
+
</xs:simpleType>
|
136
|
+
<xs:simpleType name="Email">
|
137
|
+
<xs:restriction base="xs:string">
|
138
|
+
</xs:restriction>
|
139
|
+
</xs:simpleType>
|
140
|
+
<xs:simpleType name="StringExactly2">
|
141
|
+
<xs:restriction base="xs:string">
|
142
|
+
<xs:minLength value="2"/>
|
143
|
+
<xs:maxLength value="2"/>
|
144
|
+
</xs:restriction>
|
145
|
+
</xs:simpleType>
|
146
|
+
<xs:simpleType name="String30">
|
147
|
+
<xs:restriction base="xs:string">
|
148
|
+
<xs:maxLength value="30"/>
|
149
|
+
</xs:restriction>
|
150
|
+
</xs:simpleType>
|
151
|
+
<xs:simpleType name="String50">
|
152
|
+
<xs:restriction base="xs:string">
|
153
|
+
<xs:maxLength value="50"/>
|
154
|
+
</xs:restriction>
|
155
|
+
</xs:simpleType>
|
156
|
+
<xs:simpleType name="String100">
|
157
|
+
<xs:restriction base="xs:string">
|
158
|
+
<xs:maxLength value="100"/>
|
159
|
+
</xs:restriction>
|
160
|
+
</xs:simpleType>
|
161
|
+
<xs:simpleType name="String200">
|
162
|
+
<xs:restriction base="xs:string">
|
163
|
+
<xs:maxLength value="200"/>
|
164
|
+
</xs:restriction>
|
165
|
+
</xs:simpleType>
|
166
|
+
<xs:simpleType name="String1000">
|
167
|
+
<xs:restriction base="xs:string">
|
168
|
+
<xs:maxLength value="1000"/>
|
169
|
+
</xs:restriction>
|
170
|
+
</xs:simpleType>
|
171
|
+
</xs:schema>
|