Merge branch 'master' of git.freeside.biz:/home/git/freeside
authorIvan Kohler <ivan@freeside.biz>
Sun, 17 Feb 2013 22:21:53 +0000 (14:21 -0800)
committerIvan Kohler <ivan@freeside.biz>
Sun, 17 Feb 2013 22:21:53 +0000 (14:21 -0800)
55 files changed:
FS/FS/AccessRight.pm
FS/FS/cust_main/Location.pm
httemplate/browse/part_pkg.cgi
httemplate/edit/bulk-part_pkg.html [new file with mode: 0644]
httemplate/edit/process/bulk-part_pkg.html [new file with mode: 0644]
httemplate/elements/checkbox-tristate.html [new file with mode: 0644]
httemplate/search/477.html
httemplate/search/477partIA_detail.html
httemplate/search/477partIA_summary.html
httemplate/search/477partIIA.html
httemplate/search/477partV.html
httemplate/search/477partVI_census.html
httemplate/search/agent_inventory.html
httemplate/search/bill_batch.cgi
httemplate/search/cdr.html
httemplate/search/cust_bill_event.cgi
httemplate/search/cust_bill_pay.html
httemplate/search/cust_bill_pkg_discount.html
httemplate/search/cust_credit_bill.html
httemplate/search/cust_credit_bill_pkg.html
httemplate/search/cust_credit_refund.html
httemplate/search/cust_event.html
httemplate/search/cust_main-zip.html
httemplate/search/cust_pay_batch.cgi
httemplate/search/cust_pkg_discount.html
httemplate/search/cust_pkg_svc.html
httemplate/search/cust_tax_adjustment.html
httemplate/search/cust_tax_exempt.cgi
httemplate/search/cust_tax_exempt_pkg.cgi
httemplate/search/elements/checkbox-foot.html [new file with mode: 0644]
httemplate/search/inventory_item.html
httemplate/search/mailinglistmember.html
httemplate/search/part_pkg.html
httemplate/search/pay_batch.cgi
httemplate/search/phone_avail.html
httemplate/search/phone_inventory_provisioned.html
httemplate/search/prepay_credit.html
httemplate/search/prospect_main.html
httemplate/search/qual.cgi
httemplate/search/queue.html
httemplate/search/reg_code.html
httemplate/search/report_477.html
httemplate/search/rt_ticket.html
httemplate/search/rt_transaction.html
httemplate/search/sql.html
httemplate/search/svc_broadband.cgi
httemplate/search/svc_dish.cgi
httemplate/search/svc_domain.cgi
httemplate/search/svc_external.cgi
httemplate/search/svc_forward.cgi
httemplate/search/svc_hardware.cgi
httemplate/search/svc_phone.cgi
httemplate/search/svc_www.cgi
httemplate/search/timeworked.html
httemplate/search/unprovisioned_services.html

index bd8a889..50fb0e3 100644 (file)
@@ -342,6 +342,8 @@ tie my %rights, 'Tie::IxHash',
     'Edit package definitions',
     { rightname=>'Edit global package definitions', global=>1 },
   
+    'Bulk edit package definitions',
+
     'Edit billing events',
     { rightname=>'Edit global billing events', global=>1 },
 
