Merge branch 'FREESIDE_3_BRANCH' of git.freeside.biz:/home/git/freeside into FREESIDE...
[freeside.git] / FS / FS / bill_batch.pm
1 package FS::bill_batch;
2
3 use strict;
4 use vars qw( @ISA $me $DEBUG );
5 use CAM::PDF;
6 use FS::Conf;
7 use FS::Record qw( qsearch qsearchs dbh );
8 use FS::agent;
9 use FS::cust_bill_batch;
10
11 @ISA = qw( FS::Record );
12 $me = '[ FS::bill_batch ]';
13 $DEBUG=0;
14
15 sub table { 'bill_batch' }
16
17 sub nohistory_fields { 'pdf' }
18
19 =head1 NAME
20
21 FS::bill_batch - Object methods for bill_batch records
22
23 =head1 SYNOPSIS
24
25   use FS::bill_batch;
26
27   $open_batch = FS::bill_batch->get_open_batch;
28   
29   my $pdf = $open_batch->print_pdf;
30   
31   $error = $open_batch->close;
32   
33 =head1 DESCRIPTION
34
35 An FS::bill_batch object represents a batch of invoices.  FS::bill_batch 
36 inherits from FS::Record.  The following fields are currently supported:
37
38 =over 4
39
40 =item batchnum - primary key
41
42 =item agentnum - empty for global batches or agent (see L<FS::agent>)
43
44 =item status - either 'O' (open) or 'R' (resolved/closed).
45
46 =item pdf - blob field for temporarily storing the invoice as a PDF.
47
48 =back
49
50 =head1 METHODS
51
52 =over 4
53
54 =item print_pdf
55
56 Typeset the entire batch as a PDF file.  Returns the PDF as a string.
57
58 =cut
59
60 sub print_pdf {
61   my $self = shift;
62   my $job = shift;
63   $job->update_statustext(0) if $job;
64   my @invoices = sort { $a->invnum <=> $b->invnum }
65                  qsearch('cust_bill_batch', { batchnum => $self->batchnum });
66   return "No invoices in batch ".$self->batchnum.'.' if !@invoices;
67
68   my $conf = FS::Conf->new;
69   my $duplex = $conf->exists('invoice_print_pdf-duplex');
70
71   my $pdf_out;
72   my $num = 0;
73   foreach my $invoice (@invoices) {
74     my $part = $invoice->cust_bill->print_pdf({$invoice->options});
75     die 'Failed creating PDF from invoice '.$invoice->invnum.'\n' if !$part;
76
77     if($pdf_out) {
78       $pdf_out->appendPDF(CAM::PDF->new($part));
79     }
80     else {
81       $pdf_out = CAM::PDF->new($part);
82     }
83     if ( $duplex ) {
84       my $n = $pdf_out->numPages;
85       if ( $n % 2 == 1 ) {
86         # then insert a blank page so we end on an even number
87         $pdf_out->duplicatePage($n, 1);
88       }
89     }
90     if($job) {
91       # update progressbar
92       $num++;
93       my $error = $job->update_statustext(int(100 * $num/scalar(@invoices)));
94       die $error if $error;
95     }
96   }
97   $job->update_statustext(100, 'Combining invoices') if $job;
98
99   return $pdf_out->toPDF;
100 }
101
102 =item close
103
104 Set the status of the batch to 'R' (resolved).
105
106 =cut
107
108 sub close {
109   my $self = shift;
110   $self->status('R');
111   return $self->replace;
112 }
113
114 sub check {
115   my $self = shift;
116
117   my $error =
118        $self->ut_numbern('batchnum')
119     || $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum')
120     || $self->ut_enum('status', [ 'O', 'R' ] )
121   ;
122   return $error if $error;
123
124   $self->SUPER::check;
125 }
126
127 =item agent
128
129 Returns the agent (see L<FS::agent>) for this invoice batch.
130
131 =cut
132
133 sub agent {
134   my $self = shift;
135   qsearchs( 'agent', { 'agentnum' => $self->agentnum } );
136 }
137
138 =back
139
140 =head1 SUBROUTINES
141
142 =item process_print_pdf
143
144 =cut
145
146 use Storable 'thaw';
147 use Data::Dumper;
148 use MIME::Base64;
149
150 sub process_print_pdf {
151   my $job = shift;
152   my $param = thaw(decode_base64(shift));
153   warn Dumper($param) if $DEBUG;
154   die "no batchnum specified!\n" if ! exists($param->{batchnum});
155   my $batch = FS::bill_batch->by_key($param->{batchnum});
156   die "batch '$param->{batchnum}' not found!\n" if !$batch;
157
158   if ( $param->{'close'} ) {
159     my $error = $batch->close;
160     die $error if $error;
161   }
162
163   my $pdf = $batch->print_pdf($job);
164   $batch->pdf($pdf);
165   my $error = $batch->replace;
166   die $error if $error;
167 }
168
169 =back
170
171 =head1 BUGS
172
173 =head1 SEE ALSO
174
175 L<FS::Record>, schema.html from the base documentation.
176
177 =cut
178
179 1;
180