okay to include URL in comment, RT#33582
[freeside.git] / FS / FS / cust_pkg.pm
index fb3b0ff..8eb4ce3 100644 (file)
@@ -667,8 +667,9 @@ sub check {
     || $self->ut_numbern('resume')
     || $self->ut_numbern('expire')
     || $self->ut_numbern('dundate')
-    || $self->ut_enum('no_auto', [ '', 'Y' ])
-    || $self->ut_enum('waive_setup', [ '', 'Y' ])
+    || $self->ut_flag('no_auto', [ '', 'Y' ])
+    || $self->ut_flag('waive_setup', [ '', 'Y' ])
+    || $self->ut_flag('separate_bill')
     || $self->ut_textn('agent_pkgid')
     || $self->ut_enum('recur_show_zero', [ '', 'Y', 'N', ])
     || $self->ut_enum('setup_show_zero', [ '', 'Y', 'N', ])
@@ -784,6 +785,10 @@ to a different pkgpart or location, and probably shouldn't be in any other
 case.  If it's not set, the 'unused_credit_cancel' part_pkg option will 
 be used.
 
+=item delay_cancel - for internal use, to allow proper handling of
+supplemental packages when the main package is flagged to suspend 
+before cancelling
+
 =back
 
 If there is an error, returns the error, otherwise returns false.
@@ -823,7 +828,7 @@ sub cancel {
   my $date = $options{'date'} if $options{'date'}; # expire/cancel later
   $date = '' if ($date && $date <= $cancel_time);      # complain instead?
 
-  my $delay_cancel = undef;
+  my $delay_cancel = $options{'delay_cancel'};
   if ( !$date && $self->part_pkg->option('delay_cancel',1)
        && (($self->status eq 'active') || ($self->status eq 'suspended'))
   ) {
@@ -907,7 +912,7 @@ sub cancel {
   if ( $date ) {
     $hash{'expire'} = $date;
     if ($delay_cancel) {
-      $hash{'susp'} = $cancel_time unless $self->susp;
+      # just to be sure these are clear
       $hash{'adjourn'} = undef;
       $hash{'resume'} = undef;
     }
@@ -934,21 +939,24 @@ sub cancel {
   }
 
   foreach my $supp_pkg ( $self->supplemental_pkgs ) {
-    if ($delay_cancel) {
-      $error = $supp_pkg->suspend(
-        'from_main'   => 1, 
-        'from_cancel' => 1,
-        'time'        => $cancel_time
-      );
-    } else {
-      $error = $supp_pkg->cancel(%options, 'from_main' => 1);
-    }
+    $error = $supp_pkg->cancel(%options, 
+      'from_main' => 1, 
+      'date' => $date, #in case it got changed by delay_cancel
+      'delay_cancel' => $delay_cancel,
+    );
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
       return "canceling supplemental pkg#".$supp_pkg->pkgnum.": $error";
     }
   }
 
+  if ($delay_cancel && !$options{'from_main'}) {
+    $error = $new->suspend(
+      'from_cancel' => 1,
+      'time'        => $cancel_time
+    );
+  }
+
   unless ($date) {
     foreach my $usage ( $self->cust_pkg_usage ) {
       $error = $usage->delete;
@@ -1058,7 +1066,8 @@ sub uncancel {
       setup
       susp adjourn resume expire start_date contract_end dundate
       change_date change_pkgpart change_locationnum
-      manual_flag no_auto quantity agent_pkgid recur_show_zero setup_show_zero
+      manual_flag no_auto separate_bill quantity agent_pkgid 
+      recur_show_zero setup_show_zero
     ),
   };
 
@@ -1675,15 +1684,20 @@ sub unsuspend {
            and ! $self->option('no_suspend_bill',1)
          )
       or $hash{'order_date'} == $hash{'susp'}
-      or $self->part_pkg->option('unused_credit_suspend')
-      or ( ref($reason) and $reason->unused_credit )
   ) {
     $adjust_bill = 0;
   }
 
-  # then add the length of time suspended to the bill date
   if ( $adjust_bill ) {
-    $hash{'bill'} = ( $hash{'bill'} || $hash{'setup'} ) + $inactive
+    if (    $self->part_pkg->option('unused_credit_suspend')
+         or ( ref($reason) and $reason->unused_credit ) ) {
+      # then the customer was credited for the unused time before suspending,
+      # so their next bill should be immediate 
+      $hash{'bill'} = time;
+    } else {
+      # add the length of time suspended to the bill date
+      $hash{'bill'} = ( $hash{'bill'} || $hash{'setup'} ) + $inactive;
+    }
   }
 
   $hash{'susp'} = '';
@@ -2401,6 +2415,7 @@ and, I<if the charge has not yet been billed>:
 - start_date: the date when it will be billed
 - amount: the setup fee to be charged
 - quantity: the multiplier for the setup fee
+- separate_bill: whether to put the charge on a separate invoice
 
 If you pass 'adjust_commission' => 1, and the classnum changes, and there are
 commission credits linked to this charge, they will be recalculated.
@@ -2456,7 +2471,8 @@ sub modify_charge {
   }
 
   if ( !$self->get('setup') ) {
-    # not yet billed, so allow amount, setup_cost, quantity and start_date
+    # not yet billed, so allow amount, setup_cost, quantity, start_date,
+    # and separate_bill
 
     if ( exists($opt{'amount'}) 
           and $part_pkg->option('setup_fee') != $opt{'amount'}
@@ -2486,6 +2502,12 @@ sub modify_charge {
       $self->set('start_date', $opt{'start_date'});
     }
 
+    if ( exists($opt{'separate_bill'})
+          and $opt{'separate_bill'} ne $self->separate_bill ) {
+
+      $self->set('separate_bill', $opt{'separate_bill'});
+    }
+
 
   } # else simply ignore them; the UI shouldn't allow editing the fields
 
@@ -3391,6 +3413,9 @@ really the whole point of the delay_cancel option.
 
 sub is_status_delay_cancel {
   my ($self) = @_;
+  if ( $self->main_pkgnum and $self->pkglinknum ) {
+    return $self->main_pkg->is_status_delay_cancel;
+  }
   return 0 unless $self->part_pkg->option('delay_cancel',1);
   return 0 unless $self->status eq 'suspended';
   return 0 unless $self->expire;