deliveries 0.2.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8600bc0cc56888deaa08129100cc3d3539c0151cdb6580eae01a2c8e837cbad6
4
- data.tar.gz: a8a2433d3d0fab403364049d166bc97b7fa0be01991bf246c6e5b3ce4923439b
3
+ metadata.gz: bb252635f57203e34b0d593df80f4fc0aa033ebfb70675f646cf192fc946a647
4
+ data.tar.gz: 6e232ea5c543aaeaf298e618f2f97b7192b04b6de72f45de3204b1f2f4ab1d3f
5
5
  SHA512:
6
- metadata.gz: 25de14c9c5b522e9bf60f9d439e7b9672fcf5fe484b34ea2817970e1c895eab2ad80b6b5885d47b5ec21f89d19048d6c84e9d52741b8f452031831136d4ab54a
7
- data.tar.gz: 154fff2aa85ea1071aed9aaf831c867e010566891b161e5c31b1612c436faa81ad694832d861e630bfbefc810e6f79e8ef949e858eade78bab22d2f9567d3901
6
+ metadata.gz: 75e7cd7e1b1380f0b889d91515e7f356f5847b24106e98487c4b2cc1b1a907da87dfee8c184f29d7c6ffb6c9d89398b06d3ab30a67ee8535330a2263ff989a58
7
+ data.tar.gz: f09ac1de09045873b6a8e0f91d31e011f7b7a232c4924d6b391f9b87b99d40dc62d1d0923b00c8fd96138df8dccda169f80669f259664d974dca47a8952dbb77
data/CHANGELOG.md CHANGED
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.3.0] - 2021-11-26
10
+ ### Added
11
+ - Envialia courier integration
12
+
9
13
  ## [0.2.1] - 2021-11-04
10
14
  ### Fixed
11
15
  - Field `clienteRecogida` must have the same value as `codDest` in Correos Express pickup.
@@ -21,8 +25,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
21
25
  ## [0.1.0] - 2021-09-27
22
26
  🎉 First release!
23
27
 
24
- [Unreleased]: https://github.com/ecommerce-ventures/deliveries/compare/v0.2.1...HEAD
28
+ [Unreleased]: https://github.com/ecommerce-ventures/deliveries/compare/v0.3.0...HEAD
29
+ [0.3.0]: https://github.com/ecommerce-ventures/deliveries/compare/v0.2.1...v0.3.0
25
30
  [0.2.1]: https://github.com/ecommerce-ventures/deliveries/compare/v0.2.0...v0.2.1
26
31
  [0.2.0]: https://github.com/ecommerce-ventures/deliveries/compare/v0.1.1...v0.2.0
27
32
  [0.1.1]: https://github.com/ecommerce-ventures/deliveries/compare/v0.1.0...v0.1.1
28
- [0.1.0]: https://github.com/ecommerce-ventures/deliveries/releases/tag/v0.1.0
33
+ [0.1.0]: https://github.com/ecommerce-ventures/deliveries/releases/tag/v0.1.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- deliveries (0.2.1)
4
+ deliveries (0.3.0)
5
5
  activesupport (>= 5.2.0)
6
6
  hexapdf
7
7
  httparty
@@ -35,7 +35,7 @@ GEM
35
35
  gyoku (1.3.1)
36
36
  builder (>= 2.1.2)
37
37
  hashdiff (1.0.1)
38
- hexapdf (0.17.3)
38
+ hexapdf (0.19.0)
39
39
  cmdparse (~> 3.0, >= 3.0.3)
40
40
  geom2d (~> 0.3)
41
41
  httparty (0.20.0)
@@ -46,9 +46,9 @@ GEM
46
46
  socksify
47
47
  i18n (1.8.11)
48
48
  concurrent-ruby (~> 1.0)
49
- mime-types (3.3.1)
49
+ mime-types (3.4.1)
50
50
  mime-types-data (~> 3.2015)
51
- mime-types-data (3.2021.0901)
51
+ mime-types-data (3.2021.1115)
52
52
  mini_magick (4.11.0)
53
53
  minitest (5.14.4)
54
54
  multi_xml (0.6.0)
data/README.md CHANGED
@@ -6,7 +6,7 @@ Deliveries is a gem that gives you the ability to integrate multiple shipping se
6
6
 
7
7
  Add the following line to your Gemfile
8
8
 
9
- ```bash
9
+ ```ruby
10
10
  gem 'deliveries'
11
11
  ```
12
12
  Then run:
@@ -20,7 +20,7 @@ bundle install
20
20
  Each courier requires a different configuration, below we will leave some examples
21
21
 
22
22
  #### 1. Mondial Relay
23
- ```bash
23
+ ```ruby
24
24
  Deliveries.courier(:mondial_relay).configure do |config|
25
25
  config.mondial_relay_merchant = '...'
26
26
  config.mondial_relay_key = '...'
@@ -28,7 +28,7 @@ end
28
28
  ```
29
29
 
30
30
  #### 2. Mondial Relay Dual
31
- ```bash
31
+ ```ruby
32
32
  Deliveries.courier(:mondial_relay_dual).configure do |config|
33
33
  config.dual_carrier_login = '...'
