deliveries 0.1.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 (81) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +19 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +34 -0
  6. data/CHANGELOG.md +13 -0
  7. data/Gemfile +12 -0
  8. data/Gemfile.lock +127 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +207 -0
  11. data/Rakefile +12 -0
  12. data/bin/console +15 -0
  13. data/bin/setup +8 -0
  14. data/deliveries.gemspec +36 -0
  15. data/lib/deliveries/address.rb +71 -0
  16. data/lib/deliveries/checkpoint.rb +12 -0
  17. data/lib/deliveries/collection_point.rb +45 -0
  18. data/lib/deliveries/courier.rb +69 -0
  19. data/lib/deliveries/couriers/correos_express/address.rb +31 -0
  20. data/lib/deliveries/couriers/correos_express/collection_points/search/format_response.rb +82 -0
  21. data/lib/deliveries/couriers/correos_express/collection_points/search.rb +56 -0
  22. data/lib/deliveries/couriers/correos_express/labels/generate.rb +68 -0
  23. data/lib/deliveries/couriers/correos_express/pickups/create/defaults.rb +60 -0
  24. data/lib/deliveries/couriers/correos_express/pickups/create/format_params.rb +95 -0
  25. data/lib/deliveries/couriers/correos_express/pickups/create.rb +66 -0
  26. data/lib/deliveries/couriers/correos_express/pickups/cutoff_time/format_params.rb +40 -0
  27. data/lib/deliveries/couriers/correos_express/pickups/cutoff_time.rb +61 -0
  28. data/lib/deliveries/couriers/correos_express/pickups/trace/correos.test.wsdl +163 -0
  29. data/lib/deliveries/couriers/correos_express/pickups/trace/correos.wsdl +163 -0
  30. data/lib/deliveries/couriers/correos_express/pickups/trace/format_response.rb +71 -0
  31. data/lib/deliveries/couriers/correos_express/pickups/trace.rb +49 -0
  32. data/lib/deliveries/couriers/correos_express/shipments/create/defaults.rb +80 -0
  33. data/lib/deliveries/couriers/correos_express/shipments/create/format_params.rb +99 -0
  34. data/lib/deliveries/couriers/correos_express/shipments/create.rb +101 -0
  35. data/lib/deliveries/couriers/correos_express/shipments/trace/format_response.rb +67 -0
  36. data/lib/deliveries/couriers/correos_express/shipments/trace.rb +65 -0
  37. data/lib/deliveries/couriers/correos_express.rb +162 -0
  38. data/lib/deliveries/couriers/dummy.rb +106 -0
  39. data/lib/deliveries/couriers/mondial_relay/address.rb +31 -0
  40. data/lib/deliveries/couriers/mondial_relay/collection_points/search/format_response.rb +103 -0
  41. data/lib/deliveries/couriers/mondial_relay/labels/generate.rb +40 -0
  42. data/lib/deliveries/couriers/mondial_relay/pickups/create/format_params.rb +57 -0
  43. data/lib/deliveries/couriers/mondial_relay/shipments/create/defaults.rb +96 -0
  44. data/lib/deliveries/couriers/mondial_relay/shipments/create/format_params.rb +68 -0
  45. data/lib/deliveries/couriers/mondial_relay/shipments/create.rb +36 -0
  46. data/lib/deliveries/couriers/mondial_relay/shipments/trace/format_response.rb +94 -0
  47. data/lib/deliveries/couriers/mondial_relay/shipments/trace.rb +47 -0
  48. data/lib/deliveries/couriers/mondial_relay/status_codes.rb +105 -0
  49. data/lib/deliveries/couriers/mondial_relay.rb +189 -0
  50. data/lib/deliveries/couriers/mondial_relay_dual/address.rb +31 -0
  51. data/lib/deliveries/couriers/mondial_relay_dual/pickups/create/format_params.rb +39 -0
  52. data/lib/deliveries/couriers/mondial_relay_dual/shipments/create/format_params.rb +101 -0
  53. data/lib/deliveries/couriers/mondial_relay_dual/shipments/create.rb +133 -0
  54. data/lib/deliveries/couriers/mondial_relay_dual.rb +105 -0
  55. data/lib/deliveries/couriers/spring/address.rb +23 -0
  56. data/lib/deliveries/couriers/spring/labels/generate.rb +33 -0
  57. data/lib/deliveries/couriers/spring/request.rb +43 -0
  58. data/lib/deliveries/couriers/spring/shipments/create/defaults.rb +73 -0
  59. data/lib/deliveries/couriers/spring/shipments/create/format_params.rb +77 -0
  60. data/lib/deliveries/couriers/spring/shipments/create.rb +45 -0
  61. data/lib/deliveries/couriers/spring/shipments/trace/format_response.rb +79 -0
  62. data/lib/deliveries/couriers/spring/shipments/trace.rb +29 -0
  63. data/lib/deliveries/couriers/spring.rb +84 -0
  64. data/lib/deliveries/couriers/ups/collection_points/search.rb +142 -0
  65. data/lib/deliveries/couriers/ups/json_request.rb +40 -0
  66. data/lib/deliveries/couriers/ups/labels/generate.rb +72 -0
  67. data/lib/deliveries/couriers/ups/shipments/create.rb +210 -0
  68. data/lib/deliveries/couriers/ups/shipments/trace.rb +79 -0
  69. data/lib/deliveries/couriers/ups.rb +122 -0
  70. data/lib/deliveries/couriers.rb +14 -0
  71. data/lib/deliveries/delivery.rb +20 -0
  72. data/lib/deliveries/errors.rb +22 -0
  73. data/lib/deliveries/label.rb +20 -0
  74. data/lib/deliveries/label_utils.rb +67 -0
  75. data/lib/deliveries/labels.rb +40 -0
  76. data/lib/deliveries/pickup.rb +15 -0
  77. data/lib/deliveries/shipment.rb +15 -0
  78. data/lib/deliveries/tracking_info.rb +29 -0
  79. data/lib/deliveries/version.rb +5 -0
  80. data/lib/deliveries.rb +63 -0
  81. metadata +240 -0
