summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorivan <ivan>2003-11-29 08:08:36 +0000
committerivan <ivan>2003-11-29 08:08:36 +0000
commit23ec9b00410224868d6be1e47a0d72a6c8b3f6f5 (patch)
tree33839091532e09ff4efc376ab6e8553db42e45dc
parente6c8cca8d3daec590f248e9ca16384dc8c94e21e (diff)
postscript invoice redux
-rw-r--r--FS/FS/Conf.pm37
-rw-r--r--FS/FS/cust_bill.pm165
-rw-r--r--conf/invoice_latex155
-rw-r--r--conf/invoice_latexfooter5
-rw-r--r--conf/invoice_latexnotes9
-rwxr-xr-xhttemplate/misc/print-invoice.cgi9
-rwxr-xr-xhttemplate/view/cust_bill-ps.cgi13
7 files changed, 332 insertions, 61 deletions
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index c69817d49..b30beafaf 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -174,7 +174,7 @@ sub config_items {
my $self = shift;
#quelle kludge
@config_items,
- map {
+ ( map {
my $basename = basename($_);
$basename =~ /^(.*)$/;
$basename = $1;
@@ -185,7 +185,19 @@ sub config_items {
'type' => 'textarea',
}
} glob($self->dir. '/invoice_template_*')
- ;
+ ),
+ ( map {
+ my $basename = basename($_);
+ $basename =~ /^(.*)$/;
+ $basename = $1;
+ new FS::ConfItem {
+ 'key' => $basename,
+ 'section' => 'billing',
+ 'description' => 'Alternate LaTeX template for invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ }
+ } glob($self->dir. '/invoice_latex_*')
+ );
}
=back
@@ -450,6 +462,27 @@ httemplate/docs/config.html
'type' => 'textarea',
},
+ {
+ 'key' => 'invoice_latex',
+ 'section' => 'billing',
+ 'description' => 'Optional LaTeX template for typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexnotes',
+ 'section' => 'billing',
+ 'description' => 'Notes section for LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexfooter',
+ 'section' => 'billing',
+ 'description' => 'Footer for LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
{
'key' => 'invoice_default_terms',
'section' => 'billing',
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
index aacb50425..9409bc5f6 100644
--- a/FS/FS/cust_bill.pm
+++ b/FS/FS/cust_bill.pm
@@ -784,14 +784,7 @@ sub print_text {
}
#balance due
- my $balance_due_msg = 'Balance Due';
-
- if ( $conf->config('invoice_default_terms') =~ /^\s*Net\s*(\d+)\s*$/ ) {
- $balance_due_msg .=
- ' - Please pay by '. time2str("%x", $self->_date + ($1*86400) );
- } elsif ( $conf->config('invoice_default_terms') ) {
- $balance_due_msg .= ' - '. $conf->config('invoice_default_terms');
- }
+ my $balance_due_msg = $self->balance_due_msg;
push @buf,['','-----------'];
push @buf,[$balance_due_msg, $money_char.
@@ -920,36 +913,31 @@ sub print_ps {
@buf = ();
#create the template
- my $templatefile = 'invoice_template_latex';
+ my $templatefile = 'invoice_latex';
$templatefile .= "_$template" if $template;
my @invoice_template = $conf->config($templatefile)
or die "cannot load config file $templatefile";
my %invoice_data = (
- 'invnum' => $self->invnum,
- 'date' => time2str('%b %o, %Y', $self->_date),
- 'agent' => $cust_main->agent->agent,
- 'payname' => $cust_main->payname,
- 'company' => $cust_main->company,
- 'address1' => $cust_main->address1,
- 'address2' => $cust_main->address2,
- 'city' => $cust_main->city,
- 'state' => $cust_main->state,
- 'zip' => $cust_main->zip,
- 'country' => $cust_main->country,
- 'footer' => <<'END', #should come from config value
-Ivan Kohler\\
-1339 Hayes St.\\
-San Francisco, CA~~94117\\
-ivan@sisd.com~~~~+1 415 462 1624\\
-Freeside - open-source billing - http://www.sisd.com/freeside\\
-END
-
+ 'invnum' => $self->invnum,
+ 'date' => time2str('%b %o, %Y', $self->_date),
+ 'agent' => $cust_main->agent->agent,
+ 'payname' => $cust_main->payname,
+ 'company' => $cust_main->company,
+ 'address1' => $cust_main->address1,
+ 'address2' => $cust_main->address2,
+ 'city' => $cust_main->city,
+ 'state' => $cust_main->state,
+ 'zip' => $cust_main->zip,
+ 'country' => $cust_main->country,
+ 'footer' => join("\n", $conf->config('invoice_latexfooter') ),
'quantity' => 1,
-
+ 'terms' => $conf->config('invoice_default_terms') || 'Payable upon receipt',
+ 'notes' => join("\n", $conf->config('invoice_latexnotes') ),
);
- #$invoice_data{'footer'} =~ s/\n+$//;
+ $invoice_data{'footer'} =~ s/\n+$//;
+ $invoice_data{'notes'} =~ s/\n+$//;
my $countrydefault = $conf->config('countrydefault') || 'US';
$invoice_data{'country'} = '' if $invoice_data{'country'} eq $countrydefault;
@@ -971,8 +959,8 @@ END
!~ /^%%EndDetail\s*$/ ) {
push @line_item, $line_item_line;
}
- #foreach my $line_item ( $self->_items ) {
- foreach my $line_item ( $self->_items_pkg ) {
+ foreach my $line_item ( $self->_items ) {
+ #foreach my $line_item ( $self->_items_pkg ) {
$invoice_data{'ref'} = $line_item->{'pkgnum'};
$invoice_data{'description'} = $line_item->{'description'};
if ( exists $line_item->{'ext_description'} ) {
@@ -1004,22 +992,51 @@ END
@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->owed ). '}';
+ '\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';
- $invoice_data{'total_amount'} =
- '\dollar '. sprintf('%.2f', $self->owed - $taxtotal );
- unshift @total_fill,
+ #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'} = $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'} = $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'} =
+ '\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 {
@@ -1052,7 +1069,7 @@ END
#system('dvips', '-t', 'letter', "$file.dvi", "$file.ps");
system('dvips', '-t', 'letter', "$file.dvi" );
- open(POSTSCRIPT, "<$file.ps") or die "can't open $file.ps: $!\n";
+ open(POSTSCRIPT, "<$file.ps") or die "can't open $file.ps (probable error in LaTeX template): $!\n";
#rm $file.dvi $file.log $file.aux
#unlink("$file.dvi", "$file.log", "$file.aux", "$file.ps");
@@ -1071,11 +1088,23 @@ END
#utility methods for print_*
+sub balance_due_msg {
+ my $self = shift;
+ my $msg = 'Balance Due';
+ if ( $conf->config('invoice_default_terms') =~ /^\s*Net\s*(\d+)\s*$/ ) {
+ $msg .= ' - Please pay by '. time2str("%x", $self->_date + ($1*86400) );
+ } elsif ( $conf->config('invoice_default_terms') ) {
+ $msg .= ' - '. $conf->config('invoice_default_terms');
+ }
+ $msg;
+}
+
sub _items {
my $self = shift;
my @display = scalar(@_)
? @_
- : qw( _items_pkg );
+ : qw( _items_previous _items_pkg );
+ #: qw( _items_pkg );
#: qw( _items_previous _items_pkg _items_tax _items_credits _items_payments );
my @b = ();
foreach my $display ( @display ) {
@@ -1090,13 +1119,28 @@ sub _items_previous {
my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance
my @b = ();
foreach ( @pr_cust_bill ) {
- push @b, [
- "Previous Balance, Invoice #". $_->invnum.
- " (". time2str("%x",$_->_date). ")",
- $money_char. sprintf("%10.2f",$_->owed)
- ];
+ push @b, {
+ 'description' => 'Previous Balance, Invoice \#'. $_->invnum.
+ ' ('. time2str('%x',$_->_date). ')',
+ #'pkgpart' => 'N/A',
+ 'pkgnum' => 'N/A',
+ 'amount' => sprintf("%10.2f", $_->owed),
+ };
}
@b;
+
+ #{
+ # 'description' => 'Previous Balance',
+ # #'pkgpart' => 'N/A',
+ # 'pkgnum' => 'N/A',
+ # 'amount' => sprintf("%10.2f", $pr_total ),
+ # 'ext_description' => [ map {
+ # "Invoice ". $_->invnum.
+ # " (". time2str("%x",$_->_date). ") ".
+ # sprintf("%10.2f", $_->owed)
+ # } @pr_cust_bill ],
+
+ #};
}
sub _items_pkg {
@@ -1131,7 +1175,7 @@ sub _items_cust_bill_pkg {
@d = $cust_bill_pkg->details if $cust_bill_pkg->recur == 0;
push @b, {
'description' => $description,
- 'pkgpart' => $part_pkg->pkgpart,
+ #'pkgpart' => $part_pkg->pkgpart,
'pkgnum' => $cust_pkg->pkgnum,
'amount' => sprintf("%10.2f", $cust_bill_pkg->setup),
'ext_description' => [ ( map { $_->[0]. ": ". $_->[1] }
@@ -1146,7 +1190,7 @@ sub _items_cust_bill_pkg {
'description' => "$pkg (" .
time2str('%x', $cust_bill_pkg->sdate). ' - '.
time2str('%x', $cust_bill_pkg->edate). ')',
- 'pkgpart' => $part_pkg->pkgpart,
+ #'pkgpart' => $part_pkg->pkgpart,
'pkgnum' => $cust_pkg->pkgnum,
'amount' => sprintf("%10.2f", $cust_bill_pkg->recur),
'ext_description' => [ ( map { $_->[0]. ": ". $_->[1] }
@@ -1193,14 +1237,18 @@ sub _items_credits {
#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);
+ my $reason = $_->cust_credit->reason;
+ #my $reason = substr($_->cust_credit->reason,0,32);
+ #$reason .= '...' if length($reason) < length($_->cust_credit->reason);
$reason = " ($reason) " if $reason;
- push @b,[
- "Credit #". $_->crednum. " (". time2str("%x",$_->cust_credit->_date) .")".
- $reason,
- $money_char. sprintf("%10.2f",$_->amount)
- ];
+ push @b, {
+ #'description' => 'Credit ref\#'. $_->crednum.
+ # " (". time2str("%x",$_->cust_credit->_date) .")".
+ # $reason,
+ 'description' => 'Credit applied'.
+ time2str("%x",$_->cust_credit->_date). $reason,
+ 'amount' => sprintf("%10.2f",$_->amount),
+ };
}
#foreach ( @cr_cust_credit ) {
# push @buf,[
@@ -1222,10 +1270,11 @@ sub _items_payments {
#something more elaborate if $_->amount ne ->cust_pay->paid ?
- push @b,[
- "Payment received ". time2str("%x",$_->cust_pay->_date ),
- $money_char. sprintf("%10.2f",$_->amount )
- ];
+ push @b, {
+ 'description' => "Payment received ".
+ time2str("%x",$_->cust_pay->_date ),
+ 'amount' => sprintf("%10.2f", $_->amount )
+ };
}
@b;
diff --git a/conf/invoice_latex b/conf/invoice_latex
new file mode 100644
index 000000000..a89b9a33a
--- /dev/null
+++ b/conf/invoice_latex
@@ -0,0 +1,155 @@
+%% file: Standard Multipage.tex
+%% Purpose: Multipage bill template for e-Bills
+%%
+%% Created by Mark Asplen-Taylor
+%% Asplen Management Ltd
+%% www.asplen.co.uk
+%%
+%% Modified for Freeside by Ivan Kohler
+%%
+%% Changes
+%% 0.1 4/12/00 Created
+%% 0.2 18/10/01 More fields added
+%% 1.0 16/11/01 RELEASED
+%% 1.2 16/10/02 Invoice number added
+%% 1.3 2/12/02 Logo graphic added
+%% 1.4 7/2/03 Multipage headers/footers added
+%% n/a 10/12/03 forked for Freeside; checked into CVS
+%%
+
+\documentclass[letterpaper]{article}
+
+\usepackage{fancyhdr,lastpage,ifthen,longtable,afterpage}
+\usepackage{graphicx} % required for logo graphic
+
+\addtolength{\voffset}{-0.0in} % top margin to top of header
+\addtolength{\hoffset}{-0.25in} %left margin on page
+\addtolength{\topmargin}{-0.6in} % top margin to top of header
+\setlength{\headheight}{1in} % height of header
+\setlength{\headsep}{0.5in} % between header and text
+\addtolength{\textheight}{-0.4in} % height of main text
+
+\addtolength{\textheight}{-0.5in} % height of main text
+\setlength{\footskip}{0.5in} % bottom of footer from bottom of text
+
+\addtolength{\textwidth}{2.1in} % width of text
+\setlength{\oddsidemargin}{0in} % odd page left margin
+\setlength{\evensidemargin}{0in} % even page left margin
+
+\renewcommand{\headrulewidth}{0pt}
+\renewcommand{\footrulewidth}{1pt}
+
+ % New command for address lines i.e. skip them if blank
+
+\newcommand{\addressline}[1]{\ifthenelse{\equal{#1}{}}{}{#1\newline}}
+\newcommand{\dollar}[1][]{\symbol{36}} % Inserts dollar symbol
+
+\pagestyle{fancy}
+
+%% Font options are:
+%% bch Bitsream Charter
+%% put Utopia
+%% phv Adobe Helvetica
+%% pnc New Century Schoolbook
+%% ptm Times
+%% pcr Courier
+
+\renewcommand{\familydefault}{phv}
+
+\begin{document}
+%
+%% Headers and footers defined for the first page
+\fancyfoot[CO,CE]{\small{
+\begin{tabular}{c}
+$footer
+\end{tabular}}}
+%
+%% The LH Heading comprising logo
+%% UNCOMMENT the following FOUR lines and change the path if necssary to provide a logo
+\fancyhead[LO,LE]{
+\begin{tabular}{l}
+\includegraphics{/usr/local/etc/freeside/logo.eps}
+\end{tabular}}
+%
+%% The Heading comprising isue date, customer ref & INVOICE name
+\fancyhead[RO,RE]{
+\begin{tabular}{rcl}
+Invoice date & & Invoice number \\
+\vspace{0.2cm}
+\textbf{$date} & & \textbf{$invnum} \\\hline
+\rule{0pt}{5ex} &~~ \huge{\textsc{Invoice}}& \\
+\vspace{-0.2cm}
+ & & \\\hline
+\end{tabular}}
+%
+%% Header & footer changes for subsequent pages
+%
+\afterpage{ \fancyfoot[RO,RE]{\small{\thepage\ of \pageref{LastPage}}} }
+\afterpage{ \fancyfoot[CO,CE]{\small{$org_company}} }
+\afterpage{ \fancyhead[LO,LE]{\small{}} }
+\afterpage{ \fancyhead[RO,RE]{\small{
+\begin{tabular}{ll}
+Date & Account number\\
+\textbf{ $date_of_bill} & \textbf{ $customer_ref}\\
+\end{tabular}}} }
+%
+%
+\makebox{
+\begin{minipage}[t]{2.9in}
+\vspace{0.20in}
+\textbf{$payname}\\
+\addressline{$company}
+\addressline{$address1}
+\addressline{$address2}
+\addressline{$city, $state $zip}
+\addressline{$country}
+\end{minipage}}
+\hfill
+\makebox{
+\begin{minipage}[t]{2.5in}
+\begin{flushright}
+Terms: $terms\\
+$po_line\\
+\end{flushright}
+\end{minipage}}
+\vspace{0.5cm}
+%
+\section*{\textsc{Charges}}
+\begin{longtable}{|c|l|c|r|r|}
+\hline
+\rule{0pt}{2.5ex}
+\makebox[1.4cm]{\textbf{Ref}} &
+\makebox[7.9cm][l]{\textbf{Description}} &
+\makebox[1.3cm][c]{\textbf{Quantity}} &
+\makebox[2.5cm][r]{\textbf{Unit Price}} &
+\makebox[2.5cm][r]{\textbf{Amount}} \\
+\hline
+\endfirsthead
+\multicolumn{5}{r}{\rule{0pt}{2.5ex}Continued from previous page}\\
+\hline
+\rule{0pt}{2.5ex}
+\makebox[1.4cm]{\textbf{Ref}} &
+\makebox[7.9cm][l]{\textbf{Description}} &
+\makebox[1.3cm][c]{\textbf{Quantity}} &
+\makebox[2.5cm][r]{\textbf{Unit Price}} &
+\makebox[2.5cm][r]{\textbf{Amount}} \\
+\hline
+\endhead
+\multicolumn{5}{r}{\rule{0pt}{2.5ex}/cont...}\\
+\endfoot
+%%TotalDetails
+ & \multicolumn{3}{l}{$total_item} & $total_amount\\
+%%EndTotalDetails
+\hline
+\endlastfoot
+%%Detail
+\rule{0pt}{2.5ex}$ref &
+\begin{tabular}{l}
+$description\tabularnewline
+\end{tabular}
+& $quantity & \dollar $amount & \dollar $amount\\\hline
+%%EndDetail
+\end{longtable}
+\vfill
+$notes
+\end{document}
diff --git a/conf/invoice_latexfooter b/conf/invoice_latexfooter
new file mode 100644
index 000000000..110b1e6fe
--- /dev/null
+++ b/conf/invoice_latexfooter
@@ -0,0 +1,5 @@
+Ivan Kohler\\
+P.O. Box 1272\\
+Carnelian Bay, CA~~96140\\
+ivan@sisd.com~~~~+1 415 462 1624\\
+Freeside - open-source billing - http://www.sisd.com/freeside\\
diff --git a/conf/invoice_latexnotes b/conf/invoice_latexnotes
new file mode 100644
index 000000000..29e173103
--- /dev/null
+++ b/conf/invoice_latexnotes
@@ -0,0 +1,9 @@
+%%
+%% Add any customer specific notes in here
+%%
+\section*{\textsc{Notes}}
+\begin{enumerate}
+\item PLEASE NOTE NEW ADDRESS BELOW!
+\item Please make your check payable to \textbf{Ivan Kohler}.
+\item If you have any questions please email or telephone.
+\end{enumerate}
diff --git a/httemplate/misc/print-invoice.cgi b/httemplate/misc/print-invoice.cgi
index a5500bff2..8c1240c05 100755
--- a/httemplate/misc/print-invoice.cgi
+++ b/httemplate/misc/print-invoice.cgi
@@ -11,7 +11,14 @@ my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum});
die "Can't find invoice!\n" unless $cust_bill;
open(LPR,"|$lpr") or die "Can't open $lpr: $!";
- print LPR $cust_bill->print_text; #( date )
+
+ if ( $conf->exists('invoice_latex') ) {
+ $cust_bill->print_ps;
+ #print LPR $cust_bill->print_ps; #( date )
+ } else {
+ print LPR $cust_bill->print_text; #( date )
+ }
+
close LPR
or die $! ? "Error closing $lpr: $!"
: "Exit status $? from $lpr";
diff --git a/httemplate/view/cust_bill-ps.cgi b/httemplate/view/cust_bill-ps.cgi
new file mode 100755
index 000000000..03340a16f
--- /dev/null
+++ b/httemplate/view/cust_bill-ps.cgi
@@ -0,0 +1,13 @@
+<%
+
+#untaint invnum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $invnum = $1;
+
+my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum});
+die "Invoice #$invnum not found!" unless $cust_bill;
+
+http_header('Content-Type' => 'application/postscript' );
+%>
+<%= $cust_bill->print_ps %>