X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_bill.pm;h=7e068abe86390d4cfba6881f65200ec3fd5290ce;hb=45b1d2bf265890e1872420580a907ada70fb6c4b;hp=7f1290fcfa6de61697ab153b8d846b1049713e49;hpb=f786ebaff8b6704e4e180428aaaa9afeaea7ecb9;p=freeside.git diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index 7f1290fcf..7e068abe8 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -1,5 +1,7 @@ package FS::cust_bill; -use base qw( FS::Template_Mixin FS::cust_main_Mixin FS::Record ); +use base qw( FS::cust_bill::Search FS::Template_Mixin + FS::cust_main_Mixin FS::Record + ); use strict; use vars qw( $DEBUG $me ); @@ -13,7 +15,7 @@ use HTML::Entities; use Storable qw( freeze thaw ); use GD::Barcode; use FS::UID qw( datasrc ); -use FS::Misc qw( send_email send_fax do_print ); +use FS::Misc qw( send_fax do_print ); use FS::Record qw( qsearch qsearchs dbh ); use FS::cust_main; use FS::cust_statement; @@ -446,16 +448,20 @@ followed by the previous outstanding invoices (as FS::cust_bill objects also). sub previous { my $self = shift; - my $total = 0; - my @cust_bill = sort { $a->_date <=> $b->_date } - grep { $_->owed != 0 } - qsearch( 'cust_bill', { 'custnum' => $self->custnum, - #'_date' => { op=>'<', value=>$self->_date }, - 'invnum' => { op=>'<', value=>$self->invnum }, - } ) - ; - foreach ( @cust_bill ) { $total += $_->owed; } - $total, @cust_bill; + # simple memoize; we use this a lot + if (!$self->get('previous')) { + my $total = 0; + my @cust_bill = sort { $a->_date <=> $b->_date } + grep { $_->owed != 0 } + qsearch( 'cust_bill', { 'custnum' => $self->custnum, + #'_date' => { op=>'<', value=>$self->_date }, + 'invnum' => { op=>'<', value=>$self->invnum }, + } ) + ; + foreach ( @cust_bill ) { $total += $_->owed; } + $self->set('previous', [$total, @cust_bill]); + } + return @{ $self->get('previous') }; } =item enable_previous @@ -907,6 +913,7 @@ sub hide { =item apply_payments_and_credits [ OPTION => VALUE ... ] Applies unapplied payments and credits to this invoice. +Payments with the no_auto_apply flag set will not be applied. A hash of optional arguments may be passed. Currently "manual" is supported. If true, a payment receipt is sent instead of a statement when @@ -933,7 +940,9 @@ sub apply_payments_and_credits { $self->select_for_update; #mutex - my @payments = grep { $_->unapplied > 0 } $self->cust_main->cust_pay; + my @payments = grep { $_->unapplied > 0 } + grep { !$_->no_auto_apply } + $self->cust_main->cust_pay; my @credits = grep { $_->credited > 0 } $self->cust_main->cust_credit; if ( $conf->exists('pkg-balances') ) { @@ -1029,300 +1038,6 @@ sub apply_payments_and_credits { } -=item generate_email OPTION => VALUE ... - -Options: - -=over 4 - -=item from - -sender address, required - -=item template - -alternate template name, optional - -=item print_text - -text attachment arrayref, optional - -=item subject - -email subject, optional - -=item notice_name - -notice name instead of "Invoice", optional - -=back - -Returns an argument list to be passed to L<FS::Misc::send_email>. - -=cut - -use MIME::Entity; - -sub generate_email { - - my $self = shift; - my %args = @_; - my $conf = $self->conf; - - my $me = '[FS::cust_bill::generate_email]'; - - my %return = ( - 'from' => $args{'from'}, - 'subject' => ($args{'subject'} || $self->email_subject), - 'custnum' => $self->custnum, - 'msgtype' => 'invoice', - ); - - $args{'unsquelch_cdr'} = $conf->exists('voip-cdr_email'); - - my $cust_main = $self->cust_main; - - if (ref($args{'to'}) eq 'ARRAY') { - $return{'to'} = $args{'to'}; - } else { - $return{'to'} = [ grep { $_ !~ /^(POST|FAX)$/ } - $cust_main->invoicing_list - ]; - } - - if ( $conf->exists('invoice_html') ) { - - warn "$me creating HTML/text multipart message" - if $DEBUG; - - $return{'nobody'} = 1; - - my $alternative = build MIME::Entity - 'Type' => 'multipart/alternative', - #'Encoding' => '7bit', - 'Disposition' => 'inline' - ; - - my $data; - if ( $conf->exists('invoice_email_pdf') - and scalar($conf->config('invoice_email_pdf_note')) ) { - - warn "$me using 'invoice_email_pdf_note' in multipart message" - if $DEBUG; - $data = [ map { $_ . "\n" } - $conf->config('invoice_email_pdf_note') - ]; - - } else { - - warn "$me not using 'invoice_email_pdf_note' in multipart message" - if $DEBUG; - if ( ref($args{'print_text'}) eq 'ARRAY' ) { - $data = $args{'print_text'}; - } else { - $data = [ $self->print_text(\%args) ]; - } - - } - - $alternative->attach( - 'Type' => 'text/plain', - 'Encoding' => 'quoted-printable', - #'Encoding' => '7bit', - 'Data' => $data, - 'Disposition' => 'inline', - ); - - - my $htmldata; - my $image = ''; - my $barcode = ''; - if ( $conf->exists('invoice_email_pdf') - and scalar($conf->config('invoice_email_pdf_note')) ) { - - $htmldata = join('<BR>', $conf->config('invoice_email_pdf_note') ); - - } else { - - $args{'from'} =~ /\@([\w\.\-]+)/; - my $from = $1 || 'example.com'; - my $content_id = join('.', rand()*(2**32), $$, time). "\@$from"; - - my $logo; - my $agentnum = $cust_main->agentnum; - if ( defined($args{'template'}) && length($args{'template'}) - && $conf->exists( 'logo_'. $args{'template'}. '.png', $agentnum ) - ) - { - $logo = 'logo_'. $args{'template'}. '.png'; - } else { - $logo = "logo.png"; - } - my $image_data = $conf->config_binary( $logo, $agentnum); - - $image = build MIME::Entity - 'Type' => 'image/png', - 'Encoding' => 'base64', - 'Data' => $image_data, - 'Filename' => 'logo.png', - 'Content-ID' => "<$content_id>", - ; - - if ($conf->exists('invoice-barcode')) { - my $barcode_content_id = join('.', rand()*(2**32), $$, time). "\@$from"; - $barcode = build MIME::Entity - 'Type' => 'image/png', - 'Encoding' => 'base64', - 'Data' => $self->invoice_barcode(0), - 'Filename' => 'barcode.png', - 'Content-ID' => "<$barcode_content_id>", - ; - $args{'barcode_cid'} = $barcode_content_id; - } - - $htmldata = $self->print_html({ 'cid'=>$content_id, %args }); - } - - $alternative->attach( - 'Type' => 'text/html', - 'Encoding' => 'quoted-printable', - 'Data' => [ '<html>', - ' <head>', - ' <title>', - ' '. encode_entities($return{'subject'}), - ' </title>', - ' </head>', - ' <body bgcolor="#e8e8e8">', - $htmldata, - ' </body>', - '</html>', - ], - 'Disposition' => 'inline', - #'Filename' => 'invoice.pdf', - ); - - - my @otherparts = (); - if ( $cust_main->email_csv_cdr ) { - - push @otherparts, build MIME::Entity - 'Type' => 'text/csv', - 'Encoding' => '7bit', - 'Data' => [ map { "$_\n" } - $self->call_details('prepend_billed_number' => 1) - ], - 'Disposition' => 'attachment', - 'Filename' => 'usage-'. $self->invnum. '.csv', - ; - - } - - if ( $conf->exists('invoice_email_pdf') ) { - - #attaching pdf too: - # multipart/mixed - # multipart/related - # multipart/alternative - # text/plain - # text/html - # image/png - # application/pdf - - my $related = build MIME::Entity 'Type' => 'multipart/related', - 'Encoding' => '7bit'; - - #false laziness w/Misc::send_email - $related->head->replace('Content-type', - $related->mime_type. - '; boundary="'. $related->head->multipart_boundary. '"'. - '; type=multipart/alternative' - ); - - $related->add_part($alternative); - - $related->add_part($image) if $image; - - my $pdf = build MIME::Entity $self->mimebuild_pdf(\%args); - - $return{'mimeparts'} = [ $related, $pdf, @otherparts ]; - - } else { - - #no other attachment: - # multipart/related - # multipart/alternative - # text/plain - # text/html - # image/png - - $return{'content-type'} = 'multipart/related'; - if ($conf->exists('invoice-barcode') && $barcode) { - $return{'mimeparts'} = [ $alternative, $image, $barcode, @otherparts ]; - } else { - $return{'mimeparts'} = [ $alternative, $image, @otherparts ]; - } - $return{'type'} = 'multipart/alternative'; #Content-Type of first part... - #$return{'disposition'} = 'inline'; - - } - - } else { - - if ( $conf->exists('invoice_email_pdf') ) { - warn "$me creating PDF attachment" - if $DEBUG; - - #mime parts arguments a la MIME::Entity->build(). - $return{'mimeparts'} = [ - { $self->mimebuild_pdf(\%args) } - ]; - } - - if ( $conf->exists('invoice_email_pdf') - and scalar($conf->config('invoice_email_pdf_note')) ) { - - warn "$me using 'invoice_email_pdf_note'" - if $DEBUG; - $return{'body'} = [ map { $_ . "\n" } - $conf->config('invoice_email_pdf_note') - ]; - - } else { - - warn "$me not using 'invoice_email_pdf_note'" - if $DEBUG; - if ( ref($args{'print_text'}) eq 'ARRAY' ) { - $return{'body'} = $args{'print_text'}; - } else { - $return{'body'} = [ $self->print_text(\%args) ]; - } - - } - - } - - %return; - -} - -=item mimebuild_pdf - -Returns a list suitable for passing to MIME::Entity->build(), representing -this invoice as PDF attachment. - -=cut - -sub mimebuild_pdf { - my $self = shift; - ( - 'Type' => 'application/pdf', - 'Encoding' => 'base64', - 'Data' => [ $self->print_pdf(@_) ], - 'Disposition' => 'attachment', - 'Filename' => 'invoice-'. $self->invnum. '.pdf', - ); -} - =item send HASHREF Sends this invoice to the destinations configured for this customer: sends @@ -1335,7 +1050,7 @@ I<template>: a suffix for alternate invoices I<agentnum>: obsolete, now does nothing. -I<invoice_from> overrides the default email invoice From: address. +I<from> overrides the default email invoice From: address. I<amount>: obsolete, does nothing @@ -1358,7 +1073,7 @@ sub send { $self->email($opt) if ( grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list or !@invoicing_list ) - && ! $self->invoice_noemail; + && ! $cust_main->invoice_noemail; $self->print($opt) if grep { $_ eq 'POST' } @invoicing_list; #postal @@ -1371,55 +1086,22 @@ sub send { } -=item email HASHREF | [ TEMPLATE [ , INVOICE_FROM ] ] - -Sends this invoice to the customer's email destination(s). - -Options must be passed as a hashref. Positional parameters are no longer -allowed. - -I<template>, if specified, is the name of a suffix for alternate invoices. - -I<invoice_from>, if specified, overrides the default email invoice From: -address. - -I<notice_name> is the name of the sent document. - -=cut - -sub queueable_email { - my %opt = @_; - - my $self = qsearchs('cust_bill', { 'invnum' => $opt{invnum} } ) - or die "invalid invoice number: " . $opt{invnum}; - - my %args = map {$_ => $opt{$_}} - grep { $opt{$_} } - qw( invoice_from notice_name no_coupon template ); - - my $error = $self->email( \%args ); - die $error if $error; - -} - sub email { my $self = shift; - return if $self->hide; - my $conf = $self->conf; my $opt = shift || {}; if ($opt and !ref($opt)) { - die "FS::cust_bill::email called with positional parameters"; + die ref($self). '->email called with positional parameters'; } - my $template = $opt->{template}; - my $from = delete $opt->{invoice_from}; + my $conf = $self->conf; + + my $from = delete $opt->{from}; # this is where we set the From: address $from ||= $self->_agent_invoice_from || #XXX should go away - $conf->config('invoice_from', $self->cust_main->agentnum ); + $conf->invoice_from_full( $self->cust_main->agentnum ); - my @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ } - $self->cust_main->invoicing_list; + my @invoicing_list = $self->cust_main->invoicing_list_emailonly; if ( ! @invoicing_list ) { #no recipients if ( $conf->exists('cust_bill-no_recipients-error') ) { @@ -1430,19 +1112,32 @@ sub email { } } - # this is where we set the Subject: - my $subject = $self->email_subject($template); + $self->SUPER::email( { + 'from' => $from, + 'to' => \@invoicing_list, + %$opt, + }); - my $error = send_email( - $self->generate_email( - 'from' => $from, - 'to' => [ grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ], - 'subject' => $subject, - %$opt, # template, etc. - ) - ); - die "can't email invoice: $error\n" if $error; - #die "$error\n" if $error; +} + +#this stays here for now because its explicitly used as +# FS::cust_bill::queueable_email +sub queueable_email { + my %opt = @_; + + my $self = qsearchs('cust_bill', { 'invnum' => $opt{invnum} } ) + or die "invalid invoice number: " . $opt{invnum}; + + if ( $opt{mode} ) { + $self->set('mode', $opt{mode}); + } + + my %args = map {$_ => $opt{$_}} + grep { $opt{$_} } + qw( from notice_name no_coupon template ); + + my $error = $self->email( \%args ); + die $error if $error; } @@ -1565,6 +1260,8 @@ sub fax_invoice { Place this invoice into the open batch (see C<FS::bill_batch>). If there isn't an open batch, one will be created. +HASHREF may contain any options to be passed to C<print_pdf>. + =cut sub batch_invoice { @@ -2221,7 +1918,19 @@ sub print_csv { if ( lc($opt{'format'}) eq 'billco' ) { my $lineseq = 0; - foreach my $item ( $self->_items_pkg ) { + my %items_opt = ( format => 'template', + escape_function => sub { shift } ); + # I don't know what characters billco actually tolerates in spool entries. + # Text::CSV will take care of delimiters, though. + + my @items = ( $self->_items_pkg(%items_opt), + $self->_items_fee(%items_opt) ); + foreach my $item (@items) { + + my $description = $item->{'description'}; + if ( $item->{'_is_discount'} and exists($item->{ext_description}[0]) ) { + $description .= ': ' . $item->{ext_description}[0]; + } $csv->combine( '', # 1 | N/A-Leave Empty CHAR 2 @@ -2229,7 +1938,7 @@ sub print_csv { $tracctnum, # 3 | Account Number CHAR 15 $self->invnum, # 4 | Invoice Number CHAR 15 $lineseq++, # 5 | Line Sequence (sort order) NUM 6 - $item->{'description'}, # 6 | Transaction Detail CHAR 100 + $description, # 6 | Transaction Detail CHAR 100 $item->{'amount'}, # 7 | Amount NUM* 9 '', # 8 | Line Format Control** CHAR 2 '', # 9 | Grouping Code CHAR 2 @@ -2514,7 +2223,7 @@ sub _items_extra_usage_sections { my %classnums = (); my %lines = (); - my $maxlength = $conf->config('cust_bill-latex_lineitem_maxlength') || 50; + my $maxlength = $conf->config('cust_bill-latex_lineitem_maxlength') || 40; my %usage_class = map { $_->classnum => $_ } qsearch( 'usage_class', {} ); foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) { @@ -2754,7 +2463,7 @@ sub _items_svc_phone_sections { my %classnums = (); my %lines = (); - my $maxlength = $conf->config('cust_bill-latex_lineitem_maxlength') || 50; + my $maxlength = $conf->config('cust_bill-latex_lineitem_maxlength') || 40; my %usage_class = map { $_->classnum => $_ } qsearch( 'usage_class', {} ); $usage_class{''} ||= new FS::usage_class { 'classname' => '', 'weight' => 0 }; @@ -3053,7 +2762,7 @@ sub _items_previous { sub _items_credits { my( $self, %opt ) = @_; - my $trim_len = $opt{'trim_len'} || 60; + my $trim_len = $opt{'trim_len'} || 40; my @b; #credits @@ -3150,6 +2859,66 @@ sub _items_payments { } +sub _items_total { + my $self = shift; + my $conf = $self->conf; + + my @items; + my ($pr_total) = $self->previous; + my ($previous_charges_desc, $new_charges_desc, $new_charges_amount); + + if ( $conf->exists('previous_balance-exclude_from_total') ) { + # can we do some caching on this stuff? it's going to change infrequently + # in production + $previous_charges_desc = $self->mt( + $conf->config('previous_balance-text') || 'Previous Balance' + ); + + # then return separate lines for previous balance and total new charges + if ( $pr_total ) { + push @items, + { total_item => $previous_charges_desc, + total_amount => sprintf('%.2f',$pr_total) + }; + } + $new_charges_desc = $self->mt( + $conf->config('previous_balance-text-total_new_charges') + || 'Total New Charges' + ); + + $new_charges_amount = $self->charged; + + } else { + + $new_charges_desc = $self->mt('Total Charges'); + $new_charges_amount = sprintf('%.2f',$self->charged + $pr_total); + + } + + if ( $conf->exists('invoice_show_prior_due_date') ) { + # then the due date should be shown with Total New Charges, + # and should NOT be shown with the Balance Due message. + if ( $self->due_date ) { + # localize the "Please pay by" message and the date itself + # (grammar issues with this, yeah) + $new_charges_desc .= ' - ' . $self->mt('Please pay by') . ' ' . + $self->due_date2str('short'); + } elsif ( $self->terms ) { + # phrases like "due on receipt" should be localized + $new_charges_desc .= ' - ' . $self->mt($self->terms); + } + } + + push @items, + { total_item => $new_charges_desc, + total_amount => $new_charges_amount, + }; + + @items; +} + + + =item call_details [ OPTION => VALUE ... ] Returns an array of CSV strings representing the call details for this invoice @@ -3247,6 +3016,9 @@ sub process_re_X { } +# this is called from search/cust_bill.html and given all its search +# parameters, so it needs to perform the same search. + sub re_X { # spool_invoice ftp_invoice fax_invoice print_invoice my($method, $job, %param ) = @_; @@ -3256,22 +3028,15 @@ sub re_X { } #some false laziness w/search/cust_bill.html - my $distinct = ''; - my $orderby = 'ORDER BY cust_bill._date'; - - my $extra_sql = ' WHERE '. FS::cust_bill->search_sql_where(\%param); - - my $addl_from = 'LEFT JOIN cust_main USING ( custnum )'; - - my @cust_bill = qsearch( { - #'select' => "cust_bill.*", - 'table' => 'cust_bill', - 'addl_from' => $addl_from, - 'hashref' => {}, - 'extra_sql' => $extra_sql, - 'order_by' => $orderby, - 'debug' => 1, - } ); + $param{'order_by'} = 'cust_bill._date'; + + my $query = FS::cust_bill->search(\%param); + delete $query->{'count_query'}; + delete $query->{'count_addl'}; + + $query->{debug} = 1; # was in here before, is obviously useful + + my @cust_bill = qsearch( $query ); $method .= '_invoice' unless $method eq 'email' || $method eq 'print'; @@ -3367,6 +3132,13 @@ Currently only supported on PostgreSQL. =cut sub due_date_sql { + die "don't use: doesn't account for agent-specific invoice_default_terms"; + + #we're passed a $conf but not a specific customer (that's in the query), so + # to make this work we'd need an agentnum-aware "condition_sql_conf" like + # "condition_sql_option" that retreives a conf value with SQL in an agent- + # aware fashion + my $conf = new FS::Conf; 'COALESCE( SUBSTRING( @@ -3379,219 +3151,6 @@ sub due_date_sql { ) * 86400 + cust_bill._date' } -=item search_sql_where HASHREF - -Class method which returns an SQL WHERE fragment to search for parameters -specified in HASHREF. Valid parameters are - -=over 4 - -=item _date - -List reference of start date, end date, as UNIX timestamps. - -=item invnum_min - -=item invnum_max - -=item agentnum - -=item charged - -List reference of charged limits (exclusive). - -=item owed - -List reference of charged limits (exclusive). - -=item open - -flag, return open invoices only - -=item net - -flag, return net invoices only - -=item days - -=item newest_percust - -=item custnum - -Return only invoices belonging to that customer. - -=item cust_classnum - -Limit to that customer class (single value or arrayref). - -=item payby - -Limit to customers with that payment method (single value or arrayref). - -=item refnum - -Limit to customers with that advertising source. - -=back - -Note: validates all passed-in data; i.e. safe to use with unchecked CGI params. - -=cut - -sub search_sql_where { - my($class, $param) = @_; - if ( $DEBUG ) { - warn "$me search_sql_where called with params: \n". - join("\n", map { " $_: ". $param->{$_} } keys %$param ). "\n"; - } - - my @search = (); - - #agentnum - if ( $param->{'agentnum'} =~ /^(\d+)$/ ) { - push @search, "cust_main.agentnum = $1"; - } - - #refnum - if ( $param->{'refnum'} =~ /^(\d+)$/ ) { - push @search, "cust_main.refnum = $1"; - } - - #custnum - if ( $param->{'custnum'} =~ /^(\d+)$/ ) { - push @search, "cust_bill.custnum = $1"; - } - - #customer classnum (false laziness w/ cust_main/Search.pm) - if ( $param->{'cust_classnum'} ) { - - my @classnum = ref( $param->{'cust_classnum'} ) - ? @{ $param->{'cust_classnum'} } - : ( $param->{'cust_classnum'} ); - - @classnum = grep /^(\d*)$/, @classnum; - - if ( @classnum ) { - push @search, '( '. join(' OR ', map { - $_ ? "cust_main.classnum = $_" - : "cust_main.classnum IS NULL" - } - @classnum - ). - ' )'; - } - - } - - #payby - if ( $param->{payby} ) { - my $payby = $param->{payby}; - $payby = [ $payby ] unless ref $payby; - my $payby_in = join(',', map {dbh->quote($_)} @$payby); - push @search, "cust_main.payby IN($payby_in)" if length($payby_in); - } - - #_date - if ( $param->{_date} ) { - my($beginning, $ending) = @{$param->{_date}}; - - push @search, "cust_bill._date >= $beginning", - "cust_bill._date < $ending"; - } - - #invnum - if ( $param->{'invnum_min'} =~ /^(\d+)$/ ) { - push @search, "cust_bill.invnum >= $1"; - } - if ( $param->{'invnum_max'} =~ /^(\d+)$/ ) { - push @search, "cust_bill.invnum <= $1"; - } - - #charged - if ( $param->{charged} ) { - my @charged = ref($param->{charged}) - ? @{ $param->{charged} } - : ($param->{charged}); - - push @search, map { s/^charged/cust_bill.charged/; $_; } - @charged; - } - - my $owed_sql = FS::cust_bill->owed_sql; - - #owed - if ( $param->{owed} ) { - my @owed = ref($param->{owed}) - ? @{ $param->{owed} } - : ($param->{owed}); - push @search, map { s/^owed/$owed_sql/; $_; } - @owed; - } - - #open/net flags - push @search, "0 != $owed_sql" - if $param->{'open'}; - push @search, '0 != '. FS::cust_bill->net_sql - if $param->{'net'}; - - #days - push @search, "cust_bill._date < ". (time-86400*$param->{'days'}) - if $param->{'days'}; - - #newest_percust - if ( $param->{'newest_percust'} ) { - - #$distinct = 'DISTINCT ON ( cust_bill.custnum )'; - #$orderby = 'ORDER BY cust_bill.custnum ASC, cust_bill._date DESC'; - - my @newest_where = map { my $x = $_; - $x =~ s/\bcust_bill\./newest_cust_bill./g; - $x; - } - grep ! /^cust_main./, @search; - my $newest_where = scalar(@newest_where) - ? ' AND '. join(' AND ', @newest_where) - : ''; - - - push @search, "cust_bill._date = ( - SELECT(MAX(newest_cust_bill._date)) FROM cust_bill AS newest_cust_bill - WHERE newest_cust_bill.custnum = cust_bill.custnum - $newest_where - )"; - - } - - #promised_date - also has an option to accept nulls - if ( $param->{promised_date} ) { - my($beginning, $ending, $null) = @{$param->{promised_date}}; - - push @search, "(( cust_bill.promised_date >= $beginning AND ". - "cust_bill.promised_date < $ending )" . - ($null ? ' OR cust_bill.promised_date IS NULL ) ' : ')'); - } - - #agent virtualization - my $curuser = $FS::CurrentUser::CurrentUser; - if ( $curuser->username eq 'fs_queue' - && $param->{'CurrentUser'} =~ /^(\w+)$/ ) { - my $username = $1; - my $newuser = qsearchs('access_user', { - 'username' => $username, - 'disabled' => '', - } ); - if ( $newuser ) { - $curuser = $newuser; - } else { - warn "$me WARNING: (fs_queue) can't find CurrentUser $username\n"; - } - } - push @search, $curuser->agentnums_sql; - - join(' AND ', @search ); - -} - =back =head1 BUGS