X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fcust_pkg.pm;h=d67c02c0d0166e2b0d607bd6b80b9896e84d2627;hp=2ed25a06c5341daf58c32b14efa98c5a8b1f0e07;hb=b46d1bb09d6bbc5746e533f67ace294f5cd8fb42;hpb=fc0d0fe616e4bc5dc8137ad1af1cc7072d25f19f diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index 2ed25a06c..d67c02c0d 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -8,7 +8,7 @@ use Carp qw(cluck); use Scalar::Util qw( blessed ); use List::Util qw(max); use Tie::IxHash; -use Time::Local qw( timelocal_nocheck ); +use Time::Local qw( timelocal timelocal_nocheck ); use MIME::Entity; use FS::UID qw( getotaker dbh ); use FS::Misc qw( send_email ); @@ -30,6 +30,7 @@ use FS::reason; use FS::cust_pkg_discount; use FS::discount; use FS::UI::Web; +use Data::Dumper; # need to 'use' these instead of 'require' in sub { cancel, suspend, unsuspend, # setup } @@ -125,6 +126,10 @@ Billing item definition (see L) Optional link to package location (see L) +=item order_date + +date package was ordered (also remains same on changes) + =item start_date date @@ -190,6 +195,8 @@ Previous pkgpart Previous locationnum +=item waive_setup + =back Note: setup, last_bill, bill, adjourn, susp, expire, cancel and change_date @@ -256,7 +263,12 @@ an optional queue name for ticket additions sub insert { my( $self, %options ) = @_; - if ( $self->part_pkg->option('start_1st', 1) && !$self->start_date ) { + my $error = $self->check_pkgpart; + return $error if $error; + + my $part_pkg = $self->part_pkg; + + if ( $part_pkg->option('start_1st', 1) && !$self->start_date ) { my ($sec,$min,$hour,$mday,$mon,$year) = (localtime(time) )[0,1,2,3,4,5]; $mon += 1 unless $mday == 1; until ( $mon < 12 ) { $mon -= 12; $year++; } @@ -264,13 +276,23 @@ sub insert { } foreach my $action ( qw(expire adjourn contract_end) ) { - my $months = $self->part_pkg->option("${action}_months",1); + my $months = $part_pkg->option("${action}_months",1); if($months and !$self->$action) { my $start = $self->start_date || $self->setup || time; - $self->$action( $self->part_pkg->add_freq($start, $months) ); + $self->$action( $part_pkg->add_freq($start, $months) ); } } + my $free_days = $part_pkg->option('free_days',1); + if ( $free_days && $part_pkg->option('delay_setup',1) ) { #&& !$self->start_date + my ($mday,$mon,$year) = (localtime(time) )[3,4,5]; + #my $start_date = ($self->start_date || timelocal(0,0,0,$mday,$mon,$year)) + 86400 * $free_days; + my $start_date = timelocal(0,0,0,$mday,$mon,$year) + 86400 * $free_days; + $self->start_date($start_date); + } + + $self->order_date(time); + local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; local $SIG{QUIT} = 'IGNORE'; @@ -282,7 +304,7 @@ sub insert { local $FS::UID::AutoCommit = 0; my $dbh = dbh; - my $error = $self->SUPER::insert($options{options} ? %{$options{options}} : ()); + $error = $self->SUPER::insert($options{options} ? %{$options{options}} : ()); if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; @@ -365,14 +387,70 @@ sub insert { This method now works but you probably shouldn't use it. -You don't want to delete billing items, because there would then be no record -the customer ever purchased the item. Instead, see the cancel method. +You don't want to delete packages, because there would then be no record +the customer ever purchased the package. Instead, see the cancel method and +hide cancelled packages. =cut -#sub delete { -# return "Can't delete cust_pkg records!"; -#} +sub delete { + my $self = shift; + + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + local $SIG{PIPE} = 'IGNORE'; + + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + my $dbh = dbh; + + foreach my $cust_pkg_discount ($self->cust_pkg_discount) { + my $error = $cust_pkg_discount->delete; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + #cust_bill_pkg_discount? + + foreach my $cust_pkg_detail ($self->cust_pkg_detail) { + my $error = $cust_pkg_detail->delete; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + + foreach my $cust_pkg_reason ( + qsearchs( { + 'table' => 'cust_pkg_reason', + 'hashref' => { 'pkgnum' => $self->pkgnum }, + } + ) + ) { + my $error = $cust_pkg_reason->delete; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + + #pkg_referral? + + my $error = $self->SUPER::delete(@_); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + + ''; + +} =item replace [ OLD_RECORD ] [ HASHREF | OPTION => VALUE ... ] @@ -487,7 +565,10 @@ sub replace { #trigger export of new RADIUS Expiration attribute when cust_pkg.bill changes foreach my $old_svc_acct ( @svc_acct ) { my $new_svc_acct = new FS::svc_acct { $old_svc_acct->hash }; - my $s_error = $new_svc_acct->replace($old_svc_acct); + my $s_error = + $new_svc_acct->replace( $old_svc_acct, + 'depend_jobnum' => $options->{depend_jobnum}, + ); if ( $s_error ) { $dbh->rollback if $oldAutoCommit; return $s_error; @@ -516,6 +597,7 @@ sub check { $self->ut_numbern('pkgnum') || $self->ut_foreign_key('custnum', 'cust_main', 'custnum') || $self->ut_numbern('pkgpart') + || $self->check_pkgpart || $self->ut_foreign_keyn('locationnum', 'cust_location', 'locationnum') || $self->ut_numbern('start_date') || $self->ut_numbern('setup') @@ -524,10 +606,40 @@ sub check { || $self->ut_numbern('cancel') || $self->ut_numbern('adjourn') || $self->ut_numbern('expire') + || $self->ut_numbern('dundate') || $self->ut_enum('no_auto', [ '', 'Y' ]) + || $self->ut_enum('waive_setup', [ '', 'Y' ]) + || $self->ut_numbern('agent_pkgid') + || $self->ut_enum('recur_show_zero', [ '', 'Y', 'N', ]) + || $self->ut_enum('setup_show_zero', [ '', 'Y', 'N', ]) ; return $error if $error; + return "A package with both start date (future start) and setup date (already started) will never bill" + if $self->start_date && $self->setup; + + $self->usernum($FS::CurrentUser::CurrentUser->usernum) unless $self->usernum; + + if ( $self->dbdef_table->column('manual_flag') ) { + $self->manual_flag('') if $self->manual_flag eq ' '; + $self->manual_flag =~ /^([01]?)$/ + or return "Illegal manual_flag ". $self->manual_flag; + $self->manual_flag($1); + } + + $self->SUPER::check; +} + +=item check_pkgpart + +=cut + +sub check_pkgpart { + my $self = shift; + + my $error = $self->ut_numbern('pkgpart'); + return $error if $error; + if ( $self->reg_code ) { unless ( grep { $self->pkgpart == $_->pkgpart } @@ -563,16 +675,8 @@ sub check { } - $self->usernum($FS::CurrentUser::CurrentUser->usernum) unless $self->usernum; - - if ( $self->dbdef_table->column('manual_flag') ) { - $self->manual_flag('') if $self->manual_flag eq ' '; - $self->manual_flag =~ /^([01]?)$/ - or return "Illegal manual_flag ". $self->manual_flag; - $self->manual_flag($1); - } + ''; - $self->SUPER::check; } =item cancel [ OPTION => VALUE ... ] @@ -665,72 +769,59 @@ sub cancel { } } - my %svc; - if ( $date ) { -# copied from below - foreach my $cust_svc ( - #schwartz - map { $_->[0] } - sort { $a->[1] <=> $b->[1] } - map { [ $_, $_->svc_x->table_info->{'cancel_weight'} ]; } - qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } ) - ) { - my $error = $cust_svc->cancel( ('date' => $date) ); + my %svc_cancel_opt = (); + $svc_cancel_opt{'date'} = $date if $date; + foreach my $cust_svc ( + #schwartz + map { $_->[0] } + sort { $a->[1] <=> $b->[1] } + map { [ $_, $_->svc_x ? $_->svc_x->table_info->{'cancel_weight'} : -1 ]; } + qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } ) + ) { + my $part_svc = $cust_svc->part_svc; + next if ( defined($part_svc) and $part_svc->preserve ); + my $error = $cust_svc->cancel( %svc_cancel_opt ); - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "Error expiring cust_svc: $error"; - } + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return 'Error '. ($svc_cancel_opt{'date'} ? 'expiring' : 'canceling' ). + " cust_svc: $error"; } - } else { #!date - foreach my $cust_svc ( - #schwartz - map { $_->[0] } - sort { $a->[1] <=> $b->[1] } - map { [ $_, $_->svc_x->table_info->{'cancel_weight'} ]; } - qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } ) - ) { - my $error = $cust_svc->cancel; + } - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "Error cancelling cust_svc: $error"; - } + unless ($date) { + + # Add a credit for remaining service + my $last_bill = $self->getfield('last_bill') || 0; + my $next_bill = $self->getfield('bill') || 0; + my $do_credit; + if ( exists($options{'unused_credit'}) ) { + $do_credit = $options{'unused_credit'}; + } + else { + $do_credit = $self->part_pkg->option('unused_credit_cancel', 1); } - } #if $date + if ( $do_credit + and $last_bill > 0 # the package has been billed + and $next_bill > 0 # the package has a next bill date + and $next_bill >= $cancel_time # which is in the future + ) { + my $remaining_value = $self->calc_remain('time' => $cancel_time); + if ( $remaining_value > 0 ) { + my $error = $self->cust_main->credit( + $remaining_value, + 'Credit for unused time on '. $self->part_pkg->pkg, + 'reason_type' => $conf->config('cancel_credit_type'), + ); + if ($error) { + $dbh->rollback if $oldAutoCommit; + return "Error crediting customer \$$remaining_value for unused time". + " on ". $self->part_pkg->pkg. ": $error"; + } + } #if $remaining_value + } #if $do_credit - # Add a credit for remaining service - my $last_bill = $self->getfield('last_bill') || 0; - my $next_bill = $self->getfield('bill') || 0; - my $do_credit; - if ( exists($options{'unused_credit'}) ) { - $do_credit = $options{'unused_credit'}; - } - else { - $do_credit = $self->part_pkg->option('unused_credit_cancel', 1); - } - if ( $do_credit - and $last_bill > 0 # the package has been billed - and $next_bill > 0 # the package has a next bill date - and $next_bill >= $cancel_time # which is in the future - ) { - my $remaining_value = $self->calc_remain('time' => $cancel_time); - if ( $remaining_value > 0 ) { - # && !$options{'no_credit'} ) { - # Undocumented, unused option. - # part_pkg configuration should decide this anyway. - my $error = $self->cust_main->credit( - $remaining_value, - 'Credit for unused time on '. $self->part_pkg->pkg, - 'reason_type' => $conf->config('cancel_credit_type'), - ); - if ($error) { - $dbh->rollback if $oldAutoCommit; - return "Error crediting customer \$$remaining_value for unused time on". - $self->part_pkg->pkg. ": $error"; - } - } #if $remaining_value - } #if $do_credit + } #unless $date my %hash = $self->hash; $date ? ($hash{'expire'} = $date) : ($hash{'cancel'} = $cancel_time); @@ -909,6 +1000,19 @@ sub suspend { } } + my %hash = $self->hash; + if ( $date ) { + $hash{'adjourn'} = $date; + } else { + $hash{'susp'} = $suspend_time; + } + my $new = new FS::cust_pkg ( \%hash ); + $error = $new->replace( $self, options => { $self->options } ); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + unless ( $date ) { my @labels = (); @@ -964,19 +1068,6 @@ sub suspend { } - my %hash = $self->hash; - if ( $date ) { - $hash{'adjourn'} = $date; - } else { - $hash{'susp'} = $suspend_time; - } - my $new = new FS::cust_pkg ( \%hash ); - $error = $new->replace( $self, options => { $self->options } ); - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return $error; - } - $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; #no errors @@ -1339,7 +1430,6 @@ sub change { } -use Data::Dumper; use Storable 'thaw'; use MIME::Base64; sub process_bulk_cust_pkg { @@ -1646,11 +1736,13 @@ sub num_cust_event { $sth->fetchrow_arrayref->[0]; } -=item cust_svc [ SVCPART ] +=item cust_svc [ SVCPART ] (old, deprecated usage) + +=item cust_svc [ OPTION => VALUE ... ] (current usage) Returns the services for this package, as FS::cust_svc objects (see -L). If a svcpart is specified, return only the matching -services. +L). Available options are svcpart and svcdb. If either is +spcififed, returns only the matching services. =cut @@ -1659,9 +1751,25 @@ sub cust_svc { return () unless $self->num_cust_svc(@_); - if ( @_ ) { - return qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum, - 'svcpart' => shift, } ); + my %opt = (); + if ( @_ && $_[0] =~ /^\d+/ ) { + $opt{svcpart} = shift; + } elsif ( @_ && ref($_[0]) eq 'HASH' ) { + %opt = %{ $_[0] }; + } elsif ( @_ ) { + %opt = @_; + } + + my %search = ( + 'table' => 'cust_svc', + 'hashref' => { 'pkgnum' => $self->pkgnum }, + ); + if ( $opt{svcpart} ) { + $search{hashref}->{svcpart} = $opt{'svcpart'}; + } + if ( $opt{'svcdb'} ) { + $search{addl_from} = ' LEFT JOIN part_svc USING ( svcpart ) '; + $search{hashref}->{svcdb} = $opt{'svcdb'}; } cluck "cust_pkg->cust_svc called" if $DEBUG > 2; @@ -1669,9 +1777,7 @@ sub cust_svc { #if ( $self->{'_svcnum'} ) { # values %{ $self->{'_svcnum'}->cache }; #} else { - $self->_sort_cust_svc( - [ qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } ) ] - ); + $self->_sort_cust_svc( [ qsearch(\%search) ] ); #} } @@ -1701,6 +1807,9 @@ I flag will be omitted. sub h_cust_svc { my $self = shift; + warn "$me _h_cust_svc called on $self\n" + if $DEBUG; + my ($end, $start, $mode) = @_; my @cust_svc = $self->_sort_cust_svc( [ qsearch( 'h_cust_svc', @@ -1708,11 +1817,10 @@ sub h_cust_svc { FS::h_cust_svc->sql_h_search(@_), ) ] ); - if ( $mode eq 'I' ) { + if ( defined($mode) && $mode eq 'I' ) { my %hidden_svcpart = map { $_->svcpart => $_->hidden } $self->part_svc; return grep { !$hidden_svcpart{$_->svcpart} } @cust_svc; - } - else { + } else { return @cust_svc; } } @@ -1737,10 +1845,12 @@ sub _sort_cust_svc { } -=item num_cust_svc [ SVCPART ] +=item num_cust_svc [ SVCPART ] (old, deprecated usage) -Returns the number of provisioned services for this package. If a svcpart is -specified, counts only the matching services. +=item num_cust_svc [ OPTION => VALUE ... ] (current usage) + +Returns the number of services for this package. Available options are svcpart +and svcdb. If either is spcififed, returns only the matching services. =cut @@ -1755,11 +1865,31 @@ sub num_cust_svc { cluck "cust_pkg->num_cust_svc called, _num_cust_svc:".$self->{'_num_cust_svc'} if $DEBUG > 2; - my $sql = 'SELECT COUNT(*) FROM cust_svc WHERE pkgnum = ?'; - $sql .= ' AND svcpart = ?' if @_; + my %opt = (); + if ( @_ && $_[0] =~ /^\d+/ ) { + $opt{svcpart} = shift; + } elsif ( @_ && ref($_[0]) eq 'HASH' ) { + %opt = %{ $_[0] }; + } elsif ( @_ ) { + %opt = @_; + } + + my $select = 'SELECT COUNT(*) FROM cust_svc '; + my $where = ' WHERE pkgnum = ? '; + my @param = ($self->pkgnum); - my $sth = dbh->prepare($sql) or die dbh->errstr; - $sth->execute($self->pkgnum, @_) or die $sth->errstr; + if ( $opt{'svcpart'} ) { + $where .= ' AND svcpart = ? '; + push @param, $opt{'svcpart'}; + } + if ( $opt{'svcdb'} ) { + $select .= ' LEFT JOIN part_svc USING ( svcpart ) '; + $where .= ' AND svcdb = ? '; + push @param, $opt{'svcdb'}; + } + + my $sth = dbh->prepare("$select $where") or die dbh->errstr; + $sth->execute(@param) or die $sth->errstr; $sth->fetchrow_arrayref->[0]; } @@ -1790,7 +1920,7 @@ sub available_part_svc { $self->part_pkg->pkg_svc; } -=item part_svc +=item part_svc [ OPTION => VALUE ... ] Returns a list of FS::part_svc objects representing provisioned and available services included in this package. Each FS::part_svc object also has the @@ -1804,15 +1934,20 @@ following extra fields: =item cust_pkg_svc (services) - array reference containing the provisioned services, as cust_svc objects -svcnum -label -> ($cust_svc->label)[1] - =back +Accepts one option: summarize_size. If specified and non-zero, will omit the +extra cust_pkg_svc option for objects where num_cust_svc is this size or +greater. + =cut +#svcnum +#label -> ($cust_svc->label)[1] + sub part_svc { my $self = shift; + my %opt = @_; #XXX some sort of sort order besides numeric by svcpart... my @part_svc = sort { $a->svcpart <=> $b->svcpart } map { @@ -1823,7 +1958,9 @@ sub part_svc { $part_svc->{'Hash'}{'num_avail'} = max( 0, $pkg_svc->quantity - $num_cust_svc ); $part_svc->{'Hash'}{'cust_pkg_svc'} = - $num_cust_svc ? [ $self->cust_svc($part_svc->svcpart) ] : []; + $num_cust_svc ? [ $self->cust_svc($part_svc->svcpart) ] : [] + unless exists($opt{summarize_size}) && $opt{summarize_size} > 0 + && $num_cust_svc >= $opt{summarize_size}; $part_svc->{'Hash'}{'hidden'} = $pkg_svc->hidden; $part_svc; } $self->part_pkg->pkg_svc; @@ -1855,7 +1992,7 @@ sub extra_part_svc { my $self = shift; my $pkgnum = $self->pkgnum; - my $pkgpart = $self->pkgpart; + #my $pkgpart = $self->pkgpart; # qsearch( { # 'table' => 'part_svc', @@ -1874,23 +2011,27 @@ sub extra_part_svc { # 'extra_param' => [ [$self->pkgpart=>'int'], [$self->pkgnum=>'int'] ], # } ); -#seems to benchmark slightly faster... +#seems to benchmark slightly faster... (or did?) + + my @pkgparts = map $_->pkgpart, $self->part_pkg->self_and_svc_linked; + my $pkgparts = join(',', @pkgparts); + qsearch( { #'select' => 'DISTINCT ON (svcpart) part_svc.*', #MySQL doesn't grok DISINCT ON 'select' => 'DISTINCT part_svc.*', 'table' => 'part_svc', 'addl_from' => - 'LEFT JOIN pkg_svc ON ( pkg_svc.svcpart = part_svc.svcpart - AND pkg_svc.pkgpart = ? + "LEFT JOIN pkg_svc ON ( pkg_svc.svcpart = part_svc.svcpart + AND pkg_svc.pkgpart IN ($pkgparts) AND quantity > 0 ) LEFT JOIN cust_svc ON ( cust_svc.svcpart = part_svc.svcpart ) LEFT JOIN cust_pkg USING ( pkgnum ) - ', + ", 'hashref' => {}, 'extra_sql' => "WHERE pkgsvcnum IS NULL AND cust_pkg.pkgnum = ? ", - 'extra_param' => [ [$self->pkgpart=>'int'], [$self->pkgnum=>'int'] ], + 'extra_param' => [ [$self->pkgnum=>'int'] ], } ); } @@ -2054,6 +2195,8 @@ Returns a list of lists, calling the label method for all (historical) services sub h_labels { my $self = shift; + warn "$me _h_labels called on $self\n" + if $DEBUG; map { [ $_->label(@_) ] } $self->h_cust_svc(@_); } @@ -2086,31 +2229,53 @@ sub h_labels_short { sub _labels_short { my( $self, $method ) = ( shift, shift ); + warn "$me _labels_short called on $self with $method method\n" + if $DEBUG; + my $conf = new FS::Conf; my $max_same_services = $conf->config('cust_bill-max_same_services') || 5; + warn "$me _labels_short populating \%labels\n" + if $DEBUG; + my %labels; #tie %labels, 'Tie::IxHash'; push @{ $labels{$_->[0]} }, $_->[1] foreach $self->$method(@_); + + warn "$me _labels_short populating \@labels\n" + if $DEBUG; + my @labels; foreach my $label ( keys %labels ) { my %seen = (); my @values = grep { ! $seen{$_}++ } @{ $labels{$label} }; my $num = scalar(@values); + warn "$me _labels_short $num items for $label\n" + if $DEBUG; + if ( $num > $max_same_services ) { + warn "$me _labels_short more than $max_same_services, so summarizing\n" + if $DEBUG; push @labels, "$label ($num)"; } else { if ( $conf->exists('cust_bill-consolidate_services') ) { + warn "$me _labels_short consolidating services\n" + if $DEBUG; # push @labels, "$label: ". join(', ', @values); while ( @values ) { my $detail = "$label: "; $detail .= shift(@values). ', ' - while @values && length($detail.$values[0]) < 78; + while @values + && ( length($detail.$values[0]) < 78 || $detail eq "$label: " ); $detail =~ s/, $//; push @labels, $detail; } + warn "$me _labels_short done consolidating services\n" + if $DEBUG; } else { + warn "$me _labels_short adding service data\n" + if $DEBUG; push @labels, map { "$label: $_" } @values; } } @@ -2500,12 +2665,12 @@ sub insert_discount { 'discountnum' => $self->discountnum, 'months_used' => 0, 'end_date' => '', #XXX - 'otaker' => $self->otaker, #for the create a new discount case '_type' => $self->discountnum__type, 'amount' => $self->discountnum_amount, 'percent' => $self->discountnum_percent, 'months' => $self->discountnum_months, + 'setup' => $self->discountnum_setup, #'disabled' => $self->discountnum_disabled, }; @@ -2525,7 +2690,8 @@ All svc_accts which are part of this package have their values reset. sub set_usage { my ($self, $valueref, %opt) = @_; - foreach my $cust_svc ($self->cust_svc){ + #only svc_acct can set_usage for now + foreach my $cust_svc ( $self->cust_svc( 'svcdb'=>'svc_acct' ) ) { my $svc_x = $cust_svc->svc_x; $svc_x->set_usage($valueref, %opt) if $svc_x->can("set_usage"); @@ -2545,7 +2711,8 @@ All svc_accts which are part of this package have their values incremented. sub recharge { my ($self, $valueref) = @_; - foreach my $cust_svc ($self->cust_svc){ + #only svc_acct can set_usage for now + foreach my $cust_svc ( $self->cust_svc( 'svcdb'=>'svc_acct' ) ) { my $svc_x = $cust_svc->svc_x; $svc_x->recharge($valueref) if $svc_x->can("recharge"); @@ -2836,45 +3003,56 @@ sub search { # 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"; + if ( exists($params->{'classnum'}) ) { + + my @classnum = (); + if ( ref($params->{'classnum'}) ) { + + if ( ref($params->{'classnum'}) eq 'HASH' ) { + @classnum = grep $params->{'classnum'}{$_}, keys %{ $params->{'classnum'} }; + } elsif ( ref($params->{'classnum'}) eq 'ARRAY' ) { + @classnum = @{ $params->{'classnum'} }; + } else { + die 'unhandled classnum ref '. $params->{'classnum'}; + } + + + } elsif ( $params->{'classnum'} =~ /^(\d*)$/ && $1 ne '0' ) { + @classnum = ( $1 ); } + + if ( @classnum ) { + + my @c_where = (); + my @nums = grep $_, @classnum; + push @c_where, 'part_pkg.classnum IN ('. join(',',@nums). ')' if @nums; + my $null = scalar( grep { $_ eq '' } @classnum ); + push @c_where, 'part_pkg.classnum IS NULL' if $null; + + if ( scalar(@c_where) == 1 ) { + push @where, @c_where; + } elsif ( @c_where ) { + push @where, ' ( '. join(' OR ', @c_where). ' ) '; + } + warn $where[-1]; + + } + + } - #eslaf ### # parse package report options ### my @report_option = (); - if ( exists($params->{'report_option'}) - && $params->{'report_option'} =~ /^([,\d]*)$/ - ) - { - @report_option = split(',', $1); + if ( exists($params->{'report_option'}) ) { + if ( ref($params->{'report_option'}) eq 'ARRAY' ) { + @report_option = @{ $params->{'report_option'} }; + } elsif ( $params->{'report_option'} =~ /^([,\d]*)$/ ) { + @report_option = split(',', $1); + } + } if (@report_option) { @@ -2887,7 +3065,27 @@ sub search { } @report_option; } - #eslaf + foreach my $any ( grep /^report_option_any/, keys %$params ) { + + my @report_option_any = (); + if ( ref($params->{$any}) eq 'ARRAY' ) { + @report_option_any = @{ $params->{$any} }; + } elsif ( $params->{$any} =~ /^([,\d]*)$/ ) { + @report_option_any = split(',', $1); + } + + if (@report_option_any) { + # this will result in the empty set for the dangling comma case as it should + push @where, ' ( '. join(' OR ', + 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_any + ). ' ) '; + } + + } ### # parse custom @@ -2899,7 +3097,8 @@ sub search { # parse fcc_line ### - push @where, "part_pkg.fcc_ds0s > 0" if $params->{fcc_line}; + push @where, "(part_pkg.fcc_ds0s > 0 OR pkg_class.fcc_ds0s > 0)" + if $params->{fcc_line}; ### # parse censustract @@ -2913,6 +3112,21 @@ sub search { } ### + # parse censustract2 + ### + if ( exists($params->{'censustract2'}) + && $params->{'censustract2'} =~ /^(\d*)$/ + ) + { + if ($1) { + push @where, "cust_main.censustract LIKE '$1%'"; + } else { + push @where, + "( cust_main.censustract = '' OR cust_main.censustract IS NULL )"; + } + } + + ### # parse part_pkg ### @@ -3177,6 +3391,15 @@ sub _location_sql_where { "; } +sub _X_show_zero { + my( $self, $what ) = @_; + + my $what_show_zero = $what. '_show_zero'; + length($self->$what_show_zero()) + ? ($self->$what_show_zero() eq 'Y') + : $self->part_pkg->$what_show_zero(); +} + =head1 SUBROUTINES =over 4 @@ -3396,6 +3619,10 @@ sub _upgrade_data { # class method 'UPDATE cust_pkg SET bill = bill + (365*24*60*60) WHERE bill < last_bill AND bill > 1259654400 AND bill < 1262332800 AND (SELECT plan FROM part_pkg WHERE part_pkg.pkgpart = cust_pkg.pkgpart) = \'prorate\'', + # RT6628, add order_date to cust_pkg + 'update cust_pkg set order_date = (select history_date from h_cust_pkg + where h_cust_pkg.pkgnum = cust_pkg.pkgnum and + history_action = \'insert\') where order_date is null', ); foreach my $sql (@statements) { my $sth = dbh->prepare($sql);