34
34
  config.dual_carrier_password = '...'
@@ -48,7 +48,7 @@ end
48
48
  ```
49
49
 
50
50
  #### 3. Correos Express
51
- ```bash
51
+ ```ruby
52
52
  Deliveries.courier(:correos_express).configure do |config|
53
53
  config.username = '...'
54
54
  config.password = '...'
@@ -67,7 +67,7 @@ end
67
67
  ```
68
68
 
69
69
  #### 4. Spring
70
- ```bash
70
+ ```ruby
71
71
  Deliveries.courier(:spring).configure do |config|
72
72
  config.api_key = '...'
73
73
  config.countries = {
@@ -89,7 +89,7 @@ end
89
89
  ```
90
90
 
91
91
  #### 5. UPS
92
- ```bash
92
+ ```ruby
93
93
  Deliveries.courier(:ups).configure do |config|
94
94
  config.license_number = '...'
95
95
  config.username = '...'
@@ -103,25 +103,36 @@ Deliveries.courier(:ups).configure do |config|
103
103
  end
104
104
  ```
105
105
 
106
+ #### Time Zone
107
+
108
+ This library uses Active Support's Time extension, so a time zone must be set before using it:
109
+
110
+ ```ruby
111
+ require 'active_support/time'
112
+
113
+ # Configure time zone
114
+ Time.zone = 'Madrid'
115
+ ```
116
+
106
117
  ## Usage
107
118
 
108
119
  #### Get collection point by country and postcode
109
120
 
110
- ```bash
121
+ ```ruby
111
122
  # Example Using Ups
112
123
 
113
124
  Deliveries.courier(:ups).get_collection_points(postcode: '...', country: 'it')
114
125
  ```
115
126
  #### Get collection point info
116
127
 
117
- ```bash
128
+ ```ruby
118
129
  # Example Using Mondial Relay
119
130
 
120
131
  Deliveries.courier(:mondial_relay).get_collection_point(global_point_id: 'mondial_relay~fr~00000~XXXXXX')
121
132
  ```
122
133
 
123
134
  #### Create a Shipment
124
- ```bash
135
+ ```ruby
125
136
  # Example Using Correos Express
126
137
 
127
138
  sender = Deliveries::Address.new(
@@ -158,7 +169,7 @@ response = Deliveries.courier(:correos_express).create_shipment(
158
169
  ```
159
170
 
160
171
  #### Create a Pickup
161
- ```bash
172
+ ```ruby
162
173
  # Example Using Spring
163
174
 
164
175
  sender = Deliveries::Address.new(
@@ -193,7 +204,7 @@ response = Deliveries.courier(:spring).create_pickup(
193
204
  ```
194
205
 
195
206
  #### Download a Label
196
- ```bash
207
+ ```ruby
197
208
  # Example Using Spring
198
209
 
199
210
  label = Deliveries.courier(:spring).get_label(tracking_code: '...')
@@ -0,0 +1,50 @@
1
+ require 'httparty'
2
+
3
+ module Deliveries
4
+ module Couriers
5
+ module Envialia
6
+ module Authentication
7
+ include HTTParty
8
+
9
+ def session_id
10
+ response = HTTParty.post(
11
+ login_endpoint,
12
+ body: login_body,
13
+ headers: login_headers,
14
+ debug_output: Deliveries.debug ? Deliveries.logger : nil
15
+ )
16
+
17
+ raise Deliveries::ClientError unless response.success?
18
+
19
+ response.dig('Envelope', 'Header', 'ROClientIDHeader', 'ID')
20
+ end
21
+
22
+ def login_body
23
+ <<~XML
24
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
25
+ <soapenv:Body>
26
+ <tns:LoginWSService___LoginCli2 xmlns:tns="http://tempuri.org/">
27
+ <strCodAge>#{Deliveries.courier(:envialia).config(:agency_code)}</strCodAge>
28
+ <strCliente>#{Deliveries.courier(:envialia).config(:username)}</strCliente>
29
+ <strPass>#{Deliveries.courier(:envialia).config(:password)}</strPass>
30
+ </tns:LoginWSService___LoginCli2>
31
+ </soapenv:Body>
32
+ </soapenv:Envelope>
33
+ XML
34
+ end
35
+
36
+ def login_headers
37
+ { 'Content-Type' => 'application/json;charset=UTF-8', 'Accept' => 'application/json' }
38
+ end
39
+
40
+ def login_endpoint
41
+ if Envialia.live?
42
+ Envialia::ENVIALIA_LOGIN_ENDPOINT_LIVE
43
+ else
44
+ Envialia::ENVIALIA_LOGIN_ENDPOINT_TEST
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,88 @@
1
+ require 'httparty'
2
+
3
+ module Deliveries
4
+ module Couriers
5
+ module Envialia
6
+ module Labels
7
+ class Generate
8
+ include HTTParty
9
+ include Authentication
10
+
11
+ attr_accessor :tracking_codes
12
+
13
+ def initialize(tracking_codes:)
14
+ self.tracking_codes = tracking_codes.respond_to?(:each) ? tracking_codes : [tracking_codes]
15
+ end
16
+
17
+ def execute
18
+ decoded_labels = []
19
+
20
+ tracking_codes.each do |tracking_code|
21
+ response = self.class.post(
22
+ api_endpoint,
23
+ body: body(tracking_code),
24
+ headers: headers,
25
+ debug_output: Deliveries.debug ? Deliveries.logger : nil
26
+ )
27
+
28
+ raise Deliveries::ClientError unless response.success?
29
+
30
+ labels = response.dig('Envelope', 'Body', 'WebServService___ConsEtiquetaEnvio6Response', 'strEtiquetas')
31
+
32
+ if labels.blank?
33
+ raise Deliveries::APIError.new(
34
+ 'No hay etiqutas disponibles',
35
+ 404
36
+ )
37
+ else
38
+ decoded_labels << Base64.decode64(labels).force_encoding('binary')
39
+ end
40
+ end
41
+
42
+ decoded_labels
43
+ end
44
+
45
+ private
46
+
47
+ def body(tracking_code)
48
+ <<~XML
49
+ <?xml version="1.0" encoding="utf-8"?>
50
+ <soap:Envelope
51
+ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
52
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
53
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
54
+ <soap:Header>
55
+ <ROClientIDHeader xmlns="http://tempuri.org/">
56
+ <ID>#{session_id}</ID>
57
+ </ROClientIDHeader>
58
+ </soap:Header>
59
+ <soap:Body>
60
+ <WebServService___ConsEtiquetaEnvio6>
61
+ <strCodAgeOri>#{Deliveries.courier(:envialia).config(:agency_code)}</strCodAgeOri>
62
+ <strAlbaran>#{tracking_code}</strAlbaran>
63
+ <strBulto></strBulto>
64
+ <boPaginaA4>false</boPaginaA4>
65
+ <intNumEtiqImpresasA4>0</intNumEtiqImpresasA4>
66
+ <strFormato>PDF</strFormato>
67
+ </WebServService___ConsEtiquetaEnvio6>
68
+ </soap:Body>
69
+ </soap:Envelope>
70
+ XML
71
+ end
72
+
73
+ def headers
74
+ { 'Content-Type' => 'application/json;charset=UTF-8', 'Accept' => 'application/json' }
75
+ end
76
+
77
+ def api_endpoint
78
+ if Envialia.live?
79
+ Envialia::ENVIALIA_ENDPOINT_LIVE
80
+ else
81
+ Envialia::ENVIALIA_ENDPOINT_TEST
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,147 @@
1
+ require 'httparty'
2
+
3
+ module Deliveries
4
+ module Couriers
5
+ module Envialia
6
+ module Pickups
7
+ class Create
8
+ include HTTParty
9
+ include Authentication
10
+
11
+ attr_accessor :sender, :receiver, :parcels,
12
+ :reference_code, :pickup_date, :remarks, :tracking_code
13
+
14
+ def initialize(sender:, receiver:, parcels:,
15
+ reference_code:, pickup_date:, remarks:, tracking_code:)
16
+
17
+ self.sender = sender
18
+ self.receiver = receiver
19
+ self.parcels = parcels
20
+ self.reference_code = reference_code
21
+ self.pickup_date = pickup_date
22
+ self.remarks = remarks
23
+ self.tracking_code = tracking_code
24
+ end
25
+
26
+ def execute
27
+ response = self.class.post(
28
+ api_endpoint,
29
+ body: body,
30
+ headers: headers,
31
+ debug_output: Deliveries.debug ? Deliveries.logger : nil
32
+ )
33
+
34
+ raise Deliveries::ClientError unless response.success?
35
+
36
+ pickup_number = response.dig('Envelope', 'Body', 'WebServService___GrabaRecogida3Response', 'strCodOut')
37
+
38
+ if pickup_number
39
+ Deliveries::Pickup.new(
40
+ courier_id: 'envialia',
41
+ sender: sender,
42
+ receiver: receiver,
43
+ parcels: parcels,
44
+ reference_code: reference_code,
45
+ tracking_code: pickup_number,
46
+ pickup_date: pickup_date
47
+ )
48
+ else
49
+ exception = response.dig('Envelope', 'Body', 'Fault')
50
+
51
+ if exception['faultcode'].eql?('Exception')
52
+ exception_code, exception_str = exception['faultstring'].split(':')
53
+ else
54
+ exception_code = 400
55
+ exception_str = exception['faultstring']
56
+ end
57
+
58
+ raise Deliveries::APIError.new(
59
+ exception_str.strip,
60
+ exception_code.to_i
61
+ )
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def body
68
+ <<~XML
69
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
70
+ <soap:Header>
71
+ <ROClientIDHeader xmlns="http://tempuri.org/">
72
+ <ID>#{session_id}</ID>
73
+ </ROClientIDHeader>
74
+ </soap:Header>
75
+ <soap:Body>
76
+ <WebServService___GrabaRecogida3 xmlns="http://tempuri.org/">
77
+ <strCod></strCod>
78
+ <strAlbaran>#{tracking_code}</strAlbaran>
79
+ <strCodAgeCargo>#{Deliveries.courier(:envialia).config(:agency_code)}</strCodAgeCargo>
80
+ <strCodAgeOri></strCodAgeOri>
81
+ <dtFecRec>#{pickup_date.strftime('%Y/%m/%d')}</dtFecRec>
82
+ <strNomOri>#{sender.name}</strNomOri>
83
+ <strPobOri>#{sender.city}</strPobOri>
84
+ <strDirOri>#{sender.street}</strDirOri>
85
+ <strCPOri>#{format_postcode(sender.postcode, sender.country)}</strCPOri>
86
+ <strTlfOri>#{sender.phone}</strTlfOri>
87
+ <strNomDes>#{receiver.name}</strNomDes>
88
+ <strPobDes>#{receiver.city}</strPobDes>
89
+ <strDirDes>#{receiver.street}</strDirDes>
90
+ <strCPDes>#{format_postcode(receiver.postcode, receiver.country)}</strCPDes>
91
+ <strTlfDes>#{receiver.phone}</strTlfDes>
92
+ <strCodPais>#{receiver.country}</strCodPais>
93
+ <intBul>#{parcels}</intBul>
94
+ <strCodTipoServ>72</strCodTipoServ>
95
+ <dPesoOri>0</dPesoOri>
96
+ <dAltoOri>0</dAltoOri>
97
+ <dAnchoOri>0</dAnchoOri>
98
+ <dLargoOri>0</dLargoOri>
99
+ <dReembolso>0</dReembolso>
100
+ <dValor>0</dValor>
101
+ <dAnticipo>0</dAnticipo>
102
+ <dCobCli>0</dCobCli>
103
+ <strObs>#{remarks}</strObs>
104
+ <boSabado>false</boSabado>
105
+ <boRetorno>false</boRetorno>
106
+ <boGestOri>false</boGestOri>
107
+ <boGestDes>false</boGestDes>
108
+ <boAnulado>false</boAnulado>
109
+ <boAcuse>false</boAcuse>
110
+ <strRef>#{reference_code}</strRef>
111
+ <dBaseImp>0</dBaseImp>
112
+ <dImpuesto>0</dImpuesto>
113
+ <boPorteDebCli>false</boPorteDebCli>
114
+ <strDesDirEmails>#{receiver.email}</strDesDirEmails>
115
+ <boCampo5>false</boCampo5>
116
+ <boPagoDUAImp>false</boPagoDUAImp>
117
+ <boPagoImpDes>false</boPagoImpDes>
118
+ </WebServService___GrabaRecogida3>
119
+ </soap:Body>
120
+ </soap:Envelope>
121
+ XML
122
+ end
123
+
124
+ def headers
125
+ { 'Content-Type' => 'application/json;charset=UTF-8', 'Accept' => 'application/json' }
126
+ end
127
+
128
+ def api_endpoint
129
+ if Envialia.live?
130
+ Envialia::ENVIALIA_ENDPOINT_LIVE
131
+ else
132
+ Envialia::ENVIALIA_ENDPOINT_TEST
133
+ end
134
+ end
135
+
136
+ def format_postcode(postcode, country)
137
+ if country.to_sym.downcase == :pt
138
+ postcode&.split('-')&.first
139
+ else
140
+ postcode
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,94 @@
1
+ require 'active_support/time'
2
+ require 'active_support/core_ext/hash/conversions'
3
+ require 'active_support/core_ext/time'
4
+
5
+ module Deliveries
6
+ module Couriers
7
+ module Envialia
8
+ module Pickups
9
+ class Trace
10
+ class FormatResponse
11
+ attr_accessor :response
12
+
13
+ def initialize(response:)
14
+ self.response = response
15
+ end
16
+
17
+ STATUS_CODES = {
18
+ 'R0' => 'Solicitada',
19
+ 'R1' => 'Lectura en delegación',
20
+ 'R2' => 'Asignada',
21
+ 'R3' => 'Incidencia',
22
+ 'R4' => 'Realizada',
23
+ 'R5' => 'Pendiente de asignación',
24
+ 'R6' => 'Recogida fallida',
25
+ 'R7' => 'Finalizada',
26
+ 'R8' => 'Anulada',
27
+ 'R9' => 'Lectura repartidor'
28
+ }.freeze
29
+
30
+ def execute
31
+ body = response.dig('Envelope', 'Body', 'WebServService___ConsRecEstadosResponse', 'strRecEstados')
32
+ parsed_response = Hash.from_xml(body).dig('CONSULTA', 'REC_ESTADOS')
33
+
34
+ checkpoints = formatted_checkpoints(parsed_response)
35
+
36
+ tracking_info_params = {}
37
+ tracking_info_params[:courier_id] = 'envialia'
38
+ tracking_info_params[:tracking_code] = nil
39
+ tracking_info_params[:status] = checkpoints.last.try(:status)
40
+ tracking_info_params[:checkpoints] = checkpoints
41
+
42
+ tracking_info_params
43
+ end
44
+
45
+ private
46
+
47
+ def formatted_checkpoints(shipment_statuses)
48
+ checkpoints = []
49
+ if shipment_statuses.is_a?(Array)
50
+ shipment_statuses.each do |shipment_status|
51
+ checkpoints << formatted_checkpoint(shipment_status)
52
+ end
53
+ else
54
+ checkpoints << formatted_checkpoint(shipment_statuses)
55
+ end
56
+
57
+ checkpoints
58
+ end
59
+
60
+ def formatted_checkpoint(shipment_status)
61
+ status = STATUS_CODES[shipment_status['V_COD_TIPO_EST']]
62
+
63
+ date = shipment_status['D_FEC_HORA_ALTA']
64
+
65
+ Deliveries::Checkpoint.new(
66
+ status: status(status),
67
+ location: nil,
68
+ tracked_at: Time.zone.strptime(date, '%m/%d/%Y %H:%M:%S'),
69
+ description: status
70
+ )
71
+ end
72
+
73
+ def status(code)
74
+ case code
75
+ when 'Solicitada', 'Lectura en delegación', 'Asignada'
76
+ :registered
77
+ when 'Realizada', 'Recogida Parcial (Múltiples Destinos)'
78
+ :in_transit
79
+ when 'Recogida fallida', 'Datos insuficientes', 'Error al emitir la recogida'
80
+ :delivery_failed
81
+ when 'Anulada', 'Recogida anulada'
82
+ :cancelled
83
+ when 'Finalizada'
84
+ :delivered
85
+ else
86
+ :unknown_status
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,69 @@
1
+ require 'httparty'
2
+
3
+ module Deliveries
4
+ module Couriers
5
+ module Envialia
6
+ module Pickups
7
+ class Trace
8
+ include HTTParty
9
+ include Authentication
10
+
11
+ attr_accessor :tracking_code
12
+
13
+ def initialize(tracking_code:)
14
+ self.tracking_code = tracking_code
15
+ end
16
+
17
+ def execute
18
+ response = self.class.post(
19
+ api_endpoint,
20
+ body: body,
21
+ headers: headers,
22
+ debug_output: Deliveries.debug ? Deliveries.logger : nil
23
+ )
24
+
25
+ raise Deliveries::ClientError unless response.success?
26
+
27
+ if response.dig('Envelope', 'Body', 'WebServService___ConsRecEstadosResponse', 'strRecEstados').nil?
28
+ raise Deliveries::APIError.new(
29
+ 'No se han encontrado datos para este envío',
30
+ '402'
31
+ )
32
+ else
33
+ response
34
+ end
35
+ end
36
+
37
+ def body
38
+ <<~XML
39
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
40
+ <soapenv:Header>
41
+ <tns:ROClientIDHeader xmlns:tns="http://tempuri.org/">
42
+ <tns:ID>#{session_id}</tns:ID>
43
+ </tns:ROClientIDHeader>
44
+ </soapenv:Header>
45
+ <soapenv:Body>
46
+ <tns:WebServService___ConsRecEstados xmlns:tns="http://tempuri.org/">
47
+ <tns:strCodRec>#{tracking_code}</tns:strCodRec>
48
+ </tns:WebServService___ConsRecEstados>
49
+ </soapenv:Body>
50
+ </soapenv:Envelope>
51
+ XML
52
+ end
53
+
54
+ def headers
55
+ { 'Content-Type' => 'application/json;charset=UTF-8', 'Accept' => 'application/json' }
56
+ end
57
+
58
+ def api_endpoint
59
+ if Envialia.live?
60
+ Envialia::ENVIALIA_ENDPOINT_LIVE
61
+ else
62
+ Envialia::ENVIALIA_ENDPOINT_TEST
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,148 @@
1
+ require 'httparty'
2
+
3
+ module Deliveries
4
+ module Couriers
5
+ module Envialia
6
+ module Shipments
7
+ class Create
8
+ include HTTParty
9
+ include Authentication
10
+
11
+ attr_accessor :sender, :receiver, :collection_point, :parcels,
12
+ :reference_code, :shipment_date, :remarks
13
+
14
+ def initialize(sender:, receiver:, collection_point:, parcels:,
15
+ reference_code:, shipment_date:, remarks:)
16
+ self.sender = sender
17
+ self.receiver = receiver
18
+ self.collection_point = collection_point
19
+ self.parcels = parcels
20
+ self.reference_code = reference_code
21
+ self.shipment_date = shipment_date
22
+ self.remarks = remarks
23
+ end
24
+
25
+ def execute
26
+ response = self.class.post(
27
+ api_endpoint,
28
+ body: body,
29
+ headers: headers,
30
+ debug_output: Deliveries.debug ? Deliveries.logger : nil
31
+ )
32
+
33
+ raise Deliveries::ClientError unless response.success?
34
+
35
+ tracking_code = response.dig('Envelope', 'Body', 'WebServService___GrabaEnvio8Response', 'strAlbaranOut')
36
+
37
+ if tracking_code
38
+ Deliveries::Shipment.new(
39
+ courier_id: 'envialia',
40
+ sender: sender,
41
+ receiver: receiver,
42
+ parcels: parcels,
43
+ reference_code: reference_code,
44
+ tracking_code: tracking_code,
45
+ shipment_date: shipment_date,
46
+ label: nil
47
+ )
48
+ else
49
+ exception = response.dig('Envelope', 'Body', 'Fault')
50
+
51
+ if exception['faultcode'].eql?('Exception')
52
+ exception_code, exception_str = exception['faultstring'].split(':')
53
+ else
54
+ exception_code = 400
55
+ exception_str = exception['faultstring']
56
+ end
57
+
58
+ raise Deliveries::APIError.new(
59
+ exception_str.strip,
60
+ exception_code.to_i
61
+ )
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def body
68
+ <<~XML
69
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
70
+ <soap:Header>
71
+ <ROClientIDHeader xmlns="http://tempuri.org/">
72
+ <ID>#{session_id}</ID>
73
+ </ROClientIDHeader>
74
+ </soap:Header>
75
+ <soap:Body>
76
+ <WebServService___GrabaEnvio8 xmlns="http://tempuri.org/">
77
+ <strCodAgeCargo>#{Deliveries.courier(:envialia).config(:agency_code)}</strCodAgeCargo>
78
+ <strCodAgeOri>#{Deliveries.courier(:envialia).config(:agency_code)}</strCodAgeOri>
79
+ <dtFecha>#{shipment_date.strftime('%Y/%m/%d')}</dtFecha>
80
+ <strCodTipoServ>72</strCodTipoServ>
81
+ <strCodCli>#{Deliveries.courier(:envialia).config(:username)}</strCodCli>
82
+ <strNomOri>#{sender.name}</strNomOri>
83
+ <strPobOri>#{sender.city}</strPobOri>
84
+ <strDirOri>#{sender.street}</strDirOri>
85
+ <strCPOri>#{format_postcode(sender.postcode, sender.country)}</strCPOri>
86
+ <strTlfOri>#{sender.phone}</strTlfOri>
87
+ <strNomDes>#{receiver.name}</strNomDes>
88
+ <strPobDes>#{receiver.city}</strPobDes>
89
+ <strDirDes>#{receiver.street}</strDirDes>
90
+ <strCPDes>#{format_postcode(receiver.postcode, receiver.country)}</strCPDes>
91
+ <strTlfDes>#{receiver.phone}</strTlfDes>
92
+ <strCodPais>#{receiver.country}</strCodPais>
93
+ <intDoc>0</intDoc>
94
+ <intPaq>#{parcels}</intPaq>
95
+ <dPesoOri>0</dPesoOri>
96
+ <dAltoOri>0</dAltoOri>
97
+ <dAnchoOri>0</dAnchoOri>
98
+ <dLargoOri>0</dLargoOri>
99
+ <dReembolso>0</dReembolso>
100
+ <dValor>0</dValor>
101
+ <dAnticipo>0</dAnticipo>
102
+ <dCobCli>0</dCobCli>
103
+ <strObs>#{remarks}</strObs>
104
+ <boSabado>false</boSabado>
105
+ <boRetorno>false</boRetorno>
106
+ <boGestOri>false</boGestOri>
107
+ <boGestDes>false</boGestDes>
108
+ <boAnulado>false</boAnulado>
109
+ <boAcuse>false</boAcuse>
110
+ <strRef>#{reference_code}</strRef>
111
+ <dBaseImp>0</dBaseImp>
112
+ <dImpuesto>0</dImpuesto>
113
+ <boPorteDebCli>false</boPorteDebCli>
114
+ <strDesDirEmails>#{receiver.email}</strDesDirEmails>
115
+ <boInsert>true</boInsert>
116
+ <boCampo5>false</boCampo5>
117
+ <boPagoDUAImp>false</boPagoDUAImp>
118
+ <boPagoImpDes>false</boPagoImpDes>
119
+ </WebServService___GrabaEnvio8>
120
+ </soap:Body>
121
+ </soap:Envelope>
122
+ XML
123
+ end
124
+
125
+ def headers
126
+ { 'Content-Type' => 'application/json;charset=UTF-8', 'Accept' => 'application/json' }
127
+ end
128
+
129
+ def api_endpoint
130
+ if Envialia.live?
131
+ Envialia::ENVIALIA_ENDPOINT_LIVE
132
+ else
133
+ Envialia::ENVIALIA_ENDPOINT_TEST
134
+ end
135
+ end
136
+
137
+ def format_postcode(postcode, country)
138
+ if country.to_sym.downcase == :pt
139
+ postcode&.split('-')&.first
140
+ else
141
+ postcode
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,97 @@
1
+ require 'active_support/time'
2
+ require 'active_support/core_ext/time'
3
+ require 'active_support/core_ext/hash/conversions'
4
+
5
+ module Deliveries
6
+ module Couriers
7
+ module Envialia
8
+ module Shipments
9
+ class Trace
10
+ class FormatResponse
11
+ attr_accessor :response
12
+
13
+ def initialize(response:)
14
+ self.response = response
15
+ end
16
+
17
+ STATUS_CODES = {
18
+ '0' => 'Documentado',
19
+ '1' => 'En Tránsito',
20
+ '2' => 'En Reparto',
21
+ '3' => 'Incidencia',
22
+ '4' => 'Entregado',
23
+ '5' => 'Devuelto',
24
+ '6' => 'Recanalizado',
25
+ '8' => 'Destruido (P.O.Delegación)',
26
+ '9' => 'Falta definitiva de expedición',
27
+ '10' => 'Pendiente nuevo reparto',
28
+ '11' => 'En CS destino',
29
+ '12' => 'Recogeran en delegacion',
30
+ '14' => 'Entrega Parcial',
31
+ '15' => 'Tránsito 72H.',
32
+ '16' => 'Pendiente de emisión'
33
+ }.freeze
34
+
35
+ def execute
36
+ body = response.dig('Envelope', 'Body', 'WebServService___ConsEnvEstadosResponse', 'strEnvEstados')
37
+ parsed_response = Hash.from_xml(body).dig('CONSULTA', 'ENV_ESTADOS')
38
+
39
+ checkpoints = formatted_checkpoints(parsed_response)
40
+
41
+ tracking_info_params = {}
42
+ tracking_info_params[:courier_id] = 'envialia'
43
+ tracking_info_params[:tracking_code] = nil
44
+ tracking_info_params[:status] = checkpoints.last.try(:status)
45
+ tracking_info_params[:checkpoints] = formatted_checkpoints(parsed_response)
46
+
47
+ tracking_info_params
48
+ end
49
+
50
+ private
51
+
52
+ def formatted_checkpoints(shipment_statuses)
53
+ checkpoints = []
54
+ if shipment_statuses.is_a?(Array)
55
+ shipment_statuses.each do |shipment_status|
56
+ checkpoints << formatted_checkpoint(shipment_status)
57
+ end
58
+ else
59
+ checkpoints << formatted_checkpoint(shipment_statuses)
60
+ end
61
+
62
+ checkpoints
63
+ end
64
+
65
+ def formatted_checkpoint(shipment_status)
66
+ status = STATUS_CODES[shipment_status['V_COD_TIPO_EST']]
67
+
68
+ date = shipment_status['D_FEC_HORA_ALTA']
69
+
70
+ Deliveries::Checkpoint.new(
71
+ status: status(status),
72
+ location: nil,
73
+ tracked_at: Time.zone.strptime(date, '%m/%d/%Y %H:%M:%S'),
74
+ description: status
75
+ )
76
+ end
77
+
78
+ def status(code)
79
+ case code
80
+ when 'Documentado'
81
+ :registered
82
+ when 'En Tránsito', 'En Reparto', 'En CS destino'
83
+ :in_transit
84
+ when 'Entregado'
85
+ :delivered
86
+ when 'Devuelto'
87
+ :cancelled
88
+ else
89
+ :unknown_status
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,71 @@
1
+ require 'httparty'
2
+
3
+ module Deliveries
4
+ module Couriers
5
+ module Envialia
6
+ module Shipments
7
+ class Trace
8
+ include HTTParty
9
+ include Authentication
10
+
11
+ attr_accessor :tracking_code
12
+
13
+ def initialize(tracking_code:)
14
+ self.tracking_code = tracking_code
15
+ end
16
+
17
+ def execute
18
+ response = self.class.post(
19
+ api_endpoint,
20
+ body: body,
21
+ headers: headers,
22
+ debug_output: Deliveries.debug ? Deliveries.logger : nil
23
+ )
24
+
25
+ raise Deliveries::ClientError unless response.success?
26
+
27
+ if response.dig('Envelope', 'Body', 'WebServService___ConsEnvEstadosResponse', 'strEnvEstados').nil?
28
+ raise Deliveries::APIError.new(
29
+ 'No se han encontrado datos para este envío',
30
+ '402'
31
+ )
32
+ else
33
+ response
34
+ end
35
+ end
36
+
37
+ def body
38
+ <<~XML
39
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
40
+ <soapenv:Header>
41
+ <tns:ROClientIDHeader xmlns:tns="http://tempuri.org/">
42
+ <tns:ID>#{session_id}</tns:ID>
43
+ </tns:ROClientIDHeader>
44
+ </soapenv:Header>
45
+ <soapenv:Body>
46
+ <tns:WebServService___ConsEnvEstados xmlns:tns="http://tempuri.org/">
47
+ <tns:strCodAgeCargo>#{Deliveries.courier(:envialia).config(:agency_code)}</tns:strCodAgeCargo>
48
+ <tns:strCodAgeOri>#{Deliveries.courier(:envialia).config(:agency_code)}</tns:strCodAgeOri>
49
+ <tns:strAlbaran>#{tracking_code}</tns:strAlbaran>
50
+ </tns:WebServService___ConsEnvEstados>
51
+ </soapenv:Body>
52
+ </soapenv:Envelope>
53
+ XML
54
+ end
55
+
56
+ def headers
57
+ { 'Content-Type' => 'application/json;charset=UTF-8', 'Accept' => 'application/json' }
58
+ end
59
+
60
+ def api_endpoint
61
+ if Envialia.live?
62
+ Envialia::ENVIALIA_ENDPOINT_LIVE
63
+ else
64
+ Envialia::ENVIALIA_ENDPOINT_TEST
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,93 @@
1
+ require_relative 'envialia/authentication'
2
+ require_relative 'envialia/shipments/create'
3
+ require_relative 'envialia/shipments/trace/format_response'
4
+ require_relative 'envialia/shipments/trace'
5
+ require_relative 'envialia/pickups/trace/format_response'
6
+ require_relative 'envialia/pickups/trace'
7
+ require_relative 'envialia/pickups/create'
8
+ require_relative 'envialia/labels/generate'
9
+
10
+ module Deliveries
11
+ module Couriers
12
+ module Envialia
13
+ extend Courier
14
+
15
+ Config = Struct.new(
16
+ :username,
17
+ :password,
18
+ :agency_code
19
+ )
20
+
21
+ ENVIALIA_LOGIN_ENDPOINT_LIVE = 'http://ws.envialia.com/SOAP?service=LoginService'.freeze
22
+ ENVIALIA_LOGIN_ENDPOINT_TEST = 'http://wstest.envialia.com:9085/SOAP?service=LoginService'.freeze
23
+ ENVIALIA_ENDPOINT_LIVE = 'http://ws.envialia.com/SOAP?service=WebServService'.freeze
24
+ ENVIALIA_ENDPOINT_TEST = 'http://wstest.envialia.com:9085/SOAP?service=WebServService'.freeze
25
+
26
+ module_function
27
+
28
+ def create_shipment(sender:, receiver:, parcels:, reference_code:, collection_point: nil, shipment_date: nil, remarks: nil, **)
29
+ Shipments::Create.new(
30
+ sender: sender,
31
+ receiver: receiver,
32
+ collection_point: collection_point,
33
+ shipment_date: shipment_date,
34
+ parcels: parcels,
35
+ reference_code: reference_code,
36
+ remarks: remarks
37
+ ).execute
38
+ end
39
+
40
+ def create_pickup(sender:, receiver:, parcels:, reference_code:, pickup_date: nil, remarks: nil, tracking_code: nil, **)
41
+ Pickups::Create.new(
42
+ sender: sender,
43
+ receiver: receiver,
44
+ parcels: parcels,
45
+ reference_code: reference_code,
46
+ pickup_date: pickup_date,
47
+ remarks: remarks,
48
+ tracking_code: tracking_code
49
+ ).execute
50
+ end
51
+
52
+ def shipment_info(tracking_code:, **)
53
+ response = Shipments::Trace.new(
54
+ tracking_code: tracking_code
55
+ ).execute
56
+
57
+ tracking_info_params = Shipments::Trace::FormatResponse.new(response: response).execute
58
+ tracking_info_params.merge!({ tracking_code: tracking_code })
59
+ Deliveries::TrackingInfo.new(**tracking_info_params)
60
+ end
61
+
62
+ def pickup_info(tracking_code:, **)
63
+ response = Pickups::Trace.new(
64
+ tracking_code: tracking_code
65
+ ).execute
66
+
67
+ tracking_info_params = Pickups::Trace::FormatResponse.new(response: response).execute
68
+ tracking_info_params.merge!({ tracking_code: tracking_code })
69
+ Deliveries::TrackingInfo.new(**tracking_info_params)
70
+ end
71
+
72
+ def get_label(tracking_code:, **)
73
+ pdf = Labels::Generate.new(
74
+ tracking_codes: tracking_code
75
+ ).execute.first
76
+
77
+ Deliveries::Label.new(raw: pdf)
78
+ end
79
+
80
+ def get_labels(tracking_codes:, **)
81
+ labels = Deliveries::Labels.new
82
+
83
+ Labels::Generate.new(
84
+ tracking_codes: tracking_codes
85
+ ).execute.each do |pdf|
86
+ labels << pdf
87
+ end
88
+
89
+ labels
90
+ end
91
+ end
92
+ end
93
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Deliveries
4
- VERSION = '0.2.1'
4
+ VERSION = '0.3.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deliveries
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fran Vega
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2021-11-04 00:00:00.000000000 Z
14
+ date: 2021-11-26 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activesupport
@@ -171,6 +171,15 @@ files:
171
171
  - lib/deliveries/couriers/correos_express/shipments/trace.rb
172
172
  - lib/deliveries/couriers/correos_express/shipments/trace/format_response.rb
173
173
  - lib/deliveries/couriers/dummy.rb
174
+ - lib/deliveries/couriers/envialia.rb
175
+ - lib/deliveries/couriers/envialia/authentication.rb
176
+ - lib/deliveries/couriers/envialia/labels/generate.rb
177
+ - lib/deliveries/couriers/envialia/pickups/create.rb
178
+ - lib/deliveries/couriers/envialia/pickups/trace.rb
179
+ - lib/deliveries/couriers/envialia/pickups/trace/format_response.rb
180
+ - lib/deliveries/couriers/envialia/shipments/create.rb
181
+ - lib/deliveries/couriers/envialia/shipments/trace.rb
182
+ - lib/deliveries/couriers/envialia/shipments/trace/format_response.rb
174
183
  - lib/deliveries/couriers/mondial_relay.rb
175
184
  - lib/deliveries/couriers/mondial_relay/address.rb
176
185
  - lib/deliveries/couriers/mondial_relay/collection_points/search/format_response.rb
@@ -233,7 +242,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
233
242
  - !ruby/object:Gem::Version
234
243
  version: '0'
235
244
  requirements: []
236
- rubygems_version: 3.0.3.1
245
+ rubygems_version: 3.2.22
237
246
  signing_key:
238
247
  specification_version: 4
239
248
  summary: Library to abstract multiple courier web services.