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 function clearhint_invnum() {
46 if ( this.value == 'Not found' || this.value == 'Multiple' ) {
48 this.style.color = '#000000';
53 function clearhint_custnum() {
55 if ( this.value == 'Not found' || this.value == 'Multiple' ) {
57 this.style.color = '#000000';
62 function clearhint_customer() {
64 this.style.color = '#000000';
66 if ( this.value == '(last name or company)' || this.value == 'Not found' )
71 function <% $opt{prefix} %>search_invnum() {
73 this.style.color = '#000000'
75 var invnum_obj = this;
76 var searchrow = this.getAttribute('rownum');
77 var invnum = this.value;
79 if ( invnum == 'searching...' || invnum == 'Not found' || invnum == '' )
82 if ( this.getAttribute('magic') == 'nosearch' ) {
83 this.setAttribute('magic', '');
87 if ( ( <% $opt{prefix} %>rownum - searchrow ) == 1 ) {
88 <% $opt{prefix} %>addRow();
90 var customer = document.getElementById('customer'+searchrow);
91 customer.value = 'searching...';
92 customer.disabled = true;
93 customer.style.color = '#000000';
94 customer.style.backgroundColor = '#dddddd';
96 var customer_select = document.getElementById('cust_select'+searchrow);
98 customer.style.display = '';
99 customer_select.style.display = 'none';
101 var custnum_obj = document.getElementById('custnum'+searchrow);
102 var balance = document.getElementById('balance'+searchrow);
103 var status = document.getElementById('status'+searchrow);
104 balance.innerHTML = '';
105 status.innerHTML = '';
107 function search_invnum_update(customers) {
109 var customerArray = eval('(' + customers + ')');
111 custnum_obj.disabled = false;
112 custnum_obj.style.backgroundColor = '#ffffff';
113 customer.disabled = false;
114 customer.style.backgroundColor = '#ffffff';
116 if ( customerArray.length == 0 ) {
118 custnum_obj.value = 'Not found';
119 customer.value = 'Not found';
120 custnum_obj.style.color = '#ff0000';
121 customer.style.color = '#ff0000';
123 customer.style.display = '';
124 customer_select.style.display = 'none';
126 } else if ( customerArray.length == 5 ) {
128 custnum_obj.value = customerArray[0];
129 custnum_obj.style.color = '#000000';
130 customer.value = customerArray[1];
131 balance.innerHTML = '<% $money_char %>' + customerArray[2] + ' ';
132 status.innerHTML = customerArray[3];
133 status.style.color = '#'+customerArray[4];
135 customer.style.display = '';
136 customer_select.style.display = 'none';
138 % if ( $opt{invnum_update_callback} ) {
139 <% $opt{invnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
146 invnum_search( invnum, search_invnum_update );
151 function <% $opt{prefix} %>search_custnum() {
153 this.style.color = '#000000'
155 var custnum_obj = this;
156 var searchrow = this.getAttribute('rownum');
157 var custnum = this.value;
159 if ( custnum == 'searching...' || custnum == 'Not found' || custnum == '' )
162 if ( this.getAttribute('magic') == 'nosearch' ) {
163 this.setAttribute('magic', '');
167 if ( ( <% $opt{prefix} %>rownum - searchrow ) == 1 ) {
168 <% $opt{prefix} %>addRow();
170 var customer = document.getElementById('customer'+searchrow);
171 customer.value = 'searching...';
172 customer.disabled = true;
173 customer.style.color = '#000000';
174 customer.style.backgroundColor = '#dddddd';
176 var customer_select = document.getElementById('cust_select'+searchrow);
178 customer.style.display = '';
179 customer_select.style.display = 'none';
181 var invnum = document.getElementById('invnum'+searchrow);
184 var balance = document.getElementById('balance'+searchrow);
185 balance.innerHTML = '';
187 var status = document.getElementById('status'+searchrow);
188 status.innerHTML = '';
190 function search_custnum_update(customers) {
192 var customerArray = eval('(' + customers + ')');
194 customer.disabled = false;
195 customer.style.backgroundColor = '#ffffff';
197 if ( customerArray.length == 0 ) {
199 customer.value = 'Not found';
200 customer.style.color = '#ff0000';
201 custnum_obj.style.color = '#ff0000';
203 } else if ( customerArray.length == 5 ) {
205 custnum_obj.value = customerArray[0];
206 custnum_obj.style.color = '#000000';
207 customer.value = customerArray[1];
208 balance.innerHTML = '<% $money_char %>' + customerArray[2] + ' ';
209 status.innerHTML = customerArray[3];
210 status.style.color = '#'+customerArray[4];
212 customer.style.display = '';
213 customer_select.style.display = 'none';
215 % if ( $opt{custnum_update_callback} ) {
216 <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
221 custnum_search(custnum, search_custnum_update );
225 function <% $opt{prefix} %>search_customer() {
227 var customer_obj = this;
228 var searchrow = this.getAttribute('rownum');
229 var customer = this.value;
231 if ( customer == 'searching...' || customer == 'Not found' || customer == '' )
234 if ( this.getAttribute('magic') == 'nosearch' ) {
235 this.setAttribute('magic', '');
239 if ( ( <% $opt{prefix} %>rownum - searchrow ) == 1 ) {
240 <% $opt{prefix} %>addRow();
243 var invnum = document.getElementById('invnum'+searchrow);
246 var custnum_obj = document.getElementById('custnum'+searchrow);
247 custnum_obj.value = 'searching...';
248 custnum_obj.disabled = true;
249 custnum_obj.style.color = '#000000';
250 custnum_obj.style.backgroundColor = '#dddddd';
252 var customer_select = document.getElementById('cust_select'+searchrow);
254 var balance = document.getElementById('balance'+searchrow);
255 balance.innerHTML = '';
257 var status = document.getElementById('status'+searchrow);
258 status.innerHTML = '';
260 function search_customer_update(customers) {
262 var customerArray = eval('(' + customers + ')');
264 custnum_obj.disabled = false;
265 custnum_obj.style.backgroundColor = '#ffffff';
267 if ( customerArray.length == 0 ) {
269 custnum_obj.value = 'Not found';
270 custnum_obj.style.color = '#ff0000';
271 customer_obj.style.color = '#ff0000';
273 customer_obj.style.display = '';
274 customer_select.style.display = 'none';
276 } else if ( customerArray.length == 1 ) {
278 custnum_obj.value = customerArray[0][0];
279 customer_obj.value = customerArray[0][1];
280 balance.innerHTML = '<% $money_char %>' + customerArray[0][2] + ' ';
281 status.innerHTML = customerArray[0][3];
282 status.style.color = '#'+customerArray[0][4];
284 customer_obj.style.display = '';
285 customer_select.style.display = 'none';
287 % if ( $opt{custnum_update_callback} ) {
288 <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
293 custnum_obj.value = 'Multiple'; // or something
294 custnum_obj.style.color = '#ff0000';
296 //blank the current list
297 for ( var i = customer_select.length; i >= 0; i-- )
298 customer_select.options[i] = null;
300 opt(customer_select, '', 'Multiple customers match "' + customer + '" - select one', '#ff0000');
302 //add the multiple customers
303 for ( var s = 0; s < customerArray.length; s++ )
304 opt(customer_select, customerArray[s][0] + '_' + customerArray[s][2] + '_' + customerArray[s][3] + '_' + customerArray[s][4], customerArray[s][1], '#000000');
306 opt(customer_select, 'cancel', '(Edit search string)', '#000000');
308 customer_obj.style.display = 'none';
310 customer_select.style.display = '';
316 smart_search( customer, search_customer_update );
320 function select_customer() {
322 var custnum_balance_status = this.options[this.selectedIndex].value;
323 var customer = this.options[this.selectedIndex].text;
325 var searchrow = this.getAttribute('rownum');
326 var custnum_obj = document.getElementById('custnum'+searchrow);
327 var customer_obj = document.getElementById('customer'+searchrow);
328 var balance_obj = document.getElementById('balance'+searchrow);
329 var status_obj = document.getElementById('status'+searchrow);
331 if ( custnum_balance_status == '' ) {
333 } else if ( custnum_balance_status == 'cancel' ) {
335 custnum_obj.value = '';
336 custnum_obj.style.color = '#000000';
338 this.style.display = 'none';
339 customer_obj.style.display = '';
340 customer_obj.focus();
344 var pos_underscore1 = custnum_balance_status.indexOf('_');
345 var pos_underscore2 = custnum_balance_status.indexOf('_',pos_underscore1+1);
346 var pos_underscore3 = custnum_balance_status.indexOf('_',pos_underscore2+1);
347 var custnum = custnum_balance_status.substring(0,pos_underscore1);
348 var balance = custnum_balance_status.substring(pos_underscore1+1,pos_underscore2) + ' ';
349 var status = custnum_balance_status.substring(pos_underscore2+1,pos_underscore3);
350 var color = custnum_balance_status.substring(pos_underscore3+1);
352 custnum_obj.value = custnum;
353 custnum_obj.style.color = '#000000';
355 customer_obj.value = customer;
356 customer_obj.style.color = '#000000';
358 balance_obj.innerHTML = '<% $money_char %>' + balance;
360 status_obj.innerHTML = status;
361 status_obj.style.color = '#'+color;
363 this.style.display = 'none';
364 customer_obj.style.display = '';
366 % if ( $opt{custnum_update_callback} ) {
367 <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
374 function opt(what,value,text,color) {
375 var optionName = new Option(text, value, false, false);
376 optionName.style.color = color;
377 var length = what.length;
378 what.options[length] = optionName;
383 <TABLE ID="<% $opt{prefix} %>OneTrueTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
391 % foreach my $header ( @{$opt{header}} ) {
392 <TH><% $header %></TH>
396 % for ( $row = 0; exists($param->{"custnum$row"}); $row++ ) {
401 NAME = "invnum<% $row %>"
402 ID = "invnum<% $row %>"
405 STYLE = "text-align:right;"
406 VALUE = "<% $param->{"invnum$row"} %>"
407 rownum = "<% $row %>"
409 <SCRIPT TYPE="text/javascript">
410 var invnum_input<% $row %> = document.getElementById("invnum<% $row %>");
411 invnum_input<% $row %>.onfocus = clearhint_invnum;
412 invnum_input<% $row %>.onchange = <% $opt{prefix} %>search_invnum;
418 NAME = "custnum<% $row %>"
419 ID = "custnum<% $row %>"
422 STYLE = "text-align:right;"
423 VALUE = "<% $param->{"custnum$row"} %>"
424 rownum = "<% $row %>"
426 <SCRIPT TYPE="text/javascript">
427 var custnum_input<% $row %> = document.getElementById("custnum<% $row %>");
428 custnum_input<% $row %>.onfocus = clearhint_custnum;
429 custnum_input<% $row %>.onchange = <% $opt{prefix} %>search_custnum;
435 NAME = "status<% $row %>"
436 ID = "status<% $row %>"
437 rownum = "<% $row %>"
438 STYLE = "text-align:center; font-weight: bold"
444 <INPUT TYPE="text" NAME="customer<% $row %>" ID="customer<% $row %>" SIZE=64 VALUE="<% $param->{"customer$row"} %>" rownum="<% $row %>">
445 <SCRIPT TYPE="text/javascript">
446 var customer_input<% $row %> = document.getElementById("customer<% $row %>");
447 customer_input<% $row %>.onfocus = clearhint_customer;
448 customer_input<% $row %>.onclick = clearhint_customer;
449 customer_input<% $row %>.onchange = <% $opt{prefix} %>search_customer;
451 <SELECT NAME="cust_select<% $row %>" ID="cust_select<% $row %>" rownum="<% $row %>" STYLE="color:#ff0000; display:none">
453 <SCRIPT TYPE="text/javascript">
454 var customer_select<% $row %> = document.getElementById("cust_select<% $row %>");
455 customer_select<% $row %>.onchange = select_customer;
460 % foreach my $field ( @{$opt{fields}} ) {
462 % if ( ref($field) eq 'CODE' ) {
463 % $value = &{$field}($row,$param);
465 % $value = $param->{"$field$row"};
467 % my $name = (ref($field) eq 'CODE') ? "column${col}_$row" : "$field$row";
468 % my $align = $align{ $opt{align}->[$col] || 'l' };
469 % my $size = $sizes->[$col] || 10;
470 % my $color = $opt{color}->[$col];
471 % my $font = $color ? qq(<FONT COLOR="$color">) : '';
473 % if ( $opt{footer}->[$col] eq '_TOTAL' ) {
474 % $total[$col] += $value;
475 % $onchange = $opt{prefix}. "calc_total$col();";
476 % $onchange = qq(onchange="$onchange" onkeyup="$onchange");
478 <TD ALIGN="<% $align %>">
479 % if (! $types->[$col] || $types->[$col] eq 'text') {
484 STYLE = "text-align: <% $align %>;"
485 VALUE = "<% $value %>"
488 % } elsif ($types->[$col] eq 'immutable') {
489 <% $font %><% $value %><% $font ? '</FONT>' : '' %>
490 <INPUT TYPE="hidden" ID="<% $name %>" NAME="<% $name %>" VALUE="<% $value %>" >
492 Cannot represent unknown type: <% $types->[$col] %>
497 <TD STYLE="text-align:right;">
499 NAME = "balance<% $row %>"
500 ID = "balance<% $row %>"
501 rownum = "<% $row %>"
510 <TH COLSPAN=5 ID="<% $opt{'prefix'} %>_TOTAL_TOTAL">
511 Total <% $row ? $row-1 : 0 %>
512 <% PL($opt{name_singular} || 'customer', ( $row ? $row-1 : 0 ) ) %>
515 % foreach my $footer ( @{$opt{footer}} ) {
516 % my $align = $align{ $opt{'footer_align'}->[$col] || 'c' };
517 % if ($footer eq '_TOTAL' ) {
518 % my $id = $opt{'fields'}->[$col];
519 % $id = ref($id) ? "column${col}_TOTAL" : "${id}_TOTAL";
520 <TH ALIGN="<% $align %>" ID="<% $id %>"> <% sprintf('%.2f', $total[$col] ) %></TH>
522 <TH ALIGN="<% $align %>"><% $footer %></TH>
530 <SCRIPT TYPE="text/javascript">
532 % foreach my $footer ( @{$opt{footer}} ) {
533 % if ($footer eq '_TOTAL' ) {
534 % my $name = $opt{fields}->[$col];
535 % $name = ref($name) ? "column$col" : $name;
536 var <% $opt{prefix}.$name %>_CACHE = new Array ();
537 var <% $opt{prefix} %>th_el = document.getElementById("<%$name%>_TOTAL");
538 function <% $opt{prefix} %>calc_total<% $col %>() {
543 ( <% $opt{prefix}.$name%>_CACHE[row] =
544 <% $opt{prefix}.$name%>_CACHE[row]
545 || document.getElementById("<%$name%>"+row)
551 var value = <%$name%>_CACHE[row].value;
552 value = parseFloat(value);
553 if ( ! isNaN(value) ) {
554 total = total + value;
557 <% $opt{prefix} %>th_el.innerHTML = ' ' + total.toFixed(2);
565 <% include('/elements/xmlhttp.html',
566 'url' => $p. 'misc/xmlhttp-cust_main-search.cgi',
567 'subs' => [qw( custnum_search smart_search invnum_search )],
571 <SCRIPT TYPE="text/javascript">
573 var <% $opt{prefix} %>total_el =
574 document.getElementById("<% $opt{'prefix'} %>_TOTAL_TOTAL");
576 var <% $opt{prefix} %>rownum = <% $row %>;
578 function <% $opt{prefix} %>addRow() {
580 var table = document.getElementById('<% $opt{prefix} %>OneTrueTable');
581 var tablebody = table.getElementsByTagName('tbody').item(0);
583 var row = table.insertRow(rownum+1);
585 var invnum_cell = document.createElement('TD');
587 var invnum_input = document.createElement('INPUT');
588 invnum_input.setAttribute('name', 'invnum'+<% $opt{prefix} %>rownum);
589 invnum_input.setAttribute('id', 'invnum'+<% $opt{prefix} %>rownum);
590 invnum_input.style.textAlign = 'right';
591 invnum_input.setAttribute('size', 8);
592 invnum_input.setAttribute('maxlength', 12);
593 invnum_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
594 invnum_input.onfocus = clearhint_invnum;
595 invnum_input.onchange = <% $opt{prefix} %>search_invnum;
596 invnum_cell.appendChild(invnum_input);
598 row.appendChild(invnum_cell);
600 var custnum_cell = document.createElement('TD');
602 var custnum_input = document.createElement('INPUT');
603 custnum_input.setAttribute('name', 'custnum'+<% $opt{prefix} %>rownum);
604 custnum_input.setAttribute('id', 'custnum'+<% $opt{prefix} %>rownum);
605 custnum_input.style.textAlign = 'right';
606 custnum_input.setAttribute('size', 8);
607 custnum_input.setAttribute('maxlength', 12);
608 custnum_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
609 custnum_input.onfocus = clearhint_custnum;
610 custnum_input.onchange = <% $opt{prefix} %>search_custnum;
611 custnum_cell.appendChild(custnum_input);
613 row.appendChild(custnum_cell);
615 var status_cell = document.createElement('TD');
617 var status_span = document.createElement('SPAN');
618 status_span.setAttribute('name', 'status'+<% $opt{prefix} %>rownum);
619 status_span.setAttribute('id', 'status'+<% $opt{prefix} %>rownum);
620 status_span.style.textAlign = 'center';
621 status_span.style.fontWeight = 'bold';
622 status_span.setAttribute('rownum', <% $opt{prefix} %>rownum);
623 status_cell.appendChild(status_span);
625 row.appendChild(status_cell);
627 var customer_cell = document.createElement('TD');
629 var customer_input = document.createElement('INPUT');
630 customer_input.setAttribute('name', 'customer'+<% $opt{prefix} %>rownum);
631 customer_input.setAttribute('id', 'customer'+<% $opt{prefix} %>rownum);
632 customer_input.setAttribute('size', 64);
633 customer_input.setAttribute('value', '(last name or company)' );
634 customer_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
635 customer_input.onfocus = clearhint_customer;
636 customer_input.onclick = clearhint_customer;
637 customer_input.onchange = <% $opt{prefix} %>search_customer;
638 customer_cell.appendChild(customer_input);
640 var customer_select = document.createElement('SELECT');
641 customer_select.setAttribute('name', 'cust_select'+<% $opt{prefix} %>rownum);
642 customer_select.setAttribute('id', 'cust_select'+<% $opt{prefix} %>rownum);
643 customer_select.setAttribute('rownum', <% $opt{prefix} %>rownum);
644 customer_select.style.color = '#ff0000';
645 customer_select.style.display = 'none';
646 customer_select.onchange = select_customer;
647 customer_cell.appendChild(customer_select);
649 row.appendChild(customer_cell);
651 var balance_cell = document.createElement('TD');
652 balance_cell.style.textAlign = 'right';
654 var balance_span = document.createElement('SPAN');
655 balance_span.setAttribute('name', 'balance'+<% $opt{prefix} %>rownum);
656 balance_span.setAttribute('id', 'balance'+<% $opt{prefix} %>rownum);
657 balance_span.setAttribute('rownum', <% $opt{prefix} %>rownum);
658 balance_cell.appendChild(balance_span);
660 row.appendChild(balance_cell);
663 % foreach my $field ( @{$opt{fields}} ) {
665 var my_cell = document.createElement('TD');
666 my_cell.setAttribute('align', '<% $align{ $opt{align}->[$col] || 'l' } %>');
668 % if ($types->[$col] eq 'immutable') {
670 % if ( ref($field) eq 'CODE' ) {
671 % $value = &{$field}($row,$param);
673 % $value = $param->{"$field$row"};
675 var my_text = document.createTextNode('<% $value %>');
676 my_cell.appendChild(my_text);
679 var my_input = document.createElement('INPUT');
680 my_input.setAttribute('name', '<% $field %>'+<% $opt{prefix} %>rownum);
681 my_input.setAttribute('id', '<% $field %>'+<% $opt{prefix} %>rownum);
682 my_input.style.textAlign = '<% $align{ $opt{align}->[$col] || 'l' } %>';
683 my_input.setAttribute('size', <% $sizes->[$col] || 10 %>);
684 % if ($types->[$col] eq 'immutable') {
685 my_input.setAttribute('type', 'hidden');
687 % if ( $opt{footer}->[$col] eq '_TOTAL' ) {
688 my_input.onchange = <% $opt{prefix} %>calc_total<%$col%>;
689 my_input.onkeyup = <% $opt{prefix} %>calc_total<%$col%>;
691 my_cell.appendChild(my_input);
693 row.appendChild(my_cell);
698 //update the total # of rows display
699 if ( <% $opt{prefix} %>rownum == 1 ) {
700 <% $opt{prefix} %>total_el.innerHTML =
702 + <% $opt{prefix} %>rownum
703 + ' <% $opt{name_singular} || 'customer' %>';
705 <% $opt{prefix} %>total_el.innerHTML =
707 + <% $opt{prefix} %>rownum
708 + ' <% PL($opt{name_singular} || 'customer') %>';
711 <% $opt{prefix} %>rownum++;
715 % unless ($cgi->param('error')) {
716 <% $opt{prefix} %>addRow();
723 my $conf = new FS::Conf;
725 $opt{prefix} = '' unless defined $opt{prefix};
726 $opt{prefix} .= '_' if $opt{prefix};
728 my $types = $opt{'types'} ? [ @{$opt{'types'}} ] : [];
729 my $sizes = $opt{'size'} ? [ @{$opt{'size'}} ] : [];
731 my $param = $opt{param};
732 $param = $cgi->Vars if $cgi->param('error');
734 $opt{$_} ||= [] foreach qw(align color footer footer_align);
736 my @total = map 0, @{$opt{footer}};
744 my $money_char = $conf->config('money_char') || '$';