index ba3513b..bd0af53 100644 (file)
@@ -167,15 +167,29 @@ sub _upgrade_data {
     $cust_main->set(bill_locationnum => $bill_location->locationnum);
 
     if ( $cust_main->get('ship_address1') ) {
-      my $ship_location = FS::cust_location->new(
-        {
-          custnum => $custnum,
-          map { $_ => $cust_main->get("ship_$_") } location_fields()
+      # detect duplicates
+      my $same = 1;
+      my $ship_location;
+      foreach (location_fields()) {
+        if ( length($cust_main->get("ship_$_")) and
+             $cust_main->get($_) ne $cust_main->get("ship_$_") ) {
+          $same = 0;
         }
-      );
-      $error = $ship_location->insert;
-      die "error migrating service address for customer $custnum: $error"
-        if $error;
+      }
+
+      if ( $same ) {
+        $ship_location = $bill_location;
+      } else {
+        $ship_location = FS::cust_location->new(
+          {
+            custnum => $custnum,
+            map { $_ => $cust_main->get("ship_$_") } location_fields()
+          }
+        );
+        $error = $ship_location->insert;
+        die "error migrating service address for customer $custnum: $error"
+          if $error;
+      }
 
       $cust_main->set(ship_locationnum => $ship_location->locationnum);
 
index 5b19a30..5dee5b8 100755 (executable)
@@ -1,6 +1,7 @@
 <% include( 'elements/browse.html',
                  'title'                 => 'Package Definitions',
                  'html_init'             => $html_init,
+                 'html_form'             => $html_form,
                  'html_posttotal'        => $html_posttotal,
                  'name'                  => 'package definitions',
                  'disableable'           => 1,
@@ -21,6 +22,8 @@
                  'links'                 => \@links,
                  'align'                 => $align,
                  'link_field'            => 'pkgpart',
+                 'html_init'             => $html_init,
+                 'html_foot'             => $html_foot,
              )
 %>
 <%init>
@@ -34,6 +37,7 @@ my $acl_edit_global = $curuser->access_right($edit_global);
 my $acl_config      = $curuser->access_right('Configuration'); #to edit services
                                                                #and agent types
                                                                #and bulk change
+my $acl_edit_bulk   = $curuser->access_right('Bulk edit package definitions');
 
 die "access denied"
   unless $acl_edit || $acl_edit_global;
@@ -131,9 +135,7 @@ $select = "
 
 ";
 
-my $html_init;
-#unless ( $cgi->param('active') ) {
-  $html_init = qq!
+my $html_init = qq!
     One or more service definitions are grouped together into a package 
     definition and given pricing information.  Customers purchase packages
     rather than purchase services directly.<BR><BR>
@@ -145,7 +147,6 @@ my $html_init;
     </FORM>
     <BR><BR>
   !;
-#}
 
 $cgi->param('dummy', 1);
 
@@ -436,6 +437,10 @@ if ( $taxclasses ) {
   $align .= 'l';
 }
 
+# make a table of report class optionnames =>  the actual 
+my %report_optionname_name = map { 'report_option_'.$_->num, $_->name }
+  qsearch('part_pkg_report_option', { disabled => '' });
+
 push @header, 'Plan options',
               'Services';
               #'Service', 'Quan', 'Primary';
@@ -446,8 +451,18 @@ push @fields,
                     if ( $part_pkg->plan ) {
 
                       my %options = $part_pkg->options;
-
-                      [ map { 
+                      # gather any options that are really report options,
+                      # convert them to their user-friendly names,
+                      # and sort them (I think?)
+                      my @report_options =
+                        sort { $a cmp $b }
+                        map { $report_optionname_name{$_} }
+                        grep { $options{$_}
+                               and exists($report_optionname_name{$_}) }
+                        keys %options;
+
+                      my @rows = (
+                        map { 
                               [
                                 { 'data'  => "$_: ",
                                   'align' => 'right',
@@ -458,11 +473,30 @@ push @fields,
                               ];
                             }
                         grep { $options{$_} =~ /\S/ } 
-                        grep { $_ !~ /^(setup|recur)_fee$/ }
+                        grep { $_ !~ /^(setup|recur)_fee$/ 
+                               and $_ !~ /^report_option_\d+$/ }
                         keys %options
-                      ];
+                      );
+                      if ( @report_options ) {
+                        push @rows,
+                          [ { 'data'  => 'Report classes',
+                              'align' => 'center',
+                              'style' => 'font-weight: bold',
+                              'colspan' => 2
+                            } ];
+                        foreach (@report_options) {
+                          push @rows, [
+                            { 'data'  => $_,
+                              'align' => 'center',
+                              'colspan' => 2
+                            }
+                          ];
+                        } # foreach @report_options
+                      } # if @report_options
+
+                      return \@rows;
 
-                    } else {
+                    } else { # should never happen...
 
                       [ map { [
                                 { 'data'  => uc($_),
@@ -540,4 +574,20 @@ $extra_count = ( $count_extra_sql ? ' AND ' : ' WHERE ' ). $extra_count
   if $extra_count;
 my $count_query = "SELECT COUNT(*) FROM part_pkg $count_extra_sql $extra_count";
 
+my $html_form = '';
+my $html_foot = '';
+if ( $acl_edit_bulk ) {
+  # insert a checkbox column
+  push @header, '';
+  push @fields, sub {
+    '<INPUT TYPE="checkbox" NAME="pkgpart" VALUE=' . $_[0]->pkgpart .'>';
+  };
+  push @links, '';
+  $align .= 'c';
+  $html_form = qq!<FORM ACTION="${p}edit/bulk-part_pkg.html" METHOD="POST">!;
+  $html_foot = include('/search/elements/checkbox-foot.html',
+      submit  => 'edit report classes', # for now it's only report classes
+  ) . '</FORM>';
+}
+
 </%init>
diff --git a/httemplate/edit/bulk-part_pkg.html b/httemplate/edit/bulk-part_pkg.html
new file mode 100644 (file)
index 0000000..751bf7e
--- /dev/null
@@ -0,0 +1,74 @@
+<& /elements/header.html, 'Edit package report classes' &>
+%# change that title if we add any other editing controls
+
+%# this should be centralized somewhere
+<STYLE TYPE="text/css">
+.row0 { background-color: #eeeeee; }
+.row1 { background-color: #ffffff; }
+</STYLE>
+
+<FORM ACTION="process/bulk-part_pkg.html" METHOD="POST">
+<DIV>
+The following packages will be changed:<BR>
+% foreach my $pkgpart (sort keys(%part_pkg)) {
+<INPUT TYPE="hidden" NAME="pkgpart" VALUE="<% $pkgpart %>">
+<% $part_pkg{$pkgpart}->pkg_comment %><BR>
+% }
+</DIV>
+<BR>
+<& /elements/table-grid.html &>\
+<& /elements/tr-justtitle.html, value => mt('Report classes') &>
+% my $row = 0;
+% foreach my $num (sort keys %report_class) {
+  <TR CLASS="row<%$row % 2%>">
+    <TD>
+%   if ( defined $initial_state{$num} ) {
+      <& /elements/checkbox.html,
+            field => 'report_option_'.$num,
+            value => 1,
+            curr_value => $initial_state{$num}
+      &>
+%   } else {
+%     # needs to be a tristate so that you can say "don't change it"
+      <& /elements/checkbox-tristate.html, field => 'report_option_'.$num &>
+%   }
+    </TD>
+    <TD><% $report_class{$num}->name %></TD>
+  </TR>
+%   $row++;
+% }
+</TABLE>
+<BR>
+<INPUT TYPE="submit">
+</FORM>
+<& /elements/footer.html &>
+<%init>
+die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Bulk edit package definitions');
+my @pkgparts = $cgi->param('pkgpart')
+  or die "no package definitions selected";
+
+my %part_pkg = map { $_ => FS::part_pkg->by_key($_) } @pkgparts;
+my %part_pkg_option = map { $_ => { $part_pkg{$_}->options } } @pkgparts;
+my %report_class = map { $_->num => $_ }
+  qsearch('part_pkg_report_option', { disabled => '' });
+
+my %initial_state;
+foreach my $num (keys %report_class) {
+  my $yes = 0;
+  my $no = 0;
+  foreach my $option (values %part_pkg_option) {
+    if ( $option->{"report_option_$num"} ) {
+      $yes = 1;
+    } else {
+      $no = 1;
+    }
+  }
+  if ( $yes and $no ) {
+    $initial_state{$num} = undef;
+  } elsif ( $yes ) {
+    $initial_state{$num} = 1;
+  } elsif ( $no ) {
+    $initial_state{$num} = 0;
+  } # else, uh, you didn't provide any pkgparts
+}
+</%init>
diff --git a/httemplate/edit/process/bulk-part_pkg.html b/httemplate/edit/process/bulk-part_pkg.html
new file mode 100644 (file)
index 0000000..4775a93
--- /dev/null
@@ -0,0 +1,30 @@
+% if ( $error ) {
+%  $cgi->param('error', $error);
+<% $cgi->redirect(popurl(3).'/edit/bulk-part_pkg.cgi?', $cgi->query_string) %>
+% } else {
+<% $cgi->redirect(popurl(3).'/browse/part_pkg.cgi') %>
+% }
+<%init>
+die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Bulk edit package definitions');
+
+my @pkgparts = $cgi->param('pkgpart')
+  or die "no package definitions selected";
+
+my %changes;
+foreach my $param (grep { /^report_option_\d+$/ } $cgi->param) {
+  if ( length($cgi->param($param)) ) {
+    if ( $cgi->param($param) == 1 ) {
+      $changes{$param} = 1;
+    } else {
+      $changes{$param} = '';
+    }
+  }
+}
+
+my $error;
+foreach my $pkgpart (@pkgparts) {
+  my $part_pkg = FS::part_pkg->by_key($pkgpart);
+  my %options = ( $part_pkg->options, %changes );
+  $error ||= $part_pkg->replace( options => \%options );
+}
+</%init>
diff --git a/httemplate/elements/checkbox-tristate.html b/httemplate/elements/checkbox-tristate.html
new file mode 100644 (file)
index 0000000..4c26ed7
--- /dev/null
@@ -0,0 +1,78 @@
+<%doc>
+A tristate checkbox (with three values: true, false, and null).
+Internally, this creates a checkbox, coupled via javascript to a hidden
+field that actually contains the value.  For now, the only values these
+can have are 1, 0, and empty.  Clicking the checkbox cycles between them.
+</%doc>
+<%shared>
+my $init = 0;
+</%shared>
+% if ( !$init ) {
+%   $init = 1;
+<SCRIPT TYPE="text/javascript">
+function tristate_onclick() {
+  var checkbox = this;
+  var input = checkbox.input;
+  if ( input.value == "" ) {
+    input.value = "0";
+    checkbox.checked = false;
+    checkbox.indeterminate = false;
+  } else if ( input.value == "0" ) {
+    input.value = "1";
+    checkbox.checked = true;
+    checkbox.indeterminate = false;
+  } else if ( input.value == "1" ) {
+    input.value = "";
+    checkbox.checked = true;
+    checkbox.indeterminate = true
+  }
+}
+
+var tristates = [];
+var tristate_boxes = [];
+window.onload = function() { // don't do this until all of the checkboxes exist
+%#  tristates = document.getElementsByClassName('tristate'); # curse you, IE8
+  var all_inputs = document.getElementsByTagName('input');
+  for (var i=0; i < all_inputs.length; i++) {
+    if ( all_inputs[i].className == 'tristate' ) {
+      tristates.push(all_inputs[i]);
+    }
+  }
+  for (var i=0; i < tristates.length; i++) {
+    tristate_boxes[i] =
+      document.getElementById('checkbox_' + tristates[i].name);
+    // make sure they can find each other
+    tristate_boxes[i].input = tristates[i];
+    tristates[i].checkbox = tristate_boxes[i];
+    // set event handler
+    tristate_boxes[i].onclick = tristate_onclick;
+    // set initial value
+    if ( tristates[i].value == "" ) {
+      tristate_boxes[i].indeterminate = true
+    }
+    if ( tristates[i].value != "0" ) {
+      tristate_boxes[i].checked = true;
+    }
+  }
+};
+</SCRIPT>
+% } # end of $init
+<INPUT TYPE="hidden" NAME="<% $opt{field} %>"
+                     ID="<% $opt{id} %>"
+                     VALUE="<% $curr_value %>"
+                     CLASS="tristate">
+<INPUT TYPE="checkbox" ID="checkbox_<%$opt{field}%>" CLASS="partial">
+<%init>
+
+my %opt = @_;
+
+# might be useful but I'm not implementing it yet
+#my $onchange = $opt{'onchange'}
+#                 ? 'onChange="'. $opt{'onchange'}. '(this)"'
+#                 : '';
+
+$opt{'id'} ||= 'hidden_'.$opt{'field'};
+my $curr_value = $opt{curr_value};
+$curr_value = undef
+  unless $curr_value eq '0' or $curr_value eq '1';
+</%init>
index 6f5fcdf..04764c1 100755 (executable)
@@ -97,6 +97,11 @@ for(my $i=0; $i < scalar(@part2b_row_option); $i++) {
     &FS::Report::FCC_477::save_fcc477map("part2b_row_option_$i",$part2b_row_option[$i]);
 }
 
+my $part5_report_option = $cgi->param('part5_report_option');
+if ( $part5_report_option ) {
+  FS::Report::FCC_477::save_fcc477map('part5_report_option', $part5_report_option);
+}
+
 my $url_mangler = sub {
   my $part = shift;
   my $url = $cgi->url('-path_info' => 1, '-full' => 1);
index 66f3a86..666032d 100755 (executable)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                   'html_init'        => $html_init,
                   'name'             => 'lines',
                   'query'            => $query,
@@ -11,8 +11,8 @@
                   'xml_elements'     => [ @xml_elements ],
                   'xml_omit_empty'   => 1,
                   'fields'           => [  @fields ],
-              )
-%>
+              
+&>
 <%init>
 
 my $curuser = $FS::CurrentUser::CurrentUser;
index f5c2bc2..ebf081c 100755 (executable)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                   'html_init'        => $html_init,
                   'name'             => 'lines',
                   'query'            => 'SELECT 1',
@@ -28,8 +28,8 @@
                                           sub { $total_percentage },
                                           sub { $above_200_percentage },
                                         ],
-              )
-%>
+              
+&>
 <%init>
 
 my $curuser = $FS::CurrentUser::CurrentUser;
index d2cc8c3..6a53229 100755 (executable)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                   'html_init'        => $html_init,
                   'name'             => 'lines',
                   'query'            => $query,
@@ -10,8 +10,8 @@
                   'header'           => [ @headers ],
                   'xml_elements'     => [ @xml_elements ],
                   'fields'           => [ @fields ],
-              )
-%>
+              
+&>
 <%init>
 
 my $curuser = $FS::CurrentUser::CurrentUser;
index 2fd5119..2106a44 100755 (executable)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                   'html_init'         => $html_init,
                   'name'              => 'zip code',
                   'query'             => $sql_query,
@@ -12,8 +12,8 @@
                   'url'               => $opt{url} || '',
                   'really_disable_download'  => 1,
 
-              )
-%>
+              
+&>
 <%init>
 
 my $curuser = $FS::CurrentUser::CurrentUser;
@@ -32,8 +32,8 @@ for ( qw(agentnum magic state) ) {
 }
 $search_hash{'country'} = 'US';
 $search_hash{'classnum'} = [ $cgi->param('classnum') ];
-$search_hash{report_option} = $cgi->param('partv_report_option')
-  if $cgi->param('partv_report_option');
+$search_hash{report_option} = $cgi->param('part5_report_option')
+  if $cgi->param('part5_report_option');
 
 my $sql_query = FS::cust_pkg->search( { %search_hash,
                                         'fcc_line'    => 1,
index 8425c4b..59a6fb5 100755 (executable)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                   'html_init'       => '<H2>Part VI</H2>',
                   'html_foot'       => $html_foot,
                   'name'            => 'regions',
@@ -24,8 +24,8 @@
                   'url'             => $opt{url} || '',
                   'xml_row_element' => 'Datarow',
                   'really_disable_download' => 1,
-              )
-%>
+              
+&>
 <%init>
 
 my $curuser = $FS::CurrentUser::CurrentUser;
index ac65371..015aca4 100644 (file)
@@ -1,4 +1,4 @@
-<% include('elements/search.html',
+<& elements/search.html,
      'title'         => 'Inventory summary per agent',
      'name_singular' => 'agent',
      'query'         => { 'table'     => 'agent',
@@ -10,8 +10,7 @@
                         " AND $agentnums_sql",
      'header'        => \@header,
      'fields'        => \@fields,
-   )
-%>
+&>
 <%init>
 
 die "access denied"
index b6676f2..b740bdc 100755 (executable)
@@ -26,7 +26,7 @@ function start() {
 %     -expires => '-1d',
 %   );
 %   $r->headers_out->add( 'Set-Cookie' => $cookie->as_string );
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'         => 'Invoice Batches',
                 'name_singular' => 'batch',
                 'query'         => { 'table'     => 'bill_batch',
@@ -67,9 +67,7 @@ function start() {
                  'agent_pos' => 1,
                  'html_foot' => include('.foot'),
 
-      )
-
-%>
+&>
 %}
 <%def .foot>
 <SCRIPT type="text/javascript">
index d0d7292..1b4604b 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                'title' => $title,
                'name'  => 'call detail records',
                'query' => $query,
@@ -9,27 +9,9 @@
                'fields' => \@fields,
                'links' => \@links,
                'html_form'   => qq!<FORM NAME="cdrForm" ACTION="$p/misc/cdr.cgi" METHOD="POST">!,
-               #false laziness w/queue.html
-               'html_foot' => sub {
-                                if ( $areboxes ) {
-                                  '<BR><INPUT TYPE="button" VALUE="select all" onClick="setAll(true)">'.
-                                  '<INPUT TYPE="button" VALUE="unselect all" onClick="setAll(false)">'.
-                                  qq!<BR><INPUT TYPE="submit" NAME="action" VALUE="reprocess selected" onClick="return confirm('Are you sure you want to reprocess the selected CDRs?')">!.
-                                  qq!<INPUT TYPE="submit" NAME="action" VALUE="delete selected" onClick="return confirm('Are you sure you want to delete the selected CDRs?')"><BR>!.
-                                  '<SCRIPT TYPE="text/javascript">'.
-                                  '  function setAll(setTo) { '.
-                                  '    theForm = document.cdrForm;'.
-                                  '    for (i=0,n=theForm.elements.length;i<n;i++)'.
-                                  '      if (theForm.elements[i].name.indexOf("acctid") != -1)'.
-                                  '        theForm.elements[i].checked = setTo;'.
-                                  '  }'.
-                                  '</SCRIPT>';
-                                } else {
-                                  '';
-                                }
-                              },
+               'html_foot' => $html_foot,
              )
-%>
+&>
 <%init>
 
 die "access denied"
@@ -44,8 +26,6 @@ my $totalminutes_sub = sub {
 
 my $conf = new FS::Conf;
 
-my $areboxes = 0;
-
 my $title = 'Call Detail Records';
 my $hashref = {};
 
@@ -355,7 +335,6 @@ my %links = (
 @fields = map { exists($fields{$_}) ? $fields{$_} : $_ } @fields;
 unshift @fields, sub {
                        return '' unless $edit_data;
-                       $areboxes = 1;
                        my $cdr = shift;
                        my $acctid = $cdr->acctid;
                        qq!<INPUT NAME="acctid$acctid" TYPE="checkbox" VALUE="1">!;
@@ -409,4 +388,14 @@ if ( $topmode ) {
     $nototalminutes = 1;
 }
 
+my $html_foot = include('/search/elements/checkbox-foot.html',
+  actions => [
+    { submit  => "reprocess selected",
+      name    => "action",
+      confirm => "Are you sure you want to reprocess the selected CDRs?" },
+    { submit  => "delete selected",
+      name    => "action",
+      confirm => "Are you sure you want to delete the selected CDRs?" },
+  ]
+);
 </%init>
index 00f7cd5..9fb533a 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'       => $title,
                  'html_init'   => $html_init,
                  'menubar'     => $menubar,
@@ -60,8 +60,8 @@
                               '',
                               FS::UI::Web::cust_styles(),
                             ],
-             )
-%>
+             
+&>
 <%init>
 
 my $curuser = $FS::CurrentUser::CurrentUser;
index 871227a..0b64e65 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                 'title'       => $title,
                 'name'        => 'net payments',
                 'query'       => $sql_query,
@@ -71,8 +71,8 @@
                              '',
                              FS::UI::Web::cust_styles(),
                            ],
-          )
-%>
+          
+&>
 <%init>
 
 die "access denied"
index 4225e91..f598341 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'       => 'Discounts',
                  'name'        => 'discounts',
                  'query'       => $query,
@@ -68,8 +68,8 @@
                               '',
                               FS::UI::Web::cust_styles(),
                             ],
-           )
-%>
+           
+&>
 <%init>
 
 #a little false laziness below w/cust_bill_pkg.cgi
index 7e11ee6..88f897d 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                 'title'       => $title,
                 'name'        => 'net credits',
                 'query'       => $sql_query,
@@ -64,8 +64,8 @@
                              '',
                              FS::UI::Web::cust_styles(),
                            ],
-          )
-%>
+          
+&>
 <%init>
 
 die "access denied"
index 26da106..b0dbc70 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
               'title'         => 'Credit application detail', #to line item
               'name_singular' => 'credit application',
               'query'         => $query,
@@ -96,8 +96,8 @@
                               '',
                               FS::UI::Web::cust_styles(),
                           ],
-           )
-%>
+           
+&>
 <%init>
 
 #LOTS of false laziness below w/cust_bill_pkg.cgi
index 5d89ebe..1504f0f 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                 'title'       => $title,
                 'name'        => 'net refunds',
                 'query'       => $sql_query,
@@ -57,8 +57,8 @@
                              '',
                              FS::UI::Web::cust_styles(),
                            ],
-          )
-%>
+          
+&>
 <%init>
 
 die "access denied"
