fix 'Can't call method "setup" on an undefined value' error when using into rates...
[freeside.git] / FS / bin / freeside-void-payments
index b6869a3..8c1f3db 100755 (executable)
@@ -8,13 +8,14 @@ use FS::Record qw(qsearchs);
 use FS::Conf;
 use FS::cust_main;
 use FS::cust_pay;
+use FS::cust_pay_void;
 use Business::OnlinePayment; # For retrieving the void list only.
 use Time::Local;
 use Date::Parse 'str2time';
 use Date::Format 'time2str';
 
 my %opt;
-getopts("r:f:ca:g:s:e:", \%opt);
+getopts("r:f:ca:g:s:e:vnX:", \%opt);
 
 $user = shift or die &usage;
 &adminsuidsetup( $user );
@@ -33,6 +34,10 @@ elsif($opt{'a'}) {
     or die "Agent has no payment gateway for method '$method'.";
 }
 
+if(defined($opt{'X'}) and !qsearchs('reason', { reasonnum => opt{'X'} })) {
+  die "Cancellation reason not found: '".$opt{'X'}."'";
+}
+
 my ($processor, $login, $password, $action, @bop_options) =
   FS::cust_main->default_payment_gateway($method);
 my $gatewaynum = '';
@@ -80,20 +85,68 @@ else {
 }
 
 $opt{'r'} ||= 'freeside-void-payments';
-
+my $success = 0;
+my $notfound = 0;
+my $canceled = 0;
+print "Voiding ".scalar(@auths)." transactions:\n" if $opt{'v'};
 foreach my $authnum (@auths) {
   my $paybatch = $gatewaynum . $processor . ':' . $authnum;
   my $cust_pay = qsearchs('cust_pay', { paybatch => $paybatch } );
+  my $error;
+  my $cancel_error;
   if($cust_pay) {
-    $cust_pay->void($opt{'r'});
+    $error = $cust_pay->void($opt{'r'});
+    $success++ if not $error;
+    if($opt{'X'} and not $error) {
+      $cancel_error = join(';',$cust_pay->cust_main->cancel('reason' => $opt{'X'}));
+      $canceled++ if !$cancel_error;
+    }
   }
   else {
-    warn "cust_pay record not found: '$paybatch'";
+    my $cpv = qsearchs('cust_pay_void', { paybatch => $paybatch });
+    if($cpv) {
+      $error = 'already voided '.time2str('%Y-%m-%d', $cpv->void_date) . 
+        ' by ' . $cpv->otaker;
+    }
+    else {
+      $error = 'not found';
+      $notfound++;
+    }
+  }
+  if($opt{'v'}) {
+    print $authnum;
+    if($error) {
+      print "\t($error)";
+    }
+    elsif($opt{'X'}) {
+      print "\t(canceled service)" if !$cancel_error;
+      print "\n\t(cancellation failed: $cancel_error)" if $cancel_error;
+    }
+    print "\n";
   }
 }
 
+if($opt{'v'}) {
+  print scalar(@auths)." transactions: $success voided, $notfound not found\n";
+  print "$canceled customer".($canceled == 1 ? '' : 's')." canceled\n" if $opt{'X'};
+}
+
 sub usage {
-    die "Usage:\n\n  freeside-void-payments [ -f file | [ -s start-date ] [ -e end-date ] ] [ -r 'reason' ] [ -g gatewaynum | -a agentnum ] [ -c ] user\n";
+    die "Usage:\n\n  freeside-void-payments [ options ] user
+    
+    options:
+      -a agentnum    use agentnum's gateway information
+      -g gatewaynum  use gatewaynum
+      -f file        read transaction numbers from file
+      -c             use ECHECK gateway instead of CARD
+      -r reason      specify void reason (as a string)
+      -v             be verbose
+      -s start-date
+      -e end-date    limit by payment return date
+      -X reasonnum   cancel customers whose payments are voided
+                     (specify cancellation reason number)
+
+";
 }
 
 __END__
@@ -106,10 +159,17 @@ freeside-void-payments - Automatically void a list of returned payments.
 
 =head1 SYNOPSIS
 
-  freeside-void-payments [ -f file | [ -s start-date ] [ -e end-date ] ] [ -r 'reason' ] [ -g gatewaynum | -a agentnum ] [ -c ] user
+  freeside-void-payments [ -f file | [ -s start-date ] [ -e end-date ] ] 
+                         [ -r 'reason' ] 
+                         [ -g gatewaynum | -a agentnum ] 
+                         [ -c ] [ -v ] 
+                         [ -X reasonnum ] 
+                         user
 
 =head1 DESCRIPTION
 
+=pod
+
 Voids payments that were returned by the payment processor.  Can be 
 run periodically from crontab or manually after receiving a list of 
 returned payments.  Normally this is a meaningful operation only for 
@@ -122,12 +182,12 @@ generally how the processor will identify them later.
   -f: Read the list of authorization numbers from the specified file.  
       If they are not from the default payment gateway, -g or -a 
       must be given to identify the gateway.
-      
+
   If -f is not given, the script will attempt to contact the gateway 
   and download a list of returned transactions.  To support this, 
   the Business::OnlinePayment module for the processor must implement 
-  the I<get_returns()> method.  For an example, see 
-  L<Business::OnlinePayment::WesternACH>.
+  the get_returns() method.  For an example, see 
+  Business::OnlinePayment::WesternACH.
 
   -s, -e: Specify the starting and ending dates for the void list.  
       This has no effect if -f is given.  The end date defaults to 
@@ -135,7 +195,7 @@ generally how the processor will identify them later.
 
   -r: The reason for voiding the payments, to be stored in the database.
 
-  -g: The L<FS::payment_gateway> number for the gateway that handled 
+  -g: The FS::payment_gateway number for the gateway that handled 
       these payments.  If -f is not given, this determines which 
       gateway will be contacted.  This overrides -a.
 
@@ -145,9 +205,11 @@ generally how the processor will identify them later.
   -c: Use the default gateway for check transactions rather than 
       credit cards.
 
-A warning will be emitted for each transaction that can't be found.  
-This may happen if it's already been voided, or if the gateway 
-doesn't match.
+  -v: Be verbose.
+  
+  -X: Automatically cancel all packages belonging to customers whose 
+      payments were returned.  Requires a cancellation reasonnum 
+      (from FS::reason).
 
 =head1 EXAMPLE
 
@@ -168,7 +230,7 @@ day at 8:30 every morning:
 
 =head1 BUGS
 
-Most payment gateways don't support it, making the script largely useless.
+Most payment gateways don't support it.
 
 =head1 SEE ALSO