use vars qw( @ISA $DEBUG $me $conf $money_char );
use vars qw( $invoice_lines @buf ); #yuck
use Fcntl qw(:flock); #for spool_csv
+use List::Util qw(min max);
use IPC::Run3;
use Date::Format;
use Text::Template 1.20;
qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum } );
}
+=item cust_pkg
+
+Returns the packages (see L<FS::cust_pkg>) corresponding to the line items for
+this invoice.
+
+=cut
+
+sub cust_pkg {
+ my $self = shift;
+ my @cust_pkg = map { $_->cust_pkg } $self->cust_bill_pkg;
+ my %saw = ();
+ grep { ! $saw{$_->pkgnum}++ } @cust_pkg;
+}
+
=item open_cust_bill_pkg
Returns the open line items for this invoice.
$balance;
}
+=item apply_payments_and_credits
+
+=cut
+
+sub apply_payments_and_credits {
+ my $self = shift;
+
+ my @payments = grep { $_->unapplied > 0 } $self->cust_main->cust_pay;
+ my @credits = grep { $_->credited > 0 } $self->cust_main->cust_credit;
+
+ while ( $self->owed > 0 and ( @payments || @credits ) ) {
+
+ my $app = '';
+ if ( @payments && @credits ) {
+
+ #decide which goes first by weight of top (unapplied) line item
+
+ my @open_lineitems = $self->open_cust_bill_pkg;
+
+ my $max_pay_weight =
+ max( map { $_->part_pkg->pay_weight || 0 }
+ grep { $_ }
+ map { $_->cust_pkg }
+ @open_lineitems
+ );
+ my $max_credit_weight =
+ max( map { $_->part_pkg->credit_weight || 0 }
+ grep { $_ }
+ map { $_->cust_pkg }
+ @open_lineitems
+ );
+
+ #if both are the same... payments first? it has to be something
+ if ( $max_pay_weight >= $max_credit_weight ) {
+ $app = 'pay';
+ } else {
+ $app = 'credit';
+ }
+
+ } elsif ( @payments ) {
+ $app = 'pay';
+ } elsif ( @credits ) {
+ $app = 'credit';
+ } else {
+ die "guru meditation #12 and 35";
+ }
+
+ if ( $app eq 'pay' ) {
+
+ my $payment = shift @payments;
+
+ $app = new FS::cust_bill_pay {
+ 'paynum' => $payment->paynum,
+ 'amount' => sprintf('%.2f', min( $payment->unapplied, $self->owed ) ),
+ };
+
+ } elsif ( $app eq 'credit' ) {
+
+ my $credit = shift @credits;
+
+ $app = new FS::cust_credit_bill {
+ 'crednum' => $credit->crednum,
+ 'amount' => sprintf('%.2f', min( $credit->credited, $self->owed ) ),
+ };
+
+ } else {
+ die "guru meditation #12 and 35";
+ }
+
+ $app->invnum( $self->invnum );
+
+ my $error = $app->insert;
+ die $error if $error;
+
+ }
+
+}
=item generate_email PARAMHASH
'Disposition' => 'inline',
);
- $args{'from'} =~ /\@([\w\.\-]+)/ or $1 = 'example.com';
- my $content_id = join('.', rand()*(2**32), $$, time). "\@$1";
+ $args{'from'} =~ /\@([\w\.\-]+)/;
+ 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;
- if ( defined($args{'_template'}) && length($args{'_template'})
- && -e "$path/logo_". $args{'_template'}. ".png"
+ if ( defined($args{'template'}) && length($args{'template'})
+ && -e "$path/logo_". $args{'template'}. ".png"
)
{
- $file = "$path/logo_". $args{'_template'}. ".png";
+ $file = "$path/logo_". $args{'template'}. ".png";
} else {
$file = "$path/logo.png";
}
=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 $template = scalar(@_) ? shift : '';
=cut
+sub queueable_email {
+ my %opt = @_;
+
+ my $self = qsearchs('cust_bill', { 'invnum' => $opt{invnum} } )
+ or die "invalid invoice number: " . $opt{invnum};
+
+ my @args = ( $opt{template} );
+ push @args, $opt{invoice_from}
+ if exists($opt{invoice_from}) && $opt{invoice_from};
+
+ my $error = $self->email( @args );
+ die $error if $error;
+
+}
+
sub email {
my $self = shift;
my $template = scalar(@_) ? shift : '';
=item print_latex [ TIME [ , TEMPLATE ] ]
Internal method - returns a filename of a filled-in LaTeX template for this
-invoice (Note: add ".tex" to get the actual filename).
+invoice (Note: add ".tex" to get the actual filename), and a filename of
+an associated logo (with the .eps extension included).
See print_ps and print_pdf for methods that return PostScript and PDF output.
'quantity' => 1,
'terms' => $conf->config('invoice_default_terms') || 'Payable upon receipt',
#'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",
);
}
my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
+ my $lh = new File::Temp( TEMPLATE => 'invoice.'. $self->invnum. '.XXXXXXXX',
+ DIR => $dir,
+ SUFFIX => '.eps',
+ 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")
+ or die "can't write temp file: $!\n";
+ }else{
+ print $lh $conf->config_binary('logo.eps')
+ or die "can't write temp file: $!\n";
+ }
+ close $lh;
+ $invoice_data{'logo_file'} = $lh->filename;
+
my $fh = new File::Temp( TEMPLATE => 'invoice.'. $self->invnum. '.XXXXXXXX',
DIR => $dir,
SUFFIX => '.tex',
close $fh;
$fh->filename =~ /^(.*).tex$/ or die "unparsable filename: ". $fh->filename;
- return $1;
+ return ($1, $invoice_data{'logo_file'});
}
sub print_ps {
my $self = shift;
- my $file = $self->print_latex(@_);
+ my ($file, $lfile) = $self->print_latex(@_);
my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
chdir($dir);
or die "can't open $file.ps: $! (error in LaTeX template?)\n";
unlink("$file.dvi", "$file.log", "$file.aux", "$file.ps", "$file.tex");
+ unlink("$lfile");
my $ps = '';
while (<POSTSCRIPT>) {
sub print_pdf {
my $self = shift;
- my $file = $self->print_latex(@_);
+ my ($file, $lfile) = $self->print_latex(@_);
my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
chdir($dir);
or die "can't open $file.pdf: $! (error in LaTeX template?)\n";
unlink("$file.dvi", "$file.log", "$file.aux", "$file.pdf", "$file.tex");
+ unlink("$lfile");
my $pdf = '';
while (<PDF>) {
s/\\item / <li>/;
s/\\end\{enumerate\}/<\/ol>/;
s/\\textbf\{(.*)\}/<b>$1<\/b>/;
+ s/\\\\\*/ /;
$_;
}
$conf->config_orbase('invoice_latexnotes', $template)