X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_svc.pm;h=376ef026378c91840f196c3544aa307ca7868f5f;hb=2c112f32561f23f9c538ace00db46659ce16da32;hp=f499bef71816c7ff5844ef19a0755c935ceb7011;hpb=a618055d3a8cfc55a449847b433dcdc85df92bcd;p=freeside.git diff --git a/FS/FS/cust_svc.pm b/FS/FS/cust_svc.pm index f499bef71..376ef0263 100644 --- a/FS/FS/cust_svc.pm +++ b/FS/FS/cust_svc.pm @@ -11,6 +11,7 @@ use FS::Record qw( qsearch qsearchs dbh str2time_sql str2time_sql_closing ); use FS::part_pkg; use FS::part_svc; use FS::pkg_svc; +use FS::part_svc_link; use FS::domain_record; use FS::part_export; use FS::cdr; @@ -101,6 +102,37 @@ sub table { 'cust_svc'; } Adds this service to the database. If there is an error, returns the error, otherwise returns false. +=cut + +sub insert { + 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; + + my $error = $self->SUPER::insert; + + #check if this releases a hold (see FS::pkg_svc provision_hold) + $error ||= $self->_check_provision_hold; + + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error if $error + } + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + ''; #no error + +} + =item delete Deletes this service from the database. If there is an error, returns the @@ -117,8 +149,42 @@ sub delete { my $cust_pkg = $self->cust_pkg; my $custnum = $cust_pkg->custnum if $cust_pkg; + 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; + my $error = $self->SUPER::delete; - return $error if $error; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + + foreach my $part_svc_link ( $self->part_svc_link( + link_type => 'cust_svc_unprovision_cascade', + ) + ) { + foreach my $cust_svc ( qsearch( 'cust_svc', { + 'pkgnum' => $self->pkgnum, + 'svcpart' => $part_svc_link->dst_svcpart, + }) + ) { + my $error = $cust_svc->svc_x->delete; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + + } + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; if ( $ticket_system eq 'RT_Internal' ) { unless ( $rt_session ) { @@ -143,6 +209,40 @@ sub delete { warn "error unlinking ticket $svcnum: $msg\n" if !$val; } } + + ''; + +} + +=item suspend + +Suspends the relevant service by calling the B method of the associated +FS::svc_XXX object (i.e. an FS::svc_acct object or FS::svc_domain object). + +If there is an error, returns the error, otherwise returns false. + +=cut + +sub suspend { + my( $self, %opt ) = @_; + + $self->part_svc->svcdb =~ /^([\w\-]+)$/ or return 'Illegal part_svc.svcdb'; + my $svcdb = $1; + require "FS/$svcdb.pm"; + + my $svc = qsearchs( $svcdb, { 'svcnum' => $self->svcnum } ) + or return ''; + + my $error = $svc->suspend; + return $error if $error; + + if ( $opt{labels_arryref} ) { + my( $label, $value ) = $self->label; + push @{ $opt{labels_arrayref} }, "$label: $value"; + } + + ''; + } =item cancel @@ -334,15 +434,37 @@ sub replace { #trigger a relocate export on location changes if ( $new->cust_pkg->locationnum != $old->cust_pkg->locationnum ) { - $error ||= $new->svc_x->export('relocate', - $new->cust_pkg->cust_location, - $old->cust_pkg->cust_location, + my $svc_x = $new->svc_x; + if ( $svc_x->locationnum ) { + if ( $svc_x->locationnum == $old->cust_pkg->locationnum ) { + # in this case, set the service location to be the same as the new + # package location + $svc_x->set('locationnum', $new->cust_pkg->locationnum); + # and replace it, which triggers a relocate export so we don't + # need to + $error ||= $svc_x->replace; + } else { + # the service already has a different location from its package + # so don't change it + } + } else { + # the service doesn't have a locationnum (either isn't of a type + # that has the locationnum field, or the locationnum is null and + # defaults to cust_pkg->locationnum) + # so just trigger the export here + $error ||= $new->svc_x->export('relocate', + $new->cust_pkg->cust_location, + $old->cust_pkg->cust_location, ); - } + } # if ($svc_x->locationnum) + } # if this is a location change + + #check if this releases a hold (see FS::pkg_svc provision_hold) + $error ||= $new->_check_provision_hold; if ( $error ) { $dbh->rollback if $oldAutoCommit; - return $error if $error; + return $error if $error } $dbh->commit or die $dbh->errstr if $oldAutoCommit; @@ -412,11 +534,76 @@ sub check { " services for pkgnum ". $self->pkgnum if $num_avail <= 0; + #part_svc_link rules (only make sense in pkgpart context, and + # skipping this when ignore_quantity is set DTRT when we're "forcing" + # an implicit change here (location change triggered pkgpart change, + # ->overlimit, bulk customer service changes) + foreach my $part_svc_link ( $self->part_svc_link( + link_type => 'cust_svc_provision_restrict', + ) + ) { + return $part_svc_link->dst_svc. ' must be provisioned before '. + $part_svc_link->src_svc + unless qsearchs({ + 'table' => 'cust_svc', + 'hashref' => { 'pkgnum' => $self->pkgnum, + 'svcpart' => $part_svc_link->dst_svcpart, + }, + 'order_by' => 'LIMIT 1', + }); + } + } $self->SUPER::check; } +=item check_part_svc_link_unprovision + +Checks service dependency unprovision rules for this service. + +If there is an error, returns the error, otherwise returns false. + +=cut + +sub check_part_svc_link_unprovision { + my $self = shift; + + foreach my $part_svc_link ( $self->part_svc_link( + link_type => 'cust_svc_unprovision_restrict', + ) + ) { + return $part_svc_link->dst_svc. ' must be unprovisioned before '. + $part_svc_link->src_svc + if qsearchs({ + 'table' => 'cust_svc', + 'hashref' => { 'pkgnum' => $self->pkgnum, + 'svcpart' => $part_svc_link->dst_svcpart, + }, + 'order_by' => 'LIMIT 1', + }); + } + + ''; +} + +=item part_svc_link + +Returns the service dependencies (see L) for the given +search options, taking into account this service definition as source and +this customer's agent. + +Available options are any field in part_svc_link. Typically used options are +link_type. + +=cut + +sub part_svc_link { + my $self = shift; + my $agentnum = $self->pkgnum ? $self->cust_pkg->cust_main->agentnum : ''; + FS::part_svc_link->by_agentnum($agentnum, src_svcpart=>$self->svcpart, @_); +} + =item display_svcnum Returns the displayed service number for this service: agent_svcid if it has a @@ -1018,9 +1205,10 @@ sub smart_search_param { my @or = map { my $table = $_; my $search_sql = "FS::$table"->search_sql($string); + my $addl_from = "FS::$table"->search_sql_addl_from(); "SELECT $table.svcnum AS svcnum, '$table' AS svcdb ". - "FROM $table WHERE $search_sql"; + "FROM $table $addl_from WHERE $search_sql"; } FS::part_svc->svc_tables; @@ -1053,6 +1241,35 @@ sub smart_search_param { ); } +# If the associated cust_pkg is 'on hold' +# and the associated pkg_svc has the provision_hold flag +# and there are no more available_part_svcs on the cust_pkg similarly flagged, +# then removes hold from pkg +# returns $error or '' on success, +# does not indicate if pkg status was changed +sub _check_provision_hold { + my $self = shift; + + # check status of cust_pkg + my $cust_pkg = $self->cust_pkg; + return '' unless $cust_pkg->status eq 'on hold'; + + # check flag on this svc + # small false laziness with $self->pkg_svc + # to avoid looking up cust_pkg twice + my $pkg_svc = qsearchs( 'pkg_svc', { + 'svcpart' => $self->svcpart, + 'pkgpart' => $cust_pkg->pkgpart, + }); + return '' unless $pkg_svc->provision_hold; + + # check for any others available with that flag + return '' if $cust_pkg->available_part_svc( 'provision_hold' => 1 ); + + # conditions met, remove hold + return $cust_pkg->unsuspend; +} + sub _upgrade_data { my $class = shift;