#26342 RBC fixes
[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   '^(.).{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->{'subtype'} ne '0';
65   },
66 );
67
68 %export_info = (
69   init => sub {
70     $conf = shift;
71     ($client_num,
72      $shortname,
73      $longname,
74      $trans_code, 
75      ) = $conf->config("batchconfig-RBC");
76     $i = 1;
77   },
78   header => sub { 
79     my $pay_batch = shift;
80     '$$AAPASTD0152[PROD[NL$$'."\n".
81     '000001'.
82     'A'.
83     'HDR'.
84     sprintf("%10s", $client_num).
85     sprintf("%-30s", $longname).
86     sprintf("%04u", $pay_batch->batchnum).
87     time2str("%Y%j", $pay_batch->download).
88     'CAD'.
89     '1'.
90     ' ' x 87  # filler/reserved fields
91     ;
92   },
93   row => sub {
94     my ($cust_pay_batch, $pay_batch) = @_;
95     my ($account, $aba) = split('@', $cust_pay_batch->payinfo);
96     my($bankno, $branch);
97     if ( $aba =~ /^0(\d{3})(\d{5})$/ ) { # standard format for Canadian bank ID
98       ($bankno, $branch) = ( $1, $2 );
99     } elsif ( $aba =~ /^(\d{5})\.(\d{3})$/ ) { #how we store branches
100       ($branch, $bankno) = ( $1, $2 );
101     } else {
102       die "invalid branch/routing number '$aba'\n";
103     }
104
105     $i++;
106     sprintf("%06u", $i).
107     'D'.
108     sprintf("%3s",$trans_code).
109     sprintf("%10s",$client_num).
110     ' '.
111     sprintf("%-19s", $cust_pay_batch->paybatchnum).
112     '00'.
113     sprintf("%04u", $bankno).
114     sprintf("%05u", $branch).
115     sprintf("%-18u", $account).
116     ' '.
117     sprintf("%010.0f",$cust_pay_batch->amount*100).
118     '      '.
119     time2str("%Y%j", $pay_batch->download).
120     sprintf("%-30s", $cust_pay_batch->cust_main->first . ' ' .
121                      $cust_pay_batch->cust_main->last).
122     'E'. # English
123     ' '.
124     sprintf("%-15s", $shortname).
125     'CAD'.
126     ' '.
127     'CAN'.
128     '    '.
129     'N' # no customer optional information follows
130     ;
131 # Note: IAT Address Information and Remittance records are not 
132 # supported. This means you probably can't process payments 
133 # destined to U.S. bank accounts.  If you need this feature, contact 
134 # Freeside Internet Services.
135   },
136   footer => sub {
137     my ($pay_batch, $batchcount, $batchtotal) = @_;
138     sprintf("%06u", $i + 1).
139     'Z'.
140     'TRL'.
141     sprintf("%10s", $client_num).
142     '0' x 20 .
143     sprintf("%06u", $batchcount).
144     sprintf("%014.0f", $batchtotal*100).
145     '00' .
146     '000000' . # total number of customer information records
147     ' ' x 84
148     ;
149   },
150 );
151
152 1;
153