dhl-bcs 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0edf9d48f49c8c00bd677e058a1b9a81728dbbf1
4
+ data.tar.gz: ae7cd220247f3b37aae0604d30ce3bb01acdbd16
5
+ SHA512:
6
+ metadata.gz: 726e19231bda1db3dc47835ac20c9d95bc68f3fac5a29140fdc456f741c76ee767a156b8dde6b705f25510f534159f1fb2bf16d04435962c372ae73b1c48c3b7
7
+ data.tar.gz: d358004816955f652956f13756b46fca01dd73e4217dd38f1362f9e04e6b825f2fd04839a6bf90e23071cd7b0c04532a7d1904832b539d8706946de0c979b289
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ /TAGS
12
+ /tags
13
+ /.tags*
14
+ /.idea
15
+ .directory
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.12.5
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at wagner@webit.de. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dhl-bcs.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Christoph Wagner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,217 @@
1
+ # Dhl::Bcs
2
+
3
+ This is a client for the DHL Business Customer Shipping (BCS) API version 2.0.
4
+ It is inspired by the [DHL intraship gem](https://github.com/waldher/dhl-intraship) which implements API version 1.0 which is expired.
5
+ The Dhl::Bcs gem uses [Savon 2](https://github.com/savonrb/savon) to communicate via SOAP with the DHL API.
6
+ The DHL BCS API is just for standard parcels. If you are looking for shipping of express parcels this gem is not for you.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'dhl-bcs'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install dhl-bcs
23
+
24
+ ## Usage
25
+
26
+ Initialize a new API client using
27
+
28
+ ```ruby
29
+ client = Dhl::Bcs.client(config, options)
30
+ ```
31
+
32
+ Config is the following hash:
33
+
34
+ ```ruby
35
+ config = {
36
+ api_user: 'The user for API BasicAuth', #mandatory
37
+ api_pwd: 'The password for API BasicAuth', #mandatory
38
+ user: 'your BCS user name', #mandatory
39
+ signature: 'Your BCS user password', #mandatory
40
+ ekp: 'Your DHL EKP (first part of your DHL Account number)', #mandatory
41
+ participation_number: 'Your DHL EKP (last two characters of your DHL Account number)' #mandatory
42
+ }
43
+ ```
44
+
45
+ Options is an optional parameter and can contain the following parameters:
46
+
47
+ ```ruby
48
+ options = {
49
+ test: true, # If test is set, all API calls go against the DHL test system (defaults to false)
50
+ log: false # If log is set, you get all logging (with request and response XML) to your standard logger. (defaults to true)
51
+ }
52
+ ```
53
+
54
+ ### Create shipments
55
+
56
+ To create a shipment at DHL you need a sender_address, a receiver_address, and informations about the parcel.
57
+
58
+ ```ruby
59
+ shipment = Dhl::Bcs.build_shipment(
60
+ shipper: {
61
+ name: 'Christoph Wagner',
62
+ company: 'webit! Gesellschaft für neue Medien mbH',
63
+ street_name: 'Schandauer Straße',
64
+ street_number: '34',
65
+ zip: '01309',
66
+ city: 'Dresden',
67
+ country_code: 'DE',
68
+ email: 'wagner@webit.de'
69
+ },
70
+ receiver: {
71
+ name: 'Jane Doe',
72
+ street_name: 'Willy-Brandt-Straße',
73
+ street_number: '1',
74
+ zip: '10557',
75
+ city: 'Berlin',
76
+ country_code: 'DE',
77
+ email: 'jane.doe@example.com'
78
+ },
79
+ weight: 3.5,
80
+ length: 10,
81
+ width: 20,
82
+ height: 30,
83
+ shipment_date: Date.new(2016, 7, 13)
84
+ )
85
+
86
+ client.create_shipment_order(shipment)
87
+ ```
88
+
89
+ You will get a result that looks like this:
90
+
91
+ ```ruby
92
+ [
93
+ {
94
+ status: { status_code: '0', status_text: 'ok', status_message: 'Der Webservice wurde ohne Fehler ausgeführt.' },
95
+ shipment_number: '22222222201019582121',
96
+ label_url: 'https://cig.dhl.de/gkvlabel/SANDBOX/dhl-vls/gw/shpmntws/printShipment?token=JD7HKktuvugIFEkhSvCfbEz4J8Ah0dkcVuw4PzBGRyRnW%2FwEPAwfytLtb31e7gMDsSX32%2BEB5exp8nNPs%2FhJSQ%3D%3D',
97
+ }
98
+ ]
99
+ ```
100
+
101
+ This is an Array of Hashes, cause it is possible to send up to 30 parcels at the same time, with the API.
102
+ Say you want to send 3 parcels to the same address you can also do something like that:
103
+
104
+ ```ruby
105
+ shipper = Dhl::Bcs.build_shipper(
106
+ name: 'Christoph Wagner',
107
+ company: 'webit! Gesellschaft für neue Medien mbH',
108
+ street_name: 'Schandauer Straße',
109
+ street_number: '34',
110
+ zip: '01309',
111
+ city: 'Dresden',
112
+ country_code: 'DE',
113
+ email: 'wagner@webit.de'
114
+ )
115
+
116
+ receiver = Dhl::Bcs.build_receiver(
117
+ name: 'Jane Doe',
118
+ street_name: 'Willy-Brandt-Straße',
119
+ street_number: '1',
120
+ zip: '10557',
121
+ city: 'Berlin',
122
+ country_code: 'DE',
123
+ email: 'jane.doe@example.com'
124
+ )
125
+
126
+ shipment1 = Dhl::Bcs.build_shipment(shipper: shipper, receiver: receiver, weight: 3)
127
+ shipment2 = Dhl::Bcs.build_shipment(shipper: shipper, receiver: receiver, weight: 3.5)
128
+ shipment3 = Dhl::Bcs.build_shipment(shipper: shipper, receiver: receiver, weight: 4)
129
+
130
+ client.create_shipment_order(shipment1, shipment2, shipment3)
131
+ ```
132
+
133
+ ### Validate shipments
134
+
135
+ ```ruby
136
+ client.validate_shipment(shipment1, shipment2, shipment3)
137
+ ```
138
+
139
+ ### Update shipment
140
+
141
+ You can update a shipment at DHL. It is the same like deleting it and creating a new one just with one request.
142
+ So you will get a new shipping number for it and so on.
143
+ To update a shipment you need the shipment number of the old one and give a complete new shipment that is created instead.
144
+ This works for one shipment at a time.
145
+ ```ruby
146
+ client.update_shipment_order('22222222901010000944', shipment)
147
+ ```
148
+
149
+ ### Other methods
150
+
151
+ The methods `delete_shipment_order`, `get_label`, `get_export_doc` and `do_manifest` works technically the same.
152
+ They took one or many (up to 30) shipment numbers and do something with these shipments at DHL.
153
+
154
+ To delete a shipment you can use:
155
+ ```ruby
156
+ client.delete_shipment_order('22222222901010000944')
157
+ ```
158
+
159
+ As result you will get one Hash like:
160
+ ```ruby
161
+ {
162
+ '22222222901010000944' => {
163
+ status: { status_code: '0', status_text: 'ok', status_message: nil }
164
+ }
165
+ }
166
+ ```
167
+
168
+ ### Services
169
+
170
+ There is a basic support to add Services to a shipment in this gem.
171
+
172
+ ```ruby
173
+ shipment.services << Dhl::Bcs.build_service(name: 'IndividualSenderRequirement', attributes: { active: '1', details: 'Test' })
174
+ ```
175
+
176
+ A service has a name and attributes.
177
+ Sometimes a service has children, for example the 'IdentCheck'-Service.
178
+
179
+ ```ruby
180
+ shipment.services << Dhl::Bcs.build_service(name: 'IdentCheck', attributes: { active: '1' }, children: { 'Ident' => { surname: 'Doe', given_name: 'Jon Doe', date_of_birth: '1980-12-24', minimum_age: '18' } })
181
+ ```
182
+
183
+ Check out the DHL developer documentation to configure the services you need.
184
+
185
+ ### Get API version
186
+
187
+ ```ruby
188
+ client.get_version
189
+ ```
190
+ You don't need a shipment for that.
191
+
192
+ ### Logging
193
+
194
+ If you need the last made request and its response from the client you can use:
195
+ ```ruby
196
+ client.last_log
197
+ ```
198
+ This works even if you used the option `log: false` at the client. This option controlls just the output in the log file or console.
199
+
200
+ ### Everything else
201
+ Have a deeper look at the code of this gem and find out how things work.
202
+ You can help to implement missing things or extend this documentation.
203
+
204
+ ## Development
205
+
206
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
207
+
208
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
209
+
210
+ ## Contributing
211
+
212
+ Bug reports and pull requests are welcome on GitHub at https://github.com/webit-de/dhl-bcs. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
213
+
214
+
215
+ ## License
216
+
217
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "dhl/client"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dhl/bcs/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dhl-bcs"
8
+ spec.version = Dhl::Bcs::VERSION
9
+ spec.authors = ["Christoph Wagner"]
10
+ spec.email = ["wagner@webit.de"]
11
+
12
+ spec.summary = %q{Client for DHL Business-Customer-Shipping SOAP API 2.0}
13
+ spec.description = %q{This is inspired by the dhl-intraship gem that is a little bit outdated and doesn't support the new DHL API. If you need DHL Express Services this is not for you.}
14
+ spec.homepage = "https://github.com/webit-de/dhl-bcs"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "bin"
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "savon", "~> 2.11"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.12"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "minitest", "~> 5.0"
27
+ spec.add_development_dependency "webmock", "~> 2.1"
28
+ spec.add_development_dependency "pry-byebug", "~> 3.4"
29
+ end
@@ -0,0 +1,41 @@
1
+ require 'dhl/bcs/version'
2
+ require 'dhl/bcs/errors'
3
+ require 'dhl/bcs/v2/client'
4
+ require 'dhl/bcs/v2/buildable'
5
+ require 'dhl/bcs/v2/shipment'
6
+ require 'dhl/bcs/v2/shipper'
7
+ require 'dhl/bcs/v2/receiver'
8
+ require 'dhl/bcs/v2/communication'
9
+ require 'dhl/bcs/v2/location'
10
+ require 'dhl/bcs/v2/address'
11
+ require 'dhl/bcs/v2/packstation'
12
+ require 'dhl/bcs/v2/parcel_shop'
13
+ require 'dhl/bcs/v2/postfiliale'
14
+ require 'dhl/bcs/v2/bank_data'
15
+ require 'dhl/bcs/v2/service'
16
+
17
+ module Dhl
18
+ module Bcs
19
+
20
+ def self.client(config, options = {})
21
+ V2::Client.new(config, options)
22
+ end
23
+
24
+ def self.build_shipment(*args)
25
+ V2::Shipment.build(*args)
26
+ end
27
+
28
+ def self.build_shipper(*args)
29
+ V2::Shipper.build(*args)
30
+ end
31
+
32
+ def self.build_receiver(*args)
33
+ V2::Receiver.build(*args)
34
+ end
35
+
36
+ def self.build_service(*args)
37
+ V2::Service.new(*args)
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,8 @@
1
+ module Dhl
2
+ module Bcs
3
+ class Error < StandardError; end
4
+
5
+ class RequestError < StandardError; end
6
+
7
+ end
8
+ end
@@ -0,0 +1,20 @@
1
+ module Dhl::Bcs::V2
2
+ class Address < Location
3
+
4
+ ADD_PROPS = %i(company company_addition street_name street_number address_addition dispatching_information).freeze
5
+ PROPERTIES = Location::PROPERTIES + ADD_PROPS
6
+ attr_accessor(*ADD_PROPS)
7
+
8
+ def to_soap_hash
9
+ h = {}
10
+ h['cis:name2'] = company if company
11
+ h['cis:name3'] = company_addition if company_addition
12
+ h['cis:streetName'] = street_name
13
+ h['cis:streetNumber'] = street_number
14
+ h['cis:addressAddition'] = address_addition if address_addition
15
+ h['cis:dispatchingInformation'] = dispatching_information if dispatching_information
16
+ h.merge(super)
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ module Dhl::Bcs::V2
2
+ class BankData
3
+
4
+ include Buildable
5
+
6
+ PROPERTIES = %i(account_owner bank_name iban note1 note2 bic accountreference).freeze
7
+ attr_accessor(*PROPERTIES)
8
+
9
+ def to_soap_hash
10
+ {
11
+ 'cis:accountOwner' => account_owner,
12
+ 'cis:bank_name' => bank_name,
13
+ 'cis:iban' => iban
14
+ }.tap do |h|
15
+ h['cis:note1'] = note1 if note1
16
+ h['cis:note2'] = note2 if note2
17
+ h['cis:bic'] = bic if bic
18
+ h['cis:accountreference'] = accountreference if accountreference
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+ module Dhl::Bcs::V2
2
+ module Buildable
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def build(attributes)
10
+ attrib = attributes[underscore(name.split('::').last).to_sym]
11
+ if attrib.is_a?(self)
12
+ attrib
13
+ elsif attrib.nil?
14
+ klass_attributes = {}
15
+ self::PROPERTIES.each do |prop|
16
+ value = attributes.delete(prop)
17
+ klass_attributes[prop] = value if value
18
+ end
19
+ new(klass_attributes)
20
+ elsif attrib.is_a?(Hash)
21
+ new(attrib)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ # borrowed from rails without acronyms
28
+ def underscore(camel_cased_word)
29
+ return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/
30
+ word = camel_cased_word.to_s.gsub('::'.freeze, '/'.freeze)
31
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
32
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
33
+ word.tr!('-'.freeze, '_'.freeze)
34
+ word.downcase!
35
+ word
36
+ end
37
+
38
+ end
39
+
40
+ def initialize(**attributes)
41
+ attributes.each do |property, value|
42
+ send("#{property}=", value) if self.class::PROPERTIES.include?(property)
43
+ end
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,139 @@
1
+ require 'savon'
2
+ require 'stringio'
3
+ require 'logger'
4
+
5
+ module Dhl::Bcs::V2
6
+ class Client
7
+
8
+ WSDL = 'https://cig.dhl.de/cig-wsdls/com/dpdhl/wsdl/geschaeftskundenversand-api/2.0/geschaeftskundenversand-api-2.0.wsdl'
9
+
10
+ def initialize(config, log: true, test: false, **options)
11
+ raise "User must be specified" if config[:user].nil?
12
+ raise "Signature (password) must be specified" if config[:signature].nil?
13
+ raise "EKP (first part of the DHL account number) must be specified" if config[:ekp].nil?
14
+ raise "Participation Number (last two characters of account number) must be specified" if config[:participation_number].nil?
15
+ raise "Api User must be specified" if config[:api_user].nil?
16
+ raise "Api Password must be specified" if config[:api_pwd].nil?
17
+
18
+ @ekp = config[:ekp]
19
+ @participation_number = config[:participation_number]
20
+
21
+ @logIO = StringIO.new
22
+ @logger = Logger.new($stdout) if log
23
+
24
+ @client = Savon.client({
25
+ endpoint: (test ? 'https://cig.dhl.de/services/sandbox/soap' : 'https://cig.dhl.de/services/production/soap'),
26
+ wsdl: WSDL,
27
+ basic_auth: [config[:api_user], config[:api_pwd]],
28
+ logger: Logger.new(@logIO),
29
+ log: true,
30
+ soap_header: {
31
+ 'cis:Authentification' => {
32
+ 'cis:user' => config[:user],
33
+ 'cis:signature' => config[:signature],
34
+ 'cis:type' => 0
35
+ }
36
+ },
37
+ namespaces: { 'xmlns:cis' => 'http://dhl.de/webservice/cisbase' }
38
+ })
39
+ end
40
+
41
+ def get_version(major: 2, minor: 0, build: nil)
42
+ request(:get_version,
43
+ 'bcs:Version' => {
44
+ 'majorRelease' => major,
45
+ 'minorRelease' => minor
46
+ }.tap { |h| h['build'] = build if build }
47
+ ) do |response|
48
+ response.body[:get_version_response][:version]
49
+ end
50
+ end
51
+
52
+ def validate_shipment(*shipments, **options)
53
+ request(:validate_shipment, build_shipment_orders(shipments, options)) do |response|
54
+ [response.body[:validate_shipment_response][:validation_state]].flatten.map do |validation_state|
55
+ validation_state[:status]
56
+ end
57
+ end
58
+ end
59
+
60
+ def create_shipment_order(*shipments, **options)
61
+ request(:create_shipment_order, build_shipment_orders(shipments, options)) do |response|
62
+ [response.body[:create_shipment_order_response][:creation_state]].flatten.map do |creation_state|
63
+ creation_state[:label_data]
64
+ end
65
+ end
66
+ end
67
+
68
+ def update_shipment_order(shipment_number, shipment, **options)
69
+ request(:update_shipment_order, { 'cis:shipmentNumber' => shipment_number }.merge(build_shipment_orders([shipment], options))) do |response|
70
+ clean_response_data(response.body[:update_shipment_order_response][:label_data])
71
+ end
72
+ end
73
+
74
+ {
75
+ delete_shipment_order: :deletion_state,
76
+ get_label: :label_data,
77
+ get_export_doc: :export_doc_data,
78
+ do_manifest: :manifest_state
79
+ }.each do |api_method, response_key|
80
+ define_method api_method do |*shipment_numbers|
81
+ raise Dhl::Bcs::DataError, 'No more than 30 shipment_numbers allowed per request!' if shipment_numbers.size > 30
82
+ request(api_method, 'cis:shipmentNumber' => shipment_numbers) do |response|
83
+ h = {}
84
+ [response.body[:"#{api_method}_response"][response_key]].flatten.each do |data|
85
+ h[data.delete(:shipment_number)] = clean_response_data(data)
86
+ end
87
+ h
88
+ end
89
+ end
90
+ end
91
+
92
+ # returns base64 encoded PDF Dokument
93
+ def get_manifest(date)
94
+ request(:get_manifest, 'manifestDate' => date) do |response|
95
+ response.body[:get_manifest_response][:manifest_data]
96
+ end
97
+ end
98
+
99
+ def last_log
100
+ @logIO.string
101
+ end
102
+
103
+ protected
104
+
105
+ def build_shipment_orders(shipments, label_response_type: 'URL')
106
+ raise Dhl::Bcs::DataError, 'No more than 30 shipments allowed per request!' if shipments.size > 30
107
+ {
108
+ 'ShipmentOrder' =>
109
+ shipments.map.with_index(1) do |shipment, index|
110
+ {
111
+ 'sequenceNumber' => format('%02i', index.to_s),
112
+ 'Shipment' => shipment.to_soap_hash(@ekp, @participation_number),
113
+ 'LabelResponseType' => label_response_type.to_s.upcase
114
+ }
115
+ end
116
+ }
117
+ end
118
+
119
+ def request(action, message = {})
120
+ @logIO.string = ''
121
+ response = @client.call(action, message: {
122
+ 'bcs:Version' => {
123
+ 'majorRelease' => 2,
124
+ 'minorRelease' => 0
125
+ }
126
+ }.merge(message))
127
+ @logger << @logIO.string if @logger
128
+ yield response
129
+ rescue
130
+ raise Dhl::Bcs::RequestError, @logIO.string
131
+ end
132
+
133
+ def clean_response_data(data)
134
+ data.delete(:@xmlns)
135
+ data
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,18 @@
1
+ module Dhl::Bcs::V2
2
+ class Communication
3
+
4
+ include Buildable
5
+
6
+ PROPERTIES = %i(phone email contact_person).freeze
7
+ attr_accessor(*PROPERTIES)
8
+
9
+ def to_soap_hash
10
+ {}.tap do |h|
11
+ h['cis:phone'] = phone if phone
12
+ h['cis:email'] = email if email
13
+ h['cis:contactPerson'] = contact_person if contact_person
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ module Dhl::Bcs::V2
2
+ # a location is one of Address, Packstation, Postfiliale or ParcelShop
3
+ # they have some properties in common
4
+ class Location
5
+
6
+ include Buildable
7
+
8
+ PROPERTIES = %i(zip city country country_code state).freeze
9
+ attr_accessor(*PROPERTIES)
10
+
11
+ def to_soap_hash
12
+ {
13
+ 'cis:zip' => zip,
14
+ 'cis:city' => city,
15
+ 'cis:Origin' => {}.tap { |h|
16
+ h['cis:country'] = country if country
17
+ h['cis:countryISOCode'] = country_code if country_code
18
+ h['cis:state'] = state if state
19
+ }
20
+ }
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ module Dhl::Bcs::V2
2
+ class Packstation < Location
3
+
4
+ ADD_PROPS = %i(post_number packstation_number).freeze
5
+ PROPERTIES = Location::PROPERTIES + ADD_PROPS
6
+ attr_accessor(*ADD_PROPS)
7
+
8
+ def to_soap_hash
9
+ h = {}
10
+ h['postNumber'] = post_number if post_number
11
+ h['packstationNumber'] = packstation_number
12
+ h.merge(super)
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module Dhl::Bcs::V2
2
+ class ParcelShop < Location
3
+
4
+ ADD_PROPS = %i(parcel_shop_number street_name street_number).freeze
5
+ PROPERTIES = Location::PROPERTIES + ADD_PROPS
6
+ attr_accessor(*ADD_PROPS)
7
+
8
+ def to_soap_hash
9
+ h = {}
10
+ h['parcelShopNumber'] = parcel_shop_number
11
+ h['cis:streetName'] = street_name if street_name
12
+ h['cis:streetNumber'] = street_number if street_number
13
+ h.merge(super)
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module Dhl::Bcs::V2
2
+ class Postfiliale < Location
3
+
4
+ ADD_PROPS = %i(postfilial_number post_number).freeze
5
+ PROPERTIES = Location::PROPERTIES + ADD_PROPS
6
+ attr_accessor(*ADD_PROPS)
7
+
8
+ def to_soap_hash
9
+ h = {}
10
+ h['postfilialNumber'] = postfilial_number
11
+ h['postNumber'] = post_number
12
+ h.merge(super)
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ module Dhl::Bcs::V2
2
+ class Receiver
3
+
4
+ # a location is one of Address, Packstation, Postfiliale or ParcelShop
5
+ PROPERTIES = %i(name communication location).freeze
6
+
7
+ attr_accessor(*PROPERTIES)
8
+
9
+ def self.build(name: nil, **attributes)
10
+ communication = Communication.build(attributes)
11
+ location =
12
+ if attributes.key?(:packstation_number)
13
+ Packstation
14
+ elsif attributes.key?(:postfilial_number)
15
+ Postfiliale
16
+ elsif attributes.key?(:parcel_shop_number)
17
+ ParcelShop
18
+ else
19
+ Address
20
+ end.build(attributes)
21
+ new(attributes.merge(name: name, communication: communication, location: location))
22
+ end
23
+
24
+ def initialize(**attributes)
25
+ attributes.each do |property, value|
26
+ send("#{property}=", value) if PROPERTIES.include?(property)
27
+ end
28
+ end
29
+
30
+ def to_soap_hash
31
+ {
32
+ 'cis:name1' => name,
33
+ 'Communication' => communication.to_soap_hash,
34
+ location.class.name.split('::').last => location.to_soap_hash
35
+ }
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,16 @@
1
+ module Dhl::Bcs::V2
2
+ class Service
3
+
4
+ include Buildable
5
+
6
+ PROPERTIES = %i(name attributes children).freeze
7
+ attr_accessor(*PROPERTIES)
8
+
9
+ def to_soap_hash
10
+ # make a self closing tag by appending "/" if no children
11
+ tag_name = children ? name : "#{name}/"
12
+ { tag_name => children, attributes!: { tag_name => attributes } }
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,83 @@
1
+ module Dhl::Bcs::V2
2
+ class Shipment
3
+
4
+ attr_accessor :shipper, :receiver, :bank_data, :shipment_date, :product,
5
+ :customer_reference, :services, :weight, :length, :height, :width,
6
+ :notification_email
7
+
8
+ PRODUCT_PROCEDURE_NUMBERS = {
9
+ 'V01PAK' => '01', # Paket National
10
+ 'V53WPAK' => '53', # Weltpaket
11
+ 'V54EPAK' => '54', # Europaket
12
+ 'V06TG' => '01', # Kurier Taggleich
13
+ 'V06WZ' => '01', # Kurier Wunschzeit
14
+ 'V86PARCEL' => '86', # DHL Paket Austria
15
+ 'V87PARCEL' => '87', # DHL PAKET Connect
16
+ 'V82PARCEL' => '82' # DHL PAKET International
17
+ }.freeze
18
+ PRODUCTS = PRODUCT_PROCEDURE_NUMBERS.keys.freeze
19
+
20
+ # build a shipment from hash data
21
+ def self.build(shipper:, receiver:, bank_data: nil, **shipment_attributes)
22
+ shipper = Shipper.build(shipper) if shipper.is_a?(Hash)
23
+ receiver = Receiver.build(receiver) if receiver.is_a?(Hash)
24
+ bank_data = BankData.build(bank_data) if bank_data.is_a?(Hash)
25
+ new({ shipper: shipper, receiver: receiver, bank_data: bank_data }.merge(shipment_attributes))
26
+ end
27
+
28
+ def initialize(attributes = {})
29
+ assign_attributes(
30
+ {
31
+ product: 'V01PAK',
32
+ shipment_date: Date.today,
33
+ services: []
34
+ }.merge(attributes)
35
+ )
36
+ end
37
+
38
+ def assign_attributes(attributes)
39
+ attributes.each do |key, value|
40
+ setter = :"#{key}="
41
+ send(setter, value) if respond_to?(setter)
42
+ end
43
+ end
44
+
45
+ def product=(product)
46
+ raise Error, "No valid product code #{product}. Please use one of these: #{PRODUCTS.join(', ')}" unless PRODUCTS.include?(product)
47
+ @product = product
48
+ end
49
+
50
+ def to_soap_hash(ekp, participation_number)
51
+ raise Error, 'Packing weight in kilo must be set!' unless weight
52
+ raise Error, 'Sender address must be set!' unless shipper
53
+ raise Error, 'Receiver address must be set!' unless receiver
54
+ raise Error, 'Product must be set!' unless product
55
+
56
+ account_number = "#{ekp}#{PRODUCT_PROCEDURE_NUMBERS[product]}#{participation_number}"
57
+ raise Error, 'Need a 14 character long account number. Check EKP and participation_number' if account_number.size != 14
58
+ {
59
+ 'ShipmentDetails' => {}.tap { |h|
60
+ h['product'] = product
61
+ h['shipmentDate'] = shipment_date.strftime("%Y-%m-%d")
62
+ h['cis:accountNumber'] = account_number
63
+ h['customerReference'] = customer_reference if !customer_reference.blank?
64
+
65
+ # just one ShipmentItem possible
66
+ h['ShipmentItem'] = { 'weightInKG' => weight }.tap { |si|
67
+ si['lengthInCM'] = length if length
68
+ si['widthInCM'] = width if width
69
+ si['heightInCM'] = height if height
70
+ }
71
+ h['Service'] = services.map(&:to_soap_hash) unless services.empty?
72
+ h['Notification'] = { 'recipientEmailAddress' => notification_email } if notification_email
73
+ h['BankData'] = bank_data.to_soap_hash if bank_data
74
+ },
75
+ # Shipper information
76
+ 'Shipper' => shipper.to_soap_hash,
77
+ # Receiver information
78
+ 'Receiver' => receiver.to_soap_hash
79
+ }
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,32 @@
1
+ module Dhl::Bcs::V2
2
+ class Shipper
3
+
4
+ PROPERTIES = %i(name company company_addition address communication).freeze
5
+
6
+ attr_accessor(*PROPERTIES)
7
+
8
+ def self.build(company: nil, **attributes)
9
+ address = Address.build(attributes)
10
+ communication = Communication.build(attributes)
11
+ new(attributes.merge(address: address, communication: communication, company: company))
12
+ end
13
+
14
+ def initialize(**attributes)
15
+ attributes.each do |property, value|
16
+ send("#{property}=", value) if PROPERTIES.include?(property)
17
+ end
18
+ end
19
+
20
+ def to_soap_hash
21
+ {
22
+ 'Name' => { 'cis:name1' => name }.tap { |h|
23
+ h['cis:name2'] = company if company
24
+ h['cis:name3'] = company_addition if company_addition
25
+ },
26
+ 'Address' => address.to_soap_hash,
27
+ 'Communication' => communication.to_soap_hash
28
+ }
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ module Dhl
2
+ module Bcs
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dhl-bcs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Christoph Wagner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: savon
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.11'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.11'
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.12'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.12'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.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: '2.1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.4'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.4'
97
+ description: This is inspired by the dhl-intraship gem that is a little bit outdated
98
+ and doesn't support the new DHL API. If you need DHL Express Services this is not
99
+ for you.
100
+ email:
101
+ - wagner@webit.de
102
+ executables:
103
+ - console
104
+ - setup
105
+ extensions: []
106
+ extra_rdoc_files: []
107
+ files:
108
+ - ".gitignore"
109
+ - ".travis.yml"
110
+ - CODE_OF_CONDUCT.md
111
+ - Gemfile
112
+ - LICENSE.txt
113
+ - README.md
114
+ - Rakefile
115
+ - bin/console
116
+ - bin/setup
117
+ - dhl-bcs.gemspec
118
+ - lib/dhl/bcs.rb
119
+ - lib/dhl/bcs/errors.rb
120
+ - lib/dhl/bcs/v2/address.rb
121
+ - lib/dhl/bcs/v2/bank_data.rb
122
+ - lib/dhl/bcs/v2/buildable.rb
123
+ - lib/dhl/bcs/v2/client.rb
124
+ - lib/dhl/bcs/v2/communication.rb
125
+ - lib/dhl/bcs/v2/location.rb
126
+ - lib/dhl/bcs/v2/packstation.rb
127
+ - lib/dhl/bcs/v2/parcel_shop.rb
128
+ - lib/dhl/bcs/v2/postfiliale.rb
129
+ - lib/dhl/bcs/v2/receiver.rb
130
+ - lib/dhl/bcs/v2/service.rb
131
+ - lib/dhl/bcs/v2/shipment.rb
132
+ - lib/dhl/bcs/v2/shipper.rb
133
+ - lib/dhl/bcs/version.rb
134
+ homepage: https://github.com/webit-de/dhl-bcs
135
+ licenses:
136
+ - MIT
137
+ metadata: {}
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 2.5.1
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: Client for DHL Business-Customer-Shipping SOAP API 2.0
158
+ test_files: []