-=item search HASHREF
-
-(Class method)
-
-Returns a qsearch hash expression to search for parameters specified in HASHREF.
-Valid parameters are
-
-=over 4
-
-=item agentnum
-
-=item magic
-
-active, inactive, suspended, cancel (or cancelled)
-
-=item status
-
-active, inactive, suspended, one-time charge, inactive, cancel (or cancelled)
-
-=item custom
-
- boolean selects custom packages
-
-=item classnum
-
-=item pkgpart
-
-pkgpart or arrayref or hashref of pkgparts
-
-=item setup
-
-arrayref of beginning and ending epoch date
-
-=item last_bill
-
-arrayref of beginning and ending epoch date
-
-=item bill
-
-arrayref of beginning and ending epoch date
-
-=item adjourn
-
-arrayref of beginning and ending epoch date
-
-=item susp
-
-arrayref of beginning and ending epoch date
-
-=item expire
-
-arrayref of beginning and ending epoch date
-
-=item cancel
-
-arrayref of beginning and ending epoch date
-
-=item query
-
-pkgnum or APKG_pkgnum
-
-=item cust_fields
-
-a value suited to passing to FS::UI::Web::cust_header
-
-=item CurrentUser
-
-specifies the user for agent virtualization
-
-=item fcc_line
-
- boolean selects packages containing fcc form 477 telco lines
-
-=back
-
-=cut
-
-sub search {
- my ($class, $params) = @_;
- my @where = ();
-
- ##
- # parse agent
- ##
-
- if ( $params->{'agentnum'} =~ /^(\d+)$/ and $1 ) {
- push @where,
- "cust_main.agentnum = $1";
- }
-
- ##
- # parse custnum
- ##
-
- if ( $params->{'custnum'} =~ /^(\d+)$/ and $1 ) {
- push @where,
- "cust_pkg.custnum = $1";
- }
-
- ##
- # custbatch
- ##
-
- if ( $params->{'pkgbatch'} =~ /^([\w\/\-\:\.]+)$/ and $1 ) {
- push @where,
- "cust_pkg.pkgbatch = '$1'";
- }
-
- ##
- # parse status
- ##
-
- if ( $params->{'magic'} eq 'active'
- || $params->{'status'} eq 'active' ) {
-
- push @where, FS::cust_pkg->active_sql();
-
- } elsif ( $params->{'magic'} =~ /^not[ _]yet[ _]billed$/
- || $params->{'status'} =~ /^not[ _]yet[ _]billed$/ ) {
-
- push @where, FS::cust_pkg->not_yet_billed_sql();
-
- } elsif ( $params->{'magic'} =~ /^(one-time charge|inactive)/
- || $params->{'status'} =~ /^(one-time charge|inactive)/ ) {
-
- push @where, FS::cust_pkg->inactive_sql();
-
- } elsif ( $params->{'magic'} eq 'suspended'
- || $params->{'status'} eq 'suspended' ) {
-
- push @where, FS::cust_pkg->suspended_sql();
-
- } elsif ( $params->{'magic'} =~ /^cancell?ed$/
- || $params->{'status'} =~ /^cancell?ed$/ ) {
-
- push @where, FS::cust_pkg->cancelled_sql();
-
- }
-
- ###
- # parse package class
- ###
-
- #false lazinessish w/graph/cust_bill_pkg.cgi
- my $classnum = 0;
- my @pkg_class = ();
- if ( exists($params->{'classnum'})
- && $params->{'classnum'} =~ /^(\d*)$/
- )
- {
- $classnum = $1;
- if ( $classnum ) { #a specific class
- push @where, "part_pkg.classnum = $classnum";
-
- #@pkg_class = ( qsearchs('pkg_class', { 'classnum' => $classnum } ) );
- #die "classnum $classnum not found!" unless $pkg_class[0];
- #$title .= $pkg_class[0]->classname.' ';
-
- } elsif ( $classnum eq '' ) { #the empty class
-
- push @where, "part_pkg.classnum IS NULL";
- #$title .= 'Empty class ';
- #@pkg_class = ( '(empty class)' );
- } elsif ( $classnum eq '0' ) {
- #@pkg_class = qsearch('pkg_class', {} ); # { 'disabled' => '' } );
- #push @pkg_class, '(empty class)';
- } else {
- die "illegal classnum";
- }
- }
- #eslaf
-
- ###
- # parse package report options
- ###
-
- my @report_option = ();
- if ( exists($params->{'report_option'})
- && $params->{'report_option'} =~ /^([,\d]*)$/
- )
- {
- @report_option = split(',', $1);
- }
-
- if (@report_option) {
- # this will result in the empty set for the dangling comma case as it should
- push @where,
- map{ "0 < ( SELECT count(*) FROM part_pkg_option
- WHERE part_pkg_option.pkgpart = part_pkg.pkgpart
- AND optionname = 'report_option_$_'
- AND optionvalue = '1' )"
- } @report_option;
- }
-
- #eslaf
-
- ###
- # parse custom
- ###
-
- push @where, "part_pkg.custom = 'Y'" if $params->{custom};
-
- ###
- # parse fcc_line
- ###
-
- push @where, "part_pkg.fcc_ds0s > 0" if $params->{fcc_line};
-
- ###
- # parse censustract
- ###
-
- if ( exists($params->{'censustract'}) ) {
- $params->{'censustract'} =~ /^([.\d]*)$/;
- my $censustract = "cust_main.censustract = '$1'";
- $censustract .= ' OR cust_main.censustract is NULL' unless $1;
- push @where, "( $censustract )";
- }
-
- ###
- # parse part_pkg
- ###
-
- if ( ref($params->{'pkgpart'}) ) {
-
- my @pkgpart = ();
- if ( ref($params->{'pkgpart'}) eq 'HASH' ) {
- @pkgpart = grep $params->{'pkgpart'}{$_}, keys %{ $params->{'pkgpart'} };
- } elsif ( ref($params->{'pkgpart'}) eq 'ARRAY' ) {
- @pkgpart = @{ $params->{'pkgpart'} };
- } else {
- die 'unhandled pkgpart ref '. $params->{'pkgpart'};
- }
-
- @pkgpart = grep /^(\d+)$/, @pkgpart;
-
- push @where, 'pkgpart IN ('. join(',', @pkgpart). ')' if scalar(@pkgpart);
-
- } elsif ( $params->{'pkgpart'} =~ /^(\d+)$/ ) {
- push @where, "pkgpart = $1";
- }
-
- ###
- # parse dates
- ###
-
- my $orderby = '';
-
- #false laziness w/report_cust_pkg.html
- my %disable = (
- 'all' => {},
- 'one-time charge' => { 'last_bill'=>1, 'bill'=>1, 'adjourn'=>1, 'susp'=>1, 'expire'=>1, 'cancel'=>1, },
- 'active' => { 'susp'=>1, 'cancel'=>1 },
- 'suspended' => { 'cancel' => 1 },
- 'cancelled' => {},
- '' => {},
- );
-
- if( exists($params->{'active'} ) ) {
- # This overrides all the other date-related fields
- my($beginning, $ending) = @{$params->{'active'}};
- push @where,
- "cust_pkg.setup IS NOT NULL",
- "cust_pkg.setup <= $ending",
- "(cust_pkg.cancel IS NULL OR cust_pkg.cancel >= $beginning )",
- "NOT (".FS::cust_pkg->onetime_sql . ")";
- }
- else {
- foreach my $field (qw( setup last_bill bill adjourn susp expire contract_end cancel )) {
-
- next unless exists($params->{$field});
-
- my($beginning, $ending) = @{$params->{$field}};
-
- next if $beginning == 0 && $ending == 4294967295;
-
- push @where,
- "cust_pkg.$field IS NOT NULL",
- "cust_pkg.$field >= $beginning",
- "cust_pkg.$field <= $ending";
-
- $orderby ||= "ORDER BY cust_pkg.$field";
-
- }
- }
-
- $orderby ||= 'ORDER BY bill';
-
- ###
- # parse magic, legacy, etc.
- ###
-
- if ( $params->{'magic'} &&
- $params->{'magic'} =~ /^(active|inactive|suspended|cancell?ed)$/
- ) {
-
- $orderby = 'ORDER BY pkgnum';
-
- if ( $params->{'pkgpart'} =~ /^(\d+)$/ ) {
- push @where, "pkgpart = $1";
- }
-
- } elsif ( $params->{'query'} eq 'pkgnum' ) {
-
- $orderby = 'ORDER BY pkgnum';
-
- } elsif ( $params->{'query'} eq 'APKG_pkgnum' ) {
-
- $orderby = 'ORDER BY pkgnum';
-
- push @where, '0 < (
- SELECT count(*) FROM pkg_svc
- WHERE pkg_svc.pkgpart = cust_pkg.pkgpart
- AND pkg_svc.quantity > ( SELECT count(*) FROM cust_svc
- WHERE cust_svc.pkgnum = cust_pkg.pkgnum
- AND cust_svc.svcpart = pkg_svc.svcpart
- )
- )';
-
- }
-
- ##
- # setup queries, links, subs, etc. for the search
- ##
-
- # here is the agent virtualization
- if ($params->{CurrentUser}) {
- my $access_user =
- qsearchs('access_user', { username => $params->{CurrentUser} });
-
- if ($access_user) {
- push @where, $access_user->agentnums_sql('table'=>'cust_main');
- } else {
- push @where, "1=0";
- }
- } else {
- push @where, $FS::CurrentUser::CurrentUser->agentnums_sql('table'=>'cust_main');
- }
-
- my $extra_sql = scalar(@where) ? ' WHERE '. join(' AND ', @where) : '';
-
- my $addl_from = 'LEFT JOIN cust_main USING ( custnum ) '.
- 'LEFT JOIN part_pkg USING ( pkgpart ) '.
- 'LEFT JOIN pkg_class ON ( part_pkg.classnum = pkg_class.classnum ) ';
-
- my $count_query = "SELECT COUNT(*) FROM cust_pkg $addl_from $extra_sql";
-
- my $sql_query = {
- 'table' => 'cust_pkg',
- 'hashref' => {},
- 'select' => join(', ',
- 'cust_pkg.*',
- ( map "part_pkg.$_", qw( pkg freq ) ),
- 'pkg_class.classname',
- 'cust_main.custnum AS cust_main_custnum',
- FS::UI::Web::cust_sql_fields(
- $params->{'cust_fields'}
- ),
- ),
- 'extra_sql' => "$extra_sql $orderby",
- 'addl_from' => $addl_from,
- 'count_query' => $count_query,
- };
-
-}
-