5 include( '/elements/customer-table.html',
12 'header' => [ '#', 'Item' ],
15 sub { my ($row,$param) = @_;
16 $param->{"column$row"};
24 'name_singular' => 'customer', #label
25 'custnum_update_callback' => 'name_of_js_callback' #passed a rownum
28 'type' => ['immutable', ''], # immutable, checkbox, date or ''/text
29 'align' => [ 'c', 'l', 'r', '' ],
30 'size' => [], # sizes ignored for immutable
32 'footer' => ['string', '_TOTAL'], # strings or the special
34 'footer_align' => [ 'c', 'l', 'r', '' ],
36 'param' => { column0 => 1 }, # preset column of row 0 to 1
40 Some incomplete notes for javascript programmers:
42 On page load, existing rows are initialized by passing values to addRow
43 based on existing cgi values. An empty row (marked with the 'emptyrow'
44 attribute) is created by invoking addRow without values. After that,
45 to keep the non-empty row count (totalrows) accurate, use newEmptyRow to
46 create the next row. There should only be one empty row at a time.
49 total_el - element for displaying total number of rows
50 totalrows - total number of non-empty rows
51 rownum - really more of a "next row" value, used by addRow
52 allrows - array of tr elements, one for each row
54 Don't confuse the global rownum with the element attribute rownum
55 that is set as a reference point on some of the elements generated
56 by this script. They have different values.
58 Some of the functions:
59 updateTotalRow() - updates total_el based on value of totalrows
60 addDeleteButton(searchrow) - adds delete button to searchrow
61 newEmptyRow() - replaces old empty row
62 deleteRow() - removes the row specified by this.rownum
63 addRow(values) - adds a new row (marked as empty if values aren't specified)
65 This mason element is currently only used by misc/batch-cust_pay.html,
66 and probably should be cleaned up more before being used by anything else.
69 <LINK REL="stylesheet" TYPE="text/css" HREF="<%$fsurl%>elements/calendar-win2k-2.css" TITLE="win2k-2">
70 <SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar_stripped.js"></SCRIPT>
71 <SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-en.js"></SCRIPT>
72 <SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-setup.js"></SCRIPT>
73 <SCRIPT TYPE="text/javascript">
75 var num_open_invoices = new Array;
77 function clearhint_invnum() {
79 if ( this.value == 'Not found' || this.value == 'Multiple' ) {
81 this.style.color = '#000000';
86 function clearhint_custnum() {
88 if ( this.value == 'Not found' || this.value == 'Multiple' ) {
90 this.style.color = '#000000';
95 function clearhint_customer() {
97 this.style.color = '#000000';
99 if ( this.value == '(last name or company)' || this.value == 'Not found' )
104 function update_customer(searchrow, customerArray) {
106 var display_custnum_obj = document.getElementById('display_custnum'+searchrow);
107 var custnum_obj = document.getElementById('custnum'+searchrow);
108 var customer = document.getElementById('customer'+searchrow);
109 var customer_select = document.getElementById('cust_select'+searchrow);
111 display_custnum_obj.disabled = false;
112 display_custnum_obj.style.backgroundColor = '#ffffff';
113 customer.disabled = false;
114 customer.style.backgroundColor = '#ffffff';
116 if ( customerArray.length == 0 ) {
118 custnum_obj.value = '';
119 display_custnum_obj.value = 'Not found';
120 customer.value = 'Not found';
121 display_custnum_obj.style.color = '#ff0000';
122 customer.style.color = '#ff0000';
124 customer.style.display = '';
125 customer_select.style.display = 'none';
128 } else if ( customerArray.length >= 6 ) {
130 custnum_obj.value = customerArray[0];
131 display_custnum_obj.value = customerArray[6];
132 display_custnum_obj.style.color = '#000000';
133 customer.value = customerArray[1];
135 update_balance_text(searchrow, customerArray[2]);
136 update_status_text( searchrow, customerArray[3]);
137 update_status_color(searchrow, '#'+customerArray[4]);
138 update_num_open(searchrow, customerArray[5]);
140 customer.style.display = '';
141 customer_select.style.display = 'none';
146 function search_invnum() {
148 this.style.color = '#000000'
150 var invnum_obj = this;
151 var searchrow = this.getAttribute('rownum');
152 var invnum = this.value;
154 if ( invnum == 'searching...' || invnum == 'Not found' || invnum == '' )
157 if ( this.getAttribute('magic') == 'nosearch' ) {
158 this.setAttribute('magic', '');
162 if ( document.getElementById('row'+searchrow).emptyrow ) {
163 newEmptyRow(searchrow);
165 var customer = document.getElementById('customer'+searchrow);
166 customer.value = 'searching...';
167 customer.disabled = true;
168 customer.style.color = '#000000';
169 customer.style.backgroundColor = '#dddddd';
171 var customer_select = document.getElementById('cust_select'+searchrow);
173 customer.style.display = '';
174 customer_select.style.display = 'none';
176 update_balance_text(searchrow, '');
177 update_status_text(searchrow, '');
178 update_status_color(searchrow, '#000000');
179 update_num_open(searchrow, 0);
181 function search_invnum_update(customers) {
183 var customerArray = eval('(' + customers + ')');
184 update_customer(searchrow, customerArray);
186 % if ( $opt{invnum_update_callback} ) {
187 <% $opt{invnum_update_callback} %>(searchrow)
192 invnum_search( invnum, search_invnum_update );
196 function search_custnum() {
198 this.style.color = '#000000'
200 var display_custnum_obj = this;
201 var searchrow = this.getAttribute('rownum');
202 var custnum_obj = document.getElementById('custnum'+searchrow);
203 var display_custnum = this.value;
205 if ( display_custnum == 'searching...' || display_custnum == 'Not found' || display_custnum == '' )
208 if ( this.getAttribute('magic') == 'nosearch' ) {
209 this.setAttribute('magic', '');
213 if ( document.getElementById('row'+searchrow).emptyrow ) {
214 newEmptyRow(searchrow);
217 var customer_obj = document.getElementById('customer'+searchrow);
218 customer_obj.value = 'searching...';
219 customer_obj.disabled = true;
220 customer_obj.style.color = '#000000';
221 customer_obj.style.backgroundColor = '#dddddd';
223 var customer_select = document.getElementById('cust_select'+searchrow);
225 customer_obj.style.display = '';
226 customer_select.style.display = 'none';
228 var invnum = document.getElementById('invnum'+searchrow);
231 update_balance_text(searchrow, '');
232 update_status_text( searchrow, '');
233 update_status_color(searchrow, '#000000');
234 update_num_open(searchrow, 0);
236 function search_custnum_update(customers) {
238 var customerArrayArray = eval('(' + customers + ')') || [];
240 if ( customerArrayArray.length == 0 ) {
242 update_customer(searchrow, []);
244 } else if ( customerArrayArray.length == 1 ) {
246 update_customer(searchrow, customerArrayArray[0]);
247 % if ( $opt{custnum_update_callback} ) {
248 <% $opt{custnum_update_callback} %>(searchrow)
253 custnum_obj.value = 'Multiple'; // or something
254 custnum_obj.style.color = '#ff0000';
256 //blank the current list
257 customer_select.options.length = 0;
259 opt(customer_select, '', 'Multiple customers match "' + custnum + '" - select one', '#ff0000');
260 //add the multiple customers
261 for ( var s = 0; s < customerArrayArray.length; s++ ) {
263 JSON.stringify(customerArrayArray[s]),
264 customerArrayArray[s][1],
268 opt(customer_select, 'cancel', '(Edit search string)', '#000000');
270 customer_obj.style.display = 'none';
272 customer_select.style.display = '';
278 custnum_search(display_custnum, search_custnum_update );
282 function search_customer() {
284 var customer_obj = this;
285 var searchrow = this.getAttribute('rownum');
286 var customer = this.value;
288 if ( customer == 'searching...' || customer == 'Not found' || customer == '' )
291 if ( this.getAttribute('magic') == 'nosearch' ) {
292 this.setAttribute('magic', '');
296 if ( document.getElementById('row'+searchrow).emptyrow ) {
297 newEmptyRow(searchrow);
300 var invnum = document.getElementById('invnum'+searchrow);
303 var custnum_obj = document.getElementById('display_custnum'+searchrow);
304 custnum_obj.value = 'searching...';
305 custnum_obj.disabled = true;
306 custnum_obj.style.color = '#000000';
307 custnum_obj.style.backgroundColor = '#dddddd';
309 var customer_select = document.getElementById('cust_select'+searchrow);
311 function search_customer_update(customers) {
313 var customerArrayArray = eval('(' + customers + ')') || [ [] ];
315 custnum_obj.disabled = false;
316 custnum_obj.style.backgroundColor = '#ffffff';
318 if ( customerArrayArray.length == 0 ) {
320 update_customer(searchrow, []);
322 } else if ( customerArrayArray.length == 1 ) {
324 update_customer(searchrow, customerArrayArray[0]);
325 % if ( $opt{custnum_update_callback} ) {
326 <% $opt{custnum_update_callback} %>(searchrow)
331 custnum_obj.value = 'Multiple'; // or something
332 custnum_obj.style.color = '#ff0000';
334 //blank the current list
335 customer_select.options.length = 0;
337 opt(customer_select, '', 'Multiple customers match "' + customer + '" - select one', '#ff0000');
338 //add the multiple customers
339 for ( var s = 0; s < customerArrayArray.length; s++ ) {
341 JSON.stringify(customerArrayArray[s]),
342 customerArrayArray[s][1],
346 opt(customer_select, 'cancel', '(Edit search string)', '#000000');
348 customer_obj.style.display = 'none';
350 customer_select.style.display = '';
356 smart_search( customer, search_customer_update );
360 function select_customer() {
362 var custnum_balance_status = this.options[this.selectedIndex].value;
363 var customer = this.options[this.selectedIndex].text;
365 var searchrow = this.getAttribute('rownum');
366 var display_custnum_obj = document.getElementById('display_custnum'+searchrow);
367 var custnum_obj = document.getElementById('custnum'+searchrow);
368 var customer_obj = document.getElementById('customer'+searchrow);
369 var balance_obj = document.getElementById('balance'+searchrow);
370 var status_obj = document.getElementById('status'+searchrow);
372 if ( custnum_balance_status == '' ) {
374 } else if ( custnum_balance_status == 'cancel' ) {
376 display_custnum_obj.value = '';
377 custnum_obj.value = '';
378 custnum_obj.style.color = '#000000';
380 this.style.display = 'none';
381 customer_obj.style.display = '';
382 customer_obj.focus();
386 update_customer(searchrow, JSON.parse(custnum_balance_status));
388 % if ( $opt{custnum_update_callback} ) {
389 <% $opt{custnum_update_callback} %>(searchrow)
396 function opt(what,value,text,color) {
397 var optionName = new Option(text, value, false, false);
398 optionName.style.color = color;
399 var length = what.length;
400 what.options[length] = optionName;
403 function update_status_text(rownum, newval) {
404 document.getElementById('status'+rownum).value = newval;
405 document.getElementById('status'+rownum+'_text').innerHTML = newval;
408 function update_status_color(rownum, newval) {
409 document.getElementById('statuscolor'+rownum).value = newval;
410 document.getElementById('status'+rownum+'_text').style.color = newval;
413 function update_balance_text(rownum, newval) {
414 document.getElementById('balance'+rownum).value = newval;
415 document.getElementById('balance'+rownum+'_text').innerHTML = newval;
418 function update_num_open(rownum, newval) {
419 document.getElementById('num_open'+rownum).value = newval;
420 num_open_invoices[rownum] = newval;
423 // updates display of total rows based on value of totalrows
424 function updateTotalRow () {
425 if ( totalrows == 1 ) {
429 + ' <% $opt{name_singular} || 'customer' %>';
434 + ' <% PL($opt{name_singular} || 'customer') %>';
438 var total_el, rownum, totalrows, allrows;
440 function addDeleteButton (searchrow) {
441 var td_delete = document.getElementById('delete'+searchrow);
442 var button_delete = document.createElement('INPUT');
443 button_delete.setAttribute('rownum', searchrow);
444 button_delete.setAttribute('type', 'button');
445 button_delete.setAttribute('value', 'X');
446 button_delete.onclick = deleteRow;
447 button_delete.style.color = '#ff0000';
448 button_delete.style.fontWeight = 'bold';
449 button_delete.style.paddingLeft = '2px';
450 button_delete.style.paddingRight = '2px';
451 td_delete.appendChild(button_delete);
454 function newEmptyRow (searchrow) {
455 // add delete button to current row
456 addDeleteButton(searchrow);
457 // mark current row as non-empty
458 var oldemptyrow = document.getElementById('row'+searchrow);
459 oldemptyrow.emptyrow = false;
463 // add a new empty row
467 function deleteRow() {
468 var thisrownum = this.getAttribute('rownum');
469 % if ( $opt{delete_row_callback} ) {
471 <% $opt{delete_row_callback} %>(thisrownum);
473 // remove the actual row
474 var thisrow = document.getElementById('row'+thisrownum);
475 thisrow.parentNode.removeChild(thisrow);
476 // remove row from tally of all rows
478 for (i = 0; i < allrows.length; i++) {
479 if (allrows[i] == thisrownum) continue;
480 newrows.push(allrows[i]);
483 totalrows--; // should never be deleting empty rows
485 // recalculate column totals, if any
487 % foreach my $footer ( @{$opt{footer}} ) {
488 % if ($footer eq '_TOTAL' ) {
489 calc_total<% $col %>()
495 function addRow(values) {
497 var table = document.getElementById('OneTrueTable');
498 var tablebody = table.getElementsByTagName('tbody').item(0);
500 var row = table.insertRow(table.rows.length - 1);
501 var thisrownum = values ? values.rownum : rownum;
502 row.setAttribute('id', 'row'+thisrownum);
503 row.emptyrow = values ? false : true;
505 var invnum_cell = document.createElement('TD');
507 var invnum_input = document.createElement('INPUT');
508 invnum_input.setAttribute('name', 'invnum'+thisrownum);
509 invnum_input.setAttribute('id', 'invnum'+thisrownum);
510 invnum_input.style.textAlign = 'right';
511 invnum_input.setAttribute('size', 8);
512 invnum_input.setAttribute('maxlength', 12);
513 invnum_input.setAttribute('rownum', thisrownum);
514 invnum_input.value = values ? values.invnum : '';
515 invnum_input.onfocus = clearhint_invnum;
516 invnum_input.onchange = search_invnum;
517 invnum_cell.appendChild(invnum_input);
519 row.appendChild(invnum_cell);
521 var custnum_cell = document.createElement('TD');
523 var display_custnum_input = document.createElement('INPUT');
524 display_custnum_input.setAttribute('name', 'display_custnum'+thisrownum);
525 display_custnum_input.setAttribute('id', 'display_custnum'+thisrownum);
526 display_custnum_input.style.textAlign = 'right';
527 display_custnum_input.setAttribute('size', 8);
528 display_custnum_input.setAttribute('maxlength', 12);
529 display_custnum_input.setAttribute('rownum', thisrownum);
530 display_custnum_input.value = values ? values.custnum : '';
531 display_custnum_input.onfocus = clearhint_custnum;
532 display_custnum_input.onchange = search_custnum;
533 custnum_cell.appendChild(display_custnum_input);
535 var custnum_input = document.createElement('INPUT');
536 custnum_input.type = 'hidden';
537 custnum_input.setAttribute('name', 'custnum'+thisrownum);
538 custnum_input.setAttribute('id', 'custnum'+thisrownum);
539 custnum_input.setAttribute('rownum', thisrownum);
540 custnum_input.value = values ? values.custnum : '';
541 custnum_cell.appendChild(custnum_input);
543 row.appendChild(custnum_cell);
545 var status_cell = document.createElement('TD');
546 status_cell.style.textAlign = 'center';
548 var status_span = document.createElement('SPAN');
549 status_span.setAttribute('id', 'status'+thisrownum+'_text');
550 status_span.style.fontWeight = 'bold';
551 status_span.style.color = values ? values.statuscolor : '';
552 status_span.setAttribute('rownum', thisrownum);
553 status_span.appendChild(
554 document.createTextNode(values ? values.status : '')
556 status_cell.appendChild(status_span);
558 var status_input = document.createElement('INPUT');
559 status_input.setAttribute('type', 'hidden');
560 status_input.setAttribute('name', 'status'+thisrownum);
561 status_input.setAttribute('id', 'status'+thisrownum);
562 status_input.setAttribute('rownum', thisrownum);
563 status_input.value = values ? values.status : '';
564 status_cell.appendChild(status_input);
566 var statuscolor_input = document.createElement('INPUT');
567 statuscolor_input.setAttribute('type', 'hidden');
568 statuscolor_input.setAttribute('name', 'statuscolor'+thisrownum);
569 statuscolor_input.setAttribute('id', 'statuscolor'+thisrownum);
570 statuscolor_input.setAttribute('rownum', thisrownum);
571 statuscolor_input.value = values ? values.statuscolor : '';
572 status_cell.appendChild(statuscolor_input);
574 row.appendChild(status_cell);
576 var customer_cell = document.createElement('TD');
578 var customer_input = document.createElement('INPUT');
579 customer_input.setAttribute('name', 'customer'+thisrownum);
580 customer_input.setAttribute('id', 'customer'+thisrownum);
581 customer_input.setAttribute('size', 64);
582 customer_input.setAttribute('value', '(last name or company)' );
583 customer_input.setAttribute('rownum', thisrownum);
584 customer_input.value = values ? values.customer : '';
585 customer_input.onfocus = clearhint_customer;
586 customer_input.onclick = clearhint_customer;
587 customer_input.onchange = search_customer;
588 customer_cell.appendChild(customer_input);
590 var customer_select = document.createElement('SELECT');
591 customer_select.setAttribute('name', 'cust_select'+thisrownum);
592 customer_select.setAttribute('id', 'cust_select'+thisrownum);
593 customer_select.setAttribute('rownum', thisrownum);
594 customer_select.style.color = '#ff0000';
595 customer_select.style.display = 'none';
596 customer_select.onchange = select_customer;
597 customer_cell.appendChild(customer_select);
599 row.appendChild(customer_cell);
601 var balance_cell = document.createElement('TD');
603 balance_cell.style.textAlign = 'right';
604 balance_cell.appendChild(document.createTextNode('<%$money_char%>'));
606 var balance_span = document.createElement('SPAN');
607 balance_span.setAttribute('id', 'balance'+thisrownum+'_text');
608 balance_span.setAttribute('rownum', thisrownum);
609 balance_cell.appendChild(balance_span);
611 balance_cell.appendChild(
612 document.createTextNode(String.fromCharCode(160) + (values ? values.balance : '')) //
615 var balance_input = document.createElement('INPUT');
616 balance_input.setAttribute('type', 'hidden');
617 balance_input.setAttribute('name', 'balance'+thisrownum);
618 balance_input.setAttribute('id', 'balance'+thisrownum);
619 balance_input.setAttribute('rownum', thisrownum);
620 balance_input.value = values ? values.balance : '';
621 balance_cell.appendChild(balance_input);
623 var num_open_input = document.createElement('INPUT');
624 num_open_input.setAttribute('type', 'hidden');
625 num_open_input.setAttribute('name', 'num_open'+thisrownum);
626 num_open_input.setAttribute('id', 'num_open'+thisrownum);
627 num_open_input.setAttribute('rownum', thisrownum);
628 balance_cell.appendChild(num_open_input);
630 row.appendChild(balance_cell);
633 % foreach my $field ( @{$opt{fields}} ) {
635 var my_cell = document.createElement('TD');
636 my_cell.setAttribute('align', '<% $align{ $opt{align}->[$col] || 'l' } %>');
637 % if ($opt{'color'}->[$col]) {
638 my_cell.style.color = '<% $opt{color}->[$col] %>';
641 % if ($types->[$col] eq 'immutable') {
642 var my_text = document.createTextNode(values ? values.<% $field %> : '');
643 my_cell.appendChild(my_text);
646 % my $name = (ref($field) eq 'CODE') ? "column${col}_" : $field;
647 var my_input = document.createElement('INPUT');
648 my_input.setAttribute('name', '<% $name %>'+thisrownum);
649 my_input.setAttribute('id', '<% $name %>'+thisrownum);
650 my_input.style.textAlign = '<% $align{ $opt{align}->[$col] || 'l' } %>';
651 my_input.setAttribute('size', <% $sizes->[$col] || 10 %>);
652 my_input.setAttribute('rownum', thisrownum);
653 % if ( $types->[$col] eq 'immutable' ) {
654 my_input.setAttribute('type', 'hidden');
655 % } elsif ( $types->[$col] eq 'checkbox' ) {
656 my_input.setAttribute('type', 'checkbox');
657 my_input.checked = (values && values.<% $field %>) ? true : false;
658 % } elsif ( $types->[$col] eq 'date' ) {
659 my_input_button = document.createElement('IMG');
660 my_input_button.setAttribute('src', '<% $fsurl %>images/calendar.png');
661 my_input_button.setAttribute('title', <% mt('Select date') |js_string %>);
662 my_input_button.setAttribute('name', '<% $name %>'+thisrownum+'button');
663 my_input_button.setAttribute('id', '<% $name %>'+thisrownum+'button');
665 my_input.value = (values && values.<% $field %>) || '';
666 % if ( $opt{onchange}->[$col] ) {
667 my_input.onchange = <% $opt{onchange}->[$col] %>;
669 % elsif ( $opt{footer}->[$col] eq '_TOTAL' ) {
670 my_input.onchange = calc_total<%$col%>;
671 my_input.onkeyup = calc_total<%$col%>;
673 my_cell.appendChild(my_input);
674 % if ( $types->[$col] eq 'date' ) {
675 my_cell.appendChild(my_input_button);
678 row.appendChild(my_cell);
680 % if ( $types->[$col] eq 'date' ) {
682 inputField: '<% $name %>'+thisrownum,
683 ifFormat: "<% $date_format %>",
684 button: '<% $name %>'+thisrownum+'button',
692 var td_delete = document.createElement('TD');
693 td_delete.setAttribute('id', 'delete'+thisrownum);
694 row.appendChild(td_delete);
696 addDeleteButton(thisrownum);
699 update_num_open(thisrownum, (values ? values.num_open : '0'));
701 % if ( $opt{add_row_callback} ) {
702 <% $opt{add_row_callback} %>(thisrownum, values);
705 // update the total number of rows display
706 allrows.push(thisrownum);
707 if (values) totalrows++;
710 // update the next available row number
711 if (thisrownum >= rownum) {
712 rownum = thisrownum + 1;
720 <TABLE ID="OneTrueTable" CLASS="fsinnerbox">
728 % foreach my $header ( @{$opt{header}} ) {
729 <TH><% $header %></TH>
733 % my @rownums = sort { $a <=> $b } map /^custnum(\d+)$/, keys %$param;
735 <TH COLSPAN=5 ID="_TOTAL_TOTAL">
736 Total <% @rownums || 0 %>
737 <% PL($opt{name_singular} || 'customer', ( @rownums || 0 ) ) %>
740 % foreach my $footer ( @{$opt{footer}} ) {
741 % my $align = $align{ $opt{'footer_align'}->[$col] || 'c' };
742 % if ($footer eq '_TOTAL' ) {
743 % my $id = $opt{'fields'}->[$col];
744 % $id = ref($id) ? "column${col}_TOTAL" : "${id}_TOTAL";
745 <TH ALIGN="<% $align %>" ID="<% $id %>"> <% sprintf('%.2f', $total[$col] ) %></TH>
747 <TH ALIGN="<% $align %>"><% $footer %></TH>
755 <SCRIPT TYPE="text/javascript">
758 document.getElementById("_TOTAL_TOTAL");
760 rownum = 1; // really more of a "next row", used by addrow
761 totalrows = 0; // will not include empty rows
762 allrows = []; // will include empty rows
764 % foreach my $row ( @rownums ) {
765 % if ( grep($param->{$_.$row},qw(invnum display_custnum custnum status statuscolor customer balance),@{$opt{fields}} ) ) {
769 num_open:<% $param->{"num_open$row"} |js_string %>,
770 invnum:<% $param->{"invnum$row"} |js_string %>,
771 display_custnum:<% $param->{"display_custnum$row"} |js_string %>,
772 custnum:<% $param->{"custnum$row"} |js_string %>,
773 status:<% $param->{"status$row"} |js_string %>,
774 statuscolor:<% $param->{"statuscolor$row"} |js_string %>,
775 customer:<% $param->{"customer$row"} |js_string %>,
776 balance:<% $param->{"balance$row"} |js_string %>,
778 % foreach my $field ( @{$opt{fields}} ) {
780 % if ( ref($field) eq 'CODE' ) {
781 % $value = &{$field}($row,$param) || '';
783 % $value = $param->{"$field$row"} || '';
785 % my $name = (ref($field) eq 'CODE') ? "column${col}" : "$field";
786 <% $name %>:<% $value |js_string %>,
796 % foreach my $footer ( @{$opt{footer}} ) {
797 % if ($footer eq '_TOTAL' ) {
798 % my $name = $opt{fields}->[$col];
799 % $name = ref($name) ? "column$col" : $name;
800 var th_el = document.getElementById("<%$name%>_TOTAL");
801 function calc_total<% $col %>() {
804 for (i = 0; i < allrows.length; i++) {
805 var value = document.getElementById("<%$name%>"+allrows[i]).value;
806 value = parseFloat(value);
807 if ( ! isNaN(value) ) {
808 total = total + value;
811 th_el.innerHTML = ' ' + total.toFixed(2);
813 calc_total<% $col %>()
819 <% include('/elements/xmlhttp.html',
820 'url' => $p. 'misc/xmlhttp-cust_main-search.cgi',
821 'subs' => [qw( custnum_search smart_search invnum_search )],
828 my $conf = new FS::Conf;
829 my $date_format = $conf->config('date_format') || '%m/%d/%Y';
831 my $types = $opt{'type'} ? [ @{$opt{'type'}} ] : [];
832 my $sizes = $opt{'size'} ? [ @{$opt{'size'}} ] : [];
834 my $param = $opt{param};
835 $param = $cgi->Vars if $cgi->param('error');
837 $opt{$_} ||= [] foreach qw(align color footer footer_align);
839 my @total = map 0, @{$opt{footer}};
847 my $money_char = $conf->config('money_char') || '$';