first part of ACL and re-skinning work and some other small stuff
authorivan <ivan>
Sun, 14 May 2006 16:47:31 +0000 (16:47 +0000)
committerivan <ivan>
Sun, 14 May 2006 16:47:31 +0000 (16:47 +0000)
85 files changed:
CREDITS
Changes.1.7.0 [new file with mode: 0644]
FS/FS/AccessRight.pm [new file with mode: 0644]
FS/FS/CGI.pm
FS/FS/Schema.pm
FS/FS/UI/Web.pm
FS/FS/access_group.pm [new file with mode: 0644]
FS/FS/access_groupagent.pm [new file with mode: 0644]
FS/FS/access_right.pm [new file with mode: 0644]
FS/FS/access_user.pm [new file with mode: 0644]
FS/FS/access_user_pref.pm [new file with mode: 0644]
FS/FS/access_usergroup.pm [new file with mode: 0644]
FS/FS/agent_type.pm
FS/FS/cust_bill.pm
FS/FS/m2m_Common.pm [new file with mode: 0644]
FS/FS/part_pkg/billoneday.pm [deleted file]
FS/FS/payby.pm
FS/FS/svc_domain.pm
FS/MANIFEST
FS/bin/freeside-addoutsourceuser
FS/t/AccessRight.t [new file with mode: 0644]
FS/t/access_group.t [new file with mode: 0644]
FS/t/access_groupagent.t [new file with mode: 0644]
FS/t/access_right.t [new file with mode: 0644]
FS/t/access_user.t [new file with mode: 0644]
FS/t/access_user_pref.t [new file with mode: 0644]
FS/t/access_usergroup.t [new file with mode: 0644]
htetc/handler.pl
httemplate/autohandler
httemplate/browse/access_group.html [new file with mode: 0644]
httemplate/browse/access_user.html [new file with mode: 0644]
httemplate/browse/agent_type.cgi
httemplate/browse/cust_main_county.cgi
httemplate/browse/msgcat.cgi
httemplate/browse/part_pkg.cgi
httemplate/edit/access_group.html [new file with mode: 0644]
httemplate/edit/access_user.html [new file with mode: 0644]
httemplate/edit/agent_type.cgi
httemplate/edit/cust_bill_pay.cgi
httemplate/edit/cust_credit.cgi
httemplate/edit/cust_credit_bill.cgi
httemplate/edit/cust_main.cgi
httemplate/edit/cust_pkg.cgi
httemplate/edit/elements/edit.html
httemplate/edit/part_referral.cgi
httemplate/edit/part_virtual_field.cgi
httemplate/edit/process/access_group.html [new file with mode: 0644]
httemplate/edit/process/access_user.html [new file with mode: 0644]
httemplate/edit/process/agent_type.cgi
httemplate/edit/process/cust_bill_pay.cgi
httemplate/edit/process/cust_credit.cgi
httemplate/edit/process/cust_credit_bill.cgi
httemplate/edit/process/elements/process.html
httemplate/edit/svc_domain.cgi
httemplate/elements/checkboxes-table.html [new file with mode: 0644]
httemplate/elements/cssexpr.js [new file with mode: 0644]
httemplate/elements/footer.html
httemplate/elements/header.html
httemplate/elements/menubar.html
httemplate/elements/select-access_group.html [new file with mode: 0644]
httemplate/elements/tr-select-access_group.html [new file with mode: 0644]
httemplate/elements/xmenu.css [new file with mode: 0644]
httemplate/elements/xmenu.js [new file with mode: 0644]
httemplate/index.html
httemplate/misc/batch-cust_pay.html
httemplate/misc/payment.cgi
httemplate/search/cust_bill.cgi [deleted file]
httemplate/search/cust_main-otaker.cgi
httemplate/search/cust_main-payinfo.html [deleted file]
httemplate/search/cust_main-quickpay.html [deleted file]
httemplate/search/cust_main.cgi
httemplate/search/cust_pay.html [deleted file]
httemplate/search/cust_pkg_report.cgi
httemplate/search/report_cust_bill.html
httemplate/search/report_cust_credit.html
httemplate/search/report_cust_pay.html
httemplate/search/report_prepaid_income.html
httemplate/search/report_tax.html
httemplate/search/sqlradius.html
httemplate/search/svc_acct.html [deleted file]
httemplate/search/svc_domain.cgi
httemplate/search/svc_domain.html [deleted file]
httemplate/search/svc_external.cgi
httemplate/view/cust_main/packages.html
httemplate/view/cust_main/payment_history.html

