RT#33582: RBC return batch processing failure
[freeside.git] / FS / FS / pay_batch / RBC.pm
1 package FS::pay_batch::RBC;
2
3 use strict;
4 use vars qw(@ISA %import_info %export_info $name);
5 use Date::Format 'time2str';
6 use FS::Conf;
7
8 my $conf;
9 my ($client_num, $shortname, $longname, $trans_code, $i);
10
11 $name = 'RBC';
12 # Royal Bank of Canada ACH Direct Payments Service
13
14 %import_info = (
15   'filetype'    => 'fixed',
16   'formatre'    => 
17   '^([0134]).{18}(.{4}).{3}(.).{11}(.{19}).{6}(.{30}).{17}(.{9})(.{18}).{6}(.{14}).{23}(.).{9}\r?$',
18   'fields' => [ qw(
19     recordtype
20     batchnum
21     subtype
22     paybatchnum
23     custname
24     bank
25     payinfo
26     paid
27     status
28     ) ],
29   'hook' => sub {
30       my $hash = shift;
31       $hash->{'paid'} = sprintf("%.2f", $hash->{'paid'} / 100 );
32       $hash->{'_date'} = time;
33       $hash->{'payinfo'} =~ s/^(\S+).*/$1/; # these often have trailing spaces
34       $hash->{'payinfo'} = $hash->{'payinfo'} . '@' . $hash->{'bank'};
35   },
36   'approved'    => sub { 
37       my $hash = shift;
38       $hash->{'status'} eq ' '
39   },
40   'declined'    => sub {
41       my $hash = shift;
42       grep { $hash->{'status'} eq $_ } ('E', 'R', 'U', 'T');
43   },
44   'begin_condition' => sub {
45       my $hash = shift;
46       $hash->{recordtype} eq '1'; # Detail Record
47   },
48   'end_hook'    => sub {
49       my( $hash, $total, $line ) = @_;
50       $total = sprintf("%.2f", $total);
51       # We assume here that this is an 'All Records' or 'Input Records'
52       # report.
53       my $batch_total = sprintf("%.2f", substr($line, 59, 18) / 100);
54       return "Our total $total does not match bank total $batch_total!"
55         if $total != $batch_total;
56       '';
57   },
58   'end_condition' => sub {
59       my $hash = shift;
60       $hash->{recordtype} eq '4'; # Client Trailer Record
61   },
62   'skip_condition' => sub {
63       my $hash = shift;
64       $hash->{'recordtype'} eq '3' ||
65         $hash->{'subtype'} ne '0';
66   },
67 );
68
69 %export_info = (
70   init => sub {
71     $conf = shift;
72     ($client_num,
73      $shortname,
74      $longname,
75      $trans_code, 
76      ) = $conf->config("batchconfig-RBC");
77     $i = 1;
78   },
79   header => sub { 
80     my $pay_batch = shift;
81     '$$AAPASTD0152[PROD[NL$$'."\n".
82     '000001'.
83     'A'.
84     'HDR'.
85     sprintf("%10s", $client_num).
86     sprintf("%-30s", $longname).
87     sprintf("%04u", $pay_batch->batchnum).
88     time2str("%Y%j", $pay_batch->download).
89     'CAD'.
90     '1'.
91     ' ' x 87  # filler/reserved fields
92     ;
93   },
94   row => sub {
95     my ($cust_pay_batch, $pay_batch) = @_;
96     my ($account, $aba) = split('@', $cust_pay_batch->payinfo);
97     my($bankno, $branch);
98     if ( $aba =~ /^0(\d{3})(\d{5})$/ ) { # standard format for Canadian bank ID
99       ($bankno, $branch) = ( $1, $2 );
100     } elsif ( $aba =~ /^(\d{5})\.(\d{3})$/ ) { #how we store branches
101       ($branch, $bankno) = ( $1, $2 );
102     } else {
103       die "invalid branch/routing number '$aba'\n";
104     }
105
106     $i++;
107     sprintf("%06u", $i).
108     'D'.
109     sprintf("%3s",$trans_code).
110     sprintf("%10s",$client_num).
111     ' '.
112     sprintf("%-19s", $cust_pay_batch->paybatchnum).
113     '00'.
114     sprintf("%04s", $bankno).
115     sprintf("%05s", $branch).
116     sprintf("%-18s", $account).
117     ' '.
118     sprintf("%010.0f",$cust_pay_batch->amount*100).
119     '      '.
120     time2str("%Y%j", $pay_batch->download).
121     sprintf("%-30s", $cust_pay_batch->cust_main->first . ' ' .
122                      $cust_pay_batch->cust_main->last).
123     'E'. # English
124     ' '.
125     sprintf("%-15s", $shortname).
126     'CAD'.
127     ' '.
128     'CAN'.
129     '    '.
130     'N' # no customer optional information follows
131     ;
132 # Note: IAT Address Information and Remittance records are not 
133 # supported. This means you probably can't process payments 
134 # destined to U.S. bank accounts.  If you need this feature, contact 
135 # Freeside Internet Services.
136   },
137   footer => sub {
138     my ($pay_batch, $batchcount, $batchtotal) = @_;
139     sprintf("%06u", $i + 1).
140     'Z'.
141     'TRL'.
142     sprintf("%10s", $client_num).
143     '0' x 20 .
144     sprintf("%06u", $batchcount).
145     sprintf("%014.0f", $batchtotal*100).
146     '00' .
147     '000000' . # total number of customer information records
148     ' ' x 84
149     ;
150   },
151 );
152
153 1;
154