Merge branch 'master' of git.freeside.biz:/home/git/freeside
[freeside.git] / FS / FS / cust_main / Billing_Batch.pm
index 67208e6..eb66436 100644 (file)
@@ -5,6 +5,7 @@ use vars qw( $conf );
 use FS::Record qw( qsearch qsearchs dbh );
 use FS::pay_batch;
 use FS::cust_pay_batch;
+use FS::cust_bill_pay_batch;
 
 install_callback FS::UID sub { 
   $conf = new FS::Conf;
@@ -22,8 +23,6 @@ Options may include:
 B<amount>: the amount to be paid; defaults to the customer's balance minus
 any payments in transit.
 
-B<payby>: the payment method; defaults to cust_main.payby
-
 B<realtime>: runs this as a realtime payment instead of adding it to a 
 batch.  Deprecated.
 
@@ -33,8 +32,9 @@ B<address1>, B<address2>, B<city>, B<state>, B<zip>, B<country>: sets
 the billing address for the payment; defaults to the customer's billing
 location.
 
-B<payinfo>, B<paydate>, B<payname>: sets the payment account, expiration
-date, and name; defaults to those fields in cust_main.
+B<payby>, B<payinfo>, B<paydate>, B<payname>: sets the payment method, 
+payment account, expiration date, and name; defaults to those fields 
+in cust_main.
 
 =cut
 
@@ -47,13 +47,36 @@ sub batch_card {
   }else{
     $amount = sprintf("%.2f", $self->balance - $self->in_transit_payments);
   }
-  return '' unless $amount > 0;
+  if ($amount <= 0) {
+    warn(sprintf("Customer balance %.2f - in transit amount %.2f is <= 0.\n",
+        $self->balance,
+        $self->in_transit_payments
+    ));
+    return;
+  }
   
   my $invnum = delete $options{invnum};
-  my $payby = $options{payby} || $self->payby;  #still dubious
+
+  #pay fields should all come from either cust_payby or options, not both
+  #  in theory, could just pass payby, and use it to select cust_payby,
+  #  but nothing currently needs that, so not implementing it now
+  die "Incomplete payment details" 
+    if  ($options{payby} || $options{payinfo} || $options{paydate} || $options{payname})
+    && !($options{payby} && $options{payinfo} && $options{paydate} && $options{payname});
+
+  #false laziness with Billing_Realtime
+  my @cust_payby = $self->cust_payby('CARD','CHEK');
+
+  # batch can't try out every one like realtime, just use first one
+  my $cust_payby = $cust_payby[0];
+
+  die "No customer payment info found"
+    unless $options{payinfo} || $cust_payby;
+                                                   
+  my $payby = $options{payby} || $cust_payby->payby;
 
   if ($options{'realtime'}) {
-    return $self->realtime_bop( FS::payby->payby2bop($self->payby),
+    return $self->realtime_bop( FS::payby->payby2bop($payby),
                                 $amount,
                                 %options,
                               );
@@ -66,7 +89,7 @@ sub batch_card {
   #this needs to handle mysql as well as Pg, like svc_acct.pm
   #(make it into a common function if folks need to do batching with mysql)
   $dbh->do("LOCK TABLE pay_batch IN SHARE ROW EXCLUSIVE MODE")
-    or return "Cannot lock pay_batch: " . $dbh->errstr;
+    or die "Cannot lock pay_batch: " . $dbh->errstr;
 
   my %pay_batch = (
     'status' => 'O',
@@ -91,7 +114,7 @@ sub batch_card {
   } );
 
   foreach (qw( address1 address2 city state zip country latitude longitude
-               payby payinfo paydate payname ))
+               payby payinfo paydate payname paycode paytype ))
   {
     $options{$_} = '' unless exists($options{$_});
   }
@@ -113,11 +136,18 @@ sub batch_card {
     'state'    => $options{state}    || $loc->state,
     'zip'      => $options{zip}      || $loc->zip,
     'country'  => $options{country}  || $loc->country,
-    'payby'    => $options{payby}    || $self->payby,
-    'payinfo'  => $options{payinfo}  || $self->payinfo,
-    'exp'      => $options{paydate}  || $self->paydate,
-    'payname'  => $options{payname}  || $self->payname,
+    'payby'    => $options{payby}    || $cust_payby->payby,
+    'payinfo'  => $options{payinfo}  || $cust_payby->payinfo,
+    'paymask'  => ( $options{payinfo}
+                      ? FS::payinfo_Mixin->mask_payinfo( $options{payby},
+                                                         $options{payinfo} )
+                      : $cust_payby->paymask
+                  ),
+    'exp'      => $options{paydate}  || $cust_payby->paydate,
+    'payname'  => $options{payname}  || $cust_payby->payname,
+    'paytype'  => $options{paytype}  || $cust_payby->paytype,
     'amount'   => $amount,                         # consolidating
+    'paycode'  => $options{paycode}  || '',
   } );
   
   $cust_pay_batch->paybatchnum($old_cust_pay_batch->paybatchnum)
@@ -132,7 +162,8 @@ sub batch_card {
 
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
-    die $error;
+    #die $error;
+    return $error; # e.g. "Illegal zip" ala RT#75998
   }
 
   my $unapplied =   $self->total_unapplied_credits
@@ -205,6 +236,7 @@ sub in_transit_payments {
     foreach my $cust_pay_batch ( qsearch('cust_pay_batch', {
       'batchnum' => $pay_batch->batchnum,
       'custnum' => $self->custnum,
+      'status'  => '',
     } ) ) {
       $in_transit_payments += $cust_pay_batch->amount;
     }