File Coverage

blib/lib/Business/NAB/Australian/DirectEntry/Payments.pm
Criterion Covered Total %
statement 62 62 100.0
branch 5 6 83.3
condition 7 8 87.5
subroutine 11 11 100.0
pod 2 2 100.0
total 87 89 97.7


line stmt bran cond sub pod time code
1             package Business::NAB::Australian::DirectEntry::Payments;
2             $Business::NAB::Australian::DirectEntry::Payments::VERSION = '0.03';
3             =head1 NAME
4              
5             Business::NAB::Australian::DirectEntry::Payments
6              
7             =head1 SYNOPSIS
8              
9             use Business::NAB::Australian::DirectEntry::Payments;
10              
11             # parse:
12             my $Payments = Business::NAB::Australian::DirectEntry::Payments
13             ->new_from_file( $file_path );
14              
15             foreach my $DetailRecord ( $Payments->detail_record->@* ) {
16             ...
17             }
18              
19             # build:
20             my $Payments = Business::NAB::Australian::DirectEntry::Payments->new;
21              
22             $Payments->add_descriptive_record(
23             .. # Business::NAB:: ... DescriptiveRecord object
24             );
25              
26             $Payments->add_detail_record(
27             .. # Business::NAB:: ... DetailRecord object
28             ) for ( @payments );
29              
30             # optional:
31             $Payments->add_total_record(
32             .. # Business::NAB:: ... TotalRecord object
33             );
34              
35             $Payments->to_file(
36             $file_path,
37             $bsb_number, # if TotalRecord is not set
38             $separator, # defaults to "\r\n"
39             );
40              
41             =head1 DESCRIPTION
42              
43             Class for building/parsing a "Australian Direct Entry Payments" file
44              
45             =cut
46              
47 3     3   1585432 use strict;
  3         8  
  3         163  
48 3     3   21 use warnings;
  3         9  
  3         223  
49 3     3   24 use feature qw/ signatures /;
  3         6  
  3         495  
50 3     3   2273 use autodie qw/ :all /;
  3         63808  
  3         18  
51 3     3   83050 use Carp qw/ croak /;
  3         9  
  3         239  
52              
53 3     3   1633 use Moose;
  3         1386583  
  3         30  
54             with 'Business::NAB::Role::AttributeContainer';
55             extends 'Business::NAB::FileContainer';
56              
57 3     3   31512 use Moose::Util::TypeConstraints;
  3         7  
  3         42  
58 3     3   9842 no warnings qw/ experimental::signatures /;
  3         7  
  3         220  
59              
60 3     3   25 use List::Util qw/ sum0 /;
  3         13  
  3         3027  
