line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Shipment::UPS; |
2
|
|
|
|
|
|
|
$Shipment::UPS::VERSION = '2.00'; |
3
|
3
|
|
|
3
|
|
119175
|
use strict; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
163
|
|
4
|
3
|
|
|
3
|
|
22
|
use warnings; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
126
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
|
7
|
3
|
|
|
3
|
|
2409
|
use Try::Tiny; |
|
3
|
|
|
|
|
10297
|
|
|
3
|
|
|
|
|
259
|
|
8
|
3
|
|
|
3
|
|
21962
|
use Shipment::SOAP::WSDL; |
|
3
|
|
|
|
|
14
|
|
|
3
|
|
|
|
|
142
|
|
9
|
3
|
|
|
3
|
|
58
|
use Moo; |
|
3
|
|
|
|
|
1027
|
|
|
3
|
|
|
|
|
24
|
|
10
|
3
|
|
|
3
|
|
3118
|
use MooX::Types::MooseLike::Base qw(:all); |
|
3
|
|
|
|
|
30130
|
|
|
3
|
|
|
|
|
1589
|
|
11
|
3
|
|
|
3
|
|
63
|
use namespace::clean; |
|
3
|
|
|
|
|
17554
|
|
|
3
|
|
|
|
|
27
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
extends 'Shipment::Base'; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
has 'username' => ( |
17
|
|
|
|
|
|
|
is => 'rw', |
18
|
|
|
|
|
|
|
isa => Str, |
19
|
|
|
|
|
|
|
); |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
has 'password' => ( |
22
|
|
|
|
|
|
|
is => 'rw', |
23
|
|
|
|
|
|
|
isa => Str, |
24
|
|
|
|
|
|
|
); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
has 'key' => ( |
27
|
|
|
|
|
|
|
is => 'rw', |
28
|
|
|
|
|
|
|
isa => Str, |
29
|
|
|
|
|
|
|
); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
has 'proxy_domain' => ( |
33
|
|
|
|
|
|
|
is => 'rw', |
34
|
|
|
|
|
|
|
isa => Enum [ |
35
|
|
|
|
|
|
|
qw( |
36
|
|
|
|
|
|
|
wwwcie.ups.com |
37
|
|
|
|
|
|
|
onlinetools.ups.com |
38
|
|
|
|
|
|
|
) |
39
|
|
|
|
|
|
|
], |
40
|
|
|
|
|
|
|
default => 'wwwcie.ups.com', |
41
|
|
|
|
|
|
|
); |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
has 'negotiated_rates' => ( |
45
|
|
|
|
|
|
|
is => 'rw', |
46
|
|
|
|
|
|
|
isa => Bool, |
47
|
|
|
|
|
|
|
default => 0, |
48
|
|
|
|
|
|
|
); |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
has 'residential_address' => ( |
52
|
|
|
|
|
|
|
is => 'rw', |
53
|
|
|
|
|
|
|
isa => Bool, |
54
|
|
|
|
|
|
|
default => 0, |
55
|
|
|
|
|
|
|
); |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
has 'address_validation' => ( |
59
|
|
|
|
|
|
|
is => 'rw', |
60
|
|
|
|
|
|
|
isa => Bool, |
61
|
|
|
|
|
|
|
default => 1, |
62
|
|
|
|
|
|
|
); |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
has 'label_height' => ( |
66
|
|
|
|
|
|
|
is => 'rw', |
67
|
|
|
|
|
|
|
isa => Enum [qw( 6 8 )], |
68
|
|
|
|
|
|
|
default => 6, |
69
|
|
|
|
|
|
|
); |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
has 'control_log_receipt' => ( |
73
|
|
|
|
|
|
|
is => 'rw', |
74
|
|
|
|
|
|
|
isa => InstanceOf ['Shipment::Label'], |
75
|
|
|
|
|
|
|
); |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
has 'carbon_neutral' => ( |
79
|
|
|
|
|
|
|
is => 'rw', |
80
|
|
|
|
|
|
|
isa => Bool, |
81
|
|
|
|
|
|
|
default => undef, |
82
|
|
|
|
|
|
|
); |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
my %service_map = ( |
86
|
|
|
|
|
|
|
'01' => 'UPS Next Day Air', |
87
|
|
|
|
|
|
|
'02' => 'UPS Second Day Air', |
88
|
|
|
|
|
|
|
'03' => 'UPS Ground', |
89
|
|
|
|
|
|
|
'07' => 'UPS Worldwide Express', |
90
|
|
|
|
|
|
|
'08' => 'UPS Worldwide Expedited', |
91
|
|
|
|
|
|
|
'11' => 'UPS Standard', |
92
|
|
|
|
|
|
|
'12' => 'UPS Three-Day Select', |
93
|
|
|
|
|
|
|
'13' => 'UPS Next Day Air Saver', |
94
|
|
|
|
|
|
|
'14' => 'UPS Next Day Air Early A.M.', |
95
|
|
|
|
|
|
|
'54' => 'UPS Worldwide Express Plus', |
96
|
|
|
|
|
|
|
'59' => 'UPS Second Day Air A.M.', |
97
|
|
|
|
|
|
|
'65' => 'UPS Saver', |
98
|
|
|
|
|
|
|
'82' => 'UPS Today Standard', |
99
|
|
|
|
|
|
|
'83' => 'UPS Today Dedicated Courier', |
100
|
|
|
|
|
|
|
'85' => 'UPS Today Express', |
101
|
|
|
|
|
|
|
'86' => 'UPS Today Express Saver', |
102
|
|
|
|
|
|
|
'93' => 'UPS SurePost 1 lb or Greater', |
103
|
|
|
|
|
|
|
'CA' => { |
104
|
|
|
|
|
|
|
'01' => 'UPS Express', |
105
|
|
|
|
|
|
|
'13' => 'UPS Express Saver', |
106
|
|
|
|
|
|
|
'65' => 'UPS Worldwide Express Saver', |
107
|
|
|
|
|
|
|
'02' => 'UPS Expedited', |
108
|
|
|
|
|
|
|
}, |
109
|
|
|
|
|
|
|
); |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
## Rating code to Shipping code map for cases when they differ |
112
|
|
|
|
|
|
|
my %service_code_map = ( |
113
|
|
|
|
|
|
|
'CA' => { |
114
|
|
|
|
|
|
|
'07' => '01', |
115
|
|
|
|
|
|
|
'13' => '65', |
116
|
|
|
|
|
|
|
'02' => '08', |
117
|
|
|
|
|
|
|
}, |
118
|
|
|
|
|
|
|
); |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
my %bill_type_map = ( |
122
|
|
|
|
|
|
|
'sender' => 'BillShipper', |
123
|
|
|
|
|
|
|
'recipient' => 'BillReceiver', |
124
|
|
|
|
|
|
|
'third_party' => 'BillThirdParty', |
125
|
|
|
|
|
|
|
); |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
my %signature_type_map = ( |
128
|
|
|
|
|
|
|
'default' => '1', |
129
|
|
|
|
|
|
|
'required' => '2', |
130
|
|
|
|
|
|
|
'not_required' => undef, |
131
|
|
|
|
|
|
|
'adult' => '3', |
132
|
|
|
|
|
|
|
); |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
my %package_type_map = ( |
135
|
|
|
|
|
|
|
'custom' => '02', |
136
|
|
|
|
|
|
|
'envelope' => '01', |
137
|
|
|
|
|
|
|
'tube' => '03', |
138
|
|
|
|
|
|
|
'box' => '21', |
139
|
|
|
|
|
|
|
'pack' => '04', |
140
|
|
|
|
|
|
|
'25kg_box' => '24', |
141
|
|
|
|
|
|
|
'10kg_box' => '25', |
142
|
|
|
|
|
|
|
'pallet' => '30', |
143
|
|
|
|
|
|
|
'small_express_box' => '2a', |
144
|
|
|
|
|
|
|
'medium_express_box' => '2b', |
145
|
|
|
|
|
|
|
'large_express_box' => '2c', |
146
|
|
|
|
|
|
|
); |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
my %units_type_map = ( |
149
|
|
|
|
|
|
|
'lb' => 'LBS', |
150
|
|
|
|
|
|
|
'kg' => 'KGS', |
151
|
|
|
|
|
|
|
'in' => 'IN', |
152
|
|
|
|
|
|
|
'cm' => 'CM', |
153
|
|
|
|
|
|
|
); |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
has '+package_type' => ( |
157
|
|
|
|
|
|
|
isa => Enum [ |
158
|
|
|
|
|
|
|
qw( custom envelope tube box pack 25kg_box 10kg_box pallet small_express_box medium_express_box large_express_box ) |
159
|
|
|
|
|
|
|
] |
160
|
|
|
|
|
|
|
); |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
my %printer_type_map = ( |
163
|
|
|
|
|
|
|
'pdf' => '', |
164
|
|
|
|
|
|
|
'thermal' => 'EPL', |
165
|
|
|
|
|
|
|
'image' => 'GIF', |
166
|
|
|
|
|
|
|
'ZPL' => 'ZPL', |
167
|
|
|
|
|
|
|
'SPL' => 'SPL', |
168
|
|
|
|
|
|
|
'STARPL' => 'STARPL', |
169
|
|
|
|
|
|
|
); |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
my %label_content_type_map = ( |
172
|
|
|
|
|
|
|
'thermal' => 'text/ups-epl', |
173
|
|
|
|
|
|
|
'image' => 'image/gif', |
174
|
|
|
|
|
|
|
'ZPL' => 'text/ups-zpl', |
175
|
|
|
|
|
|
|
'SPL' => 'text/ups-spl', |
176
|
|
|
|
|
|
|
'STARPL' => 'text/ups-starpl', |
177
|
|
|
|
|
|
|
); |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
# FIXME: check whether this is needed: |
181
|
|
|
|
|
|
|
#enum 'PrinterOptions' => [qw( thermal image ZPL SPL STARPL )]; |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
has '+printer_type' => (default => 'image',); |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
has '+currency' => (default => 'USD',); |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
has 'surepost' => ( |
190
|
|
|
|
|
|
|
is => 'rw', |
191
|
|
|
|
|
|
|
isa => Bool, |
192
|
|
|
|
|
|
|
default => undef, |
193
|
|
|
|
|
|
|
); |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub _build_services { |
197
|
|
|
|
|
|
|
my $self = shift; |
198
|
|
|
|
|
|
|
|
199
|
3
|
|
|
3
|
|
4446
|
use Shipment::Package; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
use Shipment::Service; |
201
|
|
|
|
|
|
|
use Shipment::UPS::WSDL::RateInterfaces::RateService::RatePort; |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
my $interface = |
204
|
|
|
|
|
|
|
Shipment::UPS::WSDL::RateInterfaces::RateService::RatePort->new( |
205
|
|
|
|
|
|
|
{proxy_domain => $self->proxy_domain,}); |
206
|
|
|
|
|
|
|
my $response; |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
my $options; |
209
|
|
|
|
|
|
|
$options->{DeliveryConfirmation}->{DCISType} = |
210
|
|
|
|
|
|
|
$signature_type_map{$self->signature_type} |
211
|
|
|
|
|
|
|
if defined $signature_type_map{$self->signature_type}; |
212
|
|
|
|
|
|
|
$options->{DeclaredValue}->{CurrencyCode} = $self->currency; |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
my $rating_options; |
215
|
|
|
|
|
|
|
$rating_options->{NegotiatedRatesIndicator} = 1 if $self->negotiated_rates; |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
my $shipment_options; |
218
|
|
|
|
|
|
|
$shipment_options->{UPScarbonneutralIndicator} = '' |
219
|
|
|
|
|
|
|
if $self->carbon_neutral; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
my @pieces; |
222
|
|
|
|
|
|
|
foreach (@{$self->packages}) { |
223
|
|
|
|
|
|
|
$options->{DeclaredValue}->{MonetaryValue} = $_->insured_value->value; |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
## SurePost doesn't accept service options |
226
|
|
|
|
|
|
|
$options = undef if $self->surepost; |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
push @pieces, |
229
|
|
|
|
|
|
|
{ PackagingType => { |
230
|
|
|
|
|
|
|
Code => $package_type_map{$self->package_type} |
231
|
|
|
|
|
|
|
|| $self->package_type, |
232
|
|
|
|
|
|
|
}, |
233
|
|
|
|
|
|
|
Dimensions => { |
234
|
|
|
|
|
|
|
UnitOfMeasurement => { |
235
|
|
|
|
|
|
|
Code => $units_type_map{$self->dim_unit} |
236
|
|
|
|
|
|
|
|| $self->dim_unit, |
237
|
|
|
|
|
|
|
}, |
238
|
|
|
|
|
|
|
Length => $_->length, |
239
|
|
|
|
|
|
|
Width => $_->width, |
240
|
|
|
|
|
|
|
Height => $_->height, |
241
|
|
|
|
|
|
|
}, |
242
|
|
|
|
|
|
|
PackageWeight => { |
243
|
|
|
|
|
|
|
UnitOfMeasurement => { |
244
|
|
|
|
|
|
|
Code => $units_type_map{$self->weight_unit} |
245
|
|
|
|
|
|
|
|| $self->weight_unit, |
246
|
|
|
|
|
|
|
}, |
247
|
|
|
|
|
|
|
Weight => $_->weight, |
248
|
|
|
|
|
|
|
}, |
249
|
|
|
|
|
|
|
PackageServiceOptions => $options, |
250
|
|
|
|
|
|
|
}; |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
my @from_addresslines = ( |
254
|
|
|
|
|
|
|
$self->from_address->address1, |
255
|
|
|
|
|
|
|
$self->from_address->address2, |
256
|
|
|
|
|
|
|
$self->from_address->address3 |
257
|
|
|
|
|
|
|
); |
258
|
|
|
|
|
|
|
my @to_addresslines = ( |
259
|
|
|
|
|
|
|
$self->to_address->address1, |
260
|
|
|
|
|
|
|
$self->to_address->address2, |
261
|
|
|
|
|
|
|
$self->to_address->address3 |
262
|
|
|
|
|
|
|
); |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
my $shipto = { |
265
|
|
|
|
|
|
|
Address => { |
266
|
|
|
|
|
|
|
AddressLine => \@to_addresslines, |
267
|
|
|
|
|
|
|
City => $self->to_address()->city, |
268
|
|
|
|
|
|
|
StateProvinceCode => $self->to_address()->province_code, |
269
|
|
|
|
|
|
|
PostalCode => $self->to_address()->postal_code, |
270
|
|
|
|
|
|
|
CountryCode => $self->to_address()->country_code, |
271
|
|
|
|
|
|
|
}, |
272
|
|
|
|
|
|
|
}; |
273
|
|
|
|
|
|
|
$shipto->{Address}->{ResidentialAddressIndicator} = 1 |
274
|
|
|
|
|
|
|
if $self->{residential_address}; |
275
|
|
|
|
|
|
|
$shipto->{Phone}{Number} = $self->to_address->phone |
276
|
|
|
|
|
|
|
if $self->to_address->phone; |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
my %services; |
279
|
|
|
|
|
|
|
try { |
280
|
|
|
|
|
|
|
$response = $interface->ProcessRate( |
281
|
|
|
|
|
|
|
{ Request => {RequestOption => 'Shop',}, |
282
|
|
|
|
|
|
|
Shipment => { |
283
|
|
|
|
|
|
|
Shipper => { |
284
|
|
|
|
|
|
|
ShipperNumber => $self->account, |
285
|
|
|
|
|
|
|
Address => { |
286
|
|
|
|
|
|
|
AddressLine => \@from_addresslines, |
287
|
|
|
|
|
|
|
City => $self->from_address()->city, |
288
|
|
|
|
|
|
|
StateProvinceCode => |
289
|
|
|
|
|
|
|
$self->from_address()->province_code, |
290
|
|
|
|
|
|
|
PostalCode => $self->from_address()->postal_code, |
291
|
|
|
|
|
|
|
CountryCode => $self->from_address()->country_code, |
292
|
|
|
|
|
|
|
}, |
293
|
|
|
|
|
|
|
}, |
294
|
|
|
|
|
|
|
ShipTo => $shipto, |
295
|
|
|
|
|
|
|
ShipmentRatingOptions => $rating_options, |
296
|
|
|
|
|
|
|
Package => \@pieces, |
297
|
|
|
|
|
|
|
ShipmentServiceOptions => $shipment_options, |
298
|
|
|
|
|
|
|
}, |
299
|
|
|
|
|
|
|
}, |
300
|
|
|
|
|
|
|
{ UsernameToken => { |
301
|
|
|
|
|
|
|
Username => $self->username, |
302
|
|
|
|
|
|
|
Password => $self->password, |
303
|
|
|
|
|
|
|
}, |
304
|
|
|
|
|
|
|
ServiceAccessToken => {AccessLicenseNumber => $self->key,}, |
305
|
|
|
|
|
|
|
}, |
306
|
|
|
|
|
|
|
); |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
#warn $response; |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
foreach my $service (@{$response->get_RatedShipment()}) { |
311
|
|
|
|
|
|
|
my $rate = $service->get_TotalCharges->get_MonetaryValue; |
312
|
|
|
|
|
|
|
my $currency = $service->get_TotalCharges->get_CurrencyCode; |
313
|
|
|
|
|
|
|
if ($self->negotiated_rates) { |
314
|
|
|
|
|
|
|
if ($service->get_NegotiatedRateCharges) { |
315
|
|
|
|
|
|
|
$rate = |
316
|
|
|
|
|
|
|
$service->get_NegotiatedRateCharges->get_TotalCharge |
317
|
|
|
|
|
|
|
->get_MonetaryValue; |
318
|
|
|
|
|
|
|
$currency = |
319
|
|
|
|
|
|
|
$service->get_NegotiatedRateCharges->get_TotalCharge |
320
|
|
|
|
|
|
|
->get_CurrencyCode; |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
$services{$service->get_Service()->get_Code()->get_value} = |
324
|
|
|
|
|
|
|
Shipment::Service->new( |
325
|
|
|
|
|
|
|
id => $service->get_Service()->get_Code()->get_value, |
326
|
|
|
|
|
|
|
name => ( |
327
|
|
|
|
|
|
|
$service_map{$self->from_address()->country_code} |
328
|
|
|
|
|
|
|
->{$service->get_Service()->get_Code()->get_value} |
329
|
|
|
|
|
|
|
|| $service_map{$service->get_Service()->get_Code() |
330
|
|
|
|
|
|
|
->get_value} |
331
|
|
|
|
|
|
|
), |
332
|
|
|
|
|
|
|
cost => Data::Currency->new($rate, $currency), |
333
|
|
|
|
|
|
|
); |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
$services{ground} = $services{'03'} || $services{'11'} || undef; |
336
|
|
|
|
|
|
|
$services{express} = |
337
|
|
|
|
|
|
|
$services{'02'} || $services{'13'} || $services{'65'} || undef; |
338
|
|
|
|
|
|
|
$services{priority} = $services{'01'} || undef; |
339
|
|
|
|
|
|
|
foreach (qw/ground express priority/) { |
340
|
|
|
|
|
|
|
delete $services{$_} if !$services{$_}; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
$self->notice(''); |
344
|
|
|
|
|
|
|
if ($response->get_Response->get_Alert) { |
345
|
|
|
|
|
|
|
foreach my $alert (@{$response->get_Response->get_Alert}) { |
346
|
|
|
|
|
|
|
warn "Notice: " . $alert->get_Description->get_value; |
347
|
|
|
|
|
|
|
$self->add_notice($alert->get_Description->get_value . "\n"); |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
catch { |
353
|
|
|
|
|
|
|
#warn $_; |
354
|
|
|
|
|
|
|
try { |
355
|
|
|
|
|
|
|
warn "Error: " |
356
|
|
|
|
|
|
|
. $response->get_detail()->get_Errors()->get_ErrorDetail() |
357
|
|
|
|
|
|
|
->get_PrimaryErrorCode()->get_Description; |
358
|
|
|
|
|
|
|
$self->error( |
359
|
|
|
|
|
|
|
$response->get_detail()->get_Errors()->get_ErrorDetail() |
360
|
|
|
|
|
|
|
->get_PrimaryErrorCode()->get_Description->get_value); |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
catch { |
363
|
|
|
|
|
|
|
#warn $_; |
364
|
|
|
|
|
|
|
warn "Error: " . $response->get_faultstring; |
365
|
|
|
|
|
|
|
$self->error($response->get_faultstring->get_value); |
366
|
|
|
|
|
|
|
}; |
367
|
|
|
|
|
|
|
}; |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
if ($self->surepost) { |
370
|
|
|
|
|
|
|
if ($self->error) { |
371
|
|
|
|
|
|
|
$self->add_notice( |
372
|
|
|
|
|
|
|
'All services other than SurePost failed due to error: ' |
373
|
|
|
|
|
|
|
. $self->error |
374
|
|
|
|
|
|
|
. "\n"); |
375
|
|
|
|
|
|
|
$self->error(''); |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
$services{93} = Shipment::Service->new( |
378
|
|
|
|
|
|
|
id => '93', |
379
|
|
|
|
|
|
|
name => $service_map{93}, |
380
|
|
|
|
|
|
|
); |
381
|
|
|
|
|
|
|
$services{surepost} = $services{93}; |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
\%services; |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
sub rate { |
389
|
|
|
|
|
|
|
my ($self, $service_id) = @_; |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
try { |
392
|
|
|
|
|
|
|
$service_id = $self->services->{$service_id}->id; |
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
catch { |
395
|
|
|
|
|
|
|
#warn $_; |
396
|
|
|
|
|
|
|
warn "service ($service_id) not available"; |
397
|
|
|
|
|
|
|
$self->error("service ($service_id) not available"); |
398
|
|
|
|
|
|
|
$service_id = ''; |
399
|
|
|
|
|
|
|
}; |
400
|
|
|
|
|
|
|
return unless $service_id; |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
my $options; |
403
|
|
|
|
|
|
|
$options->{DeliveryConfirmation}->{DCISType} = |
404
|
|
|
|
|
|
|
$signature_type_map{$self->signature_type} |
405
|
|
|
|
|
|
|
if defined $signature_type_map{$self->signature_type}; |
406
|
|
|
|
|
|
|
$options->{DeclaredValue}->{CurrencyCode} = $self->currency; |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
my $rating_options; |
409
|
|
|
|
|
|
|
$rating_options->{NegotiatedRatesIndicator} = 1 if $self->negotiated_rates; |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
my $shipment_options; |
412
|
|
|
|
|
|
|
$shipment_options->{UPScarbonneutralIndicator} = '' |
413
|
|
|
|
|
|
|
if $self->carbon_neutral; |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
my @pieces; |
416
|
|
|
|
|
|
|
foreach (@{$self->packages}) { |
417
|
|
|
|
|
|
|
$options->{DeclaredValue}->{MonetaryValue} = $_->insured_value->value; |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
## SurePost doesn't accept service options |
420
|
|
|
|
|
|
|
$options = undef if $self->surepost && $service_id eq '93'; |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
push @pieces, |
423
|
|
|
|
|
|
|
{ PackagingType => { |
424
|
|
|
|
|
|
|
Code => $package_type_map{$self->package_type} |
425
|
|
|
|
|
|
|
|| $self->package_type, |
426
|
|
|
|
|
|
|
}, |
427
|
|
|
|
|
|
|
Dimensions => { |
428
|
|
|
|
|
|
|
UnitOfMeasurement => { |
429
|
|
|
|
|
|
|
Code => $units_type_map{$self->dim_unit} |
430
|
|
|
|
|
|
|
|| $self->dim_unit, |
431
|
|
|
|
|
|
|
}, |
432
|
|
|
|
|
|
|
Length => $_->length, |
433
|
|
|
|
|
|
|
Width => $_->width, |
434
|
|
|
|
|
|
|
Height => $_->height, |
435
|
|
|
|
|
|
|
}, |
436
|
|
|
|
|
|
|
PackageWeight => { |
437
|
|
|
|
|
|
|
UnitOfMeasurement => { |
438
|
|
|
|
|
|
|
Code => $units_type_map{$self->weight_unit} |
439
|
|
|
|
|
|
|
|| $self->weight_unit, |
440
|
|
|
|
|
|
|
}, |
441
|
|
|
|
|
|
|
Weight => $_->weight, |
442
|
|
|
|
|
|
|
}, |
443
|
|
|
|
|
|
|
PackageServiceOptions => $options, |
444
|
|
|
|
|
|
|
}; |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
my @from_addresslines = ( |
448
|
|
|
|
|
|
|
$self->from_address->address1, |
449
|
|
|
|
|
|
|
$self->from_address->address2, |
450
|
|
|
|
|
|
|
$self->from_address->address3 |
451
|
|
|
|
|
|
|
); |
452
|
|
|
|
|
|
|
my @to_addresslines = ( |
453
|
|
|
|
|
|
|
$self->to_address->address1, |
454
|
|
|
|
|
|
|
$self->to_address->address2, |
455
|
|
|
|
|
|
|
$self->to_address->address3 |
456
|
|
|
|
|
|
|
); |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
my $shipto = { |
460
|
|
|
|
|
|
|
Address => { |
461
|
|
|
|
|
|
|
AddressLine => \@to_addresslines, |
462
|
|
|
|
|
|
|
City => $self->to_address()->city, |
463
|
|
|
|
|
|
|
StateProvinceCode => $self->to_address()->province_code, |
464
|
|
|
|
|
|
|
PostalCode => $self->to_address()->postal_code, |
465
|
|
|
|
|
|
|
CountryCode => $self->to_address()->country_code, |
466
|
|
|
|
|
|
|
}, |
467
|
|
|
|
|
|
|
}; |
468
|
|
|
|
|
|
|
$shipto->{Address}->{ResidentialAddressIndicator} = 1 |
469
|
|
|
|
|
|
|
if $self->{residential_address}; |
470
|
|
|
|
|
|
|
$shipto->{Phone}{Number} = $self->to_address->phone |
471
|
|
|
|
|
|
|
if $self->to_address->phone; |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
use Shipment::UPS::WSDL::RateInterfaces::RateService::RatePort; |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
my $interface = |
476
|
|
|
|
|
|
|
Shipment::UPS::WSDL::RateInterfaces::RateService::RatePort->new( |
477
|
|
|
|
|
|
|
{proxy_domain => $self->proxy_domain,}); |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
my $response; |
480
|
|
|
|
|
|
|
try { |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
$response = $interface->ProcessRate( |
483
|
|
|
|
|
|
|
{ Request => {RequestOption => 'Rate',}, |
484
|
|
|
|
|
|
|
Shipment => { |
485
|
|
|
|
|
|
|
Shipper => { |
486
|
|
|
|
|
|
|
ShipperNumber => $self->account, |
487
|
|
|
|
|
|
|
Address => { |
488
|
|
|
|
|
|
|
AddressLine => \@from_addresslines, |
489
|
|
|
|
|
|
|
City => $self->from_address->city, |
490
|
|
|
|
|
|
|
StateProvinceCode => |
491
|
|
|
|
|
|
|
$self->from_address->province_code, |
492
|
|
|
|
|
|
|
PostalCode => $self->from_address->postal_code, |
493
|
|
|
|
|
|
|
CountryCode => $self->from_address->country_code, |
494
|
|
|
|
|
|
|
}, |
495
|
|
|
|
|
|
|
}, |
496
|
|
|
|
|
|
|
ShipTo => $shipto, |
497
|
|
|
|
|
|
|
ShipmentRatingOptions => $rating_options, |
498
|
|
|
|
|
|
|
Service => {Code => $service_id,}, |
499
|
|
|
|
|
|
|
Package => \@pieces, |
500
|
|
|
|
|
|
|
ShipmentServiceOptions => $shipment_options, |
501
|
|
|
|
|
|
|
}, |
502
|
|
|
|
|
|
|
}, |
503
|
|
|
|
|
|
|
{ UsernameToken => { |
504
|
|
|
|
|
|
|
Username => $self->username, |
505
|
|
|
|
|
|
|
Password => $self->password, |
506
|
|
|
|
|
|
|
}, |
507
|
|
|
|
|
|
|
ServiceAccessToken => {AccessLicenseNumber => $self->key,}, |
508
|
|
|
|
|
|
|
}, |
509
|
|
|
|
|
|
|
); |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
#warn $response; |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
use Data::Currency; |
514
|
|
|
|
|
|
|
use Shipment::Service; |
515
|
|
|
|
|
|
|
my $rate = |
516
|
|
|
|
|
|
|
$response->get_RatedShipment->get_TotalCharges->get_MonetaryValue; |
517
|
|
|
|
|
|
|
my $currency = |
518
|
|
|
|
|
|
|
$response->get_RatedShipment->get_TotalCharges->get_CurrencyCode; |
519
|
|
|
|
|
|
|
if ($self->negotiated_rates) { |
520
|
|
|
|
|
|
|
if ($response->get_RatedShipment->get_NegotiatedRateCharges) { |
521
|
|
|
|
|
|
|
$rate = $response->get_RatedShipment->get_NegotiatedRateCharges |
522
|
|
|
|
|
|
|
->get_TotalCharge->get_MonetaryValue; |
523
|
|
|
|
|
|
|
$currency = |
524
|
|
|
|
|
|
|
$response->get_RatedShipment->get_NegotiatedRateCharges |
525
|
|
|
|
|
|
|
->get_TotalCharge->get_CurrencyCode; |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
$self->service( |
529
|
|
|
|
|
|
|
new Shipment::Service( |
530
|
|
|
|
|
|
|
id => $service_id, |
531
|
|
|
|
|
|
|
name => ( |
532
|
|
|
|
|
|
|
$service_map{$self->from_address()->country_code}->{ |
533
|
|
|
|
|
|
|
$response->get_RatedShipment->get_Service->get_Code |
534
|
|
|
|
|
|
|
->get_value |
535
|
|
|
|
|
|
|
} |
536
|
|
|
|
|
|
|
|| $service_map{ |
537
|
|
|
|
|
|
|
$response->get_RatedShipment->get_Service->get_Code |
538
|
|
|
|
|
|
|
->get_value |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
), |
541
|
|
|
|
|
|
|
cost => Data::Currency->new($rate, $currency), |
542
|
|
|
|
|
|
|
) |
543
|
|
|
|
|
|
|
); |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
$self->notice(''); |
546
|
|
|
|
|
|
|
if ($response->get_Response->get_Alert) { |
547
|
|
|
|
|
|
|
foreach my $alert (@{$response->get_Response->get_Alert}) { |
548
|
|
|
|
|
|
|
warn $alert->get_Description->get_value; |
549
|
|
|
|
|
|
|
$self->add_notice($alert->get_Description->get_value . "\n"); |
550
|
|
|
|
|
|
|
} |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
catch { |
554
|
|
|
|
|
|
|
#warn $_; |
555
|
|
|
|
|
|
|
try { |
556
|
|
|
|
|
|
|
warn $response->get_detail()->get_Errors()->get_ErrorDetail() |
557
|
|
|
|
|
|
|
->get_PrimaryErrorCode()->get_Description; |
558
|
|
|
|
|
|
|
$self->error( |
559
|
|
|
|
|
|
|
$response->get_detail()->get_Errors()->get_ErrorDetail() |
560
|
|
|
|
|
|
|
->get_PrimaryErrorCode()->get_Description->get_value); |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
catch { |
563
|
|
|
|
|
|
|
#warn $_; |
564
|
|
|
|
|
|
|
warn $response->get_faultstring; |
565
|
|
|
|
|
|
|
$self->error($response->get_faultstring->get_value); |
566
|
|
|
|
|
|
|
}; |
567
|
|
|
|
|
|
|
}; |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
sub ship { |
573
|
|
|
|
|
|
|
my ($self, $service_id) = @_; |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
try { |
576
|
|
|
|
|
|
|
$service_id = $self->services->{$service_id}->id; |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
catch { |
579
|
|
|
|
|
|
|
#warn $_; |
580
|
|
|
|
|
|
|
warn "service ($service_id) not available"; |
581
|
|
|
|
|
|
|
$self->error("service ($service_id) not available"); |
582
|
|
|
|
|
|
|
$service_id = ''; |
583
|
|
|
|
|
|
|
}; |
584
|
|
|
|
|
|
|
return unless $service_id; |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
my $package_options; |
587
|
|
|
|
|
|
|
$package_options->{DeliveryConfirmation}->{DCISType} = |
588
|
|
|
|
|
|
|
$signature_type_map{$self->signature_type} |
589
|
|
|
|
|
|
|
if defined $signature_type_map{$self->signature_type}; |
590
|
|
|
|
|
|
|
$package_options->{DeclaredValue}->{CurrencyCode} = $self->currency; |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
my $shipment_options; |
593
|
|
|
|
|
|
|
if ($self->to_address->email) { |
594
|
|
|
|
|
|
|
$shipment_options->{Notification}->{NotificationCode} = '6'; |
595
|
|
|
|
|
|
|
$shipment_options->{Notification}->{EMail}->{EMailAddress} = |
596
|
|
|
|
|
|
|
$self->to_address->email; |
597
|
|
|
|
|
|
|
$shipment_options->{Notification}->{EMail}->{SubjectCode} = '03'; |
598
|
|
|
|
|
|
|
} |
599
|
|
|
|
|
|
|
$shipment_options->{UPScarbonneutralIndicator} = '' |
600
|
|
|
|
|
|
|
if $self->carbon_neutral; |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
my $rating_options; |
603
|
|
|
|
|
|
|
$rating_options->{NegotiatedRatesIndicator} = 1 if $self->negotiated_rates; |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
my @pieces; |
606
|
|
|
|
|
|
|
my $reference_index = 1; |
607
|
|
|
|
|
|
|
foreach (@{$self->packages}) { |
608
|
|
|
|
|
|
|
$package_options->{DeclaredValue}->{MonetaryValue} = |
609
|
|
|
|
|
|
|
$_->insured_value->value; |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
## SurePost doesn't accept service options |
612
|
|
|
|
|
|
|
$package_options = undef if $self->surepost && $service_id eq '93'; |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
my @references; |
615
|
|
|
|
|
|
|
if ( $self->references |
616
|
|
|
|
|
|
|
&& $self->from_address->country_code =~ /(US|PR)/ |
617
|
|
|
|
|
|
|
&& $self->to_address->country_code =~ /(US|PR)/ |
618
|
|
|
|
|
|
|
&& $self->from_address->country_code eq |
619
|
|
|
|
|
|
|
$self->to_address->country_code) |
620
|
|
|
|
|
|
|
{ |
621
|
|
|
|
|
|
|
foreach ($self->get_reference(0), $self->get_reference(1)) { |
622
|
|
|
|
|
|
|
next if !$_; |
623
|
|
|
|
|
|
|
push @references, |
624
|
|
|
|
|
|
|
{ Code => $reference_index, |
625
|
|
|
|
|
|
|
Value => $_, |
626
|
|
|
|
|
|
|
}; |
627
|
|
|
|
|
|
|
$reference_index++; |
628
|
|
|
|
|
|
|
} |
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
push @pieces, |
631
|
|
|
|
|
|
|
{ Packaging => { |
632
|
|
|
|
|
|
|
Code => $package_type_map{$self->package_type} |
633
|
|
|
|
|
|
|
|| $self->package_type, |
634
|
|
|
|
|
|
|
}, |
635
|
|
|
|
|
|
|
Dimensions => { |
636
|
|
|
|
|
|
|
UnitOfMeasurement => { |
637
|
|
|
|
|
|
|
Code => $units_type_map{$self->dim_unit} |
638
|
|
|
|
|
|
|
|| $self->dim_unit, |
639
|
|
|
|
|
|
|
}, |
640
|
|
|
|
|
|
|
Length => $_->length, |
641
|
|
|
|
|
|
|
Width => $_->width, |
642
|
|
|
|
|
|
|
Height => $_->height, |
643
|
|
|
|
|
|
|
}, |
644
|
|
|
|
|
|
|
PackageWeight => { |
645
|
|
|
|
|
|
|
UnitOfMeasurement => { |
646
|
|
|
|
|
|
|
Code => $units_type_map{$self->weight_unit} |
647
|
|
|
|
|
|
|
|| $self->weight_unit, |
648
|
|
|
|
|
|
|
}, |
649
|
|
|
|
|
|
|
Weight => $_->weight, |
650
|
|
|
|
|
|
|
}, |
651
|
|
|
|
|
|
|
ReferenceNumber => \@references, |
652
|
|
|
|
|
|
|
PackageServiceOptions => $package_options, |
653
|
|
|
|
|
|
|
}; |
654
|
|
|
|
|
|
|
} |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
my $payment_option; |
657
|
|
|
|
|
|
|
$payment_option->{Type} = '01'; |
658
|
|
|
|
|
|
|
$payment_option->{$bill_type_map{$self->bill_type}}->{AccountNumber} = |
659
|
|
|
|
|
|
|
$self->bill_account; |
660
|
|
|
|
|
|
|
$payment_option->{$bill_type_map{$self->bill_type}}->{Address} |
661
|
|
|
|
|
|
|
->{PostalCode} = $self->bill_address->postal_code |
662
|
|
|
|
|
|
|
if $self->bill_type =~ /(recipient|third_party)/; |
663
|
|
|
|
|
|
|
$payment_option->{$bill_type_map{$self->bill_type}}->{Address} |
664
|
|
|
|
|
|
|
->{CountryCode} = $self->bill_address->country_code |
665
|
|
|
|
|
|
|
if $self->bill_type eq 'third_party'; |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
my @from_addresslines = ( |
668
|
|
|
|
|
|
|
$self->from_address->address1, |
669
|
|
|
|
|
|
|
$self->from_address->address2, |
670
|
|
|
|
|
|
|
$self->from_address->address3 |
671
|
|
|
|
|
|
|
); |
672
|
|
|
|
|
|
|
my @to_addresslines = ( |
673
|
|
|
|
|
|
|
$self->to_address->address1, |
674
|
|
|
|
|
|
|
$self->to_address->address2, |
675
|
|
|
|
|
|
|
$self->to_address->address3 |
676
|
|
|
|
|
|
|
); |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
my $shipto = { |
679
|
|
|
|
|
|
|
Name => $self->to_address->company, |
680
|
|
|
|
|
|
|
AttentionName => $self->to_address->name, |
681
|
|
|
|
|
|
|
Address => { |
682
|
|
|
|
|
|
|
AddressLine => \@to_addresslines, |
683
|
|
|
|
|
|
|
City => $self->to_address->city, |
684
|
|
|
|
|
|
|
StateProvinceCode => $self->to_address->province_code, |
685
|
|
|
|
|
|
|
PostalCode => $self->to_address->postal_code, |
686
|
|
|
|
|
|
|
CountryCode => $self->to_address->country_code, |
687
|
|
|
|
|
|
|
}, |
688
|
|
|
|
|
|
|
}; |
689
|
|
|
|
|
|
|
$shipto->{Address}->{ResidentialAddressIndicator} = 1 |
690
|
|
|
|
|
|
|
if $self->{residential_address}; |
691
|
|
|
|
|
|
|
$shipto->{Phone}{Number} = $self->to_address->phone |
692
|
|
|
|
|
|
|
if $self->to_address->phone; |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
use Shipment::UPS::WSDL::ShipInterfaces::ShipService::ShipPort; |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
my $interface = |
697
|
|
|
|
|
|
|
Shipment::UPS::WSDL::ShipInterfaces::ShipService::ShipPort->new( |
698
|
|
|
|
|
|
|
{proxy_domain => $self->proxy_domain,}); |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
my $response; |
701
|
|
|
|
|
|
|
try { |
702
|
|
|
|
|
|
|
$response = $interface->ProcessShipment( |
703
|
|
|
|
|
|
|
{ Request => { |
704
|
|
|
|
|
|
|
RequestOption => ($self->address_validation) |
705
|
|
|
|
|
|
|
? 'validate' |
706
|
|
|
|
|
|
|
: 'nonvalidate', |
707
|
|
|
|
|
|
|
}, |
708
|
|
|
|
|
|
|
Shipment => { |
709
|
|
|
|
|
|
|
Shipper => { |
710
|
|
|
|
|
|
|
Name => $self->from_address->company, |
711
|
|
|
|
|
|
|
AttentionName => $self->from_address->name, |
712
|
|
|
|
|
|
|
ShipperNumber => $self->account, |
713
|
|
|
|
|
|
|
Address => { |
714
|
|
|
|
|
|
|
AddressLine => \@from_addresslines, |
715
|
|
|
|
|
|
|
City => $self->from_address->city, |
716
|
|
|
|
|
|
|
StateProvinceCode => |
717
|
|
|
|
|
|
|
$self->from_address->province_code, |
718
|
|
|
|
|
|
|
PostalCode => $self->from_address->postal_code, |
719
|
|
|
|
|
|
|
CountryCode => $self->from_address->country_code, |
720
|
|
|
|
|
|
|
}, |
721
|
|
|
|
|
|
|
}, |
722
|
|
|
|
|
|
|
ShipTo => $shipto, |
723
|
|
|
|
|
|
|
ShipmentRatingOptions => $rating_options, |
724
|
|
|
|
|
|
|
Service => { |
725
|
|
|
|
|
|
|
Code => ( |
726
|
|
|
|
|
|
|
$service_code_map{$self->from_address |
727
|
|
|
|
|
|
|
->country_code}->{$service_id} |
728
|
|
|
|
|
|
|
|| $service_id |
729
|
|
|
|
|
|
|
), |
730
|
|
|
|
|
|
|
}, |
731
|
|
|
|
|
|
|
Package => \@pieces, |
732
|
|
|
|
|
|
|
PaymentInformation => {ShipmentCharge => $payment_option,}, |
733
|
|
|
|
|
|
|
ShipmentServiceOptions => $shipment_options, |
734
|
|
|
|
|
|
|
}, |
735
|
|
|
|
|
|
|
LabelSpecification => { |
736
|
|
|
|
|
|
|
LabelImageFormat => |
737
|
|
|
|
|
|
|
{Code => $printer_type_map{$self->printer_type},}, |
738
|
|
|
|
|
|
|
LabelStockSize => { |
739
|
|
|
|
|
|
|
Height => $self->label_height, |
740
|
|
|
|
|
|
|
Width => 4, |
741
|
|
|
|
|
|
|
}, |
742
|
|
|
|
|
|
|
}, |
743
|
|
|
|
|
|
|
}, |
744
|
|
|
|
|
|
|
{ UsernameToken => { |
745
|
|
|
|
|
|
|
Username => $self->username, |
746
|
|
|
|
|
|
|
Password => $self->password, |
747
|
|
|
|
|
|
|
}, |
748
|
|
|
|
|
|
|
ServiceAccessToken => {AccessLicenseNumber => $self->key,}, |
749
|
|
|
|
|
|
|
}, |
750
|
|
|
|
|
|
|
); |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
#warn $response; |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
$self->tracking_id($response->get_ShipmentResults() |
755
|
|
|
|
|
|
|
->get_ShipmentIdentificationNumber()->get_value); |
756
|
|
|
|
|
|
|
use Data::Currency; |
757
|
|
|
|
|
|
|
use Shipment::Service; |
758
|
|
|
|
|
|
|
my $rate = $response->get_ShipmentResults->get_ShipmentCharges |
759
|
|
|
|
|
|
|
->get_TotalCharges->get_MonetaryValue; |
760
|
|
|
|
|
|
|
my $currency = $response->get_ShipmentResults->get_ShipmentCharges |
761
|
|
|
|
|
|
|
->get_TotalCharges->get_CurrencyCode; |
762
|
|
|
|
|
|
|
if ($self->negotiated_rates) { |
763
|
|
|
|
|
|
|
if ($response->get_ShipmentResults->get_NegotiatedRateCharges) { |
764
|
|
|
|
|
|
|
$rate = |
765
|
|
|
|
|
|
|
$response->get_ShipmentResults->get_NegotiatedRateCharges |
766
|
|
|
|
|
|
|
->get_TotalCharge->get_MonetaryValue; |
767
|
|
|
|
|
|
|
$currency = |
768
|
|
|
|
|
|
|
$response->get_ShipmentResults->get_NegotiatedRateCharges |
769
|
|
|
|
|
|
|
->get_TotalCharge->get_CurrencyCode; |
770
|
|
|
|
|
|
|
} |
771
|
|
|
|
|
|
|
} |
772
|
|
|
|
|
|
|
$self->service( |
773
|
|
|
|
|
|
|
new Shipment::Service( |
774
|
|
|
|
|
|
|
id => $service_id, |
775
|
|
|
|
|
|
|
name => $self->services->{$service_id}->name, |
776
|
|
|
|
|
|
|
cost => Data::Currency->new($rate, $currency), |
777
|
|
|
|
|
|
|
) |
778
|
|
|
|
|
|
|
); |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
use Shipment::Label; |
781
|
|
|
|
|
|
|
use MIME::Base64; |
782
|
|
|
|
|
|
|
my $package_index = 0; |
783
|
|
|
|
|
|
|
foreach (@{$response->get_ShipmentResults()->get_PackageResults()}) { |
784
|
|
|
|
|
|
|
$self->get_package($package_index) |
785
|
|
|
|
|
|
|
->tracking_id($_->get_TrackingNumber()->get_value); |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
## For EPL labels, force Top Orientation by inserting the ZT command at the beginning of the file. |
788
|
|
|
|
|
|
|
## This is needed for cases when the printer defaults to the incorrect orientation. |
789
|
|
|
|
|
|
|
my $data = "ZT\n" |
790
|
|
|
|
|
|
|
if $printer_type_map{$self->printer_type} eq 'EPL'; |
791
|
|
|
|
|
|
|
$data .= decode_base64( |
792
|
|
|
|
|
|
|
$_->get_ShippingLabel()->get_GraphicImage->get_value); |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
$self->get_package($package_index)->label( |
795
|
|
|
|
|
|
|
Shipment::Label->new( |
796
|
|
|
|
|
|
|
{ tracking_id => $_->get_TrackingNumber()->get_value, |
797
|
|
|
|
|
|
|
content_type => |
798
|
|
|
|
|
|
|
$label_content_type_map{$self->printer_type}, |
799
|
|
|
|
|
|
|
data => $data, |
800
|
|
|
|
|
|
|
file_name => $_->get_TrackingNumber()->get_value . '.' |
801
|
|
|
|
|
|
|
. lc $printer_type_map{$self->printer_type}, |
802
|
|
|
|
|
|
|
}, |
803
|
|
|
|
|
|
|
) |
804
|
|
|
|
|
|
|
); |
805
|
|
|
|
|
|
|
$package_index++; |
806
|
|
|
|
|
|
|
} |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
if ($response->get_ShipmentResults()->get_ControlLogReceipt) { |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
## For EPL labels, force Top Orientation by inserting the ZT command at the beginning of the file. |
811
|
|
|
|
|
|
|
## This is needed for cases when the printer defaults to the incorrect orientation. |
812
|
|
|
|
|
|
|
my $data = "ZT\n" |
813
|
|
|
|
|
|
|
if $printer_type_map{$self->printer_type} eq 'EPL'; |
814
|
|
|
|
|
|
|
$data |
815
|
|
|
|
|
|
|
.= decode_base64( |
816
|
|
|
|
|
|
|
$response->get_ShipmentResults()->get_ControlLogReceipt() |
817
|
|
|
|
|
|
|
->get_GraphicImage->get_value); |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
$self->control_log_receipt( |
820
|
|
|
|
|
|
|
Shipment::Label->new( |
821
|
|
|
|
|
|
|
{ content_type => |
822
|
|
|
|
|
|
|
$label_content_type_map{$self->printer_type}, |
823
|
|
|
|
|
|
|
data => $data, |
824
|
|
|
|
|
|
|
file_name => 'control_log_receipt.' |
825
|
|
|
|
|
|
|
. lc $printer_type_map{$self->printer_type}, |
826
|
|
|
|
|
|
|
} |
827
|
|
|
|
|
|
|
) |
828
|
|
|
|
|
|
|
); |
829
|
|
|
|
|
|
|
} |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
$self->notice(''); |
832
|
|
|
|
|
|
|
if ($response->get_Response->get_Alert) { |
833
|
|
|
|
|
|
|
foreach my $alert (@{$response->get_Response->get_Alert}) { |
834
|
|
|
|
|
|
|
warn $alert->get_Description->get_value; |
835
|
|
|
|
|
|
|
$self->add_notice($alert->get_Description->get_value . "\n"); |
836
|
|
|
|
|
|
|
} |
837
|
|
|
|
|
|
|
} |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
} |
840
|
|
|
|
|
|
|
catch { |
841
|
|
|
|
|
|
|
#warn $_; |
842
|
|
|
|
|
|
|
try { |
843
|
|
|
|
|
|
|
warn $response->get_detail()->get_Errors()->get_ErrorDetail() |
844
|
|
|
|
|
|
|
->get_PrimaryErrorCode()->get_Description; |
845
|
|
|
|
|
|
|
$self->error( |
846
|
|
|
|
|
|
|
$response->get_detail()->get_Errors()->get_ErrorDetail() |
847
|
|
|
|
|
|
|
->get_PrimaryErrorCode()->get_Description->get_value); |
848
|
|
|
|
|
|
|
} |
849
|
|
|
|
|
|
|
catch { |
850
|
|
|
|
|
|
|
#warn $_; |
851
|
|
|
|
|
|
|
warn $response->get_faultstring; |
852
|
|
|
|
|
|
|
$self->error($response->get_faultstring->get_value); |
853
|
|
|
|
|
|
|
}; |
854
|
|
|
|
|
|
|
}; |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
} |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
sub return { |
860
|
|
|
|
|
|
|
my ($self, $service_id) = @_; |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
try { |
863
|
|
|
|
|
|
|
$service_id = $self->services->{$service_id}->id; |
864
|
|
|
|
|
|
|
} |
865
|
|
|
|
|
|
|
catch { |
866
|
|
|
|
|
|
|
#warn $_; |
867
|
|
|
|
|
|
|
warn "service ($service_id) not available"; |
868
|
|
|
|
|
|
|
$self->error("service ($service_id) not available"); |
869
|
|
|
|
|
|
|
$service_id = ''; |
870
|
|
|
|
|
|
|
}; |
871
|
|
|
|
|
|
|
return unless $service_id; |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
my $package_options; |
874
|
|
|
|
|
|
|
$package_options->{DeclaredValue}->{CurrencyCode} = $self->currency; |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
my @pieces; |
877
|
|
|
|
|
|
|
foreach (@{$self->packages}) { |
878
|
|
|
|
|
|
|
$package_options->{DeclaredValue}->{MonetaryValue} = |
879
|
|
|
|
|
|
|
$_->insured_value->value; |
880
|
|
|
|
|
|
|
push @pieces, |
881
|
|
|
|
|
|
|
{ Description => 'n/a', |
882
|
|
|
|
|
|
|
Packaging => { |
883
|
|
|
|
|
|
|
Code => $package_type_map{$self->package_type} |
884
|
|
|
|
|
|
|
|| $self->package_type, |
885
|
|
|
|
|
|
|
}, |
886
|
|
|
|
|
|
|
Dimensions => { |
887
|
|
|
|
|
|
|
UnitOfMeasurement => { |
888
|
|
|
|
|
|
|
Code => $units_type_map{$self->dim_unit} |
889
|
|
|
|
|
|
|
|| $self->dim_unit, |
890
|
|
|
|
|
|
|
}, |
891
|
|
|
|
|
|
|
Length => $_->length, |
892
|
|
|
|
|
|
|
Width => $_->width, |
893
|
|
|
|
|
|
|
Height => $_->height, |
894
|
|
|
|
|
|
|
}, |
895
|
|
|
|
|
|
|
PackageWeight => { |
896
|
|
|
|
|
|
|
UnitOfMeasurement => { |
897
|
|
|
|
|
|
|
Code => $units_type_map{$self->weight_unit} |
898
|
|
|
|
|
|
|
|| $self->weight_unit, |
899
|
|
|
|
|
|
|
}, |
900
|
|
|
|
|
|
|
Weight => $_->weight, |
901
|
|
|
|
|
|
|
}, |
902
|
|
|
|
|
|
|
PackageServiceOptions => $package_options, |
903
|
|
|
|
|
|
|
}; |
904
|
|
|
|
|
|
|
} |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
my $payment_option; |
907
|
|
|
|
|
|
|
$payment_option->{Type} = '01'; |
908
|
|
|
|
|
|
|
$payment_option->{$bill_type_map{$self->bill_type}}->{AccountNumber} = |
909
|
|
|
|
|
|
|
$self->bill_account; |
910
|
|
|
|
|
|
|
$payment_option->{$bill_type_map{$self->bill_type}}->{Address} |
911
|
|
|
|
|
|
|
->{PostalCode} = $self->bill_address->postal_code |
912
|
|
|
|
|
|
|
if $self->bill_type =~ /(recipient|third_party)/; |
913
|
|
|
|
|
|
|
$payment_option->{$bill_type_map{$self->bill_type}}->{Address} |
914
|
|
|
|
|
|
|
->{CountryCode} = $self->bill_address->country_code |
915
|
|
|
|
|
|
|
if $self->bill_type eq 'third_party'; |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
my @from_addresslines = ( |
918
|
|
|
|
|
|
|
$self->from_address->address1, |
919
|
|
|
|
|
|
|
$self->from_address->address2, |
920
|
|
|
|
|
|
|
$self->from_address->address3 |
921
|
|
|
|
|
|
|
); |
922
|
|
|
|
|
|
|
my @to_addresslines = ( |
923
|
|
|
|
|
|
|
$self->to_address->address1, |
924
|
|
|
|
|
|
|
$self->to_address->address2, |
925
|
|
|
|
|
|
|
$self->to_address->address3 |
926
|
|
|
|
|
|
|
); |
927
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
use Shipment::UPS::WSDL::ShipInterfaces::ShipService::ShipPort; |
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
my $interface = |
931
|
|
|
|
|
|
|
Shipment::UPS::WSDL::ShipInterfaces::ShipService::ShipPort->new( |
932
|
|
|
|
|
|
|
{proxy_domain => $self->proxy_domain,}); |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
my $response; |
935
|
|
|
|
|
|
|
try { |
936
|
|
|
|
|
|
|
$response = $interface->ProcessShipment( |
937
|
|
|
|
|
|
|
{ Request => { |
938
|
|
|
|
|
|
|
RequestOption => ($self->address_validation) |
939
|
|
|
|
|
|
|
? 'validate' |
940
|
|
|
|
|
|
|
: 'nonvalidate', |
941
|
|
|
|
|
|
|
}, |
942
|
|
|
|
|
|
|
Shipment => { |
943
|
|
|
|
|
|
|
ReturnService => {Code => 9,}, |
944
|
|
|
|
|
|
|
Shipper => { |
945
|
|
|
|
|
|
|
Name => $self->from_address->company, |
946
|
|
|
|
|
|
|
AttentionName => $self->from_address->name, |
947
|
|
|
|
|
|
|
ShipperNumber => $self->account, |
948
|
|
|
|
|
|
|
Address => { |
949
|
|
|
|
|
|
|
AddressLine => \@from_addresslines, |
950
|
|
|
|
|
|
|
City => $self->from_address->city, |
951
|
|
|
|
|
|
|
StateProvinceCode => |
952
|
|
|
|
|
|
|
$self->from_address->province_code, |
953
|
|
|
|
|
|
|
PostalCode => $self->from_address->postal_code, |
954
|
|
|
|
|
|
|
CountryCode => $self->from_address->country_code, |
955
|
|
|
|
|
|
|
}, |
956
|
|
|
|
|
|
|
}, |
957
|
|
|
|
|
|
|
ShipFrom => { |
958
|
|
|
|
|
|
|
Name => $self->to_address->company, |
959
|
|
|
|
|
|
|
AttentionName => $self->to_address->name, |
960
|
|
|
|
|
|
|
Address => { |
961
|
|
|
|
|
|
|
AddressLine => \@to_addresslines, |
962
|
|
|
|
|
|
|
City => $self->to_address->city, |
963
|
|
|
|
|
|
|
StateProvinceCode => |
964
|
|
|
|
|
|
|
$self->to_address->province_code, |
965
|
|
|
|
|
|
|
PostalCode => $self->to_address->postal_code, |
966
|
|
|
|
|
|
|
CountryCode => $self->to_address->country_code, |
967
|
|
|
|
|
|
|
}, |
968
|
|
|
|
|
|
|
EmailAddress => $self->from_address->email, |
969
|
|
|
|
|
|
|
}, |
970
|
|
|
|
|
|
|
ShipTo => { |
971
|
|
|
|
|
|
|
Name => $self->from_address->company, |
972
|
|
|
|
|
|
|
AttentionName => $self->from_address->name, |
973
|
|
|
|
|
|
|
Address => { |
974
|
|
|
|
|
|
|
AddressLine => \@from_addresslines, |
975
|
|
|
|
|
|
|
City => $self->from_address->city, |
976
|
|
|
|
|
|
|
StateProvinceCode => |
977
|
|
|
|
|
|
|
$self->from_address->province_code, |
978
|
|
|
|
|
|
|
PostalCode => $self->from_address->postal_code, |
979
|
|
|
|
|
|
|
CountryCode => $self->from_address->country_code, |
980
|
|
|
|
|
|
|
}, |
981
|
|
|
|
|
|
|
EmailAddress => $self->to_address->email, |
982
|
|
|
|
|
|
|
}, |
983
|
|
|
|
|
|
|
Service => {Code => $service_id,}, |
984
|
|
|
|
|
|
|
Package => \@pieces, |
985
|
|
|
|
|
|
|
PaymentInformation => {ShipmentCharge => $payment_option,}, |
986
|
|
|
|
|
|
|
}, |
987
|
|
|
|
|
|
|
LabelSpecification => { |
988
|
|
|
|
|
|
|
LabelImageFormat => |
989
|
|
|
|
|
|
|
{Code => $printer_type_map{$self->printer_type},}, |
990
|
|
|
|
|
|
|
LabelStockSize => { |
991
|
|
|
|
|
|
|
Height => $self->label_height, |
992
|
|
|
|
|
|
|
Width => 4, |
993
|
|
|
|
|
|
|
}, |
994
|
|
|
|
|
|
|
}, |
995
|
|
|
|
|
|
|
}, |
996
|
|
|
|
|
|
|
{ UsernameToken => { |
997
|
|
|
|
|
|
|
Username => $self->username, |
998
|
|
|
|
|
|
|
Password => $self->password, |
999
|
|
|
|
|
|
|
}, |
1000
|
|
|
|
|
|
|
ServiceAccessToken => {AccessLicenseNumber => $self->key,}, |
1001
|
|
|
|
|
|
|
}, |
1002
|
|
|
|
|
|
|
); |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
#warn $response; |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
$self->tracking_id($response->get_ShipmentResults() |
1007
|
|
|
|
|
|
|
->get_ShipmentIdentificationNumber()->get_value); |
1008
|
|
|
|
|
|
|
use Data::Currency; |
1009
|
|
|
|
|
|
|
use Shipment::Service; |
1010
|
|
|
|
|
|
|
$self->service( |
1011
|
|
|
|
|
|
|
new Shipment::Service( |
1012
|
|
|
|
|
|
|
id => $service_id, |
1013
|
|
|
|
|
|
|
name => $self->services->{$service_id}->name, |
1014
|
|
|
|
|
|
|
cost => Data::Currency->new( |
1015
|
|
|
|
|
|
|
$response->get_ShipmentResults() |
1016
|
|
|
|
|
|
|
->get_ShipmentCharges->get_TotalCharges() |
1017
|
|
|
|
|
|
|
->get_MonetaryValue, |
1018
|
|
|
|
|
|
|
$response->get_ShipmentResults()->get_ShipmentCharges() |
1019
|
|
|
|
|
|
|
->get_TotalCharges()->get_CurrencyCode |
1020
|
|
|
|
|
|
|
), |
1021
|
|
|
|
|
|
|
) |
1022
|
|
|
|
|
|
|
); |
1023
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
use Shipment::Label; |
1025
|
|
|
|
|
|
|
use MIME::Base64; |
1026
|
|
|
|
|
|
|
my $package_index = 0; |
1027
|
|
|
|
|
|
|
foreach (@{$response->get_ShipmentResults()->get_PackageResults()}) { |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
## For EPL labels, force Top Orientation by inserting the ZT command at the beginning of the file. |
1030
|
|
|
|
|
|
|
## This is needed for cases when the printer defaults to the incorrect orientation. |
1031
|
|
|
|
|
|
|
my $data = "ZT\n" |
1032
|
|
|
|
|
|
|
if $printer_type_map{$self->printer_type} eq 'EPL'; |
1033
|
|
|
|
|
|
|
$data .= decode_base64( |
1034
|
|
|
|
|
|
|
$_->get_ShippingLabel()->get_GraphicImage->get_value); |
1035
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
$self->get_package($package_index) |
1037
|
|
|
|
|
|
|
->tracking_id($_->get_TrackingNumber()->get_value); |
1038
|
|
|
|
|
|
|
$self->get_package($package_index)->label( |
1039
|
|
|
|
|
|
|
Shipment::Label->new( |
1040
|
|
|
|
|
|
|
{ tracking_id => $_->get_TrackingNumber()->get_value, |
1041
|
|
|
|
|
|
|
content_type => |
1042
|
|
|
|
|
|
|
$label_content_type_map{$self->printer_type}, |
1043
|
|
|
|
|
|
|
data => $data, |
1044
|
|
|
|
|
|
|
file_name => $_->get_TrackingNumber()->get_value . '.' |
1045
|
|
|
|
|
|
|
. lc $printer_type_map{$self->printer_type}, |
1046
|
|
|
|
|
|
|
}, |
1047
|
|
|
|
|
|
|
) |
1048
|
|
|
|
|
|
|
); |
1049
|
|
|
|
|
|
|
$package_index++; |
1050
|
|
|
|
|
|
|
} |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
if ($response->get_ShipmentResults()->get_ControlLogReceipt) { |
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
## For EPL labels, force Top Orientation by inserting the ZT command at the beginning of the file. |
1055
|
|
|
|
|
|
|
## This is needed for cases when the printer defaults to the incorrect orientation. |
1056
|
|
|
|
|
|
|
my $data = "ZT\n" |
1057
|
|
|
|
|
|
|
if $printer_type_map{$self->printer_type} eq 'EPL'; |
1058
|
|
|
|
|
|
|
$data |
1059
|
|
|
|
|
|
|
.= decode_base64( |
1060
|
|
|
|
|
|
|
$response->get_ShipmentResults()->get_ControlLogReceipt() |
1061
|
|
|
|
|
|
|
->get_GraphicImage->get_value); |
1062
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
$self->control_log_receipt( |
1064
|
|
|
|
|
|
|
Shipment::Label->new( |
1065
|
|
|
|
|
|
|
{ content_type => |
1066
|
|
|
|
|
|
|
$label_content_type_map{$self->printer_type}, |
1067
|
|
|
|
|
|
|
data => $data, |
1068
|
|
|
|
|
|
|
file_name => 'control_log_receipt.' |
1069
|
|
|
|
|
|
|
. lc $printer_type_map{$self->printer_type}, |
1070
|
|
|
|
|
|
|
} |
1071
|
|
|
|
|
|
|
) |
1072
|
|
|
|
|
|
|
); |
1073
|
|
|
|
|
|
|
} |
1074
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
$self->notice(''); |
1076
|
|
|
|
|
|
|
if ($response->get_Response->get_Alert) { |
1077
|
|
|
|
|
|
|
foreach my $alert (@{$response->get_Response->get_Alert}) { |
1078
|
|
|
|
|
|
|
warn $alert->get_Description->get_value; |
1079
|
|
|
|
|
|
|
$self->add_notice($alert->get_Description->get_value . "\n"); |
1080
|
|
|
|
|
|
|
} |
1081
|
|
|
|
|
|
|
} |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
} |
1084
|
|
|
|
|
|
|
catch { |
1085
|
|
|
|
|
|
|
#warn $_; |
1086
|
|
|
|
|
|
|
try { |
1087
|
|
|
|
|
|
|
warn $response->get_detail()->get_Errors()->get_ErrorDetail() |
1088
|
|
|
|
|
|
|
->get_PrimaryErrorCode()->get_Description; |
1089
|
|
|
|
|
|
|
$self->error( |
1090
|
|
|
|
|
|
|
$response->get_detail()->get_Errors()->get_ErrorDetail() |
1091
|
|
|
|
|
|
|
->get_PrimaryErrorCode()->get_Description->get_value); |
1092
|
|
|
|
|
|
|
} |
1093
|
|
|
|
|
|
|
catch { |
1094
|
|
|
|
|
|
|
#warn $_; |
1095
|
|
|
|
|
|
|
warn $response->get_faultstring; |
1096
|
|
|
|
|
|
|
$self->error($response->get_faultstring->get_value); |
1097
|
|
|
|
|
|
|
}; |
1098
|
|
|
|
|
|
|
}; |
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
} |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
sub cancel { |
1104
|
|
|
|
|
|
|
my $self = shift; |
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
if (!$self->tracking_id) { |
1107
|
|
|
|
|
|
|
$self->error('no tracking id provided'); |
1108
|
|
|
|
|
|
|
return; |
1109
|
|
|
|
|
|
|
} |
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
my $void->{ShipmentIdentificationNumber} = $self->tracking_id; |
1112
|
|
|
|
|
|
|
|
1113
|
|
|
|
|
|
|
my @tracking_ids; |
1114
|
|
|
|
|
|
|
foreach ($self->all_packages) { |
1115
|
|
|
|
|
|
|
push @tracking_ids, $_->tracking_id; |
1116
|
|
|
|
|
|
|
} |
1117
|
|
|
|
|
|
|
if ($#tracking_ids) { |
1118
|
|
|
|
|
|
|
$void->{TrackingNumber} = \@tracking_ids; |
1119
|
|
|
|
|
|
|
} |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
use Shipment::UPS::WSDL::ShipInterfaces::VoidService::VoidPort; |
1122
|
|
|
|
|
|
|
my $interface = |
1123
|
|
|
|
|
|
|
Shipment::UPS::WSDL::ShipInterfaces::VoidService::VoidPort->new( |
1124
|
|
|
|
|
|
|
{proxy_domain => $self->proxy_domain,}); |
1125
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
my $response; |
1127
|
|
|
|
|
|
|
my $success; |
1128
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
try { |
1130
|
|
|
|
|
|
|
$response = $interface->ProcessVoid( |
1131
|
|
|
|
|
|
|
{ Request => {RequestOption => '',}, |
1132
|
|
|
|
|
|
|
VoidShipment => $void, |
1133
|
|
|
|
|
|
|
}, |
1134
|
|
|
|
|
|
|
{ UsernameToken => { |
1135
|
|
|
|
|
|
|
Username => $self->username, |
1136
|
|
|
|
|
|
|
Password => $self->password, |
1137
|
|
|
|
|
|
|
}, |
1138
|
|
|
|
|
|
|
ServiceAccessToken => {AccessLicenseNumber => $self->key,}, |
1139
|
|
|
|
|
|
|
}, |
1140
|
|
|
|
|
|
|
); |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
#warn $response; |
1143
|
|
|
|
|
|
|
|
1144
|
|
|
|
|
|
|
$success = |
1145
|
|
|
|
|
|
|
$response->get_SummaryResult->get_Status->get_Description->get_value; |
1146
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
$self->notice(''); |
1148
|
|
|
|
|
|
|
if ($response->get_Response->get_Alert) { |
1149
|
|
|
|
|
|
|
foreach my $alert (@{$response->get_Response->get_Alert}) { |
1150
|
|
|
|
|
|
|
warn $alert->get_Description->get_value; |
1151
|
|
|
|
|
|
|
$self->add_notice($alert->get_Description->get_value . "\n"); |
1152
|
|
|
|
|
|
|
} |
1153
|
|
|
|
|
|
|
} |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
} |
1156
|
|
|
|
|
|
|
catch { |
1157
|
|
|
|
|
|
|
#warn $_; |
1158
|
|
|
|
|
|
|
try { |
1159
|
|
|
|
|
|
|
warn $response->get_detail()->get_Errors()->get_ErrorDetail() |
1160
|
|
|
|
|
|
|
->get_PrimaryErrorCode()->get_Description; |
1161
|
|
|
|
|
|
|
$self->error( |
1162
|
|
|
|
|
|
|
$response->get_detail()->get_Errors()->get_ErrorDetail() |
1163
|
|
|
|
|
|
|
->get_PrimaryErrorCode()->get_Description->get_value); |
1164
|
|
|
|
|
|
|
} |
1165
|
|
|
|
|
|
|
catch { |
1166
|
|
|
|
|
|
|
#warn $_; |
1167
|
|
|
|
|
|
|
warn $response->get_faultstring; |
1168
|
|
|
|
|
|
|
$self->error($response->get_faultstring->get_value); |
1169
|
|
|
|
|
|
|
}; |
1170
|
|
|
|
|
|
|
}; |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
return $success; |
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
} |
1175
|
|
|
|
|
|
|
|
1176
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
1; |
1178
|
|
|
|
|
|
|
|
1179
|
|
|
|
|
|
|
__END__ |
1180
|
|
|
|
|
|
|
|
1181
|
|
|
|
|
|
|
=pod |
1182
|
|
|
|
|
|
|
|
1183
|
|
|
|
|
|
|
=encoding UTF-8 |
1184
|
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
=head1 NAME |
1186
|
|
|
|
|
|
|
|
1187
|
|
|
|
|
|
|
Shipment::UPS |
1188
|
|
|
|
|
|
|
|
1189
|
|
|
|
|
|
|
=head1 VERSION |
1190
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
version 2.00 |
1192
|
|
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
=head1 SYNOPSIS |
1194
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
use Shipment::UPS; |
1196
|
|
|
|
|
|
|
use Shipment::Address; |
1197
|
|
|
|
|
|
|
use Shipment::Package; |
1198
|
|
|
|
|
|
|
|
1199
|
|
|
|
|
|
|
my $shipment = Shipment::UPS->new( |
1200
|
|
|
|
|
|
|
from_address => Shipment::Address->new( ... ), |
1201
|
|
|
|
|
|
|
to_address => Shipment::Address->new( ... ), |
1202
|
|
|
|
|
|
|
packages => [ Shipment::Package->new( ... ), ], |
1203
|
|
|
|
|
|
|
); |
1204
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
foreach my $service ( $shipment->all_services ) { |
1206
|
|
|
|
|
|
|
print $service->id . " (" . $service->cost . ")\n"; |
1207
|
|
|
|
|
|
|
} |
1208
|
|
|
|
|
|
|
|
1209
|
|
|
|
|
|
|
$shipment->rate( 'express' ); |
1210
|
|
|
|
|
|
|
print $shipment->service->cost . "\n"; |
1211
|
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
$shipment->ship( 'ground' ); |
1213
|
|
|
|
|
|
|
$shipment->get_package(0)->label->save; |
1214
|
|
|
|
|
|
|
|
1215
|
|
|
|
|
|
|
=head1 NAME |
1216
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
Shipment::UPS - Interface to UPS Shipping Web Services |
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
=head1 ABOUT |
1220
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
This class provides an interface to the UPS Online Tools. You must sign up for a developer access key in order to make use of this module. |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
https://www.ups.com/upsdeveloperkit |
1224
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
It is an extension of L<Shipment::Base>. |
1226
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
It makes extensive use of SOAP::WSDL in order to create/decode xml requests and responses. The Shipment::UPS::WSDL interface was created primarily using the wsdl2perl.pl script from SOAP::WSDL. |
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
=head1 Class Attributes |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
=head2 username, password, key |
1232
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
Credentials required to access UPS Online Tools. |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
=head2 proxy_domain |
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
This determines whether you will use the UPS Customer Integration Environment (for development) or the production (live) environment |
1238
|
|
|
|
|
|
|
* wwwcie.ups.com (development) |
1239
|
|
|
|
|
|
|
* onlinetools.ups.com (production) |
1240
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
=head2 negotiated_rates |
1242
|
|
|
|
|
|
|
|
1243
|
|
|
|
|
|
|
Turn negotiated rates on or off. |
1244
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
The Shipper Account/UserID used must be qualified to receive negotiated rates. You will most likely need to contact UPS to have set this up. |
1246
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
If the Shipper Account/UserID is not qualified, the published rates will be used instead and a notice set. |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
Default is off. |
1250
|
|
|
|
|
|
|
|
1251
|
|
|
|
|
|
|
=head2 residential_address |
1252
|
|
|
|
|
|
|
|
1253
|
|
|
|
|
|
|
Flag the ship to address as residential. |
1254
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
Default is false. |
1256
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
=head2 address_validation |
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
Turn address validation on or off. When on, ship will fail if the address does not pass UPS address validation |
1260
|
|
|
|
|
|
|
|
1261
|
|
|
|
|
|
|
Default is on. |
1262
|
|
|
|
|
|
|
|
1263
|
|
|
|
|
|
|
=head2 label_height |
1264
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
The label height. Can be either 6" or 8". The label width is fixed at 4". |
1266
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
=head2 control_log_receipt |
1268
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
In certain cases (i.e. for shipments with declared value over $999), UPS will return a control log receipt which must be printed off along with the label. |
1270
|
|
|
|
|
|
|
|
1271
|
|
|
|
|
|
|
type: Shipment::Label |
1272
|
|
|
|
|
|
|
|
1273
|
|
|
|
|
|
|
=head2 carbon_neutral |
1274
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
Set the Carbon Neutral Indicator - http://www.ups.com/content/us/en/resources/ship/carbonneutral/shipping.html |
1276
|
|
|
|
|
|
|
|
1277
|
|
|
|
|
|
|
type: Bool |
1278
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
=head1 Type Maps |
1280
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
=head2 service_map |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
UPS returns service codes without descriptions. This is mapped here so that we can display 'UPS Ground' instead of '03'. |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
=head2 Shipment::Base type maps |
1286
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
Shipment::Base provides abstract types which need to be mapped to UPS codes (i.e. bill_type of "sender" maps to UPS "BillShipper") |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
=head2 custom package types |
1290
|
|
|
|
|
|
|
|
1291
|
|
|
|
|
|
|
UPS provides package types in addition to the defaults in Shipment::Base |
1292
|
|
|
|
|
|
|
* 25kg_box |
1293
|
|
|
|
|
|
|
* 10kg_box |
1294
|
|
|
|
|
|
|
* pallet |
1295
|
|
|
|
|
|
|
* small_express_box |
1296
|
|
|
|
|
|
|
* medium_express_box |
1297
|
|
|
|
|
|
|
* large_express_box |
1298
|
|
|
|
|
|
|
|
1299
|
|
|
|
|
|
|
=head2 custom printer types |
1300
|
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
UPS does not offer a pdf option for labels, so the default printer type is image (gif). |
1302
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
UPS does offer additional thermal options: |
1304
|
|
|
|
|
|
|
* ZPL |
1305
|
|
|
|
|
|
|
* SPL |
1306
|
|
|
|
|
|
|
* STARPL |
1307
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
=head2 default currency |
1309
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
The default currency is USD |
1311
|
|
|
|
|
|
|
|
1312
|
|
|
|
|
|
|
=head2 surepost |
1313
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
Enable UPS SurePost |
1315
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
=head1 Class Methods |
1317
|
|
|
|
|
|
|
|
1318
|
|
|
|
|
|
|
=head2 _build_services |
1319
|
|
|
|
|
|
|
|
1320
|
|
|
|
|
|
|
This calls ProcessRate from the Rating API with RequestOption => 'Shop' |
1321
|
|
|
|
|
|
|
|
1322
|
|
|
|
|
|
|
Each RatedShipment that is returned is added to services |
1323
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
The following service mapping is used: |
1325
|
|
|
|
|
|
|
* ground => 03 (UPS Ground) or 11 (UPS Standard) |
1326
|
|
|
|
|
|
|
* express => 02 (UPS Second Day Air) |
1327
|
|
|
|
|
|
|
* priority => 01 (UPS Next Day Air) |
1328
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
This method ignores what is in $self->packages and uses a single package weighing 1 pound for rating. The idea is to list what services are available, but for accurate rate comparisons, the rate method should be used. |
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
=head2 rate |
1332
|
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
This calls ProcessRate from the Rating API with RequestOption => 'Rate' |
1334
|
|
|
|
|
|
|
|
1335
|
|
|
|
|
|
|
=head2 ship |
1336
|
|
|
|
|
|
|
|
1337
|
|
|
|
|
|
|
This method calls ProcessShipment from the Shipping API |
1338
|
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
=head2 return |
1340
|
|
|
|
|
|
|
|
1341
|
|
|
|
|
|
|
This method calls ProcessShipment from the Shipping API with |
1342
|
|
|
|
|
|
|
ReturnService => Code => 9 |
1343
|
|
|
|
|
|
|
which provides the return label to be printed off. |
1344
|
|
|
|
|
|
|
|
1345
|
|
|
|
|
|
|
This method has only been implemented for the purpose of obtaining certification with UPS. It has not been fully tested and does not offer some core options (such as the ability to email the return label). |
1346
|
|
|
|
|
|
|
|
1347
|
|
|
|
|
|
|
It assumes that you are first creating an outgoing shipment and creating the return shipment at the same time. Because of this, it uses the "to_address" as the origin and the "from_address" as the destination. |
1348
|
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
=head2 cancel |
1350
|
|
|
|
|
|
|
|
1351
|
|
|
|
|
|
|
This method calls ProcessVoid from the Shipping API |
1352
|
|
|
|
|
|
|
|
1353
|
|
|
|
|
|
|
It uses $self->tracking_id for the shipment identification number in order |
1354
|
|
|
|
|
|
|
to void a single package shipment. |
1355
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
It will use all package tracking id's |
1357
|
|
|
|
|
|
|
to void one or more packages within a multi-package shipment. |
1358
|
|
|
|
|
|
|
|
1359
|
|
|
|
|
|
|
returns "Voided" if successful |
1360
|
|
|
|
|
|
|
|
1361
|
|
|
|
|
|
|
=head1 AUTHOR |
1362
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
Andrew Baerg @ <andrew at pullingshots dot ca> |
1364
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
http://pullingshots.ca/ |
1366
|
|
|
|
|
|
|
|
1367
|
|
|
|
|
|
|
=head1 BUGS |
1368
|
|
|
|
|
|
|
|
1369
|
|
|
|
|
|
|
Please contact me directly. |
1370
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
=head1 COPYRIGHT |
1372
|
|
|
|
|
|
|
|
1373
|
|
|
|
|
|
|
Copyright (C) 2010 Andrew J Baerg, All Rights Reserved |
1374
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
=head1 NO WARRANTY |
1376
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
Absolutely, positively NO WARRANTY, neither express or implied, is |
1378
|
|
|
|
|
|
|
offered with this software. You use this software at your own risk. In |
1379
|
|
|
|
|
|
|
case of loss, no person or entity owes you anything whatsoever. You |
1380
|
|
|
|
|
|
|
have been warned. |
1381
|
|
|
|
|
|
|
|
1382
|
|
|
|
|
|
|
=head1 LICENSE |
1383
|
|
|
|
|
|
|
|
1384
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
1385
|
|
|
|
|
|
|
under the same terms as Perl itself. |
1386
|
|
|
|
|
|
|
|
1387
|
|
|
|
|
|
|
=head1 AUTHOR |
1388
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
Andrew Baerg <baergaj@cpan.org> |
1390
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
1392
|
|
|
|
|
|
|
|
1393
|
|
|
|
|
|
|
This software is copyright (c) 2013 by Andrew Baerg. |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
1396
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
1397
|
|
|
|
|
|
|
|
1398
|
|
|
|
|
|
|
=cut |