X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fcust_pkg.pm;h=e3fb1558e83b2fb6cba2ad3b9f56b0a6a183078a;hp=85234cfec5591dfb68055436626a3291ce05741c;hb=7f97d2688290e99de6aecec08a32254dae365105;hpb=341adb07d0f22f7386b0d1055aa5d6d6120198ca diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index 85234cfec..e3fb1558e 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -442,6 +442,21 @@ sub insert { my $conf = new FS::Conf; + if ($self->locationnum) { + my @part_export = + map qsearch( 'part_export', {exportnum=>$_} ), + $conf->config('cust_location-exports'); #, $agentnum + + foreach my $part_export ( @part_export ) { + my $error = $part_export->export_pkg_location($self); #, @$export_args); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "exporting to ". $part_export->exporttype. + " (transaction rolled back): $error"; + } + } + } + if ( ! $import && $conf->config('ticket_system') && $options{ticket_subject} ) { #this init stuff is still inefficient, but at least its limited to @@ -696,6 +711,24 @@ sub replace { } } + # also run exports if removing locationnum? + # doesn't seem to happen, and we don't export blank locationnum on insert... + if ($new->locationnum and ($new->locationnum != $old->locationnum)) { + my $conf = new FS::Conf; + my @part_export = + map qsearch( 'part_export', {exportnum=>$_} ), + $conf->config('cust_location-exports'); #, $agentnum + + foreach my $part_export ( @part_export ) { + my $error = $part_export->export_pkg_location($new); #, @$export_args); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "exporting to ". $part_export->exporttype. + " (transaction rolled back): $error"; + } + } + } + $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; @@ -1096,6 +1129,135 @@ sub cancel_if_expired { ''; } +=item uncancel_svc_x + +For cancelled cust_pkg, returns a list of new, uninserted FS::svc_X records +for services that would be inserted by L. Returned objects also +include the field '_uncancel_svcnum' that contains the original svcnum. +Set pkgnum before inserting. + +Accepts the following options: + +summarize_size - if true, returns empty list if number of potential services is +equal to or greater than this + +only_svcnum - arrayref of svcnum, only returns objects for these svcnum +(and only if they would otherwise be returned by this) + +=cut + +sub uncancel_svc_x { + my ($self, %opt) = @_; + + die 'uncancel_svc_x called on a non-cancelled cust_pkg' unless $self->get('cancel'); + + #find historical services within this timeframe before the package cancel + # (incompatible with "time" option to cust_pkg->cancel?) + my $fuzz = 2 * 60; #2 minutes? too much? (might catch separate unprovision) + # too little? (unprovisioing export delay?) + my($end, $start) = ( $self->get('cancel'), $self->get('cancel') - $fuzz ); + my @h_cust_svc = $self->h_cust_svc( $end, $start ); + + return () if $opt{'summarize_size'} and @h_cust_svc >= $opt{'summarize_size'}; + + my @svc_x; + foreach my $h_cust_svc (@h_cust_svc) { + next if $opt{'only_svcnum'} && !(grep { $_ == $h_cust_svc->svcnum } @{$opt{'only_svcnum'}}); + my $h_svc_x = $h_cust_svc->h_svc_x( $end, $start ); + #next unless $h_svc_x; #should this happen? + (my $table = $h_svc_x->table) =~ s/^h_//; + require "FS/$table.pm"; + my $class = "FS::$table"; + my $svc_x = $class->new( { + 'svcpart' => $h_cust_svc->svcpart, + '_uncancel_svcnum' => $h_cust_svc->svcnum, + map { $_ => $h_svc_x->get($_) } fields($table) + } ); + + # radius_usergroup + if ( $h_svc_x->isa('FS::h_svc_Radius_Mixin') ) { + $svc_x->usergroup( [ $h_svc_x->h_usergroup($end, $start) ] ); + } + + #these are pretty rare, but should handle them + # - dsl_device (mac addresses) + # - phone_device (mac addresses) + # - dsl_note (ikano notes) + # - domain_record (i.e. restore DNS information w/domains) + # - inventory_item(?) (inventory w/un-cancelling service?) + # - nas (svc_broaband nas stuff) + #this stuff is unused in the wild afaik + # - mailinglistmember + # - router.svcnum? + # - svc_domain.parent_svcnum? + # - acct_snarf (ancient mail fetching config) + # - cgp_rule (communigate) + # - cust_svc_option (used by our Tron stuff) + # - acct_rt_transaction (used by our time worked stuff) + + push @svc_x, $svc_x; + } + return @svc_x; +} + +=item uncancel_svc_summary + +Returns an array of hashrefs, one for each service that could +potentially be reprovisioned by L, with the following keys: + +svcpart + +svc + +uncancel_svcnum + +label + +reprovisionable - 1 if test reprovision succeeded, otherwise 0 + +Cannot be run from within a transaction. Performs inserts +to test the results, and then rolls back the transaction. +Does not perform exports, so does not catch if export would fail. + +Also accepts the following options: + +summarize_size - if true, returns empty list if number of potential services is +equal to or greater than this + +=cut + +sub uncancel_svc_summary { + my ($self, %opt) = @_; + + die 'uncancel_svc_summary called on a non-cancelled cust_pkg' unless $self->get('cancel'); + die 'uncancel_svc_summary called from within a transaction' unless $FS::UID::AutoCommit; + + local $FS::svc_Common::noexport_hack = 1; # very important not to run exports!!! + local $FS::UID::AutoCommit = 0; + + my @out; + foreach my $svc_x ($self->uncancel_svc_x(%opt)) { + $svc_x->pkgnum($self->pkgnum); # provisioning services on a canceled package, will be rolled back + my $part_svc = $svc_x->part_svc; + my $out = { + 'svcpart' => $part_svc->svcpart, + 'svc' => $part_svc->svc, + 'uncancel_svcnum' => $svc_x->get('_uncancel_svcnum'), + }; + if ($svc_x->insert) { # if error inserting + $out->{'label'} = "(cannot re-provision)"; + $out->{'reprovisionable'} = 0; + } else { + $out->{'label'} = $svc_x->label; + $out->{'reprovisionable'} = 1; + } + push @out, $out; + } + + dbh->rollback; + return @out; +} + =item uncancel "Un-cancels" this package: Orders a new package with the same custnum, pkgpart, @@ -1108,6 +1270,8 @@ svc_fatal: service provisioning errors are fatal svc_errors: pass an array reference, will be filled in with any provisioning errors +only_svcnum: arrayref, only attempt to re-provision these cancelled services + main_pkgnum: link the package as a supplemental package of this one. For internal use only. @@ -1164,32 +1328,12 @@ sub uncancel { # insert services ## - #find historical services within this timeframe before the package cancel - # (incompatible with "time" option to cust_pkg->cancel?) - my $fuzz = 2 * 60; #2 minutes? too much? (might catch separate unprovision) - # too little? (unprovisioing export delay?) - my($end, $start) = ( $self->get('cancel'), $self->get('cancel') - $fuzz ); - my @h_cust_svc = $self->h_cust_svc( $end, $start ); - my @svc_errors; - foreach my $h_cust_svc (@h_cust_svc) { - my $h_svc_x = $h_cust_svc->h_svc_x( $end, $start ); - #next unless $h_svc_x; #should this happen? - (my $table = $h_svc_x->table) =~ s/^h_//; - require "FS/$table.pm"; - my $class = "FS::$table"; - my $svc_x = $class->new( { - 'pkgnum' => $cust_pkg->pkgnum, - 'svcpart' => $h_cust_svc->svcpart, - map { $_ => $h_svc_x->get($_) } fields($table) - } ); - - # radius_usergroup - if ( $h_svc_x->isa('FS::h_svc_Radius_Mixin') ) { - $svc_x->usergroup( [ $h_svc_x->h_usergroup($end, $start) ] ); - } + foreach my $svc_x ($self->uncancel_svc_x('only_svcnum' => $options{'only_svcnum'})) { + $svc_x->pkgnum($cust_pkg->pkgnum); my $svc_error = $svc_x->insert; + if ( $svc_error ) { if ( $options{svc_fatal} ) { $dbh->rollback if $oldAutoCommit; @@ -1213,23 +1357,7 @@ sub uncancel { } } # svc_fatal } # svc_error - } #foreach $h_cust_svc - - #these are pretty rare, but should handle them - # - dsl_device (mac addresses) - # - phone_device (mac addresses) - # - dsl_note (ikano notes) - # - domain_record (i.e. restore DNS information w/domains) - # - inventory_item(?) (inventory w/un-cancelling service?) - # - nas (svc_broaband nas stuff) - #this stuff is unused in the wild afaik - # - mailinglistmember - # - router.svcnum? - # - svc_domain.parent_svcnum? - # - acct_snarf (ancient mail fetching config) - # - cgp_rule (communigate) - # - cust_svc_option (used by our Tron stuff) - # - acct_rt_transaction (used by our time worked stuff) + } #foreach uncancel_svc_x ## # also move over any services that didn't unprovision at cancellation @@ -1272,14 +1400,15 @@ sub uncancel { =item unexpire -Cancels any pending expiration (sets the expire field to null). +Cancels any pending expiration (sets the expire field to null) +for this package and any supplemental packages. If there is an error, returns the error, otherwise returns false. =cut sub unexpire { - my( $self, %options ) = @_; + my( $self ) = @_; my $error; my $oldAutoCommit = $FS::UID::AutoCommit; @@ -1309,6 +1438,14 @@ sub unexpire { return $error; } + foreach my $supp_pkg ( $self->supplemental_pkgs ) { + $error = $supp_pkg->unexpire; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "unexpiring supplemental pkg#".$supp_pkg->pkgnum.": $error"; + } + } + $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; #no errors @@ -1916,14 +2053,15 @@ sub unsuspend { =item unadjourn -Cancels any pending suspension (sets the adjourn field to null). +Cancels any pending suspension (sets the adjourn field to null) +for this package and any supplemental packages. If there is an error, returns the error, otherwise returns false. =cut sub unadjourn { - my( $self, %options ) = @_; + my( $self ) = @_; my $error; my $oldAutoCommit = $FS::UID::AutoCommit; @@ -1960,6 +2098,14 @@ sub unadjourn { return $error; } + foreach my $supp_pkg ( $self->supplemental_pkgs ) { + $error = $supp_pkg->unadjourn; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "unadjourning supplemental pkg#".$supp_pkg->pkgnum.": $error"; + } + } + $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; #no errors @@ -2132,7 +2278,7 @@ sub change { my $time = time; - $hash{'setup'} = $time if $self->setup; + $hash{'setup'} = $time if $self->get('setup'); $hash{'change_date'} = $time; $hash{"change_$_"} = $self->$_() @@ -2153,16 +2299,18 @@ sub change { my $unused_credit = 0; my $keep_dates = $opt->{'keep_dates'}; - # Special case. If the pkgpart is changing, and the customer is - # going to be credited for remaining time, don't keep setup, bill, - # or last_bill dates, and DO pass the flag to cancel() to credit - # the customer. + # Special case. If the pkgpart is changing, and the customer is going to be + # credited for remaining time, don't keep setup, bill, or last_bill dates, + # and DO pass the flag to cancel() to credit the customer. If the old + # package had a setup date, set the new package's setup to the package + # change date so that it has the same status as before. if ( $opt->{'pkgpart'} and $opt->{'pkgpart'} != $self->pkgpart and $self->part_pkg->option('unused_credit_change', 1) ) { $unused_credit = 1; $keep_dates = 0; - $hash{$_} = '' foreach qw(setup bill last_bill); + $hash{'last_bill'} = ''; + $hash{'bill'} = ''; } if ( $keep_dates ) { @@ -2502,6 +2650,16 @@ sub change_later { return "start_date $date is in the past"; } + # If the user entered a new location, set it up now. + if ( $opt->{'cust_location'} ) { + $error = $opt->{'cust_location'}->find_or_insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "creating location record: $error"; + } + $opt->{'locationnum'} = $opt->{'cust_location'}->locationnum; + } + if ( $self->change_to_pkgnum ) { my $change_to = FS::cust_pkg->by_key($self->change_to_pkgnum); my $new_pkgpart = $opt->{'pkgpart'} @@ -3261,16 +3419,15 @@ sub cust_svc_unsorted_arrayref { } my %search = ( - 'table' => 'cust_svc', - 'hashref' => { 'pkgnum' => $self->pkgnum }, + 'select' => 'cust_svc.*, part_svc.*', + 'table' => 'cust_svc', + 'hashref' => { 'pkgnum' => $self->pkgnum }, + 'addl_from' => 'LEFT JOIN part_svc USING ( svcpart )', ); - if ( $opt{svcpart} ) { - $search{hashref}->{svcpart} = $opt{'svcpart'}; - } - if ( $opt{'svcdb'} ) { - $search{addl_from} = ' LEFT JOIN part_svc USING ( svcpart ) '; - $search{extra_sql} = ' AND svcdb = '. dbh->quote( $opt{'svcdb'} ); - } + $search{hashref}->{svcpart} = $opt{svcpart} + if $opt{svcpart}; + $search{extra_sql} = ' AND svcdb = '. dbh->quote( $opt{svcdb} ) + if $opt{svcdb}; [ qsearch(\%search) ];