add more debug tracing to invoice generation, RT#11452
[freeside.git] / FS / FS / cust_pkg.pm
index d1c3f3b..c183a49 100644 (file)
@@ -260,6 +260,9 @@ an optional queue name for ticket additions
 sub insert {
   my( $self, %options ) = @_;
 
+  my $error = $self->check_pkgpart;
+  return $error if $error;
+
   if ( $self->part_pkg->option('start_1st', 1) && !$self->start_date ) {
     my ($sec,$min,$hour,$mday,$mon,$year) = (localtime(time) )[0,1,2,3,4,5];
     $mon += 1 unless $mday == 1;
@@ -288,7 +291,7 @@ sub insert {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
-  my $error = $self->SUPER::insert($options{options} ? %{$options{options}} : ());
+  $error = $self->SUPER::insert($options{options} ? %{$options{options}} : ());
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
     return $error;
@@ -371,14 +374,70 @@ sub insert {
 
 This method now works but you probably shouldn't use it.
 
-You don't want to delete billing items, because there would then be no record
-the customer ever purchased the item.  Instead, see the cancel method.
+You don't want to delete packages, because there would then be no record
+the customer ever purchased the package.  Instead, see the cancel method and
+hide cancelled packages.
 
 =cut
 
-#sub delete {
-#  return "Can't delete cust_pkg records!";
-#}
+sub delete {
+  my $self = 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;
+
+  foreach my $cust_pkg_discount ($self->cust_pkg_discount) {
+    my $error = $cust_pkg_discount->delete;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+  #cust_bill_pkg_discount?
+
+  foreach my $cust_pkg_detail ($self->cust_pkg_detail) {
+    my $error = $cust_pkg_detail->delete;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
+  foreach my $cust_pkg_reason (
+    qsearchs( {
+                'table' => 'cust_pkg_reason',
+                'hashref' => { 'pkgnum' => $self->pkgnum },
+              }
+            )
+  ) {
+    my $error = $cust_pkg_reason->delete;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
+  #pkg_referral?
+
+  my $error = $self->SUPER::delete(@_);
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+  '';
+
+}
 
 =item replace [ OLD_RECORD ] [ HASHREF | OPTION => VALUE ... ]
 
@@ -493,7 +552,10 @@ sub replace {
   #trigger export of new RADIUS Expiration attribute when cust_pkg.bill changes
   foreach my $old_svc_acct ( @svc_acct ) {
     my $new_svc_acct = new FS::svc_acct { $old_svc_acct->hash };
-    my $s_error = $new_svc_acct->replace($old_svc_acct);
+    my $s_error =
+      $new_svc_acct->replace( $old_svc_acct,
+                              'depend_jobnum' => $options->{depend_jobnum},
+                            );
     if ( $s_error ) {
       $dbh->rollback if $oldAutoCommit;
       return $s_error;
@@ -522,6 +584,7 @@ sub check {
     $self->ut_numbern('pkgnum')
     || $self->ut_foreign_key('custnum', 'cust_main', 'custnum')
     || $self->ut_numbern('pkgpart')
+    || $self->check_pkgpart
     || $self->ut_foreign_keyn('locationnum', 'cust_location', 'locationnum')
     || $self->ut_numbern('start_date')
     || $self->ut_numbern('setup')
@@ -534,6 +597,28 @@ sub check {
   ;
   return $error if $error;
 
+  $self->usernum($FS::CurrentUser::CurrentUser->usernum) unless $self->usernum;
+
+  if ( $self->dbdef_table->column('manual_flag') ) {
+    $self->manual_flag('') if $self->manual_flag eq ' ';
+    $self->manual_flag =~ /^([01]?)$/
+      or return "Illegal manual_flag ". $self->manual_flag;
+    $self->manual_flag($1);
+  }
+
+  $self->SUPER::check;
+}
+
+=item check_pkgpart
+
+=cut
+
+sub check_pkgpart {
+  my $self = shift;
+
+  my $error = $self->ut_numbern('pkgpart');
+  return $error if $error;
+
   if ( $self->reg_code ) {
 
     unless ( grep { $self->pkgpart == $_->pkgpart }
@@ -569,16 +654,8 @@ sub check {
 
   }
 
-  $self->usernum($FS::CurrentUser::CurrentUser->usernum) unless $self->usernum;
-
-  if ( $self->dbdef_table->column('manual_flag') ) {
-    $self->manual_flag('') if $self->manual_flag eq ' ';
-    $self->manual_flag =~ /^([01]?)$/
-      or return "Illegal manual_flag ". $self->manual_flag;
-    $self->manual_flag($1);
-  }
+  '';
 
-  $self->SUPER::check;
 }
 
 =item cancel [ OPTION => VALUE ... ]
@@ -671,72 +748,57 @@ sub cancel {
     }
   }
 
-  my %svc;
-  if ( $date ) {
-# copied from below
-    foreach my $cust_svc (
-      #schwartz
-      map  { $_->[0] }
-      sort { $a->[1] <=> $b->[1] }
-      map  { [ $_, $_->svc_x->table_info->{'cancel_weight'} ]; }
-      qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } )
-    ) {
-      my $error = $cust_svc->cancel( ('date' => $date) );
+  my %svc_cancel_opt = ();
+  $svc_cancel_opt{'date'} = $date if $date;
+  foreach my $cust_svc (
+    #schwartz
+    map  { $_->[0] }
+    sort { $a->[1] <=> $b->[1] }
+    map  { [ $_, $_->svc_x->table_info->{'cancel_weight'} ]; }
+    qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } )
+  ) {
+    my $error = $cust_svc->cancel( %svc_cancel_opt );
 
-      if ( $error ) {
-        $dbh->rollback if $oldAutoCommit;
-        return "Error expiring cust_svc: $error";
-      }
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return 'Error '. ($svc_cancel_opt{'date'} ? 'expiring' : 'canceling' ).
+             " cust_svc: $error";
     }
-  } else { #!date
-    foreach my $cust_svc (
-      #schwartz
-      map  { $_->[0] }
-      sort { $a->[1] <=> $b->[1] }
-      map  { [ $_, $_->svc_x->table_info->{'cancel_weight'} ]; }
-      qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } )
-    ) {
-      my $error = $cust_svc->cancel;
+  }
 
-      if ( $error ) {
-        $dbh->rollback if $oldAutoCommit;
-        return "Error cancelling cust_svc: $error";
-      }
+  unless ($date) {
+
+    # Add a credit for remaining service
+    my $last_bill = $self->getfield('last_bill') || 0;
+    my $next_bill = $self->getfield('bill') || 0;
+    my $do_credit;
+    if ( exists($options{'unused_credit'}) ) {
+      $do_credit = $options{'unused_credit'};
+    }
+    else {
+      $do_credit = $self->part_pkg->option('unused_credit_cancel', 1);
     }
-  } #if $date
+    if ( $do_credit
+          and $last_bill > 0 # the package has been billed
+          and $next_bill > 0 # the package has a next bill date
+          and $next_bill >= $cancel_time # which is in the future
+    ) {
+      my $remaining_value = $self->calc_remain('time' => $cancel_time);
+      if ( $remaining_value > 0 ) {
+        my $error = $self->cust_main->credit(
+          $remaining_value,
+          'Credit for unused time on '. $self->part_pkg->pkg,
+          'reason_type' => $conf->config('cancel_credit_type'),
+        );
+        if ($error) {
+          $dbh->rollback if $oldAutoCommit;
+          return "Error crediting customer \$$remaining_value for unused time".
+                 " on ". $self->part_pkg->pkg. ": $error";
+        }
+      } #if $remaining_value
+    } #if $do_credit
 
-  # Add a credit for remaining service
-  my $last_bill = $self->getfield('last_bill') || 0;
-  my $next_bill = $self->getfield('bill') || 0;
-  my $do_credit;
-  if ( exists($options{'unused_credit'}) ) {
-    $do_credit = $options{'unused_credit'};
-  }
-  else {
-    $do_credit = $self->part_pkg->option('unused_credit_cancel', 1);
-  }
-  if ( $do_credit
-        and $last_bill > 0 # the package has been billed
-        and $next_bill > 0 # the package has a next bill date
-        and $next_bill >= $cancel_time # which is in the future
-  ) {
-    my $remaining_value = $self->calc_remain('time' => $cancel_time);
-    if ( $remaining_value > 0 ) {
-      # && !$options{'no_credit'} ) {
-      # Undocumented, unused option.
-      # part_pkg configuration should decide this anyway.
-      my $error = $self->cust_main->credit(
-        $remaining_value,
-        'Credit for unused time on '. $self->part_pkg->pkg,
-        'reason_type' => $conf->config('cancel_credit_type'),
-      );
-      if ($error) {
-        $dbh->rollback if $oldAutoCommit;
-        return "Error crediting customer \$$remaining_value for unused time on".
-               $self->part_pkg->pkg. ": $error";
-      }
-    } #if $remaining_value
-  } #if $do_credit
+  } #unless $date
 
   my %hash = $self->hash;
   $date ? ($hash{'expire'} = $date) : ($hash{'cancel'} = $cancel_time);
@@ -1707,6 +1769,9 @@ I<pkg_svc.hidden> flag will be omitted.
 
 sub h_cust_svc {
   my $self = shift;
+  warn "$me _h_cust_svc called on $self\n"
+    if $DEBUG;
+
   my ($end, $start, $mode) = @_;
   my @cust_svc = $self->_sort_cust_svc(
     [ qsearch( 'h_cust_svc',
@@ -1717,8 +1782,7 @@ sub h_cust_svc {
   if ( $mode eq 'I' ) {
     my %hidden_svcpart = map { $_->svcpart => $_->hidden } $self->part_svc;
     return grep { !$hidden_svcpart{$_->svcpart} } @cust_svc;
-  }
-  else {
+  } else {
     return @cust_svc;
   }
 }
@@ -2060,6 +2124,8 @@ Returns a list of lists, calling the label method for all (historical) services
 
 sub h_labels {
   my $self = shift;
+  warn "$me _h_labels called on $self\n"
+    if $DEBUG;
   map { [ $_->label(@_) ] } $self->h_cust_svc(@_);
 }
 
@@ -2092,22 +2158,39 @@ sub h_labels_short {
 sub _labels_short {
   my( $self, $method ) = ( shift, shift );
 
+  warn "$me _labels_short called on $self with $method method\n"
+    if $DEBUG;
+
   my $conf = new FS::Conf;
   my $max_same_services = $conf->config('cust_bill-max_same_services') || 5;
 
+  warn "$me _labels_short populating \%labels\n"
+    if $DEBUG;
+
   my %labels;
   #tie %labels, 'Tie::IxHash';
   push @{ $labels{$_->[0]} }, $_->[1]
     foreach $self->$method(@_);
+
+  warn "$me _labels_short populating \@labels\n"
+    if $DEBUG;
+
   my @labels;
   foreach my $label ( keys %labels ) {
     my %seen = ();
     my @values = grep { ! $seen{$_}++ } @{ $labels{$label} };
     my $num = scalar(@values);
+    warn "$me _labels_short $num items for $label\n"
+      if $DEBUG;
+
     if ( $num > $max_same_services ) {
+      warn "$me _labels_short   more than $max_same_services, so summarizing\n"
+        if $DEBUG;
       push @labels, "$label ($num)";
     } else {
       if ( $conf->exists('cust_bill-consolidate_services') ) {
+        warn "$me _labels_short   consolidating services\n"
+          if $DEBUG;
         # push @labels, "$label: ". join(', ', @values);
         while ( @values ) {
           my $detail = "$label: ";
@@ -2116,7 +2199,11 @@ sub _labels_short {
           $detail =~ s/, $//;
           push @labels, $detail;
         }
+        warn "$me _labels_short   done consolidating services\n"
+          if $DEBUG;
       } else {
+        warn "$me _labels_short   adding service data\n"
+          if $DEBUG;
         push @labels, map { "$label: $_" } @values;
       }
     }
@@ -2506,7 +2593,6 @@ sub insert_discount {
     'discountnum' => $self->discountnum,
     'months_used' => 0,
     'end_date'    => '', #XXX
-    'otaker'      => $self->otaker,
     #for the create a new discount case
     '_type'       => $self->discountnum__type,
     'amount'      => $self->discountnum_amount,
@@ -3402,6 +3488,10 @@ sub _upgrade_data {  # class method
   'UPDATE cust_pkg SET bill = bill + (365*24*60*60) WHERE bill < last_bill
   AND bill > 1259654400 AND bill < 1262332800 AND (SELECT plan FROM part_pkg 
   WHERE part_pkg.pkgpart = cust_pkg.pkgpart) = \'prorate\'',
+    # RT6628, add order_date to cust_pkg
+    'update cust_pkg set order_date = (select history_date from h_cust_pkg 
+       where h_cust_pkg.pkgnum = cust_pkg.pkgnum and 
+       history_action = \'insert\') where order_date is null',
   );
   foreach my $sql (@statements) {
     my $sth = dbh->prepare($sql);