[freeside-commits] branch master updated. bda35ac2da6481a1e4f5414c0b7caf2b90d26fcf

Jonathan Prykop jonathan at 420.am
Tue Jun 9 14:25:07 PDT 2015


The branch, master has been updated
       via  bda35ac2da6481a1e4f5414c0b7caf2b90d26fcf (commit)
       via  536c3720cb3820d7806d2e672463b5b3d06be078 (commit)
      from  e6cf968df114ffc735b1daf74f6259a81859c3f9 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit bda35ac2da6481a1e4f5414c0b7caf2b90d26fcf
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Tue Jun 9 16:23:58 2015 -0500

    RT#33484: Delete lines from quick payment entry

diff --git a/httemplate/elements/customer-table.html b/httemplate/elements/customer-table.html
index 83abad0..090623f 100644
--- a/httemplate/elements/customer-table.html
+++ b/httemplate/elements/customer-table.html
@@ -128,8 +128,8 @@ Example:
       return;
     }
 
-    if ( ( <% $opt{prefix} %>rownum - searchrow ) == 1 ) {
-      <% $opt{prefix} %>addRow();
+    if ( document.getElementById('row'+searchrow).emptyrow ) {
+      <% $opt{prefix} %>newEmptyRow(searchrow);
     }
     var customer = document.getElementById('customer'+searchrow);
     customer.value = 'searching...';
@@ -179,8 +179,8 @@ Example:
       return;
     }
 
-    if ( ( <% $opt{prefix} %>rownum - searchrow ) == 1 ) {
-      <% $opt{prefix} %>addRow();
+    if ( document.getElementById('row'+searchrow).emptyrow ) {
+      <% $opt{prefix} %>newEmptyRow(searchrow);
     }
 
     var customer_obj = document.getElementById('customer'+searchrow);
@@ -262,8 +262,8 @@ Example:
       return;
     }
 
-    if ( ( <% $opt{prefix} %>rownum - searchrow ) == 1 ) {
-      <% $opt{prefix} %>addRow();
+    if ( document.getElementById('row'+searchrow).emptyrow ) {
+      <% $opt{prefix} %>newEmptyRow(searchrow);
     }
     
     var invnum = document.getElementById('invnum'+searchrow);
@@ -385,253 +385,101 @@ Example:
   }
 
   function update_num_open(rownum, newval) {
+    document.getElementById('num_open'+rownum).value = newval;
     num_open_invoices[rownum] = newval;
   }
 
+  function <% $opt{prefix} %>updateTotalRow () {
+    if ( <% $opt{prefix} %>totalrows == 1 ) {
+      <% $opt{prefix} %>total_el.innerHTML =
+        'Total '
+          + <% $opt{prefix} %>totalrows
+          + ' <% $opt{name_singular} || 'customer' %>';
+    } else {
+      <% $opt{prefix} %>total_el.innerHTML =
+        'Total '
+          + <% $opt{prefix} %>totalrows
+          + ' <% PL($opt{name_singular} || 'customer') %>';
+    }
+  }
 
-</SCRIPT>
+  var <% $opt{prefix} %>total_el, <% $opt{prefix} %>rownum, <% $opt{prefix} %>totalrows, <% $opt{prefix} %>allrows;
+
+  function <% $opt{prefix} %>addDeleteButton (searchrow) {
+    var td_delete = document.getElementById('delete'+searchrow);
+    var button_delete = document.createElement('INPUT');
+    button_delete.setAttribute('rownum', searchrow);
+    button_delete.setAttribute('type', 'button');
+    button_delete.setAttribute('value', 'X');
+    button_delete.onclick = <% $opt{prefix} %>deleteRow;
+    button_delete.style.color = '#ff0000';
+    button_delete.style.fontWeight = 'bold';
+    button_delete.style.paddingLeft = '2px';
+    button_delete.style.paddingRight = '2px';
+    td_delete.appendChild(button_delete);
+  }
 
-<TABLE ID="<% $opt{prefix} %>OneTrueTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+  function <% $opt{prefix} %>newEmptyRow (searchrow) {
+    // add delete button to current row
+    <% $opt{prefix} %>addDeleteButton(searchrow);
+    // mark current row as non-empty
+    var oldemptyrow = document.getElementById('row'+searchrow);
+    oldemptyrow.emptyrow = false;
+    // update totalrows
+    <% $opt{prefix} %>totalrows++
+    <% $opt{prefix} %>updateTotalRow();
+    // add a new empty row
+    <% $opt{prefix} %>addRow();
+  }
 
