summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
authorivan <ivan>2005-05-14 16:27:26 +0000
committerivan <ivan>2005-05-14 16:27:26 +0000
commit3081639bd119c6d281ef23139649b2e73ba62754 (patch)
treed7e99189f3a08391fc4fe148d18f45cb1f91f396 /FS
parentd33c75b60d9cb9f7155635dc2cd25307f06d947f (diff)
html invoices!
http://chris-linfoot.net/d6plinks/CWLT-5VZD4Y http://www.dsv.su.se/~jpalme/ietf/mhtml.html ftp://ftp.dsv.su.se/users/jpalme/draft-ietf-mhtml-info.txt http://mailformat.dan.info/headers/mime.html http://www.faqs.org/rfcs/rfc2392.html http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdosys/html/_cdosys_content-type_multipart.asp (MIME is hard, let's go shopping!)
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/Conf.pm56
-rw-r--r--FS/FS/Misc.pm116
-rw-r--r--FS/FS/cust_bill.pm357
-rw-r--r--FS/FS/part_bill_event.pm2
4 files changed, 368 insertions, 163 deletions
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index be282971e..15ac23d53 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -209,6 +209,31 @@ sub config_items {
new FS::ConfItem {
'key' => $basename,
'section' => 'billing',
+ 'description' => 'Alternate HTML template for invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ }
+ } glob($self->dir. '/invoice_html_*')
+ ),
+ ( map {
+ my $basename = basename($_);
+ $basename =~ /^(.*)$/;
+ $basename = $1;
+ ($latexname = $basename ) =~ s/latex/html/;
+ new FS::ConfItem {
+ 'key' => $basename,
+ 'section' => 'billing',
+ 'description' => "Alternate Notes section for HTML invoices. Defaults to the same data in $latexname if not specified.",
+ 'type' => 'textarea',
+ }
+ } glob($self->dir. '/invoice_htmlnotes_*')
+ ),
+ ( 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',
}
@@ -540,9 +565,38 @@ httemplate/docs/config.html
},
{
+ 'key' => 'invoice_html',
+ 'section' => 'billing',
+ 'description' => 'Optional HTML template for invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
+
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_htmlnotes',
+ 'section' => 'billing',
+ 'description' => 'Notes section for HTML invoices. Defaults to the same data in invoice_latexnotes if not specified.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_htmlfooter',
+ 'section' => 'billing',
+ 'description' => 'Footer for HTML invoices. Defaults to the same data in invoice_latexfooter if not specified.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_htmlreturnaddress',
+ 'section' => 'billing',
+ 'description' => 'Return address for HTML invoices. Defaults to the same data in invoice_latexreturnaddress if not specified.',
+ 'type' => 'textarea',
+ },
+
+ {
'key' => 'invoice_latex',
'section' => 'billing',
- 'description' => 'Optional LaTeX template for typeset PostScript invoices.',
+ 'description' => 'Optional LaTeX template for typeset PostScript invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
'type' => 'textarea',
},
diff --git a/FS/FS/Misc.pm b/FS/FS/Misc.pm
index 7d7b7d061..92780f707 100644
--- a/FS/FS/Misc.pm
+++ b/FS/FS/Misc.pm
@@ -1,12 +1,15 @@
package FS::Misc;
use strict;
-use vars qw ( @ISA @EXPORT_OK );
+use vars qw ( @ISA @EXPORT_OK $DEBUG );
use Exporter;
+use Carp;
@ISA = qw( Exporter );
@EXPORT_OK = qw( send_email send_fax );
+$DEBUG = 1;
+
=head1 NAME
FS::Misc - Miscellaneous subroutines
@@ -37,11 +40,19 @@ I<to> - (required) comma-separated scalar or arrayref of recipients
I<subject> - (required)
-I<content-type> - (optional) MIME type
+I<content-type> - (optional) MIME type for the body
+
+I<body> - (required unless I<nobody> is true) arrayref of body text lines
+
+I<mimeparts> - (optional, but required if I<nobody> is true) arrayref of MIME::Entity->build PARAMHASH refs or MIME::Entity objects. These will be passed as arguments to MIME::Entity->attach().
+
+I<nobody> - (optional) when set true, send_email will ignore the I<body> option and simply construct a message with the given I<mimeparts>. In this case,
+I<content-type>, if specified, overrides the default "multipart/mixed" for the outermost MIME container.
-I<body> - (required) arrayref of body text lines
+I<content-encoding> - (optional) when using nobody, optional top-level MIME
+encoding which, if specified, overrides the default "7bit".
-I<mimeparts> - (optional) arrayref of MIME::Entity->build PARAMHASH refs, not MIME::Entity objects. These will be passed as arguments to MIME::Entity->attach().
+I<type> - (optional) type parameter for multipart/related messages
=cut
@@ -62,44 +73,93 @@ sub send_email {
$ENV{MAILADDRESS} = $options{'from'};
my $to = ref($options{to}) ? join(', ', @{ $options{to} } ) : $options{to};
- my @mimeparts = (ref($options{'mimeparts'}) eq 'ARRAY')
- ? @{$options{'mimeparts'}} : ();
- my $mimetype = (scalar(@mimeparts)) ? 'multipart/mixed' : 'text/plain';
+ my @mimeargs = ();
+ my @mimeparts = ();
+ if ( $options{'nobody'} ) {
+
+ croak "'mimeparts' option required when 'nobody' option given\n"
+ unless $options{'mimeparts'};
+
+ @mimeparts = @{$options{'mimeparts'}};
- my @mimeargs;
- if (scalar(@mimeparts)) {
@mimeargs = (
- 'Type' => 'multipart/mixed',
+ 'Type' => ( $options{'content-type'} || 'multipart/mixed' ),
+ 'Encoding' => ( $options{'content-encoding'} || '7bit' ),
);
- push @mimeparts,
- {
+ } else {
+
+ @mimeparts = @{$options{'mimeparts'}}
+ if ref($options{'mimeparts'}) eq 'ARRAY';
+
+ if (scalar(@mimeparts)) {
+
+ @mimeargs = (
+ 'Type' => 'multipart/mixed',
+ 'Encoding' => '7bit',
+ );
+
+ unshift @mimeparts, {
+ 'Type' => ( $options{'content-type'} || 'text/plain' ),
'Data' => $options{'body'},
+ 'Encoding' => ( $options{'content-type'} ? '-SUGGEST' : '7bit' ),
'Disposition' => 'inline',
- 'Type' => (($options{'content-type'} ne '')
- ? $options{'content-type'} : 'text/plain'),
};
- } else {
- @mimeargs = (
- 'Type' => (($options{'content-type'} ne '')
- ? $options{'content-type'} : 'text/plain'),
- 'Data' => $options{'body'},
- );
+
+ } else {
+
+ @mimeargs = (
+ 'Type' => ( $options{'content-type'} || 'text/plain' ),
+ 'Data' => $options{'body'},
+ 'Encoding' => ( $options{'content-type'} ? '-SUGGEST' : '7bit' ),
+ );
+
+ }
+
}
+ $options{'from'} =~ /\@([\w\.\-]+)/ or $1 = 'example.com';
+ my $message_id = join('.', rand()*(2**32), $$, time). "\@$1";
+
my $message = MIME::Entity->build(
- 'From' => $options{'from'},
- 'To' => $to,
- 'Sender' => $options{'from'},
- 'Reply-To' => $options{'from'},
- 'Date' => time2str("%a, %d %b %Y %X %z", time),
- 'Subject' => $options{'subject'},
+ 'From' => $options{'from'},
+ 'To' => $to,
+ 'Sender' => $options{'from'},
+ 'Reply-To' => $options{'from'},
+ 'Date' => time2str("%a, %d %b %Y %X %z", time),
+ 'Subject' => $options{'subject'},
+ 'Message-ID' => "<$message_id>",
@mimeargs,
);
+ if ( $options{'type'} ) {
+ #false laziness w/cust_bill::generate_email
+ $message->head->replace('Content-type',
+ $message->mime_type.
+ '; boundary="'. $message->head->multipart_boundary. '"'.
+ '; type='. $options{'type'}
+ );
+ }
+
foreach my $part (@mimeparts) {
- next unless ref($part) eq 'HASH'; #warn?
- $message->attach(%$part);
+
+ if ( UNIVERSAL::isa($part, 'MIME::Entity') ) {
+
+ warn "attaching MIME part from MIME::Entity object\n"
+ if $DEBUG;
+ $message->add_part($part);
+
+ } elsif ( ref($part) eq 'HASH' ) {
+
+ warn "attaching MIME part from hashref:\n".
+ join("\n", map " $_: ".$part->{$_}, keys %$part ). "\n"
+ if $DEBUG;
+ $message->attach(%$part);
+
+ } else {
+ croak "mimepart $part isn't a hashref or MIME::Entity object!";
+ }
+
}
my $smtpmachine = $conf->config('smtpmachine');
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
index fe5a653c0..f62f11268 100644
--- a/FS/FS/cust_bill.pm
+++ b/FS/FS/cust_bill.pm
@@ -343,57 +343,200 @@ Returns an argument list to be passed to L<FS::Misc::send_email>.
=cut
+use MIME::Entity;
+
sub generate_email {
my $self = shift;
my %args = @_;
- my $mimeparts;
- if ($conf->exists('invoice_email_pdf')) {
- #warn "[FS::cust_bill::send] creating PDF attachment";
- #mime parts arguments a la MIME::Entity->build().
- $mimeparts = [
- {
- 'Type' => 'application/pdf',
- 'Encoding' => 'base64',
- 'Data' => [ $self->print_pdf('', $args{'template'}) ],
- 'Disposition' => 'attachment',
- 'Filename' => 'invoice.pdf',
- },
- ];
- }
+ my $me = '[FS::cust_bill::generate_email]';
- my $email_text;
- if ($conf->exists('invoice_email_pdf')
- and scalar($conf->config('invoice_email_pdf_note'))) {
+ my %return = (
+ 'from' => $args{'from'},
+ 'subject' => (($args{'subject'}) ? $args{'subject'} : 'Invoice'),
+ );
- #warn "[FS::cust_bill::send] using 'invoice_email_pdf_note'";
- $email_text = [ map { $_ . "\n" } $conf->config('invoice_email_pdf_note') ];
+ if (ref($args{'to'} eq 'ARRAY')) {
+ $return{'to'} = $args{'to'};
} else {
- #warn "[FS::cust_bill::send] not using 'invoice_email_pdf_note'";
- if (ref($args{'print_text'}) eq 'ARRAY') {
- $email_text = $args{'print_text'};
+ $return{'to'} = [ grep { $_ !~ /^(POST|FAX)$/ }
+ $self->cust_main->invoicing_list
+ ];
+ }
+
+ if ( $conf->exists('invoice_html') ) {
+
+ warn "$me creating HTML/text multipart message"
+ if $DEBUG;
+
+ $return{'nobody'} = 1;
+
+ my $alternative = build MIME::Entity
+ 'Type' => 'multipart/alternative',
+ 'Encoding' => '7bit',
+ 'Disposition' => 'inline'
+ ;
+
+ my $data;
+ if ( $conf->exists('invoice_email_pdf')
+ and scalar($conf->config('invoice_email_pdf_note')) ) {
+
+ warn "$me using 'invoice_email_pdf_note' in multipart message"
+ if $DEBUG;
+ $data = [ map { $_ . "\n" }
+ $conf->config('invoice_email_pdf_note')
+ ];
+
} else {
- $email_text = [ $self->print_text('', $args{'template'}) ];
+
+ warn "$me not using 'invoice_email_pdf_note' in multipart message"
+ if $DEBUG;
+ if ( ref($args{'print_text'}) eq 'ARRAY' ) {
+ $data = $args{'print_text'};
+ } else {
+ $data = [ $self->print_text('', $args{'template'}) ];
+ }
+
}
- }
- my @invoicing_list;
- if (ref($args{'to'} eq 'ARRAY')) {
- @invoicing_list = @{$args{'to'}};
+ $alternative->attach(
+ 'Type' => 'text/plain',
+ #'Encoding' => 'quoted-printable',
+ 'Encoding' => '7bit',
+ 'Data' => $data,
+ 'Disposition' => 'inline',
+ );
+
+ $args{'from'} =~ /\@([\w\.\-]+)/ or $1 = 'example.com';
+ my $content_id = join('.', rand()*(2**32), $$, time). "\@$1";
+
+ my $image = build MIME::Entity
+ 'Type' => 'image/png',
+ 'Encoding' => 'base64',
+ 'Path' => "$FS::UID::conf_dir/conf.$FS::UID::datasrc/logo.png",
+ 'Filename' => 'logo.png',
+ 'Content-ID' => "<$content_id>",
+ ;
+
+ $alternative->attach(
+ 'Type' => 'text/html',
+ 'Encoding' => 'quoted-printable',
+ 'Data' => [ '<html>',
+ ' <head>',
+ ' <title>',
+ ' '. encode_entities($return{'subject'}),
+ ' </title>',
+ ' </head>',
+ ' <body bgcolor="#e8e8e8">',
+ $self->print_html('', $args{'template'}, $content_id),
+ ' </body>',
+ '</html>',
+ ],
+ 'Disposition' => 'inline',
+ #'Filename' => 'invoice.pdf',
+ );
+
+ if ( $conf->exists('invoice_email_pdf') ) {
+
+ #attaching pdf too:
+ # multipart/mixed
+ # multipart/related
+ # multipart/alternative
+ # text/plain
+ # text/html
+ # image/png
+ # application/pdf
+
+ my $related = build MIME::Entity 'Type' => 'multipart/related',
+ 'Encoding' => '7bit';
+
+ #false laziness w/Misc::send_email
+ $related->head->replace('Content-type',
+ $related->mime_type.
+ '; boundary="'. $related->head->multipart_boundary. '"'.
+ '; type=multipart/alternative'
+ );
+
+ $related->add_part($alternative);
+
+ $related->add_part($image);
+
+ my $pdf = build MIME::Entity $self->mimebuild_pdf('', $args{'template'});
+
+ $return{'mimeparts'} = [ $related, $pdf ];
+
+ } else {
+
+ #no other attachment:
+ # multipart/related
+ # multipart/alternative
+ # text/plain
+ # text/html
+ # image/png
+
+ $return{'content-type'} = 'multipart/related';
+ $return{'mimeparts'} = [ $alternative, $image ];
+ $return{'type'} = 'multipart/alternative'; #Content-Type of first part...
+ #$return{'disposition'} = 'inline';
+
+ }
+
} else {
- @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ } $self->cust_main->invoicing_list;
+
+ if ( $conf->exists('invoice_email_pdf') ) {
+ warn "$me creating PDF attachment"
+ if $DEBUG;
+
+ #mime parts arguments a la MIME::Entity->build().
+ $return{'mimeparts'} = [
+ { $self->mimebuild_pdf('', $args{'template'}) }
+ ];
+ }
+
+ if ( $conf->exists('invoice_email_pdf')
+ and scalar($conf->config('invoice_email_pdf_note')) ) {
+
+ warn "$me using 'invoice_email_pdf_note'"
+ if $DEBUG;
+ $return{'body'} = [ map { $_ . "\n" }
+ $conf->config('invoice_email_pdf_note')
+ ];
+
+ } else {
+
+ warn "$me not using 'invoice_email_pdf_note'"
+ if $DEBUG;
+ if ( ref($args{'print_text'}) eq 'ARRAY' ) {
+ $return{'body'} = $args{'print_text'};
+ } else {
+ $return{'body'} = [ $self->print_text('', $args{'template'}) ];
+ }
+
+ }
+
}
- return (
- 'from' => $args{'from'},
- 'to' => [ @invoicing_list ],
- 'subject' => (($args{'subject'}) ? $args{'subject'} : 'Invoice'),
- 'body' => $email_text,
- 'mimeparts' => $mimeparts,
- );
+ %return;
+
+}
+=item mimebuild_pdf
+Returns a list suitable for passing to MIME::Entity->build(), representing
+this invoice as PDF attachment.
+
+=cut
+
+sub mimebuild_pdf {
+ my $self = shift;
+ (
+ 'Type' => 'application/pdf',
+ 'Encoding' => 'base64',
+ 'Data' => [ $self->print_pdf(@_) ],
+ 'Disposition' => 'attachment',
+ 'Filename' => 'invoice.pdf',
+ );
}
=item send [ TEMPLATENAME [ , AGENTNUM [ , INVOICE_FROM ] ] ]
@@ -419,7 +562,7 @@ sub send {
? shift
: ( $self->_agent_invoice_from || $conf->config('invoice_from') );
- my @print_text = $self->print_text('', $template);
+ #my @print_text = $self->print_text('', $template);
my @invoicing_list = $self->cust_main->invoicing_list;
if ( grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list or !@invoicing_list ) {
@@ -432,7 +575,8 @@ sub send {
$self->generate_email(
'from' => $invoice_from,
'to' => [ grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ],
- 'print_text' => [ @print_text ],
+ #'print_text' => [ @print_text ],
+ 'template' => $template,
)
);
die "can't email invoice: $error\n" if $error;
@@ -445,7 +589,7 @@ sub send {
if ($conf->config('invoice_latex')) {
$lpr_data = [ $self->print_ps('', $template) ];
} else {
- $lpr_data = \@print_text;
+ $lpr_data = [ $self->print_text('', $template) ];
}
if ( grep { $_ eq 'POST' } @invoicing_list ) { #postal
@@ -1164,14 +1308,14 @@ sub print_latex {
$invoice_data{'country'} = _latex_escape(code2country($cust_main->country));
}
- #do variable substitutions in notes
- $invoice_data{'notes'} =
- join("\n",
- map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b }
- $conf->config_orbase('invoice_latexnotes', $template)
- );
- warn "invoice notes: ". $invoice_data{'notes'}. "\n"
- if $DEBUG;
+# #do variable substitutions in notes
+# $invoice_data{'notes'} =
+# join("\n",
+# map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b }
+# $conf->config_orbase('invoice_latexnotes', $template)
+# );
+# warn "invoice notes: ". $invoice_data{'notes'}. "\n"
+# if $DEBUG;
$invoice_data{'footer'} =~ s/\n+$//;
$invoice_data{'smallfooter'} =~ s/\n+$//;
@@ -1497,7 +1641,7 @@ sub print_pdf {
}
-=item print_html [ TIME [ , TEMPLATE ] ]
+=item print_html [ TIME [ , TEMPLATE [ , CID ] ] ]
Returns an HTML invoice, as a scalar.
@@ -1506,76 +1650,13 @@ default is now. It isn't the date of the invoice; that's the `_date' field.
It is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
L<Time::Local> and L<Date::Parse> for conversion functions.
-=cut
+CID is a MIME Content-ID used to create a "cid:" URL for the logo image, used
+when emailing the invoice as part of a multipart/related MIME email.
-#sub print_html {
-# my $self = shift;
-#
-# my $file = $self->print_latex(@_);
-#
-# my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
-# chdir($dir);
-#
-# my $sfile = shell_quote $file;
-#
-# system("htlatex $sfile.tex") == 0
-# or die "hlatex $file.tex failed; is hlatex installed, or see $file.log for details?\n";
-# #system("ltoh $sfile.tex") == 0
-# # or die "ltoh $file.tex failed; is hlatex installed, or see $file.log for details?\n";
-#
-# open(HTML, "<$file.html")
-# or die "can't open $file.html: $! (error in LaTeX template?)\n";
-#
-# #unlink("$file.dvi", "$file.log", "$file.aux", "$file.html", "$file.tex");
-#
-# my $html = '';
-# while (<HTML>) {
-#
-# s/<link\s+rel="stylesheet"\s+type="text\/css"\s+href="invoice\.(\d+)\.(\w+)\.css">/<link rel="stylesheet" type="text\/css" href="cust_bill.html?$1.$2.css">/;
-## s/<link\s+//;
-# $html .= $_;
-# }
-#
-# close HTML;
-#
-# return $html;
-#
-#}
-#
-##inefficient proof-of-concept for now
-#sub print_html_css {
-# my $self = shift;
-#
-# my $file = $self->print_latex(@_);
-#
-# my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
-# chdir($dir);
-#
-# my $sfile = shell_quote $file;
-#
-# system("htlatex $sfile.tex") == 0
-# or die "hlatex $file.tex failed; is hlatex installed, or see $file.log for details?\n";
-# #system("ltoh $sfile.tex") == 0
-# # or die "ltoh $file.tex failed; is hlatex installed, or see $file.log for details?\n";
-#
-# open(CSS, "<$file.css")
-# or die "can't open $file.html: $! (error in LaTeX template?)\n";
-#
-# unlink("$file.dvi", "$file.log", "$file.aux", "$file.html", "$file.tex");
-#
-# my $css = '';
-# while (<CSS>) {
-# $css .= $_;
-# }
-#
-# close CSS;
-#
-# return $css;
-#
-#}
+=cut
sub print_html {
- my( $self, $today, $template ) = @_;
+ my( $self, $today, $template, $cid ) = @_;
$today ||= time;
my $cust_main = $self->cust_main;
@@ -1598,17 +1679,10 @@ sub print_html {
$html_template->compile()
or die 'While compiling ' . $templatefile . ': ' . $Text::Template::ERROR;
- my $returnaddress = $conf->exists('invoice_htmlreturnaddress')
- ? join("\n", $conf->config('invoice_htmlreturnaddress') )
- : join("\n", map { s/~/&nbsp;/g; s/\\\\\*?\s*$/<BR>/; }
- $conf->config('invoice_latexreturnaddress')
- );
- warn $conf->config('invoice_latexreturnaddress');
- warn $returnaddress;
-
my %invoice_data = (
'invnum' => $self->invnum,
'date' => time2str('%b&nbsp;%o,&nbsp;%Y', $self->_date),
+ 'today' => time2str('%b %o, %Y', $today),
'agent' => encode_entities($cust_main->agent->agent),
'payname' => encode_entities($cust_main->payname),
'company' => encode_entities($cust_main->company),
@@ -1617,15 +1691,18 @@ sub print_html {
'city' => encode_entities($cust_main->city),
'state' => encode_entities($cust_main->state),
'zip' => encode_entities($cust_main->zip),
-# 'footer' => join("\n", $conf->config('invoice_latexfooter') ),
-# 'smallfooter' => join("\n", $conf->config('invoice_latexsmallfooter') ),
- 'returnaddress' => $returnaddress,
'terms' => $conf->config('invoice_default_terms')
|| 'Payable upon receipt',
- #'notes' => join("\n", $conf->config('invoice_latexnotes') ),
+ 'cid' => $cid,
# 'conf_dir' => "$FS::UID::conf_dir/conf.$FS::UID::datasrc",
);
+ $invoice_data{'returnaddress'} = $conf->exists('invoice_htmlreturnaddress')
+ ? join("\n", $conf->config('invoice_htmlreturnaddress') )
+ : join("\n", map { s/~/&nbsp;/g; s/\\\\\*?\s*$/<BR>/; $_; }
+ $conf->config('invoice_latexreturnaddress')
+ );
+
my $countrydefault = $conf->config('countrydefault') || 'US';
if ( $cust_main->country eq $countrydefault ) {
$invoice_data{'country'} = '';
@@ -1634,8 +1711,20 @@ sub print_html {
encode_entities(code2country($cust_main->country));
}
- my $countrydefault = $conf->config('countrydefault') || 'US';
- $invoice_data{'country'} = '' if $invoice_data{'country'} eq $countrydefault;
+ $invoice_data{'notes'} =
+ length($conf->config_orbase('invoice_htmlnotes', $template))
+ ? join("\n", $conf->config_orbase('invoice_htmlnotes', $template) )
+ : join("\n", map {
+ s/%%(.*)$/<!-- $1 -->/;
+ s/\\section\*\{\\textsc\{(.)(.*)\}\}/<p><b><font size="+1">$1<\/font>\U$2<\/b>/;
+ s/\\begin\{enumerate\}/<ol>/;
+ s/\\item / <li>/;
+ s/\\end\{enumerate\}/<\/ol>/;
+ s/\\textbf\{(.*)\}/<b>$1<\/b>/;
+ $_;
+ }
+ $conf->config_orbase('invoice_latexnotes', $template)
+ );
# #do variable substitutions in notes
# $invoice_data{'notes'} =
@@ -1643,11 +1732,13 @@ sub print_html {
# map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b }
# $conf->config_orbase('invoice_latexnotes', $suffix)
# );
-#
-# $invoice_data{'footer'} =~ s/\n+$//;
-# $invoice_data{'smallfooter'} =~ s/\n+$//;
-# $invoice_data{'notes'} =~ s/\n+$//;
-#
+
+ $invoice_data{'footer'} = $conf->exists('invoice_htmlfooter')
+ ? join("\n", $conf->config('invoice_htmlfooter') )
+ : join("\n", map { s/~/&nbsp;/g; s/\\\\\*?\s*$/<BR>/; $_; }
+ $conf->config('invoice_latexfooter')
+ );
+
$invoice_data{'po_line'} =
( $cust_main->payby eq 'BILL' && $cust_main->payinfo )
? encode_entities("Purchase Order #". $cust_main->payinfo)
diff --git a/FS/FS/part_bill_event.pm b/FS/FS/part_bill_event.pm
index 7dfea5066..b7c8b6a2d 100644
--- a/FS/FS/part_bill_event.pm
+++ b/FS/FS/part_bill_event.pm
@@ -158,7 +158,7 @@ sub check {
if ( $self->plandata =~ /^(agent_)?templatename\s+(.*)$/m ) {
my $name= $2;
- foreach my $file (qw( template latex latexnotes )) {
+ foreach my $file (qw( template latex latexnotes html htmlnotes )) {
unless ( $conf->exists("invoice_${file}_$name") ) {
$conf->set(
"invoice_${file}_$name" =>