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.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.bundle/config +2 -0
  3. data/.circleci/config.yml +41 -0
  4. data/.gem_release.yml +5 -0
  5. data/.github/stale.yml +17 -0
  6. data/.github_changelog_generator +2 -0
  7. data/.gitignore +20 -0
  8. data/.rspec +2 -0
  9. data/.rubocop.yml +13 -0
  10. data/.rubocop_todo.yml +40 -0
  11. data/CHANGELOG.md +36 -0
  12. data/Gemfile +33 -0
  13. data/LICENSE +26 -0
  14. data/README.md +208 -0
  15. data/Rakefile +6 -0
  16. data/app/assets/javascripts/spree/backend/solidus_shipstation.js +2 -0
  17. data/app/assets/javascripts/spree/frontend/solidus_shipstation.js +2 -0
  18. data/app/assets/stylesheets/spree/backend/solidus_shipstation.css +4 -0
  19. data/app/assets/stylesheets/spree/frontend/solidus_shipstation.css +4 -0
  20. data/app/controllers/spree/shipstation_controller.rb +45 -0
  21. data/app/decorators/models/solidus_shipstation/spree/shipment_decorator.rb +33 -0
  22. data/app/helpers/solidus_shipstation/export_helper.rb +32 -0
  23. data/app/jobs/solidus_shipstation/api/schedule_shipment_syncs_job.rb +19 -0
  24. data/app/jobs/solidus_shipstation/api/sync_shipments_job.rb +41 -0
  25. data/app/queries/solidus_shipstation/shipment/between_query.rb +14 -0
  26. data/app/queries/solidus_shipstation/shipment/exportable_query.rb +24 -0
  27. data/app/queries/solidus_shipstation/shipment/pending_api_sync_query.rb +63 -0
  28. data/app/views/spree/shipstation/export.xml.builder +58 -0
  29. data/bin/console +17 -0
  30. data/bin/rails +7 -0
  31. data/bin/rails-engine +13 -0
  32. data/bin/rails-sandbox +16 -0
  33. data/bin/rake +7 -0
  34. data/bin/sandbox +86 -0
  35. data/bin/setup +8 -0
  36. data/config/locales/en.yml +5 -0
  37. data/config/routes.rb +6 -0
  38. data/db/migrate/20210220093010_add_shipstation_api_sync_fields.rb +9 -0
  39. data/lib/generators/solidus_shipstation/install/install_generator.rb +27 -0
  40. data/lib/generators/solidus_shipstation/install/templates/initializer.rb +62 -0
  41. data/lib/solidus_shipstation.rb +16 -0
  42. data/lib/solidus_shipstation/api/batch_syncer.rb +70 -0
  43. data/lib/solidus_shipstation/api/client.rb +38 -0
  44. data/lib/solidus_shipstation/api/rate_limited_error.rb +23 -0
  45. data/lib/solidus_shipstation/api/request_error.rb +33 -0
  46. data/lib/solidus_shipstation/api/request_runner.rb +50 -0
  47. data/lib/solidus_shipstation/api/shipment_serializer.rb +84 -0
  48. data/lib/solidus_shipstation/api/threshold_verifier.rb +28 -0
  49. data/lib/solidus_shipstation/configuration.rb +44 -0
  50. data/lib/solidus_shipstation/engine.rb +19 -0
  51. data/lib/solidus_shipstation/errors.rb +23 -0
  52. data/lib/solidus_shipstation/shipment_notice.rb +58 -0
  53. data/lib/solidus_shipstation/testing_support/factories.rb +4 -0
  54. data/lib/solidus_shipstation/version.rb +5 -0
  55. data/solidus_shipstation.gemspec +40 -0
  56. data/spec/controllers/spree/shipstation_controller_spec.rb +103 -0
  57. data/spec/fixtures/shipstation_xml_schema.xsd +171 -0
  58. data/spec/jobs/solidus_shipstation/api/schedule_shipment_syncs_job_spec.rb +32 -0
  59. data/spec/jobs/solidus_shipstation/api/sync_shipments_job_spec.rb +102 -0
  60. data/spec/lib/solidus_shipstation/api/batch_syncer_spec.rb +229 -0
  61. data/spec/lib/solidus_shipstation/api/client_spec.rb +120 -0
  62. data/spec/lib/solidus_shipstation/api/rate_limited_error_spec.rb +21 -0
  63. data/spec/lib/solidus_shipstation/api/request_error_spec.rb +20 -0
  64. data/spec/lib/solidus_shipstation/api/request_runner_spec.rb +64 -0
  65. data/spec/lib/solidus_shipstation/api/shipment_serializer_spec.rb +12 -0
  66. data/spec/lib/solidus_shipstation/api/threshold_verifier_spec.rb +61 -0
  67. data/spec/lib/solidus_shipstation/shipment_notice_spec.rb +111 -0
  68. data/spec/lib/solidus_shipstation_spec.rb +9 -0
  69. data/spec/models/spree/shipment_spec.rb +49 -0
  70. data/spec/queries/solidus_shipstation/shipment/between_query_spec.rb +53 -0
  71. data/spec/queries/solidus_shipstation/shipment/exportable_query_spec.rb +53 -0
  72. data/spec/queries/solidus_shipstation/shipment/pending_api_sync_query_spec.rb +37 -0
  73. data/spec/spec_helper.rb +31 -0
  74. data/spec/support/configuration_helper.rb +13 -0
  75. data/spec/support/controllers.rb +1 -0
  76. data/spec/support/webmock.rb +3 -0
  77. data/spec/support/xsd.rb +5 -0
  78. 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,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusShipstation
4
+ VERSION = '1.0.0'
5
+ 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>