-<TR>
-  <TH>Inv #</TH>
-  <TH>Cust #</TH>
-  <TH>Status</TH>
-  <TH>Customer</TH>
-  <TH>Balance</TH>
-% foreach my $header ( @{$opt{header}} ) {
-    <TH><% $header %></TH>
+  function <% $opt{prefix} %>deleteRow() {
+    var thisrownum = this.getAttribute('rownum');
+% if ( $opt{delete_row_callback} ) {
+    // callback
+    <% $opt{delete_row_callback} %>(thisrownum,'<% $opt{prefix} %>');
 % }
-</TR>
-% my $row = 0;
-% for ( $row = 0; exists($param->{"custnum$row"}); $row++ ) { 
-
-    <TR id="row<%$row%>" rownum="<%$row%>">
-      <TD>
-	<INPUT TYPE      = "text"
-               NAME      = "invnum<% $row %>"
-               ID        = "invnum<% $row %>"
-               SIZE      = 8
-               MAXLENGTH = 12
-               STYLE     = "text-align:right;"
-               VALUE     = "<% $param->{"invnum$row"} %>"
-               rownum    = "<% $row %>"
-        >
-        <SCRIPT TYPE="text/javascript">
-          var invnum_input<% $row %> = document.getElementById("invnum<% $row %>");
-          invnum_input<% $row %>.onfocus = clearhint_invnum;
-          invnum_input<% $row %>.onchange = <% $opt{prefix} %>search_invnum;
-        </SCRIPT>
-      </TD>
-
-      <TD>
-        <INPUT TYPE      = "text"
-               NAME      = "display_custnum<% $row %>"
-               ID        = "display_custnum<% $row %>"
-               SIZE      = 8
-               MAXLENGTH = 12
-               STYLE     = "text-align:right;"
-               VALUE     = "<% $param->{"display_custnum$row"} %>"
-               rownum    = "<% $row %>"
-        >
-        <INPUT TYPE      = "hidden"
-               NAME      = "custnum<% $row %>"
-               ID        = "custnum<% $row %>"
-               VALUE     = "<% $param->{"custnum$row"} %>"
-               rownum    = "<% $row %>"
-        >
-        <SCRIPT TYPE="text/javascript">
-          var custnum_input<% $row %> = document.getElementById("display_custnum<% $row %>");
-          custnum_input<% $row %>.onfocus = clearhint_custnum;
-          custnum_input<% $row %>.onchange = <% $opt{prefix} %>search_custnum;
-        </SCRIPT>
-      </TD>
-      
-      <TD STYLE="text-align: center">
-        <SPAN
-               ID        = "status<% $row %>_text"
-               rownum    = "<% $row %>"
-               STYLE     = "font-weight: bold;
-                            color: <%$param->{"statuscolor$row"} || '#000000'%>"
-
-        ><% $param->{"status$row"} %></SPAN>
-        <INPUT TYPE      = "hidden"
-               NAME      = "status<% $row %>"
-               ID        = "status<% $row %>"
-               VALUE     = "<% $param->{"status$row"} %>"
-               rownum    = "<% $row %>"
-        >
-        <INPUT TYPE      = "hidden"
-               NAME      = "statuscolor<% $row %>"
-               ID        = "statuscolor<% $row %>"
-               VALUE     = "<% $param->{"statuscolor$row"} %>"
-               rownum    = "<% $row %>"
-        >
-      </TD>
-
-      <TD>
-        <INPUT TYPE="text" NAME="customer<% $row %>" ID="customer<% $row %>" SIZE=64 VALUE="<% $param->{"customer$row"} %>" rownum="<% $row %>">
-          <SCRIPT TYPE="text/javascript">
-            var customer_input<% $row %> = document.getElementById("customer<% $row %>");
-            customer_input<% $row %>.onfocus = clearhint_customer;
-            customer_input<% $row %>.onclick = clearhint_customer;
-            customer_input<% $row %>.onchange = <% $opt{prefix} %>search_customer;
-          </SCRIPT>
-        <SELECT NAME="cust_select<% $row %>" ID="cust_select<% $row %>" rownum="<% $row %>" STYLE="color:#ff0000; display:none">
-        </SELECT>
-          <SCRIPT TYPE="text/javascript">
-            var customer_select<% $row %> = document.getElementById("cust_select<% $row %>");
-            customer_select<% $row %>.onchange = select_customer;
-          </SCRIPT>
-      </TD>
-
-      <TD STYLE="text-align:right">
-        <% $money_char %>
-        <SPAN 
-               ID        = "balance<% $row %>_text"
-               rownum    = "<% $row %>"
-        ><% $param->{"balance$row"} %></SPAN>
-         
-        <INPUT TYPE      = "hidden"
-               NAME      = "balance<% $row %>"
-               ID        = "balance<% $row %>"
-               VALUE     = "<% $param->{"balance$row"} %>"
-               rownum    = "<% $row %>"
-        >
-      </TD>
-
-%   my $col = 0;
-%   foreach my $field ( @{$opt{fields}} ) {
-%     my $value;
-%     if ( ref($field) eq 'CODE' ) {
-%       $value = &{$field}($row,$param);
-%     } else {
-%       $value = $param->{"$field$row"}; 
-%     }
-%     my $name  = (ref($field) eq 'CODE') ? "column${col}_$row" : "$field$row";
-%     my $align = $align{ $opt{align}->[$col] || 'l' };
-%     my $size  = $sizes->[$col]  || 10;
-%     my $color = $opt{color}->[$col];
-%     my $font = $color ? qq(<FONT COLOR="$color">) : '';
-%     my $onchange = '';
-%     if ( $opt{onchange}->[$col] ) {
-%       $onchange = 'onchange="'.$opt{onchange}->[$col].'"';
-%     }
-%     elsif ( $opt{footer}->[$col] eq '_TOTAL' ) {
-%       $total[$col] += $value;
-%       $onchange = $opt{prefix}. "calc_total$col();";
-%       $onchange = qq(onchange="$onchange" onkeyup="$onchange");
-%     }
-      <TD ALIGN="<% $align %>">
-%     my $type = $types->[$col] || 'text';
-%     if ($type eq 'text' or $type eq 'checkbox') {
-        <INPUT TYPE  = "<% $type %>"
-               NAME  = "<% $name %>"
-               ID    = "<% $name %>"
-               SIZE  = "<% $size %>"
-               STYLE = "text-align: <% $align %>;"
-               VALUE = "<% $value %>"
-               rownum    = "<% $row %>"
-               <% $onchange %>
-        >
-%     } elsif ($types->[$col] eq 'immutable') {
-        <% $font %><% $value %><% $font ? '</FONT>' : '' %>
-        <INPUT TYPE="hidden" ID="<% $name %>" NAME="<% $name %>" VALUE="<% $value %>" >
-%     } else {
-        Cannot represent unknown type: <% $types->[$col] %>
-%     }
-      </TD>
-%     $col++;
-%   }
-    </TR>
-% } 
-
-<TR id="row_total">
-  <TH COLSPAN=5 ID="<% $opt{'prefix'} %>_TOTAL_TOTAL">
-    Total <% $row ? $row-1 : 0 %>
-    <% PL($opt{name_singular} || 'customer', ( $row ? $row-1 : 0 ) ) %>
-  </TH>
-% my $col = 0;
-% foreach my $footer ( @{$opt{footer}} ) {
-%   my $align = $align{ $opt{'footer_align'}->[$col] || 'c' };
-%   if ($footer eq '_TOTAL' ) {
-%     my $id = $opt{'fields'}->[$col];
-%     $id = ref($id) ? "column${col}_TOTAL" : "${id}_TOTAL";
-      <TH ALIGN="<% $align %>" ID="<% $id %>"> <% sprintf('%.2f', $total[$col] ) %></TH>
-%   } else {
-      <TH ALIGN="<% $align %>"><% $footer %></TH>
-%   }
-%   $col++;
-% }
-</TR>
-
-</TABLE>
-
-<SCRIPT TYPE="text/javascript">
+    // remove the actual row
+    var thisrow = document.getElementById('row'+thisrownum);
+    thisrow.parentNode.removeChild(thisrow);
+    // remove row from tally of all rows
+    var newrows = [];
+    for (i = 0; i < allrows.length; i++) {
+      if (allrows[i] == thisrownum) continue;
+      newrows.push(allrows[i]);
+    }
+    allrows = newrows;
+    <% $opt{prefix} %>totalrows--; // should never be deleting empty rows
+    <% $opt{prefix} %>updateTotalRow();
+    // recalculate column totals, if any
 % my $col = 0;
 % foreach my $footer ( @{$opt{footer}} ) {
 %   if ($footer eq '_TOTAL' ) {
-%     my $name = $opt{fields}->[$col];
-%     $name = ref($name) ? "column$col" : $name;
-      var <% $opt{prefix}.$name %>_CACHE = new Array ();
-      var <% $opt{prefix} %>th_el = document.getElementById("<%$name%>_TOTAL");
-      function <% $opt{prefix} %>calc_total<% $col %>() {
-        var row = 0;
-        var total = 0;
-        for ( var row = 0;
-              
-              ( <% $opt{prefix}.$name%>_CACHE[row] =
-                                        <% $opt{prefix}.$name%>_CACHE[row]
-                                     || document.getElementById("<%$name%>"+row)
-              ) != null;
-              
-              row++
-            )
-        {
-          var value = <%$name%>_CACHE[row].value;
-          value = parseFloat(value);
-          if ( ! isNaN(value) ) {
-            total = total + value;
-          }
-        }
-        <% $opt{prefix} %>th_el.innerHTML = ' ' + total.toFixed(2);
-
-      }
+    <% $opt{prefix} %>calc_total<% $col %>()
 %   }
 %   $col++;
 % }
-</SCRIPT>
-
-<% include('/elements/xmlhttp.html',
-              'url'  => $p. 'misc/xmlhttp-cust_main-search.cgi',
-              'subs' => [qw( custnum_search smart_search invnum_search )],
-           )
-%>
-
-<SCRIPT TYPE="text/javascript">
-
-  var <% $opt{prefix} %>total_el =
-    document.getElementById("<% $opt{'prefix'} %>_TOTAL_TOTAL");
-
-  var <% $opt{prefix} %>rownum = <% $row %>;
+  }
 
-  function <% $opt{prefix} %>addRow() {
+  function <% $opt{prefix} %>addRow(values) {
 
     var table = document.getElementById('<% $opt{prefix} %>OneTrueTable');
     var tablebody = table.getElementsByTagName('tbody').item(0);
 
     var row = table.insertRow(table.rows.length - 1);
-    row.setAttribute('id', 'row'+rownum);
+    var thisrownum = values ? values.rownum : <% $opt{prefix} %>rownum;
+    row.setAttribute('id', 'row'+thisrownum);
+    row.emptyrow = values ? false : true;
     
     var invnum_cell = document.createElement('TD');
 
       var invnum_input = document.createElement('INPUT');
-      invnum_input.setAttribute('name', 'invnum'+<% $opt{prefix} %>rownum);
-      invnum_input.setAttribute('id',   'invnum'+<% $opt{prefix} %>rownum);
+      invnum_input.setAttribute('name', 'invnum'+thisrownum);
+      invnum_input.setAttribute('id',   'invnum'+thisrownum);
       invnum_input.style.textAlign = 'right';
       invnum_input.setAttribute('size', 8);
       invnum_input.setAttribute('maxlength', 12);
-      invnum_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+      invnum_input.setAttribute('rownum', thisrownum);
+      invnum_input.value = values ? values.invnum : '';
       invnum_input.onfocus = clearhint_invnum;
       invnum_input.onchange = <% $opt{prefix} %>search_invnum;
       invnum_cell.appendChild(invnum_input);
@@ -641,21 +489,23 @@ Example:
     var custnum_cell = document.createElement('TD');
 
       var display_custnum_input = document.createElement('INPUT');
-      display_custnum_input.setAttribute('name', 'display_custnum'+<% $opt{prefix} %>rownum);
-      display_custnum_input.setAttribute('id',   'display_custnum'+<% $opt{prefix} %>rownum);
+      display_custnum_input.setAttribute('name', 'display_custnum'+thisrownum);
+      display_custnum_input.setAttribute('id',   'display_custnum'+thisrownum);
       display_custnum_input.style.textAlign = 'right';
       display_custnum_input.setAttribute('size', 8);
       display_custnum_input.setAttribute('maxlength', 12);
-      display_custnum_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+      display_custnum_input.setAttribute('rownum', thisrownum);
+      display_custnum_input.value = values ? values.custnum : '';
       display_custnum_input.onfocus = clearhint_custnum;
       display_custnum_input.onchange = <% $opt{prefix} %>search_custnum;
       custnum_cell.appendChild(display_custnum_input);
 
       var custnum_input = document.createElement('INPUT');
       custnum_input.type = 'hidden';
-      custnum_input.setAttribute('name', 'custnum'+<% $opt{prefix} %>rownum);
-      custnum_input.setAttribute('id',   'custnum'+<% $opt{prefix} %>rownum);
-      custnum_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+      custnum_input.setAttribute('name', 'custnum'+thisrownum);
+      custnum_input.setAttribute('id',   'custnum'+thisrownum);
+      custnum_input.setAttribute('rownum', thisrownum);
+      custnum_input.value = values ? values.custnum : '';
       custnum_cell.appendChild(custnum_input);
 
     row.appendChild(custnum_cell);
@@ -664,23 +514,29 @@ Example:
       status_cell.style.textAlign = 'center';
         
       var status_span = document.createElement('SPAN');
-      status_span.setAttribute('id', 'status'+<% $opt{prefix} %>rownum+'_text');
+      status_span.setAttribute('id', 'status'+thisrownum+'_text');
       status_span.style.fontWeight = 'bold';
-      status_span.setAttribute('rownum', <% $opt{prefix} %>rownum);
+      status_span.style.color = values ? values.statuscolor : '';
+      status_span.setAttribute('rownum', thisrownum);
+      status_span.appendChild(
+        document.createTextNode(values ? values.status : '')
+      );
       status_cell.appendChild(status_span);
         
       var status_input = document.createElement('INPUT');
       status_input.setAttribute('type', 'hidden');
-      status_input.setAttribute('name', 'status'+<% $opt{prefix} %>rownum);
-      status_input.setAttribute('id',   'status'+<% $opt{prefix} %>rownum);
-      status_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+      status_input.setAttribute('name', 'status'+thisrownum);
+      status_input.setAttribute('id',   'status'+thisrownum);
+      status_input.setAttribute('rownum', thisrownum);
+      status_input.value = values ? values.status : '';
       status_cell.appendChild(status_input);
 
       var statuscolor_input = document.createElement('INPUT');
       statuscolor_input.setAttribute('type', 'hidden');
-      statuscolor_input.setAttribute('name', 'statuscolor'+<% $opt{prefix} %>rownum);
-      statuscolor_input.setAttribute('id',   'statuscolor'+<% $opt{prefix} %>rownum);
-      statuscolor_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+      statuscolor_input.setAttribute('name', 'statuscolor'+thisrownum);
+      statuscolor_input.setAttribute('id',   'statuscolor'+thisrownum);
+      statuscolor_input.setAttribute('rownum', thisrownum);
+      statuscolor_input.value = values ? values.statuscolor : '';
       status_cell.appendChild(statuscolor_input);
 
     row.appendChild(status_cell);
@@ -688,20 +544,21 @@ Example:
     var customer_cell = document.createElement('TD');
 
       var customer_input = document.createElement('INPUT');
-      customer_input.setAttribute('name', 'customer'+<% $opt{prefix} %>rownum);
-      customer_input.setAttribute('id',   'customer'+<% $opt{prefix} %>rownum);
+      customer_input.setAttribute('name', 'customer'+thisrownum);
+      customer_input.setAttribute('id',   'customer'+thisrownum);
       customer_input.setAttribute('size', 64);
       customer_input.setAttribute('value', '(last name or company)' );
-      customer_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+      customer_input.setAttribute('rownum', thisrownum);
+      customer_input.value = values ? values.customer : '';
       customer_input.onfocus = clearhint_customer;
       customer_input.onclick = clearhint_customer;
       customer_input.onchange = <% $opt{prefix} %>search_customer;
       customer_cell.appendChild(customer_input);
 
       var customer_select = document.createElement('SELECT');
-      customer_select.setAttribute('name', 'cust_select'+<% $opt{prefix} %>rownum);
-      customer_select.setAttribute('id',   'cust_select'+<% $opt{prefix} %>rownum);
-      customer_select.setAttribute('rownum', <% $opt{prefix} %>rownum);
+      customer_select.setAttribute('name', 'cust_select'+thisrownum);
+      customer_select.setAttribute('id',   'cust_select'+thisrownum);
+      customer_select.setAttribute('rownum', thisrownum);
       customer_select.style.color = '#ff0000';
       customer_select.style.display = 'none';
       customer_select.onchange = select_customer;
@@ -715,21 +572,29 @@ Example:
       balance_cell.appendChild(document.createTextNode('<%$money_char%>'));
 
       var balance_span = document.createElement('SPAN');
-      balance_span.setAttribute('id', 'balance'+<% $opt{prefix} %>rownum+'_text');
-      balance_span.setAttribute('rownum', <% $opt{prefix} %>rownum);
+      balance_span.setAttribute('id', 'balance'+thisrownum+'_text');
+      balance_span.setAttribute('rownum', thisrownum);
       balance_cell.appendChild(balance_span);
 
       balance_cell.appendChild(
-        document.createTextNode(String.fromCharCode(160)) // 
+        document.createTextNode(String.fromCharCode(160) + (values ? values.balance : '')) // 
       );
 
       var balance_input = document.createElement('INPUT');
       balance_input.setAttribute('type', 'hidden');
-      balance_input.setAttribute('name', 'balance'+<% $opt{prefix} %>rownum);
-      balance_input.setAttribute('id',   'balance'+<% $opt{prefix} %>rownum);
-      balance_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+      balance_input.setAttribute('name', 'balance'+thisrownum);
+      balance_input.setAttribute('id',   'balance'+thisrownum);
+      balance_input.setAttribute('rownum', thisrownum);
+      balance_input.value = values ? values.balance : '';
       balance_cell.appendChild(balance_input);
 
+      var num_open_input = document.createElement('INPUT');
+      num_open_input.setAttribute('type', 'hidden');
+      num_open_input.setAttribute('name', 'num_open'+thisrownum);
+      num_open_input.setAttribute('id',   'num_open'+thisrownum);
+      num_open_input.setAttribute('rownum', thisrownum);
+      balance_cell.appendChild(num_open_input);
+
     row.appendChild(balance_cell);
 
 %   my $col = 0;
@@ -739,29 +604,24 @@ Example:
       my_cell.setAttribute('align', '<% $align{ $opt{align}->[$col] || 'l' } %>');
 
 %     if ($types->[$col] eq 'immutable') {
-%       my $value;
-%       if ( ref($field) eq 'CODE' ) {
-%         $value = &{$field}($row,$param);
-%       } else {
-%         $value = $param->{"$field$row"}; 
-%       }
-        var my_text = document.createTextNode(<% $value |js_string %>);
+        var my_text = document.createTextNode(values ? values.<% $field %> : '');
         my_cell.appendChild(my_text);
 %     }
 
 %     my $name  = (ref($field) eq 'CODE') ? "column${col}_" : $field;
       var my_input = document.createElement('INPUT');
-      my_input.setAttribute('name', '<% $name %>'+<% $opt{prefix} %>rownum);
-      my_input.setAttribute('id',   '<% $name %>'+<% $opt{prefix} %>rownum);
+      my_input.setAttribute('name', '<% $name %>'+thisrownum);
+      my_input.setAttribute('id',   '<% $name %>'+thisrownum);
       my_input.style.textAlign = '<% $align{ $opt{align}->[$col] || 'l' } %>';
       my_input.setAttribute('size', <% $sizes->[$col] || 10 %>);
-      my_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+      my_input.setAttribute('rownum', thisrownum);
 %     if ( $types->[$col] eq 'immutable' ) {
-        my_input.setAttribute('type', 'hidden');
-%     }
-%     elsif ( $types->[$col] eq 'checkbox' ) {
-        my_input.setAttribute('type', 'checkbox');
+      my_input.setAttribute('type', 'hidden');
+%     } elsif ( $types->[$col] eq 'checkbox' ) {
+      my_input.setAttribute('type', 'checkbox');
+      my_input.checked = (values && values.<% $field %>) ? true : false;
 %     }
+      my_input.value = (values && values.<% $field %>) || '';
 %     if ( $opt{onchange}->[$col] ) {
         my_input.onchange   = <% $opt{onchange}->[$col] %>;
 %     }
@@ -776,38 +636,146 @@ Example:
 %     $col++;
 %   }
 
-    //update the total # of rows display
-    if ( <% $opt{prefix} %>rownum == 1 ) {
-      <% $opt{prefix} %>total_el.innerHTML =
-        'Total '
-          + <% $opt{prefix} %>rownum
-          + ' <% $opt{name_singular} || 'customer' %>';
-    } else {
-      <% $opt{prefix} %>total_el.innerHTML =
-        'Total '
-          + <% $opt{prefix} %>rownum
-          + ' <% PL($opt{name_singular} || 'customer') %>';
+    var td_delete = document.createElement('TD');
+    td_delete.setAttribute('id', 'delete'+thisrownum);
+    row.appendChild(td_delete);
+    if (values) {
+      <% $opt{prefix} %>addDeleteButton(thisrownum);
     }
 
+    update_num_open(thisrownum, (values ? values.num_open : '0'));
+
 % if ( $opt{add_row_callback} ) {
-    <% $opt{add_row_callback} %>(<% $opt{prefix} %>rownum,
-                                 '<% $opt{prefix} %>');
+    <% $opt{add_row_callback} %>(thisrownum,
+                                 '<% $opt{prefix} %>', values);
 % }
 
-    <% $opt{prefix} %>rownum++;
+    // update the total number of rows display
+    allrows.push(thisrownum);
+    if (values) <% $opt{prefix} %>totalrows++;
+    <% $opt{prefix} %>updateTotalRow();
 
-  }
+    // update the next available row number
+    if (thisrownum >= <% $opt{prefix} %>rownum) {
+      <% $opt{prefix} %>rownum = thisrownum + 1;
+    }
 
-% unless ($cgi->param('error')) {
-  <% $opt{prefix} %>addRow();
+  } // end of addRow
+
+
+</SCRIPT>
+
+<TABLE ID="<% $opt{prefix} %>OneTrueTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+
+<TR>
+  <TH>Inv #</TH>
+  <TH>Cust #</TH>
+  <TH>Status</TH>
+  <TH>Customer</TH>
+  <TH>Balance</TH>
+% foreach my $header ( @{$opt{header}} ) {
+    <TH><% $header %></TH>
+% }
+</TR>
+
+% my @rownums = sort { $a <=> $b } map /^custnum(\d+)$/, keys %$param;
+<TR id="row_total">
+  <TH COLSPAN=5 ID="<% $opt{'prefix'} %>_TOTAL_TOTAL">
+    Total <% @rownums || 0 %>
+    <% PL($opt{name_singular} || 'customer', ( @rownums || 0 ) ) %>
+  </TH>
+% my $col = 0;
+% foreach my $footer ( @{$opt{footer}} ) {
+%   my $align = $align{ $opt{'footer_align'}->[$col] || 'c' };
+%   if ($footer eq '_TOTAL' ) {
+%     my $id = $opt{'fields'}->[$col];
+%     $id = ref($id) ? "column${col}_TOTAL" : "${id}_TOTAL";
+      <TH ALIGN="<% $align %>" ID="<% $id %>"> <% sprintf('%.2f', $total[$col] ) %></TH>
+%   } else {
+      <TH ALIGN="<% $align %>"><% $footer %></TH>
+%   }
+%   $col++;
+% }
+</TR>
+
+</TABLE>
+
+<SCRIPT TYPE="text/javascript">
+
+<% $opt{prefix} %>total_el =
+  document.getElementById("<% $opt{'prefix'} %>_TOTAL_TOTAL");
+
+<% $opt{prefix} %>rownum = 1; // really more of a "next row", used by addrow
+<% $opt{prefix} %>totalrows = 0; // will not include empty rows
+<% $opt{prefix} %>allrows = []; // will include empty rows
+
+% foreach my $row ( @rownums ) {
+%   if ( grep($param->{$_.$row},qw(invnum display_custnum custnum status statuscolor customer balance),@{$opt{fields}} ) ) {
+
+<% $opt{prefix} %>addRow({
+  rownum:<% $row %>,
+  num_open:<% $param->{"num_open$row"} |js_string %>,
+  invnum:<% $param->{"invnum$row"} |js_string %>,
+  display_custnum:<% $param->{"display_custnum$row"} |js_string %>,
+  custnum:<% $param->{"custnum$row"} |js_string %>,
+  status:<% $param->{"status$row"} |js_string %>,
+  statuscolor:<% $param->{"statuscolor$row"} |js_string %>,
+  customer:<% $param->{"customer$row"} |js_string %>,
+  balance:<% $param->{"balance$row"} |js_string %>,
+%     my $col = 0;
+%     foreach my $field ( @{$opt{fields}} ) {
+%       my $value;
+%       if ( ref($field) eq 'CODE' ) {
+%         $value = &{$field}($row,$param) || '';
+%       } else {
+%         $value = $param->{"$field$row"} || '';
+%       }
+%       my $name  = (ref($field) eq 'CODE') ? "column${col}" : "$field";
+  <% $name %>:<% $value |js_string %>,
+%       $col++;
+%     }
+});
+%   }
+% }
+
+<% $opt{prefix} %>addRow();
+
+% my $col = 0;
+% foreach my $footer ( @{$opt{footer}} ) {
+%   if ($footer eq '_TOTAL' ) {
+%     my $name = $opt{fields}->[$col];
+%     $name = ref($name) ? "column$col" : $name;
+      var <% $opt{prefix} %>th_el = document.getElementById("<%$name%>_TOTAL");
+      function <% $opt{prefix} %>calc_total<% $col %>() {
+        var row = 0;
+        var total = 0;
+        for (i = 0; i < <% $opt{prefix} %>allrows.length; i++) {
+          var value = document.getElementById("<%$name%>"+<% $opt{prefix} %>allrows[i]).value;
+          value = parseFloat(value);
+          if ( ! isNaN(value) ) {
+            total = total + value;
+          }
+        }
+        <% $opt{prefix} %>th_el.innerHTML = ' ' + total.toFixed(2);
+      }
+      <% $opt{prefix} %>calc_total<% $col %>()
+%   }
+%   $col++;
 % }
 </SCRIPT>
 
+<% include('/elements/xmlhttp.html',
+              'url'  => $p. 'misc/xmlhttp-cust_main-search.cgi',
+              'subs' => [qw( custnum_search smart_search invnum_search )],
+           )
+%>
+
 <%init>
 
 my(%opt) = @_;
 my $conf = new FS::Conf;
 
+## caution when using prefix, it isn't consistently applied to tag id/name
 $opt{prefix} = '' unless defined $opt{prefix};
 $opt{prefix} .= '_' if $opt{prefix};
 
diff --git a/httemplate/misc/batch-cust_pay.html b/httemplate/misc/batch-cust_pay.html
index cc1a26a..fb3ec04 100644
--- a/httemplate/misc/batch-cust_pay.html
+++ b/httemplate/misc/batch-cust_pay.html
@@ -26,8 +26,27 @@ function warnUnload() {
 }
 window.onbeforeunload = warnUnload;
 
-function add_row_callback(rownum, prefix) {
-  document.getElementById('enable_app'+rownum).disabled = true;
+function add_row_callback(rownum, prefix, values) {
+  if (values) {
+    custnum_update_callback(rownum, prefix);
+  } else {
+    document.getElementById('enable_app'+rownum).disabled = true;
+  }
+}
+
+function delete_row_callback(rownum, prefix) {
+  var i = 0;
+  var delbutton = document.getElementById('delete'+rownum+'.'+i);
+  var delrows = [];
+  while (delbutton) {
+    delrows[i] = delbutton;
+    i++;
+    delbutton = document.getElementById('delete'+rownum+'.'+i);
+  }
+  delrows = delrows.reverse();
+  for (i = 0; i < delrows.length; i++) {
+    delrows[i].onclick();
+  }
 }
 
 function custnum_update_callback(rownum, prefix) {
@@ -313,17 +332,17 @@ function create_application_row(rownum, appnum) {
 
 %# for error handling--ugly, but the alternative is translating the whole 
 %# process of creating rows into Mason
-var row_array = <% encode_json(\@rows) %>;
+var row_obj = <% encode_json(\%rows) %>;
 function preload() {
   var rownum;
   var appnum;
-  for (rownum=0; rownum < row_array.length; rownum++) {
-    if ( row_array[rownum].length ) {
+  for (rownum in row_obj) {
+    if ( row_obj[rownum].length ) {
       var enable = document.getElementById('enable_app'+rownum);
       enable.checked = true;
       var preload_row = function(r) {//continuation from toggle_application_row
-        for (appnum=0; appnum < row_array[r].length; appnum++) {
-          this_app = row_array[r][appnum];
+        for (appnum=0; appnum < row_obj[r].length; appnum++) {
+          this_app = row_obj[r][appnum];
           var x = r + '.' + appnum;
           //set invnum
           var select_invnum = document.getElementById('invnum'+x);
@@ -345,7 +364,7 @@ function preload() {
         } //for appnum
       }; //preload_row function
       toggle_application_row.call(enable, null, preload_row);
-    } // if row_array[rownum].length
+    } // if (row_obj[rownum].length
   } //for rownum
 }
 
@@ -380,6 +399,7 @@ function preload() {
     custnum_update_callback => 'custnum_update_callback',
     invnum_update_callback => 'invnum_update_callback',
     add_row_callback => 'add_row_callback',
+    delete_row_callback => 'delete_row_callback',
 &>
 
 <BR>
@@ -387,13 +407,12 @@ function preload() {
 
 </FORM>
 
-%if ( $cgi->param('error') ) {
-<SCRIPT TYPE="text/javascript">
-%  for ( my $row = 0; defined($cgi->param("custnum$row")); $row++ ) {
-     select_discount_term(<% $row %>, '');
-%  }
-</SCRIPT>
-%}
+% #XXX I think this can go away completely, but need to test with $use_discount
+% ###not perl <SCRIPT TYPE="text/javascript">
+% #foreach my $row ( keys %rows ) {
+% ###not perl   select_discount_term(<% $row %>, '');
+% #}
+% ###not perl </SCRIPT>
 
 <% include('/elements/footer.html') %>
 
@@ -455,29 +474,31 @@ push @onchange, '';
 $m->comp('/elements/handle_uri_query');
 
 # set up for preloading
-my @rows;
-my @row_errors;
+my %rows;
+my %row_errors;
 if ( $cgi->param('error') ) {
   my $param = $cgi->Vars;
   my $enum = 0; #errors numbered separately
-  for( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
-    $rows[$row] = [];
-    $row_errors[$row] = $param->{"error$enum"};
+  my @invrows = grep /^invnum\d+\.\d+$/, keys %$param; #pare down possibilities
+  foreach my $row ( sort { $a <=> $b } map /^custnum(\d+)$/, keys %$param ) {
+#  for( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
+    $rows{$row} = [];
+    $row_errors{$row} = $param->{"error$enum"};
     $enum++;
-    for( my $app = 0; exists($param->{"invnum$row.$app"}); $app++ ) {
+    foreach my $app ( map /^invnum$row\.(\d+)$/, @invrows ) {
       next if !$param->{"invnum$row.$app"};
       my %this_app = map { $_ => ($param->{$_.$row.'.'.$app} || '') } 
         qw( invnum amount );
       $this_app{'error'} = $param->{"error$enum"} || '';
       $param->{"error$enum"} = ''; # don't pass this error through
-      $rows[$row][$app] = \%this_app;
+      $rows{$row}[$app] = \%this_app;
       $enum++;
     }
   }
-  for( my $row = 0; $row < @row_errors; $row++ ) {
-    $param->{"error$row"} = $row_errors[$row];
+  foreach my $row (keys %rows) {
+    $param->{"error$row"} = $row_errors{$row};
   }
 }
-#warn Dumper {rows => \@rows, row_errors => \@row_errors };
+#warn Dumper {rows => \%rows, row_errors => \%row_errors };
 
 </%init>
diff --git a/httemplate/misc/process/batch-cust_pay.cgi b/httemplate/misc/process/batch-cust_pay.cgi
index 1105af9..bb4b973 100644
--- a/httemplate/misc/process/batch-cust_pay.cgi
+++ b/httemplate/misc/process/batch-cust_pay.cgi
@@ -12,7 +12,8 @@ my $paybatch = time2str('webbatch-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
 my @cust_pay = ();
 #my $row = 0;
 #while ( exists($param->{"custnum$row"}) ) {
-for ( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
+my @invrows = grep(/^invnum\d+\.\d+$/, keys %$param);
+foreach my $row ( map /^custnum(\d+)$/, keys %$param ) {
   my $custnum = $param->{"custnum$row"};
   my $cust_main;
   if ( $custnum =~ /^(\d+)$/ and $1 <= 2147483647 ) {
@@ -48,7 +49,8 @@ for ( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
 
   # payment applications, if any
   my @cust_bill_pay = ();
-  for ( my $app = 0; exists($param->{"invnum$row.$app"}); $app++ ) {
+  foreach my $app ( sort {$a <=> $b} map /^invnum$row\.(\d+)$/, @invrows ) {
+#  for ( my $app = 0; exists($param->{"invnum$row.$app"}); $app++ ) {
     next if !$param->{"invnum$row.$app"};
     push @cust_bill_pay, new FS::cust_bill_pay {
                             'invnum'  => $param->{"invnum$row.$app"},

commit 536c3720cb3820d7806d2e672463b5b3d06be078
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Tue Jun 9 15:47:56 2015 -0500

    RT#33484 [actually a bug fix to d4b8e45c3589f3c6489442798f5fba439d5a29c9]

diff --git a/FS/FS/cust_main/Billing_Discount.pm b/FS/FS/cust_main/Billing_Discount.pm
index b2852f6..117bf31 100644
--- a/FS/FS/cust_main/Billing_Discount.pm
+++ b/FS/FS/cust_main/Billing_Discount.pm
@@ -47,7 +47,7 @@ sub _discount_pkgs_and_bill {
   push @where,
     "NOT EXISTS (
        SELECT 1 FROM cust_bill_pkg_discount
-         WHERE cust_bill_pkg.billpkgnum = cust_bill_pkg_discount.billpkgnu:
+         WHERE cust_bill_pkg.billpkgnum = cust_bill_pkg_discount.billpkgnum
     )";
 
   my $extra_sql = 'WHERE '. join(' AND ', @where);

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/cust_main/Billing_Discount.pm        |    2 +-
 httemplate/elements/customer-table.html    |  536 +++++++++++++---------------
 httemplate/misc/batch-cust_pay.html        |   71 ++--
 httemplate/misc/process/batch-cust_pay.cgi |    6 +-
 4 files changed, 303 insertions(+), 312 deletions(-)




More information about the freeside-commits mailing list