cdrs can be in separate invoice section, after total, summarized inline, with hints...
authorjeff <jeff>
Fri, 8 Aug 2008 18:13:05 +0000 (18:13 +0000)
committerjeff <jeff>
Fri, 8 Aug 2008 18:13:05 +0000 (18:13 +0000)
FS/FS/Schema.pm
FS/FS/cust_bill.pm
FS/FS/cust_bill_pkg.pm
FS/FS/cust_main.pm
FS/FS/part_pkg/voip_cdr.pm
conf/invoice_latex

index 6c899c5..33b3064 100644 (file)
@@ -496,9 +496,12 @@ sub tables_hashref {
         'sdate',   @date_type, '', '', 
         'edate',   @date_type, '', '', 
         'itemdesc', 'varchar', 'NULL', $char_d, '', '', 
+        'section',  'varchar', 'NULL', $char_d, '', '', 
         'quantity',  'int', 'NULL', '', '', '',
         'unitsetup', @money_typen, '', '', 
         'unitrecur', @money_typen, '', '', 
+        'duplicate',  'char', 'NULL', 1, '', '',  # does this need to be in db?
+        'post_total', 'char', 'NULL', 1, '', '',
       ],
       'primary_key' => 'billpkgnum',
       'unique' => [],
index 96c1db7..3274d38 100644 (file)
@@ -1999,9 +1999,11 @@ sub print_generic {
   my $adjust_section = { 'description' => 'Credits, Payments, and Adjustments',
                          'subtotal'    => 0 }; # adjusted below
 
+  my $unsquelched = $params{unsquelch_cdr} || $cust_main->squelch_cdr ne 'Y';
   my $multisection = $conf->exists('invoice_sections', $cust_main->agentnum);
+  my $late_sections = [];
   if ( $multisection ) {
-    push @sections, $self->_items_sections;
+    push @sections, $self->_items_sections( $late_sections );
   }else{
     push @sections, { 'description' => '', 'subtotal' => '' };
   }
@@ -2040,7 +2042,7 @@ sub print_generic {
     push @buf, ['',''];
   }
 
-  foreach my $section (@sections) {
+  foreach my $section (@sections, @$late_sections) {
 
     $section->{'subtotal'} = $other_money_char.
                              sprintf('%.2f', $section->{'subtotal'})
@@ -2056,8 +2058,8 @@ sub print_generic {
     $options{'section'} = $section if $multisection;
     $options{'format'} = $format;
     $options{'escape_function'} = $escape_function;
-    $options{'format_function'} = sub { () }
-      unless $params{unsquelch_cdr} || $cust_main->squelch_cdr ne 'Y';
+    $options{'format_function'} = sub { () } unless $unsquelched;
+    $options{'unsquelched'} = $unsquelched;
 
     foreach my $line_item ( $self->_items_pkg(%options) ) {
       my $detail = {
@@ -2278,6 +2280,11 @@ sub print_generic {
     }
   }
 
+  if ( $multisection ) {
+    push @sections, @$late_sections
+      if $unsquelched;
+  }
+
   $invoice_lines = 0;
   my $wasfunc = 0;
   foreach ( grep /invoice_lines\(\d*\)/, @invoice_template ) { #kludgy
@@ -2537,24 +2544,42 @@ sub invnum_date_pretty {
 
 sub _items_sections {
   my $self = shift;
+  my $late = shift;
 
   my %s = ();
-  foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) {
+  my %l = ();
+
+  foreach my $cust_bill_pkg ( $self->cust_bill_pkg )
+  {
 
     if ( $cust_bill_pkg->pkgnum > 0 ) {
 
-      my $desc = $cust_bill_pkg->part_pkg->categoryname;
+      my $desc = $cust_bill_pkg->section;
 
-      $s{$desc} += $cust_bill_pkg->setup
-        if ( $cust_bill_pkg->setup != 0 );
+      if ( $cust_bill_pkg->post_total ) {
+        $l{$desc} += $cust_bill_pkg->setup
+          if ( $cust_bill_pkg->setup != 0 );
 
-      $s{$desc} += $cust_bill_pkg->recur
-        if ( $cust_bill_pkg->recur != 0 );
+        $l{$desc} += $cust_bill_pkg->recur
+          if ( $cust_bill_pkg->recur != 0 );
+
+      } else {
+        $s{$desc} += $cust_bill_pkg->setup
+          if ( $cust_bill_pkg->setup != 0 );
+
+        $s{$desc} += $cust_bill_pkg->recur
+          if ( $cust_bill_pkg->recur != 0 );
+      }
 
     }
 
   }
 
+  push @$late, map { { 'description' => $_,
+                       'subtotal'    => $l{$_},
+                       'post_total'  => 1,
+                   } } sort keys %l;
+
   map { {'description' => $_, 'subtotal' => $s{$_}} } sort keys %s;
 
 }
@@ -2613,7 +2638,7 @@ sub _items_pkg {
   my @cust_bill_pkg =
     grep { $_->pkgnum &&
            ( defined($section)
-               ? $_->part_pkg->categoryname eq $section->{'description'}
+               ? $_->section eq $section->{'description'}
                : 1
            )
          } $self->cust_bill_pkg;
@@ -2643,9 +2668,13 @@ sub _items_cust_bill_pkg {
   my $format = $opt{format} || '';
   my $escape_function = $opt{escape_function} || sub { shift };
   my $format_function = $opt{format_function} || '';
+  my $unsquelched = $opt{unsquelched} || '';
 
   my @b = ();
-  foreach my $cust_bill_pkg ( @$cust_bill_pkg ) {
+  foreach my $cust_bill_pkg ( grep { $unsquelched ? 1 : ! $_->separate_cdr }
+                              @$cust_bill_pkg
+                            )
+  {
 
     my $cust_pkg = $cust_bill_pkg->cust_pkg;
 
index 638e707..b3b8ea5 100644 (file)
@@ -57,6 +57,24 @@ supported:
 
 =item itemdesc - Line item description (overrides normal package description)
 
+=item section - Invoice section (overrides normal package section)
+
+=duplicate - Indicates this item appears elsewhere on the invoice
+             (and should not be retaxed or reincluded in totals)
+
+=post_total - A hint that this item should appear after invoice totals
+
+=cut
+
+sub section {
+  my ( $self, $value ) = @_;
+  if ( defined($value) ) {
+    $self->setfield('section', $value);
+  } else {
+    $self->getfield('section') || $self->part_pkg->categoryname;
+  }
+}
+
 =item quantity - If not set, defaults to 1
 
 =item unitsetup - If not set, defaults to setup
@@ -174,6 +192,9 @@ sub check {
       || $self->ut_numbern('sdate')
       || $self->ut_numbern('edate')
       || $self->ut_textn('itemdesc')
+      || $self->ut_textn('section')
+      || $self->ut_enum('duplicate', [ '', 'Y' ])
+      || $self->ut_enum('post_total', [ '', 'Y' ])
   ;
   return $error if $error;
 
@@ -382,7 +403,7 @@ line item.
 
 sub units {
   my $self = shift;
-  $self->part_pkg->calc_units($self->cust_pkg);
+  $self->pkgnum ? $self->part_pkg->calc_units($self->cust_pkg) : 0; # 1?
 }
 
 =item quantity
@@ -425,6 +446,18 @@ sub unitrecur {
     : $self->getfield('unitrecur');
 }
 
+=item separate_cdr
+
+Returns true if this line item represents a cdr line item in its own section.
+  
+=cut
+
+# lame, but works for now
+sub separate_cdr {
+  my( $self ) = shift;
+  $self->pkgnum && $self->section ne $self->part_pkg->categoryname;
+}
+
 =back
 
 =head1 BUGS
index 8873a5b..51270d6 100644 (file)
@@ -2491,18 +2491,20 @@ sub _make_lines {
         unless $part_pkg->pkgpart == $real_pkgpart;
       push @$appended_cust_bill_pkg, $cust_bill_pkg;
 
-      $$total_setup += $cust_bill_pkg->setup;
-      $$total_recur += $cust_bill_pkg->recur;
+      unless ($cust_bill_pkg->duplicate) {
+        $$total_setup += $cust_bill_pkg->setup;
+        $$total_recur += $cust_bill_pkg->recur;
 
-      ###
-      # handle taxes
-      ###
+        ###
+        # handle taxes
+        ###
 
-      unless ( $self->tax =~ /Y/i || $self->payby eq 'COMP' ) {
+        unless ( $self->tax =~ /Y/i || $self->payby eq 'COMP' ) {
 
-        $self->_handle_taxes($part_pkg, $taxlisthash, $cust_bill_pkg);
+          $self->_handle_taxes($part_pkg, $taxlisthash, $cust_bill_pkg);
 
-      } #unless $self->tax =~ /Y/i || $self->payby eq 'COMP'
+        } #unless $self->tax =~ /Y/i || $self->payby eq 'COMP'
+      }
     }
   }
 
index 14f812f..991c33c 100644 (file)
@@ -89,6 +89,13 @@ tie my %rating_method, 'Tie::IxHash',
                           'type' => 'checkbox',
                         },
 
+    'usage_section' => { 'name' => 'Section in which to place separate usage charges',
+                       },
+
+    'summarize_usage' => { 'name' => 'Include usage summary with recurring charges when usage is in separate section',
+                          'type' => 'checkbox',
+                        },
+
     #XXX also have option for an external db
 #    'cdr_location' => { 'name' => 'CDR database location'
 #                        'type' => 'select',
@@ -120,7 +127,7 @@ tie my %rating_method, 'Tie::IxHash',
                        disable_src
                        domestic_prefix international_prefix
                        use_amaflags use_disposition output_format
-                       separate_usage
+                       separate_usage summarize_usage usage_section
                      )
                   ],
   'weight' => 40,
@@ -473,7 +480,12 @@ sub append_cust_bill_pkgs {
   return []
     unless $charges;  # unless @details?
 
-  my $cust_bill_pkg = new FS::cust_bill_pkg {
+  my @cust_bill_pkg = ();
+
+  my $want_summary = $self->option('summarize_usage', 'Hush!') &&
+                     $self->option('usage_section', 'Hush!');
+
+  push @cust_bill_pkg, new FS::cust_bill_pkg {
     'pkgnum'    => $cust_pkg->pkgnum,
     'setup'     => 0,
     'unitsetup' => 0,
@@ -483,10 +495,27 @@ sub append_cust_bill_pkgs {
     'sdate'     => $$sdate,
     'edate'     => $cust_pkg->bill,             # already fiddled
     'itemdesc'  => 'Usage charges',             # configurable?
-    'details'   => \@details,
+    'duplicate' => 'Y',
+  }
+    if $want_summary;
+
+  push @cust_bill_pkg, new FS::cust_bill_pkg {
+    'pkgnum'     => $cust_pkg->pkgnum,
+    'setup'      => 0,
+    'unitsetup ' => 0,
+    'recur'      => sprintf( "%.2f", $charges),  # hmmm
+    'unitrecur ' => 0,
+    'quantity'   => $cust_pkg->quantity,
+    'sdate'      => $$sdate,
+    'edate'      => $cust_pkg->bill,             # already fiddled
+    'itemdesc'   => 'Usage charges',             # configurable?
+    'section'    => $self->option('usage_section', 'Hush!'),
+    'details'    => \@details,
+    'post_total' => ( $want_summary ? 'Y' : '' ),
   };
 
-  return [ $cust_bill_pkg ];
+
+  return [ @cust_bill_pkg ];
 }
 
 1;
index ddd068e..fe910b2 100644 (file)
@@ -235,6 +235,7 @@ Terms: [@-- $terms --@]\\
       $OUT .= '\large\textsc{'. $section->{'pretotal'}. '}\\\\';\r
       $OUT .= '\\end{flushright}';\r
     }\r
+    $OUT .= '\pagebreak' if $section{'post_total'};\r
     $OUT .= '\captionsetup{singlelinecheck=false,justification=raggedright,font={Large,sc,bf}}';\r
     $OUT .= '\begin{longtable}{cllllllr}';\r
     $OUT .= '\caption*{ ';\r