bundled package presentation improvements
[freeside.git] / FS / FS / cust_bill.pm
index 2abd840..96c1db7 100644 (file)
@@ -12,7 +12,7 @@ use String::ShellQuote;
 use HTML::Entities;
 use Locale::Country;
 use FS::UID qw( datasrc );
-use FS::Misc qw( send_email send_fax generate_ps do_print );
+use FS::Misc qw( send_email send_fax generate_ps generate_pdf do_print );
 use FS::Record qw( qsearch qsearchs dbh );
 use FS::cust_main_Mixin;
 use FS::cust_main;
@@ -226,7 +226,12 @@ Returns the line items (see L<FS::cust_bill_pkg>) for this invoice.
 
 sub cust_bill_pkg {
   my $self = shift;
-  qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum } );
+  qsearch(
+    { 'table'    => 'cust_bill_pkg',
+      'hashref'  => { 'invnum' => $self->invnum },
+      'order_by' => 'ORDER BY billpkgnum',
+    }
+  );
 }
 
 =item cust_pkg
@@ -557,19 +562,27 @@ sub apply_payments_and_credits {
 
 }
 
-=item generate_email PARAMHASH
+=item generate_email OPTION => VALUE ...
 
-PARAMHASH can contain the following:
+Options:
 
 =over 4
 
-=item from       => sender address, required
+=item from
+
+sender address, required
+
+=item tempate
 
-=item tempate    => alternate template name, optional
+alternate template name, optional
 
-=item print_text => text attachment arrayref, optional
+=item print_text
 
-=item subject    => email subject, optional
+text attachment arrayref, optional
+
+=item subject
+
+email subject, optional
 
 =back
 
