From 41aef8bd93f7cc3a39056a8fd997d3072dfcdf8a Mon Sep 17 00:00:00 2001 From: Jonathan Prykop Date: Fri, 31 Jul 2015 01:33:11 -0500 Subject: [PATCH] RT#18361: Delay package from billing until services are provisioned --- FS/FS/Schema.pm | 1 + FS/FS/cust_pkg.pm | 33 +++++++++++-------- FS/FS/cust_svc.pm | 63 ++++++++++++++++++++++++++++++++++++ FS/FS/part_pkg.pm | 25 +++++++++----- FS/FS/pkg_svc.pm | 3 ++ httemplate/edit/process/part_pkg.cgi | 2 ++ httemplate/elements/tr-pkg_svc.html | 11 +++++++ 7 files changed, 116 insertions(+), 22 deletions(-) diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index c8b9b631d..184c6c951 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -3543,6 +3543,7 @@ sub tables_hashref { 'primary_svc', 'char', 'NULL', 1, '', '', 'hidden', 'char', 'NULL', 1, '', '', 'bulk_skip', 'char', 'NULL', 1, '', '', + 'provision_hold', 'char', 'NULL', 1, '', '', ], 'primary_key' => 'pkgsvcnum', 'unique' => [ ['pkgpart', 'svcpart'] ], diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index fbecd8d69..c5a3d2e58 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -3389,28 +3389,33 @@ Returns a list of FS::part_svc objects representing services included in this package but not yet provisioned. Each FS::part_svc object also has an extra field, I, which specifies the number of available services. +Accepts option I; if true, only returns part_svc for which the +associated pkg_svc has the provision_hold flag set. + =cut sub available_part_svc { my $self = shift; + my %opt = @_; my $pkg_quantity = $self->quantity || 1; grep { $_->num_avail > 0 } - map { - my $part_svc = $_->part_svc; - $part_svc->{'Hash'}{'num_avail'} = #evil encapsulation-breaking - $pkg_quantity * $_->quantity - $self->num_cust_svc($_->svcpart); - - # more evil encapsulation breakage - if($part_svc->{'Hash'}{'num_avail'} > 0) { - my @exports = $part_svc->part_export_did; - $part_svc->{'Hash'}{'can_get_dids'} = scalar(@exports); - } - - $part_svc; - } - $self->part_pkg->pkg_svc; + map { + my $part_svc = $_->part_svc; + $part_svc->{'Hash'}{'num_avail'} = #evil encapsulation-breaking + $pkg_quantity * $_->quantity - $self->num_cust_svc($_->svcpart); + + # more evil encapsulation breakage + if ($part_svc->{'Hash'}{'num_avail'} > 0) { + my @exports = $part_svc->part_export_did; + $part_svc->{'Hash'}{'can_get_dids'} = scalar(@exports); + } + + $part_svc; + } + grep { $opt{'provision_hold'} ? $_->provision_hold : 1 } + $self->part_pkg->pkg_svc; } =item part_svc [ OPTION => VALUE ... ] diff --git a/FS/FS/cust_svc.pm b/FS/FS/cust_svc.pm index 986c5ae49..c5099fc59 100644 --- a/FS/FS/cust_svc.pm +++ b/FS/FS/cust_svc.pm @@ -102,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->_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 @@ -428,6 +459,9 @@ sub replace { } # if ($svc_x->locationnum) } # if this is a location change + #check if this releases a hold (see FS::pkg_svc provision_hold) + $error ||= $new->_provision_hold; + if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error if $error @@ -1206,6 +1240,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 _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; diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm index 498da8a2b..97bce4537 100644 --- a/FS/FS/part_pkg.pm +++ b/FS/FS/part_pkg.pm @@ -183,7 +183,8 @@ I and I. If I is set to a hashref with svcparts as keys and quantities as values, appropriate FS::pkg_svc records will be inserted. I can be set to a hashref of svcparts and flag values ('Y' or '') to set the -'hidden' field in these records. +'hidden' field in these records, and I can be set similarly +for the 'provision_hold' field in these records. If I is set to the svcpart of the primary service, the appropriate FS::pkg_svc record will be updated. @@ -293,6 +294,7 @@ sub insert { warn " inserting pkg_svc records" if $DEBUG; my $pkg_svc = $options{'pkg_svc'} || {}; my $hidden_svc = $options{'hidden_svc'} || {}; + my $provision_hold = $options{'provision_hold'} || {}; foreach my $part_svc ( qsearch('part_svc', {} ) ) { my $quantity = $pkg_svc->{$part_svc->svcpart} || 0; my $primary_svc = @@ -306,6 +308,7 @@ sub insert { 'quantity' => $quantity, 'primary_svc' => $primary_svc, 'hidden' => $hidden_svc->{$part_svc->svcpart}, + 'provision_hold' => $provision_hold->{$part_svc->svcpart}, } ); my $error = $pkg_svc->insert; if ( $error ) { @@ -386,15 +389,15 @@ sub delete { Replaces OLD_RECORD with this one in the database. If there is an error, returns the error, otherwise returns false. -Currently available options are: I, I, I -and I +Currently available options are: I, I, I, +I, I and I If I is set to a hashref with svcparts as keys and quantities as values, the appropriate FS::pkg_svc records will be replaced. I can be set to a hashref of svcparts and flag values ('Y' or '') to set the -'hidden' field in these records. I can be set to a hashref of -svcparts and flag values ('Y' or '') to set the 'bulk_skip' field in those -records. +'hidden' field in these records. I and I can be set +to a hashref of svcparts and flag values ('Y' or '') to set the respective field +in those records. If I is set to the svcpart of the primary service, the appropriate FS::pkg_svc record will be updated. @@ -532,12 +535,14 @@ sub replace { my $pkg_svc = $options->{'pkg_svc'}; my $hidden_svc = $options->{'hidden_svc'} || {}; my $bulk_skip = $options->{'bulk_skip'} || {}; + my $provision_hold = $options->{'provision_hold'} || {}; if ( $pkg_svc ) { # if it wasn't passed, don't change existing pkg_svcs foreach my $part_svc ( qsearch('part_svc', {} ) ) { my $quantity = $pkg_svc->{$part_svc->svcpart} || 0; my $hidden = $hidden_svc->{$part_svc->svcpart} || ''; my $bulk_skip = $bulk_skip->{$part_svc->svcpart} || ''; + my $provision_hold = $provision_hold->{$part_svc->svcpart} || ''; my $primary_svc = ( defined($options->{'primary_svc'}) && $options->{'primary_svc'} && $options->{'primary_svc'} == $part_svc->svcpart @@ -554,18 +559,21 @@ sub replace { my $old_primary_svc = ''; my $old_hidden = ''; my $old_bulk_skip = ''; + my $old_provision_hold = ''; if ( $old_pkg_svc ) { $old_quantity = $old_pkg_svc->quantity; $old_primary_svc = $old_pkg_svc->primary_svc if $old_pkg_svc->dbdef_table->column('primary_svc'); # is this needed? $old_hidden = $old_pkg_svc->hidden; - $old_bulk_skip = $old_pkg_svc->old_bulk_skip; + $old_bulk_skip = $old_pkg_svc->old_bulk_skip; # should this just be bulk_skip? + $old_provision_hold = $old_pkg_svc->provision_hold; } next unless $old_quantity != $quantity || $old_primary_svc ne $primary_svc || $old_hidden ne $hidden - || $old_bulk_skip ne $bulk_skip; + || $old_bulk_skip ne $bulk_skip + || $old_provision_hold ne $provision_hold; my $new_pkg_svc = new FS::pkg_svc( { 'pkgsvcnum' => ( $old_pkg_svc ? $old_pkg_svc->pkgsvcnum : '' ), @@ -575,6 +583,7 @@ sub replace { 'primary_svc' => $primary_svc, 'hidden' => $hidden, 'bulk_skip' => $bulk_skip, + 'provision_hold' => $provision_hold, } ); my $error = $old_pkg_svc ? $new_pkg_svc->replace($old_pkg_svc) diff --git a/FS/FS/pkg_svc.pm b/FS/FS/pkg_svc.pm index 4efffd9b3..b2dc87042 100644 --- a/FS/FS/pkg_svc.pm +++ b/FS/FS/pkg_svc.pm @@ -47,6 +47,8 @@ definition includes =item hidden - 'Y' to hide this service on invoices, null otherwise. +=item provision_hold - 'Y' to release package hold when all services marked with this are provisioned + =back =head1 METHODS @@ -107,6 +109,7 @@ sub check { || $self->ut_number('svcpart') || $self->ut_number('quantity') || $self->ut_enum('hidden', [ '', 'Y' ] ) + || $self->ut_flag('provision_hold') ; return $error if $error; diff --git a/httemplate/edit/process/part_pkg.cgi b/httemplate/edit/process/part_pkg.cgi index 61a6337e3..b8042026a 100755 --- a/httemplate/edit/process/part_pkg.cgi +++ b/httemplate/edit/process/part_pkg.cgi @@ -160,6 +160,7 @@ my $args_callback = sub { my @svcparts = map { $_->svcpart } qsearch('part_svc', {}); my %pkg_svc = map { $_ => scalar($cgi->param("pkg_svc$_" )) } @svcparts; my %hidden_svc = map { $_ => scalar($cgi->param("hidden$_" )) } @svcparts; + my %provision_hold = map { $_ => scalar($cgi->param("provision_hold$_" )) } @svcparts; my %bulk_skip = map { $_ => ( $cgi->param("no_bulk_skip$_") eq 'Y' ? '' : 'Y' ) @@ -167,6 +168,7 @@ my $args_callback = sub { push @args, 'pkg_svc' => \%pkg_svc, 'hidden_svc' => \%hidden_svc, + 'provision_hold' => \%provision_hold, 'bulk_skip' => \%bulk_skip; ### diff --git a/httemplate/elements/tr-pkg_svc.html b/httemplate/elements/tr-pkg_svc.html index a44c5b9df..cfef51ce2 100644 --- a/httemplate/elements/tr-pkg_svc.html +++ b/httemplate/elements/tr-pkg_svc.html @@ -45,6 +45,12 @@ % } else { % $bulk_skip = $pkg_svc->bulk_skip; % } +% my $provision_hold = ''; +% if ( grep { $_ eq "provision_hold$svcpart" } $cgi->param ) { +% $provision_hold = $cgi->param("hidden_svc$svcpart"); +% } else { +% $provision_hold = $pkg_svc->provision_hold; +% } % % my @exports = $pkg_svc->part_svc->part_export; % foreach my $export ( @exports ) { @@ -72,6 +78,10 @@ > + + > + + % foreach ( 1 .. $columns-1 ) { % if ( $count == int( $_ * scalar(@part_svc) / $columns ) ) { @@ -127,6 +137,7 @@ my $thead = "\n\n". ntable('#cccccc', 2). 'Service'. 'Hide
from
Invoices
'. 'Bulk
Charge
'. + 'Hold
Until
Provision
'. ''; my $part_pkg = $opt{'object'}; -- 2.11.0