X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_bill.pm;h=a01880cfe497d07684af9ea47d56be47dc803e0a;hb=d1eb33c1aa96d6f069a82232ab8a842cb046cdd6;hp=33552523ae921645fc1f18b11af85a42bc5203ea;hpb=20147e99fcd4987239889bcb490cb004e7d18391;p=freeside.git diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index 33552523a..a01880cfe 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -4,12 +4,12 @@ use strict; use vars qw( @ISA $conf $money_char ); use vars qw( $invoice_lines @buf ); #yuck use Date::Format; -use Text::Template; +use Text::Template 1.20; use File::Temp 0.14; use String::ShellQuote; use FS::UID qw( datasrc ); use FS::Record qw( qsearch qsearchs ); -use FS::Misc qw( send_email ); +use FS::Misc qw( send_email send_fax ); use FS::cust_main; use FS::cust_bill_pkg; use FS::cust_credit; @@ -378,7 +378,7 @@ sub generate_email { if (ref($args{'to'} eq 'ARRAY')) { @invoicing_list = @{$args{'to'}}; } else { - @invoicing_list = grep { $_ ne 'POST' } $self->cust_main->invoicing_list; + @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ } $self->cust_main->invoicing_list; } return ( @@ -418,16 +418,17 @@ sub send { my @print_text = $self->print_text('', $template); my @invoicing_list = $self->cust_main->invoicing_list; - if ( grep { $_ ne 'POST' } @invoicing_list or !@invoicing_list ) { #email + if ( grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list or !@invoicing_list ) { + #email #better to notify this person than silence @invoicing_list = ($invoice_from) unless @invoicing_list; my $error = send_email( $self->generate_email( - 'from' => $invoice_from, - 'to' => [ grep { $_ ne 'POST' } @invoicing_list ], - 'print_text' => [ @print_text ], + 'from' => $invoice_from, + 'to' => [ grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ], + 'print_text' => [ @print_text ], ) ); die "can't email invoice: $error\n" if $error; @@ -435,16 +436,33 @@ sub send { } - if ( grep { $_ eq 'POST' } @invoicing_list ) { #postal - @print_text = $self->print_ps('', $template) - if $conf->config('invoice_latex'); - my $lpr = $conf->config('lpr'); - open(LPR, "|$lpr") - or die "Can't open pipe to $lpr: $!\n"; - print LPR @print_text; - close LPR - or die $! ? "Error closing $lpr: $!\n" - : "Exit status $? from $lpr\n"; + if ( grep { $_ =~ /^(POST|FAX)$/ } @invoicing_list ) { + my $lpr_data; + if ($conf->config('invoice_latex')) { + $lpr_data = [ $self->print_ps('', $template) ]; + } else { + $lpr_data = \@print_text; + } + + if ( grep { $_ eq 'POST' } @invoicing_list ) { #postal + my $lpr = $conf->config('lpr'); + open(LPR, "|$lpr") + or die "Can't open pipe to $lpr: $!\n"; + print LPR @{$lpr_data}; + close LPR + or die $! ? "Error closing $lpr: $!\n" + : "Exit status $? from $lpr\n"; + } + + if ( grep { $_ eq 'FAX' } @invoicing_list ) { #fax + die 'FAX invoice destination not supported with plain text invoices.' + unless $conf->exists('invoice_latex'); + my $dialstring = $self->cust_main->getfield('fax'); + #Check $dialstring? + my $error = send_fax(docdata => $lpr_data, dialstring => $dialstring); + die $error if $error; + } + } ''; @@ -756,7 +774,7 @@ sub batch_card { 'state' => $cust_main->getfield('state'), 'zip' => $cust_main->getfield('zip'), 'country' => $cust_main->getfield('country'), - 'cardnum' => $cust_main->getfield('payinfo'), + 'cardnum' => $cust_main->payinfo, 'exp' => $cust_main->getfield('paydate'), 'payname' => $cust_main->getfield('payname'), 'amount' => $self->owed, @@ -1084,9 +1102,27 @@ sub print_latex { my $templatefile = 'invoice_latex'; my $suffix = length($template) ? "_$template" : ''; $templatefile .= $suffix; - my @invoice_template = $conf->config($templatefile) + my @invoice_template = map "$_\n", $conf->config($templatefile) or die "cannot load config file $templatefile"; + my($format, $text_template); + if ( grep { /^%%Detail/ } @invoice_template ) { + #change this to a die when the old code is removed + warn "old-style invoice template $templatefile; ". + "patch with conf/invoice_latex.diff or use new conf/invoice_latex*\n"; + $format = 'old'; + } else { + $format = 'Text::Template'; + $text_template = new Text::Template( + TYPE => 'ARRAY', + SOURCE => \@invoice_template, + DELIMITERS => [ '[@--', '--@]' ], + ); + + $text_template->compile() + or die 'While compiling ' . $templatefile . ': ' . $Text::Template::ERROR; + } + my %invoice_data = ( 'invnum' => $self->invnum, 'date' => time2str('%b %o, %Y', $self->_date), @@ -1101,9 +1137,11 @@ sub print_latex { 'country' => _latex_escape($cust_main->country), 'footer' => join("\n", $conf->config('invoice_latexfooter') ), 'smallfooter' => join("\n", $conf->config('invoice_latexsmallfooter') ), + 'returnaddress' => join("\n", $conf->config('invoice_latexreturnaddress') ), 'quantity' => 1, 'terms' => $conf->config('invoice_default_terms') || 'Payable upon receipt', #'notes' => join("\n", $conf->config('invoice_latexnotes') ), + 'conf_dir' => "$FS::UID::conf_dir/conf.$FS::UID::datasrc", ); my $countrydefault = $conf->config('countrydefault') || 'US'; @@ -1125,110 +1163,198 @@ sub print_latex { ? _latex_escape("Purchase Order #". $cust_main->payinfo) : '~'; - my @line_item = (); - my @total_item = (); my @filled_in = (); - while ( @invoice_template ) { - my $line = shift @invoice_template; - - if ( $line =~ /^%%Detail\s*$/ ) { - - while ( ( my $line_item_line = shift @invoice_template ) - !~ /^%%EndDetail\s*$/ ) { - push @line_item, $line_item_line; - } - foreach my $line_item ( $self->_items ) { - #foreach my $line_item ( $self->_items_pkg ) { - $invoice_data{'ref'} = $line_item->{'pkgnum'}; - $invoice_data{'description'} = _latex_escape($line_item->{'description'}); - if ( exists $line_item->{'ext_description'} ) { - $invoice_data{'description'} .= - "\\tabularnewline\n~~". - join("\\tabularnewline\n~~", map { _latex_escape($_) } @{$line_item->{'ext_description'}} ); + if ( $format eq 'old' ) { + + my @line_item = (); + my @total_item = (); + while ( @invoice_template ) { + my $line = shift @invoice_template; + + if ( $line =~ /^%%Detail\s*$/ ) { + + while ( ( my $line_item_line = shift @invoice_template ) + !~ /^%%EndDetail\s*$/ ) { + push @line_item, $line_item_line; } - $invoice_data{'amount'} = $line_item->{'amount'}; - $invoice_data{'product_code'} = $line_item->{'pkgpart'} || 'N/A'; - push @filled_in, - map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b } @line_item; - } - - } elsif ( $line =~ /^%%TotalDetails\s*$/ ) { - - while ( ( my $total_item_line = shift @invoice_template ) - !~ /^%%EndTotalDetails\s*$/ ) { - push @total_item, $total_item_line; - } - - my @total_fill = (); - - my $taxtotal = 0; - foreach my $tax ( $self->_items_tax ) { - $invoice_data{'total_item'} = _latex_escape($tax->{'description'}); - $taxtotal += ( $invoice_data{'total_amount'} = $tax->{'amount'} ); + foreach my $line_item ( $self->_items ) { + #foreach my $line_item ( $self->_items_pkg ) { + $invoice_data{'ref'} = $line_item->{'pkgnum'}; + $invoice_data{'description'} = + _latex_escape($line_item->{'description'}); + if ( exists $line_item->{'ext_description'} ) { + $invoice_data{'description'} .= + "\\tabularnewline\n~~". + join( "\\tabularnewline\n~~", + map _latex_escape($_), @{$line_item->{'ext_description'}} + ); + } + $invoice_data{'amount'} = $line_item->{'amount'}; + $invoice_data{'product_code'} = $line_item->{'pkgpart'} || 'N/A'; + push @filled_in, + map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b } @line_item; + } + + } elsif ( $line =~ /^%%TotalDetails\s*$/ ) { + + while ( ( my $total_item_line = shift @invoice_template ) + !~ /^%%EndTotalDetails\s*$/ ) { + push @total_item, $total_item_line; + } + + my @total_fill = (); + + my $taxtotal = 0; + foreach my $tax ( $self->_items_tax ) { + $invoice_data{'total_item'} = _latex_escape($tax->{'description'}); + $taxtotal += ( $invoice_data{'total_amount'} = $tax->{'amount'} ); + push @total_fill, + map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b } + @total_item; + } + + if ( $taxtotal ) { + $invoice_data{'total_item'} = 'Sub-total'; + $invoice_data{'total_amount'} = + '\dollar '. sprintf('%.2f', $self->charged - $taxtotal ); + unshift @total_fill, + map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b } + @total_item; + } + + $invoice_data{'total_item'} = '\textbf{Total}'; + $invoice_data{'total_amount'} = + '\textbf{\dollar '. sprintf('%.2f', $self->charged + $pr_total ). '}'; push @total_fill, map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b } @total_item; - } - - if ( $taxtotal ) { - $invoice_data{'total_item'} = 'Sub-total'; + + #foreach my $thing ( sort { $a->_date <=> $b->_date } $self->_items_credits, $self->_items_payments + + # credits + foreach my $credit ( $self->_items_credits ) { + $invoice_data{'total_item'} = _latex_escape($credit->{'description'}); + #$credittotal + $invoice_data{'total_amount'} = '-\dollar '. $credit->{'amount'}; + push @total_fill, + map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b } + @total_item; + } + + # payments + foreach my $payment ( $self->_items_payments ) { + $invoice_data{'total_item'} = _latex_escape($payment->{'description'}); + #$paymenttotal + $invoice_data{'total_amount'} = '-\dollar '. $payment->{'amount'}; + push @total_fill, + map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b } + @total_item; + } + + $invoice_data{'total_item'} = '\textbf{'. $self->balance_due_msg. '}'; $invoice_data{'total_amount'} = - '\dollar '. sprintf('%.2f', $self->charged - $taxtotal ); - unshift @total_fill, + '\textbf{\dollar '. sprintf('%.2f', $self->owed + $pr_total ). '}'; + push @total_fill, map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b } @total_item; + + push @filled_in, @total_fill; + + } else { + #$line =~ s/\$(\w+)/$invoice_data{$1}/eg; + $line =~ s/\$(\w+)/exists($invoice_data{$1}) ? $invoice_data{$1} : nounder($1)/eg; + push @filled_in, $line; } + + } - $invoice_data{'total_item'} = '\textbf{Total}'; - $invoice_data{'total_amount'} = - '\textbf{\dollar '. sprintf('%.2f', $self->charged + $pr_total ). '}'; - push @total_fill, - map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b } - @total_item; - - #foreach my $thing ( sort { $a->_date <=> $b->_date } $self->_items_credits, $self->_items_payments - - # credits - foreach my $credit ( $self->_items_credits ) { - $invoice_data{'total_item'} = _latex_escape($credit->{'description'}); - #$credittotal - $invoice_data{'total_amount'} = '-\dollar '. $credit->{'amount'}; - push @total_fill, - map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b } - @total_item; - } + sub nounder { + my $var = $1; + $var =~ s/_/\-/g; + $var; + } - # payments - foreach my $payment ( $self->_items_payments ) { - $invoice_data{'total_item'} = _latex_escape($payment->{'description'}); - #$paymenttotal - $invoice_data{'total_amount'} = '-\dollar '. $payment->{'amount'}; - push @total_fill, - map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b } - @total_item; + } elsif ( $format eq 'Text::Template' ) { + + my @detail_items = (); + my @total_items = (); + + $invoice_data{'detail_items'} = \@detail_items; + $invoice_data{'total_items'} = \@total_items; + + foreach my $line_item ( $self->_items ) { + my $detail = { + ext_description => [], + }; + $detail->{'ref'} = $line_item->{'pkgnum'}; + $detail->{'quantity'} = 1; + $detail->{'description'} = _latex_escape($line_item->{'description'}); + if ( exists $line_item->{'ext_description'} ) { + @{$detail->{'ext_description'}} = map { + _latex_escape($_); + } @{$line_item->{'ext_description'}}; } - - $invoice_data{'total_item'} = '\textbf{'. $self->balance_due_msg. '}'; - $invoice_data{'total_amount'} = + $detail->{'amount'} = $line_item->{'amount'}; + $detail->{'product_code'} = $line_item->{'pkgpart'} || 'N/A'; + + push @detail_items, $detail; + } + + + my $taxtotal = 0; + foreach my $tax ( $self->_items_tax ) { + my $total = {}; + $total->{'total_item'} = _latex_escape($tax->{'description'}); + $taxtotal += ( $invoice_data{'total_amount'} = $tax->{'amount'} ); + push @total_items, $total; + } + + if ( $taxtotal ) { + my $total = {}; + $total->{'total_item'} = 'Sub-total'; + $total->{'total_amount'} = + '\dollar '. sprintf('%.2f', $self->charged - $taxtotal ); + unshift @total_items, $total; + } + + { + my $total = {}; + $total->{'total_item'} = '\textbf{Total}'; + $total->{'total_amount'} = + '\textbf{\dollar '. sprintf('%.2f', $self->charged + $pr_total ). '}'; + push @total_items, $total; + } + + #foreach my $thing ( sort { $a->_date <=> $b->_date } $self->_items_credits, $self->_items_payments + + # credits + foreach my $credit ( $self->_items_credits ) { + my $total; + $total->{'total_item'} = _latex_escape($credit->{'description'}); + #$credittotal + $total->{'total_amount'} = '-\dollar '. $credit->{'amount'}; + push @total_items, $total; + } + + # payments + foreach my $payment ( $self->_items_payments ) { + my $total = {}; + $total->{'total_item'} = _latex_escape($payment->{'description'}); + #$paymenttotal + $total->{'total_amount'} = '-\dollar '. $payment->{'amount'}; + push @total_items, $total; + } + + { + my $total; + $total->{'total_item'} = '\textbf{'. $self->balance_due_msg. '}'; + $total->{'total_amount'} = '\textbf{\dollar '. sprintf('%.2f', $self->owed + $pr_total ). '}'; - push @total_fill, - map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b } - @total_item; - - push @filled_in, @total_fill; - - } else { - #$line =~ s/\$(\w+)/$invoice_data{$1}/eg; - $line =~ s/\$(\w+)/exists($invoice_data{$1}) ? $invoice_data{$1} : nounder($1)/eg; - push @filled_in, $line; + push @total_items, $total; } - } - - sub nounder { - my $var = $1; - $var =~ s/_/\-/g; - $var; + } else { + die "guru meditation #54"; } my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc; @@ -1237,7 +1363,13 @@ sub print_latex { SUFFIX => '.tex', UNLINK => 0, ) or die "can't open temp file: $!\n"; - print $fh join("\n", @filled_in ), "\n"; + if ( $format eq 'old' ) { + print $fh join('', @filled_in ); + } elsif ( $format eq 'Text::Template' ) { + $text_template->fill_in(OUTPUT => $fh, HASH => \%invoice_data); + } else { + die "guru meditation #32"; + } close $fh; $fh->filename =~ /^(.*).tex$/ or die "unparsable filename: ". $fh->filename; @@ -1316,9 +1448,9 @@ sub print_pdf { my $sfile = shell_quote $file; system("pslatex $sfile.tex >/dev/null 2>&1") == 0 - or die "pslatex $file.tex failed: $!"; + 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: $!"; + or die "pslatex $file.tex failed; see $file.log for details?\n"; #system('dvipdf', "$file.dvi", "$file.pdf" ); system( @@ -1356,6 +1488,7 @@ sub print_pdf { sub _latex_escape { my $value = shift; $value =~ s/([#\$%&~_\^{}])( )?/"\\$1". ( ( defined($2) && length($2) ) ? "\\$2" : '' )/ge; + $value =~ s/([<>])/\$$1\$/g; $value; }