From 7d34aacffa38c4cac09b54080487a66c264e4668 Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Thu, 30 Apr 2015 02:34:51 -0700 Subject: [PATCH] service dependencies: UI, RT#33685 --- FS/FS/Mason.pm | 1 + FS/FS/Schema.pm | 25 +++ FS/FS/part_svc_link.pm | 249 ++++++++++++++++++++++++++ FS/MANIFEST | 5 +- FS/t/part_svc_link.t | 5 + httemplate/browse/part_svc_link.html | 32 ++++ httemplate/edit/elements/export_svc.html | 6 +- httemplate/edit/elements/part_svc_column.html | 7 +- httemplate/edit/part_svc.cgi | 5 +- httemplate/edit/part_svc_link.html | 51 ++++++ httemplate/edit/process/part_svc_link.html | 5 + httemplate/elements/menu.html | 1 + 12 files changed, 380 insertions(+), 12 deletions(-) create mode 100644 FS/FS/part_svc_link.pm create mode 100644 FS/t/part_svc_link.t create mode 100644 httemplate/browse/part_svc_link.html create mode 100644 httemplate/edit/part_svc_link.html create mode 100644 httemplate/edit/process/part_svc_link.html diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm index d9b91887e..78779d78a 100644 --- a/FS/FS/Mason.pm +++ b/FS/FS/Mason.pm @@ -401,6 +401,7 @@ if ( -e $addl_handler_use_file ) { use FS::legacy_cust_history; use FS::quotation_pkg_tax; use FS::cust_pkg_reason_fee; + use FS::part_svc_link; # Sammath Naur if ( $FS::Mason::addl_handler_use ) { diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 42122f700..114acb8fd 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -3618,6 +3618,31 @@ sub tables_hashref { 'index' => [ ['disabled'] ], }, + 'part_svc_link' => { + 'columns' => [ + 'svclinknum', 'serial', '', '', '', '', + #'linkname', 'varchar', 'NULL', $char_d, '', '', + 'agentnum', 'int', 'NULL', '', '', '', + 'src_svcpart', 'int', '', '', '', '', + 'dst_svcpart', 'int', '', '', '', '', + 'link_type', 'varchar', '', $char_d, '', '', + 'disabled', 'char', 'NULL', 1, '', '', + ], + 'primary_key' => 'svclinknum', + 'unique' => [ ['agentnum','src_svcpart','dst_svcpart','link_type'] ], + 'index' => [ [ 'src_svcpart' ] ], + 'foreign_keys' => [ + { columns => [ 'src_svcpart' ], + table => 'part_svc', + references => [ 'svcpart' ] + }, + { columns => [ 'dst_svcpart' ], + table => 'part_svc', + references => [ 'svcpart' ] + }, + ], + }, + #(this should be renamed to part_pop) 'svc_acct_pop' => { 'columns' => [ diff --git a/FS/FS/part_svc_link.pm b/FS/FS/part_svc_link.pm new file mode 100644 index 000000000..cf82a90db --- /dev/null +++ b/FS/FS/part_svc_link.pm @@ -0,0 +1,249 @@ +package FS::part_svc_link; +use base qw( FS::Record ); + +use strict; +use FS::Record qw( qsearchs ); # qw( qsearch qsearchs ); + +=head1 NAME + +FS::part_svc_link - Object methods for part_svc_link records + +=head1 SYNOPSIS + + use FS::part_svc_link; + + $record = new FS::part_svc_link \%hash; + $record = new FS::part_svc_link { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::part_svc_link object represents an service definition dependency. +FS::part_svc_link inherits from FS::Record. The following fields are currently +supported: + +=over 4 + +=item svclinknum + +primary key + +=cut + +#=item linkname +# +#Dependency name + +=item agentnum + +Empty for global dependencies, or agentnum (see L) for +agent-specific dependencies + +=item src_svcpart + +Source service definition (see L) + +=item dst_svcpart + +Destination service definition (see L) + +=item link_type + +Link type: + +=over 4 + +=cut + +# XXX false laziness w/edit/part_svc_link.html + +=item part_svc_restrict + +In package defintions, require the destination service definition when the +source service definition is included + +=item part_svc_restrict_soft + +Soft order block: in package definitions, warn if the destination service +definition is included without the source service definition + +=item cust_svc_provision_restrict + +Require the destination service to be provisioned before the source service + +=item cust_svc_unprovision_restrict + +Require the destination service to be unprovisioned before the source service + +=item cust_svc_unprovision_cascade + +Automatically unprovision the destination service when the source service is +unprovisioned + +=item cust_svc_suspend_cascade + +Suspend the destination service before the source service + +=back + +=back + +=head1 METHODS + +=over 4 + +=item new HASHREF + +Creates a new record. To add the record to the database, see L<"insert">. + +Note that this stores the hash reference, not a distinct copy of the hash it +points to. You can ask the object for a copy with the I method. + +=cut + +# the new method can be inherited from FS::Record, if a table method is defined + +sub table { 'part_svc_link'; } + +=item insert + +Adds this record to the database. If there is an error, returns the error, +otherwise returns false. + +=item delete + +Delete this record from the database. + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=item check + +Checks all fields to make sure this is a valid record. If there is +an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +=cut + +sub check { + my $self = shift; + + my $error = + $self->ut_numbern('svclinknum') + #|| $self->ut_textn('linkname') + || $self->ut_number('src_svcpart') + || $self->ut_number('dst_svcpart') + || $self->ut_text('link_type') + || $self->ut_enum('disabled', [ '', 'Y' ]) + ; + return $error if $error; + + $self->SUPER::check; +} + +=item description + +Returns an extended description of this dependency, including. Exact wording +depends on I. + +=cut + +sub description { + my $self = shift; + + my $src = $self->src_part_svc->svc; + my $dst = $self->dst_part_svc->svc; + + #maybe sub-classes with overrides at some point + # (and hooks each place we have manual checks for the various rules) + # but this will do for now + + $self->link_type eq 'part_svc_restrict' + and return "In package definitions, $dst is required when $src is included"; + + $self->link_type eq 'part_svc_restrict_soft' + and return "In package definitions, $dst is suggested when $src is included"; + + $self->link_type eq 'cust_svc_provision_restrict' + and return "Require $dst provisioning before $src"; + + $self->link_type eq 'cust_svc_unprovision_restrict' + and return "Require $dst unprovisioning before $src"; + + $self->link_type eq 'cust_svc_unprovision_cascade' + and return "Automatically unprovision $dst when $src is unprovisioned"; + + $self->link_type eq 'cust_svc_suspend_cascade' + and return "Suspend $dst before $src"; + + warn "WARNING: unknown part_svc_link.link_type ". $self->link_type. "\n"; + return "$src (unknown link_type ". $self->link_type. ") $dst"; + +} + +=item src_part_svc + +Returns the source service definition, as an FS::part_svc object (see +L). + +=cut + +sub src_part_svc { + my $self = shift; + qsearchs('part_svc', { svcpart=>$self->src_svcpart } ); +} + +=item src_svc + +Returns the source service definition name (part_svc.svc). + +=cut + +sub src_svc { + shift->src_part_svc->svc; +} + +=item dst_part_svc + +Returns the destination service definition, as an FS::part_svc object (see +L). + +=cut + + +sub dst_part_svc { + my $self = shift; + qsearchs('part_svc', { svcpart=>$self->dst_svcpart } ); +} + +=item dst_svc + +Returns the destination service definition name (part_svc.svc). + +=cut + +sub dst_svc { + shift->src_part_svc->svc; +} + +=back + +=head1 BUGS + +=head1 SEE ALSO + +L, L + +=cut + +1; + diff --git a/FS/MANIFEST b/FS/MANIFEST index 575184ced..422f69c0e 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -794,7 +794,7 @@ t/cust_bill_pkg_fee.t FS/part_fee_msgcat.pm t/part_fee_msgcat.t FS/part_fee_usage.pm -FS/part_fee_usage.t +t/part_fee_usage.t FS/sched_item.pm t/sched_item.t FS/sched_avail.pm @@ -841,7 +841,8 @@ t/legacy_cust_history.t FS/quotation_pkg_tax.pm t/quotation_pkg_tax.t FS/h_svc_circuit.pm -FS/h_svc_circuit.t FS/FeeOrigin_Mixin.pm FS/cust_pkg_reason_fee.pm t/cust_pkg_reason_fee.t +FS/part_svc_link.pm +t/part_svc_link.t diff --git a/FS/t/part_svc_link.t b/FS/t/part_svc_link.t new file mode 100644 index 000000000..3cac9eff2 --- /dev/null +++ b/FS/t/part_svc_link.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::part_svc_link; +$loaded=1; +print "ok 1\n"; diff --git a/httemplate/browse/part_svc_link.html b/httemplate/browse/part_svc_link.html new file mode 100644 index 000000000..d31acc646 --- /dev/null +++ b/httemplate/browse/part_svc_link.html @@ -0,0 +1,32 @@ +<& elements/browse.html, + 'title' => 'Service dependencies', + 'name_singular' => 'dependency', + 'menubar' => [ 'Add a new service dependency' => + $p.'edit/part_svc_link.html', + ], + 'query' => { 'table' => 'part_svc_link', + 'order_by' => 'ORDER BY src_svcpart', + }, + 'count_query' => 'SELECT COUNT(*) FROM part_svc_link', + 'header' => [ 'Source', 'Dependency', ], + 'fields' => [ 'src_svc', 'description', ], + 'sort_fields' => [], + 'links' => [ $svc_link, $link, ], + 'disableable' => 1, + 'disabled_statuspos' => 1, + 'agent_virt' => 1, + 'agent_null' => 1, + 'agent_null_right' => 'Configuration', + #agent_null_right_link' + 'agent_pos' => 0, +&> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $svc_link = [ "${p}edit/part_svc.cgi?", 'src_svcpart' ]; + +my $link = [ "${p}edit/part_svc_link.html?", 'svclinknum' ]; + + diff --git a/httemplate/edit/elements/export_svc.html b/httemplate/edit/elements/export_svc.html index 5962ae7f8..1735148a4 100644 --- a/httemplate/edit/elements/export_svc.html +++ b/httemplate/edit/elements/export_svc.html @@ -39,8 +39,8 @@ function toggle_selectrole() { } -<& /elements/table.html &> - >Exports +Exports + % # exports % foreach my $part_export (@part_export) { @@ -81,4 +81,4 @@ function toggle_selectrole() { % } % } -