61              
62             # we have long namespaces and use them multiple times so have
63             # normalised them out into the $parent and @subclasses below
64             my $parent = 'Business::NAB::Australian::DirectEntry::Payments';
65              
66             my @subclasses = (
67             qw/
68             DescriptiveRecord
69             DetailRecord
70             TotalRecord
71             /
72             );
73              
74             =head1 ATTRIBUTES
75              
76             All attributes are ArrayRef[Obj] where Obj are of the Business::NAB::Australian::DirectEntry::Payments::* namespace:
77              
78             DescriptiveRecord
79             DetailRecord
80             TotalRecord
81              
82             Convenience methods are available for trivial addition of new elements
83             to the arrays:
84              
85             $Payments->add_descriptive_record( $DescriptiveRecord );
86             $Payments->add_detail_record( $DetailRecord );
87             $Payments->add_total_record( $TotalRecord );
88              
89             =over
90              
91             =item descriptive_record (ArrayRef[Obj])
92              
93             =item detail_record (ArrayRef[Obj])
94              
95             =item total_record (ArrayRef[Obj])
96              
97             =back
98              
99             =cut
100              
101             __PACKAGE__->load_attributes( $parent, @subclasses );
102              
103             =head1 METHODS
104              
105             =head2 new_from_file
106              
107             Returns a new instance of the class with attributes populated from
108             the result of parsing the passed file
109              
110             my $Payments = Business::NAB::Australian::DirectEntry::Payments
111             ->new_from_file( $file_path );
112              
113             =cut
114              
115             sub new_from_file (
116 2         7 $class, $file, $class_map = undef, $sub_parent = undef
  2         6  
  2         6  
117 2     2 1 2905 ) {
  2         6  
  2         85  
118              
119 2         21 my %sub_class_map = (
120             0 => 'DescriptiveRecord',
121             1 => 'DetailRecord',
122             7 => 'TotalRecord',
123             );
124              
125 2         132 my $self = $class->new;
126              
127 2   66     38 return $self->SUPER::new_from_file(
      100        
128             ( $sub_parent // $parent ),
129             $file,
130             ( $class_map // \%sub_class_map ),
131             );
132             }
133              
134             =head2 to_file
135              
136             Writes the file content to the passed file path:
137              
138             $Payments->to_file(
139             $file_path,
140             $bsb_number, # if TotalRecord is not set, defaults to 999-999
141             $separator, # defaults to "\r\n"
142             );
143              
144             This also does some validation on the totals in the records.
145              
146             =cut
147              
148             sub to_file (
149 5         15 $self,
150 5         10 $file,
151 5         15 $bsb_number = "999-999",
152 5         12 $sep = "\r\n",
153 5     5 1 24317 ) {
  5         13  
154              
155 5         39 open( my $fh, '>', $file );
156              
157 5         6046 print $fh $self->descriptive_record->[ 0 ]->to_record . $sep;
158 5         42 print $fh $_->to_record . $sep foreach $self->detail_record->@*;
159              
160 5         31 my $record_count = scalar( $self->detail_record->@* );
161              
162 3         137 my $credit_total = sum0 map { $_->amount }
163 5         41 grep { $_->is_credit } $self->detail_record->@*;
  37         272  
164 34         1635 my $debit_total = sum0 map { $_->amount }
165 5         39 grep { $_->is_debit } $self->detail_record->@*;
  37         202  
166              
167 5         19 my $net_total = abs( $credit_total - $debit_total );
168              
169             # net total should be zero as it is the net of credit - debit records
170             # and there should always be a record that nets off the other record
171             # type totals (to describe where funds go to/from). however if this is
172             # a returns file then this check does not apply
173 5 100 100     48 if (
174             $net_total != 0
175             && $self->blessed !~ /::Returns$/
176             ) {
177 1         31 croak(
178             "Net total not equal to zero ($net_total) file detail records do"
179             . " not balance, you have debits missing a credit or vice-versa"
180             );
181             }
182              
183 4 100       31 if ( my $TotalRecord = $self->total_record->[ 0 ] ) {
184 3         68 print $fh $TotalRecord->to_record . $sep;
185             } else {
186 1 50       31 croak( "BSB number is required if total_record is not set" )
187             if !$bsb_number;
188              
189 1         46 my $TotalRecord = Business::NAB::Australian::DirectEntry::Payments::TotalRecord->new(
190             bsb_number => $bsb_number,
191             net_total_amount => $net_total,
192             credit_total_amount => $credit_total,
193             debit_total_amount => $debit_total,
194             record_count => $record_count,
195             );
196              
197 1         6 print $fh $TotalRecord->to_record . $sep;
198             }
199              
200 4         29 close( $fh );
201              
202 4         3140 return 1;
203             }
204              
205             =head1 SEE ALSO
206              
207             L<Business::NAB::Australian::DirectEntry::Payments::DescriptiveRecord>
208              
209             L<Business::NAB::Australian::DirectEntry::Payments::DetailRecord>
210              
211             L<Business::NAB::Australian::DirectEntry::Payments::TotalRecord>
212              
213             =cut
214              
215             __PACKAGE__->meta->make_immutable;