index bd6ba69..bfc5f43 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'       => $title,
                  'html_init'   => $html_init,
                  'menubar'     => $menubar,
@@ -62,8 +62,7 @@
                               #'',
                               FS::UI::Web::cust_styles(),
                             ],
-             )
-%>
+&>
 <%once>
 
 my $status_sub = sub { 
index 08800d4..f5f8c8f 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'       => 'Zip code Search Results',
                  'name'        => 'zip codes',
                  'query'       => $sql_query,
@@ -6,8 +6,7 @@
                  'header'      => [ 'Zip code', 'Customers', ],
                  'fields'      => [ 0, 1 ],
                  'links'       => [ '', $link  ],
-             )
-%>
+&>
 <%init>
 
 die "access denied"
index 830a6c6..dde5461 100755 (executable)
@@ -1,4 +1,4 @@
-<% include('elements/search.html',
+<& elements/search.html,
               'title'       => 'Batch payment details',
               'name'        => 'batch details',
              'query'       => $sql_query,
@@ -41,8 +41,7 @@
               'link_onclicks' => [ ('') x 8,
                                    $sub_receipt
                                  ],
-      )
-%>
+&>
 <%init>
 
 my $conf = new FS::Conf;
index bd28da8..23af180 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                   'title'       => 'Package discounts', 
                   'name'        => 'discounts',
                   'query'       => $query,
