use FS::cust_main_Mixin;
use FS::cust_main;
use FS::cust_bill_pkg;
+use FS::cust_bill_pkg_display;
use FS::cust_credit;
use FS::cust_pay;
use FS::cust_pkg;
sub cust_bill_pkg {
my $self = shift;
- qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum } );
+ qsearch(
+ { 'table' => 'cust_bill_pkg',
+ 'hashref' => { 'invnum' => $self->invnum },
+ 'order_by' => 'ORDER BY billpkgnum',
+ }
+ );
}
=item cust_pkg
}
-=item generate_email PARAMHASH
+=item generate_email OPTION => VALUE ...
-PARAMHASH can contain the following:
+Options:
=over 4
-=item from => sender address, required
+=item from
+
+sender address, required
+
+=item tempate
+
+alternate template name, optional
-=item tempate => alternate template name, optional
+=item print_text
-=item print_text => text attachment arrayref, optional
+text attachment arrayref, optional
-=item subject => email subject, optional
+=item subject
+
+email subject, optional
=back
my $from = $1 || 'example.com';
my $content_id = join('.', rand()*(2**32), $$, time). "\@$from";
- my $path = "$FS::UID::conf_dir/conf.$FS::UID::datasrc";
- my $file;
+ my $logo;
+ my $agentnum = $self->cust_main->agentnum;
if ( defined($args{'template'}) && length($args{'template'})
- && -e "$path/logo_". $args{'template'}. ".png"
+ && $conf->exists( 'logo_'. $args{'template'}. '.png', $agentnum )
)
{
- $file = "$path/logo_". $args{'template'}. ".png";
+ $logo = 'logo_'. $args{'template'}. '.png';
} else {
- $file = "$path/logo.png";
+ $logo = "logo.png";
}
+ my $image_data = $conf->config_binary( $logo, $agentnum);
my $image = build MIME::Entity
'Type' => 'image/png',
'Encoding' => 'base64',
- 'Path' => $file,
+ 'Data' => $image_data,
'Filename' => 'logo.png',
'Content-ID' => "<$content_id>",
;
'Encoding' => 'base64',
'Data' => [ $self->print_pdf(@_) ],
'Disposition' => 'attachment',
- 'Filename' => 'invoice.pdf',
+ 'Filename' => 'invoice-'. $self->invnum. '.pdf',
);
}
my $invoice_from =
scalar(@_)
? shift
- : ( $self->_agent_invoice_from || $conf->config('invoice_from') );
+ : ( $self->_agent_invoice_from || #XXX should go away
+ $conf->config('invoice_from', $self->cust_main->agentnum )
+ );
my $balance_over = ( scalar(@_) && $_[0] !~ /^\s*$/ ) ? shift : 0;
my $invoice_from =
scalar(@_)
? shift
- : ( $self->_agent_invoice_from || $conf->config('invoice_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;
#better to notify this person than silence
@invoicing_list = ($invoice_from) unless @invoicing_list;
+ my $subject = $self->email_subject($template);
+
my $error = send_email(
$self->generate_email(
'from' => $invoice_from,
'to' => [ grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ],
+ 'subject' => $subject,
'template' => $template,
)
);
}
+sub email_subject {
+ my $self = shift;
+
+ #my $template = scalar(@_) ? shift : '';
+ #per-template?
+
+ my $subject = $conf->config('invoice_subject', $self->cust_main->agentnum)
+ || 'Invoice';
+
+ my $cust_main = $self->cust_main;
+ my $name = $cust_main->name;
+ my $name_short = $cust_main->name_short;
+ my $invoice_number = $self->invnum;
+ my $invoice_date = $self->_date_pretty;
+
+ eval qq("$subject");
+}
+
=item lpr_data [ TEMPLATENAME ]
Returns the postscript or plaintext for this invoice as an arrayref.
}
+=item ftp_invoice [ TEMPLATENAME ]
+
+Sends this invoice data via FTP.
+
+TEMPLATENAME is unused?
+
+=cut
+
+sub ftp_invoice {
+ my $self = shift;
+ my $template = scalar(@_) ? shift : '';
+
+ $self->send_csv(
+ 'protocol' => 'ftp',
+ 'server' => $conf->config('cust_bill-ftpserver'),
+ 'username' => $conf->config('cust_bill-ftpusername'),
+ 'password' => $conf->config('cust_bill-ftppassword'),
+ 'dir' => $conf->config('cust_bill-ftpdir'),
+ 'format' => $conf->config('cust_bill-ftpformat'),
+ );
+}
+
+=item spool_invoice [ TEMPLATENAME ]
+
+Spools this invoice data (see L<FS::spool_csv>)
+
+TEMPLATENAME is unused?
+
+=cut
+
+sub spool_invoice {
+ my $self = shift;
+ my $template = scalar(@_) ? shift : '';
+
+ $self->spool_csv(
+ 'format' => $conf->config('cust_bill-spoolformat'),
+ 'agent_spools' => $conf->exists('cust_bill-spoolagent'),
+ );
+}
+
=item send_if_newest [ TEMPLATENAME [ , AGENTNUM [ , INVOICE_FROM ] ] ]
Like B<send>, but only sends the invoice if it is the newest open invoice for
=cut
sub print_latex {
-
my( $self, $today, $template ) = @_;
my %params = ( 'format' => 'latex' );
UNLINK => 0,
) or die "can't open temp file: $!\n";
- if ($template && $conf->exists("logo_${template}.eps")) {
- print $lh $conf->config_binary("logo_${template}.eps")
+ my $agentnum = $self->cust_main->agentnum;
+
+ if ( $template && $conf->exists("logo_${template}.eps", $agentnum) ) {
+ print $lh $conf->config_binary("logo_${template}.eps", $agentnum)
or die "can't write temp file: $!\n";
- }else{
- print $lh $conf->config_binary('logo.eps')
+ } else {
+ print $lh $conf->config_binary('logo.eps', $agentnum)
or die "can't write temp file: $!\n";
}
close $lh;
cid -
+unsquelch_cdr - overrides any per customer cdr squelching when true
+
=cut
+#what's with all the sprintf('%10.2f')'s in here? will it cause any
+# (alignment?) problems to change them all to '%.2f' ?
sub print_generic {
my( $self, %params ) = @_;
my $cust_main = $self->cust_main;
$cust_main->payname( $cust_main->first. ' '. $cust_main->getfield('last') )
- unless $cust_main->payname && $cust_main->payby !~ /^(CHEK|DCHK)$/;
-
+ unless $cust_main->payname
+ && $cust_main->payby !~ /^(CARD|DCRD|CHEK|DCHK)$/;
my %delimiters = ( 'latex' => [ '[@--', '--@]' ],
'html' => [ '<%=', '%>' ],
s/~/ /g;
s/\\\\\*?\s*$/<BR>/;
s/\\hyphenation\{[\w\s\-]+}//;
+ s/\\([&])/$1/g;
$_;
} @_
},
)
)
);
- } elsif ( grep /\S/, $conf->config('company_address') ) {
+ } elsif ( grep /\S/, $conf->config('company_address', $self->cust_main->agentnum) ) {
my $convert_map = $convert_maps{$format}{'returnaddress'};
$returnaddress = join( "\n", &$convert_map(
s/$/\\\\\*/;
$_
}
- ( $conf->config('company_name'),
- $conf->config('company_address'),
+ ( $conf->config('company_name', $self->cust_main->agentnum),
+ $conf->config('company_address', $self->cust_main->agentnum),
)
)
);
}
my %invoice_data = (
- 'company_name' => scalar( $conf->config('company_name') ),
- 'company_address' => join("\n", $conf->config('company_address') ). "\n",
- 'custnum' => $self->custnum,
+ 'company_name' => scalar( $conf->config('company_name', $self->cust_main->agentnum) ),
+ 'company_address' => join("\n", $conf->config('company_address', $self->cust_main->agentnum) ). "\n",
+ 'custnum' => $cust_main->display_custnum,
'invnum' => $self->invnum,
'date' => time2str($date_format, $self->_date),
'today' => time2str('%b %o, %Y', $today),
'city' => &$escape_function($cust_main->city),
'state' => &$escape_function($cust_main->state),
'zip' => &$escape_function($cust_main->zip),
+ 'fax' => &$escape_function($cust_main->fax),
'returnaddress' => $returnaddress,
#'quantity' => 1,
'terms' => $self->terms,
- 'template' => $params{'template'},
+ 'template' => $template, #params{'template'},
#'notes' => join("\n", $conf->config('invoice_latexnotes') ),
# better hang on to conf_dir for a while
'conf_dir' => "$FS::UID::conf_dir/conf.$FS::UID::datasrc",
'page' => 1,
'total_pages' => 1,
+ 'current_charges' => sprintf("%.2f", $self->charged),
+ 'duedate' => $self->due_date2str('%m/%d/%Y'), #date_format?
'ship_enable' => $conf->exists('invoice-ship_address'),
'unitprices' => $conf->exists('invoice-unitprice'),
);
+ my $countrydefault = $conf->config('countrydefault') || 'US';
my $prefix = $cust_main->has_ship_address ? 'ship_' : '';
foreach ( qw( contact company address1 address2 city state zip country fax) ){
my $method = $prefix.$_;
$invoice_data{"ship_$_"} = _latex_escape($cust_main->$method);
}
+ $invoice_data{'ship_country'} = ''
+ if ( $invoice_data{'ship_country'} eq $countrydefault );
$invoice_data{'cid'} = $params{'cid'}
if $params{'cid'};
- my $countrydefault = $conf->config('countrydefault') || 'US';
if ( $cust_main->country eq $countrydefault ) {
$invoice_data{'country'} = '';
} else {
# my( $cr_total, @cr_cust_credit ) = $self->cust_credit; #credits
#my $balance_due = $self->owed + $pr_total - $cr_total;
my $balance_due = $self->owed + $pr_total;
- $invoice_data{'balance'} = $balance_due;
+ $invoice_data{'previous_balance'} = sprintf("%.2f", $pr_total);
+ $invoice_data{'balance'} = sprintf("%.2f", $balance_due);
+
+ my $agentnum = $self->cust_main->agentnum;
#do variable substitution in notes, footer, smallfooter
foreach my $include (qw( notes footer smallfooter coupon )) {
my $inc_file = $conf->key_orbase("invoice_${format}$include", $template);
my @inc_src;
- if ( $conf->exists($inc_file) && length( $conf->config($inc_file) ) ) {
+ if ( $conf->exists($inc_file, $agentnum)
+ && length( $conf->config($inc_file, $agentnum) ) ) {
- @inc_src = $conf->config($inc_file);
+ @inc_src = $conf->config($inc_file, $agentnum);
} else {
s/--\@\]/$delimiters{$format}[1]/g;
$_;
}
- &$convert_map( $conf->config($inc_file) );
+ &$convert_map( $conf->config($inc_file, $agentnum) );
}
);
my $money_char = $money_chars{$format};
- my %other_money_chars = ( 'latex' => '\dollar ',
+ my %other_money_chars = ( 'latex' => '\dollar ',#XXX should be a config too
'html' => $conf->config('money_char') || '$',
'template' => '',
);
my $adjust_section = { 'description' => 'Credits, Payments, and Adjustments',
'subtotal' => 0 }; # adjusted below
+ my $unsquelched = $params{unsquelch_cdr} || $cust_main->squelch_cdr ne 'Y';
my $multisection = $conf->exists('invoice_sections', $cust_main->agentnum);
+ my $late_sections = [];
if ( $multisection ) {
- push @sections, $self->_items_sections;
+ push @sections, $self->_items_sections( $late_sections );
}else{
push @sections, { 'description' => '', 'subtotal' => '' };
}
- foreach my $line_item ( $conf->exists('disable_previous_balance')
- ? ()
- : $self->_items_previous
- )
+ unless ( $conf->exists('disable_previous_balance')
+ || $conf->exists('previous_balance-summary_only')
+ )
{
- my $detail = {
- ext_description => [],
- };
- $detail->{'ref'} = $line_item->{'pkgnum'};
- $detail->{'quantity'} = 1;
- $detail->{'section'} = $previous_section;
- $detail->{'description'} = &$escape_function($line_item->{'description'});
- if ( exists $line_item->{'ext_description'} ) {
- @{$detail->{'ext_description'}} = map {
- &$escape_function($_);
- } @{$line_item->{'ext_description'}};
+
+ foreach my $line_item ( $self->_items_previous ) {
+
+ my $detail = {
+ ext_description => [],
+ };
+ $detail->{'ref'} = $line_item->{'pkgnum'};
+ $detail->{'quantity'} = 1;
+ $detail->{'section'} = $previous_section;
+ $detail->{'description'} = &$escape_function($line_item->{'description'});
+ if ( exists $line_item->{'ext_description'} ) {
+ @{$detail->{'ext_description'}} = map {
+ &$escape_function($_);
+ } @{$line_item->{'ext_description'}};
+ }
+ $detail->{'amount'} = ( $old_latex ? '' : $money_char).
+ $line_item->{'amount'};
+ $detail->{'product_code'} = $line_item->{'pkgpart'} || 'N/A';
+
+ push @detail_items, $detail;
+ push @buf, [ $detail->{'description'},
+ $money_char. sprintf("%10.2f", $line_item->{'amount'}),
+ ];
}
- $detail->{'amount'} = ( $old_latex ? '' : $money_char).
- $line_item->{'amount'};
- $detail->{'product_code'} = $line_item->{'pkgpart'} || 'N/A';
-
- push @detail_items, $detail;
- push @buf, [ $detail->{'description'},
- $money_char. sprintf("%10.2f", $line_item->{'amount'}),
- ];
+
}
-
+
if ( @pr_cust_bill && !$conf->exists('disable_previous_balance') ) {
push @buf, ['','-----------'];
push @buf, [ 'Total Previous Balance',
push @buf, ['',''];
}
- foreach my $section (@sections) {
+ foreach my $section (@sections, @$late_sections) {
$section->{'subtotal'} = $other_money_char.
sprintf('%.2f', $section->{'subtotal'})
$options{'section'} = $section if $multisection;
$options{'format'} = $format;
$options{'escape_function'} = $escape_function;
+ $options{'format_function'} = sub { () } unless $unsquelched;
+ $options{'unsquelched'} = $unsquelched;
foreach my $line_item ( $self->_items_pkg(%options) ) {
my $detail = {
}
foreach my $tax ( $self->_items_tax ) {
- my $total = {};
- $total->{'total_item'} = &$escape_function($tax->{'description'});
+
$taxtotal += $tax->{'amount'};
- $total->{'total_amount'} = $other_money_char. $tax->{'amount'};
+
+ my $description = &$escape_function( $tax->{'description'} );
+ my $amount = sprintf( '%.2f', $tax->{'amount'} );
+
if ( $multisection ) {
+
my $money = $old_latex ? '' : $money_char;
push @detail_items, {
ext_description => [],
ref => '',
quantity => '',
- description => &$escape_function($tax->{'description'}),
- amount => $money. $tax->{'amount'},
+ description => $description,
+ amount => $money. $amount,
product_code => '',
section => $tax_section,
};
- }else{
- push @total_items, $total;
+
+ } else {
+
+ push @total_items, {
+ 'total_item' => $description,
+ 'total_amount' => $other_money_char. $amount,
+ };
+
}
- push @buf,[ $total->{'total_item'},
- $money_char. sprintf("%10.2f", $total->{'total_amount'}),
+
+ push @buf,[ $description,
+ $money_char. $amount,
];
}
unshift @total_items, $total;
}
}
+ $invoice_data{'taxtotal'} = sprintf('%.2f', $taxtotal);
push @buf,['','-----------'];
push @buf,[( $conf->exists('disable_previous_balance')
)
);
if ( $multisection ) {
- $adjust_section->{'pretotal'} = 'New charges total '.
- $total->{'total_amount'};
+ $adjust_section->{'pretotal'} = 'New charges total '. $other_money_char.
+ sprintf('%.2f', $self->charged );
}else{
push @total_items, $total;
}
#foreach my $thing ( sort { $a->_date <=> $b->_date } $self->_items_credits, $self->_items_payments
# credits
- foreach my $credit ( $self->_items_credits ) {
+ my $credittotal = 0;
+ foreach my $credit ( $self->_items_credits('trim_len'=>60) ) {
+
my $total;
$total->{'total_item'} = &$escape_function($credit->{'description'});
- #$credittotal
+ $credittotal += $credit->{'amount'};
$total->{'total_amount'} = '-'. $other_money_char. $credit->{'amount'};
$adjusttotal += $credit->{'amount'};
if ( $multisection ) {
product_code => '',
section => $adjust_section,
};
- }else{
+ } else {
push @total_items, $total;
}
+
}
-
- # credits (again)
- foreach ( $self->cust_credited ) {
-
- #something more elaborate if $_->amount ne $_->cust_credit->credited ?
-
- my $reason = substr($_->cust_credit->reason,0,32);
- $reason .= '...' if length($reason) < length($_->cust_credit->reason);
- $reason = " ($reason) " if $reason;
- push @buf,[
- "Credit #". $_->crednum. " (". time2str("%x",$_->cust_credit->_date) .")". $reason,
- $money_char. sprintf("%10.2f",$_->amount)
- ];
- }
+ $invoice_data{'credittotal'} = sprintf('%.2f', $credittotal);
+ #credits (again)
+ foreach my $credit ( $self->_items_credits('trim_len'=>32) ) {
+ push @buf, [ $credit->{'description'}, $money_char.$credit->{'amount'} ];
+ }
+
# payments
+ my $paymenttotal = 0;
foreach my $payment ( $self->_items_payments ) {
my $total = {};
$total->{'total_item'} = &$escape_function($payment->{'description'});
- #$paymenttotal
+ $paymenttotal += $payment->{'amount'};
$total->{'total_amount'} = '-'. $other_money_char. $payment->{'amount'};
$adjusttotal += $payment->{'amount'};
if ( $multisection ) {
$money_char. sprintf("%10.2f", $payment->{'amount'}),
];
}
+ $invoice_data{'paymenttotal'} = sprintf('%.2f', $paymenttotal);
if ( $multisection ) {
$adjust_section->{'subtotal'} = $other_money_char.
}
}
+ if ( $multisection ) {
+ push @sections, @$late_sections
+ if $unsquelched;
+ }
+
$invoice_lines = 0;
my $wasfunc = 0;
foreach ( grep /invoice_lines\(\d*\)/, @invoice_template ) { #kludgy
=cut
sub print_html {
- my( $self, $today, $template, $cid ) = @_;
+ my $self = shift;
+ my %params;
+ if ( ref $_[0] ) {
+ %params = %{ shift() };
+ }else{
+ $params{'time'} = shift;
+ $params{'template'} = shift;
+ $params{'cid'} = shift;
+ }
- my %params = ( 'format' => 'html' );
- $params{'time'} = $today if $today;
- $params{'template'} = $template if $template;
- $params{'cid'} = $cid if $cid;
+ $params{'format'} = 'html';
$self->print_generic( %params );
}
return $self->cust_main->invoice_terms
if $self->cust_main->invoice_terms;
- #use configured default or default default
- $conf->config('invoice_default_terms') || 'Payable upon receipt';
+ #use configured default
+ $conf->config('invoice_default_terms') || '';
}
sub due_date {
$msg;
}
+sub balance_due_date {
+ my $self = shift;
+ my $duedate = '';
+ if ( $conf->exists('invoice_default_terms')
+ && $conf->config('invoice_default_terms')=~ /^\s*Net\s*(\d+)\s*$/ ) {
+ $duedate = time2str("%m/%d/%Y", $self->_date + ($1*86400) );
+ }
+ $duedate;
+}
+
=item invnum_date_pretty
Returns a string with the invoice number and date, for example:
sub invnum_date_pretty {
my $self = shift;
- 'Invoice #'. $self->invnum. ' ('. time2str('%x', $self->_date). ')';
+ 'Invoice #'. $self->invnum. ' ('. $self->_date_pretty. ')';
+}
+
+=item _date_pretty
+
+Returns a string with the date, for example: "3/20/2008"
+
+=cut
+
+sub _date_pretty {
+ my $self = shift;
+ time2str('%x', $self->_date);
}
sub _items_sections {
my $self = shift;
+ my $late = shift;
my %s = ();
- foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) {
+ my %l = ();
- if ( $cust_bill_pkg->pkgnum > 0 ) {
+ foreach my $cust_bill_pkg ( $self->cust_bill_pkg )
+ {
- my $desc = $cust_bill_pkg->part_pkg->classname;
+ if ( $cust_bill_pkg->pkgnum > 0 ) {
+ my $usage = $cust_bill_pkg->usage;
+
+ foreach my $display ($cust_bill_pkg->cust_bill_pkg_display) {
+ my $desc = $display->section;
+ my $type = $display->type;
+
+ if ( $display->post_total ) {
+ if (! $type || $type eq 'S') {
+ $l{$desc} += $cust_bill_pkg->setup
+ if ( $cust_bill_pkg->setup != 0 );
+ }
+
+ if (! $type) {
+ $l{$desc} += $cust_bill_pkg->recur
+ if ( $cust_bill_pkg->recur != 0 );
+ }
+
+ if ($type && $type eq 'R') {
+ $l{$desc} += $cust_bill_pkg->recur - $usage
+ if ( $cust_bill_pkg->recur != 0 );
+ }
+
+ if ($type && $type eq 'U') {
+ $l{$desc} += $usage;
+ }
+
+ } else {
+ if (! $type || $type eq 'S') {
+ $s{$desc} += $cust_bill_pkg->setup
+ if ( $cust_bill_pkg->setup != 0 );
+ }
+
+ if (! $type) {
+ $s{$desc} += $cust_bill_pkg->recur
+ if ( $cust_bill_pkg->recur != 0 );
+ }
+
+ if ($type && $type eq 'R') {
+ $s{$desc} += $cust_bill_pkg->recur - $usage
+ if ( $cust_bill_pkg->recur != 0 );
+ }
+
+ if ($type && $type eq 'U') {
+ $s{$desc} += $usage;
+ }
- $s{$desc} += $cust_bill_pkg->setup
- if ( $cust_bill_pkg->setup != 0 );
+ }
- $s{$desc} += $cust_bill_pkg->recur
- if ( $cust_bill_pkg->recur != 0 );
+ }
}
}
+ push @$late, map { { 'description' => $_,
+ 'subtotal' => $l{$_},
+ 'post_total' => 1,
+ } } sort keys %l;
+
map { {'description' => $_, 'subtotal' => $s{$_}} } sort keys %s;
}
sub _items_pkg {
my $self = shift;
- my %options = @_;
- my $section = delete $options{'section'};
- my @cust_bill_pkg =
- grep { $_->pkgnum &&
- ( defined($section)
- ? $_->part_pkg->classname eq $section->{'description'}
- : 1
- )
- } $self->cust_bill_pkg;
- $self->_items_cust_bill_pkg(\@cust_bill_pkg, %options);
+ my @cust_bill_pkg = grep { $_->pkgnum } $self->cust_bill_pkg;
+ $self->_items_cust_bill_pkg(\@cust_bill_pkg, @_);
}
sub _taxsort {
my $format = $opt{format} || '';
my $escape_function = $opt{escape_function} || sub { shift };
+ my $format_function = $opt{format_function} || '';
+ my $unsquelched = $opt{unsquelched} || '';
+ my $section = $opt{section}->{description} if $opt{section};
my @b = ();
- foreach my $cust_bill_pkg ( @$cust_bill_pkg ) {
+ foreach my $cust_bill_pkg ( @$cust_bill_pkg )
+ {
+ foreach my $display ( grep { defined($section)
+ ? $_->section eq $section
+ : 1
+ }
+ $cust_bill_pkg->cust_bill_pkg_display
+ )
+ {
- my $cust_pkg = $cust_bill_pkg->cust_pkg;
+ my $type = $display->type;
- my $desc = $cust_bill_pkg->desc;
+ my $cust_pkg = $cust_bill_pkg->cust_pkg;
- my %details_opt = ( 'format' => $format,
- 'escape_function' => $escape_function,
- );
+ my $desc = $cust_bill_pkg->desc;
+ $desc = substr($desc, 0, 50). '...'
+ if $format eq 'latex' && length($desc) > 50;
- if ( $cust_bill_pkg->pkgnum > 0 ) {
+ my %details_opt = ( 'format' => $format,
+ 'escape_function' => $escape_function,
+ 'format_function' => $format_function,
+ );
- if ( $cust_bill_pkg->setup != 0 ) {
+ if ( $cust_bill_pkg->pkgnum > 0 ) {
- my $description = $desc;
- $description .= ' Setup' if $cust_bill_pkg->recur != 0;
+ if ( $cust_bill_pkg->setup != 0 && (!$type || $type eq 'S') ) {
- my @d = map &{$escape_function}($_),
- $cust_pkg->h_labels_short($self->_date);
- push @d, $cust_bill_pkg->details(%details_opt)
- if $cust_bill_pkg->recur == 0;
+ my $description = $desc;
+ $description .= ' Setup' if $cust_bill_pkg->recur != 0;
- push @b, {
- description => $description,
- #pkgpart => $part_pkg->pkgpart,
- pkgnum => $cust_bill_pkg->pkgnum,
- amount => sprintf("%.2f", $cust_bill_pkg->setup),
- unit_amount => sprintf("%.2f", $cust_bill_pkg->unitsetup),
- quantity => $cust_bill_pkg->quantity,
- ext_description => \@d,
- };
- }
+ my @d = ();
+ push @d, map &{$escape_function}($_),
+ $cust_pkg->h_labels_short($self->_date)
+ unless $cust_pkg->part_pkg->hide_svc_detail;
+ push @d, $cust_bill_pkg->details(%details_opt)
+ if $cust_bill_pkg->recur == 0;
- if ( $cust_bill_pkg->recur != 0 ) {
+ push @b, {
+ description => $description,
+ #pkgpart => $part_pkg->pkgpart,
+ pkgnum => $cust_bill_pkg->pkgnum,
+ amount => sprintf("%.2f", $cust_bill_pkg->setup),
+ unit_amount => sprintf("%.2f", $cust_bill_pkg->unitsetup),
+ quantity => $cust_bill_pkg->quantity,
+ ext_description => \@d,
+ };
- my $description = $desc;
- unless ( $conf->exists('disable_line_item_date_ranges') ) {
- $desc .= " (" . time2str("%x", $cust_bill_pkg->sdate).
- " - ". time2str("%x", $cust_bill_pkg->edate). ")";
}
- #at least until cust_bill_pkg has "past" ranges in addition to
- #the "future" sdate/edate ones... see #3032
- my @d = map &{$escape_function}($_),
- $cust_pkg->h_labels_short($self->_date);
- #$cust_bill_pkg->edate,
- #$cust_bill_pkg->sdate),
- push @d, $cust_bill_pkg->details(%details_opt);
-
- push @b, {
- description => $description,
- #pkgpart => $part_pkg->pkgpart,
- pkgnum => $cust_bill_pkg->pkgnum,
- amount => sprintf("%.2f", $cust_bill_pkg->recur),
- unit_amount => sprintf("%.2f", $cust_bill_pkg->unitrecur),
- quantity => $cust_bill_pkg->quantity,
- ext_description => \@d,
- };
+ if ( $cust_bill_pkg->recur != 0 &&
+ ( !$type || $type eq 'R' || $type eq 'U' )
+ )
+ {
+
+ my $is_summary = $display->summary;
+ my $description = $is_summary ? "Usage charges" : $desc;
+
+ unless ( $conf->exists('disable_line_item_date_ranges') ) {
+ $description .= " (" . time2str("%x", $cust_bill_pkg->sdate).
+ " - ". time2str("%x", $cust_bill_pkg->edate). ")";
+ }
+
+ my @d = ();
+
+ #at least until cust_bill_pkg has "past" ranges in addition to
+ #the "future" sdate/edate ones... see #3032
+ my @dates = ( $self->_date );
+ my $prev = $cust_bill_pkg->previous_cust_bill_pkg;
+ push @dates, $prev->sdate if $prev;
+
+ push @d, map &{$escape_function}($_),
+ $cust_pkg->h_labels_short(@dates)
+ #$cust_bill_pkg->edate,
+ #$cust_bill_pkg->sdate)
+ unless $cust_pkg->part_pkg->hide_svc_detail
+ || $cust_bill_pkg->itemdesc
+ || $is_summary;
+
+ push @d, $cust_bill_pkg->details(%details_opt)
+ unless ($is_summary || $type && $type eq 'R');
+
+ my $amount = 0;
+ if (!$type) {
+ $amount = $cust_bill_pkg->recur;
+ }elsif($type eq 'R') {
+ $amount = $cust_bill_pkg->recur - $cust_bill_pkg->usage;
+ }elsif($type eq 'U') {
+ $amount = $cust_bill_pkg->usage;
+ }
+
+ push @b, {
+ description => $description,
+ #pkgpart => $part_pkg->pkgpart,
+ pkgnum => $cust_bill_pkg->pkgnum,
+ amount => sprintf("%.2f", $amount),
+ unit_amount => sprintf("%.2f", $cust_bill_pkg->unitrecur),
+ quantity => $cust_bill_pkg->quantity,
+ ext_description => \@d,
+ } unless ( $type eq 'U' && ! $amount );
- }
+ }
- } else { #pkgnum tax or one-shot line item (??)
+ } else { #pkgnum tax or one-shot line item (??)
+
+ if ( $cust_bill_pkg->setup != 0 ) {
+ push @b, {
+ 'description' => $desc,
+ 'amount' => sprintf("%.2f", $cust_bill_pkg->setup),
+ };
+ }
+ if ( $cust_bill_pkg->recur != 0 ) {
+ push @b, {
+ 'description' => "$desc (".
+ time2str("%x", $cust_bill_pkg->sdate). ' - '.
+ time2str("%x", $cust_bill_pkg->edate). ')',
+ 'amount' => sprintf("%.2f", $cust_bill_pkg->recur),
+ };
+ }
- if ( $cust_bill_pkg->setup != 0 ) {
- push @b, {
- 'description' => $desc,
- 'amount' => sprintf("%.2f", $cust_bill_pkg->setup),
- };
- }
- if ( $cust_bill_pkg->recur != 0 ) {
- push @b, {
- 'description' => "$desc (".
- time2str("%x", $cust_bill_pkg->sdate). ' - '.
- time2str("%x", $cust_bill_pkg->edate). ')',
- 'amount' => sprintf("%.2f", $cust_bill_pkg->recur),
- };
}
}
}
sub _items_credits {
- my $self = shift;
+ my( $self, %opt ) = @_;
+ my $trim_len = $opt{'trim_len'} || 60;
my @b;
#credits
#something more elaborate if $_->amount ne $_->cust_credit->credited ?
- my $reason = $_->cust_credit->reason;
- #my $reason = substr($_->cust_credit->reason,0,32);
- #$reason .= '...' if length($reason) < length($_->cust_credit->reason);
+ my $reason = substr($_->cust_credit->reason, 0, $trim_len);
+ $reason .= '...' if length($reason) < length($_->cust_credit->reason);
$reason = " ($reason) " if $reason;
+
push @b, {
#'description' => 'Credit ref\#'. $_->crednum.
# " (". time2str("%x",$_->cust_credit->_date) .")".
'amount' => sprintf("%.2f",$_->amount),
};
}
- #foreach ( @cr_cust_credit ) {
- # push @buf,[
- # "Credit #". $_->crednum. " (" . time2str("%x",$_->_date) .")",
- # $money_char. sprintf("%10.2f",$_->credited)
- # ];
- #}
@b;
=over 4
-=item reprint
+=item process_reprint
=cut
process_re_X('print', @_);
}
-=item reemail
+=item process_reemail
=cut
process_re_X('email', @_);
}
-=item refax
+=item process_refax
=cut
process_re_X('fax', @_);
}
+=item process_reftp
+
+=cut
+
+sub process_reftp {
+ process_re_X('ftp', @_);
+}
+
+=item respool
+
+=cut
+
+sub process_respool {
+ process_re_X('spool', @_);
+}
+
use Storable qw(thaw);
use Data::Dumper;
use MIME::Base64;
'debug' => 1,
} );
+ $method .= '_invoice' unless $method eq 'email' || $method eq 'print';
+
warn " $me re_X $method: ". scalar(@cust_bill). " invoices found\n"
if $DEBUG;