RT# 74789 - updated format to handle credits in seperated batch files
[freeside.git] / FS / FS / pay_batch / td_eft1464.pm
index 1fbf2ad..e16b0ee 100644 (file)
@@ -48,87 +48,13 @@ my $i;
 
 $name = 'td_eft1464';
 # TD Bank EFT 1464 Byte format
+# https://www.payments.ca/sites/default/files/standard-005.pdf
 
-%import_info = (
-  'filetype'    => 'variable',
-  'parse'       => \&parse,
-  'fields' => [ qw(
-    status
-    paid
-    paybatchnum
-    ) ],
-  'hook' => sub {
-      my $hash = shift;
-      $hash->{'_date'} = time;
-      $hash->{'paid'} = sprintf('%.2f', $hash->{'paid'});
-  },
-  'approved'    => sub { 
-      my $hash = shift;
-      $hash->{'status'} eq 'A'
-  },
-  'declined'    => sub {
-      my $hash = shift;
-      $hash->{'status'} eq 'D';
-  },
-  'begin_condition' => sub {
-      my $hash = shift;
-      $hash->{'status'} eq 'A' or $hash->{'status'} eq 'D';
-  },
-  'end_condition' => sub {
-      my $hash = shift;
-      $hash->{'status'} eq 'END'
-  },
-  'close_condition' => sub {
-      my $batch = shift;
-      my @cust_pay_batch = qsearch('cust_pay_batch', 
-        { batchnum => $batch->batchnum }
-      );
-      return ( (grep {! length($_->status) } @cust_pay_batch) == 0 );
-  },
-);
-
-sub parse {
-  my ($batch, $line) = @_;
-  $batch->setfield('import_state','') if !$batch->import_state;
-  return 'END' if $batch->import_state eq 'END';
-  if( $batch->import_state eq '212' ) {
-    # APX212 fields:
-    # trace number, trans type, amount, due date, routing number, 
-    # account number, xref number, return routing number and account
-    # The only ones we take are amount and xref number.
-    if( $line =~ /CREDITS\s+DEBITS/ ) {
-      $batch->setfield('import_state', 'END');
-      return 'END';
-    }
-    $line =~ /^\d{22} D\d{3} (.{14})    \d{5}  \d{4}-\d{5}  .{12}    (.{19}).*$/
-      or die "can't parse: $line";
-    # strip leading zeroes/spaces from paybatchnum at this point
-    return ('A', $1, sprintf('%u',$2));
-  }
-  elsif( $batch->import_state eq '234' ) {
-    # APX234 fields:
-    # payor name, xref number, due date, routing number, account number,
-    # amount, reason for return
-    if( $line =~ /TOTAL NUMBER -/ ) {
-      $batch->setfield('import_state', 'END');
-      return 'END';
-    }
-    $line =~ /^.{22} (.{19})   \d\d\/\d\d\/\d\d  \d{9}  .{12} (.{14}).*$/
-      or die "can't parse: $line";
-    return ('D', $2, sprintf('%u',$1));
-  }
-  else {
-    if ( $line =~ /ITEM TRACE NUMBER/ ) {
-      $batch->setfield('import_state','212');
-    }
-    elsif ( $line =~ /REASON FOR RETURN/ ) {
-      $batch->setfield('import_state','234');
-    } # else leave it undefined
-    return 'HEADER';
-  }
-}
+%import_info = ( filetype => 'NONE' ); 
+# just to suppress warning; importing this format is a fatal error
 
 %export_info = (
+  delimiter => '',
   init => sub {
     $conf = shift;
     @opt{
@@ -140,11 +66,11 @@ sub parse {
       'retacct',
       'cpacode',
     } = $conf->config("batchconfig-td_eft1464");
-    $opt{'origid'} = sprintf('%-10s', $opt{'origid'});
-    $opt{'shortname'} = sprintf('%-15s', $opt{'shortname'});
-    $opt{'longname'} = sprintf('%-30s', $opt{'longname'});
-    $opt{'retbranch'} = '0004'.sprintf('%5s',$opt{'retbranch'});
-    $opt{'retacct'} = sprintf('%-11s', $opt{'retacct'}). ' ';
+    $opt{'origid'} = sprintf('%-10.10s', $opt{'origid'});
+    $opt{'shortname'} = sprintf('%-15.15s', $opt{'shortname'});
+    $opt{'longname'} = sprintf('%-30.30s', $opt{'longname'});
+    $opt{'retbranch'} = '0004'.sprintf('%5.5s',$opt{'retbranch'});
+    $opt{'retacct'} = sprintf('%-11.11s', $opt{'retacct'}). ' ';
     $i = 1;
   },
   header => sub { 
@@ -152,13 +78,13 @@ sub parse {
     my @cust_pay_batch = @{(shift)};
     my $time = $pay_batch->download || time;
     my $now = sprintf("%03u%03u", 
-      (localtime(time))[5],#year since 1900
+      (localtime(time))[5] % 100,#year since 1900
       (localtime(time))[7]+1);#day of year
 
     # Request settlement the next day
     my $duedate = time+86400;
     $opt{'due'} = sprintf("%03u%03u",
-      (localtime($duedate))[5],
+      (localtime($duedate))[5] % 100,
       (localtime($duedate))[7]+1);
 
     $opt{'fcn'} = 
@@ -170,17 +96,25 @@ sub parse {
       $opt{'fcn'},
       $now,
       $opt{'datacenter'},
-      ' ' x 1429 #filler
+      ' ' x 1429, #filler
     );
   },
   row => sub {
     my ($cust_pay_batch, $pay_batch) = @_;
     my ($account, $aba) = split('@', $cust_pay_batch->payinfo);
+    if ( $aba =~ /^(\d+)\.(\d+)$/ ) {  #branch.route
+      $aba = $2.$1; #routebranch
+    }
     $i++;
     # The 1464 byte format supports up to 5 payments per line,
     # but we're only going to send 1.
+
+    ## set to D for debit by default, then override to what cust_pay_batch has as payments may not have paycode.
+    my $debitorcredit = 'D';
+    $debitorcredit = $cust_pay_batch->paycode unless !$cust_pay_batch->paycode;
+
     my $control = join('',
-      'D',                  # for 'debit'
+      $debitorcredit,       # D for 'debit' or C for Credit
       sprintf("%09u", $i),  #record number
       $opt{'origid'},
       $opt{'fcn'},
@@ -190,40 +124,59 @@ sub parse {
       sprintf('%010.0f', $cust_pay_batch->amount*100),
       $opt{'due'}, #due date...? XXX
       sprintf('%09u', $aba),
-      sprintf('%-12s', $account),
-      ' ' x 22,
-      ' ' x 3,
+      sprintf('%-12.12s', $account),
+      '0' x 22,
+      '0' x 3,
       $opt{'shortname'},
-      sprintf('%-30s', 
+      sprintf('%-30.30s', 
         join(' ',
           $cust_pay_batch->first, $cust_pay_batch->last)
       ),
       $opt{'longname'},
       $opt{'origid'},
-      sprintf('%-19s', $cust_pay_batch->paybatchnum), # originator reference num
+      sprintf('%-19.19s', $cust_pay_batch->paybatchnum), # originator reference num
       $opt{'retbranch'},
       $opt{'retacct'}, 
+      ' ' x 15,
       ' ' x 22,
       ' ' x 2,
       '0' x 11,
     );
-    return $control . $payment . (' ' x 720);
+    return sprintf('%-1464s',$control . $payment) ;
   },
   footer => sub {
     my ($pay_batch, $batchcount, $batchtotal) = @_;
+    my $totaldebittxns = $pay_batch->type eq "DEBIT" ? $batchtotal*100 : 0;
+    my $countdebittxns = $pay_batch->type eq "DEBIT" ? $batchcount : 0;
+    my $totalcredittxns = $pay_batch->type eq "CREDIT" ? $batchtotal*100 : 0;
+    my $countcredittxns = $pay_batch->type eq "CREDIT" ? $batchcount : 0;
     join('',
       'Z',
       sprintf('%09u', $batchcount + 2),
       $opt{'origid'}, 
       $opt{'fcn'},
-      sprintf('%014.0f', $batchtotal*100), # total of debit txns
-      sprintf('%08u', $batchcount), # number of debit txns
-      '0' x 14, # total of credit txns
-      '0' x 8, # total of credit txns
+      sprintf('%014.0f', $totaldebittxns), # total of debit txns
+      sprintf('%08u', $countdebittxns), # number of debit txns
+      sprintf('%014.0f', $totalcredittxns), # total of debit txns
+      sprintf('%08u', $countcredittxns), # number of debit txns
       ' ' x 1396,
     )
   },
 );
 
+## this format can handle credit transactions
+sub can_handle_credits {
+  1;
+}
+
+sub _upgrade_gateway {
+  my $conf = FS::Conf->new;
+  my @batchconfig = $conf->config('batchconfig-td_eft1464');
+  my %options;
+  @options{ qw(originator datacentre short_name long_name return_branch 
+               return_account cpa_code) } = @batchconfig;
+  ( 'TD_EFT', %options );
+}
+
 1;