File Coverage

blib/lib/Business/NAB/Acknowledgement.pm
Criterion Covered Total %
statement 111 111 100.0
branch 10 12 83.3
condition 1 3 33.3
subroutine 22 22 100.0
pod 8 8 100.0
total 152 156 97.4


line stmt bran cond sub pod time code
1             package Business::NAB::Acknowledgement;
2             $Business::NAB::Acknowledgement::VERSION = '0.03';
3             =head1 NAME
4              
5             Business::NAB::Acknowledgement
6              
7             =head1 SYNOPSIS
8              
9             my $Ack = $class->new_from_xml(
10             $path_to_xml_file, # or an XML string
11             );
12              
13             my $dom = $Ack->dom; # access to XML::LibXML::Document
14             my $DateTime = $Ack->date;
15              
16             if ( $Ack->is_accepted ) {
17             ...
18             }
19              
20             =head1 DESCRIPTION
21              
22             Class for parsing NAB file acknowledgements, which are XML files using
23             some long-forgotten schema from Oracle from the late 1990s. There is no XSD
24             or DTD...
25              
26             The various elements are described in section 4 of the NAB "Australian Direct
27             Entry Payments and Dishonour Report".
28              
29             =cut
30              
31 1     1   1170128 use strict;
  1         3  
  1         47  
32 1     1   7 use warnings;
  1         2  
  1         99  
33 1     1   11 use feature qw/ signatures /;
  1         3  
  1         166  
34 1     1   677 use autodie qw/ :all /;
  1         20089  
  1         5  
35 1     1   22371 use Carp qw/ croak /;
  1         3  
  1         61  
36 1     1   1478 use XML::LibXML;
  1         42377  
  1         9  
37              
38 1     1   872 use Moose;
  1         612602  
  1         9  
39             with 'Business::NAB::Role::AttributeContainer';
40             extends 'Business::NAB::FileContainer';
41              
42 1     1   10865 use Moose::Util::TypeConstraints;
  1         2  
  1         12  
43 1     1   3198 no warnings qw/ experimental::signatures /;
  1         2  
  1         60  
44              
45 1     1   951 use Business::NAB::Types;
  1         7  
  1         1488  
46              
47             =head1 ATTRIBUTES
48              
49             =over
50              
51             =item dom (XML::LibXML::Document)
52              
53             The resulting object from parsing the XML, should you want to do
54             anything more bespoke with it
55              
56             =item date (NAB::Type::Date, required)
57              
58             DateTime the acknowledgement was generated
59              
60             =item result (Str, required)
61              
62             Inferred from the document's root element, usually the C<type>
63             attribute, which the documentation lists as:
64              
65             * info - "Standard Acknowledgement"
66             * warn - File not processed, requires intervention (approval)
67             * error - File not processed, requires review and resubmission
68              
69             =item status (Str, required)
70              
71             The acknowledgement status, inferred from the user_message element
72             or the root element:
73              
74             * accepted
75             * processed
76             * rejected
77             * pending
78             * declined
79              
80             =item customer_id (Str, required)
81              
82             NAB Direct Link Mailbox ID
83              
84             =item company_name (Str, required)
85              
86             Registered NAB Direct Link customer name
87              
88             =item original_message_id (Str, required)
89              
90             =item original_filename (Str, required)
91              
92             Original file name
93              
94             =item data_type (Str, optional)
95              
96             =item data_type_description (Str, optional)
97              
98             =item user_message (Str, optional)
99              
100             Short description of the current status of the file
101              
102             =item detailed_message (Str, optional)
103              
104             Long description of the current status of the file
105              
106             =item issue (ArrayRef[Business::NAB::Acknowledgement::Issue], optional)
107              
108             An arrayref of objects that describe the payment processing
109              
110             =back
111              
112             =cut
113              
114             has [ qw/ dom / ] => (
115             is => 'ro',
116             isa => 'XML::LibXML::Document',
117             required => 1,
118             );
119              
120             has 'date' => (
121             is => 'ro',
122             isa => 'NAB::Type::Date',
123             coerce => 1,
124             required => 1,
125             );
126              
127             has [
128             qw/
129             result
130             customer_id
131             company_name
132             original_message_id
133             original_filename
134             status
135             /
136             ] => (
137             is => 'ro',
138             isa => 'Str',
139             required => 1,
140             );
141              
142             has [
143             qw/
144             data_type
145             data_type_description
146             user_message
147             detailed_message
148             /
149             ] => (
150             is => 'ro',
151             isa => 'Str',
152             required => 0,
153             );
154              
155             __PACKAGE__->load_attributes(
156             'Business::NAB::Acknowledgement',
157             'Issue',
158             );
159              
160             =head1 METHODS
161              
162             =head2 new_from_xml
163              
164             Parses the given XML file (or string) and returns a new instance of the
165             class with the necessary attributes populates:
166              
167             my $Ack = Business::NAB::Acknowledgement->new_from_xml( $xml );
168              
169             =cut
170              
171 4     4 1 21176 sub new_from_xml ( $class, $file_or_string ) {
  4         15  
  4         11  
  4         10  
172              
173 4 100       305 my $source = -f $file_or_string ? 'location' : 'string';
174 4         51 my $dom = XML::LibXML->load_xml(
175             $source => $file_or_string,
176             );
177              
178 4         2537 my %attributes;
179              
180 4 100       36 if ( my ( $Node ) = $dom->findnodes( '//MessageAcknowledgement' ) ) {
    100          
181              
182             # message ack type
183 1         44 %attributes = _parse_message_ack( $Node );
184              
185             } elsif ( ( $Node ) = $dom->findnodes( '//PaymentsAcknowledgement' ) ) {
186              
187             # payments ack type
188 2         164 %attributes = _parse_payments_ack( $Node );
189              
190             } else {
191 1         140 croak(
192             "Unknown acknowledgement type: "
193             . $dom->documentElement->nodeName
194             );
195             }
196              
197 3         266 return $class->new(
198             dom => $dom,
199             %attributes,
200             );
201             }
202              
203             =head2 is_accepted
204              
205             =head2 is_processed
206              
207             =head2 is_pending
208              
209             =head2 is_rejected
210              
211             =head2 is_declined
212              
213             =head2 is_received
214              
215             =head2 is_held
216              
217             Boolean checks on the acknowledgement:
218              
219             if ( $Ack->is_accepted ) {
220             ...
221             }
222              
223             =cut
224              
225 3     3 1 11 sub is_accepted ( $self ) {
  3         7  
  3         7  
226 3   33     10 return $self->_is_status( 'accepted' )
227             || $self->_is_status( 'success' );
228             }
229              
230 3     3 1 9 sub is_processed ( $self ) { return $self->_is_status( 'processed' ) }
  3         6  
  3         7  
  3         11  
