summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
authorMark Wells <mark@freeside.biz>2013-09-27 17:29:17 -0700
committerMark Wells <mark@freeside.biz>2013-09-27 17:29:17 -0700
commit1d6a32338660e3d7202faa7e4ce14736b4569a48 (patch)
tree681f6a7887f43974b7e58508e5aeb5b59db531b7 /FS
parentc64ff892b9067f7aa719e78b20379d3274907946 (diff)
invoice configurations, #24723
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/Mason.pm2
-rw-r--r--FS/FS/Schema.pm45
-rw-r--r--FS/FS/Template_Mixin.pm117
-rw-r--r--FS/FS/agent.pm17
-rw-r--r--FS/FS/cust_bill.pm274
-rw-r--r--FS/FS/cust_main.pm4
-rw-r--r--FS/FS/invoice_conf.pm274
-rw-r--r--FS/FS/invoice_mode.pm157
-rw-r--r--FS/FS/part_event/Action/cust_bill_email.pm10
-rw-r--r--FS/FS/part_event/Action/cust_bill_print.pm9
-rw-r--r--FS/FS/part_event/Action/cust_bill_print_pdf.pm9
-rw-r--r--FS/FS/part_event/Action/cust_bill_send.pm9
-rw-r--r--FS/FS/part_event/Action/cust_bill_send_agent.pm17
-rw-r--r--FS/FS/part_event/Action/cust_bill_send_alternate.pm5
-rw-r--r--FS/FS/part_event/Action/cust_bill_send_if_newest.pm18
-rw-r--r--FS/FS/part_event/Action/cust_bill_send_reminder.pm12
-rw-r--r--FS/FS/part_event/Action/cust_statement_send.pm3
-rw-r--r--FS/MANIFEST4
-rw-r--r--FS/t/invoice_conf.t5
-rw-r--r--FS/t/invoice_mode.t5
20 files changed, 742 insertions, 254 deletions
diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
index a946ebf9f..9c6a0fba2 100644
--- a/FS/FS/Mason.pm
+++ b/FS/FS/Mason.pm
@@ -342,6 +342,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 9927eb3d6..05699dee4 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -4208,6 +4208,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 356de5bed..840df7558 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 68a5912f2..b46d447aa 100644
--- a/FS/FS/agent.pm
+++ b/FS/FS/agent.pm
@@ -354,6 +354,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 97dd38be5..a747a782d 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 7dc6ac4f9..ec13cb967 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -4905,9 +4905,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 000000000..043cab03c
--- /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 000000000..115dd4469
--- /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 1a3bca4b7..3331a4cb6 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 6b3e6f460..ea6e0aa8e 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 6b37f389f..6c01d4294 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 587a7c664..c6928dc00 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 670a32c5b..bbb757b59 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 cfd9264d8..fb71a5a39 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 083da8b08..c744362ce 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 073bb8fd3..354f969d4 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 74cc48ca8..67a94aaa1 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 0ef0fa39c..ed1a0cdcf 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -698,3 +698,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 000000000..b707fa3f0
--- /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 000000000..5f945f0d4
--- /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";