@@ -0,0 +1,101 @@
1
+ require 'httparty'
2
+
3
+ module Deliveries
4
+ module Couriers
5
+ module CorreosExpress
6
+ module Shipments
7
+ class Create
8
+ include HTTParty
9
+
10
+ attr_accessor :sender, :receiver, :collection_point, :parcels,
11
+ :reference_code, :shipment_date, :remarks
12
+
13
+ def initialize(sender:, receiver:, collection_point:, parcels:,
14
+ reference_code:, shipment_date:, remarks:)
15
+ self.sender = sender
16
+ self.receiver = receiver
17
+ self.collection_point = collection_point
18
+ self.parcels = parcels
19
+ self.reference_code = reference_code
20
+ self.shipment_date = shipment_date
21
+ self.remarks = remarks
22
+ end
23
+
24
+ def execute
25
+ auth = {
26
+ username: CorreosExpress.config(:username),
27
+ password: CorreosExpress.config(:password)
28
+ }
29
+
30
+ params = FormatParams.new(
31
+ sender: sender.courierize(:correos_express),
32
+ receiver: receiver.courierize(:correos_express),
33
+ collection_point: collection_point,
34
+ parcels: parcels,
35
+ reference_code: reference_code,
36
+ shipment_date: shipment_date,
37
+ remarks: remarks
38
+ ).execute
39
+
40
+ response = self.class.post(
41
+ api_endpoint,
42
+ basic_auth: auth,
43
+ body: params,
44
+ headers: headers,
45
+ debug_output: Deliveries.debug ? Deliveries.logger : nil
46
+ )
47
+ raise Deliveries::ClientError unless response.success?
48
+
49
+ parsed_response = JSON.parse(response.body, symbolize_names: true)
50
+ if parsed_response[:codigoRetorno].zero? && parsed_response.key?(:datosResultado)
51
+ tracking_code = parsed_response[:datosResultado]
52
+ decoded_label = decode_label(parsed_response.dig(:etiqueta, 0, :etiqueta1))
53
+ label = Label.new(raw: decoded_label) if decoded_label
54
+
55
+ Deliveries::Shipment.new(
56
+ courier_id: 'correos_express',
57
+ sender: sender,
58
+ receiver: receiver,
59
+ parcels: parcels,
60
+ reference_code: reference_code,
61
+ tracking_code: tracking_code,
62
+ shipment_date: shipment_date,
63
+ label: label
64
+ )
65
+ else
66
+ raise Deliveries::APIError.new(
67
+ parsed_response[:mensajeRetorno],
68
+ parsed_response[:codigoRetorno]
69
+ )
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def api_endpoint
76
+ if CorreosExpress.live?
77
+ CorreosExpress::SHIPMENTS_ENDPOINT_LIVE
78
+ else
79
+ CorreosExpress::SHIPMENTS_ENDPOINT_TEST
80
+ end
81
+ end
82
+
83
+ def headers
84
+ { 'Content-Type' => "application/json; charset='UTF-8'" }
85
+ end
86
+
87
+ def decode_label(encoded_label)
88
+ return nil if encoded_label.blank?
89
+
90
+ decoded_label = Base64.decode64(Base64.decode64(encoded_label)).force_encoding('binary')
91
+
92
+ # Check pdf file signature
93
+ return nil unless decoded_label[0..4].unpack1('H*').hex == 0x255044462d
94
+
95
+ decoded_label
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,67 @@
1
+ require 'active_support/time'
2
+
3
+ module Deliveries
4
+ module Couriers
5
+ module CorreosExpress
6
+ module Shipments
7
+ class Trace
8
+ class FormatResponse
9
+ attr_accessor :response
10
+
11
+ def initialize(response:)
12
+ self.response = response
13
+ end
14
+
15
+ def execute
16
+ tracking_info_params = {}
17
+ tracking_info_params[:courier_id] = 'correos_express'
18
+ tracking_info_params[:tracking_code] = response['NumEnvio']
19
+ tracking_info_params[:status] = status(response['DescEstado'])
20
+ tracking_info_params[:checkpoints] = formatted_checkpoints(response['EstadoEnvios'])
21
+
22
+ tracking_info_params
23
+ end
24
+
25
+ private
26
+
27
+ def formatted_checkpoints(shipment_statuses)
28
+ checkpoints = []
29
+ if shipment_statuses.is_a?(Array)
30
+ shipment_statuses.each do |shipment_status|
31
+ checkpoints << formatted_checkpoint(shipment_status)
32
+ end
33
+ else
34
+ checkpoints << formatted_checkpoint(shipment_statuses)
35
+ end
36
+
37
+ checkpoints
38
+ end
39
+
40
+ def formatted_checkpoint(shipment_status)
41
+ Deliveries::Checkpoint.new(
42
+ status: status(shipment_status['DescEstado']),
43
+ location: nil,
44
+ tracked_at: Time.zone.strptime("#{shipment_status['FechaEstado']} #{shipment_status['HoraEstado']}",
45
+ '%d%m%Y %H%M%S'),
46
+ description: shipment_status['DescEstado']
47
+ )
48
+ end
49
+
50
+ def status(code)
51
+ case code
52
+ when 'SIN RECEPCION'
53
+ :registered
54
+ when 'EN ARRASTRE', 'DELEGACION DESTINO', 'EN REPARTO', 'TRAMO ORIGEN'
55
+ :in_transit
56
+ when 'ENTREGADO'
57
+ :delivered
58
+ else
59
+ :unknown_status
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,65 @@
1
+ require 'httparty'
2
+ require 'active_support/core_ext/hash/conversions'
3
+
4
+ module Deliveries
5
+ module Couriers
6
+ module CorreosExpress
7
+ module Shipments
8
+ class Trace
9
+ include HTTParty
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
+ auth = {
19
+ username: CorreosExpress.config(:username),
20
+ password: CorreosExpress.config(:password)
21
+ }
22
+ solicitante = CorreosExpress.config(:client_code)
23
+
24
+ xml = "<?xml version='1.0'?>
25
+ <SeguimientoEnviosRequest xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
26
+ xsi:noNamespaceSchemaLocation='SeguimientoEnviosRequest.xsd'>
27
+ <Solicitante>#{solicitante}</Solicitante>
28
+ <Dato>#{tracking_code}</Dato>
29
+ </SeguimientoEnviosRequest>"
30
+
31
+ response = self.class.post(
32
+ api_endpoint,
33
+ basic_auth: auth,
34
+ body: xml,
35
+ headers: { 'Content-Type' => 'application/xml' },
36
+ debug_output: Deliveries.debug ? Deliveries.logger : nil
37
+ )
38
+
39
+ raise Deliveries::ClientError unless response.success?
40
+
41
+ result = Hash.from_xml response.force_encoding(Encoding::ISO_8859_1).encode(Encoding::UTF_8)
42
+ if result['SeguimientoEnviosResponse']['Error'] == '0'
43
+ result['SeguimientoEnviosResponse']
44
+ else
45
+ raise Deliveries::APIError.new(
46
+ result['SeguimientoEnviosResponse']['MensajeError'],
47
+ result['SeguimientoEnviosResponse']['Error']
48
+ )
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def api_endpoint
55
+ if CorreosExpress.live?
56
+ CorreosExpress::TRACKING_INFO_ENDPOINT_LIVE
57
+ else
58
+ CorreosExpress::TRACKING_INFO_ENDPOINT_TEST
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,162 @@
1
+ require_relative 'correos_express/shipments/trace/format_response'
2
+ require_relative 'correos_express/shipments/trace'
3
+ require_relative 'correos_express/shipments/create/defaults'
4
+ require_relative 'correos_express/shipments/create/format_params'
5
+ require_relative 'correos_express/shipments/create'
6
+ require_relative 'correos_express/collection_points/search'
7
+ require_relative 'correos_express/collection_points/search/format_response'
8
+ require_relative 'correos_express/labels/generate'
9
+ require_relative 'correos_express/pickups/create/defaults'
10
+ require_relative 'correos_express/pickups/create/format_params'
11
+ require_relative 'correos_express/pickups/create'
12
+ require_relative 'correos_express/pickups/trace/format_response'
13
+ require_relative 'correos_express/pickups/trace'
14
+ require_relative 'correos_express/pickups/cutoff_time/format_params'
15
+ require_relative 'correos_express/pickups/cutoff_time'
16
+ require_relative 'correos_express/address'
17
+
18
+ module Deliveries
19
+ module Couriers
20
+ module CorreosExpress
21
+ extend Courier
22
+
23
+ Config = Struct.new(
24
+ :username,
25
+ :password,
26
+ :client_code,
27
+ :shipment_sender_code,
28
+ :pickup_receiver_code,
29
+ :countries
30
+ )
31
+
32
+ COLLECTION_POINTS_ENDPOINT_TEST = 'https://www.correosexpress.com/wspsc/apiRestOficina/v1/oficinas/listadoOficinasCoordenadas'.freeze
33
+ COLLECTION_POINTS_ENDPOINT_LIVE = 'https://www.correosexpress.com/wpsc/apiRestOficina/v1/oficinas/listadoOficinasCoordenadas'.freeze
34
+ SHIPMENTS_ENDPOINT_LIVE = 'https://www.correosexpress.com/wpsc/apiRestGrabacionEnvio/json/grabacionEnvio'.freeze
35
+ SHIPMENTS_ENDPOINT_TEST = 'https://test.correosexpress.com/wspsc/apiRestGrabacionEnvio/json/grabacionEnvio'.freeze
36
+ TRACKING_INFO_ENDPOINT_LIVE = 'https://www.correosexpress.com/wpsc/apiRestSeguimientoEnvios/rest/seguimientoEnvios'.freeze
37
+ TRACKING_INFO_ENDPOINT_TEST = 'https://test.correosexpress.com/wspsc/apiRestSeguimientoEnvios/rest/seguimientoEnvios'.freeze
38
+ LABELS_ENDPOINT_LIVE = 'https://www.cexpr.es/wspsc/apiRestEtiquetaTransporte/json/etiquetaTransporte'.freeze
39
+ LABELS_ENDPOINT_TEST = 'https://www.test.cexpr.es/wsps/apiRestEtiquetaTransporte/json/etiquetaTransporte'.freeze
40
+ PICKUPS_ENDPOINT_LIVE = 'https://www.correosexpress.com/wpsc/apiRestGrabacionRecogida/json/grabarRecogida'.freeze
41
+ PICKUPS_ENDPOINT_TEST = 'https://test.correosexpress.com/wpsn/apiRestGrabacionRecogida/json/grabarRecogida'.freeze
42
+ CUTOFF_TIME_ENDPOINT_LIVE = 'https://www.correosexpress.com/wpsc/apiRestGrabacionRecogida/json/horaCorte'.freeze
43
+
44
+ module_function
45
+
46
+ def get_collection_points(postcode:, **)
47
+ raise Deliveries::APIError, 'Postcode cannot be null' if postcode.blank?
48
+
49
+ collection_points = []
50
+
51
+ points = CollectionPoints::Search.new(postcode: postcode).execute
52
+ points.each do |point|
53
+ collection_point_params = CollectionPoints::Search::FormatResponse.new(response: point).execute
54
+ collection_points << Deliveries::CollectionPoint.new(collection_point_params)
55
+ end
56
+
57
+ collection_points
58
+ end
59
+
60
+ def get_collection_point(global_point_id:)
61
+ global_point = Deliveries::CollectionPoint.parse_global_point_id(global_point_id: global_point_id)
62
+
63
+ collection_points = get_collection_points(postcode: global_point.postcode)
64
+ collection_point = collection_points.select { |col| col.point_id == global_point.point_id }.first
65
+
66
+ if collection_point.blank?
67
+ raise Deliveries::APIError.new(
68
+ "Collection Point not found - #{global_point.point_id}",
69
+ 1
70
+ )
71
+ end
72
+
73
+ collection_point
74
+ end
75
+
76
+ def shipment_info(tracking_code:, **)
77
+ response = Shipments::Trace.new(
78
+ tracking_code: tracking_code
79
+ ).execute
80
+
81
+ tracking_info_params = Shipments::Trace::FormatResponse.new(response: response).execute
82
+ Deliveries::TrackingInfo.new(tracking_info_params)
83
+ end
84
+
85
+ def pickup_info(tracking_code:, **)
86
+ response = Pickups::Trace.new(
87
+ tracking_code: tracking_code
88
+ ).execute
89
+
90
+ tracking_info_params = Pickups::Trace::FormatResponse.new(response: response).execute
91
+
92
+ Deliveries::TrackingInfo.new(tracking_info_params)
93
+ end
94
+
95
+ def get_label(tracking_code:, **)
96
+ pdf = Labels::Generate.new(
97
+ tracking_codes: tracking_code
98
+ ).execute.first
99
+
100
+ Deliveries::Label.new(raw: pdf)
101
+ end
102
+
103
+ def get_labels(tracking_codes:, **)
104
+ labels = Deliveries::Labels.new
105
+
106
+ Labels::Generate.new(
107
+ tracking_codes: tracking_codes
108
+ ).execute.each do |pdf|
109
+ labels << pdf
110
+ end
111
+
112
+ labels
113
+ end
114
+
115
+ def create_shipment(sender:, receiver:, parcels:, reference_code:, collection_point: nil, shipment_date: nil, remarks: nil, **)
116
+ Shipments::Create.new(
117
+ sender: sender,
118
+ receiver: receiver,
119
+ collection_point: collection_point,
120
+ shipment_date: shipment_date,
121
+ parcels: parcels,
122
+ reference_code: reference_code,
123
+ remarks: remarks
124
+ ).execute
125
+ end
126
+
127
+ def create_pickup(sender:, receiver:, parcels:, reference_code:,
128
+ pickup_date: nil, remarks: nil, **)
129
+ time_interval = nil
130
+ begin
131
+ params = Pickups::Create::FormatParams.new(
132
+ sender: sender.courierize(:correos_express),
133
+ receiver: receiver.courierize(:correos_express),
134
+ parcels: parcels,
135
+ reference_code: reference_code,
136
+ pickup_date: pickup_date,
137
+ remarks: remarks,
138
+ time_interval: time_interval
139
+ ).execute
140
+
141
+ pickup_number = Pickups::Create.new(params: params).execute
142
+ rescue InvalidTimeIntervalError => e
143
+ raise e if time_interval
144
+ raise e unless (cutoff_time = e.message[/\b(\d+):\d{2}\z/, 1])
145
+
146
+ time_interval = 9..cutoff_time.to_i
147
+ retry
148
+ end
149
+
150
+ Deliveries::Pickup.new(
151
+ courier_id: 'correos_express',
152
+ sender: sender,
153
+ receiver: receiver,
154
+ parcels: parcels,
155
+ reference_code: reference_code,
156
+ tracking_code: pickup_number,
157
+ pickup_date: pickup_date
158
+ )
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,106 @@
1
+ module Deliveries
2
+ module Couriers
3
+ module Dummy
4
+ extend Courier
5
+
6
+ module_function
7
+
8
+ def get_collection_point(global_point_id:)
9
+ point = CollectionPoint.parse_global_point_id global_point_id: global_point_id
10
+ CollectionPoint.new name: 'Dummy',
11
+ email: 'dummy@dummy.com',
12
+ phone: '555 555 555',
13
+ country: point.country.to_sym,
14
+ state: 'Bizkaia',
15
+ city: 'Erandio',
16
+ street: 'Axpe',
17
+ postcode: point.postcode,
18
+ courier_id: 'dummy',
19
+ point_id: point.point_id,
20
+ latitude: 43.312132,
21
+ longitude: -2.979586,
22
+ timetable: {
23
+ 0 => [],
24
+ 1 => [OpenStruct.new(open: '8:00', close: '14:00'),
25
+ OpenStruct.new(open: '16:00', close: '20:00')],
26
+ 2 => [OpenStruct.new(open: '8:00', close: '14:00'),
27
+ OpenStruct.new(open: '16:00', close: '20:00')],
28
+ 3 => [OpenStruct.new(open: '8:00', close: '14:00'),
29
+ OpenStruct.new(open: '16:00', close: '20:00')],
30
+ 4 => [OpenStruct.new(open: '8:00', close: '14:00'),
31
+ OpenStruct.new(open: '16:00', close: '20:00')],
32
+ 5 => [OpenStruct.new(open: '8:00', close: '15:00')],
33
+ 6 => []
34
+ },
35
+ url_map: nil,
36
+ url_photo: nil
37
+ end
38
+
39
+ def get_collection_points(country:, postcode:)
40
+ [
41
+ get_collection_point(global_point_id: "dummy~#{country}~#{postcode}~1"),
42
+ get_collection_point(global_point_id: "dummy~#{country}~#{postcode}~2")
43
+ ]
44
+ end
45
+
46
+ def shipment_info(tracking_code:, **)
47
+ TrackingInfo.new courier_id: 'dummy',
48
+ tracking_code: tracking_code,
49
+ status: :in_transit,
50
+ checkpoints: [
51
+ Checkpoint.new(status: :registered, location: 'Source city', tracked_at: 1.week.ago,
52
+ description: 'Parcel taken'),
53
+ Checkpoint.new(status: :in_transit, location: 'Halfway city', tracked_at: 1.day.ago,
54
+ description: 'There is some delay')
55
+ ],
56
+ url: 'https://google.com'
57
+ end
58
+
59
+ def pickup_info(tracking_code:, **)
60
+ TrackingInfo.new courier_id: 'dummy',
61
+ tracking_code: tracking_code,
62
+ status: :in_transit,
63
+ checkpoints: [
64
+ Checkpoint.new(status: :registered, location: 'Source city', tracked_at: 1.week.ago,
65
+ description: 'Parcel taken'),
66
+ Checkpoint.new(status: :in_transit, location: 'Halfway city', tracked_at: 1.day.ago,
67
+ description: 'There is some delay')
68
+ ],
69
+ url: 'https://google.com'
70
+ end
71
+
72
+ def create_shipment(sender:, receiver:, parcels:, reference_code:, shipment_date: nil, **)
73
+ Shipment.new courier_id: 'dummy',
74
+ sender: sender,
75
+ receiver: receiver,
76
+ parcels: parcels,
77
+ reference_code: reference_code,
78
+ tracking_code: reference_code,
79
+ shipment_date: shipment_date
80
+ end
81
+
82
+ def create_pickup(sender:, receiver:, parcels:, reference_code:, pickup_date: nil, **)
83
+ Pickup.new courier_id: 'dummy',
84
+ sender: sender,
85
+ receiver: receiver,
86
+ parcels: parcels,
87
+ reference_code: reference_code,
88
+ tracking_code: reference_code,
89
+ pickup_date: pickup_date
90
+ end
91
+
92
+ def get_label(**)
93
+ pdf = <<~PDF
94
+ %PDF-1.0
95
+ 1 0 obj<</Pages 2 0 R>>endobj 2 0 obj<</Kids[3 0 R]/Count 1>>endobj 3 0 obj<</MediaBox[0 0 3 3]>>endobj
96
+ trailer<</Root 1 0 R>>
97
+ PDF
98
+ Deliveries::Label.new(raw: pdf)
99
+ end
100
+
101
+ def get_labels(tracking_codes:, language: nil)
102
+ get_label tracking_code: tracking_codes, language: language
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,31 @@
1
+ module Deliveries
2
+ module Couriers
3
+ module MondialRelay
4
+ class Address < Deliveries::Address
5
+ def name
6
+ I18n.transliterate(@name.to_s).gsub(%r{[^0-9A-Z_\-'., /]}i, '').upcase.truncate(32, omission: '')
7
+ end
8
+
9
+ def street
10
+ I18n.transliterate(@street.to_s).gsub(%r{[^0-9A-Z_\-'., /]}i, '').upcase.truncate(32, omission: '')
11
+ end
12
+
13
+ def city
14
+ I18n.transliterate(@city.to_s).gsub(/[^A-Z_\-' ]/i, '').upcase.truncate(25, omission: '')
15
+ end
16
+
17
+ def country
18
+ @country.to_s.upcase
19
+ end
20
+
21
+ def phone
22
+ format_international_phone
23
+ end
24
+
25
+ def email
26
+ format_email
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,103 @@
1
+ # :stat=>"0",
2
+ # :num=>"070912",
3
+ # :lg_adr1=>"VENANDCOM ",
4
+ # :lg_adr2=>nil,
5
+ # :lg_adr3=>"CL LANGILERIA 126 ",
6
+ # :lg_adr4=>nil,
7
+ # :cp=>"48940",
8
+ # :ville=>"LEIOA ",
9
+ # :pays=>"ES",
10
+ # :localisation1=>"ENTRE METRO LAMIACO Y ROMO ",
11
+ # :localisation2=>nil,
12
+ # :horaires_lundi=>{:string=>["1000", "1330", "1700", "2000"]},
13
+ # :horaires_mardi=>{:string=>["1000", "1330", "1700", "2000"]},
14
+ # :horaires_mercredi=>{:string=>["1000", "1330", "1700", "2000"]},
15
+ # :horaires_jeudi=>{:string=>["1000", "1330", "1700", "2000"]},
16
+ # :horaires_vendredi=>{:string=>["1000", "1330", "1700", "2000"]},
17
+ # :horaires_samedi=>{:string=>["1000", "1330", "0000", "0000"]},
18
+ # :horaires_dimanche=>{:string=>["0000", "0000", "0000", "0000"]},
19
+ # :information=>nil,
20
+ # :url_photo=>"https://ww2.mondialrelay.com/public/permanent/photo_relais.aspx?ens=CC______41&num=070912&pays=ES&crc=17576B6AE31EA66C7C66463BCDD955A4",
21
+ # :url_plan=>"https://ww2.mondialrelay.com/public/permanent/plan_relais.aspx?ens=test11&num=070912&pays=ES&crc=4F83CD143A1506242F025F9C11311A3C"}
22
+
23
+ # Deliveries.courier('mondial_relay').get_collection_point(point_id: 23191, country: 'ES')
24
+
25
+ module Deliveries
26
+ module Couriers
27
+ module MondialRelay
28
+ module CollectionPoints
29
+ class Search
30
+ class FormatResponse
31
+ WEEKDAYS_STARTING_SUNDAY = %w[
32
+ dimanche
33
+ lundi
34
+ mardi
35
+ mercredi
36
+ jeudi
37
+ vendredi
38
+ samedi
39
+ ].freeze
40
+
41
+ attr_accessor :response
42
+
43
+ def initialize(response:)
44
+ self.response = response
45
+ end
46
+
47
+ def execute
48
+ point = {}
49
+
50
+ point[:courier_id] = 'mondial_relay'
51
+ point[:point_id] = response[:num]
52
+ point[:country] = response[:pays]
53
+ point[:city] = response[:ville].strip unless response[:ville].nil?
54
+ point[:postcode] = response[:cp].strip unless response[:cp].nil?
55
+ point[:url_map] = response[:url_plan]
56
+ point[:latitude] = response[:latitude].tr(',', '.').to_f
57
+ point[:longitude] = response[:longitude].tr(',', '.').to_f
58
+ point[:timetable] = formatted_timetable(response)
59
+ point[:url_photo] = response[:url_photo]
60
+ point[:name] = response[:lg_adr1].strip
61
+ point[:street] = response[:lg_adr3].strip
62
+
63
+ point
64
+ end
65
+
66
+ private
67
+
68
+ def formatted_timetable(result)
69
+ timetable = {}
70
+
71
+ week_hours = get_week_hours_from_result(result)
72
+ week_hours.each do |i, times|
73
+ timetable[i] = []
74
+ if times[0] == '0000'
75
+ timetable[i] << nil
76
+ else
77
+ timetable[i] << formatted_slot(open: times[0], close: times[1])
78
+ timetable[i] << formatted_slot(open: times[2], close: times[3]) if times[2] != '0000'
79
+ end
80
+ timetable[i] = nil unless timetable[i].any?
81
+ end
82
+
83
+ timetable
84
+ end
85
+
86
+ def get_week_hours_from_result(result)
87
+ week_hours = {}
88
+ WEEKDAYS_STARTING_SUNDAY.each_with_index do |day, i|
89
+ week_hours[i] = result["horaires_#{day}".to_sym][:string]
90
+ end
91
+
92
+ week_hours
93
+ end
94
+
95
+ def formatted_slot(open:, close:)
96
+ OpenStruct.new(open: open.insert(2, ':'), close: close.insert(2, ':'))
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,40 @@
1
+ module Deliveries
2
+ module Couriers
3
+ module MondialRelay
4
+ module Labels
5
+ class Generate
6
+ attr_accessor :tracking_codes, :language
7
+
8
+ def initialize(tracking_codes:, language:)
9
+ self.tracking_codes = [tracking_codes].flatten.join(';')
10
+ self.language = language.to_s.upcase
11
+ end
12
+
13
+ def execute
14
+ params = {
15
+ 'Enseigne' => Deliveries::Couriers::MondialRelay.config(:mondial_relay_merchant),
16
+ 'Expeditions' => tracking_codes,
17
+ 'Langue' => language
18
+ }
19
+
20
+ # Calculate security parameters.
21
+ params['Security'] = Deliveries::Couriers::MondialRelay.calculate_security_param params
22
+
23
+ response = MondialRelay.api_client.call :wsi3_get_etiquettes, message: params
24
+ response_stat = response.body.dig(:wsi3_get_etiquettes_response, :wsi3_get_etiquettes_result, :stat)
25
+ if response_stat == '0'
26
+ # Get path for 10x15 format.
27
+ "http://www.mondialrelay.com#{response.body.dig(:wsi3_get_etiquettes_response,
28
+ :wsi3_get_etiquettes_result, :url_pdf_10x15)}"
29
+ else
30
+ raise Deliveries::APIError.new(
31
+ StatusCodes.message_for(response_stat.to_i),
32
+ response_stat
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end