diff options
author | Mark Wells <mark@freeside.biz> | 2013-09-27 17:19:36 -0700 |
---|---|---|
committer | Mark Wells <mark@freeside.biz> | 2013-09-27 17:19:36 -0700 |
commit | eb3bd392a89b8b666dc512951e78913c05b98810 (patch) | |
tree | 22670bdf99b0a473abb708f6f5c55cbfa2f0c249 | |
parent | e3012c0751dad6710ea35b6d074b551bffdad09b (diff) |
invoice configurations, #24723
36 files changed, 1284 insertions, 268 deletions
diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm index 780e3ff..f1fc5cb 100644 --- a/FS/FS/Mason.pm +++ b/FS/FS/Mason.pm @@ -353,6 +353,8 @@ if ( -e $addl_handler_use_file ) { use FS::sales_pkg_class; use FS::svc_alarm; use FS::cable_model; + use FS::invoice_mode; + use FS::invoice_conf; # Sammath Naur if ( $FS::Mason::addl_handler_use ) { diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index ed37904..16546b3 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -4377,6 +4377,51 @@ sub tables_hashref { 'index' => [ [ 'derivenum', ], ], }, + 'invoice_mode' => { + 'columns' => [ + 'modenum', 'serial', '', '', '', '', + 'agentnum', 'int', 'NULL', '', '', '', + 'modename', 'varchar', '', 32, '', '', + ], + 'primary_key' => 'modenum', + 'unique' => [ ], + 'index' => [ ], + }, + + 'invoice_conf' => { + 'columns' => [ + 'confnum', 'serial', '', '', '', '', + 'modenum', 'int', '', '', '', '', + 'locale', 'varchar', 'NULL', 16, '', '', + 'notice_name', 'varchar', 'NULL', 64, '', '', + 'subject', 'varchar', 'NULL', 64, '', '', + 'htmlnotes', 'text', 'NULL', '', '', '', + 'htmlfooter', 'text', 'NULL', '', '', '', + 'htmlsummary', 'text', 'NULL', '', '', '', + 'htmlreturnaddress', 'text', 'NULL', '', '', '', + 'latexnotes', 'text', 'NULL', '', '', '', + 'latexfooter', 'text', 'NULL', '', '', '', + 'latexsummary', 'text', 'NULL', '', '', '', + 'latexcoupon', 'text', 'NULL', '', '', '', + 'latexsmallfooter', 'text', 'NULL', '', '', '', + 'latexreturnaddress', 'text', 'NULL', '', '', '', + 'latextopmargin', 'varchar', 'NULL', 16, '', '', + 'latexheadsep', 'varchar', 'NULL', 16, '', '', + 'latexaddresssep', 'varchar', 'NULL', 16, '', '', + 'latextextheight', 'varchar', 'NULL', 16, '', '', + 'latexextracouponspace','varchar', 'NULL', 16, '', '', + 'latexcouponfootsep', 'varchar', 'NULL', 16, '', '', + 'latexcouponamountenclosedsep', 'varchar', 'NULL', 16, '', '', + 'latexcoupontoaddresssep', 'varchar', 'NULL', 16, '', '', + 'latexverticalreturnaddress', 'char', 'NULL', 1, '', '', + 'latexcouponaddcompanytoaddress', 'char', 'NULL', 1, '', '', + 'logo_png', 'blob', 'NULL', '', '', '', + 'logo_eps', 'blob', 'NULL', '', '', '', + 'lpr', 'varchar', 'NULL', $char_d, '', '', + ], + 'primary_key' => 'confnum', + 'unique' => [ [ 'modenum', 'locale' ] ], + }, # name type nullability length default local diff --git a/FS/FS/Template_Mixin.pm b/FS/FS/Template_Mixin.pm index 356de5b..840df75 100644 --- a/FS/FS/Template_Mixin.pm +++ b/FS/FS/Template_Mixin.pm @@ -18,6 +18,7 @@ use FS::Record qw( qsearch qsearchs ); use FS::Misc qw( generate_ps generate_pdf ); use FS::pkg_category; use FS::pkg_class; +use FS::invoice_mode; use FS::L10N; $DEBUG = 0; @@ -30,12 +31,51 @@ FS::UID->install_callback( sub { $date_format_long = $conf->config('date_format_long') || '%b %o, %Y'; } ); -=item print_text HASHREF | [ TIME [ , TEMPLATE [ , OPTION => VALUE ... ] ] ] +=item conf [ MODE ] + +Returns a configuration handle (L<FS::Conf>) set to the customer's locale. + +If the "mode" pseudo-field is set on the object, the configuration handle +will be an L<FS::invoice_conf> for that invoice mode (and the customer's +locale). + +=cut + +sub conf { + my $self = shift; + my $mode = $self->get('mode'); + if ($self->{_conf} and !defined($mode)) { + return $self->{_conf}; + } + + my $cust_main = $self->cust_main; + my $locale = $cust_main ? $cust_main->locale : ''; + my $conf; + if ( $mode ) { + if ( ref $mode and $mode->isa('FS::invoice_mode') ) { + $mode = $mode->modenum; + } elsif ( $mode =~ /\D/ ) { + die "invalid invoice mode $mode"; + } + $conf = qsearchs('invoice_conf', { modenum => $mode, locale => $locale }); + if (!$conf) { + $conf = qsearchs('invoice_conf', { modenum => $mode, locale => '' }); + # it doesn't have a locale, but system conf still might + $conf->set('locale' => $locale) if $conf; + } + } + # if $mode is unspecified, or if there is no invoice_conf matching this mode + # and locale, then use the system config only (but with the locale) + $conf ||= FS::Conf->new({ 'locale' => $locale }); + # cache it + return $self->{_conf} = $conf; +} + +=item print_text OPTIONS Returns an text invoice, as a list of lines. -Options can be passed as a hashref (recommended) or as a list of time, template -and then any key/value pairs for any other options. +Options can be passed as a hash. I<time>, if specified, is used to control the printing of overdue messages. The default is now. It isn't the date of the invoice; that's the `_date' field. @@ -50,25 +90,19 @@ I<notice_name>, if specified, overrides "Invoice" as the name of the sent docume sub print_text { my $self = shift; - my( $today, $template, %opt ); + my %params; if ( ref($_[0]) ) { - %opt = %{ shift() }; - $today = delete($opt{'time'}) || ''; - $template = delete($opt{template}) || ''; + %params = %{ shift() }; } else { - ( $today, $template, %opt ) = @_; + %params = @_; } - my %params = ( 'format' => 'template' ); - $params{'time'} = $today if $today; - $params{'template'} = $template if $template; - $params{$_} = $opt{$_} - foreach grep $opt{$_}, qw( unsquelch_cdr notice_name ); + $params{'format'} = 'template'; # for some reason $self->print_generic( %params ); } -=item print_latex HASHREF | [ TIME [ , TEMPLATE [ , OPTION => VALUE ... ] ] ] +=item print_latex HASHREF Internal method - returns a filename of a filled-in LaTeX template for this invoice (Note: add ".tex" to get the actual filename), and a filename of @@ -76,15 +110,16 @@ an associated logo (with the .eps extension included). See print_ps and print_pdf for methods that return PostScript and PDF output. -Options can be passed as a hashref (recommended) or as a list of time, template -and then any key/value pairs for any other options. +Options can be passed as a hash. I<time>, if specified, is used to control the printing of overdue messages. The 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. -I<template>, if specified, is the name of a suffix for alternate invoices. +I<template>, if specified, is the name of a suffix for alternate invoices. +This is strongly deprecated; see L<FS::invoice_conf> for the right way to +customize invoice templates for different purposes. I<notice_name>, if specified, overrides "Invoice" as the name of the sent document (templates from 10/2009 or newer required) @@ -92,22 +127,20 @@ I<notice_name>, if specified, overrides "Invoice" as the name of the sent docume sub print_latex { my $self = shift; - my $conf = $self->conf; - my( $today, $template, %opt ); + my %params; + if ( ref($_[0]) ) { - %opt = %{ shift() }; - $today = delete($opt{'time'}) || ''; - $template = delete($opt{template}) || ''; + %params = %{ shift() }; } else { - ( $today, $template, %opt ) = @_; + %params = @_; } - my %params = ( 'format' => 'latex' ); - $params{'time'} = $today if $today; - $params{'template'} = $template if $template; - $params{$_} = $opt{$_} - foreach grep $opt{$_}, qw( unsquelch_cdr notice_name no_date no_number ); + $params{'format'} = 'latex'; + my $conf = $self->conf; + # this needs to go away + my $template = $params{'template'}; + # and this especially $template ||= $self->_agent_template if $self->can('_agent_template'); @@ -191,7 +224,8 @@ Non optional options include Optional options include -template - a value used as a suffix for a configuration template +template - a value used as a suffix for a configuration template. Please +don't use this. time - a value used to control the printing of overdue messages. The default is now. It isn't the date of the invoice; that's the `_date' field. @@ -214,6 +248,7 @@ locale - override customer's locale sub print_generic { my( $self, %params ) = @_; my $conf = $self->conf; + my $today = $params{today} ? $params{today} : time; warn "$me print_generic called on $self with suffix $params{template}\n" if $DEBUG; @@ -227,6 +262,8 @@ sub print_generic { unless $cust_main->payname && $cust_main->payby !~ /^(CARD|DCRD|CHEK|DCHK)$/; + my $locale = $params{'locale'} || $cust_main->locale; + my %delimiters = ( 'latex' => [ '[@--', '--@]' ], 'html' => [ '<%=', '%>' ], 'template' => [ '{', '}' ], @@ -235,11 +272,18 @@ sub print_generic { warn "$me print_generic creating template\n" if $DEBUG > 1; + # set the notice name here, and nowhere else. + my $notice_name = $params{notice_name} + || $conf->config('notice_name') + || $self->notice_name; + #create the template my $template = $params{template} ? $params{template} : $self->_agent_template; my $templatefile = $self->template_conf. $format; $templatefile .= "_$template" if length($template) && $conf->exists($templatefile."_$template"); + + # the base template my @invoice_template = map "$_\n", $conf->config($templatefile) or die "cannot load config data $templatefile"; @@ -380,6 +424,7 @@ sub print_generic { # generate template variables my $returnaddress; + if ( defined( $conf->config_orbase( "invoice_${format}returnaddress", $template @@ -457,7 +502,7 @@ sub print_generic { 'today' => time2str($date_format_long, $today), 'terms' => $self->terms, 'template' => $template, #params{'template'}, - 'notice_name' => ($params{'notice_name'} || $self->notice_name),#escape_function? + 'notice_name' => $notice_name, # escape? 'current_charges' => sprintf("%.2f", $self->charged), 'duedate' => $self->due_date2str($rdate_format), #date_format? @@ -499,7 +544,7 @@ sub print_generic { ); #localization - my $lh = FS::L10N->get_handle( $params{'locale'} || $cust_main->locale ); + my $lh = FS::L10N->get_handle( $locale ); $invoice_data{'emt'} = sub { &$escape_function($self->mt(@_)) }; my %info = FS::Locales->locale_info($cust_main->locale || 'en_US'); # eval to avoid death for unimplemented languages @@ -942,9 +987,6 @@ sub print_generic { foreach my $section (@sections, @$late_sections) { - warn "$me adding section \n". Dumper($section) - if $DEBUG > 1; - # begin some normalization $section->{'subtotal'} = $section->{'amount'} if $multisection @@ -1544,12 +1586,9 @@ sub print_html { my %params; if ( ref($_[0]) ) { %params = %{ shift() }; - }else{ - $params{'time'} = shift; - $params{'template'} = shift; - $params{'cid'} = shift; + } else { + %params = @_; } - $params{'format'} = 'html'; $self->print_generic( %params ); diff --git a/FS/FS/agent.pm b/FS/FS/agent.pm index 0cd07ef..d70ff18 100644 --- a/FS/FS/agent.pm +++ b/FS/FS/agent.pm @@ -378,6 +378,23 @@ sub payment_gateway { $payment_gateway; } +=item invoice_modes + +Returns all L<FS::invoice_mode> objects that are valid for this agent (i.e. +those with this agentnum or null agentnum). + +=cut + +sub invoice_modes { + my $self = shift; + qsearch( { + table => 'invoice_mode', + hashref => { agentnum => $self->agentnum }, + extra_sql => ' OR agentnum IS NULL', + order_by => ' ORDER BY modename', + } ); +} + =item num_prospect_cust_main Returns the number of prospects (customers with no packages ever ordered) for diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index 97dd38b..a747a78 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -80,7 +80,7 @@ FS::cust_bill - Object methods for cust_bill records $tax_amount = $record->tax; @lines = $cust_bill->print_text; - @lines = $cust_bill->print_text $time; + @lines = $cust_bill->print_text('time' => $time); =head1 DESCRIPTION @@ -153,7 +153,13 @@ Invoices are normally created by calling the bill method of a customer object =cut sub table { 'cust_bill'; } -sub notice_name { 'Invoice'; } + +# should be the ONLY occurrence of "Invoice" in invoice rendering code. +# (except email_subject and invnum_date_pretty) +sub notice_name { + my $self = shift; + $self->conf->config('notice_name') || 'Invoice' +} sub cust_linked { $_[0]->cust_main_custnum; } sub cust_unlinked_msg { @@ -1043,7 +1049,7 @@ Options: sender address, required -=item tempate +=item template alternate template name, optional @@ -1077,15 +1083,10 @@ sub generate_email { my %return = ( 'from' => $args{'from'}, - 'subject' => (($args{'subject'}) ? $args{'subject'} : 'Invoice'), + 'subject' => ($args{'subject'} || $self->email_subject), ); - my %opt = ( - 'unsquelch_cdr' => $conf->exists('voip-cdr_email'), - 'template' => $args{'template'}, - 'notice_name' => ( $args{'notice_name'} || 'Invoice' ), - 'no_coupon' => $args{'no_coupon'}, - ); + $args{'unsquelch_cdr'} = $conf->exists('voip-cdr_email'); my $cust_main = $self->cust_main; @@ -1127,7 +1128,7 @@ sub generate_email { if ( ref($args{'print_text'}) eq 'ARRAY' ) { $data = $args{'print_text'}; } else { - $data = [ $self->print_text(\%opt) ]; + $data = [ $self->print_text(\%args) ]; } } @@ -1184,10 +1185,10 @@ sub generate_email { 'Filename' => 'barcode.png', 'Content-ID' => "<$barcode_content_id>", ; - $opt{'barcode_cid'} = $barcode_content_id; + $args{'barcode_cid'} = $barcode_content_id; } - $htmldata = $self->print_html({ 'cid'=>$content_id, %opt }); + $htmldata = $self->print_html({ 'cid'=>$content_id, %args }); } $alternative->attach( @@ -1249,7 +1250,7 @@ sub generate_email { $related->add_part($image) if $image; - my $pdf = build MIME::Entity $self->mimebuild_pdf(\%opt); + my $pdf = build MIME::Entity $self->mimebuild_pdf(\%args); $return{'mimeparts'} = [ $related, $pdf, @otherparts ]; @@ -1281,7 +1282,7 @@ sub generate_email { #mime parts arguments a la MIME::Entity->build(). $return{'mimeparts'} = [ - { $self->mimebuild_pdf(\%opt) } + { $self->mimebuild_pdf(\%args) } ]; } @@ -1301,7 +1302,7 @@ sub generate_email { if ( ref($args{'print_text'}) eq 'ARRAY' ) { $return{'body'} = $args{'print_text'}; } else { - $return{'body'} = [ $self->print_text(\%opt) ]; + $return{'body'} = [ $self->print_text(\%args) ]; } } @@ -1330,105 +1331,48 @@ sub mimebuild_pdf { ); } -=item send HASHREF | [ TEMPLATE [ , AGENTNUM [ , INVOICE_FROM [ , AMOUNT ] ] ] ] +=item send HASHREF Sends this invoice to the destinations configured for this customer: sends email, prints and/or faxes. See L<FS::cust_main_invoice>. -Options can be passed as a hashref (recommended) or as a list of up to -four values for templatename, agentnum, invoice_from and amount. +Options can be passed as a hashref. Positional parameters are no longer +allowed. -I<template>, if specified, is the name of a suffix for alternate invoices. +I<template>: a suffix for alternate invoices -I<agentnum>, if specified, means that this invoice will only be sent for customers -of the specified agent or agent(s). AGENTNUM can be a scalar agentnum (for a -single agent) or an arrayref of agentnums. +I<agentnum>: obsolete, now does nothing. -I<invoice_from>, if specified, overrides the default email invoice From: address. +I<invoice_from> overrides the default email invoice From: address. -I<amount>, if specified, only sends the invoice if the total amount owed on this -invoice and all older invoices is greater than the specified amount. +I<amount>: obsolete, does nothing -I<notice_name>, if specified, overrides "Invoice" as the name of the sent document (templates from 10/2009 or newer required) +I<notice_name> overrides "Invoice" as the name of the sent document +(templates from 10/2009 or newer required). -I<lpr>, if specified, is passed to +I<lpr> overrides the system 'lpr' option as the command to print a document +from standard input. =cut -sub queueable_send { - my %opt = @_; - - my $self = qsearchs('cust_bill', { 'invnum' => $opt{invnum} } ) - or die "invalid invoice number: " . $opt{invnum}; - - my @args = ( $opt{template}, $opt{agentnum} ); - push @args, $opt{invoice_from} - if exists($opt{invoice_from}) && $opt{invoice_from}; - - my $error = $self->send( @args ); - die $error if $error; - -} - sub send { my $self = shift; + my $opt = ref($_[0]) ? $_[0] : +{ @_ }; my $conf = $self->conf; - my( $template, $invoice_from, $notice_name ); - my $agentnums = ''; - my $balance_over = 0; - my $lpr = ''; - - if ( ref($_[0]) ) { - my $opt = shift; - $template = $opt->{'template'} || ''; - if ( $agentnums = $opt->{'agentnum'} ) { - $agentnums = [ $agentnums ] unless ref($agentnums); - } - $invoice_from = $opt->{'invoice_from'}; - $balance_over = $opt->{'balance_over'} if $opt->{'balance_over'}; - $notice_name = $opt->{'notice_name'}; - $lpr = $opt->{'lpr'} - } else { - $template = scalar(@_) ? shift : ''; - if ( scalar(@_) && $_[0] ) { - $agentnums = ref($_[0]) ? shift : [ shift ]; - } - $invoice_from = shift if scalar(@_); - $balance_over = shift if scalar(@_) && $_[0] !~ /^\s*$/; - } - my $cust_main = $self->cust_main; - return 'N/A' unless ! $agentnums - or grep { $_ == $cust_main->agentnum } @$agentnums; - - return '' - unless $cust_main->total_owed_date($self->_date) > $balance_over; - - $invoice_from ||= $self->_agent_invoice_from || #XXX should go away - $conf->config('invoice_from', $cust_main->agentnum ); - - my %opt = ( - 'template' => $template, - 'invoice_from' => $invoice_from, - 'notice_name' => ( $notice_name || 'Invoice' ), - ); - my @invoicing_list = $cust_main->invoicing_list; - #$self->email_invoice(\%opt) - $self->email(\%opt) + $self->email($opt) if ( grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list or !@invoicing_list ) && ! $self->invoice_noemail; - $opt{'lpr'} = $lpr; - #$self->print_invoice(\%opt) - $self->print(\%opt) + $self->print($opt) if grep { $_ eq 'POST' } @invoicing_list; #postal #this has never been used post-$ORIGINAL_ISP afaik - $self->fax_invoice(\%opt) + $self->fax_invoice($opt) if grep { $_ eq 'FAX' } @invoicing_list; #fax ''; @@ -1437,16 +1381,17 @@ sub send { =item email HASHREF | [ TEMPLATE [ , INVOICE_FROM ] ] -Emails this invoice. +Sends this invoice to the customer's email destination(s). -Options can be passed as a hashref (recommended) or as a list of up to -two values for templatename and invoice_from. +Options must be passed as a hashref. Positional parameters are no longer +allowed. I<template>, if specified, is the name of a suffix for alternate invoices. -I<invoice_from>, if specified, overrides the default email invoice From: address. +I<invoice_from>, if specified, overrides the default email invoice From: +address. -I<notice_name>, if specified, overrides "Invoice" as the name of the sent document (templates from 10/2009 or newer required) +I<notice_name> is the name of the sent document. =cut @@ -1456,38 +1401,30 @@ sub queueable_email { my $self = qsearchs('cust_bill', { 'invnum' => $opt{invnum} } ) or die "invalid invoice number: " . $opt{invnum}; - my %args = ( 'template' => $opt{template} ); - $args{$_} = $opt{$_} - foreach grep { exists($opt{$_}) && $opt{$_} } - qw( invoice_from notice_name no_coupon ); + my %args = map {$_ => $opt{$_}} + grep { $opt{$_} } + qw( invoice_from notice_name no_coupon template ); my $error = $self->email( \%args ); die $error if $error; } -#sub email_invoice { sub email { my $self = shift; return if $self->hide; my $conf = $self->conf; - - my( $template, $invoice_from, $notice_name, $no_coupon ); - if ( ref($_[0]) ) { - my $opt = shift; - $template = $opt->{'template'} || ''; - $invoice_from = $opt->{'invoice_from'}; - $notice_name = $opt->{'notice_name'} || 'Invoice'; - $no_coupon = $opt->{'no_coupon'} || 0; - } else { - $template = scalar(@_) ? shift : ''; - $invoice_from = shift if scalar(@_); - $notice_name = 'Invoice'; - $no_coupon = 0; + my $opt = shift; + if ($opt and !ref($opt)) { + die "FS::cust_bill::email called with positional parameters"; } - $invoice_from ||= $self->_agent_invoice_from || #XXX should go away - $conf->config('invoice_from', $self->cust_main->agentnum ); + my $template = $opt->{template}; + my $from = delete $opt->{invoice_from}; + + # this is where we set the From: address + $from ||= $self->_agent_invoice_from || #XXX should go away + $conf->config('invoice_from', $self->cust_main->agentnum ); my @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ } $self->cust_main->invoicing_list; @@ -1497,20 +1434,19 @@ sub email { die 'No recipients for customer #'. $self->custnum; } else { #default: better to notify this person than silence - @invoicing_list = ($invoice_from); + @invoicing_list = ($from); } } + # this is where we set the Subject: my $subject = $self->email_subject($template); my $error = send_email( $self->generate_email( - 'from' => $invoice_from, + 'from' => $from, 'to' => [ grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ], 'subject' => $subject, - 'template' => $template, - 'notice_name' => $notice_name, - 'no_coupon' => $no_coupon, + %$opt, # template, etc. ) ); die "can't email invoice: $error\n" if $error; @@ -1537,12 +1473,12 @@ sub email_subject { eval qq("$subject"); } -=item lpr_data HASHREF | [ TEMPLATE ] +=item lpr_data HASHREF Returns the postscript or plaintext for this invoice as an arrayref. -Options can be passed as a hashref (recommended) or as a single optional value -for template. +Options must be passed as a hashref. Positional parameters are no longer +allowed. I<template>, if specified, is the name of a suffix for alternate invoices. @@ -1553,31 +1489,21 @@ I<notice_name>, if specified, overrides "Invoice" as the name of the sent docume sub lpr_data { my $self = shift; my $conf = $self->conf; - my( $template, $notice_name ); - if ( ref($_[0]) ) { - my $opt = shift; - $template = $opt->{'template'} || ''; - $notice_name = $opt->{'notice_name'} || 'Invoice'; - } else { - $template = scalar(@_) ? shift : ''; - $notice_name = 'Invoice'; + my $opt = shift; + if ($opt and !ref($opt)) { + # nobody does this anyway + die "FS::cust_bill::lpr_data called with positional parameters"; } - my %opt = ( - 'template' => $template, - 'notice_name' => $notice_name, - ); - my $method = $conf->exists('invoice_latex') ? 'print_ps' : 'print_text'; - [ $self->$method( \%opt ) ]; + [ $self->$method( $opt ) ]; } -=item print HASHREF | [ TEMPLATE ] +=item print HASHREF Prints this invoice. -Options can be passed as a hashref (recommended) or as a single optional -value for template. +Options must be passed as a hashref. I<template>, if specified, is the name of a suffix for alternate invoices. @@ -1585,48 +1511,34 @@ I<notice_name>, if specified, overrides "Invoice" as the name of the sent docume =cut -#sub print_invoice { sub print { my $self = shift; return if $self->hide; my $conf = $self->conf; - - my( $template, $notice_name, $lpr ); - if ( ref($_[0]) ) { - my $opt = shift; - $template = $opt->{'template'} || ''; - $notice_name = $opt->{'notice_name'} || 'Invoice'; - $lpr = $opt->{'lpr'} - } else { - $template = scalar(@_) ? shift : ''; - $notice_name = 'Invoice'; - $lpr = ''; + my $opt = shift; + if ($opt and !ref($opt)) { + die "FS::cust_bill::print called with positional parameters"; } - my %opt = ( - 'template' => $template, - 'notice_name' => $notice_name, - ); - + my $lpr = delete $opt->{lpr}; if($conf->exists('invoice_print_pdf')) { # Add the invoice to the current batch. - $self->batch_invoice(\%opt); + $self->batch_invoice($opt); } else { do_print( - $self->lpr_data(\%opt), + $self->lpr_data($opt), 'agentnum' => $self->cust_main->agentnum, 'lpr' => $lpr, ); } } -=item fax_invoice HASHREF | [ TEMPLATE ] +=item fax_invoice HASHREF Faxes this invoice. -Options can be passed as a hashref (recommended) or as a single optional -value for template. +Options must be passed as a hashref. I<template>, if specified, is the name of a suffix for alternate invoices. @@ -1638,15 +1550,9 @@ sub fax_invoice { my $self = shift; return if $self->hide; my $conf = $self->conf; - - my( $template, $notice_name ); - if ( ref($_[0]) ) { - my $opt = shift; - $template = $opt->{'template'} || ''; - $notice_name = $opt->{'notice_name'} || 'Invoice'; - } else { - $template = scalar(@_) ? shift : ''; - $notice_name = 'Invoice'; + my $opt = shift; + if ($opt and !ref($opt)) { + die "FS::cust_bill::fax_invoice called with positional parameters"; } die 'FAX invoice destination not (yet?) supported with plain text invoices.' @@ -1655,12 +1561,7 @@ sub fax_invoice { my $dialstring = $self->cust_main->getfield('fax'); #Check $dialstring? - my %opt = ( - 'template' => $template, - 'notice_name' => $notice_name, - ); - - my $error = send_fax( 'docdata' => $self->lpr_data(\%opt), + my $error = send_fax( 'docdata' => $self->lpr_data($opt), 'dialstring' => $dialstring, ); die $error if $error; @@ -1749,29 +1650,6 @@ sub spool_invoice { ); } -=item send_if_newest [ TEMPLATENAME [ , AGENTNUM [ , INVOICE_FROM ] ] ] - -Like B<send>, but only sends the invoice if it is the newest open invoice for -this customer. - -=cut - -sub send_if_newest { - my $self = shift; - - return '' - if scalar( - grep { $_->owed > 0 } - qsearch('cust_bill', { - 'custnum' => $self->custnum, - #'_date' => { op=>'>', value=>$self->_date }, - 'invnum' => { op=>'>', value=>$self->invnum }, - } ) - ); - - $self->send(@_); -} - =item send_csv OPTION => VALUE, ... Sends invoice as a CSV data-file to a remote host with the specified protocol. diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 0c50b84..13b0150 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -4895,9 +4895,9 @@ sub queueable_print { my %opt = @_; my $self = qsearchs('cust_main', { 'custnum' => $opt{custnum} } ) - or die "invalid customer number: " . $opt{custvnum}; + or die "invalid customer number: " . $opt{custnum}; - my $error = $self->print( $opt{template} ); + my $error = $self->print( { 'template' => $opt{template} } ); die $error if $error; } diff --git a/FS/FS/invoice_conf.pm b/FS/FS/invoice_conf.pm new file mode 100644 index 0000000..043cab0 --- /dev/null +++ b/FS/FS/invoice_conf.pm @@ -0,0 +1,274 @@ +package FS::invoice_conf; + +use strict; +use base qw( FS::Record FS::Conf ); +use FS::Record qw( qsearch qsearchs ); + +=head1 NAME + +FS::invoice_conf - Object methods for invoice_conf records + +=head1 SYNOPSIS + + use FS::invoice_conf; + + $record = new FS::invoice_conf \%hash; + $record = new FS::invoice_conf { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::invoice_conf object represents a set of localized invoice +configuration values. FS::invoice_conf inherits from FS::Record and FS::Conf, +and supports the FS::Conf interface. The following fields are supported: + +=over 4 + +=item confnum - primary key + +=item modenum - L<FS::invoice_mode> foreign key + +=item locale - locale string (see L<FS::Locales>) + +=item notice_name - the title to display on the invoice + +=item subject - subject line of the email + +=item htmlnotes - "notes" section (HTML) + +=item htmlfooter - footer (HTML) + +=item htmlsummary - summary header, for invoices in summary format (HTML) + +=item htmlreturnaddress - return address (HTML) + +=item latexnotes - "notes" section (LaTeX) + +=item latexfooter - footer (LaTeX) + +=item latexsummary - summary header, for invoices in summary format (LaTeX) + +=item latexreturnaddress - return address (LaTeX) + +=item latexcoupon - payment coupon section (LaTeX) + +=item latexsmallfooter - footer for pages after the first (LaTeX) + +=item latextopmargin - top margin + +=item latexheadsep - distance from bottom of header to top of body + +=item latexaddresssep - distance from top of body to customer address + +=item latextextheight - maximum height of invoice body text + +=item latexextracouponspace - additional footer space to allow for coupon + +=item latexcouponfootsep - distance from bottom of coupon content to top +of page footer + +=item latexcouponamountenclosedsep - distance from coupon balance line to +"Amount Enclosed" box + +=item latexcoupontoaddresssep - distance from "Amount Enclosed" box to +coupon mailing address + +=item latexverticalreturnaddress - 'Y' to place the return address below +the company logo rather than beside it + +=item latexcouponaddcompanytoaddress - 'Y' to add the company name to the +address on the payment coupon + +=item logo_png - company logo, as a PNG, for HTML invoices + +=item logo_eps - company logo, as an EPS, for LaTeX invoices + +=item lpr - command to print the invoice (passed on stdin as a PDF) + +=back + +=head1 METHODS + +=over 4 + +=item new HASHREF + +Creates a new invoice configuration. To add it to the database, see +L<"insert">. + +=cut + +# the new method can be inherited from FS::Record, if a table method is defined + +sub table { 'invoice_conf'; } + +=item insert + +Adds this record to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +# slightly special: you can insert/replace the invoice mode this way + +sub insert { + my $self = shift; + if (!$self->modenum) { + my $invoice_mode = FS::invoice_mode->new({ + 'modename' => $self->modename, + 'agentnum' => $self->agentnum, + }); + my $error = $invoice_mode->insert; + return $error if $error; + $self->set('modenum' => $invoice_mode->modenum); + } else { + my $invoice_mode = FS::invoice_mode->by_key($self->modenum); + my $changed = 0; + foreach (qw(agentnum modename)) { + $changed ||= ($invoice_mode->get($_) eq $self->get($_)); + $invoice_mode->set($_, $self->get($_)); + } + my $error = $invoice_mode->replace if $changed; + return $error if $error; + } + $self->SUPER::insert(@_); +} + +=item delete + +Delete this record from the database. + +=cut + +sub delete { + my $self = shift; + my $error = $self->FS::Record::delete; # not Conf::delete + return $error if $error; + my $invoice_mode = FS::invoice_mode->by_key($self->modenum); + if ( $invoice_mode and + FS::invoice_conf->count('modenum = '.$invoice_mode->modenum) == 0 ) { + $error = $invoice_mode->delete; + return $error if $error; + } + ''; +} + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub replace { + my $self = shift; + my $error = $self->SUPER::replace(@_); + return $error if $error; + + my $invoice_mode = FS::invoice_mode->by_key($self->modenum); + my $changed = 0; + foreach (qw(agentnum modename)) { + $changed ||= ($invoice_mode->get($_) eq $self->get($_)); + $invoice_mode->set($_, $self->get($_)); + } + $error = $invoice_mode->replace if $changed; + return $error if $error; +} + +=item check + +Checks all fields to make sure this is a valid example. If there is +an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +=cut + +# the check method should currently be supplied - FS::Record contains some +# data checking routines + +sub check { + my $self = shift; + + my $error = + $self->ut_numbern('confnum') + || $self->ut_number('modenum') + || $self->ut_textn('locale') + || $self->ut_anything('notice_name') + || $self->ut_anything('subject') + || $self->ut_anything('htmlnotes') + || $self->ut_anything('htmlfooter') + || $self->ut_anything('htmlsummary') + || $self->ut_anything('htmlreturnaddress') + || $self->ut_anything('latexnotes') + || $self->ut_anything('latexfooter') + || $self->ut_anything('latexsummary') + || $self->ut_anything('latexcoupon') + || $self->ut_anything('latexsmallfooter') + || $self->ut_anything('latexreturnaddress') + || $self->ut_textn('latextopmargin') + || $self->ut_textn('latexheadsep') + || $self->ut_textn('latexaddresssep') + || $self->ut_textn('latextextheight') + || $self->ut_textn('latexextracouponspace') + || $self->ut_textn('latexcouponfootsep') + || $self->ut_textn('latexcouponamountenclosedsep') + || $self->ut_textn('latexcoupontoaddresssep') + || $self->ut_flag('latexverticalreturnaddress') + || $self->ut_flag('latexcouponaddcompanytoaddress') + || $self->ut_anything('logo_png') + || $self->ut_anything('logo_eps') + ; + return $error if $error; + + $self->SUPER::check; +} + +# hook _config to substitute our own values; let FS::Conf do the rest of +# the interface + +sub _config { + my $self = shift; + # if we fall back, we still want FS::Conf to respect our locale + $self->{locale} = $self->get('locale'); + my ($key, $agentnum, $nodefault) = @_; + # some fields, but not all, start with invoice_ + my $colname = $key; + if ( $key =~ /^invoice_(.*)$/ ) { + $colname = $1; + } + if ( length($self->get($colname)) ) { + return FS::conf->new({ 'name' => $key, + 'value' => $self->get($colname) }); + } else { + return $self->FS::Conf::_config(@_); + } +} + +# disambiguation +sub set { + my $self = shift; + $self->FS::Record::set(@_); +} + +sub exists { + my $self = shift; + $self->FS::Conf::exists(@_); +} + +=back + +=head1 SEE ALSO + +L<FS::Template_Mixin>, L<FS::Record>, schema.html from the base documentation. + +=cut + +1; + diff --git a/FS/FS/invoice_mode.pm b/FS/FS/invoice_mode.pm new file mode 100644 index 0000000..115dd44 --- /dev/null +++ b/FS/FS/invoice_mode.pm @@ -0,0 +1,157 @@ +package FS::invoice_mode; + +use strict; +use base qw( FS::Record ); +use FS::Record qw( qsearch qsearchs ); +use FS::invoice_conf; + +=head1 NAME + +FS::invoice_mode - Object methods for invoice_mode records + +=head1 SYNOPSIS + + use FS::invoice_mode; + + $record = new FS::invoice_mode \%hash; + $record = new FS::invoice_mode { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::invoice_mode object represents an invoice rendering style. +FS::invoice_mode inherits from FS::Record. The following fields are +currently supported: + +=over 4 + +=item modenum - primary key + +=item agentnum - the agent who owns this invoice mode (can be null) + +=item modename - descriptive name for internal use + + +=back + +=head1 METHODS + +=over 4 + +=item new HASHREF + +Creates a new invoice mode. To add the object to the database, +see L<"insert">. + +Note that this stores the hash reference, not a distinct copy of the hash it +points to. You can ask the object for a copy with the I<hash> method. + +=cut + +# the new method can be inherited from FS::Record, if a table method is defined + +sub table { 'invoice_mode'; } + +=item insert + +Adds this record to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +# the insert method can be inherited from FS::Record + +=item delete + +Delete this record from the database. + +=cut + +# the delete method can be inherited from FS::Record + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +# the replace method can be inherited from FS::Record + +=item check + +Checks all fields to make sure this is a valid example. If there is +an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +=cut + +# the check method should currently be supplied - FS::Record contains some +# data checking routines + +sub check { + my $self = shift; + + my $error = + $self->ut_numbern('modenum') + || $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum') + || $self->ut_text('modename') + ; + return $error if $error; + + $self->SUPER::check; +} + +=item invoice_conf [ LOCALE ] + +Returns the L<FS::invoice_conf> for this invoice mode, with the specified +locale. If there isn't one with that locale, returns the one with null +locale. If that doesn't exist, returns nothing. + +=cut + +sub invoice_conf { + my $self = shift; + my $locale = shift; + my $invoice_conf; + if ( $locale ) { + $invoice_conf = qsearchs('invoice_conf', { + modenum => $self->modenum, + locale => $locale, + }); + } + $invoice_conf ||= qsearchs('invoice_conf', { + modenum => $self->modenum, + locale => '', + }); + $invoice_conf; +} + +=item agent + +Returns the agent associated with this invoice mode, if any. + +=cut + +sub agent { + my $self = shift; + $self->agentnum ? FS::agent->by_key($self->agentnum) : ''; +} + +=back + +=head1 SEE ALSO + +L<FS::Record>, schema.html from the base documentation. + +=cut + +1; + diff --git a/FS/FS/part_event/Action/cust_bill_email.pm b/FS/FS/part_event/Action/cust_bill_email.pm index 1a3bca4..3331a4c 100644 --- a/FS/FS/part_event/Action/cust_bill_email.pm +++ b/FS/FS/part_event/Action/cust_bill_email.pm @@ -9,14 +9,22 @@ sub eventtable_hashref { { 'cust_bill' => 1 }; } +sub option_fields { + ( + 'modenum' => { label => 'Invoice mode', + type => 'select-invoice_mode', + }, + ); +} + sub default_weight { 51; } sub do_action { my( $self, $cust_bill ) = @_; - #my $cust_main = $self->cust_main($cust_bill); my $cust_main = $cust_bill->cust_main; + $cust_bill->set('mode' => $self->option('modenum')); $cust_bill->email unless $cust_main->invoice_noemail; } diff --git a/FS/FS/part_event/Action/cust_bill_print.pm b/FS/FS/part_event/Action/cust_bill_print.pm index 6b3e6f4..ea6e0aa 100644 --- a/FS/FS/part_event/Action/cust_bill_print.pm +++ b/FS/FS/part_event/Action/cust_bill_print.pm @@ -9,6 +9,14 @@ sub eventtable_hashref { { 'cust_bill' => 1 }; } +sub option_fields { + ( + 'modenum' => { label => 'Invoice mode', + type => 'select-invoice_mode', + }, + ); +} + sub default_weight { 51; } sub do_action { @@ -17,6 +25,7 @@ sub do_action { #my $cust_main = $self->cust_main($cust_bill); my $cust_main = $cust_bill->cust_main; + $cust_bill->set('mode' => $self->option('modenum')); $cust_bill->print; } diff --git a/FS/FS/part_event/Action/cust_bill_print_pdf.pm b/FS/FS/part_event/Action/cust_bill_print_pdf.pm index 6b37f38..6c01d42 100644 --- a/FS/FS/part_event/Action/cust_bill_print_pdf.pm +++ b/FS/FS/part_event/Action/cust_bill_print_pdf.pm @@ -9,6 +9,14 @@ sub eventtable_hashref { { 'cust_bill' => 1 }; } +sub option_fields { + ( + 'modenum' => { label => 'Invoice mode', + type => 'select-invoice_mode' + }, + ); +} + sub default_weight { 51; } sub do_action { @@ -20,6 +28,7 @@ sub do_action { my $opt = { $self->options }; $opt->{'notice_name'} ||= 'Invoice'; + $cust_bill->set('mode' => $self->option('modenum')); $cust_bill->batch_invoice($opt); } diff --git a/FS/FS/part_event/Action/cust_bill_send.pm b/FS/FS/part_event/Action/cust_bill_send.pm index 587a7c6..c6928dc 100644 --- a/FS/FS/part_event/Action/cust_bill_send.pm +++ b/FS/FS/part_event/Action/cust_bill_send.pm @@ -9,11 +9,20 @@ sub eventtable_hashref { { 'cust_bill' => 1 }; } +sub option_fields { + ( + 'modenum' => { label => 'Invoice mode', + type => 'select-invoice_mode', + }, + ); +} + sub default_weight { 50; } sub do_action { my( $self, $cust_bill ) = @_; + $cust_bill->set('mode' => $self->option('modenum')); $cust_bill->send; } diff --git a/FS/FS/part_event/Action/cust_bill_send_agent.pm b/FS/FS/part_event/Action/cust_bill_send_agent.pm index 670a32c..bbb757b 100644 --- a/FS/FS/part_event/Action/cust_bill_send_agent.pm +++ b/FS/FS/part_event/Action/cust_bill_send_agent.pm @@ -7,6 +7,9 @@ sub description { 'Send invoice (email/print/fax) with alternate template, for specific agents'; } +# this event is just cust_bill_send_alternate + an implicit (and inefficient) +# 'agent' condition + sub eventtable_hashref { { 'cust_bill' => 1 }; } @@ -17,6 +20,9 @@ sub option_fields { type => 'select-agent', multiple => 1 }, + 'modenum' => { label => 'Invoice mode', + type => 'select-invoice_mode', + }, 'agent_templatename' => { label => 'Template', type => 'select-invoice_template', }, @@ -32,10 +38,15 @@ sub do_action { #my $cust_main = $self->cust_main($cust_bill); my $cust_main = $cust_bill->cust_main; + my %agentnums = map { $_=>1 } split(/\s*,\s*/, $self->option('agentnum')); + if (keys(%agentnums) and !exists($agentnums{$cust_main->agentnum})) { + return; + } + + $cust_bill->set('mode' => $self->option('modenum')); $cust_bill->send( - $self->option('agent_templatename'), - [ split(/\s*,\s*/, $self->option('agentnum') ) ], - $self->option('agent_invoice_from'), + 'template' => $self->option('agent_templatename'), + 'invoice_from' => $self->option('agent_invoice_from'), ); } diff --git a/FS/FS/part_event/Action/cust_bill_send_alternate.pm b/FS/FS/part_event/Action/cust_bill_send_alternate.pm index cfd9264..fb71a5a 100644 --- a/FS/FS/part_event/Action/cust_bill_send_alternate.pm +++ b/FS/FS/part_event/Action/cust_bill_send_alternate.pm @@ -11,6 +11,8 @@ sub eventtable_hashref { sub option_fields { ( + 'modenum' => { label => 'Invoice mode', + type => 'select-invoice_mode' }, 'templatename' => { label => 'Template', type => 'select-invoice_template', }, @@ -25,7 +27,8 @@ sub do_action { #my $cust_main = $self->cust_main($cust_bill); my $cust_main = $cust_bill->cust_main; - $cust_bill->send( $self->option('templatename') ); + $cust_bill->set('mode' => $self->option('modenum')); + $cust_bill->send({'template' => $self->option('templatename')}); } 1; diff --git a/FS/FS/part_event/Action/cust_bill_send_if_newest.pm b/FS/FS/part_event/Action/cust_bill_send_if_newest.pm index 083da8b..c744362 100644 --- a/FS/FS/part_event/Action/cust_bill_send_if_newest.pm +++ b/FS/FS/part_event/Action/cust_bill_send_if_newest.pm @@ -18,6 +18,9 @@ sub eventtable_hashref { sub option_fields { ( + 'modenum' => { label => 'Invoice mode', + type => 'select-invoice_mode', + }, 'if_newest_templatename' => { label => 'Template', type => 'select-invoice_template', }, @@ -29,10 +32,17 @@ sub default_weight { 50; } sub do_action { my( $self, $cust_bill ) = @_; - #my $cust_main = $self->cust_main($cust_bill); - my $cust_main = $cust_bill->cust_main; - - $cust_bill->send( $self->option('templatename') ); + my $invnum = $cust_bill->invnum; + my $custnum = $cust_bill->custnum; + return '' if scalar( + grep { $_->owed > 0 } + qsearch('cust_bill', { + 'custnum' => $custnum, + 'invnum' => { op=>'>', value=>$invnum }, + }) + ); + $cust_bill->set('mode' => $self->option('modenum')); + $cust_bill->send( 'template' => $self->option('templatename') ); } 1; diff --git a/FS/FS/part_event/Action/cust_bill_send_reminder.pm b/FS/FS/part_event/Action/cust_bill_send_reminder.pm index 073bb8f..354f969 100644 --- a/FS/FS/part_event/Action/cust_bill_send_reminder.pm +++ b/FS/FS/part_event/Action/cust_bill_send_reminder.pm @@ -11,9 +11,13 @@ sub eventtable_hashref { sub option_fields { ( + 'modenum' => { label => 'Invoice mode', + type => 'select-invoice_mode', + }, + # totally unnecessary, since the invoice mode can set notice_name and lpr, + # but for compatibility... 'notice_name' => 'Reminder name', - #'notes' => { 'label' => 'Reminder notes' }, - #include standard notes? no/prepend/append + #'notes' => { 'label' => 'Reminder notes' }, # invoice mode does this 'lpr' => 'Optional alternate print command', ); } @@ -23,9 +27,7 @@ sub default_weight { 50; } sub do_action { my( $self, $cust_bill ) = @_; - #my $cust_main = $self->cust_main($cust_bill); - #my $cust_main = $cust_bill->cust_main; - + $cust_bill->set('mode' => $self->option('modenum')); $cust_bill->send({ 'notice_name' => $self->option('notice_name'), 'lpr' => $self->option('lpr'), diff --git a/FS/FS/part_event/Action/cust_statement_send.pm b/FS/FS/part_event/Action/cust_statement_send.pm index 74cc48c..67a94aa 100644 --- a/FS/FS/part_event/Action/cust_statement_send.pm +++ b/FS/FS/part_event/Action/cust_statement_send.pm @@ -19,7 +19,8 @@ sub default_weight { sub do_action { my( $self, $cust_statement ) = @_; - $cust_statement->send( 'statement' ); #XXX configure + $cust_statement->send( 'template' => 'statement' ); #XXX configure + #XXX use an invoice mode? } diff --git a/FS/MANIFEST b/FS/MANIFEST index a3b11f7..339965e 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -720,3 +720,7 @@ FS/svc_alarm.pm t/svc_alarm.t FS/cable_model.pm t/cable_model.t +FS/invoice_mode.pm +t/invoice_mode.t +FS/invoice_conf.pm +t/invoice_conf.t diff --git a/FS/t/invoice_conf.t b/FS/t/invoice_conf.t new file mode 100644 index 0000000..b707fa3 --- /dev/null +++ b/FS/t/invoice_conf.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::invoice_conf; +$loaded=1; +print "ok 1\n"; diff --git a/FS/t/invoice_mode.t b/FS/t/invoice_mode.t new file mode 100644 index 0000000..5f945f0 --- /dev/null +++ b/FS/t/invoice_mode.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::invoice_mode; +$loaded=1; +print "ok 1\n"; diff --git a/bin/generate-table-module b/bin/generate-table-module index b536360..37a5812 100755 --- a/bin/generate-table-module +++ b/bin/generate-table-module @@ -95,10 +95,10 @@ close TEST; # add them to MANIFEST ### -#system('cvs edit FS/MANIFEST'); - open(MANIFEST,">>FS/MANIFEST") or die $!; print MANIFEST "FS/$table.pm\n", "t/$table.t\n"; close MANIFEST; +system("git add FS/FS/$table.pm FS/t/$table.t"); + diff --git a/httemplate/browse/invoice_conf.html b/httemplate/browse/invoice_conf.html new file mode 100644 index 0000000..c8fd1bf --- /dev/null +++ b/httemplate/browse/invoice_conf.html @@ -0,0 +1,70 @@ +<& elements/browse.html, + 'title' => 'Invoice modes', + 'name_singular' => 'configuration', + 'menubar' => \@menubar, + 'query' => { + 'select' => $select, + 'table' => 'invoice_conf', + 'addl_from' => ' JOIN invoice_mode USING (modenum)', + 'extra_sql' => ' WHERE '.$curuser->agentnums_sql( + 'table' => 'invoice_mode', + 'null_right' => ['Edit global templates'], + ), + 'order_by' => q( ORDER BY modename asc, COALESCE(locale,'') asc), + }, + 'count_query' => 'SELECT COUNT(*) FROM invoice_conf JOIN invoice_mode USING (modenum)', + 'header' => [ 'Name', 'Agent', 'Locale', 'Overrides', ], + 'fields' => [ $modename, + $agent, + $locale_label, + $overrides, + ], + 'align' => 'llcl', + 'links' => [ '', '', $link ], + 'disable_maxselect' => 1, +&> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right([ 'View templates', 'View global templates', + 'Edit templates', 'Edit global templates', ]); + +my @overrides = grep {$_ ne 'modenum' and $_ ne 'confnum'} FS::invoice_conf->fields; +my $select = join(',', 'modename', 'agentnum', 'confnum', 'invoice_conf.*'); + +my @menubar = (); +if ( $curuser->access_right(['Edit templates', 'Edit global templates']) ) { + push @menubar, 'Add a new invoice mode' => $p.'edit/invoice_conf.html'; +} + +my $locale_style = 'font-size:0.8em; padding:3px; background-color:'; + +my $last_modenum = 0; +my $modename = sub { + return '' if $_[0]->modenum == $last_modenum; + $_[0]->modename; +}; + +my $agent = sub { + return '' if $_[0]->modenum == $last_modenum; + $last_modenum = $_[0]->modenum; + $_[0]->agentnum ? FS::agent->by_key($_[0]->agentnum)->agent : '(global)'; +}; + +my $locale_label = sub { + my $l = $_[0]->locale; + $l ? +{ FS::Locales->locale_info($l) }->{'label'} : '(default)'; +}; + +my $overrides = sub { + my $invoice_conf = shift; + [ map { [ { data => $_ } ] } + grep { length $invoice_conf->get($_) } + @overrides + ], +}; + +my $link = [ $p.'edit/invoice_conf.html?', 'confnum' ]; +</%init> diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html index 3270f04..051788c 100644 --- a/httemplate/edit/elements/edit.html +++ b/httemplate/edit/elements/edit.html @@ -331,6 +331,7 @@ Example: % qw( width height config ), #htmlarea % qw( alt_format ), #select-cust_location % qw( classnum ), # select-inventory_item +% qw( aligned ), # columnstart % ; % % #select-table diff --git a/httemplate/edit/invoice_conf.html b/httemplate/edit/invoice_conf.html new file mode 100644 index 0000000..b7b3a4e --- /dev/null +++ b/httemplate/edit/invoice_conf.html @@ -0,0 +1,296 @@ +<& elements/edit.html, + 'body_etc' => $body_etc, + 'name_singular' => 'invoice configuration', + 'table' => 'invoice_conf', + 'viewall_dir' => 'browse', + 'fields' => \@fields, + 'labels' => \%labels, + 'new_callback' => \&new_callback, + 'edit_callback' => \&edit_callback, + 'error_callback' => \&error_callback, + 'html_init' => \&html_init, + 'html_table_bottom' => \&html_table_bottom, + 'html_bottom' => '</DIV>', # close tablebreak-tabs +&> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +# ??? +die "access denied" + unless $curuser->access_right([ 'Edit templates', 'Edit global templates' ]); + +my $body_etc = ''; +$body_etc = q!onload="document.getElementById('locale').onchange()"! + if $cgi->param('locale') eq 'new'; + +my $modenum = $cgi->param('modenum'); +my $mode = $modenum ? qsearchs('invoice_mode', { modenum => $modenum }) : ''; + +my %textarea = (type => 'textarea', rows => 10, cols => 40); +my @fields = ( + { field => 'modenum', type => 'hidden' }, + { field => 'agentnum', + type => 'select-agent', + }, + { field => 'modename', size=>60, }, + { type => 'tablebreak-tabs', + include_opt_callback => \&menubar_opt_callback, + }, + { field => 'locale', type => 'hidden' }, + { field => 'notice_name', size=>60, }, + { field => 'subject', size=>60, }, + { field => 'lpr', size=>60, }, + + { type => 'columnstart', aligned => 1 }, + { type => 'title', value => '<BR>' }, + map ( { +{ type => 'justtitle', value => $_ } } + 'Notes', + 'Footer', + 'Summary header', + 'Return address', + 'Coupon', + 'Small footer', + 'Top margin', + 'Header separation', + 'Address separation', + 'Text height', + 'Coupon height', + 'Footer separation', + ), + + { type => 'columnnext' }, + { type => 'title', value => 'LaTeX' }, + { field => 'latexnotes', %textarea }, + { field => 'latexfooter', %textarea }, + { field => 'latexsummary', %textarea }, + { field => 'latexreturnaddress', %textarea }, + { field => 'latexcoupon', %textarea }, + { field => 'latexsmallfooter', %textarea }, + { field => 'latextopmargin', size => 16 }, + { field => 'latexheadsep', size => 16 }, + { field => 'latexaddresssep', size => 16 }, + { field => 'latextextheight', size => 16 }, + { field => 'latexextracouponspace', size => 16 }, + { field => 'latexcouponfootsep', size => 16 }, + # are these still used? + #{ field => 'latexcouponamountenclosedsep', size => 16 }, + #{ field => 'latexverticalreturnaddress', type => 'checkbox' }, + #{ field => 'latexcouponaddcompanytoaddress',type => 'checkbox' }, + # logo -- implement if someone really needs it... + + { type => 'columnnext' }, + { type => 'title', value => 'HTML' }, + { field => 'htmlnotes', %textarea }, #htmlarea? + { field => 'htmlfooter', %textarea }, + { field => 'htmlsummary', %textarea }, + { field => 'htmlreturnaddress', %textarea }, + # logo + + { type => 'columnend' }, +); + +my %labels = ( + 'confnum' => 'Configuration', + 'locale' => 'Locale', + 'agentnum' => 'Agent', + 'modename' => 'Mode name', + 'notice_name' => 'Notice name', + 'subject' => 'Email Subject: header', + 'lpr' => 'Alternate lpr command', + + map { $_ => '' } (qw( + latexnotes + latexfooter + latexsummary + latexreturnaddress + latexcoupon + latexsmallfooter + latextopmargin + latexheadsep + latexaddresssep + latextextheight + latexextracouponspace + latexcouponfootsep + htmlnotes + htmlfooter + htmlsummary + htmlreturnaddress + logo_png + logo_eps + ) ), + +); + +sub get_invoice_mode { # because we can't quite use agent_virt here + my $modenum = shift; + qsearchs({ + 'table' => 'invoice_mode', + 'hashref' => { 'modenum' => $modenum }, + 'extra_sql' => ' AND '. + $FS::CurrentUser::CurrentUser->agentnums_sql( + 'null_right' => 'Edit global templates', + 'viewall_right' => 'Edit global templates' ), + }); +}; + +sub error_callback { + my ($cgi, $object) = @_; + foreach (qw(modename agentnum)) { + $object->set($_, $cgi->param($_)); + } + if ($object->confnum) { + return edit_callback(@_); + } else { + return new_callback(@_); + } +} + +sub new_callback { + my ($cgi, $object, $fields_arrayref, $opt_hashref) = @_; + my $modenum; + if ( $cgi->param('locale') =~ /^(\w+)$/ ) { + $object->set('locale' => $1); + } + + if ( $cgi->param('modenum') =~ /^(\d+)$/ ) { + $modenum = $1; # we're adding a locale to an existing mode + $object->set('modenum' => $modenum); + my $invoice_mode = get_invoice_mode($modenum) + or die "invoice mode $modenum not found"; + + $object->set('modename', $invoice_mode->modename); + $object->set('agentnum', $invoice_mode->agentnum); + + # also, need to select a locale + # make a list of available locales + my %existing_locales = map { $_->locale } + qsearch('invoice_conf', { modenum => $modenum }); + + my @locales = grep { !exists($existing_locales{$_}) } + FS::Conf->new->config('available-locales'); + my %labels; + foreach (@locales) { + my %info = FS::Locales->locale_info($_); + $labels{$_} = $info{'label'}; + } + unshift @locales, 'new'; + $labels{'new'} = 'Select language'; + + # insert a field def + my $i = 0; + $i++ until ( $fields_arrayref->[$i]->{'field'} eq 'locale' ); + my $locale_field = $fields_arrayref->[$i]; + + my $onchange_locale = "document.getElementById('submit').disabled = + (this.options[this.selectedIndex].value == 'new');"; + + %$locale_field = ( + field => 'locale', + type => 'select', + options => \@locales, + labels => \%labels, + curr_value => 'new', + onchange => $onchange_locale, + ); + + } # otherwise it's a completely new mode, so the locale is default + +} + +sub edit_callback { + # massive false laziness with msg_template UI + my ($cgi, $object, $fields_arrayref, $opt_hashref) = @_; + + # a little different here in that we treat the content object + # as "primary" (this is edit/invoice_conf.html, etc.) + # so all we need from the invoice_mode is its name + # (and agent identity) + my $modenum = $object->modenum; + my $invoice_mode = get_invoice_mode($modenum) + or die "invoice mode $modenum not found"; + $object->set('modename', $invoice_mode->modename); + $object->set('agentnum', $invoice_mode->agentnum); + +} + +sub menubar_opt_callback { + my $object = shift; + my $modenum = $object->modenum or return; + my (@tabs, @options, %labels); + my $display_new = 0; + my $selected = ''; + foreach my $l ('', FS::Conf->new->config('available-locales')) { + my $invoice_conf = + qsearchs('invoice_conf', { modenum => $modenum, locale => $l }); + if ( $invoice_conf ) { + my %info = FS::Locales->locale_info($l) if $l; + my $label = $info{'label'} || mt('Default'); + push @tabs, $label, $invoice_conf->confnum; + $selected = $label if $object->locale eq $l; + } + else { + $display_new = 1; # there is at least one unused locale left + } + } + push @tabs, mt('New'), "modenum=$modenum;locale=new" if $display_new; + $selected = mt('New') if $object->locale eq 'new'; + $selected ||= mt('Default'); + ( + 'url_base' => $cgi->url() . '?', + 'selected' => $selected, + 'tabs' => \@tabs + ); +} + +sub html_init { +q! +<STYLE> +.fstabcontainer th { vertical-align: middle; text-align: center } +</STYLE> +! +} + +sub html_table_bottom { + my $object = shift; + my $locale = ''; + my $modenum = ''; + + if ($object->locale =~ /^(\w+)$/) { + $locale = $1; + } + if ($object->modenum =~ /^(\d+)$/) { + $modenum = $1; + } + my $html; + my $show_delete = 1; + # don't allow the default locale to be removed unless it's the last one + # in the mode + $show_delete = 0 if ( + $locale eq 'new' or + $modenum eq '' or + ($locale eq '' and + FS::invoice_conf->count("modenum = $modenum and locale is not null") > 0 + ) + ); + + if ( $show_delete ) { + # set up a delete link + my $confnum = $object->confnum; + my $url = $p."misc/delete-invoice_conf.html?$confnum"; + my $link = qq!<A HREF="javascript:areyousure('$url','Really delete this configuration?')">! . + 'Delete this configuration' . + '</A>'; + $html = qq!<TR><TD></TD> + <TD STYLE="font-style: italic; font-size: small">$link</TD></TR> + <SCRIPT TYPE="text/javascript"> + function areyousure(url, message) { + if (confirm(message)) window.location.href = url; + } + </SCRIPT> + !; + } + $html; +} + +</%init> diff --git a/httemplate/edit/process/invoice_conf.html b/httemplate/edit/process/invoice_conf.html new file mode 100644 index 0000000..1d45e12 --- /dev/null +++ b/httemplate/edit/process/invoice_conf.html @@ -0,0 +1,21 @@ +<& elements/process.html, + 'table' => 'invoice_conf', + 'viewall_dir' => 'browse', + 'fields' => [ FS::invoice_conf->fields, 'modename', 'agentnum' ], + 'precheck_callback' => \&precheck_callback, +&> +<%init> +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right(['Edit templates','Edit global templates']); + +sub precheck_callback { + my $cgi = shift; + $cgi->param('locale') =~ /^(\w*)$/; + my $locale = $1; + return mt('Language required') if $locale eq 'new'; # the user didn't choose + die "unknown locale $locale" if ( $locale and + !FS::Locales->locale_info($locale) ); +} +# invoice_conf itself knows to create/update invoice_mode if necessary, +# so nothing special here +</%init> diff --git a/httemplate/elements/columnstart.html b/httemplate/elements/columnstart.html index be37d81..1ffbcb9 100644 --- a/httemplate/elements/columnstart.html +++ b/httemplate/elements/columnstart.html @@ -1,6 +1,81 @@ +<%doc> +<table> + <& /elements/columnstart.html &> + <tr> ... </tr> + <tr> ... </tr> + <& /elements/columnnext.html &> + ... + <& /elements/columnend.html &> +</table> + +Pass 'aligned' => 1 to have corresponding rows in the columns line up. +</%doc> +% my $id = sprintf('table%08d', rand(100000000)); <TR> <TD CLASS="background" COLSPAN=99> - <TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0> + <TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 id="<%$id%>"> <TR> <TD VALIGN="top"> <TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0> +% if ( $aligned ) { +%# Instead of changing all the tr-* elements to sometimes output table +%# cells without wrapping them in a row, we're just going to completely +%# rebuild the table on the client side. +<script type="text/javascript"> +<&| onload.js &> + var table = document.getElementById('<%$id%>'); // has one row, always + var rows = []; // row contents, each containing + var n_rows = []; // rows in each subtable + var n_cols = []; // cols in each subtable + var total_rows = 0; // max(n_rows) + for(var i=0; i < table.rows[0].cells.length; i++) { + // these are cells created by columnstart/columnnext + // each contains a table, and nothing else + var subtable = table.rows[0].cells[i].children[0]; + n_rows[i] = subtable.rows.length; + if ( total_rows < n_rows[i] ) { + total_rows = n_rows[i]; + } + n_cols[i] = 0; + var subrows = []; // the rows of this table + for(var j=0; j < n_rows[i]; j++) { + // these are the actual tr-* rows within the table, and + // can contain multiple cells + subrows[j] = []; + var tr = subtable.rows[j]; + if ( n_cols[i] < tr.cells.length ) { + n_cols[i] = tr.cells.length; + } + for(var k=0; k < tr.cells.length; k++) { + subrows[j][k] = tr.cells[k]; + } + } // for(j) + rows[i] = subrows; + } // for(i) + var new_table = document.createElement('TABLE'); + for (var j = 0; j < total_rows; j++) { + var tr = document.createElement('TR'); + for (var i = 0; i < rows.length; i++) { // subtables + var k = 0; // subrow position + if ( j < n_rows[i] ) { // then subtable i has this row + for (k = 0; k < rows[i][j].length; k++) { // cells + tr.appendChild(rows[i][j][k]); + } + } // else k is just 0 + if ( k < n_cols[i] ) { // then we need a spacer + var spacer = document.createElement('TD'); + spacer.setAttribute('colspan', n_cols[i] - k); + tr.appendChild(spacer); + } + } // for(i); subtables + // tr is complete + new_table.appendChild(tr); + } // for(j); rows + table.parentNode.insertBefore( new_table, table ); + table.parentNode.removeChild(table); +</&> +</script> +% } # if $aligned +<%args> +$aligned => 0 +</%args> diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index 8cbbd17..edb5e81 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -600,6 +600,7 @@ $config_billing{'Billing events'} = [ $fsurl.'browse/part_event.html', 'Billing || $curuser->access_right('Edit global billing events'); if ( $curuser->access_right('Configuration') ) { #$config_billing{'Invoice events'} = [ $fsurl.'browse/part_bill_event.cgi', 'Deprecated, old-style actions for overdue invoices' ]; + $config_billing{'Invoice configurations'} = [ $fsurl.'browse/invoice_conf.html', 'Adjust invoice settings for special-purpose notices' ]; $config_billing{'Invoice templates'} = [ $fsurl.'browse/invoice_template.html', 'Edit templates for HTML, plaintext and typeset invoices' ]; $config_billing{'separator'} = ''; #its a separator! $config_billing{'Prepaid cards'} = [ $fsurl.'search/prepay_credit.html', 'View outstanding cards, generate new cards' ]; diff --git a/httemplate/elements/tr-select-invoice_mode.html b/httemplate/elements/tr-select-invoice_mode.html new file mode 100644 index 0000000..3dccdcc --- /dev/null +++ b/httemplate/elements/tr-select-invoice_mode.html @@ -0,0 +1,10 @@ +<& tr-select-table.html, + 'label' => 'Invoice mode', + 'table' => 'invoice_mode', + 'field' => 'modenum', + 'name_col' => 'modename', + 'agent_virt' => 1, + 'agent_null' => 1, + 'empty_label' => '(none)', + @_ +&> diff --git a/httemplate/misc/delete-invoice_conf.html b/httemplate/misc/delete-invoice_conf.html new file mode 100644 index 0000000..6cc6ddc --- /dev/null +++ b/httemplate/misc/delete-invoice_conf.html @@ -0,0 +1,19 @@ +<%init> +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" + unless $curuser->access_right(['Edit templates', 'Edit global templates']); + +my ($query) = $cgi->keywords; +$query =~ /^(\d+)$/ or die "bad confnum"; +my $invoice_conf = FS::invoice_conf->by_key($1) + or die "couldn't find invoice_conf #$1"; +if ( !$curuser->access_right('Edit global templates') ) { + my $agentnum = FS::invoice_mode->by_key($invoice_conf->modenum)->agentnum; + die "access denied" + unless $curuser->agentnums_href->{$agentnum}; +} + +my $error = $invoice_conf->delete; # may also delete the invoice_mode +my $url = $p.'browse/invoice_conf.html'; +</%init> +<% $cgi->redirect($url) %> diff --git a/httemplate/misc/email-invoice.cgi b/httemplate/misc/email-invoice.cgi index 269722f..b24e042 100755 --- a/httemplate/misc/email-invoice.cgi +++ b/httemplate/misc/email-invoice.cgi @@ -12,7 +12,7 @@ my $invnum = $3; my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum}); die "Can't find invoice!\n" unless $cust_bill; -$cust_bill->email($template); +$cust_bill->email({ 'template' => $template }); my $custnum = $cust_bill->getfield('custnum'); diff --git a/httemplate/misc/fax-invoice.cgi b/httemplate/misc/fax-invoice.cgi index 2591fce..f72fc7e 100755 --- a/httemplate/misc/fax-invoice.cgi +++ b/httemplate/misc/fax-invoice.cgi @@ -12,7 +12,7 @@ my $invnum = $3; my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum}); die "Can't find invoice!\n" unless $cust_bill; -$cust_bill->fax_invoice($template); +$cust_bill->fax_invoice({ 'template' => $template }); my $custnum = $cust_bill->getfield('custnum'); diff --git a/httemplate/misc/print-invoice.cgi b/httemplate/misc/print-invoice.cgi index aeef687..5ce6e76 100755 --- a/httemplate/misc/print-invoice.cgi +++ b/httemplate/misc/print-invoice.cgi @@ -12,7 +12,7 @@ my $invnum = $3; my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum}); die "Can't find invoice!\n" unless $cust_bill; -$cust_bill->print($template); +$cust_bill->print({ 'template' => $template}); my $custnum = $cust_bill->getfield('custnum'); diff --git a/httemplate/misc/send-invoice.cgi b/httemplate/misc/send-invoice.cgi index 32dfe27..08dd0e0 100644 --- a/httemplate/misc/send-invoice.cgi +++ b/httemplate/misc/send-invoice.cgi @@ -13,6 +13,10 @@ my $invnum = $cgi->param('invnum'); my $template = $cgi->param('template'); my $notice_name = $cgi->param('notice_name') if $cgi->param('notice_name'); my $method = $cgi->param('method'); +my $mode; +if ( $cgi->param('mode') =~ /^(\d+)$/ ) { + $mode = $1; +} $method .= '_invoice' if $method eq 'fax'; #! @@ -21,6 +25,7 @@ die "unknown method $method" unless $method{$method}; my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum}); die "Can't find invoice!\n" unless $cust_bill; +$cust_bill->set('mode' => $mode) if $mode; $cust_bill->$method({ 'template' => $template, 'notice_name' => $notice_name, }); diff --git a/httemplate/view/cust_bill.cgi b/httemplate/view/cust_bill.cgi index 95ce60b..4822ab7 100755 --- a/httemplate/view/cust_bill.cgi +++ b/httemplate/view/cust_bill.cgi @@ -104,12 +104,35 @@ % my $br = 0; % if ( $cust_bill->num_cust_event ) { $br++; <A HREF="<%$p%>search/cust_event.html?invnum=<% $cust_bill->invnum %>">( <% mt('View invoice events') |h %> )</A> -% } +% } % if ( $cust_bill->num_cust_bill_event ) { $br++; <A HREF="<%$p%>search/cust_bill_event.cgi?invnum=<% $cust_bill->invnum %>">( <% mt('View deprecated, old-style invoice events') |h %> )</A> % } +% my @modes = grep {! $_->disabled} +% $cust_bill->cust_main->agent->invoice_modes; +% if ( @modes ) { +( <% mt('View as:') %> +<FORM STYLE="display:inline" ACTION="<% $cgi->url %>" METHOD="GET"> +<INPUT NAME="invnum" VALUE="<% $invnum %>" TYPE="hidden"> +<& /elements/select-table.html, + table => 'invoice_mode', + field => 'mode', + curr_value => scalar($cgi->param('mode')), + records => \@modes, + name_col => 'modename', + onchange => 'change_invoice_mode', + empty_label => '(default)', +&> ) +<SCRIPT TYPE="text/javascript"> +function change_invoice_mode(obj) { + obj.form.submit(); +} +</SCRIPT> +% $br++; +% } + <% $br ? '<BR><BR>' : '' %> % if ( $conf->exists('invoice_html') ) { @@ -126,7 +149,9 @@ my $curuser = $FS::CurrentUser::CurrentUser; die "access denied" unless $curuser->access_right('View invoices'); -my( $invnum, $template, $notice_name ); +my $conf = FS::Conf->new; + +my( $invnum, $mode, $template, $notice_name ); my($query) = $cgi->keywords; if ( $query =~ /^((.+)-)?(\d+)$/ ) { $template = $2; @@ -136,10 +161,9 @@ if ( $query =~ /^((.+)-)?(\d+)$/ ) { $invnum = $cgi->param('invnum'); $template = $cgi->param('template'); $notice_name = $cgi->param('notice_name'); + $mode = $cgi->param('mode'); } -my $conf = new FS::Conf; - my %opt = ( 'unsquelch_cdr' => $conf->exists('voip-cdr_email'), 'template' => $template, @@ -163,10 +187,13 @@ my $cust_bill = qsearchs({ }); die "Invoice #$invnum not found!" unless $cust_bill; +$cust_bill->set('mode' => $mode); + my $custnum = $cust_bill->custnum; my $display_custnum = $cust_bill->cust_main->display_custnum; my $link = "invnum=$invnum"; +$link .= ';mode=' . $mode if $mode; $link .= ';template='. uri_escape($template) if $template; $link .= ';notice_name='. $notice_name if $notice_name; diff --git a/httemplate/view/cust_statement.html b/httemplate/view/cust_statement.html index 3e1345e..5d37b31 100755 --- a/httemplate/view/cust_statement.html +++ b/httemplate/view/cust_statement.html @@ -35,10 +35,10 @@ % if ( $conf->exists('invoice_html') ) { - <% join('', $cust_statement->print_html('', $templatename) ) %> + <% join('', $cust_statement->print_html('template' => $templatename) ) %> % } else { - <PRE><% join('', $cust_statement->print_text('', $templatename) ) %></PRE> + <PRE><% join('', $cust_statement->print_text('template' => $templatename) ) %></PRE> % } <% include('/elements/footer.html') %> diff --git a/httemplate/view/elements/cust_bill-typeset b/httemplate/view/elements/cust_bill-typeset index 00f503f..778e538 100644 --- a/httemplate/view/elements/cust_bill-typeset +++ b/httemplate/view/elements/cust_bill-typeset @@ -6,7 +6,7 @@ die "access denied" my $type = shift; -my( $invnum, $template, $notice_name ); +my( $invnum, $mode, $template, $notice_name ); my($query) = $cgi->keywords; if ( $query =~ /^((.+)-)?(\d+)(.pdf)?$/ ) { #probably not necessary anymore? $template = $2; @@ -16,7 +16,8 @@ if ( $query =~ /^((.+)-)?(\d+)(.pdf)?$/ ) { #probably not necessary anymore? $invnum = $cgi->param('invnum'); $invnum =~ s/\.pdf//i; #probably not necessary anymore $template = $cgi->param('template'); - $notice_name = ( $cgi->param('notice_name') || 'Invoice' ); + $notice_name = $cgi->param('notice_name'); + $mode = $cgi->param('mode'); } my $conf = new FS::Conf; @@ -36,6 +37,8 @@ my $cust_bill = qsearchs({ }); die "Invoice #$invnum not found!" unless $cust_bill; +$cust_bill->set(mode => $mode); + my $method = "print_$type"; my $content = $cust_bill->$method(\%opt); |