summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS/Conf.pm7
-rw-r--r--FS/FS/Mason.pm1
-rw-r--r--FS/FS/Schema.pm12
-rw-r--r--FS/FS/Tron.pm93
-rw-r--r--FS/FS/cust_svc.pm12
-rw-r--r--FS/FS/cust_svc_option.pm136
-rw-r--r--FS/MANIFEST2
-rw-r--r--FS/t/cust_svc_option.t5
-rwxr-xr-xbin/tron-scan24
-rw-r--r--httemplate/elements/dashboard-toplist.html10
-rw-r--r--httemplate/elements/mcp_lint.html37
11 files changed, 333 insertions, 6 deletions
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index 728a2136b..08b299fba 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -2357,6 +2357,13 @@ worry that config_items is freeside-specific and icky.
'type' => 'text',
},
+ {
+ 'key' => 'mcp_svcpart',
+ 'section' => '',
+ 'description' => 'Master Control Program svcpart. Leave this blank.',
+ 'type' => 'text',
+ },
+
);
1;
diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
index 219f6b738..77ff66822 100644
--- a/FS/FS/Mason.pm
+++ b/FS/FS/Mason.pm
@@ -93,6 +93,7 @@ Initializes the Mason environment, loads all Freeside and RT libraries, etc.
use FS::Misc qw( send_email send_fax states_hash counties state_label );
use FS::Report::Table::Monthly;
use FS::TicketSystem;
+ use FS::Tron qw( tron_lint );
use FS::agent;
use FS::agent_type;
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index e461fc153..86cf4a36c 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -1003,6 +1003,18 @@ sub tables_hashref {
'index' => [ ['svcnum'], ['pkgnum'], ['svcpart'] ],
},
+ 'cust_svc_option' => {
+ 'columns' => [
+ 'optionnum', 'serial', '', '', '', '',
+ 'svcnum', 'int', '', '', '', '',
+ 'optionname', 'varchar', '', $char_d, '', '',
+ 'optionvalue', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'optionnum',
+ 'unique' => [],
+ 'index' => [ [ 'svcnum' ], [ 'optionname' ] ],
+ },
+
'part_pkg' => {
'columns' => [
'pkgpart', 'serial', '', '', '', '',
diff --git a/FS/FS/Tron.pm b/FS/FS/Tron.pm
new file mode 100644
index 000000000..33280955e
--- /dev/null
+++ b/FS/FS/Tron.pm
@@ -0,0 +1,93 @@
+package FS::Tron;
+# a program to monitor outside systems
+
+use strict;
+use warnings;
+use base 'Exporter';
+use Net::SSH qw( sshopen2 ); #sshopen3 );
+use FS::Record qw( qsearchs );
+use FS::svc_external;
+use FS::cust_svc_option;
+
+our @EXPORT_OK = qw( tron_scan tron_lint);
+
+our %desired = (
+ #lenient for now, so we can fix up important stuff
+ 'freeside_version' => qr/^1\.(7\.3|9\.0)/,
+ 'debian_version' => qr/^4/,
+ 'apache_mpm' => qw/^(Prefork|$)/,
+
+ #stuff to add/replace later
+ #'pg_version' => qr/^8\.[1-9]/,
+ #'apache_version' => qr/^2/,
+ #'apache_mpm' => qw/^Prefork/,
+);
+
+sub tron_scan {
+ my $cust_svc = shift;
+
+ my $svc_external;
+ if ( ref($cust_svc) ) {
+ $svc_external = $cust_svc->svc_x;
+ } else {
+ $svc_external = qsearchs('svc_external', { 'svcnum' => $cust_svc } );
+ $cust_svc = $svc_external->cust_svc;
+ }
+
+ #don't scan again if things are okay
+ my $bad = 0;
+ foreach my $option ( keys %desired ) {
+ my $current = $cust_svc->option($option);
+ $bad++ unless $current =~ $desired{$option};
+ }
+ return '' unless $bad;
+
+ #do the scan
+ my %hash = ();
+ my $machine = $svc_external->title; # or better as a cust_svc_option??
+ sshopen2($machine, *READER, *WRITER, '/usr/local/bin/freeside-yori all');
+ while (<READER>) {
+ chomp;
+ my($option, $value) = split(/: ?/);
+ next unless defined($option) && exists($desired{$option});
+ $hash{$option} = $value;
+ }
+ close READER;
+
+ unless ( keys %hash ) {
+ return "error scanning $machine\n";
+ }
+
+ # store the results
+ foreach my $option ( keys %hash ) {
+ my %opthash = ( 'optionname' => $option,
+ 'svcnum' => $cust_svc->svcnum,
+ );
+ my $cust_svc_option = qsearchs('cust_svc_option', \%opthash )
+ || new FS::cust_svc_option \%opthash;
+ next if $cust_svc_option->optionvalue eq $hash{$option};
+ $cust_svc_option->optionvalue( $hash{$option} );
+ my $error = $cust_svc_option->optionnum
+ ? $cust_svc_option->replace
+ : $cust_svc_option->insert;
+ return $error if $error;
+ }
+
+ '';
+
+}
+
+sub tron_lint {
+ my $cust_svc = shift;
+
+ my @lint;
+ foreach my $option ( keys %desired ) {
+ my $current = $cust_svc->option($option);
+ push @lint, "$option is $current" unless $current =~ $desired{$option};
+ }
+
+ @lint;
+
+}
+
+1;
diff --git a/FS/FS/cust_svc.pm b/FS/FS/cust_svc.pm
index 381b97f14..2d6224c92 100644
--- a/FS/FS/cust_svc.pm
+++ b/FS/FS/cust_svc.pm
@@ -3,6 +3,7 @@ package FS::cust_svc;
use strict;
use vars qw( @ISA $DEBUG $me $ignore_quantity );
use Carp;
+#use Scalar::Util qw( blessed );
use FS::Conf;
use FS::Record qw( qsearch qsearchs dbh str2time_sql );
use FS::cust_pkg;
@@ -16,7 +17,7 @@ use FS::cdr;
#most FS::svc_ classes are autoloaded in svc_x emthod
use FS::svc_acct; #this one is used in the cache stuff
-@ISA = qw( FS::cust_main_Mixin FS::Record );
+@ISA = qw( FS::cust_main_Mixin FS::option_Common ); #FS::Record );
$DEBUG = 0;
$me = '[cust_svc]';
@@ -220,7 +221,13 @@ returns the error, otherwise returns false.
=cut
sub replace {
+# my $new = shift;
+#
+# my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
+# ? shift
+# : $new->replace_old;
my ( $new, $old ) = ( shift, shift );
+ $old = $new->replace_old unless defined($old);
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
@@ -233,8 +240,6 @@ sub replace {
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
- $old = $new->replace_old unless defined($old);
-
if ( $new->svcpart != $old->svcpart ) {
my $svc_x = $new->svc_x;
my $new_svc_x = ref($svc_x)->new({$svc_x->hash, svcpart=>$new->svcpart });
@@ -246,6 +251,7 @@ sub replace {
}
}
+ #my $error = $new->SUPER::replace($old, @_);
my $error = $new->SUPER::replace($old);
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
diff --git a/FS/FS/cust_svc_option.pm b/FS/FS/cust_svc_option.pm
new file mode 100644
index 000000000..0a242d551
--- /dev/null
+++ b/FS/FS/cust_svc_option.pm
@@ -0,0 +1,136 @@
+package FS::cust_svc_option;
+
+use strict;
+use vars qw( @ISA );
+#use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_svc_option - Object methods for cust_svc_option records
+
+=head1 SYNOPSIS
+
+ use FS::cust_svc_option;
+
+ $record = new FS::cust_svc_option \%hash;
+ $record = new FS::cust_svc_option { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_svc_option object represents an customer service option.
+ FS::cust_svc_option inherits from FS::Record. The following fields are
+ currently supported:
+
+=over 4
+
+=item optionnum
+
+primary key
+
+=item svcnum
+
+svcnum (see L<FS::cust_svc>)
+
+=item optionname
+
+Option Name
+
+=item optionvalue
+
+Option Value
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new option. To add the option 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 { 'cust_svc_option'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=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.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid option. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('optionnum')
+ || $self->ut_foreign_key('svcnum', 'cust_svc', 'svcnum')
+ || $self->ut_alpha('optionname')
+ || $self->ut_anything('optionvalue')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+The author forgot to customize this manpage.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/MANIFEST b/FS/MANIFEST
index d83bf1daa..5dcd69649 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -418,3 +418,5 @@ t/pkg_category.t
FS/phone_avail.pm
t/phone_avail.t
FS/Yori.pm
+FS/cust_svc_option.pm
+t/cust_svc_option.t
diff --git a/FS/t/cust_svc_option.t b/FS/t/cust_svc_option.t
new file mode 100644
index 000000000..eeaa170db
--- /dev/null
+++ b/FS/t/cust_svc_option.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_svc_option;
+$loaded=1;
+print "ok 1\n";
diff --git a/bin/tron-scan b/bin/tron-scan
new file mode 100755
index 000000000..914d6d407
--- /dev/null
+++ b/bin/tron-scan
@@ -0,0 +1,24 @@
+#!/usr/bin/perl
+
+use FS::UID qw(adminsuidsetup);
+use FS::Conf;
+use FS::Record qw(qsearch);
+use FS::Tron qw(tron_scan tron_lint);
+use FS::cust_svc;
+
+adminsuidsetup shift;
+
+my $conf = new FS::Conf;
+my $mcp_svcpart = $conf->config('mcp_svcpart') or die "no mcp_svcpart";
+
+#tron_scan($_) foreach qsearch('cust_svc', { 'svcpart' => $mcp_svcpart } );
+foreach my $svc ( qsearch('cust_svc', { 'svcpart' => $mcp_svcpart } ) ) {
+ my $error = tron_scan($svc);
+ warn $error if $error;
+
+ my @lint = tron_lint($svc);
+ print $svc->svc_x->title. "\n". join('', map " $_\n", @lint )
+ if @lint;
+}
+
+1;
diff --git a/httemplate/elements/dashboard-toplist.html b/httemplate/elements/dashboard-toplist.html
index 7ee6f2d43..d8cd7f306 100644
--- a/httemplate/elements/dashboard-toplist.html
+++ b/httemplate/elements/dashboard-toplist.html
@@ -23,6 +23,9 @@
<TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
<A HREF="view/cust_main.cgi?<% $custnum %>"><% $cust_main->name %></A>
</TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% include('/elements/mcp_lint.html', 'cust_main'=>$cust_main) %>
+ </TD>
<TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ALIGN="right">
<FONT SIZE="-1"><A HREF="<% FS::TicketSystem->href_new_ticket($cust_main, join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list ) ) %>">(new ticket)</A></FONT>
</TD>
@@ -55,7 +58,7 @@
% } elsif ( $line =~ /^\-\-+$/ ) { #divider
%
<TR>
- <TH CLASS="grid" COLSPAN="<% scalar(@custom_priorities) + 3 %>"></TH>
+ <TH CLASS="grid" COLSPAN="<% scalar(@custom_priorities) + 4 %>"></TH>
</TR>
% next;
@@ -63,13 +66,14 @@
% } elsif ( $line =~ /^\s*$/ ) {
<TR>
- <TD CLASS="grid" COLSPAN="<% scalar(@custom_priorities) + 3 %>" BGCOLOR="<% $bgcolor %>">&nbsp;</TD>
+ <TD CLASS="grid" COLSPAN="<% scalar(@custom_priorities) + 4 %>" BGCOLOR="<% $bgcolor %>">&nbsp;</TD>
</TR>
% } elsif ( $line =~ /^\S/ ) { #label line
<TR>
<TH CLASS="grid" BGCOLOR="#cccccc"><% $line %></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Lint</TH>
<TH CLASS="grid" BGCOLOR="#cccccc"></TH>
% foreach my $priority ( @custom_priorities, '' ) {
<TH CLASS="grid" BGCOLOR="#cccccc">
@@ -81,7 +85,7 @@
% } else { #regular line
<TR>
- <TD CLASS="grid" COLSPAN="<% scalar(@custom_priorities) + 3 %>" BGCOLOR="<% $bgcolor %>"><% $line %></TD>
+ <TD CLASS="grid" COLSPAN="<% scalar(@custom_priorities) + 4 %>" BGCOLOR="<% $bgcolor %>"><% $line %></TD>
</TR>
% }
diff --git a/httemplate/elements/mcp_lint.html b/httemplate/elements/mcp_lint.html
new file mode 100644
index 000000000..826549c65
--- /dev/null
+++ b/httemplate/elements/mcp_lint.html
@@ -0,0 +1,37 @@
+% foreach my $lint (@lint) {
+ <FONT COLOR="#FF0000"><% $lint %></FONT><BR>
+% }
+
+<%init>
+
+my(%opt) = @_;
+
+my $conf = new FS::Conf;
+
+my @svc = ();
+if ( $opt{svc} ) {
+ @svc = ref($opt{svc}) ? @{ $opt{svc} } : ( $opt{svc} );
+} elsif ( $opt{cust_main} ) {
+ my $custnum = $opt{cust_main}->custnum;
+ @svc = qsearchs({
+ 'table' => 'cust_svc',
+ 'addl_from' => ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'svcpart' => $conf->config('mcp_svcpart') },
+ 'extra_sql' => " AND custnum = $custnum ",
+ });
+} else {
+ die 'neither svc nor cust_main options passed to mcp_lint';
+}
+
+my @lint = ();
+foreach my $svc ( @svc ) {
+ my @svc_lint = tron_lint($svc);
+ if ( scalar(@svc) > 1 ) {
+ push @lint, map $svc->title.": $_", @svc_lint;
+ } else {
+ push @lint, @svc_lint;
+ }
+}
+
+</%init>