diff --git a/CREDITS b/CREDITS
index 930b4f3..72b0491 100644 (file)
--- 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 (file)
index 0000000..8dcc36e
--- /dev/null
@@ -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 (file)
index 0000000..01d63e3
--- /dev/null
@@ -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
+
index f1f2a3d..d598f52 100644 (file)
@@ -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);
index 9125758..e811856 100644 (file)
@@ -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
index dc45e01..10ddbf3 100644 (file)
@@ -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 (file)
index 0000000..9d870e5
--- /dev/null
@@ -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 (file)
index 0000000..6b5def1
--- /dev/null
@@ -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 (file)
index 0000000..67200f2
--- /dev/null
@@ -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 (file)
index 0000000..ca311d3
--- /dev/null
@@ -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 (file)
index 0000000..ff957f2
--- /dev/null
@@ -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 (file)
index 0000000..4d8836c
--- /dev/null
@@ -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;
+
index 968b3b7..b28c572 100644 (file)
@@ -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
 
index cce028b..bcae4d6 100644 (file)
@@ -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 (file)
index 0000000..fd8700a
--- /dev/null
@@ -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 (file)
index 8740547..0000000
+++ /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;
index 4425df0..9f8b689 100644 (file)
@@ -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
 
index 191d856..bdaf79b 100644 (file)
@@ -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";
     }
   }
 
index c309251..bd810a8 100644 (file)
@@ -339,3 +339,4 @@ FS/access_groupagent.pm
 t/access_groupagent.t
 FS/access_right.pm
 t/access_right.t
+FS/m2m_Common.pm
index cad07f1..02a4351 100644 (file)
@@ -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 (file)
index 0000000..a966842
--- /dev/null
@@ -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 (file)
index 0000000..be14109
--- /dev/null
@@ -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 (file)
index 0000000..aff1f25
--- /dev/null
@@ -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 (file)
index 0000000..66cd362
--- /dev/null
@@ -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 (file)
index 0000000..cab679d
--- /dev/null
@@ -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 (file)
index 0000000..2822098
--- /dev/null
@@ -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 (file)
index 0000000..383a7cf
--- /dev/null
@@ -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";
index e8cd333..cbe2dd3 100644 (file)
@@ -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 '
index a3f7eb0..ad0ab8b 100644 (file)
@@ -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 (file)
index 0000000..6ba89ea
--- /dev/null
@@ -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 (file)
index 0000000..38d5430
--- /dev/null
@@ -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,
+                                    ''
+                                  ],
+             )
+%>
index 2e1bdad..a5ffb10 100755 (executable)
@@ -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,
+                                    '',
+                                  ],
+             )
 %>
index 1e0e088..9e3feb8 100755 (executable)
@@ -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') %>
index d4adf9f..318ebfd 100755 (executable)
@@ -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') %>
index 0afa547..41d8635 100755 (executable)
@@ -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 (file)
index 0000000..11b8df7
--- /dev/null
@@ -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 (file)
index 0000000..2b19dbf
--- /dev/null
@@ -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>'
+                     ;
+                   },
+           )
+%>
index 944ddd0..f5afd3a 100755 (executable)
@@ -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') %>
index 24bce30..9d3bdd8 100755 (executable)
@@ -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
-
-%>
index aae0df2..946b108 100755 (executable)
@@ -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
-
-%>
index 1a97e13..409ea3c 100755 (executable)
@@ -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
-
-%>
index 80fec93..bb2a861 100755 (executable)
@@ -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') %>
 
-%>
index ce1c866..174d4dd 100755 (executable)
@@ -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') %>
index 5486b4b..120c03a 100644 (file)
   # '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') ) {
     );
   }
 