@@ -50,8 +50,8 @@
                                      '',
                                      FS::UI::Web::cust_styles(),
                                    ],
-           )
-%>
+           
+&>
 <%init>
 
 die "access denied"
index 9c5b32f..cdc7035 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
               'title'       => $part_svc->svc.' services in package #'.$pkgnum,
              'name'        => 'services',
               'html_form'   => $html_form,
@@ -30,8 +30,8 @@
                            ('')x4,
                          ],
               'html_foot' => sub { $areboxes ? $html_foot : '' }
-          )
-%>
+          
+&>
 <%init>
 
 die "access denied"
index 9254765..6125a1c 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
               'title'         => $title,
               'name_singular' => 'tax adjustment',
               'query'         => $query,
@@ -12,9 +12,8 @@
                                        },
                                  ],
               'links'         => [ '', '', '', $ilink ],
-          )
-%>
-
+          
+&>
 <%init>
 
 die "access denied"
index f075d19..005d77c 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'       => 'Legacy tax exemptions',
                  'name'        => 'legacy tax exemptions',
                  'query'       => $query,
@@ -46,8 +46,8 @@
                               '',
                               FS::UI::Web::cust_styles(),
                             ],
-           )
-%>
+           
+&>
 <%init>
 
 my $join_cust = FS::UI::Web::join_cust_main('cust_tax_exempt');