+
diff --git a/httemplate/edit/elements/part_svc_column.html b/httemplate/edit/elements/part_svc_column.html index a6ccaf867..23a6deb56 100644 --- a/httemplate/edit/elements/part_svc_column.html +++ b/httemplate/edit/elements/part_svc_column.html @@ -63,15 +63,14 @@ my %communigate_fields = ( ); -

+
%# include export selection <& export_svc.html, part_svc => $part_svc, svcdb => $svcdb &> -For the selected table, you can give fields default or fixed (unchangeable) -values, or select an inventory class to manually or automatically fill in -that field. + +Fields <& /elements/table-grid.html, cellpadding => 4 &> Field diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi index 7a47f1550..a07fc6005 100755 --- a/httemplate/edit/part_svc.cgi +++ b/httemplate/edit/part_svc.cgi @@ -187,10 +187,9 @@ window.onload = function() {
-
-Table <% $widget->html %> +Table <% $widget->html %> -<% include('/elements/footer.html') %> +<& /elements/footer.html &> <%init> diff --git a/httemplate/edit/part_svc_link.html b/httemplate/edit/part_svc_link.html new file mode 100644 index 000000000..64a99d6f4 --- /dev/null +++ b/httemplate/edit/part_svc_link.html @@ -0,0 +1,51 @@ +<& elements/edit.html, + 'table' => 'part_svc_link', + 'name_singular' => 'dependency', + 'labels' => \%labels, + 'fields' => \@fields, +&> +<%init> +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my @fields = ( + { field => 'agentnum', + type => 'select-agent', + empty_label => '(global)', + }, + { field => 'src_svcpart', + type => 'select-part_svc', + empty_label => 'Select service definition', + }, + { field => 'dst_svcpart', + type => 'select-part_svc', + empty_label => 'Select service definition', + }, + { field => 'link_type', + type => 'select', + #XXX false laziness w/part_svc_link POD documentation + options =>[ qw( + part_svc_restrict part_svc_restrict_soft + cust_svc_provision_restrict cust_svc_unprovision_restrict + cust_svc_unprovision_cascade cust_svc_suspend_cascade + )], + labels => { + part_svc_restrict => 'In package defintions, prevent including the destination service definition unless the source service definition is also included', + part_svc_restrict_soft => 'Soft order block: in package definitions, warn if the destination service definition is included without the source service definition', + cust_svc_provision_restrict => 'Require the target service to be provisioned before the source service', + cust_svc_unprovision_restrict => 'Require the target service to be unprovisioned before the source service', + cust_svc_unprovision_cascade => 'Automatically unprovision the target service when the source service is unprovisioned', + cust_svc_suspend_cascade => 'Suspend the target service before the source service', + }, + }, + { field => 'disabled', type => 'checkbox', value => 'Y' } +); +my %labels = ( + 'svclinknum ' => '', + 'agentnum' => 'Agent', + 'src_svcpart' => 'Source service', + 'dst_svcpart' => 'Destination service', + 'link_type' => 'Dependency type', + 'disabled' => 'Disabled' +); + diff --git a/httemplate/edit/process/part_svc_link.html b/httemplate/edit/process/part_svc_link.html new file mode 100644 index 000000000..ffe979d29 --- /dev/null +++ b/httemplate/edit/process/part_svc_link.html @@ -0,0 +1,5 @@ +<& elements/process.html, table=>'part_svc_link', viewall_dir=>'browse' &> +<%init> +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index 0aefcd77a..e152cb690 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -577,6 +577,7 @@ tie my %config_export_svc, 'Tie::IxHash', (); if ( $curuser->access_right('Configuration') ) { $config_export_svc{'Service definitions'} = [ $fsurl.'browse/part_svc.cgi', 'Services are items you offer to your customers' ]; $config_export_svc{'Service classes'} = [ $fsurl.'browse/part_svc_class.html', 'Services classes are user-defined, informational types for services' ]; + $config_export_svc{'Service dependencies'} = [ $fsurl.'browse/part_svc_link.html', 'Services depencies define rules between service definitions' ]; $config_export_svc{'Provisioning exports'} = [ $fsurl.'browse/part_export.cgi', 'Provisioning services to external machines, databases and APIs' ]; } $config_export_svc{'Dialup'} = [ \%config_dialup, '' ] -- 2.11.0