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 'types' => ['immutable', ''], # immutable 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
42 <SCRIPT TYPE="text/javascript">
44 var num_open_invoices = new Array;
46 function clearhint_invnum() {
48 if ( this.value == 'Not found' || this.value == 'Multiple' ) {
50 this.style.color = '#000000';
55 function clearhint_custnum() {
57 if ( this.value == 'Not found' || this.value == 'Multiple' ) {
59 this.style.color = '#000000';
64 function clearhint_customer() {
66 this.style.color = '#000000';
68 if ( this.value == '(last name or company)' || this.value == 'Not found' )
73 function update_customer(searchrow, customerArray) {
75 var display_custnum_obj = document.getElementById('display_custnum'+searchrow);
76 var custnum_obj = document.getElementById('custnum'+searchrow);
77 var customer = document.getElementById('customer'+searchrow);
78 var customer_select = document.getElementById('cust_select'+searchrow);
80 display_custnum_obj.disabled = false;
81 display_custnum_obj.style.backgroundColor = '#ffffff';
82 customer.disabled = false;
83 customer.style.backgroundColor = '#ffffff';
85 if ( customerArray.length == 0 ) {
87 custnum_obj.value = '';
88 display_custnum_obj.value = 'Not found';
89 customer.value = 'Not found';
90 display_custnum_obj.style.color = '#ff0000';
91 customer.style.color = '#ff0000';
93 customer.style.display = '';
94 customer_select.style.display = 'none';
97 } else if ( customerArray.length >= 6 ) {
99 custnum_obj.value = customerArray[0];
100 display_custnum_obj.value = customerArray[6];
101 display_custnum_obj.style.color = '#000000';
102 customer.value = customerArray[1];
104 update_balance_text(searchrow, customerArray[2]);
105 update_status_text( searchrow, customerArray[3]);
106 update_status_color(searchrow, '#'+customerArray[4]);
107 update_num_open(searchrow, customerArray[5]);
109 customer.style.display = '';
110 customer_select.style.display = 'none';
115 function <% $opt{prefix} %>search_invnum() {
117 this.style.color = '#000000'
119 var invnum_obj = this;
120 var searchrow = this.getAttribute('rownum');
121 var invnum = this.value;
123 if ( invnum == 'searching...' || invnum == 'Not found' || invnum == '' )
126 if ( this.getAttribute('magic') == 'nosearch' ) {
127 this.setAttribute('magic', '');
131 if ( document.getElementById('row'+searchrow).emptyrow ) {
132 <% $opt{prefix} %>newEmptyRow(searchrow);
134 var customer = document.getElementById('customer'+searchrow);
135 customer.value = 'searching...';
136 customer.disabled = true;
137 customer.style.color = '#000000';
138 customer.style.backgroundColor = '#dddddd';
140 var customer_select = document.getElementById('cust_select'+searchrow);
142 customer.style.display = '';
143 customer_select.style.display = 'none';
145 update_balance_text(searchrow, '');
146 update_status_text(searchrow, '');
147 update_status_color(searchrow, '#000000');
148 update_num_open(searchrow, 0);
150 function search_invnum_update(customers) {
152 var customerArray = eval('(' + customers + ')');
153 update_customer(searchrow, customerArray);
155 % if ( $opt{invnum_update_callback} ) {
156 <% $opt{invnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
161 invnum_search( invnum, search_invnum_update );
165 function <% $opt{prefix} %>search_custnum() {
167 this.style.color = '#000000'
169 var display_custnum_obj = this;
170 var searchrow = this.getAttribute('rownum');
171 var custnum_obj = document.getElementById('custnum'+searchrow);
172 var display_custnum = this.value;
174 if ( display_custnum == 'searching...' || display_custnum == 'Not found' || display_custnum == '' )
177 if ( this.getAttribute('magic') == 'nosearch' ) {
178 this.setAttribute('magic', '');
182 if ( document.getElementById('row'+searchrow).emptyrow ) {
183 <% $opt{prefix} %>newEmptyRow(searchrow);
186 var customer_obj = document.getElementById('customer'+searchrow);
187 customer_obj.value = 'searching...';
188 customer_obj.disabled = true;
189 customer_obj.style.color = '#000000';
190 customer_obj.style.backgroundColor = '#dddddd';
192 var customer_select = document.getElementById('cust_select'+searchrow);
194 customer_obj.style.display = '';
195 customer_select.style.display = 'none';
197 var invnum = document.getElementById('invnum'+searchrow);
200 update_balance_text(searchrow, '');
201 update_status_text( searchrow, '');
202 update_status_color(searchrow, '#000000');
203 update_num_open(searchrow, 0);
205 function search_custnum_update(customers) {
207 var customerArrayArray = eval('(' + customers + ')') || [];
209 if ( customerArrayArray.length == 0 ) {
211 update_customer(searchrow, []);
213 } else if ( customerArrayArray.length == 1 ) {
215 update_customer(searchrow, customerArrayArray[0]);
216 % if ( $opt{custnum_update_callback} ) {
217 <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
222 custnum_obj.value = 'Multiple'; // or something
223 custnum_obj.style.color = '#ff0000';
225 //blank the current list
226 customer_select.options.length = 0;
228 opt(customer_select, '', 'Multiple customers match "' + custnum + '" - select one', '#ff0000');
229 //add the multiple customers
230 for ( var s = 0; s < customerArrayArray.length; s++ ) {
232 JSON.stringify(customerArrayArray[s]),
233 customerArrayArray[s][1],
237 opt(customer_select, 'cancel', '(Edit search string)', '#000000');
239 customer_obj.style.display = 'none';
241 customer_select.style.display = '';
247 custnum_search(display_custnum, search_custnum_update );
251 function <% $opt{prefix} %>search_customer() {
253 var customer_obj = this;
254 var searchrow = this.getAttribute('rownum');
255 var customer = this.value;
257 if ( customer == 'searching...' || customer == 'Not found' || customer == '' )
260 if ( this.getAttribute('magic') == 'nosearch' ) {
261 this.setAttribute('magic', '');
265 if ( document.getElementById('row'+searchrow).emptyrow ) {
266 <% $opt{prefix} %>newEmptyRow(searchrow);
269 var invnum = document.getElementById('invnum'+searchrow);
272 var custnum_obj = document.getElementById('display_custnum'+searchrow);
273 custnum_obj.value = 'searching...';
274 custnum_obj.disabled = true;
275 custnum_obj.style.color = '#000000';
276 custnum_obj.style.backgroundColor = '#dddddd';
278 var customer_select = document.getElementById('cust_select'+searchrow);
280 function search_customer_update(customers) {
282 var customerArrayArray = eval('(' + customers + ')') || [ [] ];
284 custnum_obj.disabled = false;
285 custnum_obj.style.backgroundColor = '#ffffff';
287 if ( customerArrayArray.length == 0 ) {
289 update_customer(searchrow, []);
291 } else if ( customerArrayArray.length == 1 ) {
293 update_customer(searchrow, customerArrayArray[0]);
294 % if ( $opt{custnum_update_callback} ) {
295 <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
300 custnum_obj.value = 'Multiple'; // or something
301 custnum_obj.style.color = '#ff0000';
303 //blank the current list
304 customer_select.options.length = 0;
306 opt(customer_select, '', 'Multiple customers match "' + customer + '" - select one', '#ff0000');
307 //add the multiple customers
308 for ( var s = 0; s < customerArrayArray.length; s++ ) {
310 JSON.stringify(customerArrayArray[s]),
311 customerArrayArray[s][1],
315 opt(customer_select, 'cancel', '(Edit search string)', '#000000');
317 customer_obj.style.display = 'none';
319 customer_select.style.display = '';
325 smart_search( customer, search_customer_update );
329 function select_customer() {
331 var custnum_balance_status = this.options[this.selectedIndex].value;
332 var customer = this.options[this.selectedIndex].text;
334 var searchrow = this.getAttribute('rownum');
335 var display_custnum_obj = document.getElementById('display_custnum'+searchrow);
336 var custnum_obj = document.getElementById('custnum'+searchrow);
337 var customer_obj = document.getElementById('customer'+searchrow);
338 var balance_obj = document.getElementById('balance'+searchrow);
339 var status_obj = document.getElementById('status'+searchrow);
341 if ( custnum_balance_status == '' ) {
343 } else if ( custnum_balance_status == 'cancel' ) {
345 display_custnum_obj.value = '';
346 custnum_obj.value = '';
347 custnum_obj.style.color = '#000000';
349 this.style.display = 'none';
350 customer_obj.style.display = '';
351 customer_obj.focus();
355 update_customer(searchrow, JSON.parse(custnum_balance_status));
357 % if ( $opt{custnum_update_callback} ) {
358 <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
365 function opt(what,value,text,color) {
366 var optionName = new Option(text, value, false, false);
367 optionName.style.color = color;
368 var length = what.length;
369 what.options[length] = optionName;
372 function update_status_text(rownum, newval) {
373 document.getElementById('status'+rownum).value = newval;
374 document.getElementById('status'+rownum+'_text').innerHTML = newval;
377 function update_status_color(rownum, newval) {
378 document.getElementById('statuscolor'+rownum).value = newval;
379 document.getElementById('status'+rownum+'_text').style.color = newval;
382 function update_balance_text(rownum, newval) {
383 document.getElementById('balance'+rownum).value = newval;
384 document.getElementById('balance'+rownum+'_text').innerHTML = newval;
387 function update_num_open(rownum, newval) {
388 document.getElementById('num_open'+rownum).value = newval;
389 num_open_invoices[rownum] = newval;
392 function <% $opt{prefix} %>updateTotalRow () {
393 if ( <% $opt{prefix} %>totalrows == 1 ) {
394 <% $opt{prefix} %>total_el.innerHTML =
396 + <% $opt{prefix} %>totalrows
397 + ' <% $opt{name_singular} || 'customer' %>';
399 <% $opt{prefix} %>total_el.innerHTML =
401 + <% $opt{prefix} %>totalrows
402 + ' <% PL($opt{name_singular} || 'customer') %>';
406 var <% $opt{prefix} %>total_el, <% $opt{prefix} %>rownum, <% $opt{prefix} %>totalrows, <% $opt{prefix} %>allrows;
408 function <% $opt{prefix} %>addDeleteButton (searchrow) {
409 var td_delete = document.getElementById('delete'+searchrow);
410 var button_delete = document.createElement('INPUT');
411 button_delete.setAttribute('rownum', searchrow);
412 button_delete.setAttribute('type', 'button');
413 button_delete.setAttribute('value', 'X');
414 button_delete.onclick = <% $opt{prefix} %>deleteRow;
415 button_delete.style.color = '#ff0000';
416 button_delete.style.fontWeight = 'bold';
417 button_delete.style.paddingLeft = '2px';
418 button_delete.style.paddingRight = '2px';
419 td_delete.appendChild(button_delete);
422 function <% $opt{prefix} %>newEmptyRow (searchrow) {
423 // add delete button to current row
424 <% $opt{prefix} %>addDeleteButton(searchrow);
425 // mark current row as non-empty
426 var oldemptyrow = document.getElementById('row'+searchrow);
427 oldemptyrow.emptyrow = false;
429 <% $opt{prefix} %>totalrows++
430 <% $opt{prefix} %>updateTotalRow();
431 // add a new empty row
432 <% $opt{prefix} %>addRow();
435 function <% $opt{prefix} %>deleteRow() {
436 var thisrownum = this.getAttribute('rownum');
437 % if ( $opt{delete_row_callback} ) {
439 <% $opt{delete_row_callback} %>(thisrownum,'<% $opt{prefix} %>');
441 // remove the actual row
442 var thisrow = document.getElementById('row'+thisrownum);
443 thisrow.parentNode.removeChild(thisrow);
444 // remove row from tally of all rows
446 for (i = 0; i < allrows.length; i++) {
447 if (allrows[i] == thisrownum) continue;
448 newrows.push(allrows[i]);
451 <% $opt{prefix} %>totalrows--; // should never be deleting empty rows
452 <% $opt{prefix} %>updateTotalRow();
453 // recalculate column totals, if any
455 % foreach my $footer ( @{$opt{footer}} ) {
456 % if ($footer eq '_TOTAL' ) {
457 <% $opt{prefix} %>calc_total<% $col %>()
463 function <% $opt{prefix} %>addRow(values) {
465 var table = document.getElementById('<% $opt{prefix} %>OneTrueTable');
466 var tablebody = table.getElementsByTagName('tbody').item(0);
468 var row = table.insertRow(table.rows.length - 1);
469 var thisrownum = values ? values.rownum : <% $opt{prefix} %>rownum;
470 row.setAttribute('id', 'row'+thisrownum);
471 row.emptyrow = values ? false : true;
473 var invnum_cell = document.createElement('TD');
475 var invnum_input = document.createElement('INPUT');
476 invnum_input.setAttribute('name', 'invnum'+thisrownum);
477 invnum_input.setAttribute('id', 'invnum'+thisrownum);
478 invnum_input.style.textAlign = 'right';
479 invnum_input.setAttribute('size', 8);
480 invnum_input.setAttribute('maxlength', 12);
481 invnum_input.setAttribute('rownum', thisrownum);
482 invnum_input.value = values ? values.invnum : '';
483 invnum_input.onfocus = clearhint_invnum;
484 invnum_input.onchange = <% $opt{prefix} %>search_invnum;
485 invnum_cell.appendChild(invnum_input);
487 row.appendChild(invnum_cell);
489 var custnum_cell = document.createElement('TD');
491 var display_custnum_input = document.createElement('INPUT');
492 display_custnum_input.setAttribute('name', 'display_custnum'+thisrownum);
493 display_custnum_input.setAttribute('id', 'display_custnum'+thisrownum);
494 display_custnum_input.style.textAlign = 'right';
495 display_custnum_input.setAttribute('size', 8);
496 display_custnum_input.setAttribute('maxlength', 12);
497 display_custnum_input.setAttribute('rownum', thisrownum);
498 display_custnum_input.value = values ? values.custnum : '';
499 display_custnum_input.onfocus = clearhint_custnum;
500 display_custnum_input.onchange = <% $opt{prefix} %>search_custnum;
501 custnum_cell.appendChild(display_custnum_input);
503 var custnum_input = document.createElement('INPUT');
504 custnum_input.type = 'hidden';
505 custnum_input.setAttribute('name', 'custnum'+thisrownum);
506 custnum_input.setAttribute('id', 'custnum'+thisrownum);
507 custnum_input.setAttribute('rownum', thisrownum);
508 custnum_input.value = values ? values.custnum : '';
509 custnum_cell.appendChild(custnum_input);
511 row.appendChild(custnum_cell);
513 var status_cell = document.createElement('TD');
514 status_cell.style.textAlign = 'center';
516 var status_span = document.createElement('SPAN');
517 status_span.setAttribute('id', 'status'+thisrownum+'_text');
518 status_span.style.fontWeight = 'bold';
519 status_span.style.color = values ? values.statuscolor : '';
520 status_span.setAttribute('rownum', thisrownum);
521 status_span.appendChild(
522 document.createTextNode(values ? values.status : '')
524 status_cell.appendChild(status_span);
526 var status_input = document.createElement('INPUT');
527 status_input.setAttribute('type', 'hidden');
528 status_input.setAttribute('name', 'status'+thisrownum);
529 status_input.setAttribute('id', 'status'+thisrownum);
530 status_input.setAttribute('rownum', thisrownum);
531 status_input.value = values ? values.status : '';
532 status_cell.appendChild(status_input);
534 var statuscolor_input = document.createElement('INPUT');
535 statuscolor_input.setAttribute('type', 'hidden');
536 statuscolor_input.setAttribute('name', 'statuscolor'+thisrownum);
537 statuscolor_input.setAttribute('id', 'statuscolor'+thisrownum);
538 statuscolor_input.setAttribute('rownum', thisrownum);
539 statuscolor_input.value = values ? values.statuscolor : '';
540 status_cell.appendChild(statuscolor_input);
542 row.appendChild(status_cell);
544 var customer_cell = document.createElement('TD');
546 var customer_input = document.createElement('INPUT');
547 customer_input.setAttribute('name', 'customer'+thisrownum);
548 customer_input.setAttribute('id', 'customer'+thisrownum);
549 customer_input.setAttribute('size', 64);
550 customer_input.setAttribute('value', '(last name or company)' );
551 customer_input.setAttribute('rownum', thisrownum);
552 customer_input.value = values ? values.customer : '';
553 customer_input.onfocus = clearhint_customer;
554 customer_input.onclick = clearhint_customer;
555 customer_input.onchange = <% $opt{prefix} %>search_customer;
556 customer_cell.appendChild(customer_input);
558 var customer_select = document.createElement('SELECT');
559 customer_select.setAttribute('name', 'cust_select'+thisrownum);
560 customer_select.setAttribute('id', 'cust_select'+thisrownum);
561 customer_select.setAttribute('rownum', thisrownum);
562 customer_select.style.color = '#ff0000';
563 customer_select.style.display = 'none';
564 customer_select.onchange = select_customer;
565 customer_cell.appendChild(customer_select);
567 row.appendChild(customer_cell);
569 var balance_cell = document.createElement('TD');
571 balance_cell.style.textAlign = 'right';
572 balance_cell.appendChild(document.createTextNode('<%$money_char%>'));
574 var balance_span = document.createElement('SPAN');
575 balance_span.setAttribute('id', 'balance'+thisrownum+'_text');
576 balance_span.setAttribute('rownum', thisrownum);
577 balance_cell.appendChild(balance_span);
579 balance_cell.appendChild(
580 document.createTextNode(String.fromCharCode(160) + (values ? values.balance : '')) //
583 var balance_input = document.createElement('INPUT');
584 balance_input.setAttribute('type', 'hidden');
585 balance_input.setAttribute('name', 'balance'+thisrownum);
586 balance_input.setAttribute('id', 'balance'+thisrownum);
587 balance_input.setAttribute('rownum', thisrownum);
588 balance_input.value = values ? values.balance : '';
589 balance_cell.appendChild(balance_input);
591 var num_open_input = document.createElement('INPUT');
592 num_open_input.setAttribute('type', 'hidden');
593 num_open_input.setAttribute('name', 'num_open'+thisrownum);
594 num_open_input.setAttribute('id', 'num_open'+thisrownum);
595 num_open_input.setAttribute('rownum', thisrownum);
596 balance_cell.appendChild(num_open_input);
598 row.appendChild(balance_cell);
601 % foreach my $field ( @{$opt{fields}} ) {
603 var my_cell = document.createElement('TD');
604 my_cell.setAttribute('align', '<% $align{ $opt{align}->[$col] || 'l' } %>');
606 % if ($types->[$col] eq 'immutable') {
607 var my_text = document.createTextNode(values ? values.<% $field %> : '');
608 my_cell.appendChild(my_text);
611 % my $name = (ref($field) eq 'CODE') ? "column${col}_" : $field;
612 var my_input = document.createElement('INPUT');
613 my_input.setAttribute('name', '<% $name %>'+thisrownum);
614 my_input.setAttribute('id', '<% $name %>'+thisrownum);
615 my_input.style.textAlign = '<% $align{ $opt{align}->[$col] || 'l' } %>';
616 my_input.setAttribute('size', <% $sizes->[$col] || 10 %>);
617 my_input.setAttribute('rownum', thisrownum);
618 % if ( $types->[$col] eq 'immutable' ) {
619 my_input.setAttribute('type', 'hidden');
620 % } elsif ( $types->[$col] eq 'checkbox' ) {
621 my_input.setAttribute('type', 'checkbox');
622 my_input.checked = (values && values.<% $field %>) ? true : false;
624 my_input.value = (values && values.<% $field %>) || '';
625 % if ( $opt{onchange}->[$col] ) {
626 my_input.onchange = <% $opt{onchange}->[$col] %>;
628 % elsif ( $opt{footer}->[$col] eq '_TOTAL' ) {
629 my_input.onchange = <% $opt{prefix} %>calc_total<%$col%>;
630 my_input.onkeyup = <% $opt{prefix} %>calc_total<%$col%>;
632 my_cell.appendChild(my_input);
634 row.appendChild(my_cell);
639 var td_delete = document.createElement('TD');
640 td_delete.setAttribute('id', 'delete'+thisrownum);
641 row.appendChild(td_delete);
643 <% $opt{prefix} %>addDeleteButton(thisrownum);
646 update_num_open(thisrownum, (values ? values.num_open : '0'));
648 % if ( $opt{add_row_callback} ) {
649 <% $opt{add_row_callback} %>(thisrownum,
650 '<% $opt{prefix} %>', values);
653 // update the total number of rows display
654 allrows.push(thisrownum);
655 if (values) <% $opt{prefix} %>totalrows++;
656 <% $opt{prefix} %>updateTotalRow();
658 // update the next available row number
659 if (thisrownum >= <% $opt{prefix} %>rownum) {
660 <% $opt{prefix} %>rownum = thisrownum + 1;
668 <TABLE ID="<% $opt{prefix} %>OneTrueTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
676 % foreach my $header ( @{$opt{header}} ) {
677 <TH><% $header %></TH>
681 % my @rownums = sort { $a <=> $b } map /^custnum(\d+)$/, keys %$param;
683 <TH COLSPAN=5 ID="<% $opt{'prefix'} %>_TOTAL_TOTAL">
684 Total <% @rownums || 0 %>
685 <% PL($opt{name_singular} || 'customer', ( @rownums || 0 ) ) %>
688 % foreach my $footer ( @{$opt{footer}} ) {
689 % my $align = $align{ $opt{'footer_align'}->[$col] || 'c' };
690 % if ($footer eq '_TOTAL' ) {
691 % my $id = $opt{'fields'}->[$col];
692 % $id = ref($id) ? "column${col}_TOTAL" : "${id}_TOTAL";
693 <TH ALIGN="<% $align %>" ID="<% $id %>"> <% sprintf('%.2f', $total[$col] ) %></TH>
695 <TH ALIGN="<% $align %>"><% $footer %></TH>
703 <SCRIPT TYPE="text/javascript">
705 <% $opt{prefix} %>total_el =
706 document.getElementById("<% $opt{'prefix'} %>_TOTAL_TOTAL");
708 <% $opt{prefix} %>rownum = 1; // really more of a "next row", used by addrow
709 <% $opt{prefix} %>totalrows = 0; // will not include empty rows
710 <% $opt{prefix} %>allrows = []; // will include empty rows
712 % foreach my $row ( @rownums ) {
713 % if ( grep($param->{$_.$row},qw(invnum display_custnum custnum status statuscolor customer balance),@{$opt{fields}} ) ) {
715 <% $opt{prefix} %>addRow({
717 num_open:<% $param->{"num_open$row"} |js_string %>,
718 invnum:<% $param->{"invnum$row"} |js_string %>,
719 display_custnum:<% $param->{"display_custnum$row"} |js_string %>,
720 custnum:<% $param->{"custnum$row"} |js_string %>,
721 status:<% $param->{"status$row"} |js_string %>,
722 statuscolor:<% $param->{"statuscolor$row"} |js_string %>,
723 customer:<% $param->{"customer$row"} |js_string %>,
724 balance:<% $param->{"balance$row"} |js_string %>,
726 % foreach my $field ( @{$opt{fields}} ) {
728 % if ( ref($field) eq 'CODE' ) {
729 % $value = &{$field}($row,$param) || '';
731 % $value = $param->{"$field$row"} || '';
733 % my $name = (ref($field) eq 'CODE') ? "column${col}" : "$field";
734 <% $name %>:<% $value |js_string %>,
741 <% $opt{prefix} %>addRow();
744 % foreach my $footer ( @{$opt{footer}} ) {
745 % if ($footer eq '_TOTAL' ) {
746 % my $name = $opt{fields}->[$col];
747 % $name = ref($name) ? "column$col" : $name;
748 var <% $opt{prefix} %>th_el = document.getElementById("<%$name%>_TOTAL");
749 function <% $opt{prefix} %>calc_total<% $col %>() {
752 for (i = 0; i < <% $opt{prefix} %>allrows.length; i++) {
753 var value = document.getElementById("<%$name%>"+<% $opt{prefix} %>allrows[i]).value;
754 value = parseFloat(value);
755 if ( ! isNaN(value) ) {
756 total = total + value;
759 <% $opt{prefix} %>th_el.innerHTML = ' ' + total.toFixed(2);
761 <% $opt{prefix} %>calc_total<% $col %>()
767 <% include('/elements/xmlhttp.html',
768 'url' => $p. 'misc/xmlhttp-cust_main-search.cgi',
769 'subs' => [qw( custnum_search smart_search invnum_search )],
776 my $conf = new FS::Conf;
778 ## caution when using prefix, it isn't consistently applied to tag id/name
779 $opt{prefix} = '' unless defined $opt{prefix};
780 $opt{prefix} .= '_' if $opt{prefix};
782 my $types = $opt{'type'} ? [ @{$opt{'type'}} ] : [];
783 my $sizes = $opt{'size'} ? [ @{$opt{'size'}} ] : [];
785 my $param = $opt{param};
786 $param = $cgi->Vars if $cgi->param('error');
788 $opt{$_} ||= [] foreach qw(align color footer footer_align);
790 my @total = map 0, @{$opt{footer}};
798 my $money_char = $conf->config('money_char') || '$';