index 7e1a239..40b9ed7 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'       => 'Tax exemptions',
                  'name'        => 'tax exemptions',
                  'query'       => $query,
@@ -77,8 +77,7 @@
                               '',
                               FS::UI::Web::cust_styles(),
                             ],
-           )
-%>
+&>
 <%once>
 
 my $join_cust = "
diff --git a/httemplate/search/elements/checkbox-foot.html b/httemplate/search/elements/checkbox-foot.html
new file mode 100644 (file)
index 0000000..be1caab
--- /dev/null
@@ -0,0 +1,86 @@
+<%doc>
+<& /elements/search.html,
+  # options...
+  html_foot => include('elements/checkbox-foot.html',
+                  actions => [
+                    { label   => 'Edit selected packages',
+                      action  => 'popup_package_edit()',
+                    },
+                    { submit  => 'Delete selected packages',
+                      confirm => 'Really delete these packages?'
+                    },
+                  ],
+                  filter        => '.name = "pkgpart"', # see below
+               ),
+&>
+
+This creates a footer for a search page containing a column of checkboxes.
+Typically this is used to select several items from the search result and 
+apply some change to all of them at once.  The footer always provides 
+"select all" and "unselect all" buttons.
+
+"actions" is an arrayref of action buttons to show.  Each element of the
+array is a hashref of either:
+
+- "submit" and, optionally, "confirm".  Creates a submit button.  The value 
+of "submit" becomes the "value" property of the button (and thus its label).
+If "confirm" is specified, the button will have an onclick handler that 
+displays the value of "confirm" in a popup message box and asks the user to 
+confirm the choice.
+
+- "onclick" and "label".  Creates a non-submit button that executes the 
+Javascript code in "onclick".  "label" is used as the text of the button.
+
+If you want only a single action, you can forget the arrayref-of-hashrefs
+business and just put "submit" and "confirm" (or "onclick" and "label") 
+elements in the argument list.
+
+"filter" is a javascript expression to limit which checkboxes are included in
+the "select/unselect all" actions.  By default, any input with type="checkbox"
+will be included.  If this option is given, it will be evaluated with the 
+HTML node in a variable named "obj".  The expression should return true or
+false.
+
+</%doc>
+<DIV ID="checkbox_footer" STYLE="display:block">
+<INPUT TYPE="button" VALUE="<% emt('select all') %>" onclick="setAll(true)">
+<INPUT TYPE="button" VALUE="<% emt('unselect all') %>" onclick="setAll(false)">
+<BR>
+% foreach my $action (@$actions) {
+%   if ( $action->{onclick} ) {
+<INPUT TYPE="button" <% $action->{name} %> onclick="<% $opt{onclick} %>"\
+  VALUE="<% $action->{label} |h%>">
+%   } elsif ( $action->{submit} ) {
+<INPUT TYPE="submit" <% $action->{name} %> <% $action->{confirm} %>\
+  VALUE="<% $action->{submit} |h%>">
+%   } # else do nothing
+% } #foreach
+</DIV>
+<SCRIPT>
+var checkboxes = [];
+var inputs = document.getElementsByTagName('input');
+for (var i = 0; i < inputs.length; i++) {
+  var obj = inputs[i];
+  if ( obj.type == "checkbox" && <% $filter %> ) {
+    checkboxes.push(obj);
+  }
+}
+%# avoid the need for "$areboxes" late-evaluation hackery
+if ( checkboxes.length == 0 ) {
+  document.getElementById('checkbox_footer').style.display = 'none';
+}
+function setAll(setTo) {
+  for (var i = 0; i < checkboxes.length; i++) {
+    checkboxes[i].checked = setTo;
+  }
+}
+</SCRIPT>
+<%init>
+my %opt = @_;
+my $actions = $opt{'actions'} || [ \%opt ];
+foreach (@$actions) {
+  $_->{confirm} &&= qq!onclick="return confirm('! . $_->{confirm} . qq!')"!;
+  $_->{name} &&= qq!NAME="! . $_->{name} . qq!"!;
+}
+my $filter = $opt{filter} || 'true';
+</%init>
index 64722ff..0e4251f 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'       => $title,
 
                  'menubar'     => [ 'View inventory classes' =>
@@ -87,8 +87,8 @@
 <INPUT TYPE="hidden" NAME="classnum" VALUE="$classnum">
 <INPUT TYPE="hidden" NAME="avail"    VALUE="! .$cgi->param('avail') . '">', #'
                   'html_foot' => $sub_foot,
-             )
-%>
+             
+&>
 <%init>
 
 my $curuser = $FS::CurrentUser::CurrentUser;
