"on hold" package ordering and status, RT#28508
[freeside.git] / FS / FS / cust_main / Billing.pm
index f4c30ce..2878276 100644 (file)
@@ -329,6 +329,10 @@ An array ref of specific packages (objects) to attempt billing, instead trying a
 
 A hashref of pkgparts to exclude from this billing run (can also be specified as a comma-separated scalar).
 
+=item no_prepaid
+
+Do not bill prepaid packages.  Used by freeside-daily.
+
 =item invoice_time
 
 Used in conjunction with the I<time> option, this option specifies the date of for the generated invoices.  Other calculations, such as whether or not to generate the invoice in the first place, are not affected.
@@ -442,21 +446,23 @@ sub bill {
 
     next if $options{'not_pkgpart'}->{$cust_pkg->pkgpart};
 
+    my $part_pkg = $cust_pkg->part_pkg;
+
+    next if $options{'no_prepaid'} && $part_pkg->is_prepaid;
+
     warn "  bill package ". $cust_pkg->pkgnum. "\n" if $DEBUG;
 
     #? to avoid use of uninitialized value errors... ?
     $cust_pkg->setfield('bill', '')
       unless defined($cust_pkg->bill);
  
-    #my $part_pkg = $cust_pkg->part_pkg;
-
     my $real_pkgpart = $cust_pkg->pkgpart;
     my %hash = $cust_pkg->hash;
 
     # we could implement this bit as FS::part_pkg::has_hidden, but we already
     # suffer from performance issues
     $options{has_hidden} = 0;
-    my @part_pkg = $cust_pkg->part_pkg->self_and_bill_linked;
+    my @part_pkg = $part_pkg->self_and_bill_linked;
     $options{has_hidden} = 1 if ($part_pkg[1] && $part_pkg[1]->hidden);
  
     # if this package was changed from another package,
@@ -533,8 +539,6 @@ sub bill {
 
     my @cust_bill_pkg = _omit_zero_value_bundles(@{ $cust_bill_pkg{$pass} });
 
-    next unless @cust_bill_pkg; #don't create an invoice w/o line items
-
     warn "$me billing pass $pass\n"
            #.Dumper(\@cust_bill_pkg)."\n"
       if $DEBUG > 2;
@@ -547,13 +551,29 @@ sub bill {
       hashref => { 'billpkgnum' => '' }
     );
     warn "$me found pending fee events:\n".Dumper(\@pending_event_fees)."\n"
-      if @pending_event_fees;
+      if @pending_event_fees and $DEBUG > 1;
+
+    # determine whether to generate an invoice
+    my $generate_bill = scalar(@cust_bill_pkg) > 0;
+
+    foreach my $event_fee (@pending_event_fees) {
+      $generate_bill = 1 unless $event_fee->nextbill;
+    }
+    
+    # don't create an invoice with no line items, or where the only line 
+    # items are fees that are supposed to be held until the next invoice
+    next if !$generate_bill;
 
+    # calculate fees...
     my @fee_items;
     foreach my $event_fee (@pending_event_fees) {
       my $object = $event_fee->cust_event->cust_X;
+      my $part_fee = $event_fee->part_fee;
       my $cust_bill;
-      if ( $object->isa('FS::cust_main') ) {
+      if ( $object->isa('FS::cust_main')
+           or $object->isa('FS::cust_pkg')
+           or $object->isa('FS::cust_pay_batch') )
+      {
         # Not the real cust_bill object that will be inserted--in particular
         # there are no taxes yet.  If you want to charge a fee on the total 
         # invoice amount including taxes, you have to put the fee on the next
@@ -564,12 +584,20 @@ sub bill {
             'charged'       => ${ $total_setup{$pass} } +
                                ${ $total_recur{$pass} },
         });
+
+        # If this is a package event, only apply the fee to line items 
+        # from that package.
+        if ($object->isa('FS::cust_pkg')) {
+          $cust_bill->set('cust_bill_pkg', 
+            [ grep  { $_->pkgnum == $object->pkgnum } @cust_bill_pkg ]
+          );
+        }
+
       } elsif ( $object->isa('FS::cust_bill') ) {
         # simple case: applying the fee to a previous invoice (late fee, 
         # etc.)
         $cust_bill = $object;
       }
-      my $part_fee = $event_fee->part_fee;
       # if the fee def belongs to a different agent, don't charge the fee.
       # event conditions should prevent this, but just in case they don't,
       # skip the fee.
@@ -581,11 +609,14 @@ sub bill {
       # also skip if it's disabled
       next if $part_fee->disabled eq 'Y';
       # calculate the fee
-      my $fee_item = $event_fee->part_fee->lineitem($cust_bill);
+      my $fee_item = $part_fee->lineitem($cust_bill) or next;
       # link this so that we can clear the marker on inserting the line item
       $fee_item->set('cust_event_fee', $event_fee);
       push @fee_items, $fee_item;
+
     }
+    
+    # add fees to the invoice
     foreach my $fee_item (@fee_items) {
 
       push @cust_bill_pkg, $fee_item;
@@ -1096,10 +1127,16 @@ sub _make_lines {
   my $recur_billed_amount = 0;
   my $sdate;
   if (     ! $cust_pkg->start_date
-       and ( ! $cust_pkg->susp || $cust_pkg->option('suspend_bill',1)
-                               || ( $part_pkg->option('suspend_bill', 1) )
-                                     && ! $cust_pkg->option('no_suspend_bill',1)
-                                  )
+       and 
+           ( ! $cust_pkg->susp
+               || ( $cust_pkg->susp != $cust_pkg->order_date
+                      && (    $cust_pkg->option('suspend_bill',1)
+                           || ( $part_pkg->option('suspend_bill', 1)
+                                 && ! $cust_pkg->option('no_suspend_bill',1)
+                              )
+                         )
+                  )
+           )
        and
             ( $part_pkg->freq ne '0' && ( $cust_pkg->bill || 0 ) <= $cmp_time )
          || ( $part_pkg->plan eq 'voip_cdr'