summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CREDITS6
-rw-r--r--Changes.1.7.039
-rw-r--r--FS/FS/AccessRight.pm77
-rw-r--r--FS/FS/CGI.pm5
-rw-r--r--FS/FS/Schema.pm87
-rw-r--r--FS/FS/UI/Web.pm14
-rw-r--r--FS/FS/access_group.pm121
-rw-r--r--FS/FS/access_groupagent.pm124
-rw-r--r--FS/FS/access_right.pm127
-rw-r--r--FS/FS/access_user.pm167
-rw-r--r--FS/FS/access_user_pref.pm127
-rw-r--r--FS/FS/access_usergroup.pm144
-rw-r--r--FS/FS/agent_type.pm3
-rw-r--r--FS/FS/cust_bill.pm5
-rw-r--r--FS/FS/m2m_Common.pm110
-rw-r--r--FS/FS/part_pkg/billoneday.pm48
-rw-r--r--FS/FS/payby.pm3
-rw-r--r--FS/FS/svc_domain.pm6
-rw-r--r--FS/MANIFEST1
-rw-r--r--FS/bin/freeside-addoutsourceuser4
-rw-r--r--FS/t/AccessRight.t5
-rw-r--r--FS/t/access_group.t5
-rw-r--r--FS/t/access_groupagent.t5
-rw-r--r--FS/t/access_right.t5
-rw-r--r--FS/t/access_user.t5
-rw-r--r--FS/t/access_user_pref.t5
-rw-r--r--FS/t/access_usergroup.t5
-rw-r--r--htetc/handler.pl2
-rw-r--r--httemplate/autohandler10
-rw-r--r--httemplate/browse/access_group.html33
-rw-r--r--httemplate/browse/access_user.html63
-rwxr-xr-xhttemplate/browse/agent_type.cgi108
-rwxr-xr-xhttemplate/browse/cust_main_county.cgi185
-rwxr-xr-xhttemplate/browse/msgcat.cgi20
-rwxr-xr-xhttemplate/browse/part_pkg.cgi14
-rw-r--r--httemplate/edit/access_group.html10
-rw-r--r--httemplate/edit/access_user.html37
-rwxr-xr-xhttemplate/edit/agent_type.cgi52
-rwxr-xr-xhttemplate/edit/cust_bill_pay.cgi87
-rwxr-xr-xhttemplate/edit/cust_credit.cgi71
-rwxr-xr-xhttemplate/edit/cust_credit_bill.cgi90
-rwxr-xr-xhttemplate/edit/cust_main.cgi105
-rwxr-xr-xhttemplate/edit/cust_pkg.cgi136
-rw-r--r--httemplate/edit/elements/edit.html36
-rwxr-xr-xhttemplate/edit/part_referral.cgi36
-rw-r--r--httemplate/edit/part_virtual_field.cgi19
-rw-r--r--httemplate/edit/process/access_group.html5
-rw-r--r--httemplate/edit/process/access_user.html8
-rwxr-xr-xhttemplate/edit/process/agent_type.cgi39
-rwxr-xr-xhttemplate/edit/process/cust_bill_pay.cgi16
-rwxr-xr-xhttemplate/edit/process/cust_credit.cgi17
-rwxr-xr-xhttemplate/edit/process/cust_credit_bill.cgi16
-rw-r--r--httemplate/edit/process/elements/process.html19
-rwxr-xr-xhttemplate/edit/svc_domain.cgi55
-rw-r--r--httemplate/elements/checkboxes-table.html110
-rw-r--r--httemplate/elements/cssexpr.js66
-rw-r--r--httemplate/elements/footer.html3
-rw-r--r--httemplate/elements/header.html289
-rw-r--r--httemplate/elements/menubar.html1
-rw-r--r--httemplate/elements/select-access_group.html15
-rw-r--r--httemplate/elements/tr-select-access_group.html22
-rw-r--r--httemplate/elements/xmenu.css185
-rw-r--r--httemplate/elements/xmenu.js668
-rw-r--r--httemplate/index.html12
-rw-r--r--httemplate/misc/batch-cust_pay.html2
-rw-r--r--httemplate/misc/payment.cgi38
-rwxr-xr-xhttemplate/search/cust_bill.cgi165
-rwxr-xr-xhttemplate/search/cust_main-otaker.cgi47
-rwxr-xr-xhttemplate/search/cust_main-payinfo.html20
-rwxr-xr-xhttemplate/search/cust_main-quickpay.html44
-rwxr-xr-xhttemplate/search/cust_main.cgi50
-rwxr-xr-xhttemplate/search/cust_pay.html18
-rwxr-xr-xhttemplate/search/cust_pkg_report.cgi39
-rw-r--r--httemplate/search/report_cust_bill.html54
-rw-r--r--httemplate/search/report_cust_credit.html62
-rw-r--r--httemplate/search/report_cust_pay.html81
-rw-r--r--httemplate/search/report_prepaid_income.html26
-rwxr-xr-xhttemplate/search/report_tax.html73
-rw-r--r--httemplate/search/sqlradius.html52
-rwxr-xr-xhttemplate/search/svc_acct.html19
-rwxr-xr-xhttemplate/search/svc_domain.cgi2
-rwxr-xr-xhttemplate/search/svc_domain.html19
-rwxr-xr-xhttemplate/search/svc_external.cgi21
-rwxr-xr-xhttemplate/view/cust_main/packages.html4
-rw-r--r--httemplate/view/cust_main/payment_history.html26
85 files changed, 3577 insertions, 1198 deletions
diff --git a/CREDITS b/CREDITS
index 930b4f351..72b049129 100644
--- a/CREDITS
+++ b/CREDITS
@@ -157,5 +157,11 @@ Perl backend version (c) copyright 2005 Nathan Schmidt
Scott Edwards <supadupa@gmail.com> contributed magic for XMLHTTP error
handling, and other patches.
+Contains XMenu <http://webfx.eae.net/dhtml/xmenu/xmenu.html>
+by Erik Arvidsson, licensed under the terms of the GNU GPL.
+
+Contains public domain artwork from openclipart.org by mimooh and other
+authors.
+
Everything else is my (Ivan Kohler <ivan@420.am>) fault.
diff --git a/Changes.1.7.0 b/Changes.1.7.0
new file mode 100644
index 000000000..8dcc36e08
--- /dev/null
+++ b/Changes.1.7.0
@@ -0,0 +1,39 @@
+- tax report overhaul
+- package classes
+- UI overhaul. No more Apache::ASP support, HTML::Mason only
+- lots of CDR/telephony work
+- inventory classes and inventory (better description from directleap/specs.txt)
+- vonage click2call bs :)
+- zip code report
+- sales/credit/receipt summary report now has options for agent, 12mo cumulative totals
+- gross sales report/graph broken down by agent and package class
+- config switch to base tax off shipping address if present (warning: tax reports can take a long time with this switch on)
+- plesk provisioning
+
+-------- some of the above, nicely:
+
+- Charge taxes based on shipping address if present. Note that tax
+ reports can take a bit longer than they used to.
+
+- Per-agent A/R Aging
+ - Bookeeping/Collections | Accounts Receivable Aging Summary
+
+- Per-agent Sales/Credit/Receipt Summary and "pre-selection" of agent
+ and time period as you requested.
+ - Bookeeping/Collections | Sales, Credits and Receipts Summary
+
+- Package classes
+ - go to Sysadmin | View/Edit package classes and create some classes
+ - go to Sysadmin | View/Edit package definitions, edit the existing
+ package defs and put them into classes
+
+- The sales report you requested, broken down by agent and
+ package class. This works fine right now, but it will show more
+ information once you enter some package classes.
+ - Bookeeping/Collections | Sales report (by agent, package class ...
+
+--------
+
+and...
+- ACLs
+- Agent virtualization
diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm
new file mode 100644
index 000000000..01d63e35d
--- /dev/null
+++ b/FS/FS/AccessRight.pm
@@ -0,0 +1,77 @@
+package FS::AccessRight;
+
+use strict;
+user vars qw(@rights %rights);
+use Tie::IxHash;
+
+=head1 NAME
+
+FS::AccessRight - Access control rights.
+
+=head1 SYNOPSIS
+
+ use FS::AccessRight;
+
+=head1 DESCRIPTION
+
+Access control rights - Permission to perform specific actions that can be
+assigned to users and/or groups.
+
+=cut
+
+@rights = (
+ 'Reports' => [
+ '_desc' => 'Access to high-level reporting',
+ ],
+ 'Configuration' => [
+ '_desc' => 'Access to configuration',
+
+ 'Settings' => {},
+
+ 'agent' => [
+ '_desc' => 'Master access to reseller configuration',
+ 'agent_type' => {},
+ 'agent' => {},
+ ],
+
+ 'export_svc_pkg' => [
+ '_desc' => 'Access to export, service and package configuration',
+ 'part_export' => {},
+ 'part_svc' => {},
+ 'part_pkg' => {},
+ 'pkg_class' => {},
+ ],
+
+ 'billing' => [
+ '_desc' => 'Access to billing configuration',
+ 'payment_gateway' => {},
+ 'part_bill_event' => {},
+ 'prepay_credit' => {},
+ 'rate' => {},
+ 'cust_main_county' => {},
+ ],
+
+ 'dialup' => [
+ '_desc' => 'Access to dialup configuraiton',
+ 'svc_acct_pop' => {},
+ ],
+
+ 'broadband' => [
+ '_desc' => 'Access to broadband configuration',
+ 'router' => {},
+ 'addr_block' => {},
+ ],
+
+ 'misc' => [
+ 'part_referral' => {},
+ 'part_virtual_field' => {},
+ 'msgcat' => {},
+ 'inventory_class' => {},
+ ],
+
+ },
+
+);
+
+#turn it into a more hash-like structure, but ordered via IxHash
+
diff --git a/FS/FS/CGI.pm b/FS/FS/CGI.pm
index f1f2a3dca..d598f5218 100644
--- a/FS/FS/CGI.pm
+++ b/FS/FS/CGI.pm
@@ -62,9 +62,9 @@ sub header {
</HEAD>
<BODY BGCOLOR="#e8e8e8"$etc>
<FONT SIZE=6>
- $title
+ <CENTER>$title</CENTER>
</FONT>
- <BR><BR>
+ <BR><!--<BR>-->
END
$x .= $menubar. "<BR><BR>" if $menubar;
$x;
@@ -115,6 +115,7 @@ sub menubar { #$menubar=menubar('Main Menu', '../', 'Item', 'url', ... );
my($item,$url,@html);
while (@_) {
($item,$url)=splice(@_,0,2);
+ next if $item =~ /^\s*Main\s+Menu\s*$/i;
push @html, qq!<A HREF="$url">$item</A>!;
}
join(' | ',@html);
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 9125758d0..e81185666 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -244,6 +244,8 @@ sub tables_hashref {
my $username_len = 32; #usernamemax config file
+ # name type nullability length default local
+
return {
'agent' => {
@@ -445,6 +447,9 @@ sub tables_hashref {
'index' => [ ['last'], [ 'company' ], [ 'referral_custnum' ],
[ 'daytime' ], [ 'night' ], [ 'fax' ], [ 'refnum' ],
[ 'county' ], [ 'state' ], [ 'country' ], [ 'zip' ],
+ [ 'ship_last' ], [ 'ship_company' ],
+ [ 'payby' ], [ 'paydate' ],
+
],
},
@@ -1444,16 +1449,94 @@ sub tables_hashref {
'inventory_class' => {
'columns' => [
- 'classnum', 'serial', '', '', '', '',
- 'classname', 'varchar', $char_d, '', '', '',
+ 'classnum', 'serial', '', '', '', '',
+ 'classname', 'varchar', '', $char_d, '', '',
],
'primary_key' => 'classnum',
'unique' => [],
'index' => [],
},
+ 'access_user' => {
+ 'columns' => [
+ 'usernum', 'serial', '', '', '', '',
+ 'username', 'varchar', '', $char_d, '', '',
+ '_password', 'varchar', '', $char_d, '', '',
+ 'last', 'varchar', '', $char_d, '', '',
+ 'first', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'usernum',
+ 'unique' => [ [ 'username' ] ],
+ 'index' => [],
+ },
+
+ 'access_user_pref' => {
+ 'columns' => [
+ 'prefnum', 'serial', '', '', '', '',
+ 'usernum', 'int', '', '', '', '',
+ 'prefname', 'varchar', '', $char_d, '', '',
+ 'prefvalue', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'prefnum',
+ 'unique' => [],
+ 'index' => [ [ 'usernum' ] ],
+ },
+
+ 'access_group' => {
+ 'columns' => [
+ 'groupnum', 'serial', '', '', '', '',
+ 'groupname', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'groupnum',
+ 'unique' => [ [ 'groupname' ] ],
+ 'index' => [],
+ },
+
+ 'access_usergroup' => {
+ 'columns' => [
+ 'usergroupnum', 'serial', '', '', '', '',
+ 'usernum', 'int', '', '', '', '',
+ 'groupnum', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'usergroupnum',
+ 'unique' => [ [ 'usernum', 'groupnum' ] ],
+ 'index' => [ [ 'usernum' ] ],
+ },
+
+ 'access_groupagent' => {
+ 'columns' => [
+ 'groupagentnum', 'serial', '', '', '', '',
+ 'groupnum', 'int', '', '', '', '',
+ 'agentnum', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'groupagentnum',
+ 'unique' => [ [ 'groupnum', 'agentnum' ] ],
+ 'index' => [ [ 'groupnum' ] ],
+ },
+
+ 'access_right' => {
+ 'columns' => [
+ 'rightnum', 'serial', '', '', '', '',
+ 'righttype', 'varchar', '', $char_d, '', '',
+ 'rightobjnum', 'int', '', '', '', '',
+ 'rightname', 'varchar', '', '', '', '',
+ ],
+ 'primary_key' => 'rightnum',
+ 'unique' => [ [ 'righttype', 'rightobjnum', 'rightname' ] ],
+ 'index' => [],
+ },
+
};
+ #'new_table' => {
+ # 'columns' => [
+ # 'num', 'serial', '', '', '', '',
+ # ],
+ # 'primary_key' => 'num',
+ # 'unique' => [],
+ # 'index' => [],
+ #},
+
}
=back
diff --git a/FS/FS/UI/Web.pm b/FS/FS/UI/Web.pm
index dc45e0188..10ddbf33f 100644
--- a/FS/FS/UI/Web.pm
+++ b/FS/FS/UI/Web.pm
@@ -184,6 +184,10 @@ sub process {
$self->job_status(@args);
+ } else {
+
+ die "unknown sub $sub";
+
}
}
@@ -228,11 +232,19 @@ sub start_job {
my $error = $job->insert( '_JOB', encode_base64(nfreeze(\%param)) );
if ( $error ) {
+
+ warn "job not inserted: $error\n"
+ if $DEBUG;
+
$error; #this doesn't seem to be handled well,
# will trigger "illegal jobnum" below?
# (should never be an error inserting the job, though, only thing
# would be Pg f%*kage)
} else {
+
+ warn "job inserted successfully with jobnum ". $job->jobnum. "\n"
+ if $DEBUG;
+
$job->jobnum;
}
@@ -253,7 +265,7 @@ sub job_status {
my @return;
if ( $job && $job->status ne 'failed' ) {
@return = ( 'progress', $job->statustext );
- } elsif ( !$job ) { #handle job gone case : job sucessful
+ } elsif ( !$job ) { #handle job gone case : job successful
# so close popup, redirect parent window...
@return = ( 'complete' );
} else {
diff --git a/FS/FS/access_group.pm b/FS/FS/access_group.pm
new file mode 100644
index 000000000..9d870e57f
--- /dev/null
+++ b/FS/FS/access_group.pm
@@ -0,0 +1,121 @@
+package FS::access_group;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::access_group - Object methods for access_group records
+
+=head1 SYNOPSIS
+
+ use FS::access_group;
+
+ $record = new FS::access_group \%hash;
+ $record = new FS::access_group { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::access_group object represents an example. FS::access_group inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item groupnum - primary key
+
+=item groupname -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new example. To add the example 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 { 'access_group'; }
+
+=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 example. 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('groupnum')
+ || $self->ut_text('groupname')
+ ;
+ 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/FS/access_groupagent.pm b/FS/FS/access_groupagent.pm
new file mode 100644
index 000000000..6b5def1a3
--- /dev/null
+++ b/FS/FS/access_groupagent.pm
@@ -0,0 +1,124 @@
+package FS::access_groupagent;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::access_groupagent - Object methods for access_groupagent records
+
+=head1 SYNOPSIS
+
+ use FS::access_groupagent;
+
+ $record = new FS::access_groupagent \%hash;
+ $record = new FS::access_groupagent { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::access_groupagent object represents an example. FS::access_groupagent inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item groupagentnum - primary key
+
+=item groupnum -
+
+=item agentnum -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new example. To add the example 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 { 'access_groupagent'; }
+
+=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 example. 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('groupagentnum')
+ || $self->ut_number('groupnum')
+ || $self->ut_number('agentnum')
+ ;
+ 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/FS/access_right.pm b/FS/FS/access_right.pm
new file mode 100644
index 000000000..67200f245
--- /dev/null
+++ b/FS/FS/access_right.pm
@@ -0,0 +1,127 @@
+package FS::access_right;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::access_right - Object methods for access_right records
+
+=head1 SYNOPSIS
+
+ use FS::access_right;
+
+ $record = new FS::access_right \%hash;
+ $record = new FS::access_right { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::access_right object represents an example. FS::access_right inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item rightnum - primary key
+
+=item righttype -
+
+=item rightobjnum -
+
+=item rightname -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new example. To add the example 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 { 'access_right'; }
+
+=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 example. 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('rightnum')
+ || $self->ut_text('righttype')
+ || $self->ut_text('rightobjnum')
+ || $self->ut_text('rightname')
+ ;
+ 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/FS/access_user.pm b/FS/FS/access_user.pm
new file mode 100644
index 000000000..ca311d3b8
--- /dev/null
+++ b/FS/FS/access_user.pm
@@ -0,0 +1,167 @@
+package FS::access_user;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::m2m_Common;
+use FS::access_usergroup;
+
+@ISA = qw( FS::m2m_Common FS::Record );
+
+=head1 NAME
+
+FS::access_user - Object methods for access_user records
+
+=head1 SYNOPSIS
+
+ use FS::access_user;
+
+ $record = new FS::access_user \%hash;
+ $record = new FS::access_user { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::access_user object represents an example. FS::access_user inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item usernum - primary key
+
+=item username -
+
+=item _password -
+
+=item last -
+
+=item first -
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new example. To add the example 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 { 'access_user'; }
+
+=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 example. 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('usernum')
+ || $self->ut_text('username')
+ || $self->ut_text('_password')
+ || $self->ut_text('last')
+ || $self->ut_text('first')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item name
+
+Returns a name string for this user: "Last, First".
+
+=cut
+
+sub name {
+ my $self = shift;
+ $self->get('last'). ', '. $self->first;
+}
+
+=item access_usergroup
+
+=cut
+
+sub access_usergroup {
+ my $self = shift;
+ qsearch( 'access_usergroup', { 'usernum' => $self->usernum } );
+}
+
+#=item access_groups
+#
+#=cut
+#
+#sub access_groups {
+#
+#}
+#
+#=item access_groupnames
+#
+#=cut
+#
+#sub access_groupnames {
+#
+#}
+
+=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/FS/access_user_pref.pm b/FS/FS/access_user_pref.pm
new file mode 100644
index 000000000..ff957f2a1
--- /dev/null
+++ b/FS/FS/access_user_pref.pm
@@ -0,0 +1,127 @@
+package FS::access_user_pref;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::access_user_pref - Object methods for access_user_pref records
+
+=head1 SYNOPSIS
+
+ use FS::access_user_pref;
+
+ $record = new FS::access_user_pref \%hash;
+ $record = new FS::access_user_pref { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::access_user_pref object represents an example. FS::access_user_pref inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item prefnum - primary key
+
+=item usernum -
+
+=item prefname -
+
+=item prefvalue -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new example. To add the example 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 { 'access_user_pref'; }
+
+=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 example. 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('prefnum')
+ || $self->ut_number('usernum')
+ || $self->ut_text('prefname')
+ || $self->ut_textn('prefvalue')
+ ;
+ 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/FS/access_usergroup.pm b/FS/FS/access_usergroup.pm
new file mode 100644
index 000000000..4d8836c15
--- /dev/null
+++ b/FS/FS/access_usergroup.pm
@@ -0,0 +1,144 @@
+package FS::access_usergroup;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::access_user;
+use FS::access_group;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::access_usergroup - Object methods for access_usergroup records
+
+=head1 SYNOPSIS
+
+ use FS::access_usergroup;
+
+ $record = new FS::access_usergroup \%hash;
+ $record = new FS::access_usergroup { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::access_usergroup object represents an example. FS::access_usergroup inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item usergroupnum - primary key
+
+=item usernum -
+
+=item groupnum -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new example. To add the example 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 { 'access_usergroup'; }
+
+=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 example. 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('usergroupnum')
+ || $self->ut_number('usernum')
+ || $self->ut_number('groupnum')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item access_user
+
+=cut
+
+sub access_user {
+ my $self = shift;
+ qsearchs( 'access_user', { 'usernum' => $self->usernum } );
+}
+
+=item access_group
+
+=cut
+
+sub access_group {
+ my $self = shift;
+ qsearchs( 'access_group', { 'groupnum' => $self->groupnum } );
+}
+
+=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/FS/agent_type.pm b/FS/FS/agent_type.pm
index 968b3b72e..b28c57285 100644
--- a/FS/FS/agent_type.pm
+++ b/FS/FS/agent_type.pm
@@ -3,10 +3,11 @@ package FS::agent_type;
use strict;
use vars qw( @ISA );
use FS::Record qw( qsearch );
+use FS::m2m_Common;
use FS::agent;
use FS::type_pkgs;
-@ISA = qw( FS::Record );
+@ISA = qw( FS::m2m_Common FS::Record );
=head1 NAME
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
index cce028b2c..bcae4d646 100644
--- a/FS/FS/cust_bill.pm
+++ b/FS/FS/cust_bill.pm
@@ -2463,6 +2463,7 @@ use Data::Dumper;
use MIME::Base64;
sub process_re_X {
my( $method, $job ) = ( shift, shift );
+ warn "process_re_X $method for job $job\n" if $DEBUG;
my $param = thaw(decode_base64(shift));
warn Dumper($param) if $DEBUG;
@@ -2478,6 +2479,10 @@ sub process_re_X {
sub re_X {
my($method, $job, %param ) = @_;
# [ 'begin', 'end', 'agentnum', 'open', 'days', 'newest_percust' ],
+ if ( $DEBUG ) {
+ warn "re_X $method for job $job with param:\n".
+ join( '', map { " $_ => ". $param{$_}. "\n" } keys %param );
+ }
#some false laziness w/search/cust_bill.html
my $distinct = '';
diff --git a/FS/FS/m2m_Common.pm b/FS/FS/m2m_Common.pm
new file mode 100644
index 000000000..fd8700a2d
--- /dev/null
+++ b/FS/FS/m2m_Common.pm
@@ -0,0 +1,110 @@
+package FS::m2m_Common;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use FS::Schema qw( dbdef );
+use FS::Record qw( qsearch qsearchs ); #dbh );
+
+@ISA = qw( FS::Record );
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::m2m_Common - Base class for classes in a many-to-many relationship
+
+=head1 SYNOPSIS
+
+use FS::m2m_Common;
+
+@ISA = qw( FS::m2m_Common );
+
+=head1 DESCRIPTION
+
+FS::m2m_Common is intended as a base class for classes which have a
+many-to-many relationship with another table (via a linking table).
+
+Note: It is currently assumed that the link table contains two fields
+named the same as the primary keys of ths base and target tables.
+
+=head1 METHODS
+
+=over 4
+
+=item process_m2m
+
+=cut
+
+sub process_m2m {
+ my( $self, %opt ) = @_;
+
+ my $self_pkey = $self->dbdef_table->primary_key;
+
+ my $link_table = $self->_load_table($opt{'link_table'});
+
+ my $target_table = $self->_load_table($opt{'target_table'});
+ my $target_pkey = dbdef->table($target_table)->primary_key;
+
+ foreach my $target_obj ( qsearch($target_table, {} ) ) {
+
+ my $targetnum = $target_obj->$target_pkey();
+
+ my $link_obj = qsearchs( $link_table, {
+ $self_pkey => $self->$self_pkey(),
+ $target_pkey => $targetnum,
+ });
+
+ if ( $link_obj && ! $opt{'params'}->{"$target_pkey$targetnum"} ) {
+
+ my $d_link_obj = $link_obj; #need to save $link_obj for below.
+ my $error = $d_link_obj->delete;
+ die $error if $error;
+
+ } elsif ( $opt{'params'}->{"$target_pkey$targetnum"} && ! $link_obj ) {
+
+ #ok to clobber it now (but bad form nonetheless?)
+ #$link_obj = new "FS::$link_table" ( {
+ $link_obj = "FS::$link_table"->new( {
+ $self_pkey => $self->$self_pkey(),
+ $target_pkey => $targetnum,
+ });
+ my $error = $link_obj->insert;
+ die $error if $error;
+ }
+
+ }
+
+ '';
+}
+
+sub _load_table {
+ my( $self, $table ) = @_;
+ eval "use FS::$table";
+ die $@ if $@;
+ $table;
+}
+
+#=item target_table
+#
+#=cut
+#
+#sub target_table {
+# my $self = shift;
+# my $target_table = $self->_target_table;
+# eval "use FS::$target_table";
+# die $@ if $@;
+# $target_table;
+#}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_pkg/billoneday.pm b/FS/FS/part_pkg/billoneday.pm
deleted file mode 100644
index 8740547a3..000000000
--- a/FS/FS/part_pkg/billoneday.pm
+++ /dev/null
@@ -1,48 +0,0 @@
-package FS::part_pkg::billoneday;
-
-use strict;
-use vars qw(@ISA %info);
-use Time::Local qw(timelocal);
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-%info = (
- 'name' => 'charge a full month every (selectable) billing day',
- 'fields' => {
- 'setup_fee' => { 'name' => 'Setup fee for this package',
- 'default' => 0,
- },
- 'recur_fee' => { 'name' => 'Recurring fee for this package',
- 'default' => 0,
- },
- 'cutoff_day' => { 'name' => 'billing day',
- 'default' => 1,
- },
-
- },
- 'fieldorder' => [ 'setup_fee', 'recur_fee','cutoff_day'],
- #'setup' => 'what.setup_fee.value',
- #'recur' => '\'my $mnow = $sdate; my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($sdate) )[0,1,2,3,4,5]; $sdate = timelocal(0,0,0,$self->option('cutoff_day'),$mon,$year); \' + what.recur_fee.value',
- 'freq' => 'm',
- 'weight' => 30,
-);
-
-sub calc_recur {
- my($self, $cust_pkg, $sdate ) = @_;
-
- my $mnow = $$sdate;
- my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($mnow) )[0,1,2,3,4,5];
- my $mstart = timelocal(0,0,0,$self->option('cutoff_day'),$mon,$year);
- my $mend = timelocal(0,0,0,$self->option('cutoff_day'), $mon == 11 ? 0 : $mon+1, $year+($mon==11));
-
- if($mday > $self->option('cutoff_date') and $mstart != $mnow ) {
- $$sdate = timelocal(0,0,0,$self->option('cutoff_day'), $mon == 11 ? 0 : $mon+1, $year+($mon==11));
- }
- else{
- $$sdate = timelocal(0,0,0,$self->option('cutoff_day'), $mon, $year);
- }
- $self->option('recur_fee');
-}
-1;
diff --git a/FS/FS/payby.pm b/FS/FS/payby.pm
index 4425df040..9f8b68918 100644
--- a/FS/FS/payby.pm
+++ b/FS/FS/payby.pm
@@ -115,7 +115,8 @@ sub cust_payby2longname {
=head1 BUGS
-This should eventually be an actual database table.
+This should eventually be an actual database table, and all tables that
+currently have a char payby field should have a foreign key into here instead.
=head1 SEE ALSO
diff --git a/FS/FS/svc_domain.pm b/FS/FS/svc_domain.pm
index 191d85604..bdaf79b2f 100644
--- a/FS/FS/svc_domain.pm
+++ b/FS/FS/svc_domain.pm
@@ -230,7 +230,11 @@ sub delete {
my $error = $domain_record->delete;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
- return $error;
+ return "can't delete DNS entry: ".
+ join(' ', map $domain_record->$_(),
+ qw( reczone recaf rectype recdata )
+ ).
+ ":$error";
}
}
diff --git a/FS/MANIFEST b/FS/MANIFEST
index c309251ff..bd810a8db 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -339,3 +339,4 @@ FS/access_groupagent.pm
t/access_groupagent.t
FS/access_right.pm
t/access_right.t
+FS/m2m_Common.pm
diff --git a/FS/bin/freeside-addoutsourceuser b/FS/bin/freeside-addoutsourceuser
index cad07f1fd..02a435141 100644
--- a/FS/bin/freeside-addoutsourceuser
+++ b/FS/bin/freeside-addoutsourceuser
@@ -3,6 +3,7 @@
username=$1
domain=$2
password=$3
+realdomain=$4
freeside-adduser -h /usr/local/etc/freeside/htpasswd \
-s conf.DBI:Pg:dbname=$domain/secrets \
@@ -10,6 +11,5 @@ freeside-adduser -h /usr/local/etc/freeside/htpasswd \
$username $password 2>/dev/null
[ -e /usr/local/etc/freeside/dbdef.DBI:Pg:dbname=$domain ] \
- || ( freeside-setup -s $username 2>/dev/null; \
- /home/ivan/freeside/bin/populate-msgcat $username 2>/dev/null )
+ || ( freeside-setup -d $realdomain $username 2>/dev/null )
diff --git a/FS/t/AccessRight.t b/FS/t/AccessRight.t
new file mode 100644
index 000000000..a96684224
--- /dev/null
+++ b/FS/t/AccessRight.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::AccessRight;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/access_group.t b/FS/t/access_group.t
new file mode 100644
index 000000000..be141099b
--- /dev/null
+++ b/FS/t/access_group.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::access_group;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/access_groupagent.t b/FS/t/access_groupagent.t
new file mode 100644
index 000000000..aff1f2524
--- /dev/null
+++ b/FS/t/access_groupagent.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::access_groupagent;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/access_right.t b/FS/t/access_right.t
new file mode 100644
index 000000000..66cd362e8
--- /dev/null
+++ b/FS/t/access_right.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::access_right;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/access_user.t b/FS/t/access_user.t
new file mode 100644
index 000000000..cab679d8d
--- /dev/null
+++ b/FS/t/access_user.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::access_user;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/access_user_pref.t b/FS/t/access_user_pref.t
new file mode 100644
index 000000000..282209830
--- /dev/null
+++ b/FS/t/access_user_pref.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::access_user_pref;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/access_usergroup.t b/FS/t/access_usergroup.t
new file mode 100644
index 000000000..383a7cf9c
--- /dev/null
+++ b/FS/t/access_usergroup.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::access_usergroup;
+$loaded=1;
+print "ok 1\n";
diff --git a/htetc/handler.pl b/htetc/handler.pl
index e8cd3333b..cbe2dd35d 100644
--- a/htetc/handler.pl
+++ b/htetc/handler.pl
@@ -179,6 +179,8 @@ sub handler
use FS::inventory_class;
use FS::inventory_item;
use FS::pkg_class;
+ use FS::access_user;
+ use FS::access_group;
if ( %%%RT_ENABLED%%% ) {
eval '
diff --git a/httemplate/autohandler b/httemplate/autohandler
index a3f7eb008..ad0ab8ba6 100644
--- a/httemplate/autohandler
+++ b/httemplate/autohandler
@@ -9,7 +9,15 @@ if ( UNIVERSAL::can(dbh, 'sprintProfile') ) {
if ( lc($r->content_type) eq 'text/html' ) {
- $profile = '<PRE>'. encode_entities(dbh->sprintProfile()).
+ # barely worth it, just in case someone tries to use profiling on a
+ # non-RT install
+ eval "use Text::Wrapper;";
+ die $@ if $@;
+
+ my $wrapper = new Text::Wrapper( columns => 80 );
+
+ $profile = '<PRE>'.
+ encode_entities( $wrapper->wrap( dbh->sprintProfile() ) ).
#"\n\n". &sprintAutoProfile(). '</PRE>';
"\n\n". '</PRE>';
}
diff --git a/httemplate/browse/access_group.html b/httemplate/browse/access_group.html
new file mode 100644
index 000000000..6ba89ea81
--- /dev/null
+++ b/httemplate/browse/access_group.html
@@ -0,0 +1,33 @@
+<%
+
+my $html_init =
+ "Internal access groups control access to the back-office interface.<BR><BR>".
+ qq!<A HREF="${p}edit/access_group.html"><I>Add an internal access group</I></A><BR><BR>!;
+
+my $count_query = 'SELECT COUNT(*) FROM access_group';
+
+my $link = [ $p.'edit/access_group.html?', 'groupnum' ];
+
+%><%= include( 'elements/browse.html',
+ 'title' => 'Internal Access Groups',
+ 'menubar' => [ # 'Main menu' => $p,
+ 'Internal users' => $p.'browse/access_user.html',
+ ],
+ 'html_init' => $html_init,
+ 'name' => 'internal access groups',
+ 'query' => { 'table' => 'access_group',
+ 'hashref' => {},
+ 'extra_sql' => 'ORDER BY groupname', #??
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#',
+ 'Group name',
+ ],
+ 'fields' => [ 'groupnum',
+ 'groupname',
+ ],
+ 'links' => [ $link,
+ $link,
+ ],
+ )
+%>
diff --git a/httemplate/browse/access_user.html b/httemplate/browse/access_user.html
new file mode 100644
index 000000000..38d5430b1
--- /dev/null
+++ b/httemplate/browse/access_user.html
@@ -0,0 +1,63 @@
+<%
+
+my $html_init =
+ "Internal users have access to the back-office interface. Typically, this is your employees and contractors, but in a VISP setup, you can also add accounts for your reseller's employees. It is <B>highly recommended</B> to add a <B>separate account for each person</B> rather than using role accounts.<BR><BR>".
+ qq!<A HREF="${p}edit/access_user.html"><I>Add an internal user</I></A><BR><BR>!;
+
+#false laziness w/agent_type.cgi
+my $groups_sub = sub {
+ my $access_user = shift;
+
+ [ map {
+ my $access_usergroup = $_;
+ my $access_group = $access_usergroup->access_group;
+ [
+ {
+ 'data' => $access_group->groupname,
+ 'align' => 'left',
+ 'link' =>
+ $p. 'edit/access_group.html?'. $access_usergroup->groupnum,
+ },
+ ];
+ }
+ grep { $_->access_group # and ! $_->access_group->disabled
+ }
+ $access_user->access_usergroup,
+
+ ];
+
+};
+
+my $count_query = 'SELECT COUNT(*) FROM access_user';
+
+my $link = [ $p.'edit/access_user.html?', 'usernum' ];
+
+%><%= include( 'elements/browse.html',
+ 'title' => 'Internal Users',
+ 'menubar' => [ #'Main menu' => $p,
+ 'Internal access groups' => $p.'browse/access_group.html',
+ ],
+ 'html_init' => $html_init,
+ 'name' => 'internal users',
+ 'query' => { 'table' => 'access_user',
+ 'hashref' => {},
+ 'extra_sql' => 'ORDER BY last, first',
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#',
+ 'Username',
+ 'Full name',
+ 'Groups'
+ ],
+ 'fields' => [ 'usernum',
+ 'username',
+ 'name', # sub { shift->name },
+ $groups_sub,
+ ],
+ 'links' => [ $link,
+ $link,
+ $link,
+ ''
+ ],
+ )
+%>
diff --git a/httemplate/browse/agent_type.cgi b/httemplate/browse/agent_type.cgi
index 2e1bdad42..a5ffb1048 100755
--- a/httemplate/browse/agent_type.cgi
+++ b/httemplate/browse/agent_type.cgi
@@ -1,60 +1,62 @@
-<!-- mason kludge -->
-<%= include("/elements/header.html","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>
+my $html_init =
+ 'Agent types define groups of packages that you can then assign to'.
+ ' particular agents.<BR><BR>'.
+ qq!<A HREF="${p}edit/agent_type.cgi"><I>Add a new agent type</I></A><BR><BR>!;
-<%
-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 $count_query = 'SELECT COUNT(*) FROM agent_type';
- 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 ;
- }
- }
+#false laziness w/access_user.html
+my $packages_sub = sub {
+ my $agent_type = shift;
- print "</TR>";
-}
+ [ map {
+ my $type_pkgs = $_;
+ my $part_pkg = $type_pkgs->part_pkg;
+ [
+ {
+ 'data' => $part_pkg->pkg. ' - '. $part_pkg->comment,
+ 'align' => 'left',
+ 'link' => $p. 'edit/part_pkg.cgi?'. $type_pkgs->pkgpart,
+ },
+ ];
+ }
+ #sort {
+ # }
+ grep {
+ $_->part_pkg and ! $_->part_pkg->disabled
+ }
+ $agent_type->type_pkgs #XXX the method should order itself by something
+ ];
-print <<END;
- </TABLE>
- </BODY>
-</HTML>
-END
+};
+my $link = [ $p.'edit/agent_type.cgi?', 'typenum' ];
+
+%><%= include( 'elements/browse.html',
+ 'title' => 'Agent Types',
+ 'menubar' => [ #'Main menu' => $p,
+ 'Agents' =>"${p}browse/agent.cgi",
+ ],
+ 'html_init' => $html_init,
+ 'name' => 'agent types',
+ 'query' => { 'table' => 'agent_type',
+ 'hashref' => {},
+ 'extra_sql' => 'ORDER BY typenum', # 'ORDER BY atype',
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#',
+ 'Agent Type',
+ 'Packages',
+ ],
+ 'fields' => [ 'typenum',
+ 'atype',
+ $packages_sub,
+ ],
+ 'links' => [ $link,
+ $link,
+ '',
+ ],
+ )
%>
diff --git a/httemplate/browse/cust_main_county.cgi b/httemplate/browse/cust_main_county.cgi
index 1e0e0880c..9e3feb8f3 100755
--- a/httemplate/browse/cust_main_county.cgi
+++ b/httemplate/browse/cust_main_county.cgi
@@ -1,33 +1,34 @@
-<!-- mason kludge -->
-<%
+<%= include('/elements/header.html', "Tax Rate Listing", menubar(
+ 'Edit tax rates' => $p. "edit/cust_main_county.cgi",
+)) %>
+
+ 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.
+<%
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 ) { %>
-if ( $enable_taxclasses ) {
- print '<BR>Click on <u>expand taxclasses</u> to specify tax classes';
-}
+ <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
+<% } %>
+
+<BR><BR>
+<%= table() %>
+
+ <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>
+<%
my @regions = sort { $a->country cmp $b->country
or $a->state cmp $b->state
or $a->county cmp $b->county
@@ -39,10 +40,12 @@ my $sup=0;
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
+ <TD BGCOLOR="#ffffff"><%= $hashref->{country} %></TD>
+
+ <%
my $j;
if ( $sup ) {
@@ -74,69 +77,73 @@ END
$j = 1;
}
- print "<TD ROWSPAN=$j", $hashref->{state}
+ %>
+
+ <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
-
-%>
+ qq!">expand country</A></FONT>!
+ %>
+ <% if ( $j>1 ) { %>
+ <FONT SIZE=-1><A HREF="<%= $p %>edit/process/cust_main_county-collapse.cgi?<%= $hashref->{taxnum} %>">collapse state</A></FONT>
+ <% } %>
+
+ </TD>
+ <% } %>
+
+<% # $sup=$newsup; %>
+
+ <TD<% if ( $hashref->{county} ) {
+ %> BGCOLOR="#ffffff"><%= $hashref->{county} %>
+ <% } else {
+ %> BGCOLOR="#cccccc">(ALL)
+ <% if ( $hashref->{state} ) { %>
+ <FONT SIZE=-1><A HREF="<%= $p %>edit/cust_main_county-expand.cgi?<%= $hashref->{taxnum} %>">expand state</A></FONT>
+ <% } %>
+ <% } %>
+ </TD>
+
+ <TD<% if ( $hashref->{taxclass} ) {
+ %> BGCOLOR="#ffffff"><%= $hashref->{taxclass} %>
+ <% } else {
+ %> BGCOLOR="#cccccc">(ALL)
+ <% if ( $enable_taxclasses ) { %>
+ <FONT SIZE=-1><A HREF="<%= $p %>edit/cust_main_county-expand.cgi?taxclass<%= $hashref->{taxnum} %>">expand taxclasses</A></FONT>
+ <% } %>
+ <% } %>
+ </TD>
+
+ <TD<% if ( $hashref->{taxname} ) {
+ %> BGCOLOR="#ffffff"><%= $hashref->{taxname} %>
+ <% } else {
+ %> BGCOLOR="#cccccc">Tax
+ <% } %>
+ </TD>
+
+ <TD BGCOLOR="#ffffff"><%= $hashref->{tax} %>%</TD>
+
+ <TD BGCOLOR="#ffffff">
+
+ <% if ( $hashref->{exempt_amount} > 0 ) { %>
+ $<%= sprintf("%.2f", $hashref->{exempt_amount} ) %>&nbsp;per&nbsp;month<BR>
+ <% } %>
+
+ <% if ( $hashref->{setuptax} =~ /^Y$/i ) { %>
+ Setup&nbsp;fee<BR>
+ <% } %>
+
+ <% if ( $hashref->{recurtax} =~ /^Y$/i ) { %>
+ Recurring&nbsp;fee<BR>
+ <% } %>
+
+ </TD>
+
+ </TR>
+
+<% } %>
+
+</TABLE>
+
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/browse/msgcat.cgi b/httemplate/browse/msgcat.cgi
index d4adf9f1a..318ebfdff 100755
--- a/httemplate/browse/msgcat.cgi
+++ b/httemplate/browse/msgcat.cgi
@@ -1,10 +1,6 @@
-<!-- mason kludge -->
-<%
-
-print header("View Message catalog", menubar(
- 'Main Menu' => $p,
+<%= include('/elements/header.html', "View Message catalog", menubar(
'Edit message catalog' => $p. "edit/msgcat.cgi",
-)), '<BR>';
+)) %><%
my $widget = new HTML::Widgets::SelectLayers(
'selected_layer' => 'en_US',
@@ -38,13 +34,7 @@ my $widget = new HTML::Widgets::SelectLayers(
},
);
-
-print $widget->html;
-
-print <<END;
- </TABLE>
- </BODY>
-</HTML>
-END
-
%>
+
+<%= $widget->html %>
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/browse/part_pkg.cgi b/httemplate/browse/part_pkg.cgi
index 0afa54750..41d86358c 100755
--- a/httemplate/browse/part_pkg.cgi
+++ b/httemplate/browse/part_pkg.cgi
@@ -11,8 +11,8 @@ my $select = '*';
my $orderby = 'pkgpart';
if ( $cgi->param('active') ) {
- $orderby = 'num_active';
-
+ $orderby = 'num_active DESC';
+}
$select = "
*,
@@ -33,13 +33,13 @@ if ( $cgi->param('active') ) {
";
-}
+#}
my $conf = new FS::Conf;
my $taxclasses = $conf->exists('enable_taxclasses');
my $html_init;
-unless ( $cgi->param('active') ) {
+#unless ( $cgi->param('active') ) {
$html_init = qq!
One or more service definitions are grouped together into a package
definition and given pricing information. Customers purchase packages
@@ -47,7 +47,7 @@ unless ( $cgi->param('active') ) {
<A HREF="${p}edit/part_pkg.cgi"><I>Add a new package definition</I></A>
<BR><BR>
!;
-}
+#}
my $posttotal;
if ( $cgi->param('showdisabled') ) {
@@ -85,7 +85,7 @@ unless ( 0 ) { #already showing only one class or something?
$align .= 'l';
}
-if ( $cgi->param('active') ) {
+#if ( $cgi->param('active') ) {
push @header, 'Customer<BR>packages';
my %col = (
'active' => '00CC00',
@@ -117,7 +117,7 @@ if ( $cgi->param('active') ) {
} (qw( active suspended cancelled ))
]; };
$align .= 'r';
-}
+#}
push @header, 'Frequency';
push @fields, sub { shift->freq_pretty; };
diff --git a/httemplate/edit/access_group.html b/httemplate/edit/access_group.html
new file mode 100644
index 000000000..11b8df7bc
--- /dev/null
+++ b/httemplate/edit/access_group.html
@@ -0,0 +1,10 @@
+<%= include( 'elements/edit.html',
+ 'name' => 'Internal Access Group',
+ 'table' => 'access_group',
+ 'labels' => {
+ 'groupnum' => 'Group number',
+ 'groupname' => 'Group name',
+ },
+ 'viewall_dir' => 'browse',
+ )
+%>
diff --git a/httemplate/edit/access_user.html b/httemplate/edit/access_user.html
new file mode 100644
index 000000000..2b19dbf7b
--- /dev/null
+++ b/httemplate/edit/access_user.html
@@ -0,0 +1,37 @@
+<%= include( 'elements/edit.html',
+ 'name' => 'Internal User',
+ 'table' => 'access_user',
+ 'fields' => [
+ 'username',
+ { field=>'_password', type=>'password' },
+ 'last',
+ 'first',
+ ],
+ 'labels' => {
+ 'usernum' => 'User number',
+ 'username' => 'Username',
+ '_password' => 'Password',
+ 'last' => 'Last name',
+ 'first' => 'First name',
+ },
+ 'viewall_dir' => 'browse',
+ 'html_bottom' =>
+ sub {
+ my $access_user = shift;
+
+ '<BR>Internal Access Groups<BR>'.
+ ntable("#cccccc",2).
+ '<TR><TD>'.
+ include( '/elements/checkboxes-table.html',
+ 'source_obj' => $access_user,
+ 'link_table' => 'access_usergroup',
+ 'target_table' => 'access_group',
+ 'name_col' => 'groupname',
+ 'target_link' => $p.'edit/access_group.html?',
+ #'disable-able' => 1,
+ ).
+ '</TR></TD></TABLE>'
+ ;
+ },
+ )
+%>
diff --git a/httemplate/edit/agent_type.cgi b/httemplate/edit/agent_type.cgi
index 944ddd0d0..f5afd3a96 100755
--- a/httemplate/edit/agent_type.cgi
+++ b/httemplate/edit/agent_type.cgi
@@ -14,9 +14,7 @@ if ( $cgi->param('error') ) {
}
my $action = $agent_type->typenum ? 'Edit' : 'Add';
-%>
-
-<%= include("/elements/header.html","$action Agent Type", menubar(
+%><%= include("/elements/header.html","$action Agent Type", menubar(
'Main Menu' => "$p",
'View all agent types' => "${p}browse/agent_type.cgi",
))
@@ -29,47 +27,29 @@ my $action = $agent_type->typenum ? 'Edit' : 'Add';
<FORM ACTION="<%= popurl(1) %>process/agent_type.cgi" METHOD=POST>
<INPUT TYPE="hidden" NAME="typenum" VALUE="<%= $agent_type->typenum %>">
Agent Type #<%= $agent_type->typenum || "(NEW)" %>
-<BR><BR>
+<BR>
Agent Type
<INPUT TYPE="text" NAME="atype" SIZE=32 VALUE="<%= $agent_type->atype %>">
<BR><BR>
Select which packages agents of this type may sell to customers<BR>
-
-<% foreach my $part_pkg (
- qsearch({ 'table' => 'part_pkg',
- 'hashref' => { 'disabled' => '' },
- 'select' => 'part_pkg.*',
- 'addl_from' => 'LEFT JOIN type_pkgs USING ( pkgpart )',
- 'extra_sql' => ( $agent_type->typenum
- ? 'OR typenum = '. $agent_type->typenum
- : ''
- ),
- })
- ) {
+<%= ntable("#cccccc", 2) %><TR><TD>
+<%= include('/elements/checkboxes-table.html',
+ 'source_obj' => $agent_type,
+ 'link_table' => 'type_pkgs',
+ 'target_table' => 'part_pkg',
+ 'name_callback' => sub { $_[0]->pkg. ' - '. $_[0]->comment; },
+ 'target_link' => $p.'edit/part_pkg.cgi?',
+ 'disable-able' => 1,
+
+ )
%>
-
- <BR>
- <INPUT TYPE="checkbox" NAME="pkgpart<%= $part_pkg->pkgpart %>" <%=
- qsearchs('type_pkgs',{
- 'typenum' => $agent_type->typenum,
- 'pkgpart' => $part_pkg->pkgpart,
- })
- ? 'CHECKED '
- : ''
- %> VALUE="ON">
-
- <A HREF="<%= $p %>edit/part_pkg.cgi?<%= $part_pkg->pkgpart %>"><%= $part_pkg->pkgpart %>:
- <%= $part_pkg->pkg %> - <%= $part_pkg->comment %></A>
- <%= $part_pkg->disabled =~ /^Y/i ? ' (DISABLED)' : '' %>
-
-<% } %>
-
-<BR><BR>
+</TD></TR></TABLE>
+<BR>
<INPUT TYPE="submit" VALUE="<%= $agent_type->typenum ? "Apply changes" : "Add agent type" %>">
</FORM>
- </BODY>
-</HTML>
+
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/edit/cust_bill_pay.cgi b/httemplate/edit/cust_bill_pay.cgi
index 24bce308a..9d3bdd8cb 100755
--- a/httemplate/edit/cust_bill_pay.cgi
+++ b/httemplate/edit/cust_bill_pay.cgi
@@ -1,4 +1,3 @@
-<!-- mason kludge -->
<%
my($paynum, $amount, $invnum);
@@ -18,78 +17,76 @@ my $otaker = getotaker;
my $p1 = popurl(1);
-print header("Apply Payment", '');
-print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
- "</FONT><BR><BR>"
- if $cgi->param('error');
-print <<END;
- <FORM ACTION="${p1}process/cust_bill_pay.cgi" METHOD=POST>
-END
+%><%= header("Apply Payment", '') %>
+<% if ( $cgi->param('error') ) { %>
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <%= $cgi->param('error') %></FONT>
+ <BR><BR>
+<% } %>
+
+<FORM ACTION="<%= $p1 %>process/cust_bill_pay.cgi" METHOD=POST>
+
+<%
my $cust_pay = qsearchs('cust_pay', { 'paynum' => $paynum } );
die "payment $paynum not found!" unless $cust_pay;
my $unapplied = $cust_pay->unapplied;
+%>
+
+Payment #<B><%= $paynum %></B>
+<INPUT TYPE="hidden" NAME="paynum" VALUE="<%= $paynum %>">
-print "Payment # <B>$paynum</B>".
- qq!<INPUT TYPE="hidden" NAME="paynum" VALUE="$paynum">!.
- '<BR>Date: <B>'. time2str("%D", $cust_pay->_date). '</B>'.
- '<BR>Amount: $<B>'. $cust_pay->paid. '</B>'.
- "<BR>Unapplied amount: \$<B>$unapplied</B>"
- ;
+<BR>Date: <B><%= time2str("%D", $cust_pay->_date) %></B>
+<BR>Amount: $<B><%= $cust_pay->paid %></B>
+
+<BR>Unapplied amount: $<B><%= $unapplied %></B>
+
+<%
my @cust_bill = grep $_->owed != 0,
qsearch('cust_bill', { 'custnum' => $cust_pay->custnum } );
-print <<END;
+%>
+
<SCRIPT>
function changed(what) {
cust_bill = what.options[what.selectedIndex].value;
-END
-foreach my $cust_bill ( @cust_bill ) {
+<% foreach my $cust_bill ( @cust_bill ) {
my $invnum = $cust_bill->invnum;
my $changeto = $cust_bill->owed < $unapplied
? $cust_bill->owed
: $unapplied;
- print <<END;
+%>
if ( cust_bill == $invnum ) {
- what.form.amount.value = "$changeto";
+ what.form.amount.value = "<%= $changeto %>";
}
-END
-}
+<% } %>
-print <<END;
if ( cust_bill == "Refund" ) {
- what.form.amount.value = "$unapplied";
+ what.form.amount.value = "<%= $unapplied %>";
}
}
</SCRIPT>
-END
-
-print qq!<BR>Invoice #<SELECT NAME="invnum" SIZE=1 onChange="changed(this)">!,
- '<OPTION VALUE="">';
-foreach my $cust_bill ( @cust_bill ) {
- print '<OPTION'. ( $cust_bill->invnum eq $invnum ? ' SELECTED' : '' ).
- ' VALUE="'. $cust_bill->invnum. '">'. $cust_bill->invnum.
- ' - '. time2str("%D",$cust_bill->_date).
- ' - $'. $cust_bill->owed;
-}
-print qq!<OPTION VALUE="Refund">Refund!;
-print "</SELECT>";
-print qq!<BR>Amount \$<INPUT TYPE="text" NAME="amount" VALUE="$amount" SIZE=8 MAXLENGTH=8>!;
+<BR>Invoice #<SELECT NAME="invnum" SIZE=1 onChange="changed(this)">
+<OPTION VALUE="">
+
+<% foreach my $cust_bill ( @cust_bill ) { %>
+
+ <OPTION<%= $cust_bill->invnum eq $invnum ? ' SELECTED' : '' %> VALUE="<%= $cust_bill->invnum %>"><%= $cust_bill->invnum %> - <%= time2str("%D", $cust_bill->_date) %> - $<%= $cust_bill->owed %>
+
+<% } %>
+
+<OPTION VALUE="Refund">Refund
+</SELECT>
+
+<BR>Amount $<INPUT TYPE="text" NAME="amount" VALUE="<%= $amount %>" SIZE=8 MAXLENGTH=8>
-print <<END;
<BR>
-<INPUT TYPE="submit" VALUE="Apply">
-END
+<CENTER><INPUT TYPE="submit" VALUE="Apply"></CENTER>
-print <<END;
+</FORM>
- </FORM>
- </BODY>
+</BODY>
</HTML>
-END
-
-%>
diff --git a/httemplate/edit/cust_credit.cgi b/httemplate/edit/cust_credit.cgi
index aae0df2fc..946b1087b 100755
--- a/httemplate/edit/cust_credit.cgi
+++ b/httemplate/edit/cust_credit.cgi
@@ -1,4 +1,3 @@
-<!-- mason kludge -->
<%
my $conf = new FS::Conf;
@@ -25,39 +24,57 @@ my $otaker = getotaker;
my $p1 = popurl(1);
-print header("Post Credit", '');
-print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
- "</FONT>"
- if $cgi->param('error');
-print <<END, small_custview($custnum, $conf->config('countrydefault'));
- <FORM ACTION="${p1}process/cust_credit.cgi" METHOD=POST>
- <INPUT TYPE="hidden" NAME="crednum" VALUE="">
- <INPUT TYPE="hidden" NAME="custnum" VALUE="$custnum">
- <INPUT TYPE="hidden" NAME="paybatch" VALUE="">
- <INPUT TYPE="hidden" NAME="_date" VALUE="$_date">
- <INPUT TYPE="hidden" NAME="credited" VALUE="">
- <INPUT TYPE="hidden" NAME="otaker" VALUE="$otaker">
-END
-
-print '<BR><BR>Credit'. ntable("#cccccc", 2).
- '<TR><TD ALIGN="right">Date</TD><TD BGCOLOR="#ffffff">'.
- time2str("%D",$_date). '</TD></TR>';
-
-print qq!<TR><TD ALIGN="right">Amount</TD><TD BGCOLOR="#ffffff">\$<INPUT TYPE="text" NAME="amount" VALUE="$amount" SIZE=8 MAXLENGTH=8></TD></TR>!;
+%>
+
+<%= header("Post Credit", '') %>
+
+<% if ( $cgi->param('error') ) { %>
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <%= $cgi->param('error') %></FONT>
+ <BR><BR>
+<% } %>
+
+<!-- <%= small_custview($custnum, $conf->config('countrydefault')) %> -->
+
+<FORM ACTION="<%= $p1 %>process/cust_credit.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="crednum" VALUE="">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<%= $custnum %>">
+<INPUT TYPE="hidden" NAME="paybatch" VALUE="">
+<INPUT TYPE="hidden" NAME="_date" VALUE="<%= $_date %>">
+<INPUT TYPE="hidden" NAME="credited" VALUE="">
+<INPUT TYPE="hidden" NAME="otaker" VALUE="<%= $otaker %>">
+
+Credit
+
+<%= ntable("#cccccc", 2) %>
+ <TR>
+ <TD ALIGN="right">Date</TD>
+ <TD BGCOLOR="#ffffff"><%= time2str("%D",$_date) %></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Amount</TD>
+ <TD BGCOLOR="#ffffff">$<INPUT TYPE="text" NAME="amount" VALUE="<%= $amount %>" SIZE=8 MAXLENGTH=8></TD>
+ </TR>
+
+<%
#print qq! <INPUT TYPE="checkbox" NAME="refund" VALUE="$refund">Also post refund!;
+%>
-print qq!<TR><TD ALIGN="right">Reason</TD><TD BGCOLOR="#ffffff"><INPUT TYPE="text" NAME="reason" VALUE="$reason"></TD></TR>!;
+ <TR>
+ <TD ALIGN="right">Reason</TD>
+ <TD BGCOLOR="#ffffff"><INPUT TYPE="text" NAME="reason" VALUE="<%= $reason %>" SIZE=32></TD>
+ </TR>
-print qq!<TR><TD ALIGN="right">Auto-apply<BR>to invoices</TD><TD><SELECT NAME="apply"><OPTION VALUE="yes" SELECTED>yes<OPTION>no</SELECT></TD>!;
+ <TR>
+ <TD ALIGN="right">Auto-apply<BR>to invoices</TD>
+ <TD><SELECT NAME="apply"><OPTION VALUE="yes" SELECTED>yes<OPTION>no</SELECT></TD>
+ </TR>
-print <<END;
</TABLE>
+
<BR>
-<INPUT TYPE="submit" VALUE="Post credit">
+<CENTER><INPUT TYPE="submit" VALUE="Post credit"></CENTER>
</FORM>
</BODY>
</HTML>
-END
-
-%>
diff --git a/httemplate/edit/cust_credit_bill.cgi b/httemplate/edit/cust_credit_bill.cgi
index 1a97e1312..409ea3c25 100755
--- a/httemplate/edit/cust_credit_bill.cgi
+++ b/httemplate/edit/cust_credit_bill.cgi
@@ -1,4 +1,3 @@
-<!-- mason kludge -->
<%
my($crednum, $amount, $invnum);
@@ -23,79 +22,78 @@ my $otaker = getotaker;
my $p1 = popurl(1);
-print header("Apply Credit", '');
-print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
- "</FONT><BR><BR>"
- if $cgi->param('error');
-print <<END;
- <FORM ACTION="${p1}process/cust_credit_bill.cgi" METHOD=POST>
-END
+%><%= header("Apply Credit", '') %>
+<% if ( $cgi->param('error') ) { %>
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <%= $cgi->param('error') %></FONT>
+ <BR><BR>
+<% } %>
+
+<FORM ACTION="<%= $p1 %>process/cust_credit_bill.cgi" METHOD=POST>
+
+<%
my $cust_credit = qsearchs('cust_credit', { 'crednum' => $crednum } );
die "credit $crednum not found!" unless $cust_credit;
my $credited = $cust_credit->credited;
+%>
+
+Credit #<B><%= $crednum %></B>
+<INPUT TYPE="hidden" NAME="crednum" VALUE="<%= $crednum %>">
+
+<BR>Date: <B><%= time2str("%D", $cust_credit->_date) %></B>
-print "Credit # <B>$crednum</B>".
- qq!<INPUT TYPE="hidden" NAME="crednum" VALUE="$crednum">!.
- '<BR>Date: <B>'. time2str("%D", $cust_credit->_date). '</B>'.
- '<BR>Amount: $<B>'. $cust_credit->amount. '</B>'.
- "<BR>Unapplied amount: \$<B>$credited</B>".
- '<BR>Reason: <B>'. $cust_credit->reason. '</B>'
- ;
+<BR>Amount: $<B><%= $cust_credit->amount %></B>
+<BR>Unapplied amount: $<B><%= $credited %></B>
+
+<BR>Reason: <B><%= $cust_credit->reason %></B>
+
+<%
my @cust_bill = grep $_->owed != 0,
qsearch('cust_bill', { 'custnum' => $cust_credit->custnum } );
-print <<END;
+%>
+
<SCRIPT>
function changed(what) {
cust_bill = what.options[what.selectedIndex].value;
-END
-foreach my $cust_bill ( @cust_bill ) {
+<% foreach my $cust_bill ( @cust_bill ) {
my $invnum = $cust_bill->invnum;
my $changeto = $cust_bill->owed < $cust_credit->credited
? $cust_bill->owed
: $cust_credit->credited;
- print <<END;
+%>
if ( cust_bill == $invnum ) {
- what.form.amount.value = "$changeto";
+ what.form.amount.value = "<%= $changeto %>";
}
-END
-}
+<% } %>
-print <<END;
if ( cust_bill == "Refund" ) {
- what.form.amount.value = "$credited";
+ what.form.amount.value = "<%= $credited %>";
}
}
</SCRIPT>
-END
-
-print qq!<BR>Invoice #<SELECT NAME="invnum" SIZE=1 onChange="changed(this)">!,
- '<OPTION VALUE="">';
-foreach my $cust_bill ( @cust_bill ) {
- print '<OPTION'. ( $cust_bill->invnum eq $invnum ? ' SELECTED' : '' ).
- ' VALUE="'. $cust_bill->invnum. '">'. $cust_bill->invnum.
- ' - '. time2str("%D",$cust_bill->_date).
- ' - $'. $cust_bill->owed;
-}
-print qq!<OPTION VALUE="Refund">Refund!;
-print "</SELECT>";
-print qq!<BR>Amount \$<INPUT TYPE="text" NAME="amount" VALUE="$amount" SIZE=8 MAXLENGTH=8>!;
+<BR>Invoice #<SELECT NAME="invnum" SIZE=1 onChange="changed(this)">
+<OPTION VALUE="">
+
+<% foreach my $cust_bill ( @cust_bill ) { %>
+
+<OPTION<%= $cust_bill->invnum eq $invnum ? ' SELECTED' : '' %> VALUE="<%= $cust_bill->invnum %>"><%= $cust_bill->invnum %> - <%= time2str("%D",$cust_bill->_date) %> - $<%= $cust_bill->owed %>
+
+<% } %>
+
+<OPTION VALUE="Refund">Refund
+</SELECT>
+
+<BR>Amount $<INPUT TYPE="text" NAME="amount" VALUE="<%= $amount %>" SIZE=8 MAXLENGTH=8>
-print <<END;
<BR>
-<INPUT TYPE="submit" VALUE="Apply">
-END
+<CENTER><INPUT TYPE="submit" VALUE="Apply"></CENTER>
-print <<END;
+</FORM>
- </FORM>
- </BODY>
+</BODY>
</HTML>
-END
-
-%>
diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi
index 80fec9359..bb2a8618e 100755
--- a/httemplate/edit/cust_main.cgi
+++ b/httemplate/edit/cust_main.cgi
@@ -397,49 +397,66 @@ unless ( $custnum ) {
if ( @part_pkg ) {
-# print "<BR><BR>First package", &itable("#cccccc", "0 ALIGN=LEFT"),
-#apiabuse & undesirable wrapping
- print "<BR>First package", &ntable("#cccccc"),
- qq!<TR><TD COLSPAN=2><SELECT NAME="pkgpart_svcpart">!;
-
- print qq!<OPTION VALUE="">(none)!;
-
- foreach my $part_pkg ( @part_pkg ) {
- print qq!<OPTION VALUE="!,
-# $part_pkg->pkgpart. "_". $pkgpart{ $part_pkg->pkgpart }, '"';
- $part_pkg->pkgpart. "_". $part_pkg->svcpart('svc_acct'), '"';
- print " SELECTED" if $saved_pkgpart && ( $part_pkg->pkgpart == $saved_pkgpart );
- print ">", $part_pkg->pkg, " - ", $part_pkg->comment;
- }
- print "</SELECT></TD></TR>";
-
- #false laziness: (mostly) copied from edit/svc_acct.cgi
- #$ulen = $svc_acct->dbdef_table->column('username')->length;
- my $ulen = dbdef->table('svc_acct')->column('username')->length;
- my $ulen2 = $ulen+2;
- my $passwordmax = $conf->config('passwordmax') || 8;
- my $pmax2 = $passwordmax + 2;
- print <<END;
-<TR><TD ALIGN="right">Username</TD>
-<TD><INPUT TYPE="text" NAME="username" VALUE="$username" SIZE=$ulen2 MAXLENGTH=$ulen></TD></TR>
-<TR><TD ALIGN="right">Password</TD>
-<TD><INPUT TYPE="text" NAME="_password" VALUE="$password" SIZE=$pmax2 MAXLENGTH=$passwordmax>
-(blank to generate)</TD></TR>
-END
-
- print '<TR><TD ALIGN="right">Access number</TD><TD>'
- .
- &FS::svc_acct_pop::popselector($popnum).
- '</TD></TR></TABLE>'
- ;
- }
-}
+ # print "<BR><BR>First package", &itable("#cccccc", "0 ALIGN=LEFT"),
+ #apiabuse & undesirable wrapping
+
+ %>
+ <BR>First package
+ <%= ntable("#cccccc") %>
+
+ <TR>
+ <TD COLSPAN=2>
+ <SELECT NAME="pkgpart_svcpart">
+ <OPTION VALUE="">(none)
+
+ <% foreach my $part_pkg ( @part_pkg ) { %>
+
+ <OPTION VALUE="<%= $part_pkg->pkgpart. "_". $part_pkg->svcpart('svc_acct') %>"<%= ( $saved_pkgpart && $part_pkg->pkgpart == $saved_pkgpart ) ? ' SELECTED' : '' %>><%= $part_pkg->pkg. " - ". $part_pkg->comment %>
+
+ <% } %>
+ </SELECT>
+ </TD>
+ </TR>
+
+ <%
+ #false laziness: (mostly) copied from edit/svc_acct.cgi
+ #$ulen = $svc_acct->dbdef_table->column('username')->length;
+ my $ulen = dbdef->table('svc_acct')->column('username')->length;
+ my $ulen2 = $ulen+2;
+ my $passwordmax = $conf->config('passwordmax') || 8;
+ my $pmax2 = $passwordmax + 2;
+ %>
+
+ <TR>
+ <TD ALIGN="right">Username</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="username" VALUE="<%= $username %>" SIZE=<%= $ulen2 %> MAXLENGTH=<%= $ulen %>>
+ </TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Password</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="_password" VALUE="<%= $password %>" SIZE=<%= $pmax2 %> MAXLENGTH=<%= $passwordmax %>>
+ (blank to generate)
+ </TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Access number</TD>
+ <TD><%= FS::svc_acct_pop::popselector($popnum) %></TD>
+ </TR>
+ </TABLE>
+
+ <% } %>
+
+<% } %>
-my $otaker = $cust_main->otaker;
-print qq!<INPUT TYPE="hidden" NAME="otaker" VALUE="$otaker">!,
- qq!<BR><INPUT TYPE="submit" NAME="submit" VALUE="!,
- $custnum ? "Apply Changes" : "Add Customer", qq!"><BR>!,
- "</FORM></DIV></BODY></HTML>",
-;
+<INPUT TYPE="hidden" NAME="otaker" VALUE="<%= $cust_main->otaker %>">
+<BR>
+<INPUT TYPE="submit" NAME="submit" VALUE="<%= $custnum ? "Apply Changes" : "Add Customer" %>">
+<BR>
+</FORM>
+
+<%= include('/elements/footer.html') %>
-%>
diff --git a/httemplate/edit/cust_pkg.cgi b/httemplate/edit/cust_pkg.cgi
index ce1c86612..174d4dde1 100755
--- a/httemplate/edit/cust_pkg.cgi
+++ b/httemplate/edit/cust_pkg.cgi
@@ -1,4 +1,3 @@
-<!-- mason kludge -->
<%
my %pkg = ();
@@ -29,48 +28,62 @@ if ( $cgi->param('error') ) {
}
my $p1 = popurl(1);
-print header("Add/Edit Packages", '');
-print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
- "</FONT>"
- if $cgi->param('error');
+%><%= include('/elements/header.html', "Add/Edit Packages", '') %>
-print qq!<FORM ACTION="${p1}process/cust_pkg.cgi" METHOD=POST>!;
+<% if ( $cgi->param('error') ) { %>
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <%= $cgi->param('error') %></FONT>
+<% } %>
-print qq!<INPUT TYPE="hidden" NAME="custnum" VALUE="$custnum">!;
+<FORM ACTION="<%= $p1 %>process/cust_pkg.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<%= $custnum %>">
+
+<%
#current packages
-my @cust_pkg = qsearch('cust_pkg',{ 'custnum' => $custnum, 'cancel' => '' } );
+my @cust_pkg = qsearch('cust_pkg', { 'custnum' => $custnum, 'cancel' => '' } );
if (@cust_pkg) {
- print <<END;
-Current packages - select to remove (services are moved to a new package below)
-<TABLE>
- <TR STYLE="background-color: #cccccc;">
- <TH COLSPAN="2">Pkg #</TH>
- <TH>Package description</TH>
- </TR>
-<BR><BR>
-END
+%>
+
+ Current packages - select to remove (services are moved to a new package below)
+ <TABLE>
+ <TR STYLE="background-color: #cccccc;">
+ <TH COLSPAN="2">Pkg #</TH>
+ <TH>Package description</TH>
+ </TR>
+ <BR><BR>
- foreach (sort { $all_pkg{$a->getfield('pkgpart')} cmp $all_pkg{$b->getfield('pkgpart')} } @cust_pkg) {
+ <%
+
+ foreach ( sort { $all_pkg{ $a->getfield('pkgpart') }
+ cmp $all_pkg{ $b->getfield('pkgpart') }
+ }
+ @cust_pkg
+ )
+ {
my($pkgnum,$pkgpart)=( $_->getfield('pkgnum'), $_->getfield('pkgpart') );
my $checked = $remove_pkg{$pkgnum} ? ' CHECKED' : '';
- print <<END;
- <TR>
- <TD><INPUT TYPE="checkbox" NAME="remove_pkg" VALUE="$pkgnum"${checked}></TD>
- <TD ALIGN="right">$pkgnum:</TD>\n
- <TD>$all_pkg{$pkgpart} - $all_comment{$pkgpart}</TD>
- </TR>
-END
- }
- print qq!</TABLE><BR><BR>!;
-}
-print <<END;
-Order new packages<BR><BR>
-END
+ %>
+
+ <TR>
+ <TD><INPUT TYPE="checkbox" NAME="remove_pkg" VALUE="<%= $pkgnum %>"<%= $checked %>></TD>
+ <TD ALIGN="right"><%= $pkgnum %>:</TD>
+ <TD><%= $all_pkg{$pkgpart} %> - <%= $all_comment{$pkgpart} %></TD>
+ </TR>
+
+ <% } %>
+
+ </TABLE>
+ <BR><BR>
+<% } %>
+
+Order new packages
+<BR><BR>
+
+<%
my $cust_main = qsearchs('cust_main',{'custnum'=>$custnum});
my $agent = qsearchs('agent',{'agentnum'=> $cust_main->agentnum });
@@ -79,13 +92,15 @@ my %agent_pkgs = map { ( $_->pkgpart , $all_pkg{$_->pkgpart} ) }
my $count = 0;
my $pkgparts = 0;
-print <<END;
+%>
+
<TABLE>
<TR STYLE="background-color: #cccccc;">
<TH>Qty.</TH>
<TH COLSPAN="2">Package Description</TH>
</TR>
-END
+
+<%
#foreach my $type_pkgs ( qsearch('type_pkgs',{'typenum'=> $agent->typenum }) ) {
foreach my $pkgpart ( sort { $agent_pkgs{$a} cmp $agent_pkgs{$b} }
keys(%agent_pkgs) ) {
@@ -93,38 +108,43 @@ foreach my $pkgpart ( sort { $agent_pkgs{$a} cmp $agent_pkgs{$b} }
next unless exists $pkg{$pkgpart}; #skip disabled ones
#print qq!<TR>! if ( $count == 0 );
my $value = $cgi->param("pkg$pkgpart") || 0;
- print <<END;
+%>
+
<TR>
- <TD><INPUT TYPE="text" NAME="pkg$pkgpart" VALUE="$value" SIZE="2" MAXLENGTH="2"></TD>
- <TD ALIGN="right">$pkgpart:</TD>
- <TD>$pkg{$pkgpart} - $comment{$pkgpart}</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="<%= "pkg$pkgpart" %>" VALUE="<%= $value %>" SIZE="2" MAXLENGTH="2">
+ </TD>
+ <TD ALIGN="right"><%= $pkgpart %>:</TD>
+ <TD><%= $pkg{$pkgpart} %> - <%= $comment{$pkgpart}%></TD>
</TR>
-END
+
+<%
$count ++ ;
#if ( $count == 2 ) {
# print qq!</TR>\n! ;
# $count = 0;
#}
}
-print qq!</TABLE>!;
-
-unless ( $pkgparts ) {
- my $p2 = popurl(2);
- my $typenum = $agent->typenum;
- my $agent_type = qsearchs( 'agent_type', { 'typenum' => $typenum } );
- my $atype = $agent_type->atype;
- print <<END;
-(No <a href="${p2}browse/part_pkg.cgi">package definitions</a>, or agent type
-<a href="${p2}edit/agent_type.cgi?$typenum">$atype</a> not allowed to purchase
-any packages.)
-END
-}
+%>
-#submit
-print <<END;
-<P><INPUT TYPE="submit" VALUE="Order">
- </FORM>
- </BODY>
-</HTML>
-END
+</TABLE>
+
+<% unless ( $pkgparts ) {
+ my $p2 = popurl(2);
+ my $typenum = $agent->typenum;
+ my $agent_type = qsearchs( 'agent_type', { 'typenum' => $typenum } );
+ my $atype = $agent_type->atype;
%>
+
+ (No <A HREF="<%= $p2 %>browse/part_pkg.cgi">package definitions</A>,
+ or agent type
+ <A HREF="<%= $p2 %>edit/agent_type.cgi?<%= $typenum %>"><%= $atype %></a>
+ is not allowed to purchase any packages.)
+
+<% } %>
+
+<P><INPUT TYPE="submit" VALUE="Order">
+
+</FORM>
+
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html
index 5486b4b00..120c03a3c 100644
--- a/httemplate/edit/elements/edit.html
+++ b/httemplate/edit/elements/edit.html
@@ -17,6 +17,13 @@
# 'menubar' => '', #menubar arrayref
#
# 'viewall_dir' => '', #'search' or 'browse', defaults to 'search'
+ #
+ # 'html_bottom' => '', #string
+ # 'html_bottom' => sub {
+ # my $object = shift;
+ # # ...
+ # "html_string";
+ # },
my(%opt) = @_;
@@ -27,6 +34,7 @@
my $fields = $opt{'fields'}
#|| [ grep { $_ ne $pkey } dbdef->table($table)->columns ];
|| [ grep { $_ ne $pkey } fields($table) ];
+ #my @actualfields = map { ref($_) ? $_->{'field'} : $_ } @$fields;
my $object;
if ( $cgi->param('error') ) {
@@ -63,10 +71,7 @@
);
}
-%>
-
-
-<%= include("/elements/header.html", $title,
+%><%= include("/elements/header.html", $title,
include( '/elements/menubar.html', @menubar )
)
%>
@@ -86,7 +91,18 @@
<%= ntable("#cccccc",2) %>
-<% foreach my $field ( @$fields ) { %>
+<% foreach my $f ( @$fields ) {
+
+ my( $field, $type);
+ if ( ref($f) ) {
+ $field = $f->{'field'},
+ $type = $f->{'type'} || 'text',
+ } else {
+ $field = $f;
+ $type = 'text';
+ }
+
+%>
<TR>
@@ -98,12 +114,11 @@
</TD>
<%
- #just text in one size for now... eventually more options for
- # uneditable, hidden, <SELECT>, etc. fields
+ #eventually more options for <SELECT>, etc. fields
%>
<TD>
- <INPUT TYPE="text" NAME="<%= $field %>" VALUE="<%= $object->$field() %>">
+ <INPUT TYPE="<%= $type %>" NAME="<%= $field %>" VALUE="<%= $object->$field() %>">
<TD>
</TR>
@@ -112,6 +127,11 @@
</TABLE>
+<%= ref( $opt{'html_bottom'} )
+ ? &{ $opt{'html_bottom'} }( $object )
+ : $opt{'html_bottom'}
+%>
+
<BR>
<INPUT TYPE="submit" VALUE="<%= $object->$pkey() ? "Apply changes" : "Add $opt{'name'}" %>">
diff --git a/httemplate/edit/part_referral.cgi b/httemplate/edit/part_referral.cgi
index f784dfa3e..dce1e6394 100755
--- a/httemplate/edit/part_referral.cgi
+++ b/httemplate/edit/part_referral.cgi
@@ -1,4 +1,3 @@
-<!-- mason kludge -->
<%
my $part_referral;
@@ -17,32 +16,29 @@ my $action = $part_referral->refnum ? 'Edit' : 'Add';
my $hashref = $part_referral->hashref;
my $p1 = popurl(1);
-print header("$action Advertising source", menubar(
+
+%><%= include('/elements/header.html', "$action Advertising source", menubar(
'Main Menu' => popurl(2),
'View all advertising sources' => popurl(2). "browse/part_referral.cgi",
-));
+)) %>
+
+<% if ( $cgi->param('error') ) { %>
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <%= $cgi->param('error') %></FONT>
+<% } %>
-print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
- "</FONT>"
- if $cgi->param('error');
+<FORM ACTION="<%= $p1 %>process/part_referral.cgi" METHOD=POST>
-print qq!<FORM ACTION="${p1}process/part_referral.cgi" METHOD=POST>!;
+<INPUT TYPE="hidden" NAME="refnum" VALUE="<%= $hashref->{refnum} %>">
-print qq!<INPUT TYPE="hidden" NAME="refnum" VALUE="$hashref->{refnum}">!;
+<%
#print "Referral #", $hashref->{refnum} ? $hashref->{refnum} : "(NEW)";
+%>
-print <<END;
-Advertising source <INPUT TYPE="text" NAME="referral" SIZE=32 VALUE="$hashref->{referral}">
-END
+Advertising source <INPUT TYPE="text" NAME="referral" SIZE=32 VALUE="<%= $hashref->{referral} %>">
-print qq!<BR><INPUT TYPE="submit" VALUE="!,
- $hashref->{refnum} ? "Apply changes" : "Add advertising source",
- qq!">!;
+<BR>
+<INPUT TYPE="submit" VALUE="<%= $hashref->{refnum} ? "Apply changes" : "Add advertising source" %>">
-print <<END;
- </FORM>
- </BODY>
-</HTML>
-END
+</FORM>
-%>
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/edit/part_virtual_field.cgi b/httemplate/edit/part_virtual_field.cgi
index fb10321e8..7b2c768a7 100644
--- a/httemplate/edit/part_virtual_field.cgi
+++ b/httemplate/edit/part_virtual_field.cgi
@@ -1,4 +1,3 @@
-<!-- mason kludge -->
<%
my ($vfieldpart, $part_virtual_field);
@@ -21,12 +20,14 @@ if ( $cgi->param('error') ) {
my $action = $part_virtual_field->vfieldpart ? 'Edit' : 'Add';
my $p1 = popurl(1);
-print header("$action Virtual Field Definition", '');
-print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
- "</FONT>"
- if $cgi->param('error');
-%>
+%><%= include('/elements/header.html', "$action Virtual Field Definition") %>
+
+<% if ( $cgi->param('error') ) { %>
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <%= $cgi->param('error') %></FONT>
+ <BR><BR>
+<% } %>
+
<FORM ACTION="<%=$p1%>process/generic.cgi" METHOD="POST">
<INPUT TYPE="hidden" NAME="table" VALUE="part_virtual_field">
@@ -83,10 +84,8 @@ Field #<B><%=$vfieldpart or "(NEW)"%></B><BR><BR>
</FORM>
-<BR><BR>
+<BR>
<FONT SIZE=-2>If you don't understand what <I>check_block</I> and
<I>list_source</I> mean, <B>LEAVE THEM BLANK</B>. We mean it.</FONT>
-
-</BODY>
-</HTML>
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/edit/process/access_group.html b/httemplate/edit/process/access_group.html
new file mode 100644
index 000000000..e8c6d07b1
--- /dev/null
+++ b/httemplate/edit/process/access_group.html
@@ -0,0 +1,5 @@
+<%= include( 'elements/process.html',
+ 'table' => 'access_group',
+ 'viewall_dir' => 'browse',
+ )
+%>
diff --git a/httemplate/edit/process/access_user.html b/httemplate/edit/process/access_user.html
new file mode 100644
index 000000000..a6c2a36b1
--- /dev/null
+++ b/httemplate/edit/process/access_user.html
@@ -0,0 +1,8 @@
+<%= include( 'elements/process.html',
+ 'table' => 'access_user',
+ 'viewall_dir' => 'browse',
+ 'process_m2m' => { 'link_table' => 'access_usergroup',
+ 'target_table' => 'access_group',
+ },
+ )
+%>
diff --git a/httemplate/edit/process/agent_type.cgi b/httemplate/edit/process/agent_type.cgi
index 516594573..fd8ca8833 100755
--- a/httemplate/edit/process/agent_type.cgi
+++ b/httemplate/edit/process/agent_type.cgi
@@ -11,43 +11,24 @@ my $new = new FS::agent_type ( {
my $error;
if ( $typenum ) {
- $error=$new->replace($old);
+ $error = $new->replace($old);
} else {
- $error=$new->insert;
- $typenum=$new->getfield('typenum');
+ $error = $new->insert;
+ $typenum = $new->getfield('typenum');
}
+#$error ||= $new->process_m2m( );
if ( $error ) {
$cgi->param('error', $error);
print $cgi->redirect(popurl(2). "agent_type.cgi?". $cgi->query_string );
} else {
- #false laziness w/ edit/process/part_svc.cgi
- foreach my $part_pkg (qsearch('part_pkg',{})) {
- my($pkgpart)=$part_pkg->getfield('pkgpart');
-
- my($type_pkgs)=qsearchs('type_pkgs',{
- 'typenum' => $typenum,
- 'pkgpart' => $pkgpart,
- });
- if ( $type_pkgs && ! $cgi->param("pkgpart$pkgpart") ) {
- my($d_type_pkgs)=$type_pkgs; #need to save $type_pkgs for below.
- $error=$d_type_pkgs->delete;
- die $error if $error;
-
- } elsif ( $cgi->param("pkgpart$pkgpart")
- && ! $type_pkgs
- ) {
- #ok to clobber it now (but bad form nonetheless?)
- $type_pkgs=new FS::type_pkgs ({
- 'typenum' => $typenum,
- 'pkgpart' => $pkgpart,
- });
- $error= $type_pkgs->insert;
- die $error if $error;
- }
-
- }
+ my $error = $new->process_m2m(
+ 'link_table' => 'type_pkgs',
+ 'target_table' => 'part_pkg',
+ 'params' => scalar($cgi->Vars)
+ );
+ die $error if $error;
print $cgi->redirect(popurl(3). "browse/agent_type.cgi");
}
diff --git a/httemplate/edit/process/cust_bill_pay.cgi b/httemplate/edit/process/cust_bill_pay.cgi
index 0025b16b5..fc668bb07 100755
--- a/httemplate/edit/process/cust_bill_pay.cgi
+++ b/httemplate/edit/process/cust_bill_pay.cgi
@@ -33,11 +33,19 @@ if ($cgi->param('invnum') =~ /^Refund$/) {
my $error = $new->insert;
if ( $error ) {
+
$cgi->param('error', $error);
- print $cgi->redirect(popurl(2). "cust_bill_pay.cgi?". $cgi->query_string );
+ %><%= $cgi->redirect(popurl(2). "cust_bill_pay.cgi?". $cgi->query_string ) %><%
+
} else {
- print $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum");
-}
+ #print $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum");
+
+ %><%= header('Payment application sucessful') %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+
+ </BODY></HTML>
-%>
+<% } %>
diff --git a/httemplate/edit/process/cust_credit.cgi b/httemplate/edit/process/cust_credit.cgi
index 85bfd4489..6a4ef194a 100755
--- a/httemplate/edit/process/cust_credit.cgi
+++ b/httemplate/edit/process/cust_credit.cgi
@@ -13,14 +13,23 @@ my $error = $new->insert;
if ( $error ) {
$cgi->param('error', $error);
- print $cgi->redirect(popurl(2). "cust_credit.cgi?". $cgi->query_string );
+
+ %><%= $cgi->redirect(popurl(2). "cust_credit.cgi?". $cgi->query_string ) %><%
+
} else {
+
if ( $cgi->param('apply') eq 'yes' ) {
my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum })
or die "unknown custnum $custnum";
$cust_main->apply_credits;
}
- print $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum");
-}
+ #print $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum");
+
+ %><%= header('Credit sucessful') %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+
+ </BODY></HTML>
-%>
+<% } %>
diff --git a/httemplate/edit/process/cust_credit_bill.cgi b/httemplate/edit/process/cust_credit_bill.cgi
index 28f892f62..3b759536f 100755
--- a/httemplate/edit/process/cust_credit_bill.cgi
+++ b/httemplate/edit/process/cust_credit_bill.cgi
@@ -34,11 +34,19 @@ if ($cgi->param('invnum') =~ /^Refund$/) {
my $error = $new->insert;
if ( $error ) {
+
$cgi->param('error', $error);
- print $cgi->redirect(popurl(2). "cust_credit_bill.cgi?". $cgi->query_string );
+ %><%= $cgi->redirect(popurl(2). "cust_credit_bill.cgi?". $cgi->query_string ) %><%
+
} else {
- print $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum");
-}
+ #print $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum");
+
+ %><%= header('Credit application sucessful') %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+
+ </BODY></HTML>
-%>
+<% } %>
diff --git a/httemplate/edit/process/elements/process.html b/httemplate/edit/process/elements/process.html
index 83ff6f728..59ad35ee4 100644
--- a/httemplate/edit/process/elements/process.html
+++ b/httemplate/edit/process/elements/process.html
@@ -2,10 +2,21 @@
# options example...
#
+ ###
+ ##req
+ ##
# 'table' =>
+ #
# #? 'primary_key' => #required when the dbdef doesn't know...???
# #? 'fields' => []
+ #
+ ###
+ ##opt
+ ###
# 'viewall_dir' => '', #'search' or 'browse', defaults to 'search'
+ # 'process_m2m' => { 'link_table' => 'link_table_name',
+ # 'target_table' => 'target_table_name',
+ # }.
my(%opt) = @_;
@@ -31,12 +42,16 @@
if ( $pkeyvalue ) {
$error = $new->replace($old);
} else {
- warn $new;
$error = $new->insert;
- warn $error;
$pkeyvalue = $new->getfield($pkey);
}
+ if ( !$error && $opt{'process_m2m'} ) {
+ $error = $new->process_m2m( %{ $opt{'process_m2m'} },
+ 'params' => scalar($cgi->Vars),
+ );
+ }
+
if ( $error ) {
$cgi->param('error', $error);
print $cgi->redirect(popurl(2). "$table.html?". $cgi->query_string );
diff --git a/httemplate/edit/svc_domain.cgi b/httemplate/edit/svc_domain.cgi
index ca0e3398f..f47ba0a8f 100755
--- a/httemplate/edit/svc_domain.cgi
+++ b/httemplate/edit/svc_domain.cgi
@@ -1,4 +1,3 @@
-<!-- mason kludge -->
<%
my($svcnum, $pkgnum, $svcpart, $kludge_action, $purpose, $part_svc,
@@ -66,33 +65,31 @@ my $otaker = getotaker;
my $domain = $svc_domain->domain;
my $p1 = popurl(1);
-print header("$action $svc", '');
-
-print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
- "</FONT>"
- if $cgi->param('error');
-
-print <<END;
- <FORM ACTION="${p1}process/svc_domain.cgi" METHOD=POST>
- <INPUT TYPE="hidden" NAME="svcnum" VALUE="$svcnum">
- <INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum">
- <INPUT TYPE="hidden" NAME="svcpart" VALUE="$svcpart">
-END
-
-print qq!<INPUT TYPE="radio" NAME="action" VALUE="N"!;
-print ' CHECKED' if $kludge_action eq 'N';
-print qq!>New!;
-print qq!<BR><INPUT TYPE="radio" NAME="action" VALUE="M"!;
-print ' CHECKED' if $kludge_action eq 'M';
-print qq!>Transfer!;
-
-print <<END;
-<P>Domain <INPUT TYPE="text" NAME="domain" VALUE="$domain" SIZE=28 MAXLENGTH=63>
-<BR>Purpose/Description: <INPUT TYPE="text" NAME="purpose" VALUE="$purpose" SIZE=64>
-<P><INPUT TYPE="submit" VALUE="Submit">
- </FORM>
- </BODY>
-</HTML>
-END
%>
+
+<%= include('/elements/header.html', "$action $svc", '') %>
+
+<% if ( $cgi->param('error') ) { %>
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <%= $cgi->param('error') %></FONT>
+<% } %>
+
+<FORM ACTION="<%= $p1 %>process/svc_domain.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="svcnum" VALUE="<%= $svcnum %>">
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<%= $pkgnum %>">
+<INPUT TYPE="hidden" NAME="svcpart" VALUE="<%= $svcpart %>">
+
+<INPUT TYPE="radio" NAME="action" VALUE="N"<%= $kludge_action eq 'N' ? ' CHECKED' : '' %>>New
+<BR>
+
+<INPUT TYPE="radio" NAME="action" VALUE="M"<%= $kludge_action eq 'M' ? ' CHECKED' : '' %>>Transfer
+
+<P>Domain <INPUT TYPE="text" NAME="domain" VALUE="<%= $domain %>" SIZE=28 MAXLENGTH=63>
+
+<BR>Purpose/Description: <INPUT TYPE="text" NAME="purpose" VALUE="<%= $purpose %>" SIZE=64>
+
+<P><INPUT TYPE="submit" VALUE="Submit">
+
+</FORM>
+
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/elements/checkboxes-table.html b/httemplate/elements/checkboxes-table.html
new file mode 100644
index 000000000..d26ebef35
--- /dev/null
+++ b/httemplate/elements/checkboxes-table.html
@@ -0,0 +1,110 @@
+<%
+
+ ##
+ # required
+ ##
+ # 'target_table' => 'table_name',
+ # 'link_table' => 'table_name',
+ #
+ # 'name_col' => 'name_column',
+ # #or
+ # 'name_callback' => sub { },
+ #
+ ##
+ # recommended (required?)
+ ##
+ # 'source_obj' => $obj,
+ # #or?
+ # #'source_table' => 'table_name',
+ # #'sourcenum' => '4', #current value of primary key in source_table
+ # # # (none is okay, just pass it if you have it)
+ ##
+ # optional
+ ##
+ # 'disable-able' => 1,
+
+ my( %opt ) = @_;
+
+ my $target_pkey = dbdef->table($opt{'target_table'})->primary_key;
+
+ my( $source_pkey, $sourcenum, $source_obj );
+ if ( $opt{'source_obj'} ) {
+
+ $source_obj = $opt{'source_obj'};
+ #$source_table = $source_obj->dbdef_table->table;
+ $source_pkey = $source_obj->dbdef_table->primary_key;
+ $sourcenum = $source_obj->$source_pkey();
+
+ } else {
+
+ #$source_obj?
+ $source_pkey = $opt{'source_table'}
+ ? dbdef->table($opt{'source_table'})->primary_key
+ : '';
+ $sourcenum = $opt{'sourcenum'};
+ }
+
+ my $hashref = $opt{'hashref'} || {};
+
+ my $extra_sql = '';
+
+ if ( $opt{'disable-able'} ) {
+ $hashref->{'disabled'} = '';
+
+ $extra_sql .= ( $sourcenum && $source_pkey )
+ ? "OR $source_pkey = $sourcenum"
+ : '';
+ }
+
+%>
+
+<% foreach my $target_obj (
+ qsearch({ 'table' => $opt{'target_table'},
+ 'hashref' => $hashref,
+ 'select' => $opt{'target_table'}. '.*',
+ 'addl_from' => "LEFT JOIN $opt{'link_table'} USING ( $target_pkey )",
+ 'extra_sql' => $extra_sql,
+ })
+ ) {
+
+ my $targetnum = $target_obj->$target_pkey();
+%>
+
+ <INPUT TYPE="checkbox" NAME="<%= $target_pkey. $targetnum %>" <%=
+ qsearchs( $opt{'link_table'}, {
+ $source_pkey => $sourcenum,
+ $target_pkey => $targetnum,
+ })
+ ? 'CHECKED '
+ : ''
+ %> VALUE="ON">
+
+ <% if ( $opt{'target_link'} ) { %>
+
+ <A HREF="<%= $opt{'target_link'} %><%= $targetnum %>"><%
+
+ }
+ %><%= $targetnum %>:
+
+ <% if ( $opt{'name_callback'} ) { %>
+
+ <%= &{ $opt{'name_callback'} }( $target_obj ) %><%= $opt{'target_link'} ? '</A>' : '' %>
+
+ <% } else {
+ my $name_col = $opt{'name_col'};
+ %>
+
+ <%= $target_obj->$name_col() %><%= $opt{'target_link'} ? '</A>' : '' %>
+
+ <% } %>
+
+ <% if ( $opt{'disable-able'} ) { %>
+
+ <%= $target_obj->disabled =~ /^Y/i ? ' (DISABLED)' : '' %>
+
+ <% } %>
+
+ <BR>
+
+<% } %>
+
diff --git a/httemplate/elements/cssexpr.js b/httemplate/elements/cssexpr.js
new file mode 100644
index 000000000..c434d8da0
--- /dev/null
+++ b/httemplate/elements/cssexpr.js
@@ -0,0 +1,66 @@
+function constExpression(x) {
+ return x;
+}
+
+function simplifyCSSExpression() {
+ try {
+ var ss,sl, rs, rl;
+ ss = document.styleSheets;
+ sl = ss.length
+
+ for (var i = 0; i < sl; i++) {
+ simplifyCSSBlock(ss[i]);
+ }
+ }
+ catch (exc) {
+ //alert("Got an error while processing css. The page should still work but might be a bit slower");
+ throw exc;
+ }
+}
+
+function simplifyCSSBlock(ss) {
+ var rs, rl;
+
+ for (var i = 0; i < ss.imports.length; i++)
+ simplifyCSSBlock(ss.imports[i]);
+
+ if (ss.cssText.indexOf("expression(constExpression(") == -1)
+ return;
+
+ rs = ss.rules;
+ rl = rs.length;
+ for (var j = 0; j < rl; j++)
+ simplifyCSSRule(rs[j]);
+
+}
+
+function simplifyCSSRule(r) {
+ var str = r.style.cssText;
+ var str2 = str;
+ var lastStr;
+ do {
+ lastStr = str2;
+ str2 = simplifyCSSRuleHelper(lastStr);
+ } while (str2 != lastStr)
+
+ if (str2 != str)
+ r.style.cssText = str2;
+}
+
+function simplifyCSSRuleHelper(str) {
+ var i, i2;
+ i = str.indexOf("expression(constExpression(");
+ if (i == -1) return str;
+ i2 = str.indexOf("))", i);
+ var hd = str.substring(0, i);
+ var tl = str.substring(i2 + 2);
+ var exp = str.substring(i + 27, i2);
+ var val = eval(exp)
+ return hd + val + tl;
+}
+
+if (/msie/i.test(navigator.userAgent) && window.attachEvent != null) {
+ window.attachEvent("onload", function () {
+ simplifyCSSExpression();
+ });
+}
diff --git a/httemplate/elements/footer.html b/httemplate/elements/footer.html
index 6029d7637..32d121996 100644
--- a/httemplate/elements/footer.html
+++ b/httemplate/elements/footer.html
@@ -1,2 +1,5 @@
+ </TD>
+ </TR>
+ </TABLE>
</BODY>
</HTML>
diff --git a/httemplate/elements/header.html b/httemplate/elements/header.html
index 10e4e40f1..49814577e 100644
--- a/httemplate/elements/header.html
+++ b/httemplate/elements/header.html
@@ -2,20 +2,285 @@
my($title, $menubar) = ( shift, shift );
my $etc = @_ ? shift : ''; #$etc is for things like onLoad= etc.
my $head = @_ ? shift : ''; #$head is for things that go in the <HEAD> section
+ my $conf = new FS::Conf;
%>
- <HTML>
- <HEAD>
- <TITLE>
- <%= $title %>
- </TITLE>
- <META HTTP-Equiv="Cache-Control" Content="no-cache">
- <META HTTP-Equiv="Pragma" Content="no-cache">
- <META HTTP-Equiv="Expires" Content="0">
- <%= $head %>
- </HEAD>
- <BODY BGCOLOR="#e8e8e8"<%= $etc %>>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+ <HEAD>
+ <TITLE>
+ <%= $title %>
+ </TITLE>
+ <META HTTP-Equiv="Cache-Control" Content="no-cache">
+ <META HTTP-Equiv="Pragma" Content="no-cache">
+ <META HTTP-Equiv="Expires" Content="0">
+ <script type="text/javascript" src="<%=$fsurl%>elements/cssexpr.js"></script>
+ <script type="text/javascript" src="<%=$fsurl%>elements/xmenu.js"></script>
+ <link href="<%=$fsurl%>elements/xmenu.css" type="text/css" rel="stylesheet">
+ <%
+
+ tie my %report_menu, 'Tie::IxHash',
+ 'Report one' => [ 'there', 'theretip' ],
+ 'Report too' => [ 'here', 'heretip' ],
+ ;
+
+ tie my %config_employees, 'Tie::IxHash',
+ 'View/Edit employees' => [ $fsurl.'browse/access_user.html', 'Setup internal users' ],
+ 'View/Edit employee groups' => [ $fsurl.'browse/access_group.html', 'Employee groups allow you to control access to the backend' ],
+ ;
+
+ tie my %config_export_svc_pkg, 'Tie::IxHash',
+ 'View/Edit exports' => [ $fsurl.'browse/part_export.cgi', 'Provisioning services to external machines, databases and APIs' ],
+ 'View/Edit service definitions' => [ $fsurl.'browse/part_svc.cgi', 'Services are items you offer to your customers' ],
+ 'View/Edit package definitions' => [ $fsurl.'browse/part_pkg.cgi', 'One or more services are grouped together into a package and given pricing information. Customers purchase packages, not services' ],
+ 'View/Edit package classes' => [ $fsurl.'browse/pkg_class.html', 'Package classes define groups of packages, for reporting and convenience purposes.' ],
+ ;
+
+ tie my %config_agent, 'Tie::IxHash',
+ 'View/Edit agent types' => [ $fsurl.'browse/agent_type.cgi', 'Agent types define groups of package definitions that you can then assign to particular agents' ],
+ 'View/Edit agents' => [ $fsurl.'browse/agent.cgi', 'Agents are resellers of your service. Agents may be limited to a subset of your full offerings (via their type)' ],
+ ;
+
+ tie my %config_billing, 'Tie::IxHash',
+ 'View/Edit payment gateways' => [ $fsurl.'browse/payment_gateway.html', 'Credit card and electronic check processors' ],
+ 'View/Edit invoice events' => [ $fsurl.'browse/part_bill_event.cgi', 'Actions for overdue invoices' ],
+ 'View/Edit prepaid cards' => [ $fsurl.'browse/prepay_credit.html', 'View outstanding cards, generate new cards' ],
+ 'View/Edit call rates and regions' => [ $fsurl.'browse/rate.cgi', 'Manage rate plans, regions and prefixes for VoIP and call billing' ],
+ 'View/Edit locales and tax rates' => [ $fsurl.'browse/cust_main_county.cgi', 'Change tax rates, or break down a country into states, or a state into counties and assign different tax rates to each' ],
+ ;
+
+ tie my %config_dialup, 'Tie::IxHash',
+ 'View/Edit access numbers' => [ $fsurl.'browse/svc_acct_pop.cgi', 'Points of Presence' ],
+ ;
+
+ tie my %config_broadband, 'Tie::IxHash',
+ 'View/Edit routers' => [ $fsurl.'browse/router.cgi', 'Broadband access routers' ],
+ 'View/Edit address blocks' => [ $fsurl.'browse/addr_block.cgi', 'Manage address blocks and block assignments to broadband routers' ],
+ ;
+
+ tie my %config_misc, 'Tie::IxHash',
+ 'View/Edit advertising sources' => [ $fsurl.'browse/part_referral.cgi', 'Where a customer heard about your service. Tracked for informational purposes' ],
+ 'View/Edit virtual fields' => [ $fsurl.'browse/part_virtual_field.cgi', 'Locally defined fields', ],
+ 'View/Edit message catalog' => [ $fsurl.'browse/msgcat.cgi', 'Change error messages and other customizable labels' ],
+ 'View/Edit inventory classes and inventory' => [ $fsurl.'browse/inventory_class.html', 'Setup inventory classes and stock inventory' ],
+ ;
+
+ tie my %config_menu, 'Tie::IxHash',
+ 'Settings' => [ $fsurl.'config/config-view.cgi', 'XXXconfigittip' ],
+ 'separator' => '', #its a separator!
+ 'Employees' => [ \%config_employees, 'XXXtooltip' ],
+ 'Provisioning, services and packages'
+ => [ \%config_export_svc_pkg, 'XXXtootip' ],
+ 'Resellers' => [ \%config_agent, 'XXXtootip' ],
+ 'Billing' => [ \%config_billing, 'XXXtootip' ],
+ 'Dialup' => [ \%config_dialup, 'XXXtootip' ],
+ 'Fixed (username-less) broadband'
+ => [ \%config_broadband, 'XXXtootip' ],
+ 'Miscellaneous' => [ \%config_misc, 'XXXtootip' ],
+ ;
+
+ tie my %menu, 'Tie::IxHash',
+ 'Home' => [ $fsurl, 'hometip', ],
+ 'Top item one' => [ 'nowhere_yet', 'nowheretip', ],
+ 'Top item too' => [ 'nowhere_yet_either', 'eithertip', ],
+ 'Reports' => [ \%report_menu, 'reportmenutip' ],
+ 'Configuration' => [ \%config_menu, 'configmenutip' ],
+ ;
+
+ use vars qw($gmenunum);
+ $gmenunum = 0;
+
+ sub submenu {
+ my($submenu, $title) = @_;
+ my $menunum = $gmenunum++;
+
+ #return two args: html, menuname
+
+ "var myMenu$menunum = new WebFXMenu;\n".
+ #"myMenu$menunum.useAutoPosition = true;\n".
+ "myMenu$menunum.emptyText = '$title';\n".
+
+ (
+ join("\n", map {
+
+ if ( !ref( $submenu->{$_} ) ) {
+
+ "myMenu$menunum.add(new WebFXMenuSeparator());";
+
+ } else {
+
+ my($url_or_submenu, $tooltip ) = @{ $submenu->{$_} };
+ if ( ref($url_or_submenu) ) {
+
+ my($subhtml, $submenuname ) = submenu($url_or_submenu, $_); #mmm, recursion
+
+ "$subhtml\n".
+ "myMenu$menunum.add(new WebFXMenuItem(\"$_\", null, \"$tooltip\", $submenuname ));";
+
+ } else {
+
+ "myMenu$menunum.add(new WebFXMenuItem(\"$_\", \"$url_or_submenu\", \"$tooltip\" ));";
+
+ }
+
+ }
+
+ } keys %$submenu )
+ ). "\n".
+ "myMenu$menunum.width = 224\n",
+
+ "myMenu$menunum";
+
+ }
+
+ %>
+ <SCRIPT TYPE="text/javascript">
+
+ webfxMenuImagePath = "<%=$fsurl%>images/";
+ webfxMenuUseHover = 1;
+ webfxMenuShowTime = 300;
+ webfxMenuHideTime = 500;
+
+ var myBar = new WebFXMenuBar;
+
+ <% foreach my $item ( keys %menu ) {
+
+ my( $url_or_submenu, $tooltip ) = @{ $menu{$item} };
+
+ if ( ref($url_or_submenu) ) {
+
+ warn $item;
+
+ my( $subhtml, $submenuname ) = submenu($url_or_submenu, $item);
+
+ %>
+
+ <%= $subhtml %>
+ myBar.add(new WebFXMenuButton("<%= $item %>", null, "<%= $tooltip %>", <%= $submenuname %> ));
+
+ <% } else { %>
+
+ myBar.add(new WebFXMenuButton("<%= $item %>", "<%= $url_or_submenu %>", "<%= $tooltip %>" ));
+
+ <% }
+
+ }
+ %>
+
+ myBar.show( null, 'vertical' );
+ //myBar.show( null, 'horizontal' );
+
+ //var myMenu = new WebFXMenu;
+ //myMenu.add(new WebFXMenuItem("Menu Item 1", "http://www.domain.com", "Tool tip to show"));
+ //myMenu.add(new WebFXMenuSeparator());
+ //myMenu.add(new WebFXMenuItem("Menu Item 2", "http://www.domain.com", "Tool tip to show"));
+
+ //var mySubMenu = new WebFXMenu;
+ //mySubMenu.add(new WebFXMenuItem("Menu Item 3", "http://www.domain.com", "Tool tip to show"));
+ //myMenu.add(new WebFXMenuItem("Menu Item 4 with sub menu", null, "Tool tip to show", mySubMenu));
+
+ myBar.width = 154;
+
+ </SCRIPT>
+
+ <SCRIPT TYPE="text/javascript">
+ function clearhint_search_cust () {
+ alert(this);
+ if ( this.value='(cust #, name or company)' )
+ this.value = '';
+ }
+ </SCRIPT>
+
+ <%= $head %>
+
+ </HEAD>
+ <BODY BACKGROUND="<%=$fsurl%>images/background-cheat.png" <%= $etc %> STYLE="margin-top:0; margin-bottom:0; margin-left:0; margin-right:0">
+ <table width="100%" CELLPADDING=0 CELLSPACING=0 STYLE="padding-left:0; padding-right:4">
+ <tr>
+ <td rowspan=2 BGCOLOR="#ffffff">
+ <IMG BORDER=0 ALT="freeside" SRC="<%=$fsurl%>images/small-logo.png">
+ </td>
+ <td align=left rowspan=2 BGCOLOR="#ffffff"> <!-- valign="top" -->
+ <font size=6><%= $conf->config('company_name') %> Billing</font>
+ </td>
+ <td align=right valign=top BGCOLOR="#ffffff">Logged in as <b><%= getotaker %>&nbsp</b><br><FONT SIZE="-2"><a href="<%=$fsurl%>pref/XXXwritethis">Preferences</a>&nbsp;<BR><BR></FONT>
+ </td>
+ </tr>
+ <tr>
+ <td align=right valign=bottom BGCOLOR="#ffffff">
+
+ <table>
+ <tr>
+ <td align=right BGCOLOR="#ffffff">
+ <FONT SIZE="-2">
+ <A HREF="http://www.sisd.com/freeside">Freeside</A>&nbsp;v<%= $FS::VERSION %><BR>
+ <A HREF="<%= $fsurl %>docs/">Documentation</A><BR>
+ </FONT>
+ </td>
+ <% if ( $conf->config('ticket_system') eq 'RT_Internal' ) { %>
+ <% eval "use RT;"; %>
+ <td bgcolor=#000000></td>
+ <td align=left>
+ <FONT SIZE="-2">
+ <A HREF="http://www.bestpractical.com/rt">RT<A>&nbsp;v<%= $RT::VERSION %><BR>
+ <A HREF="http://wiki.bestpractical.com/">Documentation</A><BR>
+ </FONT>
+ </td>
+ <% } %>
+
+ </tr>
+ </table>
+
+ </td>
+ </tr>
+ </table>
+
+ <TABLE WIDTH="100%" CELLSPACING=0 CELLPADDING=4>
+ <TR>
+ <TD COLSPAN=4 WIDTH="100%" STYLE="padding:0"><IMG BORDER=0 ALT="" SRC="<%=$fsurl%>images/black-gradient.png" HEIGHT="13" WIDTH="100%"></TD>
+ </TR>
+ <TR>
+ <TD COLSPAN=1 BGCOLOR="#000000" WIDTH="154">
+ </TD>
+ <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
+ <FORM ACTION="<%=$fsurl%>edit/cust_main.cgi" METHOD="GET" STYLE="margin:0">
+ <INPUT TYPE="submit" VALUE="New customer">
+ </FORM>
+ </TD>
+ <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
+ <FORM ACTION="<%=$fsurl%>search/cust_main.cgi" METHOD="GET" STYLE="margin:0">
+ <INPUT NAME="search_cust" TYPE="text" VALUE="(cust #, name or company)" SIZE="23" onFocus="clearhint_search_cust" onClick="clearhint_search_cust">
+ <INPUT TYPE="submit" VALUE="Search customers">
+ </FORM>
+ </TD>
+ <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
+ <FORM ACTION="<%=$fsurl%>rt/index.html" METHOD="GET" STYLE="margin:0">
+ <INPUT NAME="q" TYPE="text" VALUE="" onFocus="clearhint_search_ticket" onClick="clearhint_search_ticket">
+ <INPUT TYPE="submit" VALUE="Search tickets">
+ </FORM>
+ </TD>
+ </TR>
+ </TABLE>
+ <TABLE WIDTH="100%" HEIGHT="100%" CELLSPACING=0 CELLPADDING=4>
+ <TR>
+ <TD BGCOLOR="#000000" STYLE="padding:0" WIDTH="154"></TD>
+ <TD STYLE="padding:0" WIDTH="13"><IMG BORDER=0 ALT="" SRC="<%=$fsurl%>images/black-gray-corner.png"></TD>
+ <TD STYLE="padding:0"><IMG BORDER=0 ALT="" SRC="<%=$fsurl%>images/black-gray-top.png" HEIGHT="13" WIDTH="100%"></TD>
+ </TR>
+ <TR HEIGHT="100%">
+ <TD BGCOLOR="#000000" ALIGN="left" HEIGHT="100%" WIDTH="154" VALIGN="top" ALIGN="right">
+ <SCRIPT TYPE="text/javascript">
+ document.write(myBar);
+ </SCRIPT>
+ <BR>
+ <IMG SRC="<%=$fsurl%>images/32clear.gif" HEIGHT="1" WIDTH="154">
+
+ </TD>
+ <TD STYLE="padding:0" HEIGHT="100%" WIDTH=13 VALIGN="top"><IMG WIDTH="13" HEIGHT="100%" BORDER=0 ALT="" SRC="<%=$fsurl%>images/black-gray-side.png"></TD>
+ <TD BGCOLOR="#e8e8e8" HEIGHT="100%"> <!-- WIDTH="100%"> -->
+
<FONT SIZE=6>
<%= $title %>
</FONT>
+
<BR><BR>
- <%= $menubar ? "$menubar<BR><BR>" : '' %>
+ <%= $menubar !~ /^\s*$/ ? "$menubar<BR><BR>" : '' %>
diff --git a/httemplate/elements/menubar.html b/httemplate/elements/menubar.html
index 87a50312c..29facb6b6 100644
--- a/httemplate/elements/menubar.html
+++ b/httemplate/elements/menubar.html
@@ -2,6 +2,7 @@
my($item, $url, @html);
while (@_) {
($item, $url) = splice(@_,0,2);
+ next if $item =~ /^\s*Main\s+Menu\s*$/i;
push @html, qq!<A HREF="$url">$item</A>!;
}
%>
diff --git a/httemplate/elements/select-access_group.html b/httemplate/elements/select-access_group.html
new file mode 100644
index 000000000..b05f565ea
--- /dev/null
+++ b/httemplate/elements/select-access_group.html
@@ -0,0 +1,15 @@
+<%
+ my( $groupnum, %opt ) = @_;
+
+ %opt{'records'} = delete $opt{'access_group'}
+ if $opt{'access_group'};
+
+%><%= include( '/elements/select-table.html',
+ 'table' => 'access_group',
+ 'name_col' => 'groupname',
+ 'value' => $groupnum,
+ 'empty_label' => '(none)',
+ #'hashref' => { 'disabled' => '' },
+ %opt,
+ )
+%>
diff --git a/httemplate/elements/tr-select-access_group.html b/httemplate/elements/tr-select-access_group.html
new file mode 100644
index 000000000..0beec0842
--- /dev/null
+++ b/httemplate/elements/tr-select-access_group.html
@@ -0,0 +1,22 @@
+<%
+ my( $groupnum, %opt ) = @_;
+
+ $opt{'access_group'} ||= [ qsearch( 'access_group', {} ) ]; # { disabled=>'' } )
+
+ #warn "***** tr-select-access_group: \n". Dumper(%opt);
+%>
+
+<% if ( scalar(@{ $opt{'access_group'} }) == 0 ) { %>
+
+ <INPUT TYPE="hidden" NAME="groupnum" VALUE="">
+
+<% } else { %>
+
+ <TR>
+ <TD ALIGN="right"><%= $opt{'label'} || 'Access group' %></TD>
+ <TD>
+ <%= include( '/elements/select-access_group.html', $groupnum, %opt ) %>
+ </TD>
+ </TR>
+
+<% } %>
diff --git a/httemplate/elements/xmenu.css b/httemplate/elements/xmenu.css
new file mode 100644
index 000000000..5bb8a0deb
--- /dev/null
+++ b/httemplate/elements/xmenu.css
@@ -0,0 +1,185 @@
+
+.webfx-menu, .webfx-menu * {
+ /*
+ Set the box sizing to content box
+ in the future when IE6 supports box-sizing
+ there will be an issue to fix the sizes
+
+ There is probably an issue with IE5 mac now
+ because IE5 uses content-box but the script
+ assumes all versions of IE uses border-box.
+
+ At the time of this writing mozilla did not support
+ box-sizing for absolute positioned element.
+
+ Opera only supports content-box
+ */
+ box-sizing: content-box;
+ -moz-box-sizing: content-box;
+}
+
+.webfx-menu {
+ position: absolute;
+ z-index: 100;
+ visibility: hidden;
+ width: 154px;
+ border: 1px solid black;
+ padding: 1px;
+ background: white;
+ filter: progid:DXImageTransform.Microsoft.Shadow(color="#777777", Direction=135, Strength=4)
+ alpha(Opacity=95);
+ -moz-opacity: 0.95;
+ /* a drop shadow would be nice in moz/others too... */
+}
+
+.webfx-menu-empty {
+ display: block;
+ border: 1px solid white;
+ padding: 2px 5px 2px 5px;
+ font-size: 11px;
+ /* font-family: Tahoma, Verdan, Helvetica, Sans-Serif; */
+ color: black;
+}
+
+.webfx-menu a {
+ display: block;
+ width: expression(constExpression(ieBox ? "100%": "auto")); /* should be ignored by mz and op */
+ height: expression(constExpression("1px"));
+ overflow: visible;
+ padding: 2px 0px 2px 5px;
+ font-size: 11px;
+ font-family: Tahoma, Verdan, Helvetica, Sans-Serif;
+ text-decoration: none;
+ vertical-align: center;
+ color: black;
+ border: 1px solid white;
+}
+
+.webfx-menu a:visited,
+.webfx-menu a:visited:hover {
+ color: black;
+}
+
+.webfx-menu a:hover {
+ color: black;
+ /* background: #faf7fa; #f5ebf4; #efdfef; white; #BC79B8; */
+ /* background: #ffe6fe; */
+ /* background: #ffc2fe; */
+ background: #fff2fe;
+ border: 1px solid #7e0079; /*rgb(120,172,255);#ff8800;*/
+}
+
+.webfx-menu a .arrow {
+ float: right;
+ border: 0;
+ width: 3px;
+ margin-right: 3px;
+ margin-top: 4px;
+}
+
+/* separtor */
+.webfx-menu div {
+ height: 0;
+ height: expression(constExpression(ieBox ? "2px" : "0"));
+ border-top: 1px solid #7e0079; /* rgb(120,172,255); */
+ border-bottom: 1px solid rgb(234,242,255);
+ overflow: hidden;
+ margin: 2px 0px 2px 0px;
+ font-size: 0mm;
+}
+
+.webfx-menu-bar {
+ /* i want a vertical bar */
+ display: block;
+
+ /* background: rgb(120,172,255);/*rgb(255,128,0);*/
+ /* background: #a097ed; */
+ background: #000000;
+ /* border: 1px solid #7E0079; */
+ /* border: 1px solid #000000; */
+ /* border: none */
+
+ padding: 2px;
+
+ font-family: Verdana, Helvetica, Sans-Serif;
+ font-size: 11px;
+
+ /* IE5.0 has the wierdest box model for inline elements */
+ padding: expression(constExpression(ie50 ? "0px" : "2px"));
+}
+
+.webfx-menu-bar a,
+.webfx-menu-bar a:visited {
+ /* i want a vertical bar */
+ display: block;
+
+ /* border: 1px solid black; /*rgb(0,0,0);/*rgb(255,128,0);*/
+ /* border: 1px solid black; /* #ffffff; */
+ /* border-bottom: 1px solid black; */
+ border-bottom: 1px solid white;
+ /* border-bottom: 1px solid rgb(0,66,174);
+ /* border-bottom: 1px solid black;
+ border-bottom: 1px solid black;
+ border-bottom: 1px solid black; */
+
+ padding: 1px 5px 1px 5px;
+
+ /* color: black; */
+ color: white;
+ text-decoration: none;
+
+ /* IE5.0 Does not paint borders and padding on inline elements without a height/width */
+ height: expression(constExpression(ie50 ? "17px" : "auto"));
+}
+
+.webfx-menu-bar a:hover {
+ /* color: black; */
+ color: white;
+ /* background: rgb(120,172,255); */
+ /* background: #BC79B8; */
+ background: #7E0079;
+ /* border-left: 1px solid rgb(234,242,255);
+ border-right: 1px solid rgb(0,66,174);
+ border-top: 1px solid rgb(234,242,255);
+ border-bottom: 1px solid rgb(0,66,174); */
+}
+
+.webfx-menu-bar a .arrow {
+ border: 0;
+ float: right;
+/* vertical-align: top; */
+ width: 3px;
+ margin-right: 3px;
+ margin-top: 4px;
+}
+
+.webfx-menu-bar a:active, .webfx-menu-bar a:focus {
+ -moz-outline: none;
+ outline: none;
+ /*
+ ie does not support outline but ie55 can hide the outline using
+ a proprietary property on HTMLElement. Did I say that IE sucks at CSS?
+ */
+ ie-dummy: expression(this.hideFocus=true);
+
+ border-left: 1px solid rgb(0,66,174);
+ border-right: 1px solid rgb(234,242,255);
+ border-top: 1px solid rgb(0,66,174);
+ border-bottom: 1px solid rgb(234,242,255);
+}
+
+.webfx-menu-title {
+ color: black;
+ /* background: #faf7fa; #f5ebf4; #efdfef; white; #BC79B8; */
+ background: #7e0079;
+/* border: 1px solid #7e0079; /*rgb(120,172,255);#ff8800;*/
+ padding: 3px 1px 3px 6px;
+ display: block;
+ font-size: 13px;
+ font-family: Tahoma, Verdan, Helvetica, Sans-Serif;
+ text-decoration: none;
+ color: white;
+/* border: 1px solid white; */
+ border-bottom: 1px solid white;
+}
+
diff --git a/httemplate/elements/xmenu.js b/httemplate/elements/xmenu.js
new file mode 100644
index 000000000..134265f53
--- /dev/null
+++ b/httemplate/elements/xmenu.js
@@ -0,0 +1,668 @@
+//<script>
+/*
+ * This script was created by Erik Arvidsson (erik@eae.net)
+ * for WebFX (http://webfx.eae.net)
+ * Copyright 2001
+ *
+ * For usage see license at http://webfx.eae.net/license.html
+ *
+ * Created: 2001-01-12
+ * Updates: 2001-11-20 Added hover mode support and removed Opera focus hacks
+ * 2001-12-20 Added auto positioning and some properties to support this
+ * 2002-08-13 toString used ' for attributes. Changed to " to allow in args
+ */
+
+// check browsers
+var ua = navigator.userAgent;
+var opera = /opera [56789]|opera\/[56789]/i.test(ua);
+var ie = !opera && /MSIE/.test(ua);
+var ie50 = ie && /MSIE 5\.[01234]/.test(ua);
+var ie6 = ie && /MSIE [6789]/.test(ua);
+var ieBox = ie && (document.compatMode == null || document.compatMode != "CSS1Compat");
+var moz = !opera && /gecko/i.test(ua);
+var nn6 = !opera && /netscape.*6\./i.test(ua);
+var khtml = /KHTML/i.test(ua);
+
+// define the default values
+
+webfxMenuDefaultWidth = 154;
+
+webfxMenuDefaultBorderLeft = 1;
+webfxMenuDefaultBorderRight = 1;
+webfxMenuDefaultBorderTop = 1;
+webfxMenuDefaultBorderBottom = 1;
+
+webfxMenuDefaultPaddingLeft = 1;
+webfxMenuDefaultPaddingRight = 1;
+webfxMenuDefaultPaddingTop = 1;
+webfxMenuDefaultPaddingBottom = 1;
+
+webfxMenuDefaultShadowLeft = 0;
+webfxMenuDefaultShadowRight = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 :0;
+webfxMenuDefaultShadowTop = 0;
+webfxMenuDefaultShadowBottom = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 : 0;
+
+
+webfxMenuItemDefaultHeight = 18;
+webfxMenuItemDefaultText = "Untitled";
+webfxMenuItemDefaultHref = "javascript:void(0)";
+
+webfxMenuSeparatorDefaultHeight = 6;
+
+webfxMenuDefaultEmptyText = "Empty";
+
+webfxMenuDefaultUseAutoPosition = nn6 ? false : true;
+
+
+
+// other global constants
+
+webfxMenuImagePath = "";
+
+webfxMenuUseHover = opera ? true : false;
+webfxMenuHideTime = 500;
+webfxMenuShowTime = 200;
+
+
+
+var webFXMenuHandler = {
+ idCounter : 0,
+ idPrefix : "webfx-menu-object-",
+ all : {},
+ getId : function () { return this.idPrefix + this.idCounter++; },
+ overMenuItem : function (oItem) {
+ if (this.showTimeout != null)
+ window.clearTimeout(this.showTimeout);
+ if (this.hideTimeout != null)
+ window.clearTimeout(this.hideTimeout);
+ var jsItem = this.all[oItem.id];
+ if (webfxMenuShowTime <= 0)
+ this._over(jsItem);
+ else if ( jsItem )
+ //this.showTimeout = window.setTimeout(function () { webFXMenuHandler._over(jsItem) ; }, webfxMenuShowTime);
+ // I hate IE5.0 because the piece of shit crashes when using setTimeout with a function object
+ this.showTimeout = window.setTimeout("webFXMenuHandler._over(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuShowTime);
+ },
+ outMenuItem : function (oItem) {
+ if (this.showTimeout != null)
+ window.clearTimeout(this.showTimeout);
+ if (this.hideTimeout != null)
+ window.clearTimeout(this.hideTimeout);
+ var jsItem = this.all[oItem.id];
+ if (webfxMenuHideTime <= 0)
+ this._out(jsItem);
+ else if ( jsItem )
+ //this.hideTimeout = window.setTimeout(function () { webFXMenuHandler._out(jsItem) ; }, webfxMenuHideTime);
+ this.hideTimeout = window.setTimeout("webFXMenuHandler._out(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuHideTime);
+ },
+ blurMenu : function (oMenuItem) {
+ window.setTimeout("webFXMenuHandler.all[\"" + oMenuItem.id + "\"].subMenu.hide();", webfxMenuHideTime);
+ },
+ _over : function (jsItem) {
+ if (jsItem.subMenu) {
+ jsItem.parentMenu.hideAllSubs();
+ jsItem.subMenu.show();
+ }
+ else
+ jsItem.parentMenu.hideAllSubs();
+ },
+ _out : function (jsItem) {
+ // find top most menu
+ var root = jsItem;
+ var m;
+ if (root instanceof WebFXMenuButton)
+ m = root.subMenu;
+ else {
+ m = jsItem.parentMenu;
+ while (m.parentMenu != null && !(m.parentMenu instanceof WebFXMenuBar))
+ m = m.parentMenu;
+ }
+ if (m != null)
+ m.hide();
+ },
+ hideMenu : function (menu) {
+ if (this.showTimeout != null)
+ window.clearTimeout(this.showTimeout);
+ if (this.hideTimeout != null)
+ window.clearTimeout(this.hideTimeout);
+
+ this.hideTimeout = window.setTimeout("webFXMenuHandler.all['" + menu.id + "'].hide()", webfxMenuHideTime);
+ },
+ showMenu : function (menu, src, dir) {
+ if (this.showTimeout != null)
+ window.clearTimeout(this.showTimeout);
+ if (this.hideTimeout != null)
+ window.clearTimeout(this.hideTimeout);
+
+ if (arguments.length < 3)
+ dir = "vertical";
+
+ menu.show(src, dir);
+ }
+};
+
+function WebFXMenu() {
+ this._menuItems = [];
+ this._subMenus = [];
+ this.id = webFXMenuHandler.getId();
+ this.top = 0;
+ this.left = 0;
+ this.shown = false;
+ this.parentMenu = null;
+ webFXMenuHandler.all[this.id] = this;
+}
+
+WebFXMenu.prototype.width = webfxMenuDefaultWidth;
+WebFXMenu.prototype.emptyText = webfxMenuDefaultEmptyText;
+WebFXMenu.prototype.useAutoPosition = webfxMenuDefaultUseAutoPosition;
+
+WebFXMenu.prototype.borderLeft = webfxMenuDefaultBorderLeft;
+WebFXMenu.prototype.borderRight = webfxMenuDefaultBorderRight;
+WebFXMenu.prototype.borderTop = webfxMenuDefaultBorderTop;
+WebFXMenu.prototype.borderBottom = webfxMenuDefaultBorderBottom;
+
+WebFXMenu.prototype.paddingLeft = webfxMenuDefaultPaddingLeft;
+WebFXMenu.prototype.paddingRight = webfxMenuDefaultPaddingRight;
+WebFXMenu.prototype.paddingTop = webfxMenuDefaultPaddingTop;
+WebFXMenu.prototype.paddingBottom = webfxMenuDefaultPaddingBottom;
+
+WebFXMenu.prototype.shadowLeft = webfxMenuDefaultShadowLeft;
+WebFXMenu.prototype.shadowRight = webfxMenuDefaultShadowRight;
+WebFXMenu.prototype.shadowTop = webfxMenuDefaultShadowTop;
+WebFXMenu.prototype.shadowBottom = webfxMenuDefaultShadowBottom;
+
+
+
+WebFXMenu.prototype.add = function (menuItem) {
+ this._menuItems[this._menuItems.length] = menuItem;
+ if (menuItem.subMenu) {
+ this._subMenus[this._subMenus.length] = menuItem.subMenu;
+ menuItem.subMenu.parentMenu = this;
+ }
+
+ menuItem.parentMenu = this;
+};
+
+WebFXMenu.prototype.show = function (relObj, sDir) {
+ if (this.useAutoPosition)
+ this.position(relObj, sDir);
+
+ var divElement = document.getElementById(this.id);
+ if ( divElement ) {
+
+ divElement.style.left = opera ? this.left : this.left + "px";
+ divElement.style.top = opera ? this.top : this.top + "px";
+ divElement.style.visibility = "visible";
+
+ if ( ie ) {
+ var shimElement = document.getElementById(this.id + "Shim");
+ if ( shimElement ) {
+ shimElement.style.width = divElement.offsetWidth;
+ shimElement.style.height = divElement.offsetHeight;
+ shimElement.style.top = divElement.style.top;
+ shimElement.style.left = divElement.style.left;
+ /*shimElement.style.zIndex = divElement.style.zIndex - 1; */
+ shimElement.style.display = "block";
+ shimElement.style.filter='progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
+ }
+ }
+
+ }
+
+ this.shown = true;
+
+ if (this.parentMenu)
+ this.parentMenu.show();
+};
+
+WebFXMenu.prototype.hide = function () {
+ this.hideAllSubs();
+ var divElement = document.getElementById(this.id);
+ if ( divElement ) {
+ divElement.style.visibility = "hidden";
+ if ( ie ) {
+ var shimElement = document.getElementById(this.id + "Shim");
+ if ( shimElement ) {
+ shimElement.style.display = "none";
+ }
+ }
+ }
+
+ this.shown = false;
+};
+
+WebFXMenu.prototype.hideAllSubs = function () {
+ for (var i = 0; i < this._subMenus.length; i++) {
+ if (this._subMenus[i].shown)
+ this._subMenus[i].hide();
+ }
+};
+
+WebFXMenu.prototype.toString = function () {
+ var top = this.top + this.borderTop + this.paddingTop;
+ var str = "<div id='" + this.id + "' class='webfx-menu' style='" +
+ "width:" + (!ieBox ?
+ this.width - this.borderLeft - this.paddingLeft - this.borderRight - this.paddingRight :
+ this.width) + "px;" +
+ (this.useAutoPosition ?
+ "left:" + this.left + "px;" + "top:" + this.top + "px;" :
+ "") +
+ (ie50 ? "filter: none;" : "") +
+ "'>";
+
+ if (this._menuItems.length == 0) {
+ str += "<span class='webfx-menu-empty'>" + this.emptyText + "</span>";
+ }
+ else {
+ str += '<span class="webfx-menu-title" onmouseover="webFXMenuHandler.overMenuItem(this)"' +
+ (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") +
+ '>' + this.emptyText + '</span>';
+ // str += '<div id="' + this.id + '-title">' + this.emptyText + '</div>';
+ // loop through all menuItems
+ for (var i = 0; i < this._menuItems.length; i++) {
+ var mi = this._menuItems[i];
+ str += mi;
+ if (!this.useAutoPosition) {
+ if (mi.subMenu && !mi.subMenu.useAutoPosition)
+ mi.subMenu.top = top - mi.subMenu.borderTop - mi.subMenu.paddingTop;
+ top += mi.height;
+ }
+ }
+
+ }
+
+ str += "</div>";
+
+ if ( ie ) {
+ str += "<iframe id='" + this.id + "Shim' src='javascript:false;' scrolling='no' frameBorder='0' style='position:absolute; top:0px; left: 0px; display:none;'></iframe>";
+ }
+
+ for (var i = 0; i < this._subMenus.length; i++) {
+ this._subMenus[i].left = this.left + this.width - this._subMenus[i].borderLeft;
+ str += this._subMenus[i];
+ }
+
+ return str;
+};
+// WebFXMenu.prototype.position defined later
+
+function WebFXMenuItem(sText, sHref, sToolTip, oSubMenu) {
+ this.text = sText || webfxMenuItemDefaultText;
+ this.href = (sHref == null || sHref == "") ? webfxMenuItemDefaultHref : sHref;
+ this.subMenu = oSubMenu;
+ if (oSubMenu)
+ oSubMenu.parentMenuItem = this;
+ this.toolTip = sToolTip;
+ this.id = webFXMenuHandler.getId();
+ webFXMenuHandler.all[this.id] = this;
+};
+WebFXMenuItem.prototype.height = webfxMenuItemDefaultHeight;
+WebFXMenuItem.prototype.toString = function () {
+ return "<a" +
+ " id='" + this.id + "'" +
+ " href=\"" + this.href + "\"" +
+ (this.toolTip ? " title=\"" + this.toolTip + "\"" : "") +
+ " onmouseover='webFXMenuHandler.overMenuItem(this)'" +
+ (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") +
+ (this.subMenu ? " unselectable='on' tabindex='-1'" : "") +
+ ">" +
+ (this.subMenu ? "<img class='arrow' src=\"" + webfxMenuImagePath + "arrow.right.black.png\">" : "") +
+ this.text +
+ "</a>";
+};
+
+
+function WebFXMenuSeparator() {
+ this.id = webFXMenuHandler.getId();
+ webFXMenuHandler.all[this.id] = this;
+};
+WebFXMenuSeparator.prototype.height = webfxMenuSeparatorDefaultHeight;
+WebFXMenuSeparator.prototype.toString = function () {
+ return "<div" +
+ " id='" + this.id + "'" +
+ (webfxMenuUseHover ?
+ " onmouseover='webFXMenuHandler.overMenuItem(this)'" +
+ " onmouseout='webFXMenuHandler.outMenuItem(this)'"
+ :
+ "") +
+ "></div>"
+};
+
+function WebFXMenuBar() {
+ this._parentConstructor = WebFXMenu;
+ this._parentConstructor();
+}
+WebFXMenuBar.prototype = new WebFXMenu;
+WebFXMenuBar.prototype.toString = function () {
+ var str = "<div id='" + this.id + "' class='webfx-menu-bar'>";
+
+ // loop through all menuButtons
+ for (var i = 0; i < this._menuItems.length; i++)
+ str += this._menuItems[i];
+
+ str += "</div>";
+
+ for (var i = 0; i < this._subMenus.length; i++)
+ str += this._subMenus[i];
+
+ return str;
+};
+
+function WebFXMenuButton(sText, sHref, sToolTip, oSubMenu) {
+ this._parentConstructor = WebFXMenuItem;
+ this._parentConstructor(sText, sHref, sToolTip, oSubMenu);
+}
+WebFXMenuButton.prototype = new WebFXMenuItem;
+WebFXMenuButton.prototype.toString = function () {
+ return "<a" +
+ " id='" + this.id + "'" +
+ " href='" + this.href + "'" +
+ (this.toolTip ? " title='" + this.toolTip + "'" : "") +
+ (webfxMenuUseHover ?
+ (" onmouseover='webFXMenuHandler.overMenuItem(this)'" +
+ " onmouseout='webFXMenuHandler.outMenuItem(this)'") :
+ (
+ " onfocus='webFXMenuHandler.overMenuItem(this)'" +
+ (this.subMenu ?
+ " onblur='webFXMenuHandler.blurMenu(this)'" :
+ ""
+ )
+ )) +
+ ">" +
+ (this.subMenu ? "<img class='arrow' src='" + webfxMenuImagePath + "arrow.right.png'>" : "") +
+ this.text +
+ "</a>";
+};
+
+
+
+
+
+/* Position functions */
+
+
+function getInnerLeft(el) {
+
+ if (el == null) return 0;
+
+ if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
+
+ return parseInt( getLeft(el) + parseInt(getBorderLeft(el)) );
+
+}
+
+
+
+function getLeft(el, debug) {
+
+ if (el == null) return 0;
+
+ //if ( debug )
+ // alert ( el.offsetLeft + ' - ' + getInnerLeft(el.offsetParent) );
+
+ return parseInt( el.offsetLeft + parseInt(getInnerLeft(el.offsetParent)) );
+
+}
+
+
+
+function getInnerTop(el) {
+
+ if (el == null) return 0;
+
+ if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
+
+ return parseInt( getTop(el) + parseInt(getBorderTop(el)) );
+
+}
+
+
+
+function getTop(el) {
+
+ if (el == null) return 0;
+
+ return parseInt( el.offsetTop + parseInt(getInnerTop(el.offsetParent)) );
+
+}
+
+
+
+function getBorderLeft(el) {
+
+ return ie ?
+
+ el.clientLeft :
+
+ ( khtml
+ ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
+ : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-left-width"))
+ );
+
+}
+
+
+
+function getBorderTop(el) {
+
+ return ie ?
+
+ el.clientTop :
+
+ ( khtml
+ ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
+ : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-top-width"))
+ );
+
+}
+
+
+
+function opera_getLeft(el) {
+
+ if (el == null) return 0;
+
+ return el.offsetLeft + opera_getLeft(el.offsetParent);
+
+}
+
+
+
+function opera_getTop(el) {
+
+ if (el == null) return 0;
+
+ return el.offsetTop + opera_getTop(el.offsetParent);
+
+}
+
+
+
+function getOuterRect(el, debug) {
+
+ return {
+
+ left: (opera ? opera_getLeft(el) : getLeft(el, debug)),
+
+ top: (opera ? opera_getTop(el) : getTop(el)),
+
+ width: el.offsetWidth,
+
+ height: el.offsetHeight
+
+ };
+
+}
+
+
+
+// mozilla bug! scrollbars not included in innerWidth/height
+
+function getDocumentRect(el) {
+
+ return {
+
+ left: 0,
+
+ top: 0,
+
+ width: (ie ?
+
+ (ieBox ? document.body.clientWidth : document.documentElement.clientWidth) :
+
+ window.innerWidth
+
+ ),
+
+ height: (ie ?
+
+ (ieBox ? document.body.clientHeight : document.documentElement.clientHeight) :
+
+ window.innerHeight
+
+ )
+
+ };
+
+}
+
+
+
+function getScrollPos(el) {
+
+ return {
+
+ left: (ie ?
+
+ (ieBox ? document.body.scrollLeft : document.documentElement.scrollLeft) :
+
+ window.pageXOffset
+
+ ),
+
+ top: (ie ?
+
+ (ieBox ? document.body.scrollTop : document.documentElement.scrollTop) :
+
+ window.pageYOffset
+
+ )
+
+ };
+
+}
+
+
+/* end position functions */
+
+WebFXMenu.prototype.position = function (relEl, sDir) {
+ var dir = sDir;
+ // find parent item rectangle, piRect
+ var piRect;
+ if (!relEl) {
+ var pi = this.parentMenuItem;
+ if (!this.parentMenuItem)
+ return;
+
+ relEl = document.getElementById(pi.id);
+ if (dir == null)
+ dir = pi instanceof WebFXMenuButton ? "vertical" : "horizontal";
+ //alert('created RelEl from parent: ' + pi.id);
+ piRect = getOuterRect(relEl, 1);
+ }
+ else if (relEl.left != null && relEl.top != null && relEl.width != null && relEl.height != null) { // got a rect
+ //alert('passed a Rect as RelEl: ' + typeof(relEl));
+
+ piRect = relEl;
+ }
+ else {
+ //alert('passed an element as RelEl: ' + typeof(relEl));
+ piRect = getOuterRect(relEl);
+ }
+
+ var menuEl = document.getElementById(this.id);
+ var menuRect = getOuterRect(menuEl);
+ var docRect = getDocumentRect();
+ var scrollPos = getScrollPos();
+ var pMenu = this.parentMenu;
+
+ if (dir == "vertical") {
+ if (piRect.left + menuRect.width - scrollPos.left <= docRect.width) {
+ //alert('piRect.left: ' + piRect.left);
+ this.left = piRect.left;
+ if ( ! ie )
+ this.left = this.left + 138;
+ } else if (docRect.width >= menuRect.width) {
+ //konq (not safari though) winds up here by accident and positions the menus all weird
+ //alert('docRect.width + scrollPos.left - menuRect.width');
+
+ this.left = docRect.width + scrollPos.left - menuRect.width;
+ } else {
+ //alert('scrollPos.left: ' + scrollPos.left);
+ this.left = scrollPos.left;
+ }
+
+ if (piRect.top + piRect.height + menuRect.height <= docRect.height + scrollPos.top)
+
+ this.top = piRect.top + piRect.height;
+
+ else if (piRect.top - menuRect.height >= scrollPos.top)
+
+ this.top = piRect.top - menuRect.height;
+
+ else if (docRect.height >= menuRect.height)
+
+ this.top = docRect.height + scrollPos.top - menuRect.height;
+
+ else
+
+ this.top = scrollPos.top;
+ }
+ else {
+ if (piRect.top + menuRect.height - this.borderTop - this.paddingTop <= docRect.height + scrollPos.top)
+
+ this.top = piRect.top - this.borderTop - this.paddingTop;
+
+ else if (piRect.top + piRect.height - menuRect.height + this.borderTop + this.paddingTop >= 0)
+
+ this.top = piRect.top + piRect.height - menuRect.height + this.borderBottom + this.paddingBottom + this.shadowBottom;
+
+ else if (docRect.height >= menuRect.height)
+
+ this.top = docRect.height + scrollPos.top - menuRect.height;
+
+ else
+
+ this.top = scrollPos.top;
+
+
+
+ var pMenuPaddingLeft = pMenu ? pMenu.paddingLeft : 0;
+
+ var pMenuBorderLeft = pMenu ? pMenu.borderLeft : 0;
+
+ var pMenuPaddingRight = pMenu ? pMenu.paddingRight : 0;
+
+ var pMenuBorderRight = pMenu ? pMenu.borderRight : 0;
+
+
+
+ if (piRect.left + piRect.width + menuRect.width + pMenuPaddingRight +
+
+ pMenuBorderRight - this.borderLeft + this.shadowRight <= docRect.width + scrollPos.left)
+
+ this.left = piRect.left + piRect.width + pMenuPaddingRight + pMenuBorderRight - this.borderLeft;
+
+ else if (piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight >= 0)
+
+ this.left = piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight;
+
+ else if (docRect.width >= menuRect.width)
+
+ this.left = docRect.width + scrollPos.left - menuRect.width;
+
+ else
+
+ this.left = scrollPos.left;
+ }
+};
diff --git a/httemplate/index.html b/httemplate/index.html
index c8a46d08f..33083f6e5 100644
--- a/httemplate/index.html
+++ b/httemplate/index.html
@@ -168,7 +168,7 @@
<TR><TD>
<BR>
<!-- <BR>View active NAS ports:
- <A HREF="browse/nas.cgi">session server</A>
+ <A HREF="browse/nas.cgi">session server</A> -->
<!-- or <A HREF="browse/nas-sqlradius.cgi">RADIUS</A>
<BR> -->
<A HREF="search/queue.html">View pending job queue</A>
@@ -177,7 +177,15 @@
<BR><A HREF="misc/dump.cgi">Download database dump</A>
<BR><BR><CENTER><HR WIDTH="94%" NOSHADE></CENTER><BR>
<A NAME="config" HREF="config/config-view.cgi">Configuration</a><!-- - <font size="+2" color="#ff0000">start here</font> -->
- <BR><BR><A NAME="admin">Provisioning, services and packages</a>
+ <BR><BR>
+ <A NAME="employees">Employees</A>
+ <UL>
+ <LI><A HREF="browse/access_user.html">View/Edit employees</A>
+ - Setup internal users
+ <LI><A HREF="browse/access_group.html">View/Edit employee groups</A>
+ - Employee groups allow you to control access to the backend
+ </UL>
+ <A NAME="admin">Provisioning, services and packages</a>
<ul>
<LI><A HREF="browse/part_export.cgi">View/Edit exports</A>
- Provisioning services to external machines, databases and APIs.
diff --git a/httemplate/misc/batch-cust_pay.html b/httemplate/misc/batch-cust_pay.html
index b40869476..41537ee7e 100644
--- a/httemplate/misc/batch-cust_pay.html
+++ b/httemplate/misc/batch-cust_pay.html
@@ -1,8 +1,6 @@
<%= include("/elements/header.html", 'Quick payment entry',
menubar(
'Main Menu' => $p, #popurl(1),
- 'Old-style quick payment entry' =>
- $p. 'search/cust_main-quickpay.html',
),
( $cgi->param('error') ? '' : 'onload="addRow()"' ),
)
diff --git a/httemplate/misc/payment.cgi b/httemplate/misc/payment.cgi
index d4fb4a2be..ec50c03f1 100644
--- a/httemplate/misc/payment.cgi
+++ b/httemplate/misc/payment.cgi
@@ -35,28 +35,18 @@
<INPUT TYPE="hidden" NAME="custnum" VALUE="<%= $custnum %>">
<INPUT TYPE="hidden" NAME="payby" VALUE="<%= $payby %>">
<INPUT TYPE="hidden" NAME="paybatch" VALUE="<%= $paybatch %>">
-<SCRIPT>
-var mywindow = -1;
-function myopen(filename,windowname,properties) {
- myclose();
- mywindow = window.open(filename,windowname,properties);
-}
-function myclose() {
- if ( mywindow != -1 )
- mywindow.close();
- mywindow = -1;
-}
-var achwindow = -1;
-function achopen(filename,windowname,properties) {
- achclose();
- achwindow = window.open(filename,windowname,properties);
-}
-function achclose() {
- if ( achwindow != -1 )
- achwindow.close();
- achwindow = -1;
+
+<SCRIPT TYPE="text/javascript" SRC="../elements/overlibmws.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" SRC="../elements/overlibmws_iframe.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" SRC="../elements/overlibmws_draggable.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript">
+function OLiframeContent(src, width, height, name) {
+ return ('<iframe src="'+src+'" width="'+width+'" height="'+height+'"'
+ +(name?' name="'+name+'" id="'+name+'"':'')+' scrolling="auto">'
+ +'<div>[iframe not supported]</div></iframe>');
}
</SCRIPT>
+
<% #include( '/elements/table.html', '#cccccc' ) %>
<%= ntable('#cccccc') %>
<TR>
@@ -112,7 +102,7 @@ function achclose() {
<TR>
<TD ALIGN="right">CVV2</TD>
<TD><INPUT TYPE="text" NAME="paycvv" VALUE="<%= $paycvv %>" SIZE=4 MAXLENGTH=4>
- (<A HREF="javascript:myopen('../docs/cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)
+ (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/cvv2.html', 480, 352, 'cvv2_popup' ), CAPTION, 'CVV2 Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;">help</A>)
</TD>
</TR>
<TR>
@@ -173,7 +163,7 @@ function achclose() {
<TD ALIGN="right">ABA/Routing&nbsp;number</TD>
<TD>
<INPUT TYPE="text" SIZE=10 MAXLENGTH=9 NAME="payinfo2" VALUE="<%=$payinfo2%>">
- (<A HREF="javascript:achopen('../docs/ach.html','ach','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=384,height=256')">help</A>)
+ (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/ach.html', 380, 240, 'ach_popup' ), CAPTION, 'ACH Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;">help</A>)
</TD>
</TR>
<TR>
@@ -205,5 +195,5 @@ function achclose() {
<BR>
<INPUT TYPE="submit" NAME="process" VALUE="Process payment">
</FORM>
-</BODY>
-</HTML>
+
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/search/cust_bill.cgi b/httemplate/search/cust_bill.cgi
deleted file mode 100755
index 5b0538ca3..000000000
--- a/httemplate/search/cust_bill.cgi
+++ /dev/null
@@ -1,165 +0,0 @@
-<%
-
-my $conf = new FS::Conf;
-my $maxrecords = $conf->config('maxsearchrecordsperpage');
-
-my $orderby = ''; #removeme
-
-my $limit = '';
-$limit .= "LIMIT $maxrecords" if $maxrecords;
-
-my $offset = $cgi->param('offset') || 0;
-$limit .= " OFFSET $offset" if $offset;
-
-my($total, $tot_amount, $tot_balance);
-
-my(@cust_bill);
-if ( $cgi->keywords ) {
- my($query) = $cgi->keywords;
- my $owed = "charged - ( select coalesce(sum(amount),0) from cust_bill_pay
- where cust_bill_pay.invnum = cust_bill.invnum )
- - ( select coalesce(sum(amount),0) from cust_credit_bill
- where cust_credit_bill.invnum = cust_bill.invnum )";
- my @where;
- if ( $query =~ /^(OPEN(\d*)_)?(invnum|date|custnum)$/ ) {
- my($open, $days, $field) = ($1, $2, $3);
- $field = "_date" if $field eq 'date';
- $orderby = "ORDER BY cust_bill.$field";
- push @where, "0 != $owed" if $open;
- push @where, "cust_bill._date < ". (time-86400*$days) if $days;
- } else {
- die "unknown query string $query";
- }
-
- my $extra_sql = scalar(@where) ? 'WHERE '. join(' AND ', @where) : '';
-
- my $statement = "SELECT COUNT(*), sum(charged), sum($owed)
- FROM cust_bill $extra_sql";
- my $sth = dbh->prepare($statement) or die dbh->errstr. " doing $statement";
- $sth->execute or die "Error executing \"$statement\": ". $sth->errstr;
-
- ( $total, $tot_amount, $tot_balance ) = @{$sth->fetchrow_arrayref};
-
- @cust_bill = qsearch(
- 'cust_bill',
- {},
- "cust_bill.*, $owed as owed",
- "$extra_sql $orderby $limit"
- );
-} else {
- $cgi->param('invnum') =~ /^\s*(FS-)?(\d+)\s*$/;
- my $invnum = $2;
- @cust_bill = qsearchs('cust_bill', { 'invnum' => $invnum } );
- $total = scalar(@cust_bill);
-}
-
-#if ( scalar(@cust_bill) == 1 ) {
-if ( $total == 1 ) {
- my $invnum = $cust_bill[0]->invnum;
- print $cgi->redirect(popurl(2). "view/cust_bill.cgi?$invnum"); #redirect
-} elsif ( scalar(@cust_bill) == 0 ) {
-%>
-<!-- mason kludge -->
-<%
- eidiot("Invoice not found.");
-} else {
-%>
-<!-- mason kludge -->
-<%
-
- #begin pager
- my $pager = '';
- if ( $total != scalar(@cust_bill) && $maxrecords ) {
- unless ( $offset == 0 ) {
- $cgi->param('offset', $offset - $maxrecords);
- $pager .= '<A HREF="'. $cgi->self_url.
- '"><B><FONT SIZE="+1">Previous</FONT></B></A> ';
- }
- my $poff;
- my $page;
- for ( $poff = 0; $poff < $total; $poff += $maxrecords ) {
- $page++;
- if ( $offset == $poff ) {
- $pager .= qq!<FONT SIZE="+2">$page</FONT> !;
- } else {
- $cgi->param('offset', $poff);
- $pager .= qq!<A HREF="!. $cgi->self_url. qq!">$page</A> !;
- }
- }
- unless ( $offset + $maxrecords > $total ) {
- $cgi->param('offset', $offset + $maxrecords);
- $pager .= '<A HREF="'. $cgi->self_url.
- '"><B><FONT SIZE="+1">Next</FONT></B></A> ';
- }
- }
- #end pager
-
- print header("Invoice Search Results", menubar(
- 'Main Menu', popurl(2)
- )).
- "$total matching invoices found<BR>".
- "\$$tot_balance total balance<BR>".
- "\$$tot_amount total amount<BR>".
- "<BR>$pager". table(). <<END;
- <TR>
- <TH></TH>
- <TH>Balance</TH>
- <TH>Amount</TH>
- <TH>Date</TH>
- <TH>Contact name</TH>
- <TH>Company</TH>
- </TR>
-END
-
- foreach my $cust_bill ( @cust_bill ) {
- my($invnum, $owed, $charged, $date ) = (
- $cust_bill->invnum,
- sprintf("%.2f", $cust_bill->getfield('owed')),
- sprintf("%.2f", $cust_bill->charged),
- $cust_bill->_date,
- );
- my $pdate = time2str("%b %d %Y", $date);
-
- my $rowspan = 1;
-
- my $view = popurl(2). "view/cust_bill.cgi?$invnum";
- print <<END;
- <TR>
- <TD ROWSPAN=$rowspan><A HREF="$view">$invnum</A></TD>
- <TD ROWSPAN=$rowspan ALIGN="right"><A HREF="$view">\$$owed</A></TD>
- <TD ROWSPAN=$rowspan ALIGN="right"><A HREF="$view">\$$charged</A></TD>
- <TD ROWSPAN=$rowspan><A HREF="$view">$pdate</A></TD>
-END
- my $custnum = $cust_bill->custnum;
- my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
- if ( $cust_main ) {
- my $cview = popurl(2). "view/cust_main.cgi?". $cust_main->custnum;
- my ( $name, $company ) = (
- $cust_main->last. ', '. $cust_main->first,
- $cust_main->company,
- );
- print <<END;
- <TD ROWSPAN=$rowspan><A HREF="$cview">$name</A></TD>
- <TD ROWSPAN=$rowspan><A HREF="$cview">$company</A></TD>
-END
- } else {
- print <<END
- <TD ROWSPAN=$rowspan COLSPAN=2>WARNING: couldn't find cust_main.custnum $custnum (cust_bill.invnum $invnum)</TD>
-END
- }
-
- print "</TR>";
- }
- $tot_balance = sprintf("%.2f", $tot_balance);
- $tot_amount = sprintf("%.2f", $tot_amount);
- print "</TABLE>$pager<BR>". table(). <<END;
- <TR><TD>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD><TH>Total<BR>Balance</TH><TH>Total<BR>Amount</TH></TR>
- <TR><TD></TD><TD ALIGN="right">\$$tot_balance</TD><TD ALIGN="right">\$$tot_amount</TD></TD></TR>
- </TABLE>
- </BODY>
-</HTML>
-END
-
-}
-
-%>
diff --git a/httemplate/search/cust_main-otaker.cgi b/httemplate/search/cust_main-otaker.cgi
index 03c2619af..6ac0bde18 100755
--- a/httemplate/search/cust_main-otaker.cgi
+++ b/httemplate/search/cust_main-otaker.cgi
@@ -1,28 +1,23 @@
-<HTML>
- <HEAD>
- <TITLE>Customer Search</TITLE>
- </HEAD>
- <BODY BGCOLOR="#e8e8e8">
- <FONT SIZE=7>
- Customer Search
- </FONT>
- <BR>
- <FORM ACTION="cust_main.cgi" METHOD="GET">
- Search for <B>Order taker</B>:
- <INPUT TYPE="hidden" NAME="otaker_on" VALUE="TRUE">
- <% my $sth = dbh->prepare("SELECT DISTINCT otaker FROM cust_main")
- or die dbh->errstr;
- $sth->execute() or die $sth->errstr;
-# my @otakers = map { $_->[0] } @{$sth->selectall_arrayref};
- %>
- <SELECT NAME="otaker">
- <% my $otaker; while ( $otaker = $sth->fetchrow_arrayref ) { %>
- <OPTION><%= $otaker->[0] %></OTAKER>
- <% } %>
- </SELECT>
- <P><INPUT TYPE="submit" VALUE="Search">
+<%= include('/elements/header.html', 'Customer Search' ) %>
- </FORM>
- </BODY>
-</HTML>
+<FORM ACTION="cust_main.cgi" METHOD="GET">
+Search for <B>Order taker</B>:
+ <INPUT TYPE="hidden" NAME="otaker_on" VALUE="TRUE">
+
+<% my $sth = dbh->prepare("SELECT DISTINCT otaker FROM cust_main")
+ or die dbh->errstr;
+ $sth->execute() or die $sth->errstr;
+ #my @otakers = map { $_->[0] } @{$sth->fetchall_arrayref};
+%>
+<SELECT NAME="otaker">
+<% my $otaker; while ( $otaker = $sth->fetchrow_arrayref ) { %>
+ <OPTION><%= $otaker->[0] %>
+<% } %>
+</SELECT>
+
+<P><INPUT TYPE="submit" VALUE="Search">
+
+</FORM>
+
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/search/cust_main-payinfo.html b/httemplate/search/cust_main-payinfo.html
deleted file mode 100755
index b82b610d8..000000000
--- a/httemplate/search/cust_main-payinfo.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<HTML>
- <HEAD>
- <TITLE>Customer Search</TITLE>
- </HEAD>
- <BODY BGCOLOR="#e8e8e8">
- <FONT SIZE=7>
- Customer Search
- </FONT>
- <BR>
- <FORM ACTION="cust_main.cgi" METHOD="GET">
- Search for <B>Credit card #</B>:
- <INPUT TYPE="hidden" NAME="card_on" VALUE="TRUE">
- <INPUT TYPE="text" NAME="card">
-
- <P><INPUT TYPE="submit" VALUE="Search">
-
- </FORM>
- </BODY>
-</HTML>
-
diff --git a/httemplate/search/cust_main-quickpay.html b/httemplate/search/cust_main-quickpay.html
deleted file mode 100755
index 154a64199..000000000
--- a/httemplate/search/cust_main-quickpay.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<HTML>
- <HEAD>
- <TITLE>Quick payment entry</TITLE>
- </HEAD>
- <BODY BGCOLOR="#e8e8e8">
- <FONT SIZE=7>
- Quick payment entry
- </FONT>
- <BR><BR>
- <A HREF="../">Main Menu</A><BR><BR>
- <FORM ACTION="cust_main.cgi" METHOD="GET">
- <INPUT TYPE="hidden" NAME="quickpay" VALUE="yes">
- <INPUT TYPE="checkbox" NAME="last_on" CHECKED> Search for <B>last name</B>:
- <INPUT TYPE="text" NAME="last_text">
- using search method: <SELECT NAME="last_type">
- <OPTION SELECTED>All
- <OPTION>Fuzzy
- <OPTION>Substring
- <OPTION>Exact
- </SELECT>
-
- <P><INPUT TYPE="checkbox" NAME="company_on" CHECKED> Search for <B>company</B>:
- <INPUT TYPE="text" NAME="company_text">
- using search method: <SELECT NAME="company_type">
- <OPTION SELECTED>All
- <OPTION>Fuzzy
- <OPTION>Substring
- <OPTION>Exact
- </SELECT>
-
- <P><INPUT TYPE="submit" VALUE="Search">
-
- </FORM>
-
- <HR>Explanation of search methods:
- <UL>
- <LI><B>All</B> - Try all search methods.
- <LI><B>Fuzzy</B> - Searches for matches that are close to your text.
- <LI><B>Substring</B> - Searches for matches that contain your text.
- <LI><B>Exact</B> - Finds exact matches only, but much faster than the other search methods.
- </UL>
- </BODY>
-</HTML>
-
diff --git a/httemplate/search/cust_main.cgi b/httemplate/search/cust_main.cgi
index 36ad39da8..8b70ff490 100755
--- a/httemplate/search/cust_main.cgi
+++ b/httemplate/search/cust_main.cgi
@@ -220,14 +220,13 @@ if ( scalar(@cust_main) == 1 && ! $cgi->param('referral_custnum') ) {
eidiot "No matching customers found!\n";
} else {
%>
-<!-- mason kludge -->
-<%
+<%= include('/elements/header.html', "Customer Search Results", '' ) %>
- $total ||= scalar(@cust_main);
- print header("Customer Search Results",menubar(
- 'Main Menu', popurl(2)
- )), "$total matching customers found ";
+ <% $total ||= scalar(@cust_main); %>
+ <%= $total %> matching customers found
+
+ <%
#begin pager
my $pager = '';
if ( $total != scalar(@cust_main) && $maxrecords ) {
@@ -368,12 +367,14 @@ END
my $pcompany = $company
? qq!<A HREF="$view"><FONT SIZE=-1>$company</FONT></A>!
: '<FONT SIZE=-1>&nbsp;</FONT>';
- print <<END;
+ %>
+
<TR>
- <TD ROWSPAN=$rowspan><A HREF="$view"><FONT SIZE=-1>$custnum</FONT></A></TD>
- <TD ROWSPAN=$rowspan><A HREF="$view"><FONT SIZE=-1>$last, $first</FONT></A></TD>
- <TD ROWSPAN=$rowspan>$pcompany</TD>
-END
+ <TD ROWSPAN=<%= $rowspan || 1 %>><A HREF="<%= $view %>"><FONT SIZE=-1><%= $custnum %></FONT></A></TD>
+ <TD ROWSPAN=<%= $rowspan || 1 %>><A HREF="<%= $view %>"><FONT SIZE=-1><%= "$last, $first" %></FONT></A></TD>
+ <TD ROWSPAN=<%= $rowspan || 1 %>><%= $pcompany %></TD>
+
+ <%
if ( defined dbdef->table('cust_main')->column('ship_last') ) {
my($ship_last,$ship_first,$ship_company)=(
$cust_main->ship_last || $cust_main->getfield('last'),
@@ -383,15 +384,18 @@ END
my $pship_company = $ship_company
? qq!<A HREF="$view"><FONT SIZE=-1>$ship_company</FONT></A>!
: '<FONT SIZE=-1>&nbsp;</FONT>';
- print <<END;
- <TD ROWSPAN=$rowspan><A HREF="$view"><FONT SIZE=-1>$ship_last, $ship_first</FONT></A></TD>
- <TD ROWSPAN=$rowspan>$pship_company</A></TD>
-END
- }
+ %>
- foreach my $addl_col ( @addl_cols ) {
- print "<TD ROWSPAN=$rowspan ALIGN=right><FONT SIZE=-1>";
- if ( $addl_col eq 'tickets' ) {
+ <TD ROWSPAN=<%= $rowspan || 1 %>><A HREF="<%= $view %>"><FONT SIZE=-1><%= "$ship_last, $ship_first" %></FONT></A></TD>
+ <TD ROWSPAN=<%= $rowspan || 1 %>><%= $pship_company %></A></TD>
+
+ <% }
+
+ foreach my $addl_col ( @addl_cols ) { %>
+
+ <TD ROWSPAN=<%= $rowspan || 1 %> ALIGN=right><FONT SIZE=-1>
+
+ <% if ( $addl_col eq 'tickets' ) {
if ( @custom_priorities ) {
print &itable('', 0);
foreach my $priority ( @custom_priorities, '' ) {
@@ -461,10 +465,14 @@ END
}
print "</TR>";
}
+
+ %>
- print "</TABLE>$pager</BODY></HTML>";
+ </TABLE><%= $pager %>
-}
+ <%= include('/elements/footer.html') %>
+
+<% }
#undef $cache; #does this help?
diff --git a/httemplate/search/cust_pay.html b/httemplate/search/cust_pay.html
deleted file mode 100755
index 6414cf771..000000000
--- a/httemplate/search/cust_pay.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<HTML>
- <HEAD>
- <TITLE>Check # Search</TITLE>
- </HEAD>
- <BODY BGCOLOR="#e8e8e8">
- <FONT SIZE=7>
- Check # Search
- </FONT>
- <BR><BR>
- <FORM ACTION="cust_pay.cgi" METHOD="GET">
- Search for <B>check #</B>:
- <INPUT TYPE="text" NAME="payinfo">
- <INPUT TYPE="hidden" NAME="payby" VALUE="BILL">
- <BR><BR><INPUT TYPE="submit" VALUE="Search">
- </FORM>
- </BODY>
-</HTML>
-
diff --git a/httemplate/search/cust_pkg_report.cgi b/httemplate/search/cust_pkg_report.cgi
index 412c3f79d..d9aada5f4 100755
--- a/httemplate/search/cust_pkg_report.cgi
+++ b/httemplate/search/cust_pkg_report.cgi
@@ -1,23 +1,22 @@
-<HTML>
- <HEAD>
- <TITLE>Packages</TITLE>
- </HEAD>
- <BODY BGCOLOR="#e8e8e8">
- <H1>Packages</H1>
- <FORM ACTION="cust_pkg.cgi" METHOD="GET">
- <INPUT TYPE="hidden" NAME="magic" VALUE="bill">
- Return packages with next bill date:<BR><BR>
- <TABLE>
- <%= include( '/elements/tr-input-beginning_ending.html' ) %>
- <%= include( '/elements/tr-select-agent.html',
- $cgi->param('agentnum'),
- )
- %>
- </TABLE>
- <BR><INPUT TYPE="submit" VALUE="Get Report">
+<%= include('/elements/header.html', 'Packages' ) %>
- </FORM>
+<FORM ACTION="cust_pkg.cgi" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="bill">
- </BODY>
-</HTML>
+Return packages with next bill date:
+<BR><BR>
+ <TABLE>
+ <%= include( '/elements/tr-input-beginning_ending.html' ) %>
+ <%= include( '/elements/tr-select-agent.html',
+ $cgi->param('agentnum'),
+ )
+ %>
+ </TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/search/report_cust_bill.html b/httemplate/search/report_cust_bill.html
index a7be76689..f1b7bfa14 100644
--- a/httemplate/search/report_cust_bill.html
+++ b/httemplate/search/report_cust_bill.html
@@ -1,28 +1,28 @@
- <HEAD>
- <TITLE>Invoice report criteria</TITLE>
- </HEAD>
- <BODY BGCOLOR="#e8e8e8">
- <H1>Invoice report criteria</H1>
- <FORM ACTION="cust_bill.html" METHOD="GET">
- <INPUT TYPE="hidden" NAME="magic" VALUE="_date">
- <TABLE>
- <%= include( '/elements/tr-select-agent.html',
- $cgi->param('agentnum'),
- 'label' => 'Invoices for agent: ',
- )
- %>
- <%= include( '/elements/tr-input-beginning_ending.html' ) %>
- <TR>
- <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="open" VALUE="1" CHECKED></TD>
- <TD>Show only open invoices</TD>
- </TR>
- <TR>
- <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="newest_percust" VALUE="1"></TD>
- <TD>Show only the single most recent invoice per-customer</TD>
- </TR>
- </TABLE>
- <BR><INPUT TYPE="submit" VALUE="Get Report">
- </FORM>
- </BODY>
-</HTML>
+<%= include('/elements/header.html', 'Invoice report criteria' ) %>
+<FORM ACTION="cust_bill.html" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="_date">
+
+<TABLE>
+ <%= include( '/elements/tr-select-agent.html',
+ $cgi->param('agentnum'),
+ 'label' => 'Invoices for agent: ',
+ )
+ %>
+ <%= include( '/elements/tr-input-beginning_ending.html' ) %>
+ <TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="open" VALUE="1" CHECKED></TD>
+ <TD>Show only open invoices</TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="newest_percust" VALUE="1"></TD>
+ <TD>Show only the single most recent invoice per-customer</TD>
+ </TR>
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/search/report_cust_credit.html b/httemplate/search/report_cust_credit.html
index 56bbd0ac0..8ca52dc9a 100644
--- a/httemplate/search/report_cust_credit.html
+++ b/httemplate/search/report_cust_credit.html
@@ -1,36 +1,38 @@
-<HTML>
- <HEAD>
- <TITLE>Credit report criteria</TITLE>
- </HEAD>
- <BODY BGCOLOR="#e8e8e8">
- <H1>Credit report criteria</H1>
- <FORM ACTION="cust_credit.html" METHOD="GET">
- <INPUT TYPE="hidden" NAME="magic" VALUE="_date">
- <TABLE>
- <TR>
- <TD ALIGN="right">Credits by employee: </TD>
+<%= include('/elements/header.html', 'Credit report' ) %>
+
+<FORM ACTION="cust_credit.html" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="_date">
+
+<TABLE>
+ <TR>
+ <TD ALIGN="right">Credits by employee: </TD>
+
<%
my $sth = dbh->prepare("SELECT DISTINCT otaker FROM cust_credit")
or die dbh->errstr;
$sth->execute or die $sth->errstr;
my @otakers = map { $_->[0] } @{$sth->fetchall_arrayref};
%>
- <TD><SELECT NAME="otaker">
- <OPTION VALUE="">all</OPTION>
- <% foreach my $otaker ( @otakers ) { %>
- <OPTION VALUE="<%= $otaker %>"><%= $otaker %></OPTION>
- <% } %>
- </SELECT>
- </TD>
- </TR>
- <%= include( '/elements/tr-select-agent.html',
- $cgi->param('agentnum'),
- 'label' => 'for agent: ',
- )
- %>
- <%= include( '/elements/tr-input-beginning_ending.html' ) %>
- </TABLE>
- <BR><INPUT TYPE="submit" VALUE="Get Report">
- </FORM>
- </BODY>
-</HTML>
+
+ <TD><SELECT NAME="otaker">
+ <OPTION VALUE="">all</OPTION>
+ <% foreach my $otaker ( @otakers ) { %>
+ <OPTION VALUE="<%= $otaker %>"><%= $otaker %></OPTION>
+ <% } %>
+ </SELECT>
+ </TD>
+ </TR>
+ <%= include( '/elements/tr-select-agent.html',
+ $cgi->param('agentnum'),
+ 'label' => 'for agent: ',
+ )
+ %>
+ <%= include( '/elements/tr-input-beginning_ending.html' ) %>
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/search/report_cust_pay.html b/httemplate/search/report_cust_pay.html
index 5d8b74e77..8adf7dc13 100644
--- a/httemplate/search/report_cust_pay.html
+++ b/httemplate/search/report_cust_pay.html
@@ -1,38 +1,43 @@
-<HTML>
- <HEAD>
- <TITLE>Payment report criteria</TITLE>
- </HEAD>
- <BODY BGCOLOR="#e8e8e8">
- <H1>Payment report criteria</H1>
- <FORM ACTION="cust_pay.cgi" METHOD="GET">
- <INPUT TYPE="hidden" NAME="magic" VALUE="_date">
- <TABLE>
- <TR>
- <TD ALIGN="right">Payments of type: </TD>
- <TD><SELECT NAME="payby">
- <OPTION VALUE="">all</OPTION>
- <OPTION VALUE="CARD">credit card (all)</OPTION>
- <OPTION VALUE="CARD-VisaMC">credit card (Visa/MasterCard)</OPTION>
- <OPTION VALUE="CARD-Amex">credit card (American Express)</OPTION>
- <OPTION VALUE="CARD-Discover">credit card (Discover)</OPTION>
- <OPTION VALUE="CARD-Maestro">credit card (Maestro/Switch/Solo)</OPTION>
- <OPTION VALUE="CHEK">electronic check / ACH</OPTION>
- <OPTION VALUE="BILL">check</OPTION>
- <OPTION VALUE="PREP">prepaid card</OPTION>
- <OPTION VALUE="CASH">cash</OPTION>
- <OPTION VALUE="WEST">Western Union</OPTION>
- <OPTION VALUE="MCRD">manual credit card</OPTION>
- </SELECT>
- </TD>
- </TR>
- <%= include( '/elements/tr-select-agent.html',
- $cgi->param('agentnum'),
- 'label' => 'for agent: ',
- )
- %>
- <%= include( '/elements/tr-input-beginning_ending.html' ) %>
- </TABLE>
- <BR><INPUT TYPE="submit" VALUE="Get Report">
- </FORM>
- </BODY>
-</HTML>
+<%= include('/elements/header.html', 'Payment report' ) %>
+
+<FORM ACTION="cust_pay.cgi" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="_date">
+
+<TABLE>
+
+ <TR>
+ <TD ALIGN="right">Payments of type: </TD>
+ <TD>
+ <SELECT NAME="payby">
+ <OPTION VALUE="">all</OPTION>
+ <OPTION VALUE="CARD">credit card (all)</OPTION>
+ <OPTION VALUE="CARD-VisaMC">credit card (Visa/MasterCard)</OPTION>
+ <OPTION VALUE="CARD-Amex">credit card (American Express)</OPTION>
+ <OPTION VALUE="CARD-Discover">credit card (Discover)</OPTION>
+ <OPTION VALUE="CARD-Maestro">credit card (Maestro/Switch/Solo)</OPTION>
+ <OPTION VALUE="CHEK">electronic check / ACH</OPTION>
+ <OPTION VALUE="BILL">check</OPTION>
+ <OPTION VALUE="PREP">prepaid card</OPTION>
+ <OPTION VALUE="CASH">cash</OPTION>
+ <OPTION VALUE="WEST">Western Union</OPTION>
+ <OPTION VALUE="MCRD">manual credit card</OPTION>
+ </SELECT>
+ </TD>
+ </TR>
+
+ <%= include( '/elements/tr-select-agent.html',
+ $cgi->param('agentnum'),
+ 'label' => 'for agent: ',
+ )
+ %>
+
+ <%= include( '/elements/tr-input-beginning_ending.html' ) %>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/search/report_prepaid_income.html b/httemplate/search/report_prepaid_income.html
index 57c318eba..4359918f9 100644
--- a/httemplate/search/report_prepaid_income.html
+++ b/httemplate/search/report_prepaid_income.html
@@ -1,13 +1,13 @@
-<HTML>
- <HEAD>
- <TITLE>Prepaid Income (Unearned Revenue) Report</TITLE>
- <LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2">
- <SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT>
- <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT>
- <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT>
- </HEAD>
- <BODY BGCOLOR="#e8e8e8">
- <H1>Prepaid Income (Unearned Revenue) Report</H1>
+<%= include('/elements/header.html', 'Prepaid Income (Unearned Revenue) Report',
+ '',
+ '',
+ '<LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2">
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT>
+ '
+) %>
+
<FORM ACTION="report_prepaid_income.cgi" METHOD="GET">
<TABLE>
<TR>
@@ -32,8 +32,6 @@
});
</SCRIPT>
- <INPUT TYPE="submit" VALUE="Generate report">
- </BODY>
-</HTML>
- <TABLE>
+<INPUT TYPE="submit" VALUE="Generate report">
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/search/report_tax.html b/httemplate/search/report_tax.html
index 7a8ecd4f0..bdeb8e237 100755
--- a/httemplate/search/report_tax.html
+++ b/httemplate/search/report_tax.html
@@ -1,40 +1,35 @@
-<HTML>
- <HEAD>
- <TITLE>Tax Report Criteria</TITLE>
- </HEAD>
- <BODY BGCOLOR="#e8e8e8">
- <H1>Tax Report Criteria</H1>
- <FORM ACTION="report_tax.cgi" METHOD="GET">
-
- <TABLE>
-
- <%= include( '/elements/tr-select-agent.html' ) %>
-
- <%= include( '/elements/tr-input-beginning_ending.html' ) %>
-
- <% my $conf = new FS::Conf;
- if ( $conf->exists('enable_taxclasses') ) {
- %>
- <TR>
- <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="show_taxclasses" VALUE="1"></TD>
- <TD>Show tax classes</TD>
- </TR>
- <% } %>
-
- <% my @pkg_class = qsearch('pkg_class', {});
- if ( @pkg_class ) {
- %>
- <TR>
- <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="show_pkgclasses" VALUE="1"></TD>
- <TD>Show package classes</TD>
- </TR>
- <% } %>
-
- </TABLE>
-
- <BR><INPUT TYPE="submit" VALUE="Get Report">
- </FORM>
-
- </BODY>
-</HTML>
+<%= include('/elements/header.html', 'Tax Report' ) %>
+<FORM ACTION="report_tax.cgi" METHOD="GET">
+
+<TABLE>
+
+ <%= include( '/elements/tr-select-agent.html' ) %>
+
+ <%= include( '/elements/tr-input-beginning_ending.html' ) %>
+
+ <% my $conf = new FS::Conf;
+ if ( $conf->exists('enable_taxclasses') ) {
+ %>
+ <TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="show_taxclasses" VALUE="1"></TD>
+ <TD>Show tax classes</TD>
+ </TR>
+ <% } %>
+
+ <% my @pkg_class = qsearch('pkg_class', {});
+ if ( @pkg_class ) {
+ %>
+ <TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="show_pkgclasses" VALUE="1"></TD>
+ <TD>Show package classes</TD>
+ </TR>
+ <% } %>
+
+</TABLE>
+
+<BR><INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/search/sqlradius.html b/httemplate/search/sqlradius.html
index 8f4878dbc..645505101 100644
--- a/httemplate/search/sqlradius.html
+++ b/httemplate/search/sqlradius.html
@@ -1,9 +1,5 @@
-<%= include( '/elements/header.html', 'Search RADIUS sessions', '', '', '
-<LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2">
-<SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT>
-<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT>
-<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT>
-') %>
+<%= include( '/elements/header.html', 'Search RADIUS sessions' ) %>
+
<FORM NAME="OneTrueForm" ACTION="sqlradius.cgi" METHOD="GET">
<% #include( '/elements/table.html' ) %>
<%= ntable('#cccccc') %>
@@ -47,48 +43,10 @@
</TR>
<% } %>
-<TR>
- <TD ALIGN="right">From: </TD>
- <TD>
- <INPUT TYPE="text" NAME="beginning" ID="beginning_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="beginning_button" STYLE="cursor: pointer" TITLE="Select date">
- </TD>
- <SCRIPT TYPE="text/javascript">
- Calendar.setup({
- inputField: "beginning_text",
- ifFormat: "%m/%d/%Y",
- button: "beginning_button",
- align: "BR"
- });
- </SCRIPT>
-</TR>
-<TR>
- <TD></TD>
- <TD><i>m/d/y</i></TD>
-</TR>
-<TR>
- <TD ALIGN="right">To: </TD>
- <TD>
- <INPUT TYPE="text" NAME="ending" ID="ending_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="ending_button" STYLE="cursor:pointer" TITLE="Select date">
- </TD>
- <SCRIPT TYPE="text/javascript">
- Calendar.setup({
- inputField: "ending_text",
- ifFormat: "%m/%d/%Y",
- button: "ending_button",
- align: "BR"
- });
- </SCRIPT>
-</TR>
-<TR>
- <TD></TD>
- <TD><i>m/d/y</i>
- <BR><FONT SIZE="-1">(leave one or both dates blank for an open-ended search)</FONT>
- </TD>
-</TR>
+<%= include( '/elements/tr-input-beginning_ending.html' ) %>
+
</TABLE>
<BR><INPUT TYPE="submit" VALUE="View sessions">
</FORM>
-</BODY>
-</HTML>
-
+<%= include('/elements/footer.html') %>
diff --git a/httemplate/search/svc_acct.html b/httemplate/search/svc_acct.html
deleted file mode 100755
index c504c2f34..000000000
--- a/httemplate/search/svc_acct.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<HTML>
- <HEAD>
- <TITLE>Account Search</TITLE>
- </HEAD>
- <BODY BGCOLOR="#e8e8e8">
- <FONT SIZE=7>
- Account Search
- </FONT>
- <BR><BR>
- <FORM ACTION="svc_acct.cgi" METHOD="GET">
- Search for <B>username</B>:
- <INPUT TYPE="text" NAME="username">
-
- <P><INPUT TYPE="submit" VALUE="Search">
-
- </FORM>
- </BODY>
-</HTML>
-
diff --git a/httemplate/search/svc_domain.cgi b/httemplate/search/svc_domain.cgi
index f261ea9f3..b02eea8bd 100755
--- a/httemplate/search/svc_domain.cgi
+++ b/httemplate/search/svc_domain.cgi
@@ -61,7 +61,7 @@ my $link_cust = sub {
$svc_x->custnum ? [ "${p}view/cust_main.cgi?", 'custnum' ] : '';
};
-%><%= include ('elements/search.html',
+%><%= include( 'elements/search.html',
'title' => "Domain Search Results",
'name' => 'domains',
'query' => $sql_query,
diff --git a/httemplate/search/svc_domain.html b/httemplate/search/svc_domain.html
deleted file mode 100755
index b759102f4..000000000
--- a/httemplate/search/svc_domain.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<HTML>
- <HEAD>
- <TITLE>Domain Search</TITLE>
- </HEAD>
- <BODY BGCOLOR="#e8e8e8">
- <FONT SIZE=7>
- Domain Search
- </FONT>
- <BR><BR>
- <FORM ACTION="svc_domain.cgi" METHOD="GET">
- Search for <B>domain</B>:
- <INPUT TYPE="text" NAME="domain">
-
- <P><INPUT TYPE="submit" VALUE="Search">
-
- </FORM>
- </BODY>
-</HTML>
-
diff --git a/httemplate/search/svc_external.cgi b/httemplate/search/svc_external.cgi
index 8dbb949c8..7968f3c43 100755
--- a/httemplate/search/svc_external.cgi
+++ b/httemplate/search/svc_external.cgi
@@ -38,17 +38,18 @@ if ( $query eq 'svcnum' ) {
}
if ( scalar(@svc_external) == 1 ) {
- print $cgi->redirect(popurl(2). "view/svc_external.cgi?". $svc_external[0]->svcnum);
- #exit;
+
+ %><%= $cgi->redirect(popurl(2). "view/svc_external.cgi?". $svc_external[0]->svcnum) %><%
+
} elsif ( scalar(@svc_external) == 0 ) {
-%>
-<!-- mason kludge -->
-<%
- eidiot "No matching external services found!\n";
-} else {
-%>
-<!-- mason kludge -->
-<%= include("/elements/header.html","External Search Results",'') %>
+
+ %><%= include('/elements/header.html', 'External Search Results' ) %>
+
+ No matching external services found
+
+<% } else {
+
+ %><%= include('/elements/header.html', 'External Search Results', '') %>
<%= scalar(@svc_external) %> matching external services found
<TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0>
diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html
index ece1b62bb..32e0ee1fc 100755
--- a/httemplate/view/cust_main/packages.html
+++ b/httemplate/view/cust_main/packages.html
@@ -67,7 +67,7 @@ foreach my $pkg (sort pkgsort_pkgnum_cancel @$packages) {
<!--pkgnum: <%=$pkg->{pkgnum}%>-->
<TR>
- <TD ROWSPAN=<%=$rowspan%>>
+ <TD ROWSPAN=<%= $rowspan || 1 %>>
<A NAME="cust_pkg<%=$pkg->{pkgnum}%>"><%=$pkg->{pkgnum}%></A>:
<%=$pkg->{pkg}%> - <%=$pkg->{comment}%><BR>
<% unless ($pkg->{cancel}) { %>
@@ -75,7 +75,7 @@ foreach my $pkg (sort pkgsort_pkgnum_cancel @$packages) {
(&nbsp;<%=pkg_dates_link($pkg)%>&nbsp;|&nbsp;<%=pkg_customize_link($pkg,$cust_main->custnum)%>&nbsp;)
<% } %>
</TD>
- <TD ROWSPAN=<%=$rowspan%>>
+ <TD ROWSPAN=<%= $rowspan || 1 %>>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="100%">
<%
diff --git a/httemplate/view/cust_main/payment_history.html b/httemplate/view/cust_main/payment_history.html
index a0bec3906..f0cd993ff 100644
--- a/httemplate/view/cust_main/payment_history.html
+++ b/httemplate/view/cust_main/payment_history.html
@@ -58,7 +58,10 @@
<% } %>
-<BR><A HREF="<%= $p %>edit/cust_credit.cgi?<%= $custnum %>">Post credit</A>
+<BR>
+
+<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('<%= $p %>edit/cust_credit.cgi?<%= $custnum %>', 392, 336, 'cust_credit_popup' ), CAPTION, 'Post credit', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK )">Post credit</A>
+
<BR>
<%
@@ -115,8 +118,10 @@ foreach my $cust_pay ($cust_main->cust_pay) {
#completely unapplied
$pre = '<B><FONT COLOR="#FF0000">Unapplied ';
$post = '</FONT></B>';
- $apply = qq! (<A HREF="${p}edit/cust_bill_pay.cgi?!.
- $cust_pay->paynum. '">apply</A>)';
+ $apply = qq! (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('${p}edit/cust_bill_pay.cgi?!.
+ $cust_pay->paynum.
+ qq!', 392, 336, 'cust_credit_popup' ), CAPTION, 'Post credit', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK )">apply</A>)!;
+
} elsif ( scalar(@cust_bill_pay) == 1
&& scalar(@cust_pay_refund) == 0
&& $cust_pay->unapplied == 0 ) {
@@ -151,8 +156,9 @@ foreach my $cust_pay ($cust_main->cust_pay) {
$desc .= '&nbsp;&nbsp;'.
'<B><FONT COLOR="#FF0000">$'.
$cust_pay->unapplied. ' unapplied</FONT></B>'.
- qq! (<A HREF="${p}edit/cust_bill_pay.cgi?!.
- $cust_pay->paynum. '">apply</A>)'.
+ qq! (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('${p}edit/cust_bill_pay.cgi?!.
+ $cust_pay->paynum.
+ qq!', 392, 336, 'cust_credit_popup' ), CAPTION, 'Post credit', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK )">apply</A>)!.
'<BR>';
}
}
@@ -264,8 +270,9 @@ foreach my $cust_credit ($cust_main->cust_credit) {
#completely unapplied
$pre = '<B><FONT COLOR="#FF0000">Unapplied ';
$post = '</FONT></B>';
- $apply = qq! (<A HREF="${p}edit/cust_credit_bill.cgi?!.
- $cust_credit->crednum. '">apply</A>)';
+ $apply = qq! (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('${p}edit/cust_credit_bill.cgi?!.
+ $cust_credit->crednum.
+ qq!', 392, 336, 'cust_credit_popup' ), CAPTION, 'Post credit', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK )">apply</A>)!;
} elsif ( scalar(@cust_credit_bill) == 1
&& scalar(@cust_credit_refund) == 0
&& $cust_credit->credited == 0 ) {
@@ -299,8 +306,9 @@ foreach my $cust_credit ($cust_main->cust_credit) {
if ( $cust_credit->credited > 0 ) {
$desc .= '&nbsp;&nbsp;<B><FONT COLOR="#FF0000">$'.
$cust_credit->credited. ' unapplied</FONT></B>'.
- qq! (<A HREF="${p}edit/cust_credit_bill.cgi?!.
- $cust_credit->crednum. '">apply</A>)'.
+ qq! (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('${p}edit/cust_credit_bill.cgi?!.
+ $cust_credit->crednum.
+ qq!', 392, 336, 'cust_credit_popup' ), CAPTION, 'Post credit', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK )">apply</A>)!.
'<BR>';
}
}