feature to email CSV of CDRs with invoices #5727
[freeside.git] / FS / FS / cust_bill.pm
index 6fac0a9..4e28af3 100644 (file)
@@ -605,6 +605,8 @@ sub generate_email {
     'subject'   => (($args{'subject'}) ? $args{'subject'} : 'Invoice'),
   );
 
+  my %cdrs = ( 'unsquelch_cdr' => $conf->exists('voip-cdr_email') );
+
   if (ref($args{'to'}) eq 'ARRAY') {
     $return{'to'} = $args{'to'};
   } else {
@@ -643,7 +645,7 @@ sub generate_email {
       if ( ref($args{'print_text'}) eq 'ARRAY' ) {
         $data = $args{'print_text'};
       } else {
-        $data = [ $self->print_text('', $args{'template'}) ];
+        $data = [ $self->print_text('', $args{'template'}, %cdrs) ];
       }
 
     }
@@ -690,7 +692,11 @@ sub generate_email {
                          '    </title>',
                          '  </head>',
                          '  <body bgcolor="#e8e8e8">',
-                         $self->print_html('', $args{'template'}, $content_id),
+                         $self->print_html({ time          => '',
+                                             template      => $args{'template'},
+                                             cid           => $content_id,
+                                             %cdrs,
+                                          }),
                          '  </body>',
                          '</html>',
                        ],
@@ -698,6 +704,18 @@ sub generate_email {
       #'Filename'    => 'invoice.pdf',
     );
 
+    my @otherparts = ();
+    if ( $self->cust_main->email_csv_cdr ) {
+
+      push @otherparts, build MIME::Entity
+        'Type'        => 'text/csv',
+        'Encoding'    => '7bit',
+        'Data'        => [ map { "$_\n" } $self->call_details ],
+        'Disposition' => 'attachment',
+      ;
+
+    }
+
     if ( $conf->exists('invoice_email_pdf') ) {
 
       #attaching pdf too:
@@ -723,9 +741,9 @@ sub generate_email {
 
       $related->add_part($image);
 
-      my $pdf = build MIME::Entity $self->mimebuild_pdf('', $args{'template'});
+      my $pdf = build MIME::Entity $self->mimebuild_pdf('', $args{'template'}, %cdrs);
 
-      $return{'mimeparts'} = [ $related, $pdf ];
+      $return{'mimeparts'} = [ $related, $pdf, @otherparts ];
 
     } else {
 
@@ -737,7 +755,7 @@ sub generate_email {
       #   image/png
 
       $return{'content-type'} = 'multipart/related';
-      $return{'mimeparts'} = [ $alternative, $image ];
+      $return{'mimeparts'} = [ $alternative, $image, @otherparts ];
       $return{'type'} = 'multipart/alternative'; #Content-Type of first part...
       #$return{'disposition'} = 'inline';
 
@@ -751,7 +769,7 @@ sub generate_email {
 
       #mime parts arguments a la MIME::Entity->build().
       $return{'mimeparts'} = [
-        { $self->mimebuild_pdf('', $args{'template'}) }
+        { $self->mimebuild_pdf('', $args{'template'}, %cdrs) }
       ];
     }
   
@@ -771,7 +789,7 @@ sub generate_email {
       if ( ref($args{'print_text'}) eq 'ARRAY' ) {
         $return{'body'} = $args{'print_text'};
       } else {
-        $return{'body'} = [ $self->print_text('', $args{'template'}) ];
+        $return{'body'} = [ $self->print_text('', $args{'template'}, %cdrs) ];
       }
 
     }
@@ -796,7 +814,7 @@ sub mimebuild_pdf {
     'Encoding'    => 'base64',
     'Data'        => [ $self->print_pdf(@_) ],
     'Disposition' => 'attachment',
-    'Filename'    => 'invoice.pdf',
+    'Filename'    => 'invoice-'. $self->invnum. '.pdf',
   );
 }
 
@@ -1457,11 +1475,9 @@ sub print_csv {
   
       } else { #pkgnum tax
         next unless $cust_bill_pkg->setup != 0;
-        my $itemdesc = defined $cust_bill_pkg->dbdef_table->column('itemdesc')
-                         ? ( $cust_bill_pkg->itemdesc || 'Tax' )
-                         : 'Tax';
-        ($pkg, $setup, $recur, $sdate, $edate) =
-          ( $itemdesc, sprintf("%10.2f",$cust_bill_pkg->setup), '', '', '' );
+        $pkg = $cust_bill_pkg->desc;
+        $setup = sprintf('%10.2f', $cust_bill_pkg->setup );
+        ( $sdate, $edate ) = ( '', '' );
       }
   
       $csv->combine(
@@ -1613,11 +1629,12 @@ L<Time::Local> and L<Date::Parse> for conversion functions.
 =cut
 
 sub print_text {
-  my( $self, $today, $template ) = @_;
+  my( $self, $today, $template, %opt ) = @_;
 
   my %params = ( 'format' => 'template' );
   $params{'time'} = $today if $today;
   $params{'template'} = $template if $template;
+  $params{'unsquelch_cdr'} = $opt{'unsquelch_cdr'} if $opt{'unsquelch_cdr'};
 
   $self->print_generic( %params );
 }
@@ -1638,11 +1655,12 @@ L<Time::Local> and L<Date::Parse> for conversion functions.
 =cut
 
 sub print_latex {
-  my( $self, $today, $template ) = @_;
+  my( $self, $today, $template, %opt ) = @_;
 
   my %params = ( 'format' => 'latex' );
   $params{'time'} = $today if $today;
   $params{'template'} = $template if $template;
+  $params{'unsquelch_cdr'} = $opt{'unsquelch_cdr'} if $opt{'unsquelch_cdr'};
 
   $template ||= $self->_agent_template;
 
@@ -2280,7 +2298,8 @@ sub print_generic {
   
     # credits
     my $credittotal = 0;
-    foreach my $credit ( $self->_items_credits ) {
+    foreach my $credit ( $self->_items_credits('trim_len'=>60) ) {
+
       my $total;
       $total->{'total_item'} = &$escape_function($credit->{'description'});
       $credittotal += $credit->{'amount'};
@@ -2297,26 +2316,18 @@ sub print_generic {
           product_code => '',
           section      => $adjust_section,
         };
-      }else{
+      } else {
         push @total_items, $total;
       }
+
     }
     $invoice_data{'credittotal'} = sprintf('%.2f', $credittotal);
-  
-    # credits (again)
-    foreach ( $self->cust_credited ) {
-  
-      #something more elaborate if $_->amount ne $_->cust_credit->credited ?
-
-      my $reason = substr($_->cust_credit->reason,0,32);
-      $reason .= '...' if length($reason) < length($_->cust_credit->reason);
-      $reason = " ($reason) " if $reason;
-      push @buf,[
-        "Credit #". $_->crednum. " (". time2str("%x",$_->cust_credit->_date) .")".        $reason,
-        $money_char. sprintf("%10.2f",$_->amount)
-      ];
-    }
 
+    #credits (again)
+    foreach my $credit ( $self->_items_credits('trim_len'=>32) ) {
+      push @buf, [ $credit->{'description'}, $money_char.$credit->{'amount'} ];
+    }
+  
     # payments
     my $paymenttotal = 0;
     foreach my $payment ( $self->_items_payments ) {
@@ -2585,8 +2596,8 @@ sub terms {
   return $self->cust_main->invoice_terms
     if $self->cust_main->invoice_terms;
 
-  #use configured default or default default
-  $conf->config('invoice_default_terms') || 'Payable upon receipt';
+  #use configured default
+  $conf->config('invoice_default_terms') || '';
 }
 
 sub due_date {
@@ -2802,8 +2813,19 @@ sub _items_cust_bill_pkg {
   my $section = $opt{section}->{description} if $opt{section};
 
   my @b = ();
+  my ($s, $r, $u) = ( undef, undef, undef );
   foreach my $cust_bill_pkg ( @$cust_bill_pkg )
   {
+
+    foreach ( $s, $r, $u ) {
+      if ( $_ && !$cust_bill_pkg->hidden ) {
+        $_->{amount}      = sprintf( "%.2f", $_->{amount} ),
+        $_->{unit_amount} = sprintf( "%.2f", $_->{unit_amount} ),
+        push @b, { %$_ };
+        $_ = undef;
+      }
+    }
+
     foreach my $display ( grep { defined($section)
                                  ? $_->section eq $section
                                  : 1
@@ -2832,19 +2854,28 @@ sub _items_cust_bill_pkg {
           my $description = $desc;
           $description .= ' Setup' if $cust_bill_pkg->recur != 0;
 
-          my @d = map &{$escape_function}($_),
-                         $cust_pkg->h_labels_short($self->_date);
+          my @d = ();
+          push @d, map &{$escape_function}($_),
+                       $cust_pkg->h_labels_short($self->_date)
+            unless $cust_pkg->part_pkg->hide_svc_detail
+                || $cust_bill_pkg->hidden;
           push @d, $cust_bill_pkg->details(%details_opt)
             if $cust_bill_pkg->recur == 0;
 
-          push @b, {
-            description     => $description,
-            #pkgpart         => $part_pkg->pkgpart,
-            pkgnum          => $cust_bill_pkg->pkgnum,
-            amount          => sprintf("%.2f", $cust_bill_pkg->setup),
-            unit_amount     => sprintf("%.2f", $cust_bill_pkg->unitsetup),
-            quantity        => $cust_bill_pkg->quantity,
-            ext_description => \@d,
+          if ( $cust_bill_pkg->hidden ) {
+            $s->{amount}      += $cust_bill_pkg->setup;
+            $s->{unit_amount} += $cust_bill_pkg->unitsetup;
+            push @{ $s->{ext_description} }, @d;
+          } else {
+            $s = {
+              description     => $description,
+              #pkgpart         => $part_pkg->pkgpart,
+              pkgnum          => $cust_bill_pkg->pkgnum,
+              amount          => $cust_bill_pkg->setup,
+              unit_amount     => $cust_bill_pkg->unitsetup,
+              quantity        => $cust_bill_pkg->quantity,
+              ext_description => \@d,
+            };
           };
 
         }
@@ -2862,16 +2893,23 @@ sub _items_cust_bill_pkg {
                             " - ". time2str("%x", $cust_bill_pkg->edate). ")";
           }
 
+          my @d = ();
+
           #at least until cust_bill_pkg has "past" ranges in addition to
           #the "future" sdate/edate ones... see #3032
-          my @d = ();
+          my @dates = ( $self->_date );
+          my $prev = $cust_bill_pkg->previous_cust_bill_pkg;
+          push @dates, $prev->sdate if $prev;
+
           push @d, map &{$escape_function}($_),
-                         $cust_pkg->h_labels_short($self->_date)
-                                                #$cust_bill_pkg->edate,
-                                                #$cust_bill_pkg->sdate),
-            ;
-  
-          @d = () if ($cust_bill_pkg->itemdesc || $is_summary);
+                       $cust_pkg->h_labels_short(@dates)
+                                                 #$cust_bill_pkg->edate,
+                                                 #$cust_bill_pkg->sdate)
+            unless $cust_pkg->part_pkg->hide_svc_detail
+                || $cust_bill_pkg->itemdesc
+                || $cust_bill_pkg->hidden
+                || $is_summary;
+
           push @d, $cust_bill_pkg->details(%details_opt)
             unless ($is_summary || $type && $type eq 'R');
   
@@ -2884,17 +2922,45 @@ sub _items_cust_bill_pkg {
             $amount = $cust_bill_pkg->usage;
           }
   
-          push @b, {
-            description     => $description,
-            #pkgpart         => $part_pkg->pkgpart,
-            pkgnum          => $cust_bill_pkg->pkgnum,
-            amount          => sprintf("%.2f", $amount),
-            unit_amount     => sprintf("%.2f", $cust_bill_pkg->unitrecur),
-            quantity        => $cust_bill_pkg->quantity,
-            ext_description => \@d,
-          } unless ( $type eq 'U' && ! $amount );
+          if ( !$type || $type eq 'R' ) {
+
+            if ( $cust_bill_pkg->hidden ) {
+              $r->{amount}      += $amount;
+              $r->{unit_amount} += $cust_bill_pkg->unitrecur;
+              push @{ $r->{ext_description} }, @d;
+            } else {
+              $r = {
+                description     => $description,
+                #pkgpart         => $part_pkg->pkgpart,
+                pkgnum          => $cust_bill_pkg->pkgnum,
+                amount          => $amount,
+                unit_amount     => $cust_bill_pkg->unitrecur,
+                quantity        => $cust_bill_pkg->quantity,
+                ext_description => \@d,
+              };
+            }
+
+          } elsif ( $amount ) {  # && $type eq 'U'
+
+            if ( $cust_bill_pkg->hidden ) {
+              $u->{amount}      += $amount;
+              $u->{unit_amount} += $cust_bill_pkg->unitrecur;
+              push @{ $u->{ext_description} }, @d;
+            } else {
+              $u = {
+                description     => $description,
+                #pkgpart         => $part_pkg->pkgpart,
+                pkgnum          => $cust_bill_pkg->pkgnum,
+                amount          => $amount,
+                unit_amount     => $cust_bill_pkg->unitrecur,
+                quantity        => $cust_bill_pkg->quantity,
+                ext_description => \@d,
+              };
+            }
 
-        }
+          }
+
+        } # recurring or usage with recurring charge
 
       } else { #pkgnum tax or one-shot line item (??)
 
@@ -2919,12 +2985,21 @@ sub _items_cust_bill_pkg {
 
   }
 
+  foreach ( $s, $r, $u ) {
+    if ( $_ ) {
+      $_->{amount}      = sprintf( "%.2f", $_->{amount} ),
+      $_->{unit_amount} = sprintf( "%.2f", $_->{unit_amount} ),
+      push @b, { %$_ };
+    }
+  }
+
   @b;
 
 }
 
 sub _items_credits {
-  my $self = shift;
+  my( $self, %opt ) = @_;
+  my $trim_len = $opt{'trim_len'} || 60;
 
   my @b;
   #credits
@@ -2932,10 +3007,10 @@ sub _items_credits {
 
     #something more elaborate if $_->amount ne $_->cust_credit->credited ?
 
-    my $reason = $_->cust_credit->reason;
-    #my $reason = substr($_->cust_credit->reason,0,32);
-    #$reason .= '...' if length($reason) < length($_->cust_credit->reason);
+    my $reason = substr($_->cust_credit->reason, 0, $trim_len);
+    $reason .= '...' if length($reason) < length($_->cust_credit->reason);
     $reason = " ($reason) " if $reason;
+
     push @b, {
       #'description' => 'Credit ref\#'. $_->crednum.
       #                 " (". time2str("%x",$_->cust_credit->_date) .")".
@@ -2945,12 +3020,6 @@ sub _items_credits {
       'amount'      => sprintf("%.2f",$_->amount),
     };
   }
-  #foreach ( @cr_cust_credit ) {
-  #  push @buf,[
-  #    "Credit #". $_->crednum. " (" . time2str("%x",$_->_date) .")",
-  #    $money_char. sprintf("%10.2f",$_->credited)
-  #  ];
-  #}
 
   @b;
 
@@ -2976,6 +3045,22 @@ sub _items_payments {
 
 }
 
+=item call_details
+
+Returns an array of CSV strings representing the call details for this invoice
+
+=cut
+
+sub call_details {
+  my $self = shift;
+  map { $_->details( 'format_function' => sub{ shift },
+                     'escape_function' => sub{ return() },
+                   )
+      }
+    grep { $_->pkgnum }
+    $self->cust_bill_pkg;
+}
+
 
 =back