X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_pkg.pm;h=e2d337546cb5b624a5f72c9bdc04453db740c5e5;hb=5bb3332639ffadd71b00ad41fd2a7cbb038092ab;hp=10472fc3640d84b6c01271328bb43cf81319b415;hpb=262b110b9cb124f62acec0f3cbd23826d6140f4b;p=freeside.git diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index 10472fc36..e2d337546 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -242,7 +242,8 @@ The following options are available: =item change -If set true, supresses any referral credit to a referring customer. +If set true, supresses actions that should only be taken for new package +orders. (Currently this includes: intro periods when delay_setup is on.) =item options @@ -268,6 +269,7 @@ sub insert { my $part_pkg = $self->part_pkg; + # if the package def says to start only on the first of the month: 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; @@ -275,6 +277,8 @@ sub insert { $self->start_date( timelocal_nocheck(0,0,0,1,$mon,$year) ); } + # set up any automatic expire/adjourn/contract_end timers + # based on the start date foreach my $action ( qw(expire adjourn contract_end) ) { my $months = $part_pkg->option("${action}_months",1); if($months and !$self->$action) { @@ -283,12 +287,16 @@ sub insert { } } - 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); + # if this package has "free days" and delayed setup fee, tehn + # set start date that many days in the future. + # (this should have been set in the UI, but enforce it here) + if ( ! $options{'change'} + && ( my $free_days = $part_pkg->option('free_days',1) ) + && $part_pkg->option('delay_setup',1) + #&& ! $self->start_date + ) + { + $self->start_date( $part_pkg->default_start_date ); } $self->order_date(time); @@ -553,9 +561,12 @@ sub replace { } - my $error = $new->SUPER::replace($old, - $options->{options} ? $options->{options} : () - ); + my $error = $new->export_pkg_change($old) + || $new->SUPER::replace( $old, + $options->{options} + ? $options->{options} + : () + ); if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; @@ -979,15 +990,20 @@ sub uncancel { $dbh->rollback if $oldAutoCommit; return $svc_error; } else { + # if we've failed to insert the svc_x object, svc_Common->insert + # will have removed the cust_svc already. if not, then both records + # were inserted but we failed for some other reason (export, most + # likely). in that case, report the error and delete the records. push @svc_errors, $svc_error; - # is this necessary? svc_Common::insert already deletes the - # cust_svc if inserting svc_x fails. my $cust_svc = qsearchs('cust_svc', { 'svcnum' => $svc_x->svcnum }); if ( $cust_svc ) { - my $cs_error = $cust_svc->delete; - if ( $cs_error ) { + # except if export_insert failed, export_delete probably won't be + # much better + local $FS::svc_Common::noexport_hack = 1; + my $cleanup_error = $svc_x->delete; # also deletes cust_svc + if ( $cleanup_error ) { # and if THAT fails, then run away $dbh->rollback if $oldAutoCommit; - return $cs_error; + return $cleanup_error; } } } # svc_fatal @@ -1256,7 +1272,8 @@ sub suspend { Unsuspends all services (see L and L) in this package, then unsuspends the package itself (clears the susp field and the -adjourn field if it is in the past). +adjourn field if it is in the past). If the suspend reason includes an +unsuspension package, that package will be ordered. Available options are: @@ -1360,6 +1377,9 @@ sub unsuspend { } + my $cust_pkg_reason = $self->last_cust_pkg_reason('susp'); + my $reason = $cust_pkg_reason ? $cust_pkg_reason->reason : ''; + my %hash = $self->hash; my $inactive = time - $hash{'susp'}; @@ -1386,6 +1406,33 @@ sub unsuspend { return $error; } + my $unsusp_pkg; + + if ( $reason && $reason->unsuspend_pkgpart ) { + my $part_pkg = FS::part_pkg->by_key($reason->unsuspend_pkgpart) + or $error = "Unsuspend package definition ".$reason->unsuspend_pkgpart. + " not found."; + my $start_date = $self->cust_main->next_bill_date + if $reason->unsuspend_hold; + + if ( $part_pkg ) { + $unsusp_pkg = FS::cust_pkg->new({ + 'custnum' => $self->custnum, + 'pkgpart' => $reason->unsuspend_pkgpart, + 'start_date' => $start_date, + 'locationnum' => $self->locationnum, + # discount? probably not... + }); + + $error ||= $self->cust_main->order_pkg( 'cust_pkg' => $unsusp_pkg ); + } + + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + if ( $conf->config('unsuspend_email_admin') ) { my $error = send_email( @@ -1399,6 +1446,11 @@ sub unsuspend { 'Customer: #'. $self->custnum. ' '. $self->cust_main->name. "\n", 'Package : #'. $self->pkgnum. " (". $self->part_pkg->pkg_comment. ")\n", ( map { "Service : $_\n" } @labels ), + ($unsusp_pkg ? + "An unsuspension fee was charged: ". + $unsusp_pkg->part_pkg->pkg_comment."\n" + : '' + ), ], ); @@ -1980,14 +2032,30 @@ sub num_cust_event { =item cust_svc [ OPTION => VALUE ... ] (current usage) +=item cust_svc_unsorted [ OPTION => VALUE ... ] + Returns the services for this package, as FS::cust_svc objects (see L). Available options are svcpart and svcdb. If either is spcififed, returns only the matching services. +As an optimization, use the cust_svc_unsorted version if you are not displaying +the results. + =cut sub cust_svc { my $self = shift; + cluck "cust_pkg->cust_svc called" if $DEBUG > 2; + $self->_sort_cust_svc( $self->cust_svc_unsorted_arrayref(@_) ); +} + +sub cust_svc_unsorted { + my $self = shift; + @{ $self->cust_svc_unsorted_arrayref(@_) }; +} + +sub cust_svc_unsorted_arrayref { + my $self = shift; return () unless $self->num_cust_svc(@_); @@ -2012,13 +2080,7 @@ sub cust_svc { $search{extra_sql} = ' AND svcdb = '. dbh->quote( $opt{'svcdb'} ); } - cluck "cust_pkg->cust_svc called" if $DEBUG > 2; - - #if ( $self->{'_svcnum'} ) { - # values %{ $self->{'_svcnum'}->cache }; - #} else { - $self->_sort_cust_svc( [ qsearch(\%search) ] ); - #} + [ qsearch(\%search) ]; } @@ -2536,6 +2598,18 @@ sub cust_main { qsearchs( 'cust_main', { 'custnum' => $self->custnum } ); } +=item balance + +Returns the balance for this specific package, when using +experimental package balance. + +=cut + +sub balance { + my $self = shift; + $self->cust_main->balance_pkgnum( $self->pkgnum ); +} + #these subs are in location_Mixin.pm now... unfortunately the POD doesn't mixin =item cust_location @@ -2805,6 +2879,39 @@ sub reexport { } +=item export_pkg_change OLD_CUST_PKG + +Calls the "pkg_change" export action for all services attached to this package. + +=cut + +sub export_pkg_change { + my( $self, $old ) = ( shift, 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 $svc_x ( map $_->svc_x, $self->cust_svc ) { + my $error = $svc_x->export('pkg_change', $self, $old); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + ''; + +} + =item insert_reason Associates this package with a (suspension or cancellation) reason (see @@ -3171,7 +3278,14 @@ specifies the user for agent virtualization =item fcc_line - boolean selects packages containing fcc form 477 telco lines +boolean; if true, returns only packages with more than 0 FCC phone lines + +=item state, country + +Limit to packages whose customer is located in the specified state and +country. For FCC 477 reporting. This will use the customer's service +address if there is one, but isn't yet smart enough to use the package +address. =back @@ -3366,6 +3480,20 @@ sub search { } ### + # parse country/state + ### + + for (qw(state country)) { + if ( exists($params->{$_}) + && uc($params->{$_}) =~ /^([A-Z]{2})$/ ) + { + push @where, + "COALESCE(cust_location.$_, cust_main.ship_$_, cust_main.$_) = '$1'"; + } + } + + + ### # parse part_pkg ### @@ -3489,7 +3617,8 @@ sub search { 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 ) '; + 'LEFT JOIN pkg_class ON ( part_pkg.classnum = pkg_class.classnum ) '. + 'LEFT JOIN cust_location USING ( locationnum ) '; my $select; my $count_query; @@ -3498,7 +3627,6 @@ sub search { $select = "DISTINCT substr($zip,1,5) as zip"; $orderby = "ORDER BY substr($zip,1,5)"; - $addl_from .= 'LEFT JOIN cust_location ON ( locationnum )'; $count_query = "SELECT COUNT( DISTINCT substr($zip,1,5) )"; } else { $select = join(', ',