X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fpart_pkg.pm;h=97bce4537443e51517ab844e90ec923175cf2489;hp=540919ee5932996621135b7c10fb7a6b8e3e10d2;hb=41aef8bd93f7cc3a39056a8fd997d3072dfcdf8a;hpb=296230b7b4fc12cf532f48fe9253cc94f565d5b5 diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm index 540919ee5..97bce4537 100644 --- a/FS/FS/part_pkg.pm +++ b/FS/FS/part_pkg.pm @@ -30,6 +30,7 @@ use FS::part_pkg_link; use FS::part_pkg_discount; use FS::part_pkg_vendor; use FS::part_pkg_currency; +use FS::part_svc_link; $DEBUG = 0; $setup_hack = 0; @@ -126,6 +127,18 @@ part_pkg, will be equal to pkgpart. ordered. The package will not start billing or have a setup fee charged until it is manually unsuspended. +=item change_to_pkgpart - When this package is ordered, schedule a future +package change. The 'expire_months' field will determine when the package +change occurs. + +=item expire_months - Number of months until this package expires (or changes +to another package). + +=item adjourn_months - Number of months until this package becomes suspended. + +=item contract_end_months - Number of months until the package's contract +ends. + =back =head1 METHODS @@ -170,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. @@ -280,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 = @@ -293,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 ) { @@ -301,6 +317,12 @@ sub insert { } } + my $error = $self->check_pkg_svc(%options); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } if ( $options{'cust_pkg'} ) { @@ -367,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. @@ -513,11 +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 @@ -534,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 : '' ), @@ -555,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) @@ -564,6 +593,13 @@ sub replace { return $error; } } #foreach $part_svc + + my $error = $new->check_pkg_svc(%$options); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } #if $options->{pkg_svc} my @part_pkg_vendor = $old->part_pkg_vendor; @@ -707,6 +743,11 @@ sub check { || $self->ut_numbern('delay_start') || $self->ut_foreign_keyn('successor', 'part_pkg', 'pkgpart') || $self->ut_foreign_keyn('family_pkgpart', 'part_pkg', 'pkgpart') + || $self->ut_numbern('expire_months') + || $self->ut_numbern('adjourn_months') + || $self->ut_numbern('contract_end_months') + || $self->ut_numbern('change_to_pkgpart') + || $self->ut_foreign_keyn('change_to_pkgpart', 'part_pkg', 'pkgpart') || $self->ut_alphan('agent_pkgpartid') || $self->SUPER::check ; @@ -722,6 +763,69 @@ sub check { ''; } +=item check_pkg_svc + +Checks pkg_svc records as a whole (for part_svc_link dependencies). + +If there is an error, returns the error, otherwise returns false. + +=cut + +sub check_pkg_svc { + my( $self, %opt ) = @_; + + my $agentnum = $self->agentnum; + + my %pkg_svc = map { $_->svcpart => $_ } $self->pkg_svc; + + foreach my $svcpart ( keys %pkg_svc ) { + + foreach my $part_svc_link ( $self->part_svc_link( + 'src_svcpart' => $svcpart, + 'link_type' => 'part_pkg_restrict', + ) + ) { + + return $part_svc_link->dst_svc. ' must be included with '. + $part_svc_link->src_svc + unless $pkg_svc{ $part_svc_link->dst_svcpart }; + } + + } + + return '' if $opt{part_pkg_restrict_soft_override}; + + foreach my $svcpart ( keys %pkg_svc ) { + + foreach my $part_svc_link ( $self->part_svc_link( + 'src_svcpart' => $svcpart, + 'link_type' => 'part_pkg_restrict_soft', + ) + ) { + return $part_svc_link->dst_svc. ' is suggested with '. + $part_svc_link->src_svc + unless $pkg_svc{ $part_svc_link->dst_svcpart }; + } + + } + + ''; +} + +=item part_svc_link OPTION => VALUE ... + +Returns the service dependencies (see L) for the given +search options, taking into account this package definition's agent. + +Available options are any field in part_svc_link. Typically used options are +src_svcpart and link_type. + +=cut + +sub part_svc_link { + FS::part_svc_link->by_agentnum( shift->agentnum, @_ ); +} + =item supersede OLD [, OPTION => VALUE ... ] Inserts this package as a successor to the package OLD. All options are as @@ -1314,6 +1418,11 @@ sub option { my %plandata = map { /^(\w+)=(.*)$/; ( $1 => $2 ); } split("\n", $self->get('plandata') ); return $plandata{$opt} if exists $plandata{$opt}; + + # check whether the option is defined in plan info (if so, don't warn) + if (exists $plans{ $self->plan }->{fields}->{$opt}) { + return ''; + } cluck "WARNING: (pkgpart ". $self->pkgpart. ") Package def option $opt ". "not found in options or plandata!\n" unless $ornull; @@ -1613,6 +1722,19 @@ for this package. Returns the voice usage pools (see L) defined for this package. +=item change_to_pkg + +Returns the automatic transfer target for this package, or an empty string +if there isn't one. + +=cut + +sub change_to_pkg { + my $self = shift; + my $pkgpart = $self->change_to_pkgpart or return ''; + FS::part_pkg->by_key($pkgpart); +} + =item _rebless Reblesses the object into the FS::part_pkg::PLAN class (if available), where @@ -2119,6 +2241,19 @@ sub queueable_upgrade { FS::upgrade_journal->set_done($upgrade); } + # migrate adjourn_months, expire_months, and contract_end_months to + # real fields + foreach my $field (qw(adjourn_months expire_months contract_end_months)) { + foreach my $option (qsearch('part_pkg_option', { optionname => $field })) { + my $part_pkg = $option->part_pkg; + my $error = $option->delete; + if ( $option->optionvalue and $part_pkg->get($field) eq '' ) { + $part_pkg->set($field, $option->optionvalue); + $error ||= $part_pkg->replace; + } + die $error if $error; + } + } } =item curuser_pkgs_sql