File Coverage

blib/lib/Business/UPS/Tracking/Request.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             # ============================================================================
2             package Business::UPS::Tracking::Request;
3             # ============================================================================
4 1     1   4456 use utf8;
  1         4  
  1         10  
5 1     1   68 use 5.0100;
  1         4  
  1         37  
6              
7 1     1   1662 use Moose;
  0            
  0            
8              
9             use DateTime;
10             use XML::LibXML;
11             use Moose::Util::TypeConstraints;
12             use Try::Tiny;
13              
14             use Business::UPS::Tracking::Utils;
15             use Business::UPS::Tracking::Response;
16              
17             =encoding utf8
18              
19             =head1 NAME
20              
21             Business::UPS::Tracking::Request - A tracking request
22              
23             =head1 SYNOPSIS
24              
25             my $request = Business::UPS::Tracking::Request->new(
26             tracking => $tracking_object,
27             ReferenceNumber => 'myreferencenumber',
28             );
29             $request->DestinationPostalCode('1020');
30             my $response = $request->run();
31            
32             OR
33            
34             my $response = $tracking_object->request(
35             ReferenceNumber => 'myreferencenumber',
36             DestinationPostalCode => '1020',
37             );
38            
39             =head1 DESCRIPTION
40              
41             This class represents a UPS tracking request. You can search either for a
42             UPS TrackingNumber or for a custom ReferenceNumber. Since ReferenceNumbers are
43             not guaranteed to be unique you can provide additional parameters to narrow
44             the ReferenceNumber search.
45              
46             You have to provide either a ReferenceNumber or a TrackingNumber.
47              
48             =head1 ACCESSORS
49              
50             =head2 tracking
51              
52             L<Business::UPS::Tracking> object.
53              
54             =head2 TrackingNumber
55              
56             UPS tracking number. This number has to start with '1Z' and have a valid
57             checksum. You can globally turn off this check by setting
58             C<$Business::UPS::Tracking::CHECKSUM = 0> (which is not recommended,
59             but eg. needed for testing since test shipments at the UPS server do not
60             have a valid checksum)
61              
62             =head2 ReferenceNumber
63              
64             Custom reference number.
65              
66             =head2 ShipperNumber
67              
68             Shipper customer number. Only in combination with L<ReferenceNumber>.
69              
70             =head2 DestinationPostalCode
71              
72             Shipment destination postal code. Only in combination with L<ReferenceNumber>.
73              
74             =head2 DestinationCountryCountry
75              
76             Shipment destination country (<>ISO 3166-1 alpha-2)s. Only in combination
77             with L<ReferenceNumber>.
78              
79             =head2 OriginPostalCode
80              
81             Shipment origin postal code. Only in combination with L<ReferenceNumber>.
82              
83             =head2 OriginCountryCode
84              
85             Shipment origin country (ISO 3166-1 alpha-2). Only in combination
86             with L<ReferenceNumber>.
87              
88             =head2 ShipmentIdentificationNumber
89              
90             Shipment identification number. Only in combination with L<ReferenceNumber>.
91              
92             =head2 PickupDateRangeBegin
93              
94             Shipment pickup range. Either a string formated 'YYYYMMDD' or a L<DateTime>
95             object. Only in combination with L<ReferenceNumber>.
96              
97             =head2 PickupDateRangeEnd
98              
99             Shipment pickup range. Either a string formated 'YYYYMMDD' or a L<DateTime>
100             object. Only in combination with L<ReferenceNumber>.
101              
102             =head2 ShmipmentType
103              
104             Type of shipment. '01' small packackage or '02' freight. Only in combination
105             with L<ReferenceNumber>.
106              
107             =head2 CustomerContext
108              
109             Arbitraty string that will be echoed back by UPS webservice.
110              
111             =head2 IncludeFreight
112              
113             Indicates whether the search should only include freight or small package
114             only. The default is small package only.
115              
116             =cut
117              
118             has 'tracking' => (
119             is => 'rw',
120             required => 1,
121             isa => 'Business::UPS::Tracking',
122             );
123             has 'TrackingNumber' => (
124             is => 'rw',
125             isa => 'Business::UPS::Tracking::Type::TrackingNumber',
126             documentation => 'Shipment tracking number',
127             );
128             has 'ReferenceNumber' => (
129             is => 'rw',
130             isa => 'Str',
131             documentation => 'Shipment reference number',
132             );
133             has 'ShipperNumber' => (
134             is => 'rw',
135             isa => 'Str',
136             documentation => 'Shipper UPS customernumber',
137             );
138             has 'DestinationPostalCode' => (
139             is => 'rw',
140             isa => 'Str',
141             documentation => 'Shipment destination postal code',
142             );
143             has 'DestinationCountryCode' => (
144             is => 'rw',
145             isa => 'Business::UPS::Tracking::Type::CountryCode',
146             documentation => 'Shipment destination country code',
147             );
148             has 'OriginPostalCode' => (
149             is => 'rw',
150             isa => 'Str',
151             documentation => 'Shipment origin postal code',
152             );
153             has 'OriginCountryCode' => (
154             is => 'rw',
155             isa => 'Business::UPS::Tracking::Type::CountryCode',
156             documentation => 'Shipment origin country code',
157             );
158             has 'CustomerContext' => (
159             is => 'rw',
160             isa => 'Str',
161             );
162             has 'ShipmentIdentificationNumber' => (
163             is => 'rw',
164             isa => 'Str',
165             documentation => 'Shipment identification number',
166             );
167             has 'PickupDateRangeBegin' => (
168             is => 'rw',
169             isa => 'Business::UPS::Tracking::Type::DateStr',
170             coerce => 1,
171             documentation => 'Shipment pickup date range begin',
172             );
173             has 'PickupDateRangeEnd' => (
174             is => 'rw',
175             isa => 'Business::UPS::Tracking::Type::DateStr',
176             coerce => 1,
177             documentation => 'Shipment pickup date range end',
178             );
179             has 'ShmipmentType' => (
180             is => 'rw',
181             isa => enum( [ '01', '02' ] ),
182             default => '01',
183             documentation => 'Shipment type ["01" - Small shipment (Default), "02" - Freight ]',
184             );
185             has 'IncludeFreight' => (
186             is => 'rw',
187             isa => 'Bool',
188             default => 0,
189             );
190              
191             =head1 METHODS
192              
193             =head2 tracking_request
194              
195             my $xmlrequest = $request->tracking_request;
196              
197             Generates the xml request body.
198              
199             =cut
200              
201             sub tracking_request {
202             my ($self) = @_;
203              
204             my $dom = XML::LibXML::Document->new('1.0');
205             my $track_request = $dom->createElement('TrackRequest');
206             my $request = $track_request->addNewChild( '', 'Request' );
207             $request->addNewChild( '', 'RequestAction' )->appendTextNode('Track');
208             $request->addNewChild( '', 'RequestOption' )->appendTextNode('activity');
209              
210             # Tracking number search
211             if ( $self->TrackingNumber ) {
212             $track_request->addNewChild( '', 'TrackingNumber' )
213             ->appendTextNode( $self->TrackingNumber );
214             }
215             # Shipment identification number search
216             elsif ( $self->ShipmentIdentificationNumber ) {
217             $track_request->addNewChild( '', 'ShipmentIdentificationNumber' )
218             ->appendTextNode( $self->ShipmentIdentificationNumber );
219             }
220             # Reference number search
221             elsif ( $self->ReferenceNumber ) {
222             $track_request->addNewChild( '', 'ReferenceNumber' )
223             ->addNewChild( '', 'Value' )
224             ->appendTextNode( $self->ReferenceNumber );
225              
226             foreach my $key (
227             qw(ShipperNumber DestinationPostalCode DestinationCountryCode OriginPostalCode OriginCountryCode)
228             )
229             {
230             if ( my $value = $self->$key ) {
231             $track_request->addNewChild( '', $key )
232             ->appendTextNode($value);
233             }
234             }
235              
236             if ( $self->PickupDateRangeBegin && $self->PickupDateRangeEnd ) {
237             my $range = $track_request->addNewChild( '', 'PickupDateRange' );
238             $range->addNewChild( '', 'BeginDate' )
239             ->appendTextNode( $self->PickupDateRangeBegin );
240             $range->addNewChild( '', 'EndDate' )
241             ->appendTextNode( $self->PickupDateRangeEnd );
242             }
243              
244             if ( $self->ShmipmentType ) {
245             my $shipmenttype
246             = $track_request->addNewChild( '', 'ShipmentType' );
247             $shipmenttype->addNewChild( '', 'Code' )
248             ->appendTextNode( $self->ShmipmentType );
249             }
250             }
251             else {
252             Business::UPS::Tracking::X->throw(
253             "Please provide either 'TrackingNumber','ShipmentIdentificationNumber' or 'ReferenceNumber'"
254             );
255             }
256              
257             # Small package only or small package and freight
258             if ( $self->IncludeFreight ) {
259             $track_request->addNewChild( '', 'IncludeFreight' )
260             ->appendTextNode('01');
261             }
262              
263             # Customer context
264             if ( $self->CustomerContext ) {
265             $request->addNewChild( '', 'TransactionReference' )
266             ->addNewChild( '', 'CustomerContext' )
267             ->appendTextNode( $self->CustomerContext );
268             }
269              
270             $dom->setDocumentElement($track_request);
271             return $dom->toString();
272             }
273              
274             =head2 run
275              
276             my $response = $request->run;
277              
278             Executes the request and returns either an exception or a
279             L<Business::UPS::Tracking::Response> object.
280              
281             =cut
282              
283             sub run {
284             my ($self) = @_;
285              
286             my $tracking = $self->tracking;
287              
288             # Get request string
289             my $content = $tracking->access_request . "\n" . $self->tracking_request;
290              
291             my $count = 0;
292             while (1) {
293             # HTTP request
294             my $response = $tracking->_ua->post(
295             $tracking->url,
296             Content_Type => 'text/xml',
297             Content => $content,
298             );
299             # Success
300             if ( $response->is_success ) {
301             return try {
302             return Business::UPS::Tracking::Response->new(
303             request => $self,
304             xml => $response->content,
305             );
306             } catch {
307             my $e = $_;
308             if (defined $e
309             && ref $e
310             && $e->isa('Business::UPS::Tracking::X')) {
311             $e->rethrow();
312             } else {
313             Business::UPS::Tracking::X->throw($e || 'Unknown error');
314             }
315             }
316             }
317             # Failed but try again
318             elsif ( $count < $tracking->retry_http ) {
319             $count++;
320             sleep 1;
321             next;
322             }
323             # Failed and stop trying
324             else {
325             Business::UPS::Tracking::X::HTTP->throw(
326             error => $response->status_line,
327             http_response => $response,
328             request => $self
329             );
330             }
331             }
332             return;
333             }
334              
335             =head1 METHODS
336              
337             =head2 meta
338              
339             Moose meta method
340              
341             =cut
342              
343             __PACKAGE__->meta->make_immutable;
344             no Moose;
345             1;