@@ -838,13 +851,15 @@ sub send {
 
   my @invoicing_list = $self->cust_main->invoicing_list;
 
+  #$self->email_invoice($template, $invoice_from)
   $self->email($template, $invoice_from)
     if grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list or !@invoicing_list;
 
+  #$self->print_invoice($template)
   $self->print($template)
     if grep { $_ eq 'POST' } @invoicing_list; #postal
 
-  $self->fax($template)
+  $self->fax_invoice($template)
     if grep { $_ eq 'FAX' } @invoicing_list; #fax
 
   '';
@@ -876,6 +891,7 @@ sub queueable_email {
 
 }
 
+#sub email_invoice {
 sub email {
   my $self = shift;
   my $template = scalar(@_) ? shift : '';
@@ -925,6 +941,7 @@ TEMPLATENAME, if specified, is the name of a suffix for alternate invoices.
 
 =cut
 
+#sub print_invoice {
 sub print {
   my $self = shift;
   my $template = scalar(@_) ? shift : '';
@@ -932,7 +949,7 @@ sub print {
   do_print $self->lpr_data($template);
 }
 
-=item fax [ TEMPLATENAME ] 
+=item fax_invoice [ TEMPLATENAME ] 
 
 Faxes this invoice.
 
@@ -940,7 +957,7 @@ TEMPLATENAME, if specified, is the name of a suffix for alternate invoices.
 
 =cut
 
-sub fax {
+sub fax_invoice {
   my $self = shift;
   my $template = scalar(@_) ? shift : '';
 
@@ -1614,6 +1631,8 @@ L<Time::Local> and L<Date::Parse> for conversion functions.
 
 cid - 
 
+unsquelch_cdr - overrides any per customer cdr squelching when true
+
 =cut
 
 sub print_generic {
@@ -1671,6 +1690,7 @@ sub print_generic {
                  'footer'        => sub { map "$_", @_ },
                  'smallfooter'   => sub { map "$_", @_ },
                  'returnaddress' => sub { map "$_", @_ },
+                 'coupon'        => sub { map "$_", @_ },
                },
     'html'  => {
                  'notes' =>
@@ -1702,6 +1722,7 @@ sub print_generic {
                        $_;
                      }  @_
                    },
+                 'coupon'        => sub { "" },
                },
     'template' => {
                  'notes' =>
@@ -1731,6 +1752,7 @@ sub print_generic {
                        $_;
                      }  @_
                    },
+                 'coupon'        => sub { "" },
                },
   );
 
@@ -1823,6 +1845,7 @@ sub print_generic {
     'date'            => time2str($date_format, $self->_date),
     'today'           => time2str('%b %o, %Y', $today),
     'agent'           => &$escape_function($cust_main->agent->agent),
+    'agent_custid'    => &$escape_function($cust_main->agent_custid),
     'payname'         => &$escape_function($cust_main->payname),
     'company'         => &$escape_function($cust_main->company),
     'address1'        => &$escape_function($cust_main->address1),
@@ -1831,7 +1854,7 @@ sub print_generic {
     'state'           => &$escape_function($cust_main->state),
     'zip'             => &$escape_function($cust_main->zip),
     'returnaddress'   => $returnaddress,
-    'quantity'        => 1,
+    #'quantity'        => 1,
     'terms'           => $self->terms,
     'template'        => $params{'template'},
     #'notes'           => join("\n", $conf->config('invoice_latexnotes') ),
@@ -1839,12 +1862,24 @@ sub print_generic {
     'conf_dir'        => "$FS::UID::conf_dir/conf.$FS::UID::datasrc",
     'page'            => 1,
     'total_pages'     => 1,
+    'current_charges' => sprintf("%.2f", $self->charged),
+    'duedate'         => $self->due_date2str('%m/%d/%Y'), #date_format?
+    'ship_enable'     => $conf->exists('invoice-ship_address'),
+    'unitprices'      => $conf->exists('invoice-unitprice'),
   );
 
+  my $countrydefault = $conf->config('countrydefault') || 'US';
+  my $prefix = $cust_main->has_ship_address ? 'ship_' : '';
+  foreach ( qw( contact company address1 address2 city state zip country fax) ){
+    my $method = $prefix.$_;
+    $invoice_data{"ship_$_"} = _latex_escape($cust_main->$method);
+  }
+  $invoice_data{'ship_country'} = ''
+    if ( $invoice_data{'ship_country'} eq $countrydefault );
+  
   $invoice_data{'cid'} = $params{'cid'}
     if $params{'cid'};
 
-  my $countrydefault = $conf->config('countrydefault') || 'US';
   if ( $cust_main->country eq $countrydefault ) {
     $invoice_data{'country'} = '';
   } else {
@@ -1872,8 +1907,18 @@ sub print_generic {
   push @address, ''
     while (scalar(@address) < 5);
 
+  $invoice_data{'logo_file'} = $params{'logo_file'}
+    if $params{'logo_file'};
+
+  my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance
+#  my( $cr_total, @cr_cust_credit ) = $self->cust_credit; #credits
+  #my $balance_due = $self->owed + $pr_total - $cr_total;
+  my $balance_due = $self->owed + $pr_total;
+  $invoice_data{'previous_balance'} = sprintf("%.2f", $pr_total);
+  $invoice_data{'balance'} = sprintf("%.2f", $balance_due);
+
   #do variable substitution in notes, footer, smallfooter
-  foreach my $include (qw( notes footer smallfooter )) {
+  foreach my $include (qw( notes footer smallfooter coupon )) {
 
     my $inc_file = $conf->key_orbase("invoice_${format}$include", $template);
     my @inc_src;
@@ -1919,11 +1964,6 @@ sub print_generic {
       ? &$escape_function("Purchase Order #". $cust_main->payinfo)
       : $nbsp;
 
-  my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance
-#  my( $cr_total, @cr_cust_credit ) = $self->cust_credit; #credits
-  #my $balance_due = $self->owed + $pr_total - $cr_total;
-  my $balance_due = $self->owed + $pr_total;
-
   my %money_chars = ( 'latex'    => '',
                       'html'     => $conf->config('money_char') || '$',
                       'template' => '',
@@ -1983,10 +2023,8 @@ sub print_generic {
         &$escape_function($_);
       } @{$line_item->{'ext_description'}};
     }
-    {
-      my $money = $old_latex ? '' : $money_char;
-      $detail->{'amount'} = $money. $line_item->{'amount'};
-    }
+    $detail->{'amount'} = ( $old_latex ? '' : $money_char).
+                          $line_item->{'amount'};
     $detail->{'product_code'} = $line_item->{'pkgpart'} || 'N/A';
   
     push @detail_items, $detail;
@@ -2018,22 +2056,24 @@ 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';
 
     foreach my $line_item ( $self->_items_pkg(%options) ) {
       my $detail = {
         ext_description => [],
       };
       $detail->{'ref'} = $line_item->{'pkgnum'};
-      $detail->{'quantity'} = 1;
+      $detail->{'quantity'} = $line_item->{'quantity'};
       $detail->{'section'} = $section;
       $detail->{'description'} = &$escape_function($line_item->{'description'});
       if ( exists $line_item->{'ext_description'} ) {
         @{$detail->{'ext_description'}} = @{$line_item->{'ext_description'}};
       }
-      {
-        my $money = $old_latex ? '' : $money_char;
-        $detail->{'amount'} = $money. $line_item->{'amount'};
-      }
+      $detail->{'amount'} = ( $old_latex ? '' : $money_char ).
+                              $line_item->{'amount'};
+      $detail->{'unit_amount'} = ( $old_latex ? '' : $money_char ).
+                                 $line_item->{'unit_amount'};
       $detail->{'product_code'} = $line_item->{'pkgpart'} || 'N/A';
   
       push @detail_items, $detail;
@@ -2101,6 +2141,7 @@ sub print_generic {
       unshift @total_items, $total;
     }
   }
+  $invoice_data{'taxtotal'} = sprintf('%.2f', $taxtotal);
   
   push @buf,['','-----------'];
   push @buf,[( $conf->exists('disable_previous_balance') 
@@ -2124,8 +2165,8 @@ sub print_generic {
                )
       );
     if ( $multisection ) {
-      $adjust_section->{'pretotal'} = 'New charges total '.
-                                      $total->{'total_amount'};
+      $adjust_section->{'pretotal'} = 'New charges total '. $other_money_char.
+                                      sprintf('%.2f', $self->charged );
     }else{
       push @total_items, $total;
     }
@@ -2146,10 +2187,11 @@ sub print_generic {
     #foreach my $thing ( sort { $a->_date <=> $b->_date } $self->_items_credits, $self->_items_payments
   
     # credits
+    my $credittotal = 0;
     foreach my $credit ( $self->_items_credits ) {
       my $total;
       $total->{'total_item'} = &$escape_function($credit->{'description'});
-      #$credittotal
+      $credittotal += $credit->{'amount'};
       $total->{'total_amount'} = '-'. $other_money_char. $credit->{'amount'};
       $adjusttotal += $credit->{'amount'};
       if ( $multisection ) {
@@ -2167,6 +2209,7 @@ sub print_generic {
         push @total_items, $total;
       }
     }
+    $invoice_data{'credittotal'} = sprintf('%.2f', $credittotal);
   
     # credits (again)
     foreach ( $self->cust_credited ) {
@@ -2183,10 +2226,11 @@ sub print_generic {
     }
 
     # payments
+    my $paymenttotal = 0;
     foreach my $payment ( $self->_items_payments ) {
       my $total = {};
       $total->{'total_item'} = &$escape_function($payment->{'description'});
-      #$paymenttotal
+      $paymenttotal += $payment->{'amount'};
       $total->{'total_amount'} = '-'. $other_money_char. $payment->{'amount'};
       $adjusttotal += $payment->{'amount'};
       if ( $multisection ) {
@@ -2207,6 +2251,7 @@ sub print_generic {
                    $money_char. sprintf("%10.2f", $payment->{'amount'}),
                  ];
     }
+    $invoice_data{'paymenttotal'} = sprintf('%.2f', $paymenttotal);
   
     if ( $multisection ) {
       $adjust_section->{'subtotal'} = $other_money_char.
@@ -2233,9 +2278,6 @@ sub print_generic {
     }
   }
 
-  $invoice_data{'logo_file'} = $params{'logo_file'}
-    if $params{'logo_file'};
-
   $invoice_lines = 0;
   my $wasfunc = 0;
   foreach ( grep /invoice_lines\(\d*\)/, @invoice_template ) { #kludgy
@@ -2303,8 +2345,8 @@ sub print_ps {
   my ($file, $lfile) = $self->print_latex(@_);
   my $ps = generate_ps($file);
   unlink($lfile);
-  $ps;
 
+  $ps;
 }
 
 =item print_pdf [ TIME [ , TEMPLATE ] ]
@@ -2322,44 +2364,10 @@ sub print_pdf {
   my $self = shift;
 
   my ($file, $lfile) = $self->print_latex(@_);
+  my $pdf = generate_pdf($file);
+  unlink($lfile);
 
-  my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
-  chdir($dir);
-
-  #system('pdflatex', "$file.tex");
-  #system('pdflatex', "$file.tex");
-  #! LaTeX Error: Unknown graphics extension: .eps.
-
-  my $sfile = shell_quote $file;
-
-  system("pslatex $sfile.tex >/dev/null 2>&1") == 0
-    or die "pslatex $file.tex failed; see $file.log for details?\n";
-  system("pslatex $sfile.tex >/dev/null 2>&1") == 0
-    or die "pslatex $file.tex failed; see $file.log for details?\n";
-
-  #system('dvipdf', "$file.dvi", "$file.pdf" );
-  system(
-    "dvips -q -t letter -f $sfile.dvi ".
-    "| gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=$sfile.pdf ".
-    "     -c save pop -"
-  ) == 0
-    or die "dvips | gs failed: $!";
-
-  open(PDF, "<$file.pdf")
-    or die "can't open $file.pdf: $! (error in LaTeX template?)\n";
-
-  unlink("$file.dvi", "$file.log", "$file.aux", "$file.pdf", "$file.tex");
-  unlink("$lfile");
-
-  my $pdf = '';
-  while (<PDF>) {
-    $pdf .= $_;
-  }
-
-  close PDF;
-
-  return $pdf;
-
+  $pdf;
 }
 
 =item print_html [ TIME [ , TEMPLATE [ , CID ] ] ]
@@ -2505,6 +2513,16 @@ sub balance_due_msg {
   $msg;
 }
 
+sub balance_due_date {
+  my $self = shift;
+  my $duedate = '';
+  if (    $conf->exists('invoice_default_terms') 
+       && $conf->config('invoice_default_terms')=~ /^\s*Net\s*(\d+)\s*$/ ) {
+    $duedate = time2str("%m/%d/%Y", $self->_date + ($1*86400) );
+  }
+  $duedate;
+}
+
 =item invnum_date_pretty
 
 Returns a string with the invoice number and date, for example:
@@ -2525,7 +2543,7 @@ sub _items_sections {
 
     if ( $cust_bill_pkg->pkgnum > 0 ) {
 
-      my $desc = $cust_bill_pkg->part_pkg->classname;
+      my $desc = $cust_bill_pkg->part_pkg->categoryname;
 
       $s{$desc} += $cust_bill_pkg->setup
         if ( $cust_bill_pkg->setup != 0 );
@@ -2595,7 +2613,7 @@ sub _items_pkg {
   my @cust_bill_pkg =
     grep { $_->pkgnum &&
            ( defined($section)
-               ? $_->part_pkg->classname eq $section->{'description'}
+               ? $_->part_pkg->categoryname eq $section->{'description'}
                : 1
            )
          } $self->cust_bill_pkg;
@@ -2624,6 +2642,7 @@ sub _items_cust_bill_pkg {
 
   my $format = $opt{format} || '';
   my $escape_function = $opt{escape_function} || sub { shift };
+  my $format_function = $opt{format_function} || '';
 
   my @b = ();
   foreach my $cust_bill_pkg ( @$cust_bill_pkg ) {
@@ -2634,6 +2653,7 @@ sub _items_cust_bill_pkg {
 
     my %details_opt = ( 'format'          => $format,
                         'escape_function' => $escape_function,
+                        'format_function' => $format_function,
                       );
 
     if ( $cust_bill_pkg->pkgnum > 0 ) {
@@ -2653,6 +2673,8 @@ sub _items_cust_bill_pkg {
           #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,
         };
       }
@@ -2661,8 +2683,8 @@ sub _items_cust_bill_pkg {
 
         my $description = $desc;
         unless ( $conf->exists('disable_line_item_date_ranges') ) {
-          $desc .= " (" . time2str("%x", $cust_bill_pkg->sdate).
-                   " - ". time2str("%x", $cust_bill_pkg->edate). ")";
+          $description .= " (" . time2str("%x", $cust_bill_pkg->sdate).
+                          " - ". time2str("%x", $cust_bill_pkg->edate). ")";
         }
 
         #at least until cust_bill_pkg has "past" ranges in addition to
@@ -2671,6 +2693,7 @@ sub _items_cust_bill_pkg {
                     $cust_pkg->h_labels_short($self->_date);
                                               #$cust_bill_pkg->edate,
                                               #$cust_bill_pkg->sdate),
+        @d = () if $cust_bill_pkg->itemdesc;
         push @d, $cust_bill_pkg->details(%details_opt);
 
         push @b, {
@@ -2678,8 +2701,9 @@ sub _items_cust_bill_pkg {
           #pkgpart         => $part_pkg->pkgpart,
           pkgnum          => $cust_bill_pkg->pkgnum,
           amount          => sprintf("%.2f", $cust_bill_pkg->recur),
+          unit_amount     => sprintf("%.2f", $cust_bill_pkg->unitrecur),
+          quantity        => $cust_bill_pkg->quantity,
           ext_description => \@d,
-
         };
 
       }