index ee395f4..a678d45 100644 (file)
@@ -1,4 +1,4 @@
-<% include('elements/search.html',
+<& elements/search.html,
              'title'         => $title,
              'name_singular' => 'member',
              'query'         => $query,
@@ -6,8 +6,7 @@
              'header'        => [ 'Email address' ],
              'fields'        => [ $email_sub, ], #just this one for now
              'html_init'     => $html_init,
-          )
-%>
+&>
 <%init>
 
 #XXX ACL:
index 57da9d4..2178346 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
               'title'        => $title,
               'name_singular' => $name,
               'header'       => \@header,
@@ -14,8 +14,8 @@
               'links'        => \@links,
               'align'        => $align,
               'sort_fields'  => [],
-          )
-%>
+          
+&>
 <%init>
 
 #this is about reports about packages definitions (starting w/commission ones)
index aeaa012..34e9fd7 100755 (executable)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'         => 'Payment Batches',
                 'name_singular' => 'batch',
                 'query'         => { 'table'     => 'pay_batch',
                                    ],
                  'html_init'     => $html_init,
                  'html_foot'     => include('.upload_incoming'),
-      )
-%>
+&>
 <%def .upload_incoming>
 % if ( FS::payment_gateway->count("gateway_namespace = 'Business::BatchPayment' AND disabled IS NULL") > 0 ) { 
 <& /elements/form-file_upload.html,
index 312c84e..faf3544 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
               'title'         => 'Phone Number (DID) Search Results',
               'name_singular' => 'phone number',
               'query'         => {
@@ -81,8 +81,8 @@
                            FS::UI::Web::cust_styles(),
                           '',
                          ],
-      )
-%>
+      
+&>
 <%init>
 
 die "access denied"
