1 package FS::TemplateItem_Mixin;
4 use vars qw( $DEBUG $me ); # but NOT $conf
7 use FS::Record qw( qsearch qsearchs dbh );
12 $me = '[FS::TemplateItem_Mixin]';
16 Returns the package (see L<FS::cust_pkg>) for this invoice line item.
22 carp "$me $self -> cust_pkg" if $DEBUG;
23 qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
28 Returns the package definition for this invoice line item.
34 if ( $self->pkgpart_override ) {
35 qsearchs('part_pkg', { 'pkgpart' => $self->pkgpart_override } );
38 my $cust_pkg = $self->cust_pkg;
39 $part_pkg = $cust_pkg->part_pkg if $cust_pkg;
47 Returns a description for this line item. For typical line items, this is the
48 I<pkg> field of the corresponding B<FS::part_pkg> object (see L<FS::part_pkg>).
49 For one-shot line items and named taxes, it is the I<itemdesc> field of this
50 line item, and for generic taxes, simply returns "Tax".
57 if ( $self->pkgnum > 0 ) {
58 $self->itemdesc || $self->part_pkg->pkg;
60 my $desc = $self->itemdesc || 'Tax';
61 $desc .= ' '. $self->itemcomment if $self->itemcomment =~ /\S/;
66 =item details [ OPTION => VALUE ... ]
68 Returns an array of detail information for the invoice line item.
70 Currently available options are: I<format>, I<escape_function> and
73 If I<format> is set to html or latex then the array members are improved
74 for tabular appearance in those environments if possible.
76 If I<escape_function> is set then the array members are processed by this
77 function before being returned.
79 I<format_function> overrides the normal HTML or LaTeX function for returning
80 formatted CDRs. It can be set to a subroutine which returns an empty list
83 'format_function' => sub { () },
88 my ( $self, %opt ) = @_;
89 my $escape_function = $opt{escape_function} || sub { shift };
91 my $csv = new Text::CSV_XS;
93 if ( $opt{format_function} ) {
95 #this still expects to be passed a cust_bill_pkg_detail object as the
96 #second argument, which is expensive
97 carp "deprecated format_function passed to cust_bill_pkg->details";
98 my $format_sub = $opt{format_function} if $opt{format_function};
100 map { ( $_->format eq 'C'
101 ? &{$format_sub}( $_->detail, $_ )
102 : &{$escape_function}( $_->detail )
105 qsearch ({ 'table' => $self->detail_table,
106 'hashref' => { 'billpkgnum' => $self->billpkgnum },
107 'order_by' => 'ORDER BY detailnum',
110 } elsif ( $opt{'no_usage'} ) {
112 my $sql = "SELECT detail FROM ". $self->detail_table.
113 " WHERE billpkgnum = ". $self->billpkgnum.
114 " AND ( format IS NULL OR format != 'C' ) ".
115 " ORDER BY detailnum";
116 my $sth = dbh->prepare($sql) or die dbh->errstr;
117 $sth->execute or die $sth->errstr;
119 map &{$escape_function}( $_->[0] ), @{ $sth->fetchall_arrayref };
124 my $format = $opt{format} || '';
125 if ( $format eq 'html' ) {
127 $format_sub = sub { my $detail = shift;
128 $csv->parse($detail) or return "can't parse $detail";
129 join('</TD><TD>', map { &$escape_function($_) }
134 } elsif ( $format eq 'latex' ) {
138 $csv->parse($detail) or return "can't parse $detail";
139 #join(' & ', map { '\small{'. &$escape_function($_). '}' }
143 foreach ($csv->fields) {
144 $result .= ' & ' if $column > 1;
145 if ($column > 6) { # KLUDGE ALERT!
146 $result .= '\multicolumn{1}{l}{\scriptsize{'.
147 &$escape_function($_). '}}';
149 $result .= '\scriptsize{'. &$escape_function($_). '}';
158 $format_sub = sub { my $detail = shift;
159 $csv->parse($detail) or return "can't parse $detail";
160 join(' - ', map { &$escape_function($_) }
167 my $sql = "SELECT format, detail FROM ". $self->detail_table.
168 " WHERE billpkgnum = ". $self->billpkgnum.
169 " ORDER BY detailnum";
170 my $sth = dbh->prepare($sql) or die dbh->errstr;
171 $sth->execute or die $sth->errstr;
173 #avoid the fetchall_arrayref and loop for less memory usage?
175 map { (defined($_->[0]) && $_->[0] eq 'C')
176 ? &{$format_sub}( $_->[1] )
177 : &{$escape_function}( $_->[1] );
179 @{ $sth->fetchall_arrayref };
185 =item details_header [ OPTION => VALUE ... ]
187 Returns a list representing an invoice line item detail header, if any.
188 This relies on the behavior of voip_cdr in that it expects the header
189 to be the first CSV formatted detail (as is expected by invoice generation
190 routines). Returns the empty list otherwise.
197 my $csv = new Text::CSV_XS;
200 qsearch ({ 'table' => $self->detail_table,
201 'hashref' => { 'billpkgnum' => $self->billpkgnum,
204 'order_by' => 'ORDER BY detailnum LIMIT 1',
206 return() unless scalar(@detail);
207 $csv->parse($detail[0]->detail) or return ();
216 my( $self, $value ) = @_;
217 if ( defined($value) ) {
218 $self->setfield('quantity', $value);
220 $self->getfield('quantity') || 1;
228 my( $self, $value ) = @_;
229 if ( defined($value) ) {
230 $self->setfield('unitsetup', $value);
232 $self->getfield('unitsetup') eq ''
233 ? $self->getfield('setup')
234 : $self->getfield('unitsetup');
242 my( $self, $value ) = @_;
243 if ( defined($value) ) {
244 $self->setfield('unitrecur', $value);
246 $self->getfield('unitrecur') eq ''
247 ? $self->getfield('recur')
248 : $self->getfield('unitrecur');
251 =item cust_bill_pkg_display [ type => TYPE ]
253 Returns an array of display information for the invoice line item optionally
258 sub cust_bill_pkg_display {
259 my ( $self, %opt ) = @_;
261 my $class = 'FS::'. $self->display_table;
263 my $default = $class->new( { billpkgnum =>$self->billpkgnum } );
265 my $type = $opt{type} if exists $opt{type};
268 if ( $self->get('display') ) {
269 @result = grep { defined($type) ? ($type eq $_->type) : 1 }
270 @{ $self->get('display') };
272 my $hashref = { 'billpkgnum' => $self->billpkgnum };
273 $hashref->{type} = $type if defined($type);
275 @result = qsearch ({ 'table' => $self->display_table,
276 'hashref' => { 'billpkgnum' => $self->billpkgnum },
277 'order_by' => 'ORDER BY billpkgdisplaynum',
281 push @result, $default unless ( scalar(@result) || $type );
287 =item cust_bill_pkg_detail [ CLASSNUM ]
289 Returns the list of associated cust_bill_pkg_detail objects
290 The optional CLASSNUM argument will limit the details to the specified usage
295 sub cust_bill_pkg_detail {
297 my $classnum = shift || '';
299 my %hash = ( 'billpkgnum' => $self->billpkgnum );
300 $hash{classnum} = $classnum if $classnum;
302 qsearch( $self->detail_table, \%hash ),
306 =item cust_bill_pkg_discount
308 Returns the list of associated cust_bill_pkg_discount objects.
312 sub cust_bill_pkg_discount {
314 qsearch( $self->discount_table, { 'billpkgnum' => $self->billpkgnum } );