summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS/Conf.pm28
-rw-r--r--FS/FS/cust_bill.pm51
-rw-r--r--FS/FS/cust_main.pm32
-rw-r--r--FS/FS/part_bill_event.pm24
-rwxr-xr-xhttemplate/edit/part_bill_event.cgi60
-rwxr-xr-xhttemplate/edit/process/part_bill_event.cgi5
6 files changed, 176 insertions, 24 deletions
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index 3cddc20..e69a156 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -108,6 +108,22 @@ sub exists {
-e "$dir/$file";
}
+=item config_orbase KEY SUFFIX
+
+Returns the configuration value or values (depending on context) for
+KEY_SUFFIX, if it exists, otherwise for KEY
+
+=cut
+
+sub config_orbase {
+ my( $self, $file, $suffix ) = @_;
+ if ( $self->exists("${file}_$suffix") ) {
+ $self->config("${file}_$suffix");
+ } else {
+ $self->config($file);
+ }
+}
+
=item touch KEY
Creates the specified configuration key if it does not exist.
@@ -197,6 +213,18 @@ sub config_items {
'type' => 'textarea',
}
} glob($self->dir. '/invoice_latex_*')
+ ),
+ ( map {
+ my $basename = basename($_);
+ $basename =~ /^(.*)$/;
+ $basename = $1;
+ new FS::ConfItem {
+ 'key' => $basename,
+ 'section' => 'billing',
+ 'description' => 'Alternate Notes section for LaTeX typeset PostScript invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ }
+ } glob($self->dir. '/invoice_latexnotes_*')
);
}
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
index 7e2c8fb..367e1bc 100644
--- a/FS/FS/cust_bill.pm
+++ b/FS/FS/cust_bill.pm
@@ -316,15 +316,23 @@ sub owed {
$balance;
}
-=item send
+=item send [ TEMPLATENAME [ , AGENTNUM ] ]
Sends this invoice to the destinations configured for this customer: send
emails or print. See L<FS::cust_main_invoice>.
+TEMPLATENAME, if specified, is the name of a suffix for alternate invoices.
+
+AGENTNUM, if specified, means that this invoice will only be sent for customers
+of the specified agent.
+
=cut
sub send {
- my($self,$template) = @_;
+ my $self = shift;
+ my $template = scalar(@_) ? shift : '';
+ return '' if scalar(@_) && $_[0] && $self->cust_main->agentnum ne shift;
+
my @print_text = $self->print_text('', $template);
my @invoicing_list = $self->cust_main->invoicing_list;
@@ -654,6 +662,31 @@ sub batch_card {
'';
}
+sub _agent_template {
+ my $self = shift;
+
+ my $cust_bill_event = qsearchs( 'part_bill_event',
+ {
+ 'payby' => $self->cust_main->payby,
+ 'plan' => 'send_agent',
+ 'eventcode' => { 'op' => 'LIKE',
+ 'value' => '_%, '. $self->cust_main->agentnum. ');' },
+ },
+ '',
+ 'ORDER BY seconds LIMIT 1'
+ );
+
+ return '' unless $cust_bill_event;
+
+ if ( $cust_bill_event->eventcode =~ /\(\s*'(.*)'\s*,\s*(\d+)\s*\)\;$/ ) {
+ return $1;
+ } else {
+ warn "can't parse eventcode for agent-specific invoice template";
+ return '';
+ }
+
+}
+
=item print_text [ TIME [ , TEMPLATE ] ]
Returns an text invoice, as a list of lines.
@@ -798,10 +831,11 @@ sub print_text {
sprintf("%10.2f", $balance_due ) ];
#create the template
+ $template ||= $self->_agent_template;
my $templatefile = 'invoice_template';
- $templatefile .= "_$template" if $template;
+ $templatefile .= "_$template" if length($template);
my @invoice_template = $conf->config($templatefile)
- or die "cannot load config file $templatefile";
+ or die "cannot load config file $templatefile";
$invoice_lines = 0;
my $wasfunc = 0;
foreach ( grep /invoice_lines\(\d*\)/, @invoice_template ) { #kludgy
@@ -923,8 +957,10 @@ sub print_latex {
@buf = ();
#create the template
+ $template ||= $self->_agent_template;
my $templatefile = 'invoice_latex';
- $templatefile .= "_$template" if $template;
+ my $suffix = length($template) ? "_$template" : '';
+ $templatefile .= $suffix;
my @invoice_template = $conf->config($templatefile)
or die "cannot load config file $templatefile";
@@ -954,7 +990,7 @@ sub print_latex {
$invoice_data{'notes'} =
join("\n",
map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b }
- $conf->config('invoice_latexnotes')
+ $conf->config_orbase('invoice_latexnotes', $suffix)
);
$invoice_data{'footer'} =~ s/\n+$//;
@@ -1396,9 +1432,6 @@ The delete method.
print_text formatting (and some logic :/) is in source, but needs to be
slurped in from a file. Also number of lines ($=).
-missing print_ps for a nice postscript copy (maybe HylaFAX-cover-page-style
-or something similar so the look can be completely customized?)
-
=head1 SEE ALSO
L<FS::Record>, L<FS::cust_main>, L<FS::cust_bill_pay>, L<FS::cust_pay>,
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index f275d10..fbd4618 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -1028,6 +1028,38 @@ sub suspend {
grep { $_->suspend } $self->unsuspended_pkgs;
}
+=item suspend_if_pkgpart PKGPART [ , PKGPART ... ]
+
+Suspends all unsuspended packages (see L<FS::cust_pkg>) matching the listed
+PKGPARTs (see L<FS::part_pkg>). Always returns a list: an empty list on
+success or a list of errors.
+
+=cut
+
+sub suspend_if_pkgpart {
+ my $self = shift;
+ my @pkgparts = @_;
+ grep { $_->suspend }
+ grep { my $pkgpart = $_->pkgpart; grep { $pkgpart eq $_ } @pkgparts }
+ $self->unsuspended_pkgs;
+}
+
+=item suspend_unless_pkgpart PKGPART [ , PKGPART ... ]
+
+Suspends all unsuspended packages (see L<FS::cust_pkg>) unless they match the
+listed PKGPARTs (see L<FS::part_pkg>). Always returns a list: an empty list
+on success or a list of errors.
+
+=cut
+
+sub suspend_unless_pkgpart {
+ my $self = shift;
+ my @pkgparts = @_;
+ grep { $_->suspend }
+ grep { my $pkgpart = $_->pkgpart; ! grep { $pkgpart eq $_ } @pkgparts }
+ $self->unsuspended_pkgs;
+}
+
=item cancel [ OPTION => VALUE ... ]
Cancels all uncancelled packages (see L<FS::cust_pkg>) for this customer.
diff --git a/FS/FS/part_bill_event.pm b/FS/FS/part_bill_event.pm
index 86f9294..4774b8d 100644
--- a/FS/FS/part_bill_event.pm
+++ b/FS/FS/part_bill_event.pm
@@ -149,22 +149,20 @@ sub check {
|| $self->ut_textn('plan')
|| $self->ut_anything('plandata')
;
+ #|| $self->ut_snumber('seconds')
return $error if $error;
#quelle kludge
- if ( $self->plandata =~ /^templatename\s+(.*)$/ ) {
- my $name= $1;
- unless ( $conf->exists("invoice_template_$name") ) {
- $conf->set(
- "invoice_template_$name" =>
- join("\n", $conf->config('invoice_template') )
- );
- }
- unless ( $conf->exists("invoice_latex_$name") ) {
- $conf->set(
- "invoice_latex_$name" =>
- join("\n", $conf->config('invoice_latex') )
- );
+ if ( $self->plandata =~ /^(agent_)?templatename\s+(.*)$/m ) {
+ my $name= $2;
+
+ foreach my $file (qw( template latex latexnotes )) {
+ unless ( $conf->exists("invoice_${file}_$name") ) {
+ $conf->set(
+ "invoice_${file}_$name" =>
+ join("\n", $conf->config("invoice_$file") )
+ );
+ }
}
}
diff --git a/httemplate/edit/part_bill_event.cgi b/httemplate/edit/part_bill_event.cgi
index 48ed791..d0fa973 100755
--- a/httemplate/edit/part_bill_event.cgi
+++ b/httemplate/edit/part_bill_event.cgi
@@ -68,6 +68,31 @@ print '<TR><TD ALIGN="right">Action</TD><TD>';
#print ntable();
+sub select_pkgpart {
+ my $label = shift;
+ my $plandata = shift;
+ my %selected = map { $_=>1 } split(/,\s*/, $plandata->{$label});
+ qq(<SELECT NAME="$label" MULTIPLE>).
+ join("\n", map {
+ '<OPTION VALUE="'. $_->pkgpart. '"'.
+ ( $selected{$_->pkgpart} ? ' SELECTED' : '' ).
+ '>'. $_->pkg. ' - '. $_->comment
+ } qsearch('part_pkg', { 'disabled' => '' } ) ).
+ '</SELECT>';
+}
+
+sub select_agentnum {
+ my $plandata = shift;
+ my $agentnum = $plandata->{'agentnum'};
+ '<SELECT NAME="agentnum">'.
+ join("\n", map {
+ '<OPTION VALUE="'. $_->agentnum. '"'.
+ ( $_->agentnum == $agentnum ? ' SELECTED' : '' ).
+ '>'. $_->agent
+ } qsearch('agent', { 'disabled' => '' } ) ).
+ '</SELECT>';
+}
+
#this is pretty kludgy right here.
tie my %events, 'Tie::IxHash',
@@ -84,6 +109,18 @@ tie my %events, 'Tie::IxHash',
'code' => '$cust_main->suspend();',
'weight' => 10,
},
+ 'suspend-if-pkgpart' => {
+ 'name' => 'Suspend packages',
+ 'code' => '$cust_main->suspend_if_pkgpart(%%%if_pkgpart%%%);',
+ 'html' => sub { &select_pkgpart('if_pkgpart', @_) },
+ 'weight' => 10,
+ },
+ 'suspend-unless-pkgpart' => {
+ 'name' => 'Suspend packages except',
+ 'code' => '$cust_main->suspend_unless_pkgpart(%%%unless_pkgpart%%%);',
+ 'html' => sub { &select_pkgpart('unless_pkgpart', @_) },
+ 'weight' => 10,
+ },
'cancel' => {
'name' => 'Cancel',
'code' => '$cust_main->cancel();',
@@ -140,6 +177,26 @@ tie my %events, 'Tie::IxHash',
'weight' => 50,
},
+ 'send_agent' => {
+ 'name' => 'Send invoice (email/print) ',
+ 'code' => '$cust_bill->send(\'%%%agent_templatename%%%\', %%%agentnum%%%);',
+ 'html' => sub {
+ '<TABLE BORDER=0>
+ <TR>
+ <TD ALIGN="right">only for agent </TD>
+ <TD>'. &select_agentnum(@_). '</TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">with template </TD>
+ <TD>
+ <INPUT TYPE="text" NAME="agent_templatename" VALUE="%%%agent_templatename%%%">
+ </TD>
+ </TR>
+ </TABLE>';
+ },
+ 'weight' => 50,
+ },
+
'send_csv_ftp' => {
'name' => 'Upload CSV invoice data to an FTP server',
'code' => '$cust_bill->send_csv( protocol => \'ftp\',
@@ -188,6 +245,9 @@ foreach my $event ( keys %events ) {
my %plandata = map { /^(\w+) (.*)$/; ($1, $2); }
split(/\n/, $part_bill_event->plandata);
my $html = $events{$event}{html};
+ if ( ref($html) eq 'CODE' ) {
+ $html = &{$html}(\%plandata);
+ }
while ( $html =~ /%%%(\w+)%%%/ ) {
my $field = $1;
$html =~ s/%%%$field%%%/$plandata{$field}/;
diff --git a/httemplate/edit/process/part_bill_event.cgi b/httemplate/edit/process/part_bill_event.cgi
index e224bf6..77dcd24 100755
--- a/httemplate/edit/process/part_bill_event.cgi
+++ b/httemplate/edit/process/part_bill_event.cgi
@@ -5,7 +5,7 @@ my $eventpart = $cgi->param('eventpart');
my $old = qsearchs('part_bill_event',{'eventpart'=>$eventpart}) if $eventpart;
#s/days/seconds/
-$cgi->param('seconds', $cgi->param('days') * 86400 );
+$cgi->param('seconds', int( $cgi->param('days') * 86400 ) );
my $error;
if ( ! $cgi->param('plan_weight_eventcode') ) {
@@ -21,7 +21,8 @@ if ( ! $cgi->param('plan_weight_eventcode') ) {
my $plandata = '';
while ( $eventcode =~ /%%%(\w+)%%%/ ) {
my $field = $1;
- my $value = $cgi->param($field);
+ my $value = join(', ', $cgi->param($field) );
+ $cgi->param($field, $value); #in case it errors out
$eventcode =~ s/%%%$field%%%/$value/;
$plandata .= "$field $value\n";
}