eWay self-signup fixes
[freeside.git] / FS / FS / pay_batch.pm
index 2561d3d..5cd40cd 100644 (file)
@@ -7,6 +7,7 @@ use Text::CSV_XS;
 use FS::Record qw( dbh qsearch qsearchs );
 use FS::cust_pay;
 use FS::Conf;
+use Date::Parse qw(str2time);
 use Business::CreditCard qw(cardtype);
 
 @ISA = qw(FS::Record);
@@ -201,9 +202,10 @@ sub import_results {
 
   my $conf = new FS::Conf;
 
-  my $filetype            = $info->{'filetype'};      # CSV or fixed
+  my $filetype            = $info->{'filetype'};      # CSV, fixed, variable
   my @fields              = @{ $info->{'fields'}};
   my $formatre            = $info->{'formatre'};      # for fixed
+  my $parse               = $info->{'parse'};         # for variable
   my @all_values;
   my $begin_condition     = $info->{'begin_condition'};
   my $end_condition       = $info->{'end_condition'};
@@ -212,6 +214,7 @@ sub import_results {
   my $hook                = $info->{'hook'};
   my $approved_condition  = $info->{'approved'};
   my $declined_condition  = $info->{'declined'};
+  my $close_condition     = $info->{'close_condition'};
 
   my $csv = new Text::CSV_XS;
 
@@ -285,7 +288,17 @@ sub import_results {
         };
         push @values, $line;
         push @all_values, \@values;
-      }else{
+      }
+      elsif ($filetype eq 'variable') {
+        my @values = ( eval { $parse->($self, $line) } );
+        if( $@ ) {
+          $dbh->rollback if $oldAutoCommit;
+          return $@;
+        };
+        push @values, $line;
+        push @all_values, \@values;
+      }
+      else {
         $dbh->rollback if $oldAutoCommit;
         return "Unknown file type $filetype";
       }
@@ -343,20 +356,21 @@ sub import_results {
 
     &{$hook}(\%hash, $cust_pay_batch->hashref);
 
+    my $error = '';
     if ( &{$approved_condition}(\%hash) ) {
 
-      $new_cust_pay_batch->status('Approved');
+      $error = $new_cust_pay_batch->approve($hash{'paybatch'} || $self->batchnum);
+      $total += $hash{'paid'};
 
     } elsif ( &{$declined_condition}(\%hash) ) {
 
-      $new_cust_pay_batch->status('Declined');
+      $error = $new_cust_pay_batch->decline;
 
     }
 
-    my $error = $new_cust_pay_batch->replace($cust_pay_batch);
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
-      return "error updating status of paybatchnum $hash{'paybatchnum'}: $error\n";
+      return $error;
     }
 
     # purge CVV when the batch is processed
@@ -366,60 +380,22 @@ sub import_results {
           $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,
-        'payinfo'  => ( $hash{'payinfo'} || $cust_pay_batch->payinfo ),
-        map { $_ => $hash{$_} } (qw( paid _date )),
-      } );
-      $error = $cust_pay->insert;
-      if ( $error ) {
-        $dbh->rollback if $oldAutoCommit;
-        return "error adding payment paybatchnum $hash{'paybatchnum'}: $error\n";
-      }
-      $total += $hash{'paid'};
-  
-      $cust_pay->cust_main->apply_payments;
-
-    } elsif ( $new_cust_pay_batch->status =~ /Declined/i ) {
 
-      #false laziness w/cust_main::collect
-
-      my $due_cust_event = $new_cust_pay_batch->cust_main->due_cust_event(
-        #'check_freq' => '1d', #?
-        'eventtable' => 'cust_pay_batch',
-        'objects'    => [ $new_cust_pay_batch ],
-      );
-      unless( ref($due_cust_event) ) {
-        $dbh->rollback if $oldAutoCommit;
-        return $due_cust_event;
-      }
-
-      foreach my $cust_event ( @$due_cust_event ) {
-        
-        #XXX lock event
-    
-        #re-eval event conditions (a previous event could have changed things)
-        next unless $cust_event->test_conditions;
-
-       if ( my $error = $cust_event->do_event() ) {
-         # gah, even with transactions.
-         #$dbh->commit if $oldAutoCommit; #well.
-         $dbh->rollback if $oldAutoCommit;
-          return $error;
-       }
+    }
 
-      }
+  } # foreach (@all_values)
 
+  if ( defined($close_condition) ) {
+    # Allow the module to decide whether to close the batch.
+    # $close_condition can also die() to abort the whole import.
+    my $close = eval { $close_condition->($self) };
+    if ( $@ ) {
+      $dbh->rollback;
+      die $@;
     }
-
+    $self->set_status('I') if !$close;
   }
-  
+
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   '';
 
@@ -481,7 +457,20 @@ sub export_batch {
 
   my @cust_pay_batch = sort { $a->paybatchnum <=> $b->paybatchnum }
                       qsearch('cust_pay_batch', { batchnum => $self->batchnum } );
-
+  
+  # handle batch-increment_expiration option
+  if ( $self->payby eq 'CARD' ) {
+    my ($cmon, $cyear) = (localtime(time))[4,5];
+    foreach (@cust_pay_batch) {
+      my $etime = str2time($_->exp) or next;
+      my ($day, $mon, $year) = (localtime($etime))[3,4,5];
+      if( $conf->exists('batch-increment_expiration') ) {
+        $year++ while( $year < $cyear or ($year == $cyear and $mon <= $cmon) );
+        $_->exp( sprintf('%4u-%02u-%02u', $year + 1900, $mon+1, $day) );
+      }
+      $_->setfield('expmmyy', sprintf('%02u%02u', $mon+1, $year % 100));
+    }
+  }
   my $h = $info->{'header'};
   if(ref($h) eq 'CODE') {
     $batch .= &$h($self, \@cust_pay_batch) . "\n";
@@ -538,6 +527,52 @@ sub export_batch {
   return $batch;
 }
 
+sub manual_approve {
+  my $self = shift;
+  my $date = time;
+  my %opt = @_;
+  my $paybatch = $opt{'paybatch'} || $self->batchnum;
+  my $conf = FS::Conf->new;
+  return 'manual batch approval disabled' 
+    if ( ! $conf->exists('batch-manual_approval') );
+  return 'batch already resolved' if $self->status eq 'R';
+  return 'batch not yet submitted' if $self->status eq 'O';
+
+  local $SIG{HUP} = 'IGNORE';
+  local $SIG{INT} = 'IGNORE';
+  local $SIG{QUIT} = 'IGNORE';
+  local $SIG{TERM} = 'IGNORE';
+  local $SIG{TSTP} = 'IGNORE';
+  local $SIG{PIPE} = 'IGNORE';
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  my $payments = 0;
+  foreach my $cust_pay_batch ( 
+    qsearch('cust_pay_batch', { batchnum => $self->batchnum,
+        status   => '' })
+  ) {
+    my $new_cust_pay_batch = new FS::cust_pay_batch { 
+      $cust_pay_batch->hash,
+      'paid'  => $cust_pay_batch->amount,
+      '_date' => $date,
+    };
+    my $error = $new_cust_pay_batch->approve($paybatch);
+    if ( $error ) {
+      $dbh->rollback;
+      return 'paybatchnum '.$cust_pay_batch->paybatchnum.": $error";
+    }
+    $payments++;
+  }
+  return 'no unresolved payments in batch' if $payments == 0;
+  $self->set_status('R');
+  
+  $dbh->commit;
+  return;
+}
+
 =back
 
 =head1 BUGS