summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/Conf.pm6
-rw-r--r--FS/FS/Mason.pm2
-rw-r--r--FS/FS/Schema.pm35
-rw-r--r--FS/FS/bill_batch.pm151
-rw-r--r--FS/FS/cust_bill.pm27
-rw-r--r--FS/FS/cust_bill_batch.pm70
-rw-r--r--FS/FS/cust_bill_batch_option.pm126
7 files changed, 416 insertions, 1 deletions
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index 89a36af43..9046b261b 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -1036,6 +1036,12 @@ worry that config_items is freeside-specific and icky.
'type' => 'textarea'
},
+ {
+ 'key' => 'invoice_print_pdf',
+ 'section' => 'invoicing',
+ 'description' => 'Store postal invoices for download in PDF format rather than printing them directly.',
+ 'type' => 'checkbox',
+ },
{
'key' => 'invoice_default_terms',
diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
index 150a6c081..84c9f7fa9 100644
--- a/FS/FS/Mason.pm
+++ b/FS/FS/Mason.pm
@@ -240,6 +240,8 @@ if ( -e $addl_handler_use_file ) {
use FS::cgp_rule;
use FS::cgp_rule_condition;
use FS::cgp_rule_action;
+ use FS::bill_batch;
+ use FS::cust_bill_batch;
# Sammath Naur
if ( $FS::Mason::addl_handler_use ) {
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 859224fb0..01512f9f6 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -2848,6 +2848,41 @@ sub tables_hashref {
'index' => [['listnum'],['svcnum'],['contactemailnum'],['email']],
},
+ 'bill_batch' => {
+ 'columns' => [
+ 'batchnum', 'serial', '', '', '', '',
+ 'status', 'char', 'NULL','1', '', '',
+ 'pdf', 'blob', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'batchnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'cust_bill_batch' => {
+ 'columns' => [
+ 'billbatchnum', 'serial', '', '', '', '',
+ 'batchnum', 'int', '', '', '', '',
+ 'invnum', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'billbatchnum',
+ 'unique' => [],
+ 'index' => [ [ 'batchnum' ], [ 'invnum' ] ],
+ },
+
+ 'cust_bill_batch_option' => {
+ 'columns' => [
+ 'optionnum', 'serial', '', '', '', '',
+ 'billbatchnum', 'int', '', '', '', '',
+ 'optionname', 'varchar', '', $char_d, '', '',
+ 'optionvalue', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'optionnum',
+ 'unique' => [],
+ 'index' => [ [ 'billbatchnum' ], [ 'optionname' ] ],
+ },
+
+
# name type nullability length default local
diff --git a/FS/FS/bill_batch.pm b/FS/FS/bill_batch.pm
new file mode 100644
index 000000000..136db0d9e
--- /dev/null
+++ b/FS/FS/bill_batch.pm
@@ -0,0 +1,151 @@
+package FS::bill_batch;
+
+use strict;
+use vars qw( @ISA $me $DEBUG );
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::cust_bill_batch;
+
+@ISA = qw( FS::Record );
+$me = '[ FS::bill_batch ]';
+$DEBUG=0;
+
+sub table { 'bill_batch' }
+
+sub nohistory_fields { 'pdf' }
+
+=head1 NAME
+
+FS::bill_batch - Object methods for bill_batch records
+
+=head1 SYNOPSIS
+
+ use FS::bill_batch;
+
+ $open_batch = FS::bill_batch->get_open_batch;
+
+ my $pdf = $open_batch->print_pdf;
+
+ $error = $open_batch->close;
+
+=head1 DESCRIPTION
+
+An FS::bill_batch object represents a batch of invoices. FS::bill_batch
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item batchnum - primary key
+
+=item status - either 'O' (open) or 'R' (resolved/closed).
+
+=item pdf - blob field for temporarily storing the invoice as a PDF.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item print_pdf
+
+Typeset the entire batch as a PDF file. Returns the PDF as a string.
+
+=cut
+
+sub print_pdf {
+ eval 'use CAM::PDF';
+ warn "Failed to load CAM::PDF: '$@'\n" if $@;
+
+ my $self = shift;
+ my $job = shift;
+ $job->update_statustext(0) if $job;
+ my @invoices = sort { $a->invnum <=> $b->invnum }
+ qsearch('cust_bill_batch', { batchnum => $self->batchnum });
+ return "No invoices in batch ".$self->batchnum.'.' if !@invoices;
+
+ my $pdf_out;
+ my $num = 0;
+ foreach my $invoice (@invoices) {
+ my $part = $invoice->cust_bill->print_pdf({$invoice->options});
+ die 'Failed creating PDF from invoice '.$invoice->invnum.'\n' if !$part;
+
+ if($pdf_out) {
+ $pdf_out->appendPDF(CAM::PDF->new($part));
+ }
+ else {
+ $pdf_out = CAM::PDF->new($part);
+ }
+ if($job) {
+ # update progressbar
+ $num++;
+ my $error = $job->update_statustext(int(100 * $num/scalar(@invoices)));
+ die $error if $error;
+ }
+ }
+
+ return $pdf_out->toPDF;
+}
+
+=item close
+
+Set the status of the batch to 'R' (resolved).
+
+=cut
+
+sub close {
+ my $self = shift;
+ $self->status('R');
+ return $self->replace;
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=item get_open_batch
+
+Returns the currently open batch. There should only be one at a time.
+
+=cut
+
+sub get_open_batch {
+ my $class = shift;
+ my $batch = qsearchs('bill_batch', { status => 'O' });
+ return $batch if $batch;
+ $batch = FS::bill_batch->new({status => 'O'});
+ my $error = $batch->insert;
+ die $error if $error;
+ return $batch;
+}
+
+use Storable 'thaw';
+use Data::Dumper;
+use MIME::Base64;
+
+sub process_print_pdf {
+ my $job = shift;
+ my $param = thaw(decode_base64(shift));
+ warn Dumper($param) if $DEBUG;
+ die "no batchnum specified!\n" if ! exists($param->{batchnum});
+ my $batch = FS::bill_batch->by_key($param->{batchnum});
+ die "batch '$param->{batchnum}' not found!\n" if !$batch;
+
+ my $pdf = $batch->print_pdf($job);
+ $batch->pdf($pdf);
+ my $error = $batch->replace;
+ die $error if $error;
+}
+
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
index 127053013..a1dab4ac0 100644
--- a/FS/FS/cust_bill.pm
+++ b/FS/FS/cust_bill.pm
@@ -34,6 +34,8 @@ use FS::cust_bill_pay;
use FS::cust_bill_pay_batch;
use FS::part_bill_event;
use FS::payby;
+use FS::bill_batch;
+use FS::cust_bill_batch;
@ISA = qw( FS::cust_main_Mixin FS::Record );
@@ -1300,7 +1302,13 @@ sub print {
'notice_name' => $notice_name,
);
- do_print $self->lpr_data(\%opt);
+ if($conf->exists('invoice_print_pdf')) {
+ # Add the invoice to the current batch.
+ $self->batch_invoice(\%opt);
+ }
+ else {
+ do_print $self->lpr_data(\%opt);
+ }
}
=item fax_invoice HASHREF | [ TEMPLATE ]
@@ -1346,6 +1354,23 @@ sub fax_invoice {
}
+=item batch_invoice [ HASHREF ]
+
+Place this invoice into the open batch (see C<FS::bill_batch>). If there
+isn't an open batch, one will be created.
+
+=cut
+
+sub batch_invoice {
+ my ($self, $opt) = @_;
+ my $batch = FS::bill_batch->get_open_batch;
+ my $cust_bill_batch = FS::cust_bill_batch->new({
+ batchnum => $batch->batchnum,
+ invnum => $self->invnum,
+ });
+ return $cust_bill_batch->insert($opt);
+}
+
=item ftp_invoice [ TEMPLATENAME ]
Sends this invoice data via FTP.
diff --git a/FS/FS/cust_bill_batch.pm b/FS/FS/cust_bill_batch.pm
new file mode 100644
index 000000000..4569e6bc8
--- /dev/null
+++ b/FS/FS/cust_bill_batch.pm
@@ -0,0 +1,70 @@
+package FS::cust_bill_batch;
+
+use strict;
+use vars qw( @ISA $me $DEBUG );
+use FS::Record qw( qsearch qsearchs dbh );
+
+@ISA = qw( FS::option_Common );
+$me = '[ FS::cust_bill_batch ]';
+$DEBUG=0;
+
+sub table { 'cust_bill_batch' }
+
+=head1 NAME
+
+FS::cust_bill_batch - Object methods for cust_bill_batch records
+
+=head1 DESCRIPTION
+
+An FS::cust_bill_batch object represents the inclusion of an invoice in a
+processing batch. FS::cust_bill_batch inherits from FS::option_Common. The
+following fields are currently supported:
+
+=over 4
+
+=item billbatchnum - primary key
+
+=item invnum - invoice number (see C<FS::cust_bill>)
+
+=item batchnum - batchn number (see C<FS::bill_batch>)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item bill_batch
+
+Returns the C<FS::bill_batch> object.
+
+=cut
+
+sub bill_batch {
+ my $self = shift;
+ FS::bill_batch->by_key($self->batchnum);
+}
+
+=item cust_bill
+
+Returns the C<FS::cust_bill> object.
+
+=cut
+
+sub cust_bill {
+ my $self = shift;
+ FS::cust_bill->by_key($self->invnum);
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill_batch_option.pm b/FS/FS/cust_bill_batch_option.pm
new file mode 100644
index 000000000..9bba830fd
--- /dev/null
+++ b/FS/FS/cust_bill_batch_option.pm
@@ -0,0 +1,126 @@
+package FS::cust_bill_batch_option;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_bill_batch_option - Object methods for cust_bill_batch_option records
+
+=head1 SYNOPSIS
+
+ use FS::cust_bill_batch_option;
+
+ $record = new FS::cust_bill_batch_option \%hash;
+ $record = new FS::cust_bill_batch_option { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_bill_batch_option object represents an option key and value for
+an invoice batch entry. FS::cust_bill_batch_option inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item optionnum - primary key
+
+=item billbatchnum -
+
+=item optionname -
+
+=item optionvalue -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new option. To add the option 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 { 'cust_bill_batch_option'; }
+
+=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 option. 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('optionnum')
+ || $self->ut_foreign_key('billbatchnum', 'cust_bill_batch', 'billbatchnum')
+ || $self->ut_text('optionname')
+ || $self->ut_textn('optionvalue')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+