summaryrefslogtreecommitdiff
path: root/httemplate/browse
diff options
context:
space:
mode:
Diffstat (limited to 'httemplate/browse')
-rw-r--r--httemplate/browse/addr_block.cgi76
-rwxr-xr-xhttemplate/browse/agent.cgi111
-rwxr-xr-xhttemplate/browse/agent_type.cgi60
-rwxr-xr-xhttemplate/browse/cust_main_county.cgi142
-rwxr-xr-xhttemplate/browse/cust_pay_batch.cgi76
-rw-r--r--httemplate/browse/generic.cgi46
-rwxr-xr-xhttemplate/browse/msgcat.cgi50
-rwxr-xr-xhttemplate/browse/nas.cgi80
-rwxr-xr-xhttemplate/browse/part_bill_event.cgi71
-rwxr-xr-xhttemplate/browse/part_export.cgi39
-rwxr-xr-xhttemplate/browse/part_pkg.cgi173
-rwxr-xr-xhttemplate/browse/part_referral.cgi64
-rwxr-xr-xhttemplate/browse/part_svc.cgi133
-rw-r--r--httemplate/browse/part_virtual_field.cgi39
-rwxr-xr-xhttemplate/browse/queue.cgi7
-rw-r--r--httemplate/browse/router.cgi38
-rwxr-xr-xhttemplate/browse/svc_acct_pop.cgi63
17 files changed, 1268 insertions, 0 deletions
diff --git a/httemplate/browse/addr_block.cgi b/httemplate/browse/addr_block.cgi
new file mode 100644
index 000000000..06ac556cf
--- /dev/null
+++ b/httemplate/browse/addr_block.cgi
@@ -0,0 +1,76 @@
+<%= header('Address Blocks', menubar('Main Menu' => $p)) %>
+<%
+
+use NetAddr::IP;
+
+my @addr_block = qsearch('addr_block', {});
+my @router = qsearch('router', {});
+my $block;
+my $p2 = popurl(2);
+my $path = $p2 . "edit/process/addr_block";
+
+%>
+
+<% if ($cgi->param('error')) { %>
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <%=$cgi->param('error')%></FONT>
+ <BR><BR>
+<% } %>
+
+<%=table()%>
+
+<% foreach $block (sort {$a->NetAddr cmp $b->NetAddr} @addr_block) { %>
+ <TR>
+ <TD><%=$block->NetAddr%></TD>
+ <% if (my $router = $block->router) { %>
+ <% if (scalar($block->svc_broadband) == 0) { %>
+ <TD>
+ <%=$router->routername%>
+ </TD>
+ <TD>
+ <FORM ACTION="<%=$path%>/deallocate.cgi" METHOD="POST">
+ <INPUT TYPE="hidden" NAME="blocknum" VALUE="<%=$block->blocknum%>">
+ <INPUT TYPE="submit" NAME="submit" VALUE="Deallocate">
+ </FORM>
+ </TD>
+ <% } else { %>
+ <TD COLSPAN="2">
+ <%=$router->routername%>
+ </TD>
+ <% } %>
+ <% } else { %>
+ <TD>
+ <FORM ACTION="<%=$path%>/allocate.cgi" METHOD="POST">
+ <INPUT TYPE="hidden" NAME="blocknum" VALUE="<%=$block->blocknum%>">
+ <SELECT NAME="routernum" SIZE="1">
+ <% foreach (@router) { %>
+ <OPTION VALUE="<%=$_->routernum %>"><%=$_->routername%></OPTION>
+ <% } %>
+ </SELECT>
+ <INPUT TYPE="submit" NAME="submit" VALUE="Allocate">
+ </FORM>
+ </TD>
+ <TD>
+ <FORM ACTION="<%=$path%>/split.cgi" METHOD="POST">
+ <INPUT TYPE="hidden" NAME="blocknum" VALUE="<%=$block->blocknum%>">
+ <INPUT TYPE="submit" NAME="submit" VALUE="Split">
+ </FORM>
+ </TD>
+ </TR>
+<% }
+ } %>
+ <TR><TD COLSPAN="3"><BR></TD></TR>
+ <TR>
+ <FORM ACTION="<%=$path%>/add.cgi" METHOD="POST">
+ <TD>Gateway/Netmask</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="ip_gateway" SIZE="15">/<INPUT TYPE="text" NAME="ip_netmask" SIZE="2">
+ </TD>
+ <TD>
+ <INPUT TYPE="submit" NAME="submit" VALUE="Add">
+ </TD>
+ </FORM>
+ </TR>
+</TABLE>
+</BODY>
+</HTML>
+
diff --git a/httemplate/browse/agent.cgi b/httemplate/browse/agent.cgi
new file mode 100755
index 000000000..2eef5bb91
--- /dev/null
+++ b/httemplate/browse/agent.cgi
@@ -0,0 +1,111 @@
+<!-- mason kludge -->
+
+<%
+
+ my %search;
+ if ( $cgi->param('showdisabled')
+ || !dbdef->table('agent')->column('disabled') ) {
+ %search = ();
+ } else {
+ %search = ( 'disabled' => '' );
+ }
+
+ #bad false laziness with search/cust_main.cgi (also needs fixing up for
+ #old mysql)
+ my $ncancelled = "
+ 0 < ( SELECT COUNT(*) FROM cust_pkg
+ WHERE cust_pkg.custnum = cust_main.custnum
+ AND ( cust_pkg.cancel IS NULL
+ OR cust_pkg.cancel = 0
+ )
+ )
+ OR 0 = ( SELECT COUNT(*) FROM cust_pkg
+ WHERE cust_pkg.custnum = cust_main.custnum
+ )
+ ";
+
+ my $ncancelled_sth = dbh->prepare("SELECT COUNT(*) FROM cust_main
+ WHERE agentnum = ?
+ AND ( $ncancelled ) ")
+ or die dbh->errstr;
+
+ my $total_sth = dbh->prepare("SELECT COUNT(*) FROM cust_main
+ WHERE agentnum = ? ")
+ or die dbh->errstr;
+
+%>
+
+<%= header('Agent Listing', menubar(
+ 'Main Menu' => $p,
+ 'Agent Types' => $p. 'browse/agent_type.cgi',
+# 'Add new agent' => '../edit/agent.cgi'
+)) %>
+Agents are resellers of your service. Agents may be limited to a subset of your
+full offerings (via their type).<BR><BR>
+<A HREF="<%= $p %>edit/agent.cgi"><I>Add a new agent</I></A><BR><BR>
+
+<% if ( dbdef->table('agent')->column('disabled') ) { %>
+ <%= $cgi->param('showdisabled')
+ ? do { $cgi->param('showdisabled', 0);
+ '( <a href="'. $cgi->self_url. '">hide disabled agents</a> )'; }
+ : do { $cgi->param('showdisabled', 1);
+ '( <a href="'. $cgi->self_url. '">show disabled agents</a> )'; }
+ %>
+<% } %>
+
+<%= table() %>
+<TR>
+ <TH COLSPAN=<%= ( $cgi->param('showdisabled') || !dbdef->table('agent')->column('disabled') ) ? 2 : 3 %>>Agent</TH>
+ <TH>Type</TH>
+ <TH>Customers</TH>
+ <TH><FONT SIZE=-1>Freq.</FONT></TH>
+ <TH><FONT SIZE=-1>Prog.</FONT></TH>
+</TR>
+<%
+# <TH><FONT SIZE=-1>Agent #</FONT></TH>
+# <TH>Agent</TH>
+
+foreach my $agent ( sort {
+ #$a->getfield('agentnum') <=> $b->getfield('agentnum')
+ $a->getfield('agent') cmp $b->getfield('agent')
+} qsearch('agent', \%search ) ) {
+
+ $ncancelled_sth->execute($agent->agentnum) or die $ncancelled_sth->errstr;
+ my $num_ncancelled = $ncancelled_sth->fetchrow_arrayref->[0];
+
+ $total_sth->execute($agent->agentnum) or die $total_sth->errstr;
+ my $num_total = $total_sth->fetchrow_arrayref->[0];
+
+ my $num_cancelled = $num_total - $num_ncancelled;
+
+ my $cust_main_link = $p. 'search/cust_main.cgi?agentnum_on=1&'.
+ 'agentnum='. $agent->agentnum;
+
+%>
+
+ <TR>
+ <TD><A HREF="<%=$p%>edit/agent.cgi?<%= $agent->agentnum %>">
+ <%= $agent->agentnum %></A></TD>
+<% if ( dbdef->table('agent')->column('disabled')
+ && !$cgi->param('showdisabled') ) { %>
+ <TD><%= $agent->disabled ? 'DISABLED' : '' %></TD>
+<% } %>
+
+ <TD><A HREF="<%=$p%>edit/agent.cgi?<%= $agent->agentnum %>">
+ <%= $agent->agent %></A></TD>
+ <TD><A HREF="<%=$p%>edit/agent_type.cgi?<%= $agent->typenum %>"><%= $agent->agent_type->atype %></A></TD>
+ <TD>
+ <FONT COLOR="#00CC00"><B><%= $num_ncancelled %></B></FONT>
+ <A HREF="<%= $cust_main_link %>&showcancelledcustomers=0">active</A>
+ <BR><FONT COLOR="#FF0000"><B><%= $num_cancelled %></B></FONT>
+ <A HREF="<%= $cust_main_link %>&showcancelledcustomers=1&cancelled=1">cancelled</A>
+ </TD>
+ <TD><%= $agent->freq %></TD>
+ <TD><%= $agent->prog %></TD>
+ </TR>
+
+<% } %>
+
+ </TABLE>
+ </BODY>
+</HTML>
diff --git a/httemplate/browse/agent_type.cgi b/httemplate/browse/agent_type.cgi
new file mode 100755
index 000000000..5473804e8
--- /dev/null
+++ b/httemplate/browse/agent_type.cgi
@@ -0,0 +1,60 @@
+<!-- mason kludge -->
+<%= header("Agent Type Listing", menubar(
+ 'Main Menu' => $p,
+ 'Agents' => $p. 'browse/agent.cgi',
+)) %>
+Agent types define groups of packages that you can then assign to particular
+agents.<BR><BR>
+<A HREF="<%= $p %>edit/agent_type.cgi"><I>Add a new agent type</I></A><BR><BR>
+
+<%= table() %>
+<TR>
+ <TH COLSPAN=2>Agent Type</TH>
+ <TH COLSPAN=2>Packages</TH>
+</TR>
+
+<%
+foreach my $agent_type ( sort {
+ $a->getfield('typenum') <=> $b->getfield('typenum')
+} qsearch('agent_type',{}) ) {
+ my $hashref = $agent_type->hashref;
+ #more efficient to do this with SQL...
+ my @type_pkgs = grep { $_->part_pkg and ! $_->part_pkg->disabled }
+ qsearch('type_pkgs',{'typenum'=> $hashref->{typenum} });
+ my $rowspan = scalar(@type_pkgs);
+ $rowspan = int($rowspan/2+0.5) ;
+ print <<END;
+ <TR>
+ <TD ROWSPAN=$rowspan><A HREF="${p}edit/agent_type.cgi?$hashref->{typenum}">
+ $hashref->{typenum}
+ </A></TD>
+ <TD ROWSPAN=$rowspan><A HREF="${p}edit/agent_type.cgi?$hashref->{typenum}">$hashref->{atype}</A></TD>
+END
+
+ my($type_pkgs);
+ my($tdcount) = -1 ;
+ foreach $type_pkgs ( @type_pkgs ) {
+ my($pkgpart)=$type_pkgs->getfield('pkgpart');
+ my($part_pkg) = qsearchs('part_pkg',{'pkgpart'=> $pkgpart });
+ print qq!<TR>! if ($tdcount == 0) ;
+ $tdcount = 0 if ($tdcount == -1) ;
+ print qq!<TD><A HREF="${p}edit/part_pkg.cgi?$pkgpart">!,
+ $part_pkg->getfield('pkg'),"</A></TD>";
+ $tdcount ++ ;
+ if ($tdcount == 2)
+ {
+ print qq!</TR>\n! ;
+ $tdcount = 0 ;
+ }
+ }
+
+ print "</TR>";
+}
+
+print <<END;
+ </TABLE>
+ </BODY>
+</HTML>
+END
+
+%>
diff --git a/httemplate/browse/cust_main_county.cgi b/httemplate/browse/cust_main_county.cgi
new file mode 100755
index 000000000..1e0e0880c
--- /dev/null
+++ b/httemplate/browse/cust_main_county.cgi
@@ -0,0 +1,142 @@
+<!-- mason kludge -->
+<%
+
+my $conf = new FS::Conf;
+my $enable_taxclasses = $conf->exists('enable_taxclasses');
+
+print header("Tax Rate Listing", menubar(
+ 'Main Menu' => $p,
+ 'Edit tax rates' => $p. "edit/cust_main_county.cgi",
+)),<<END;
+ Click on <u>expand country</u> to specify a country's tax rates by state.
+ <BR>Click on <u>expand state</u> to specify a state's tax rates by county.
+END
+
+if ( $enable_taxclasses ) {
+ print '<BR>Click on <u>expand taxclasses</u> to specify tax classes';
+}
+
+print '<BR><BR>'. &table(). <<END;
+ <TR>
+ <TH><FONT SIZE=-1>Country</FONT></TH>
+ <TH><FONT SIZE=-1>State</FONT></TH>
+ <TH>County</TH>
+ <TH>Taxclass<BR><FONT SIZE=-1>(per-package classification)</FONT></TH>
+ <TH>Tax name<BR><FONT SIZE=-1>(printed on invoices)</FONT></TH>
+ <TH><FONT SIZE=-1>Tax</FONT></TH>
+ <TH><FONT SIZE=-1>Exemption</TH>
+ </TR>
+END
+
+my @regions = sort { $a->country cmp $b->country
+ or $a->state cmp $b->state
+ or $a->county cmp $b->county
+ or $a->taxclass cmp $b->taxclass
+ } qsearch('cust_main_county',{});
+
+my $sup=0;
+#foreach $cust_main_county ( @regions ) {
+for ( my $i=0; $i<@regions; $i++ ) {
+ my $cust_main_county = $regions[$i];
+ my $hashref = $cust_main_county->hashref;
+ print <<END;
+ <TR>
+ <TD BGCOLOR="#ffffff">$hashref->{country}</TD>
+END
+
+ my $j;
+ if ( $sup ) {
+ $sup--;
+ } else {
+
+ #lookahead
+ for ( $j=1; $i+$j<@regions; $j++ ) {
+ last if $hashref->{country} ne $regions[$i+$j]->country
+ || $hashref->{state} ne $regions[$i+$j]->state
+ || $hashref->{tax} != $regions[$i+$j]->tax
+ || $hashref->{exempt_amount} != $regions[$i+$j]->exempt_amount
+ || $hashref->{setuptax} ne $regions[$i+$j]->setuptax
+ || $hashref->{recurtax} ne $regions[$i+$j]->recurtax;
+ }
+
+ my $newsup=0;
+ if ( $j>1 && $i+$j+1 < @regions
+ && ( $hashref->{state} ne $regions[$i+$j+1]->state
+ || $hashref->{country} ne $regions[$i+$j+1]->country
+ )
+ && ( ! $i
+ || $hashref->{state} ne $regions[$i-1]->state
+ || $hashref->{country} ne $regions[$i-1]->country
+ )
+ ) {
+ $sup = $j-1;
+ } else {
+ $j = 1;
+ }
+
+ print "<TD ROWSPAN=$j", $hashref->{state}
+ ? ' BGCOLOR="#ffffff">'. $hashref->{state}
+ : qq! BGCOLOR="#cccccc">(ALL) <FONT SIZE=-1>!.
+ qq!<A HREF="${p}edit/cust_main_county-expand.cgi?!. $hashref->{taxnum}.
+ qq!">expand country</A></FONT>!;
+
+ print qq! <FONT SIZE=-1><A HREF="${p}edit/process/cust_main_county-collapse.cgi?!. $hashref->{taxnum}. qq!">collapse state</A></FONT>! if $j>1;
+
+ print "</TD>";
+ }
+
+# $sup=$newsup;
+
+ print "<TD";
+ if ( $hashref->{county} ) {
+ print ' BGCOLOR="#ffffff">'. $hashref->{county};
+ } else {
+ print ' BGCOLOR="#cccccc">(ALL)';
+ if ( $hashref->{state} ) {
+ print qq!<FONT SIZE=-1>!.
+ qq!<A HREF="${p}edit/cust_main_county-expand.cgi?!. $hashref->{taxnum}.
+ qq!">expand state</A></FONT>!;
+ }
+ }
+ print "</TD>";
+
+ print "<TD";
+ if ( $hashref->{taxclass} ) {
+ print ' BGCOLOR="#ffffff">'. $hashref->{taxclass};
+ } else {
+ print ' BGCOLOR="#cccccc">(ALL)';
+ if ( $enable_taxclasses ) {
+ print qq!<FONT SIZE=-1>!.
+ qq!<A HREF="${p}edit/cust_main_county-expand.cgi?taxclass!.
+ $hashref->{taxnum}. qq!">expand taxclasses</A></FONT>!;
+ }
+
+ }
+ print "</TD>";
+
+ print "<TD";
+ if ( $hashref->{taxname} ) {
+ print ' BGCOLOR="#ffffff">'. $hashref->{taxname};
+ } else {
+ print ' BGCOLOR="#cccccc">Tax';
+ }
+ print "</TD>";
+
+ print "<TD BGCOLOR=\"#ffffff\">$hashref->{tax}%</TD>".
+ '<TD BGCOLOR="#ffffff">';
+ print '$'. sprintf("%.2f", $hashref->{exempt_amount} ).
+ '&nbsp;per&nbsp;month<BR>'
+ if $hashref->{exempt_amount} > 0;
+ print 'Setup&nbsp;fee<BR>' if $hashref->{setuptax} =~ /^Y$/i;
+ print 'Recurring&nbsp;fee<BR>' if $hashref->{recurtax} =~ /^Y$/i;
+ print '</TD></TR>';
+
+}
+
+print <<END;
+ </TABLE>
+ </BODY>
+</HTML>
+END
+
+%>
diff --git a/httemplate/browse/cust_pay_batch.cgi b/httemplate/browse/cust_pay_batch.cgi
new file mode 100755
index 000000000..3420e97b6
--- /dev/null
+++ b/httemplate/browse/cust_pay_batch.cgi
@@ -0,0 +1,76 @@
+<!-- mason kludge -->
+<%= header("Pending credit card batch", menubar( 'Main Menu' => $p,)) %>
+
+<FORM ACTION="<%=$p%>misc/download-batch.cgi" METHOD="POST">
+Download batch in format <SELECT NAME="format">
+<OPTION VALUE="csv-td_canada_trust-merchant_pc_batch">CSV file for TD Canada Trust Merchant PC Batch</OPTION>
+</SELECT><INPUT TYPE="submit" VALUE="Download"></FORM>
+<BR><BR>
+
+<FORM ACTION="<%=$p%>misc/upload-batch.cgi" METHOD="POST" ENCTYPE="multipart/form-data">
+Upload results<BR>
+Filename <INPUT TYPE="file" NAME="batch_results"><BR>
+Format <SELECT NAME="format">
+<OPTION VALUE="csv-td_canada_trust-merchant_pc_batch">CSV results from TD Canada Trust Merchant PC Batch</OPTION>
+</SELECT><BR>
+<INPUT TYPE="submit" VALUE="Upload"></FORM>
+<BR>
+
+<%
+ my $statement = "SELECT SUM(amount) from cust_pay_batch";
+ my $sth = dbh->prepare($statement) or die dbh->errstr. "doing $statement";
+ $sth->execute or die "Error executing \"$statement\": ". $sth->errstr;
+ my $total = $sth->fetchrow_arrayref->[0];
+
+ my $c_statement = "SELECT COUNT(*) from cust_pay_batch";
+ my $c_sth = dbh->prepare($c_statement)
+ or die dbh->errstr. "doing $c_statement";
+ $c_sth->execute or die "Error executing \"$c_statement\": ". $c_sth->errstr;
+ my $cards = $c_sth->fetchrow_arrayref->[0];
+%>
+<%= $cards %> credit card payments batched<BR>
+$<%= sprintf("%.2f", $total) %> total in pending batch<BR>
+
+<BR>
+<%= &table() %>
+ <TR>
+ <TH>#</TH>
+ <TH><font size=-1>inv#</font></TH>
+ <TH COLSPAN=2>Customer</TH>
+ <TH>Card name</TH>
+ <TH>Card</TH>
+ <TH>Exp</TH>
+ <TH>Amount</TH>
+ </TR>
+
+<%
+foreach my $cust_pay_batch ( sort { $a->paybatchnum <=> $b->paybatchnum }
+ qsearch('cust_pay_batch', {} )
+) {
+ my $cardnum = $cust_pay_batch->cardnum;
+ #$cardnum =~ s/.{4}$/xxxx/;
+ $cardnum = 'x'x(length($cardnum)-4). substr($cardnum,(length($cardnum)-4));
+
+ $cust_pay_batch->exp =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+ my( $mon, $year ) = ( $2, $1 );
+ $mon = "0$mon" if $mon < 10;
+ my $exp = "$mon/$year";
+
+%>
+
+ <TR>
+ <TD><%= $cust_pay_batch->paybatchnum %></TD>
+ <TD><A HREF="../view/cust_bill.cgi?<%= $cust_pay_batch->invnum %>"><%= $cust_pay_batch->invnum %></TD>
+ <TD><A HREF="../view/cust_main.cgi?<%= $cust_pay_batch->custnum %>"><%= $cust_pay_batch->custnum %></TD>
+ <TD><%= $cust_pay_batch->get('last'). ', '. $cust_pay_batch->first %></TD>
+ <TD><%= $cust_pay_batch->payname %></TD>
+ <TD><%= $cardnum %></TD>
+ <TD><%= $exp %></TD>
+ <TD align="right">$<%= $cust_pay_batch->amount %></TD>
+ </TR>
+
+<% } %>
+
+ </TABLE>
+ </BODY>
+</HTML>
diff --git a/httemplate/browse/generic.cgi b/httemplate/browse/generic.cgi
new file mode 100644
index 000000000..9ac0f2391
--- /dev/null
+++ b/httemplate/browse/generic.cgi
@@ -0,0 +1,46 @@
+<%
+
+use FS::Record qw(qsearch dbdef);
+use DBIx::DBSchema;
+use DBIx::DBSchema::Table;
+
+my $error;
+my $p2 = popurl(2);
+my ($table) = $cgi->keywords;
+my $dbdef = dbdef or die "Cannot fetch dbdef!";
+my $dbdef_table = $dbdef->table($table) or die "Cannot fetch schema for $table";
+
+my $pkey = $dbdef_table->primary_key or die "Cannot fetch pkey for $table";
+print header("Browse $table", menubar('Main Menu' => $p));
+
+my @rec = qsearch($table, {});
+my @col = $dbdef_table->columns;
+
+if ($cgi->param('error')) { %>
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <%=$cgi->param('error')%></FONT>
+ <BR><BR>
+<% }
+%>
+<A HREF="<%=$p2%>edit/<%=$table%>.cgi"><I>Add a new <%=$table%></I></A><BR><BR>
+
+<%=table()%>
+<TH>
+<% foreach (grep { $_ ne $pkey } @col) {
+ %><TD><%=$_%></TD>
+ <% } %>
+</TH>
+<% foreach $rec (sort {$a->getfield($pkey) cmp $b->getfield($pkey) } @rec) {
+ %>
+ <TR>
+ <TD>
+ <A HREF="<%=$p2%>edit/<%=$table%>.cgi?<%=$rec->getfield($pkey)%>">
+ <%=$rec->getfield($pkey)%></A> </TD> <%
+ foreach $col (grep { $_ ne $pkey } @col) { %>
+ <TD><%=$rec->getfield($col)%></TD> <% } %>
+ </A>
+ </TR>
+<% } %>
+</TABLE>
+</BODY>
+</HTML>
+
diff --git a/httemplate/browse/msgcat.cgi b/httemplate/browse/msgcat.cgi
new file mode 100755
index 000000000..d4adf9f1a
--- /dev/null
+++ b/httemplate/browse/msgcat.cgi
@@ -0,0 +1,50 @@
+<!-- mason kludge -->
+<%
+
+print header("View Message catalog", menubar(
+ 'Main Menu' => $p,
+ 'Edit message catalog' => $p. "edit/msgcat.cgi",
+)), '<BR>';
+
+my $widget = new HTML::Widgets::SelectLayers(
+ 'selected_layer' => 'en_US',
+ 'options' => { 'en_US'=>'en_US' },
+ 'layer_callback' => sub {
+ my $layer = shift;
+ my $html = "<BR>Messages for locale $layer<BR>". table().
+ "<TR><TH COLSPAN=2>Code</TH>".
+ "<TH>Message</TH>";
+ $html .= "<TH>en_US Message</TH>" unless $layer eq 'en_US';
+ $html .= '</TR>';
+
+ #foreach my $msgcat ( sort { $a->msgcode cmp $b->msgcode }
+ # qsearch('msgcat', { 'locale' => $layer } ) ) {
+ foreach my $msgcat ( qsearch('msgcat', { 'locale' => $layer } ) ) {
+ $html .= '<TR><TD>'. $msgcat->msgnum. '</TD>'.
+ '<TD>'. $msgcat->msgcode. '</TD>'.
+ '<TD>'. $msgcat->msg. '</TD>';
+ unless ( $layer eq 'en_US' ) {
+ my $en_msgcat = qsearchs('msgcat', {
+ 'locale' => 'en_US',
+ 'msgcode' => $msgcat->msgcode,
+ } );
+ $html .= '<TD>'. $en_msgcat->msg. '</TD>';
+ }
+ $html .= '</TR>';
+ }
+
+ $html .= '</TABLE>';
+ $html;
+ },
+
+);
+
+print $widget->html;
+
+print <<END;
+ </TABLE>
+ </BODY>
+</HTML>
+END
+
+%>
diff --git a/httemplate/browse/nas.cgi b/httemplate/browse/nas.cgi
new file mode 100755
index 000000000..9ccbfe632
--- /dev/null
+++ b/httemplate/browse/nas.cgi
@@ -0,0 +1,80 @@
+<!-- mason kludge -->
+<%
+
+print header('NAS ports', menubar(
+ 'Main Menu' => $p,
+));
+
+my $now = time;
+
+foreach my $nas ( sort { $a->nasnum <=> $b->nasnum } qsearch( 'nas', {} ) ) {
+ print $nas->nasnum. ": ". $nas->nas. " ".
+ $nas->nasfqdn. " (". $nas->nasip. ") ".
+ "as of ". time2str("%c",$nas->last).
+ " (". &pretty_interval($now - $nas->last). " ago)<br>".
+ &table(). "<TR><TH>Nas<BR>Port #</TH><TH>Global<BR>Port #</BR></TH>".
+ "<TH>IP address</TH><TH>User</TH><TH>Since</TH><TH>Duration</TH><TR>",
+ ;
+ foreach my $port ( sort {
+ $a->nasport <=> $b->nasport || $a->portnum <=> $b->portnum
+ } qsearch( 'port', { 'nasnum' => $nas->nasnum } ) ) {
+ my $session = $port->session;
+ my($user, $since, $pretty_since, $duration);
+ if ( ! $session ) {
+ $user = "(empty)";
+ $since = 0;
+ $pretty_since = "(never)";
+ $duration = '';
+ } elsif ( $session->logout ) {
+ $user = "(empty)";
+ $since = $session->logout;
+ } else {
+ my $svc_acct = $session->svc_acct;
+ $user = "<A HREF=\"$p/view/svc_acct.cgi?". $svc_acct->svcnum. "\">".
+ $svc_acct->username. "</A>";
+ $since = $session->login;
+ }
+ $pretty_since = time2str("%c", $since) if $since;
+ $duration = pretty_interval( $now - $since ). " ago"
+ unless defined($duration);
+ print "<TR><TD>". $port->nasport. "</TD><TD>". $port->portnum. "</TD><TD>".
+ $port->ip. "</TD><TD>$user</TD><TD>$pretty_since".
+ "</TD><TD>$duration</TD></TR>"
+ ;
+ }
+ print "</TABLE><BR>";
+}
+
+#Time::Duration??
+sub pretty_interval {
+ my $interval = shift;
+ my %howlong = (
+ '604800' => 'week',
+ '86400' => 'day',
+ '3600' => 'hour',
+ '60' => 'minute',
+ '1' => 'second',
+ );
+
+ my $pretty = "";
+ foreach my $key ( sort { $b <=> $a } keys %howlong ) {
+ my $value = int( $interval / $key );
+ if ( $value ) {
+ if ( $value == 1 ) {
+ $pretty .=
+ ( $howlong{$key} eq 'hour' ? 'an ' : 'a ' ). $howlong{$key}. " "
+ } else {
+ $pretty .= $value. ' '. $howlong{$key}. 's ';
+ }
+ }
+ $interval -= $value * $key;
+ }
+ $pretty =~ /^\s*(\S.*\S)\s*$/;
+ $1;
+}
+
+#print &table(), <<END;
+#<TR>
+# <TH>#</TH>
+# <TH>NAS</
+%>
diff --git a/httemplate/browse/part_bill_event.cgi b/httemplate/browse/part_bill_event.cgi
new file mode 100755
index 000000000..670474d48
--- /dev/null
+++ b/httemplate/browse/part_bill_event.cgi
@@ -0,0 +1,71 @@
+<!-- mason kludge -->
+<%
+
+my %search;
+if ( $cgi->param('showdisabled') ) {
+ %search = ();
+} else {
+ %search = ( 'disabled' => '' );
+}
+
+my @part_bill_event = qsearch('part_bill_event', \%search );
+my $total = scalar(@part_bill_event);
+
+%>
+<%= header('Invoice Event Listing', menubar( 'Main Menu' => $p) ) %>
+
+ Invoice events are actions taken on overdue invoices.<BR><BR>
+<A HREF="<%= $p %>edit/part_bill_event.cgi"><I>Add a new invoice event</I></A>
+<BR><BR>
+<%= $total %> events
+<%= $cgi->param('showdisabled')
+ ? do { $cgi->param('showdisabled', 0);
+ '( <a href="'. $cgi->self_url. '">hide disabled events</a> )'; }
+ : do { $cgi->param('showdisabled', 1);
+ '( <a href="'. $cgi->self_url. '">show disabled events</a> )'; }
+%>
+<%= table() %>
+ <TR>
+ <TH COLSPAN=<%= $cgi->param('showdisabled') ? 2 : 3 %>>Event</TH>
+ <TH>Payby</TH>
+ <TH>After</TH>
+ <TH>Action</TH>
+ <TH>Options</TH>
+ <TH>Code</TH>
+ </TR>
+
+<% foreach my $part_bill_event ( sort { $a->payby cmp $b->payby
+ || $a->seconds <=> $b->seconds
+ || $a->weight <=> $b->weight
+ || $a->eventpart <=> $b->eventpart
+ } @part_bill_event ) {
+ my $url = "${p}edit/part_bill_event.cgi?". $part_bill_event->eventpart;
+ use Time::Duration;
+ my $delay = duration_exact($part_bill_event->seconds);
+ my $plandata = $part_bill_event->plandata;
+ $plandata =~ s/\n/<BR>/go;
+%>
+ <TR>
+ <TD><A HREF="<%= $url %>">
+ <%= $part_bill_event->eventpart %></A></TD>
+<% unless ( $cgi->param('showdisabled') ) { %>
+ <TD>
+ <%= $part_bill_event->disabled ? 'DISABLED' : '' %></TD>
+<% } %>
+ <TD><A HREF="<%= $url %>">
+ <%= $part_bill_event->event %></A></TD>
+ <TD>
+ <%= $part_bill_event->payby %></TD>
+ <TD>
+ <%= $delay %></TD>
+ <TD>
+ <%= $part_bill_event->plan %></TD>
+ <TD>
+ <%= $plandata %></TD>
+ <TD><FONT SIZE="-1">
+ <%= $part_bill_event->eventcode %></FONT></TD>
+ </TR>
+<% } %>
+</TABLE>
+</BODY>
+</HTML>
diff --git a/httemplate/browse/part_export.cgi b/httemplate/browse/part_export.cgi
new file mode 100755
index 000000000..79c57aefc
--- /dev/null
+++ b/httemplate/browse/part_export.cgi
@@ -0,0 +1,39 @@
+<!-- mason kludge -->
+<%= header("Export Listing", menubar( 'Main Menu' => "$p#sysadmin" )) %>
+Provisioning services to external machines, databases and APIs.<BR><BR>
+<A HREF="<%= $p %>edit/part_export.cgi"><I>Add a new export</I></A><BR><BR>
+<SCRIPT>
+function part_export_areyousure(href) {
+ if (confirm("Are you sure you want to delete this export?") == true)
+ window.location.href = href;
+}
+</SCRIPT>
+
+<%= table() %>
+ <TR>
+ <TH COLSPAN=2>Export</TH>
+ <TH>Options</TH>
+ </TR>
+
+<% foreach my $part_export ( sort {
+ $a->getfield('exportnum') <=> $b->getfield('exportnum')
+ } qsearch('part_export',{}) ) {
+%>
+ <TR>
+ <TD><A HREF="<%= $p %>edit/part_export.cgi?<%= $part_export->exportnum %>"><%= $part_export->exportnum %></A></TD>
+ <TD><%= $part_export->exporttype %> to <%= $part_export->machine %> (<A HREF="<%= $p %>edit/part_export.cgi?<%= $part_export->exportnum %>">edit</A>&nbsp;|&nbsp;<A HREF="javascript:part_export_areyousure('<%= $p %>misc/delete-part_export.cgi?<%= $part_export->exportnum %>')">delete</A>)</TD>
+ <TD>
+ <%= itable() %>
+ <% my %opt = $part_export->options;
+ foreach my $opt ( keys %opt ) { %>
+ <TR><TD><%= $opt %></TD><TD><%= encode_entities($opt{$opt}) %></TD></TR>
+ <% } %>
+ </TABLE>
+ </TD>
+ </TR>
+
+<% } %>
+
+</TABLE>
+</BODY>
+</HTML>
diff --git a/httemplate/browse/part_pkg.cgi b/httemplate/browse/part_pkg.cgi
new file mode 100755
index 000000000..180f18263
--- /dev/null
+++ b/httemplate/browse/part_pkg.cgi
@@ -0,0 +1,173 @@
+<!-- mason kludge -->
+<%
+
+my %search;
+if ( $cgi->param('showdisabled') ) {
+ %search = ();
+} else {
+ %search = ( 'disabled' => '' );
+}
+
+my @part_pkg = qsearch('part_pkg', \%search );
+my $total = scalar(@part_pkg);
+
+my $sortby;
+my %num_active_cust_pkg = ();
+my( $suspended_sth, $canceled_sth ) = ( '', '' );
+if ( $cgi->param('active') ) {
+ my $active_sth = dbh->prepare(
+ 'SELECT COUNT(*) FROM cust_pkg WHERE pkgpart = ?'.
+ ' AND ( cancel IS NULL OR cancel = 0 )'.
+ ' AND ( susp IS NULL OR susp = 0 )'
+ ) or die dbh->errstr;
+ foreach my $part_pkg ( @part_pkg ) {
+ $active_sth->execute($part_pkg->pkgpart) or die $active_sth->errstr;
+ $num_active_cust_pkg{$part_pkg->pkgpart} =
+ $active_sth->fetchrow_arrayref->[0];
+ }
+ $sortby = sub {
+ $num_active_cust_pkg{$b->pkgpart} <=> $num_active_cust_pkg{$a->pkgpart};
+ };
+
+ $suspended_sth = dbh->prepare(
+ 'SELECT COUNT(*) FROM cust_pkg WHERE pkgpart = ?'.
+ ' AND ( cancel IS NULL OR cancel = 0 )'.
+ ' AND susp IS NOT NULL AND susp != 0'
+ ) or die dbh->errstr;
+
+ $canceled_sth = dbh->prepare(
+ 'SELECT COUNT(*) FROM cust_pkg WHERE pkgpart = ?'.
+ ' AND cancel IS NOT NULL AND cancel != 0'
+ ) or die dbh->errstr;
+
+} else {
+ $sortby = \*pkgpart_sort;
+}
+
+%>
+<%= header("Package Definition Listing",menubar( 'Main Menu' => $p )) %>
+<% unless ( $cgi->param('active') ) { %>
+ One or more service definitions are grouped together into a package
+ definition and given pricing information. Customers purchase packages
+ rather than purchase services directly.<BR><BR>
+ <A HREF="<%= $p %>edit/part_pkg.cgi"><I>Add a new package definition</I></A>
+ <BR><BR>
+<% } %>
+
+<%= $total %> package definitions
+<%
+if ( $cgi->param('showdisabled') ) {
+ $cgi->param('showdisabled', 0);
+ print qq!( <a href="!. $cgi->self_url. qq!">hide disabled packages</a> )!;
+} else {
+ $cgi->param('showdisabled', 1);
+ print qq!( <a href="!. $cgi->self_url. qq!">show disabled packages</a> )!;
+}
+
+my $colspan = $cgi->param('showdisabled') ? 2 : 3;
+print &table(), <<END;
+ <TR>
+ <TH COLSPAN=$colspan>Package</TH>
+ <TH>Comment</TH>
+END
+print ' <TH><FONT SIZE=-1>Customer<BR>packages</FONT></TH>'
+ if $cgi->param('active');
+print <<END;
+ <TH><FONT SIZE=-1>Freq.</FONT></TH>
+ <TH><FONT SIZE=-1>Plan</FONT></TH>
+ <TH><FONT SIZE=-1>Data</FONT></TH>
+ <TH>Service</TH>
+ <TH><FONT SIZE=-1>Quan.</FONT></TH>
+END
+print '<TH><FONT SIZE=-1>Primary</FONT></TH>'
+ if dbdef->table('pkg_svc')->column('primary_svc');
+print '</TR>';
+
+foreach my $part_pkg ( sort $sortby @part_pkg ) {
+ my($hashref)=$part_pkg->hashref;
+ my(@pkg_svc)=grep $_->getfield('quantity'),
+ qsearch('pkg_svc',{'pkgpart'=> $hashref->{pkgpart} });
+ my($rowspan)=scalar(@pkg_svc);
+ my $plandata;
+ if ( $hashref->{plan} ) {
+ $plandata = $hashref->{plandata};
+ $plandata =~ s/^(\w+)=/$1&nbsp;/mg;
+ $plandata =~ s/\n/<BR>/g;
+ } else {
+ $hashref->{plan} = "(legacy)";
+ $plandata = "Setup&nbsp;". $hashref->{setup}.
+ "<BR>Recur&nbsp;". $hashref->{recur};
+ }
+ print <<END;
+ <TR>
+ <TD ROWSPAN=$rowspan><A HREF="${p}edit/part_pkg.cgi?$hashref->{pkgpart}">$hashref->{pkgpart}</A></TD>
+END
+
+ unless ( $cgi->param('showdisabled') ) {
+ print "<TD ROWSPAN=$rowspan>";
+ print "DISABLED" if $hashref->{disabled};
+ print '</TD>';
+ }
+
+ print <<END;
+ <TD ROWSPAN=$rowspan><A HREF="${p}edit/part_pkg.cgi?$hashref->{pkgpart}">$hashref->{pkg}</A></TD>
+ <TD ROWSPAN=$rowspan>$hashref->{comment}</TD>
+END
+ if ( $cgi->param('active') ) {
+ print " <TD ROWSPAN=$rowspan>";
+ print '<FONT COLOR="#00CC00"><B>'.
+ $num_active_cust_pkg{$hashref->{'pkgpart'}}.
+ qq!</B></FONT>&nbsp;<A HREF="${p}search/cust_pkg.cgi?magic=active;pkgpart=$hashref->{pkgpart}">active</A><BR>!;
+
+ $suspended_sth->execute( $part_pkg->pkgpart ) or die $suspended_sth->errstr;
+ my $num_suspended = $suspended_sth->fetchrow_arrayref->[0];
+ print '<FONT COLOR="#FF9900"><B>'. $num_suspended.
+ qq!</B></FONT>&nbsp;<A HREF="${p}search/cust_pkg.cgi?magic=suspended;pkgpart=$hashref->{pkgpart}">suspended</A><BR>!;
+
+ $canceled_sth->execute( $part_pkg->pkgpart ) or die $canceled_sth->errstr;
+ my $num_canceled = $canceled_sth->fetchrow_arrayref->[0];
+ print '<FONT COLOR="#FF0000"><B>'. $num_canceled.
+ qq!</B></FONT>&nbsp;<A HREF="${p}search/cust_pkg.cgi?magic=canceled;pkgpart=$hashref->{pkgpart}">canceled</A>!;
+
+
+ print '</TD>';
+ }
+ print <<END;
+ <TD ROWSPAN=$rowspan>$hashref->{freq}</TD>
+ <TD ROWSPAN=$rowspan>$hashref->{plan}</TD>
+ <TD ROWSPAN=$rowspan>$plandata</TD>
+END
+
+ my($pkg_svc);
+ my($n)="";
+ foreach $pkg_svc ( @pkg_svc ) {
+ my($svcpart)=$pkg_svc->getfield('svcpart');
+ my($part_svc) = qsearchs('part_svc',{'svcpart'=> $svcpart });
+ print $n,qq!<TD><A HREF="${p}edit/part_svc.cgi?$svcpart">!,
+ $part_svc->getfield('svc'),"</A></TD><TD>",
+ $pkg_svc->getfield('quantity'),"</TD>";
+ if ( dbdef->table('pkg_svc')->column('primary_svc') ) {
+ print '<TD>';
+ print 'PRIMARY' if $pkg_svc->primary_svc =~ /^Y/i;
+ print '</TD>';
+ }
+ print "</TR>\n";
+ $n="<TR>";
+ }
+
+ print "</TR>";
+}
+
+$colspan = $cgi->param('showdisabled') ? 8 : 9;
+print <<END;
+
+ </TABLE>
+ </BODY>
+</HTML>
+END
+
+sub pkgpart_sort {
+ $a->pkgpart <=> $b->pkgpart;
+}
+
+%>
diff --git a/httemplate/browse/part_referral.cgi b/httemplate/browse/part_referral.cgi
new file mode 100755
index 000000000..3f59abcf5
--- /dev/null
+++ b/httemplate/browse/part_referral.cgi
@@ -0,0 +1,64 @@
+<!-- mason kludge -->
+<%= header("Advertising source Listing", menubar(
+ 'Main Menu' => $p,
+# 'Add new referral' => "../edit/part_referral.cgi",
+)) %>
+Where a customer heard about your service. Tracked for informational purposes.
+<BR><BR>
+<A HREF="<%= $p %>edit/part_referral.cgi"><I>Add a new advertising source</I></A>
+<BR><BR>
+
+<%
+ my $today = timelocal(0, 0, 0, (localtime(time))[3..5] );
+ my %past;
+ tie %past, 'Tie::IxHash',
+ 'Today' => 0,
+ 'Past week' => 518400, # 60sec * 60min * 24hrs * 6days
+ 'Past 30 days' => 2505600, # 60sec * 60min * 24hrs * 29days
+ 'Past 60 days' => 5097600, # 60sec * 60min * 24hrs * 29days
+ 'Past 90 days' => 7689600, # 60sec * 60min * 24hrs * 29days
+ 'Past 6 months' => 15724800, # 60sec * 60min * 24hrs * 182days
+ 'Past year' => 31486000, # 60sec * 60min * 24hrs * 364days
+ 'Total' => $today,
+ ;
+
+ my $sth = dbh->prepare("SELECT COUNT(*) FROM h_cust_main
+ WHERE history_action = 'insert'
+ AND refnum = ?
+ AND history_date > ? ")
+ or die dbh->errstr;
+%>
+
+<%= table() %>
+<TR>
+ <TH COLSPAN=2 ROWSPAN=2>Advertising source</TH>
+ <TH COLSPAN=<%= scalar(keys %past) %>>Customers</TH>
+</TR>
+<% for my $period ( keys %past ) { %>
+ <TH><FONT SIZE=-1><%= $period %></FONT></TH>
+<% } %>
+</TR>
+
+<%
+foreach my $part_referral ( sort {
+ $a->getfield('refnum') <=> $b->getfield('refnum')
+} qsearch('part_referral',{}) ) {
+%>
+ <TR>
+ <TD><A HREF="<%= $p %>edit/part_referral.cgi?<%= $part_referral->refnum %>">
+ <%= $part_referral->refnum %></A></TD>
+ <TD><A HREF="<%= $p %>edit/part_referral.cgi?<%= $part_referral->refnum %>">
+ <%= $part_referral->referral %></A></TD>
+ <% for my $period ( values %past ) {
+ $sth->execute($part_referral->refnum, $today-$period)
+ or die $sth->errstr;
+ my $number = $sth->fetchrow_arrayref->[0];
+ %>
+ <TD ALIGN="right"><%= $number %></TD>
+ <% } %>
+ </TR>
+<% } %>
+
+ </TABLE>
+ </BODY>
+</HTML>
diff --git a/httemplate/browse/part_svc.cgi b/httemplate/browse/part_svc.cgi
new file mode 100755
index 000000000..ef0de13cc
--- /dev/null
+++ b/httemplate/browse/part_svc.cgi
@@ -0,0 +1,133 @@
+<!-- mason kludge -->
+<%
+
+my %search;
+if ( $cgi->param('showdisabled') ) {
+ %search = ();
+} else {
+ %search = ( 'disabled' => '' );
+}
+
+my @part_svc =
+ sort { $a->getfield('svcpart') <=> $b->getfield('svcpart') }
+ qsearch('part_svc', \%search );
+my $total = scalar(@part_svc);
+
+my %num_active_cust_svc = ();
+if ( $cgi->param('active') ) {
+ my $active_sth = dbh->prepare(
+ 'SELECT COUNT(*) FROM cust_svc WHERE svcpart = ?'
+ ) or die dbh->errstr;
+ foreach my $part_svc ( @part_svc ) {
+ $active_sth->execute($part_svc->svcpart) or die $active_sth->errstr;
+ $num_active_cust_svc{$part_svc->svcpart} =
+ $active_sth->fetchrow_arrayref->[0];
+ }
+ @part_svc = sort { $num_active_cust_svc{$b->svcpart} <=>
+ $num_active_cust_svc{$a->svcpart} } @part_svc;
+}
+
+%>
+<%= header('Service Definition Listing', menubar( 'Main Menu' => $p) ) %>
+
+<SCRIPT>
+function part_export_areyousure(href) {
+ if (confirm("Are you sure you want to delete this export?") == true)
+ window.location.href = href;
+}
+</SCRIPT>
+
+ Service definitions are the templates for items you offer to your customers.<BR><BR>
+
+<FORM METHOD="POST" ACTION="<%= $p %>edit/part_svc.cgi">
+<A HREF="<%= $p %>edit/part_svc.cgi"><I>Add a new service definition</I></A><% if ( @part_svc ) { %>&nbsp;or&nbsp;<SELECT NAME="clone"><OPTION></OPTION>
+<% foreach my $part_svc ( @part_svc ) { %>
+ <OPTION VALUE="<%= $part_svc->svcpart %>"><%= $part_svc->svc %></OPTION>
+<% } %>
+</SELECT><INPUT TYPE="submit" VALUE="Clone existing service">
+<% } %>
+</FORM><BR>
+
+<%= $total %> service definitions
+<%= $cgi->param('showdisabled')
+ ? do { $cgi->param('showdisabled', 0);
+ '( <a href="'. $cgi->self_url. '">hide disabled services</a> )'; }
+ : do { $cgi->param('showdisabled', 1);
+ '( <a href="'. $cgi->self_url. '">show disabled services</a> )'; }
+%>
+<%= table() %>
+ <TR>
+ <TH COLSPAN=<%= $cgi->param('showdisabled') ? 2 : 3 %>>Service</TH>
+ <TH>Table</TH>
+<% if ( $cgi->param('active') ) { %>
+ <TH><FONT SIZE=-1>Customer<BR>Services</FONT></TH>
+<% } %>
+ <TH>Export</TH>
+ <TH>Field</TH>
+ <TH COLSPAN=2>Modifier</TH>
+ </TR>
+
+<% foreach my $part_svc ( @part_svc ) {
+ my $hashref = $part_svc->hashref;
+ my $svcdb = $hashref->{svcdb};
+ my $svc_x = "FS::$svcdb"->new( { svcpart => $part_svc->svcpart } );
+ my @dfields = $svc_x->fields;
+ push @dfields, 'usergroup' if $svcdb eq 'svc_acct'; #kludge
+ my @fields =
+ grep { $svc_x->pvf($_)
+ or $_ ne 'svcnum' && $part_svc->part_svc_column($_)->columnflag }
+ @dfields ;
+ my $rowspan = scalar(@fields) || 1;
+ my $url = "${p}edit/part_svc.cgi?$hashref->{svcpart}";
+%>
+
+ <TR>
+ <TD ROWSPAN=<%= $rowspan %>><A HREF="<%= $url %>">
+ <%= $hashref->{svcpart} %></A></TD>
+<% unless ( $cgi->param('showdisabled') ) { %>
+ <TD ROWSPAN=<%= $rowspan %>>
+ <%= $hashref->{disabled} ? 'DISABLED' : '' %></TD>
+<% } %>
+ <TD ROWSPAN=<%= $rowspan %>><A HREF="<%= $url %>">
+ <%= $hashref->{svc} %></A></TD>
+ <TD ROWSPAN=<%= $rowspan %>>
+ <%= $hashref->{svcdb} %></TD>
+<% if ( $cgi->param('active') ) { %>
+ <TD ROWSPAN=<%= $rowspan %>>
+ <FONT COLOR="#00CC00"><B><%= $num_active_cust_svc{$hashref->{svcpart}} %></B></FONT>&nbsp;<A HREF="<%=$p%>search/<%= $hashref->{svcdb} %>.cgi?svcpart=<%= $hashref->{svcpart} %>">active</A>
+ </TD>
+<% } %>
+ <TD ROWSPAN=<%= $rowspan %>><%= itable() %>
+<%
+# my @part_export =
+map { qsearchs('part_export', { exportnum => $_->exportnum } ) } qsearch('export_svc', { svcpart => $part_svc->svcpart } ) ;
+ foreach my $part_export (
+ map { qsearchs('part_export', { exportnum => $_->exportnum } ) }
+ qsearch('export_svc', { svcpart => $part_svc->svcpart } )
+ ) {
+%>
+ <TR>
+ <TD><A HREF="<%= $p %>edit/part_export.cgi?<%= $part_export->exportnum %>"><%= $part_export->exportnum %>:&nbsp;<%= $part_export->exporttype %>&nbsp;to&nbsp;<%= $part_export->machine %></A></TD></TR>
+<% } %>
+ </TABLE></TD>
+
+<% my($n1)='';
+ foreach my $field ( @fields ) {
+ my $flag = $part_svc->part_svc_column($field)->columnflag;
+%>
+ <%= $n1 %><TD><%= $field %></TD><TD>
+
+<% if ( $flag eq "D" ) { print "Default"; }
+ elsif ( $flag eq "F" ) { print "Fixed"; }
+ elsif ( not $flag ) { }
+ else { print "(Unknown!)"; }
+%>
+ </TD><TD><%= $part_svc->part_svc_column($field)->columnvalue%></TD>
+<% $n1="</TR><TR>";
+ }
+%>
+ </TR>
+<% } %>
+</TABLE>
+</BODY>
+</HTML>
diff --git a/httemplate/browse/part_virtual_field.cgi b/httemplate/browse/part_virtual_field.cgi
new file mode 100644
index 000000000..9bb5c1373
--- /dev/null
+++ b/httemplate/browse/part_virtual_field.cgi
@@ -0,0 +1,39 @@
+<%= header('Virtual field definitions', menubar('Main Menu' => $p)) %>
+<%
+
+my %pvfs;
+my $block;
+my $p2 = popurl(2);
+my $dbtable;
+
+foreach (qsearch('part_virtual_field', {})) {
+ push @{ $pvfs{$_->dbtable} }, $_;
+}
+%>
+
+<% if ($cgi->param('error')) { %>
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <%=$cgi->param('error')%></FONT>
+ <BR><BR>
+<% } %>
+
+<A HREF="<%=$p2%>edit/part_virtual_field.cgi"><I>Add a new field</I></A><BR><BR>
+
+<% foreach $dbtable (sort { $a cmp $b } keys (%pvfs)) { %>
+<H3><%=$dbtable%></H3>
+
+<%=table()%>
+<TH><TD>Field name</TD><TD>Description</TD></TH>
+<% foreach $pvf (sort {$a->name cmp $b->name} @{ $pvfs{$dbtable} }) { %>
+ <TR>
+ <TD></TD>
+ <TD>
+ <A HREF="<%=$p2%>edit/part_virtual_field.cgi?<%=$pvf->vfieldpart%>">
+ <%=$pvf->name%></A></TD>
+ <TD><%=$pvf->label%></TD>
+ </TR>
+<% } %>
+</TABLE>
+<% } %>
+</BODY>
+</HTML>
+
diff --git a/httemplate/browse/queue.cgi b/httemplate/browse/queue.cgi
new file mode 100755
index 000000000..b53c1402d
--- /dev/null
+++ b/httemplate/browse/queue.cgi
@@ -0,0 +1,7 @@
+<!-- mason kludge -->
+<%
+
+print header("Job Queue", menubar( 'Main Menu' => $p, )).
+ joblisting({}). '</BODY></HTML>';
+
+%>
diff --git a/httemplate/browse/router.cgi b/httemplate/browse/router.cgi
new file mode 100644
index 000000000..feee4ecaa
--- /dev/null
+++ b/httemplate/browse/router.cgi
@@ -0,0 +1,38 @@
+<%= header('Routers', menubar('Main Menu' => $p)) %>
+<%
+
+my @router = qsearch('router', {});
+my $p2 = popurl(2);
+
+%>
+
+<% if ($cgi->param('error')) { %>
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <%=$cgi->param('error')%></FONT>
+ <BR><BR>
+<% } %>
+
+<A HREF="<%=$p2%>edit/router.cgi"><I>Add a new router</I></A><BR><BR>
+
+<%=table()%>
+ <TR>
+ <TD><B>Router name</B></TD>
+ <TD><B>Address block(s)</B></TD>
+ </TR>
+<% foreach my $router (sort {$a->routernum <=> $b->routernum} @router) {
+ my @addr_block = $router->addr_block;
+%>
+ <TR>
+ <TD ROWSPAN="<%=scalar(@addr_block)%>">
+ <A HREF="<%=$p2%>edit/router.cgi?<%=$router->routernum%>"><%=$router->routername%></A>
+ </TD>
+ <TD>
+ <% foreach my $block ( @addr_block ) { %>
+ <%=$block->NetAddr%></BR>
+ <% } %>
+ </TD>
+ </TR>
+<% } %>
+</TABLE>
+</BODY>
+</HTML>
+
diff --git a/httemplate/browse/svc_acct_pop.cgi b/httemplate/browse/svc_acct_pop.cgi
new file mode 100755
index 000000000..44cda81ad
--- /dev/null
+++ b/httemplate/browse/svc_acct_pop.cgi
@@ -0,0 +1,63 @@
+<!-- mason kludge -->
+<%
+ my $accounts_sth = dbh->prepare("SELECT COUNT(*) FROM svc_acct
+ WHERE popnum = ? ")
+ or die dbh->errstr;
+%>
+<%= header('Access Number Listing', menubar( 'Main Menu' => $p )) %>
+Points of Presence<BR><BR>
+<A HREF="<%= $p %>edit/svc_acct_pop.cgi"><I>Add new Access Number</I></A><BR><BR>
+<%= table() %>
+ <TR>
+ <TH></TH>
+ <TH>City</TH>
+ <TH>State</TH>
+ <TH>Area code</TH>
+ <TH>Exchange</TH>
+ <TH>Local</TH>
+ <TH>Accounts</TH>
+ </TR>
+
+<%
+foreach my $svc_acct_pop ( sort {
+ #$a->getfield('popnum') <=> $b->getfield('popnum')
+ $a->state cmp $b->state || $a->city cmp $b->city
+ || $a->ac <=> $b->ac || $a->exch <=> $b->exch || $a->loc <=> $b->loc
+} qsearch('svc_acct_pop',{}) ) {
+
+ my $svc_acct_pop_link = $p . 'edit/svc_acct_pop.cgi?'. $svc_acct_pop->popnum;
+
+ $accounts_sth->execute($svc_acct_pop->popnum) or die $accounts_sth->errstr;
+ my $num_accounts = $accounts_sth->fetchrow_arrayref->[0];
+
+ my $svc_acct_link = $p. 'search/svc_acct.cgi?popnum='. $svc_acct_pop->popnum;
+
+%>
+ <TR>
+ <TD><A HREF="<%= $svc_acct_pop_link %>">
+ <%= $svc_acct_pop->popnum %></A></TD>
+ <TD><A HREF="<%= $svc_acct_pop_link %>">
+ <%= $svc_acct_pop->city %></A></TD>
+ <TD><A HREF="<%= $svc_acct_pop_link %>">
+ <%= $svc_acct_pop->state %></A></TD>
+ <TD><A HREF="<%= $svc_acct_pop_link %>">
+ <%= $svc_acct_pop->ac %></A></TD>
+ <TD><A HREF="<%= $svc_acct_pop_link %>">
+ <%= $svc_acct_pop->exch %></A></TD>
+ <TD><A HREF="<%= $svc_acct_pop_link %>">
+ <%= $svc_acct_pop->loc %></A></TD>
+ <TD>
+ <FONT COLOR="#00CC00"><B><%= $num_accounts %></B></FONT>
+ <% if ( $num_accounts ) { %><A HREF="<%= $svc_acct_link %>"><% } %>
+ active
+ <% if ( $num_accounts ) { %></A><% } %>
+ </TD>
+ </TR>
+<% } %>
+
+ <TR>
+ </TR>
+ </TABLE>
+ </BODY>
+</HTML>
+