index 03d2154..b3efdbd 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
               'title'         => 'LATA Search Results',
               'name_singular' => 'LATA',
               'query'         => {
@@ -72,8 +72,8 @@
                            '',
                            '',
                          ],
-      )
-%>
+      
+&>
 <%init>
 
 die "access denied"
index 3640351..7566e65 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'       => 'Unused Prepaid Cards'.
                                   ($agent ? ' for '. $agent->agent : ''),
                  'menubar'     => [
@@ -47,8 +47,8 @@
                          $agent ? [ "${p}edit/agent.cgi?", 'agentnum' ] : '';
                        },
                  ],
-      )
-%>
+      
+&>
 <%init>
 
 die "access denied"
index 328d120..ab37b90 100644 (file)
@@ -1,4 +1,4 @@
-<% include('elements/search.html',
+<& elements/search.html,
              'title'         => 'Prospect Search Results',
              'name_singular' => 'prospect',
              'query'         => $query,
@@ -23,8 +23,7 @@
                                   '', #link to contact edit???
                                 ],
              'agent_virt'    => 1,
-          )
-%>
+&>
 <%init>
 
 die "access denied"
index 7133ef0..7b718e4 100755 (executable)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'         => 'Qualifications',
                 'name_singular' => 'qualification',
                 'query'         => { 'table'     => 'qual',
@@ -51,8 +51,8 @@
                                      '',
                                      '',
                                    ],
-      )
-%>
+      
+&>
 <%init>
 
 die "access denied"
index 1c12470..141c535 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'       => 'Job Queue',
                  'name'        => 'jobs',
                 'html_form'   => qq!<FORM NAME="jobForm" ACTION="$p/misc/queue.cgi" METHOD="POST">!,
                                     '';
                                   }
                                 },
-             )
-
-%>
+             
+&>
 <%init>
 
 die "access denied"
index f7d6d20..42211e5 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'       => 'Unused Registration Codes for '.
                                   $agent->agent,
                  'name'        => 'registration codes',
@@ -23,8 +23,8 @@
                    #$plink,
                    '',
                  ],
-      )
-%>
+      
+&>
 <%init>
 
 die "access denied"
index f593a94..b842b1f 100755 (executable)
                      'table'        => 'part_pkg_report_option',
                      'name_col'     => 'name',
                      'hashref'      => { 'disabled' => '' },
