correctly divide tax into setup and recur portions when calculating credit amounts...
[freeside.git] / FS / FS / cust_credit.pm
index 01ee89d..6b21d69 100644 (file)
@@ -9,7 +9,6 @@ use vars qw( $conf $unsuspendauto $me $DEBUG
 use List::Util qw( min );
 use Date::Format;
 use FS::UID qw( dbh );
-use FS::Misc qw(send_email);
 use FS::Record qw( qsearch qsearchs dbdef );
 use FS::CurrentUser;
 use FS::cust_pkg;
@@ -41,7 +40,6 @@ $FS::UID::callback{'FS::cust_credit'} = sub {
 
 our %reasontype_map = ( 'referral_credit_type' => 'Referral Credit',
                         'cancel_credit_type'   => 'Cancellation Credit',
-                        'signup_credit_type'   => 'Self-Service Credit',
                       );
 
 =head1 NAME
@@ -277,35 +275,6 @@ sub delete {
     return $error;
   }
 
-  if ( !$opt{void} and $conf->config('deletecredits') ne '' ) {
-
-    my $cust_main = $self->cust_main;
-
-    my $error = send_email(
-      'from'    => $conf->invoice_from_full($self->cust_main->agentnum),
-                                 #invoice_from??? well as good as any
-      'to'      => $conf->config('deletecredits'),
-      'subject' => 'FREESIDE NOTIFICATION: Credit deleted',
-      'body'    => [
-        "This is an automatic message from your Freeside installation\n",
-        "informing you that the following credit has been deleted:\n",
-        "\n",
-        'crednum: '. $self->crednum. "\n",
-        'custnum: '. $self->custnum.
-          " (". $cust_main->last. ", ". $cust_main->first. ")\n",
-        'amount: $'. sprintf("%.2f", $self->amount). "\n",
-        'date: '. time2str("%a %b %e %T %Y", $self->_date). "\n",
-        'reason: '. $self->reason. "\n",
-      ],
-    );
-
-    if ( $error ) {
-      $dbh->rollback if $oldAutoCommit;
-      return "can't send credit deletion notification: $error";
-    }
-
-  }
-
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
 
   '';
@@ -408,14 +377,14 @@ sub void {
   my $cust_credit_void = new FS::cust_credit_void ( {
       map { $_ => $self->get($_) } $self->fields
     } );
-  $cust_credit_void->set('void_reasonnum', $reason->reasonnum);
+  $cust_credit_void->set('void_reasonnum', $reason->reasonnum) if $reason;
   my $error = $cust_credit_void->insert;
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
     return $error;
   }
 
-  $error = $self->delete(void => 1); # suppress deletecredits warning
+  $error = $self->delete();
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
     return $error;
@@ -754,19 +723,31 @@ sub calculate_tax_adjustment {
     $cust_bill{ $invnum}->custnum == $arg{custnum}
       or die "lineitem #$billpkgnum not found\n";
 
-    # calculate credit ratio.
-    # (First deduct any existing credits applied to this line item, to avoid
-    # rounding errors.)
-    my $charged = $cust_bill_pkg->get($setuprecur);
-    my $previously_credited =
-      $cust_bill_pkg->credited( '', '', setuprecur => $setuprecur) || 0;
+    # tax_Xlocation records don't distinguish setup and recur, so calculate
+    # the fraction of setup+recur (after deducting credits) that's setup. This
+    # will also be the fraction of tax (after deducting credits) that's tax on
+    # setup.
+    my ($setup, $recur);
+    $setup = $cust_bill_pkg->get('setup') || 0;
+    if ($setup) {
+      $setup -= $cust_bill_pkg->credited('', '', setuprecur => 'setup') || 0;
+    }
+    $recur = $cust_bill_pkg->get('recur') || 0;
+    if ($recur) {
+      $recur -= $cust_bill_pkg->credited('', '', setuprecur => 'recur') || 0;
+    }
+    my $setup_ratio = $setup / ($setup + $recur);
+
+    # Calculate the fraction of tax to credit: it's the fraction of this charge
+    # (either setup or recur) that's being credited.
+    my $charged = ($setuprecur eq 'setup') ? $setup : $recur;
+    next if $charged == 0; # shouldn't happen, but still...
 
-    $charged -= $previously_credited;
     if ($charged < $amount) {
       $error = "invoice #$invnum: tried to credit $amount, but only $charged was charged";
       last;
     }
-    my $ratio = $amount / $charged;
+    my $credit_ratio = $amount / $charged;
 
     # gather taxes that apply to the selected item
     foreach my $table (
@@ -781,7 +762,16 @@ sub calculate_tax_adjustment {
         foreach ($tax_link->cust_credit_bill_pkg) {
           $tax_amount -= $_->amount;
         }
-        my $tax_credit = sprintf('%.2f', $tax_amount * $ratio);
+        # split tax amount based on setuprecur
+        # (this method ensures that, if you credit both setup and recur tax,
+        # it always equals the entire tax despite any rounding)
+        my $setup_tax = sprintf('%.2f', $tax_amount * $setup_ratio);
+        if ( $setuprecur eq 'setup' ) {
+          $tax_amount = $setup_tax;
+        } else {
+          $tax_amount = $tax_amount - $setup_tax;
+        }
+        my $tax_credit = sprintf('%.2f', $tax_amount * $credit_ratio);
         my $pkey = $tax_link->get($tax_link->primary_key);
         push @taxlines, {
           table   => $table,
@@ -822,8 +812,6 @@ Example:
 
 =cut
 
-use Data::Dumper; #XXX
-
 #maybe i should just be an insert with extra args instead of a class method
 sub credit_lineitems {
   my( $class, %arg ) = @_;
@@ -887,7 +875,6 @@ sub credit_lineitems {
   # determine the tax adjustments
   my %tax_adjust = $class->calculate_tax_adjustment(%arg);
 
-  warn Dumper \%arg;
   foreach my $billpkgnum ( @{$arg{billpkgnums}} ) {
     my $setuprecur = shift @{$arg{setuprecurs}};
     my $amount = shift @{$arg{amounts}};