fix long package locations showing up as line item on typeset invoices, RT#10093
[freeside.git] / FS / FS / pay_batch.pm
index ffa6e20..afff261 100644 (file)
@@ -4,10 +4,10 @@ use strict;
 use vars qw( @ISA $DEBUG %import_info %export_info $conf );
 use Time::Local;
 use Text::CSV_XS;
-use XML::Simple qw(XMLin XMLout);
 use FS::Record qw( dbh qsearch qsearchs );
 use FS::cust_pay;
 use FS::Conf;
+use Business::CreditCard qw(cardtype);
 
 @ISA = qw(FS::Record);
 
@@ -196,13 +196,19 @@ sub import_results {
   my $info = $import_info{$format}
     or die "unknown format $format";
 
+  my $job = $param->{'job'};
+  $job->update_statustext(0) if $job;
+
+  my $conf = new FS::Conf;
+
   my $filetype            = $info->{'filetype'};      # CSV or fixed
-  my @fields              = @{ $info->{'fields'} };
+  my @fields              = @{ $info->{'fields'}};
   my $formatre            = $info->{'formatre'};      # for fixed
   my @all_values;
   my $begin_condition     = $info->{'begin_condition'};
   my $end_condition       = $info->{'end_condition'};
   my $end_hook            = $info->{'end_hook'};
+  my $skip_condition      = $info->{'skip_condition'};
   my $hook                = $info->{'hook'};
   my $approved_condition  = $info->{'approved'};
   my $declined_condition  = $info->{'declined'};
@@ -241,11 +247,13 @@ sub import_results {
   # process one line at a time.
 
   if ($filetype eq 'XML') {
+    eval "use XML::Simple";
+    die $@ if $@;
     my @xmlkeys = @{ $info->{'xmlkeys'} };  # for XML
     my $xmlrow  = $info->{'xmlrow'};        # also for XML
 
     # Do everything differently.
-    my $data = XMLin($fh, KeepRoot => 1);
+    my $data = XML::Simple::XMLin($fh, KeepRoot => 1);
     my $rows = $data;
     # $xmlrow = [ RootKey, FirstLevelKey, SecondLevelKey... ]
     $rows = $rows->{$_} foreach( @$xmlrow );
@@ -255,7 +263,7 @@ sub import_results {
     }
     $rows = [ $rows ] if ref($rows) ne 'ARRAY';
     foreach my $row (@$rows) {
-      push @all_values, [ @{$row}{@xmlkeys} ];
+      push @all_values, [ @{$row}{@xmlkeys}, $row ];
     }
   }
   else {
@@ -268,13 +276,14 @@ sub import_results {
           $dbh->rollback if $oldAutoCommit;
           return "can't parse: ". $csv->error_input();
         };
-        push @all_values, [ $csv->fields() ];
+        push @all_values, [ $csv->fields(), $line ];
       }elsif ($filetype eq 'fixed'){
-        my @values = $line =~ /$formatre/;
+        my @values = ( $line =~ /$formatre/ );
         unless (@values) {
           $dbh->rollback if $oldAutoCommit;
           return "can't parse: ". $line;
         };
+        push @values, $line;
         push @all_values, \@values;
       }else{
         $dbh->rollback if $oldAutoCommit;
@@ -283,19 +292,34 @@ sub import_results {
     }
   }
 
+  my $num = 0;
   foreach (@all_values) {
+    if($job) {
+      $num++;
+      $job->update_statustext(int(100 * $num/scalar(@all_values)));
+    }
     my @values = @$_;
 
     my %hash;
+    my $line = pop @values;
     foreach my $field ( @fields ) {
       my $value = shift @values;
       next unless $field;
       $hash{$field} = $value;
     }
 
-    if ( defined($end_condition) and &{$end_condition}(\%hash) ) {
+    if ( defined($begin_condition) ) {
+      if ( &{$begin_condition}(\%hash, $line) ) {
+        undef $begin_condition;
+      }
+      else {
+        next;
+      }
+    }
+
+    if ( defined($end_condition) and &{$end_condition}(\%hash, $line) ) {
       my $error;
-      $error = &{$end_hook}(\%hash, $total) if defined($end_hook);
+      $error = &{$end_hook}(\%hash, $total, $line) if defined($end_hook);
       if ( $error ) {
         $dbh->rollback if $oldAutoCommit;
         return $error;
@@ -303,6 +327,10 @@ sub import_results {
       last;
     }
 
+    if ( defined($skip_condition) and &{$skip_condition}(\%hash, $line) ) {
+      next;
+    }
+
     my $cust_pay_batch =
       qsearchs('cust_pay_batch', { 'paybatchnum' => $hash{'paybatchnum'}+0 } );
     unless ( $cust_pay_batch ) {
@@ -331,13 +359,23 @@ sub import_results {
       return "error updating status of paybatchnum $hash{'paybatchnum'}: $error\n";
     }
 
+    # purge CVV when the batch is processed
+    if ( $payby =~ /^(CARD|DCRD)$/ ) {
+      my $payinfo = $hash{'payinfo'} || $cust_pay_batch->payinfo;
+      if ( ! grep { $_ eq cardtype($payinfo) }
+          $conf->config('cvv-save') ) {
+        $new_cust_pay_batch->cust_main->remove_cvv;
+      }
+    }
+
     if ( $new_cust_pay_batch->status =~ /Approved/i ) {
 
       my $cust_pay = new FS::cust_pay ( {
         'custnum'  => $custnum,
        'payby'    => $payby,
-        'paybatch' => $self->batchnum,
-        map { $_ => $hash{$_} } (qw( paid _date payinfo )),
+        'paybatch' => $hash{'paybatch'} || $self->batchnum,
+        'payinfo'  => ( $hash{'payinfo'} || $cust_pay_batch->payinfo ),
+        map { $_ => $hash{$_} } (qw( paid _date )),
       } );
       $error = $cust_pay->insert;
       if ( $error ) {
@@ -387,8 +425,31 @@ sub import_results {
 
 }
 
-sub export_batch {
+use MIME::Base64;
+use Storable 'thaw';
+use Data::Dumper;
+sub process_import_results {
+  my $job = shift;
+  my $param = thaw(decode_base64(shift));
+  $param->{'job'} = $job;
+  warn Dumper($param) if $DEBUG;
+  my $batchnum = delete $param->{'batchnum'} or die "no batchnum specified\n";
+  my $batch = FS::pay_batch->by_key($batchnum) or die "batchnum '$batchnum' not found\n";
+
+  my $file = $param->{'uploaded_files'} or die "no files provided\n";
+  $file =~ s/^(\w+):([\.\w]+)$/$2/;
+  my $dir = '%%%FREESIDE_CACHE%%%/cache.' . $FS::UID::datasrc;
+  open( $param->{'filehandle'}, 
+        '<',
+        "$dir/$file" )
+      or die "unable to open '$file'.\n";
+  my $error = $batch->import_results($param);
+  unlink $file;
+  die $error if $error;
+}
+
 # Formerly httemplate/misc/download-batch.cgi
+sub export_batch {
   my $self = shift;
   my $conf = new FS::Conf;
   my $format = shift || $conf->config('batch-default_format')
@@ -396,26 +457,23 @@ sub export_batch {
   my $info = $export_info{$format} or die "Format not found: '$format'\n";
   &{$info->{'init'}}($conf) if exists($info->{'init'});
 
+  my $curuser = $FS::CurrentUser::CurrentUser;
+
   my $oldAutoCommit = $FS::UID::AutoCommit;
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;  
 
-  my $error;
-
   my $first_download;
-  if($self->status eq 'O') {
+  my $status = $self->status;
+  if ($status eq 'O') {
     $first_download = 1;
-  }
-  elsif($self->status eq 'I' and
-        $FS::CurrentUser::CurrentUser->access_right('Reprocess batches')) {
+    my $error = $self->set_status('I');
+    die "error updating pay_batch status: $error\n" if $error;
+  } elsif ($status eq 'I' && $curuser->access_right('Reprocess batches')) {
     $first_download = 0;
+  } else {
+    die "No pending batch.\n";
   }
-  else {
-    die "No pending batch.\n"
-  }
-
-  $error = $self->set_status('I');
-  die "error updating pay_batch status: $error\n" if $error;
 
   my $batch = '';
   my $batchtotal = 0;
@@ -432,29 +490,34 @@ sub export_batch {
     $batch .= $h . "\n";
   }
   foreach my $cust_pay_batch (@cust_pay_batch) {
-    if($first_download) {
+
+    if ($first_download) {
       my $balance = $cust_pay_batch->cust_main->balance;
-      $error = '';
-      if($balance <= 0) { # then don't charge this customer
-        $error = $cust_pay_batch->delete;
-        undef $cust_pay_batch;
-      }
-      elsif($balance < $cust_pay_batch->amount) { # then reduce the charge to the remaining balance
+      if ($balance <= 0) { # then don't charge this customer
+        my $error = $cust_pay_batch->delete;
+        if ( $error ) {
+          $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+          die $error;
+        }
+        next;
+      } elsif ($balance < $cust_pay_batch->amount) {
+        # reduce the charge to the remaining balance
         $cust_pay_batch->amount($balance);
-        $error = $cust_pay_batch->replace;
+        my $error = $cust_pay_batch->replace;
+        if ( $error ) {
+          $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+          die $error;
+        }
       }
       # else $balance >= $cust_pay_batch->amount
-      if($error) {
-        $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
-        die $error;
-      }
-    }
-    if($cust_pay_batch) { # that is, it wasn't deleted
-      $batchcount++;
-      $batchtotal += $cust_pay_batch->amount;
-      $batch .= &{$info->{'row'}}($cust_pay_batch, $self) . "\n";
     }
+
+    $batchcount++;
+    $batchtotal += $cust_pay_batch->amount;
+    $batch .= &{$info->{'row'}}($cust_pay_batch, $self, $batchcount, $batchtotal) . "\n";
+
   }
+
   my $f = $info->{'footer'};
   if(ref($f) eq 'CODE') {
     $batch .= &$f($self, $batchcount, $batchtotal) . "\n";
@@ -464,7 +527,7 @@ sub export_batch {
   }
 
   if ($info->{'autopost'}) {
-    $error = &{$info->{'autopost'}}($self, $batch);
+    my $error = &{$info->{'autopost'}}($self, $batch);
     if($error) {
       $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
       die $error;