-                     'element_name' => 'partv_report_option',
+                     'element_name' => 'part5_report_option',
+                     'curr_value'   =>
+                            FS::Report::FCC_477::restore_fcc477map("part5_report_option"),
                  )
             %>
         </TD>
index 1ed5a38..8603ef2 100644 (file)
@@ -1,4 +1,4 @@
-<% include('elements/search.html',
+<& elements/search.html,
              'title'         => 'Time worked summary',
              'name_singular' => 'ticket',
              'query'         => $query,
@@ -31,8 +31,7 @@
                                   '',
                                   '',
                                 ],
-          )
-%>
+&>
 <%once>
 
 my $format_seconds_sub = sub {
index 1ae607b..eb250fb 100644 (file)
@@ -1,4 +1,4 @@
-<% include('elements/search.html',
+<& elements/search.html,
              'title'         => 'Time worked',
              'name_singular' => 'transaction',
              'query'         => $query,
@@ -29,8 +29,7 @@
                                   '',
                                   '',
                                 ],
-          )
-%>
+&>
 <%once>
 
 my $format_seconds_sub = sub {
index bf54469..71aa006 100644 (file)
@@ -1,9 +1,9 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                'title' => 'Query Results',
                'name'  => 'rows',
                'query' => "SELECT $sql",
-          )
-%>
+          
+&>
 <%init>
 
 die "access denied"
index ee62e90..e56ec39 100755 (executable)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
               'title'       => 'Broadband Search Results',
               'name'        => 'broadband services',
               'html_init'   => $html_init,
@@ -49,8 +49,8 @@
                                  '',
                                  FS::UI::Web::cust_styles(),
                                ],
-          )
-%>
+          
+&>
 <%init>
 
 die "access denied" unless
index a2e56d2..1578aa1 100755 (executable)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'       => 'Dish Network Search Results',
                  'name'        => 'services',
                  'query'       => $sql_query,
@@ -34,8 +34,8 @@
                               '',
                               FS::UI::Web::cust_styles(),
                             ],
-             )
-%>
+             
+&>
 <%init>
 
 die "access denied"
index 508d889..56cfa30 100755 (executable)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'             => "Domain Search Results",
                  'name'              => 'domains',
                  'query'             => $sql_query,
@@ -34,8 +34,8 @@
                               '',
                               FS::UI::Web::cust_styles(),
                             ],
-              )
-%>
+              
+&>
 <%init>
 
 die "access denied"
index 7d57697..1909987 100755 (executable)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'             => 'External service search results',
                  'name'              => 'external services',
                  'query'             => $sql_query,
@@ -40,9 +40,8 @@
                               '',
                               FS::UI::Web::cust_styles(),
                             ],
-          )
-%>
-
+          
+&>
 <%init>
 
 die "access denied"
index eda790a..6a23bb3 100755 (executable)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'             => "Mail forward Search Results",
                  'name'              => 'mail forwards',
                  'query'             => $sql_query,
@@ -39,8 +39,8 @@
                               '',
                               FS::UI::Web::cust_styles(),
                             ],
-             )
-%>
+             
+&>
 <%init>
 
 die "access denied"
index 085d750..28aa132 100644 (file)
@@ -1,4 +1,4 @@
-<% include('elements/search.html',
+<& elements/search.html,
             'title'             => 'Hardware service search results',
             'name'              => 'installations',
             'query'             => $sql_query,
@@ -34,8 +34,7 @@
                                       FS::UI::Web::cust_colors() ],
             'style'             => [ $svc_cancel_style, ('') x 7,
                                       FS::UI::Web::cust_styles() ],
-            )
-%>
+&>
 <%init>
 
 die "access denied"
index 65aa1ae..cc4aa60 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'             => "Phone number search results",
                  'name'              => 'phone numbers',
                  'query'             => $sql_query,
@@ -46,8 +46,8 @@
                               ( map '', @header ),
                               FS::UI::Web::cust_styles(),
                             ],
-              )
-%>
+              
+&>
 <%init>
 
 die "access denied"
index 5132f94..eefe893 100755 (executable)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'       => 'Virtual Host Search Results',
                  'name'        => 'virtual hosts',
                  'query'       => $sql_query,
@@ -45,8 +45,8 @@
                               '',
                               FS::UI::Web::cust_styles(),
                             ],
-             )
-%>
+             
+&>
 <%init>
 
 die "access denied"
index bbfd033..fa4b895 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
                  'title'       => 'Time Worked',
                  'name'        => 'time',
                 'html_form'   => qq!<FORM NAME="timeForm" ACTION="${p}misc/timeworked.html" METHOD="POST">!,
@@ -33,9 +33,8 @@
                    '',
                  ],
                  'html_foot' => $html_foot,
-             )
-
-%>
+             
+&>
 <%init>
 
 die "access denied"
index 89d5f2d..a7791ba 100644 (file)
@@ -1,4 +1,4 @@
-<% include( 'elements/search.html',
+<& elements/search.html,
               'title'         => 'Unprovisioned Service Search Results',
               'name' => 'packages with unprovisioned services',
               'query'         => {
@@ -54,8 +54,8 @@
                              '',
                               FS::UI::Web::cust_styles(),
                            ],
-      )
-%>
+      
+&>
 <%init>
 
 die "access denied"