231 3     3 1 9 sub is_pending ( $self ) { return $self->_is_status( 'pending' ) }
  3         8  
  3         7  
  3         9  
232 3     3 1 4205 sub is_rejected ( $self ) { return $self->_is_status( 'rejected' ) }
  3         9  
  3         6  
  3         18  
233 3     3 1 8 sub is_held ( $self ) { return $self->_is_status( 'held' ) }
  3         9  
  3         6  
  3         10  
234 3     3 1 11 sub is_declined ( $self ) { return $self->_is_status( 'declined' ) }
  3         38  
  3         6  
  3         10  
235 3     3 1 9 sub is_received ( $self ) { return $self->_is_status( 'received' ) }
  3         7  
  3         5  
  3         13  
236              
237 24     24   67 sub _is_status ( $self, $status ) {
  24         33  
  24         59  
  24         37  
238 24 50       1170 return $self->status eq $status
    100          
239             ? 1
240             : $self->result =~ /$status/i ? 1 : 0;
241             }
242              
243 1     1   2 sub _parse_message_ack ( $Node ) {
  1         3  
  1         2  
244              
245 1         6 my %attributes = _parse_common_ack( $Node );
246              
247 1         54 $attributes{ status } = lc( $Node->getAttribute( 'type' ) );
248              
249 1 50       23 if ( my ( $Message ) = $Node->findnodes( './MessageDetails' ) ) {
250              
251             $attributes{ original_message_id }
252 1         31 = $Message->findvalue( './OriginalMessageId' );
253             $attributes{ original_filename }
254 1         92 = $Message->findvalue( './OriginalFilename' );
255              
256 1         67 $attributes{ data_type } = $Message->findvalue( './Datatype' );
257             $attributes{ data_type_description }
258 1         66 = $Message->findvalue( './DatatypeDescription' );
259              
260 1         78 $attributes{ data_type_description } =~ s/\s+$//;
261             }
262              
263 1         10 return %attributes;
264             }
265              
266 2     2   7 sub _parse_payments_ack ( $Node ) {
  2         7  
  2         5  
267              
268 2         11 my %attributes = _parse_common_ack( $Node );
269              
270             $attributes{ original_message_id }
271 2         202 = $Node->findvalue( './OriginalMessageId' );
272             $attributes{ original_filename }
273 2         170 = $Node->findvalue( './OriginalFilename' );
274              
275 2         144 $attributes{ user_message } = $Node->findvalue( './UserMessage' );
276              
277             ( $attributes{ status } ) = (
278             $attributes{ user_message }
279 2         194 =~ /(ACCEPTED|PROCESSED|REJECTED|PENDING|DECLINED|SUCCESS|HELD)/i
280             );
281              
282 2         13 $attributes{ status } = lc( $attributes{ status } );
283              
284             $attributes{ detailed_message }
285 2         9 = $Node->findvalue( './DetailedMessage' );
286              
287 2         176 return %attributes;
288             }
289              
290 3     3   39 sub _parse_common_ack ( $Node ) {
  3         8  
  3         6  
291              
292 3         6 my %attributes;
293              
294 3         19 $attributes{ result } = $Node->getAttribute( 'type' );
295 3         67 $attributes{ issue } = [];
296              
297 3         21 $attributes{ date } = $Node->findvalue( './DateTime' );
298 3         474 $attributes{ customer_id } = $Node->findvalue( './CustomerId' );
299 3         259 $attributes{ company_name } = $Node->findvalue( './CompanyName' );
300              
301 3         214 foreach my $Issue ( $Node->findnodes( './Issues/Issue' ) ) {
302              
303             push(
304 15         333 @{ $attributes{ issue } },
  15         45  
305             {
306             code => $Issue->getAttribute( 'type' ),
307             itemId => $Issue->getAttribute( 'itemId' ),
308             detail => $Issue->to_literal,
309             },
310             );
311             }
312              
313 3         78 return %attributes;
314             }
315              
316             =head1 SEE ALSO
317              
318             L<Business::NAB::Types>
319              
320             L<Business::NAB::Acknowledgement::Issue>
321              
322             =cut
323              
324             __PACKAGE__->meta->make_immutable;