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 ) {
'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' => [
--- /dev/null
+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<FS::agent>) for
+agent-specific dependencies
+
+=item src_svcpart
+
+Source service definition (see L<FS::part_svc>)
+
+=item dst_svcpart
+
+Destination service definition (see L<FS::part_svc>)
+
+=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<hash> 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<link_type>.
+
+=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<FS::part_svc>).
+
+=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<FS::part_svc>).
+
+=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<FS::part_svc>, L<FS::Record>
+
+=cut
+
+1;
+
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
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
--- /dev/null
+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";
--- /dev/null
+<& 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' ];
+
+</%init>
}
</&>
</script>
-<& /elements/table.html &>
- <TR><TH COLSPAN=<% $columns %>>Exports</TH></TR>
+<FONT CLASS="fsinnerbox-title">Exports</FONT>
+<TABLE CLASS="fsinnerbox">
<TR>
% # exports
% foreach my $part_export (@part_export) {
% }
% }
</TR>
-</TABLE><BR><BR>
+</TABLE><BR>
);
</%once>
<INPUT TYPE="hidden" NAME="svcdb" VALUE="<% $svcdb %>">
-<BR><BR>
+<BR>
%# 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.
+
+<FONT CLASS="fsinnerbox-title">Fields</FONT>
<& /elements/table-grid.html, cellpadding => 4 &>
<TR>
<TH BGCOLOR="#cccccc">Field</TH>
<BR>
-<BR>
-Table <% $widget->html %>
+<FONT SIZE="+1"><B>Table</B></FONT> <% $widget->html %>
-<% include('/elements/footer.html') %>
+<& /elements/footer.html &>
<%init>
--- /dev/null
+<& 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'
+);
+</%init>
--- /dev/null
+<& elements/process.html, table=>'part_svc_link', viewall_dir=>'browse' &>
+<%init>
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+</%init>
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, '' ]