-%>
-
-
-<%= include("/elements/header.html", $title,
+%><%= include("/elements/header.html", $title,
               include( '/elements/menubar.html', @menubar )
            )
 %>
 
 <%= 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>
 
     </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>
 
 </TABLE>
 
+<%= ref( $opt{'html_bottom'} )
+      ? &{ $opt{'html_bottom'} }( $object )
+      : $opt{'html_bottom'}
+%>
+
 <BR>
 
 <INPUT TYPE="submit" VALUE="<%= $object->$pkey() ? "Apply changes" : "Add $opt{'name'}" %>">
index f784dfa..dce1e63 100755 (executable)
@@ -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') %>
index fb10321..7b2c768 100644 (file)
@@ -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 (file)
index 0000000..e8c6d07
--- /dev/null
@@ -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 (file)
index 0000000..a6c2a36
--- /dev/null
@@ -0,0 +1,8 @@
+<%= include( 'elements/process.html',
+               'table'       => 'access_user',
+               'viewall_dir' => 'browse',
+               'process_m2m' => { 'link_table'   => 'access_usergroup',
+                                  'target_table' => 'access_group',
+                                },
+           )
+%>
index 5165945..fd8ca88 100755 (executable)
@@ -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");
 }
index 0025b16..fc668bb 100755 (executable)
@@ -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>
 
-%>
+<% } %>
index 85bfd44..6a4ef19 100755 (executable)
@@ -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>
 
-%>
+<% } %>
index 28f892f..3b75953 100755 (executable)
@@ -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>
 
-%>
+<% } %>
index 83ff6f7..59ad35e 100644 (file)
@@ -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) = @_;
 
   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 );
index ca0e339..f47ba0a 100755 (executable)
@@ -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 (file)
index 0000000..d26ebef
--- /dev/null
@@ -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 (file)
index 0000000..c434d8d
--- /dev/null
@@ -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();
+       });
+}
index 6029d76..32d1219 100644 (file)
@@ -1,2 +1,5 @@
+        </TD>
+      </TR>
+    </TABLE>
   </BODY>
 </HTML>
index 10e4e40..4981457 100644 (file)
   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>" : '' %>
index 87a5031..29facb6 100644 (file)
@@ -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 (file)
index 0000000..b05f565
--- /dev/null
@@ -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 (file)
index 0000000..0beec08
--- /dev/null
@@ -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 (file)
index 0000000..5bb8a0d
--- /dev/null
@@ -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 (file)
index 0000000..134265f
--- /dev/null
@@ -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;
+       }
+};
index c8a46d0..33083f6 100644 (file)
     <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>
       <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.
index b408694..41537ee 100644 (file)
@@ -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()"' ),
           )
index d4fb4a2..ec50c03 100644 (file)
 <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 (executable)
index 5b0538c..0000000
+++ /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
-
-}
-
-%>
index 03c2619..6ac0bde 100755 (executable)
@@ -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 (executable)
index b82b610..0000000
+++ /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 (executable)
index 154a641..0000000
+++ /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>
-
index 36ad39d..8b70ff4 100755 (executable)
@@ -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 (executable)
index 6414cf7..0000000
+++ /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>
-
index 412c3f7..d9aada5 100755 (executable)
@@ -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') %>
index a7be766..f1b7bfa 100644 (file)
@@ -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') %>
index 56bbd0a..8ca52dc 100644 (file)
@@ -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') %>
index 5d8b74e..8adf7dc 100644 (file)
@@ -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') %>
index 57c318e..4359918 100644 (file)
@@ -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') %>
index 7a8ecd4..bdeb8e2 100755 (executable)
@@ -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') %>
index 8f4878d..6455051 100644 (file)
@@ -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') %>
   </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 (executable)
index c504c2f..0000000
+++ /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>
-
index f261ea9..b02eea8 100755 (executable)
@@ -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 (executable)
index b759102..0000000
+++ /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>
-
index 8dbb949..7968f3c 100755 (executable)
@@ -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>
index ece1b62..32e0ee1 100755 (executable)
@@ -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%">
 
 <%
index a0bec39..f0cd993 100644 (file)
 
 <% } %>
 
-<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>';
     }
   }