allow changing package class of one-time charges post-billing, #25342
[freeside.git] / FS / FS / cust_credit.pm
index c716602..9678934 100644 (file)
@@ -7,7 +7,7 @@ use vars qw( $conf $unsuspendauto $me $DEBUG
            );
 use List::Util qw( min );
 use Date::Format;
-use FS::UID qw( dbh getotaker );
+use FS::UID qw( dbh );
 use FS::Misc qw(send_email);
 use FS::Record qw( qsearch qsearchs dbdef );
 use FS::CurrentUser;
@@ -19,6 +19,9 @@ use FS::part_pkg;
 use FS::reason_type;
 use FS::reason;
 use FS::cust_event;
+use FS::agent;
+use FS::sales;
+use FS::cust_credit_void;
 
 $me = '[ FS::cust_credit ]';
 $DEBUG = 0;
@@ -201,6 +204,8 @@ the void method instead to leave a record of the deleted credit.
 # very similar to FS::cust_pay::delete
 sub delete {
   my $self = shift;
+  my %opt = @_;
+
   return "Can't delete closed credit" if $self->closed =~ /^Y/i;
 
   local $SIG{HUP} = 'IGNORE';
@@ -236,7 +241,7 @@ sub delete {
     return $error;
   }
 
-  if ( $conf->config('deletecredits') ne '' ) {
+  if ( !$opt{void} and $conf->config('deletecredits') ne '' ) {
 
     my $cust_main = $self->cust_main;
 
@@ -311,6 +316,9 @@ sub check {
     || $self->ut_enum('closed', [ '', 'Y' ])
     || $self->ut_foreign_keyn('pkgnum', 'cust_pkg', 'pkgnum')
     || $self->ut_foreign_keyn('eventnum', 'cust_event', 'eventnum')
+    || $self->ut_foreign_keyn('commission_agentnum',  'agent', 'agentnum')
+    || $self->ut_foreign_keyn('commission_salesnum',  'sales', 'salesnum')
+    || $self->ut_foreign_keyn('commission_pkgnum', 'cust_pkg', 'pkgnum')
   ;
   return $error if $error;
 
@@ -331,6 +339,53 @@ sub check {
   $self->SUPER::check;
 }
 
+=item void [ REASON ]
+
+Voids this credit: deletes the credit and all associated applications and 
+adds a record of the voided credit to the cust_credit_void table.
+
+=cut
+
+# yes, false laziness with cust_pay and cust_bill
+# but frankly I don't have time to fix it now
+
+sub void {
+  my $self = shift;
+  my $reason = shift;
+
+  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 $cust_credit_void = new FS::cust_credit_void ( {
+      map { $_ => $self->get($_) } $self->fields
+    } );
+  $cust_credit_void->set('void_reason', $reason);
+  my $error = $cust_credit_void->insert;
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+
+  $error = $self->delete(void => 1); # suppress deletecredits warning
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+  '';
+
+}
+
 =item cust_credit_refund
 
 Returns all refund applications (see L<FS::cust_credit_refund>) for this credit.
@@ -717,8 +772,7 @@ sub credit_lineitems {
   my %cust_bill_pkg = ();
   my %cust_credit_bill_pkg = ();
   my %taxlisthash = ();
-  # except here they're billpaynums
-  my %unapplied_payments;
+  my %unapplied_payments = (); #invoice numbers, and then billpaynums
   foreach my $billpkgnum ( @{$arg{billpkgnums}} ) {
     my $setuprecur = shift @{$arg{setuprecurs}};
     my $amount = shift @{$arg{amounts}};
@@ -753,7 +807,7 @@ sub credit_lineitems {
         $dbh->rollback if $oldAutoCommit;
         return "Error unapplying payment: $error";
       }
-      $unapplied_payments{$cust_bill_pay_pkg->billpaynum}
+      $unapplied_payments{$invnum}{$cust_bill_pay_pkg->billpaynum}
         += $cust_bill_pay_pkg->amount;
     }
 
@@ -893,27 +947,37 @@ sub credit_lineitems {
           $dbh->rollback if $oldAutoCommit;
           return "Error unapplying payment: $error";
         }
-        $unapplied_payments{$cust_bill_pay_pkg->billpaynum}
+        $unapplied_payments{$invnum}{$cust_bill_pay_pkg->billpaynum}
           += $cust_bill_pay_pkg->amount;
       }
     } #foreach $taxline
 
     # if we unapplied any payments from line items, also unapply that 
     # amount from the invoice
-    foreach my $billpaynum (keys %unapplied_payments) {
+    foreach my $billpaynum (keys %{$unapplied_payments{$invnum}}) {
       my $cust_bill_pay = FS::cust_bill_pay->by_key($billpaynum)
         or die "broken payment application $billpaynum";
+      my @subapps = $cust_bill_pay->lineitem_applications;
       $error = $cust_bill_pay->delete; # can't replace
 
       my $new_cust_bill_pay = FS::cust_bill_pay->new({
           $cust_bill_pay->hash,
           billpaynum => '',
           amount => sprintf('%.2f', 
-              $cust_bill_pay->amount - $unapplied_payments{$billpaynum}),
+              $cust_bill_pay->amount 
+              - $unapplied_payments{$invnum}{$billpaynum}),
       });
 
       if ( $new_cust_bill_pay->amount > 0 ) {
         $error ||= $new_cust_bill_pay->insert;
+        # Also reapply it to everything it was applied to before.
+        # Note that we've already deleted cust_bill_pay_pkg records for the
+        # items we're crediting, so they aren't on this list.
+        foreach my $cust_bill_pay_pkg (@subapps) {
+          $cust_bill_pay_pkg->billpaypkgnum('');
+          $cust_bill_pay_pkg->billpaynum($new_cust_bill_pay->billpaynum);
+          $error ||= $cust_bill_pay_pkg->insert;
+        }
       }
       if ( $error ) {
         $dbh->rollback if $oldAutoCommit;