quick payment tool improvements, RT10698
[freeside.git] / httemplate / elements / customer-table.html
1 <%doc>
2
3 Example:
4
5   include( '/elements/customer-table.html',
6
7              ###
8              # required
9              ###
10
11              #listrefs...
12              'header'        => [ '#', 'Item' ],
13              'fields'        => [
14                                   'column',
15                                   sub { my ($row,$param) = @_;
16                                         $param->{"column$row"};
17                                       },
18                                 ],
19
20              ###
21              # optional
22              ###
23
24              'name_singular' => 'customer', #label
25              'custnum_update_callback' => 'name_of_js_callback' #passed a rownum
26
27              #listrefs
28              'types'         => ['immutable', ''], # immutable or ''/text
29              'align'         => [ 'c', 'l', 'r', '' ],
30              'size'          => [],                # sizes ignored for immutable
31              'color'         => [],
32              'footer'        => ['string', '_TOTAL'], # strings or the special
33                                                       #value _TOTAL
34              'footer_align'  => [ 'c', 'l', 'r', '' ],
35
36              'param'         => { column0 => 1 },  # preset column of row 0 to 1
37
38          )
39
40 </%doc>
41
42 <SCRIPT TYPE="text/javascript">
43
44   function clearhint_invnum() {
45
46     if ( this.value == 'Not found' || this.value == 'Multiple' ) {
47       this.value = '';
48       this.style.color = '#000000';
49     }
50
51   }
52
53   function clearhint_custnum() {
54
55     if ( this.value == 'Not found' || this.value == 'Multiple' ) {
56       this.value = '';
57       this.style.color = '#000000';
58     }
59
60   }
61
62   function clearhint_customer() {
63
64     this.style.color = '#000000';
65
66     if ( this.value == '(last name or company)' || this.value == 'Not found' )
67       this.value = '';
68
69   }
70   
71   function <% $opt{prefix} %>search_invnum() {
72
73     this.style.color = '#000000'
74
75     var invnum_obj = this;
76     var searchrow = this.getAttribute('rownum');
77     var invnum = this.value;
78
79     if ( invnum == 'searching...' || invnum == 'Not found' || invnum == '' )
80       return;
81
82     if ( this.getAttribute('magic') == 'nosearch' ) {
83       this.setAttribute('magic', '');
84       return;
85     }
86
87     if ( ( <% $opt{prefix} %>rownum - searchrow ) == 1 ) {
88       <% $opt{prefix} %>addRow();
89     }
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';
95
96     var customer_select = document.getElementById('cust_select'+searchrow);
97
98     customer.style.display = '';
99     customer_select.style.display = 'none';
100     
101     var custnum_obj = document.getElementById('custnum'+searchrow);
102     var balance = document.getElementById('balance'+searchrow);
103     balance.value = '';
104
105     function search_invnum_update(customers) {
106       
107       var customerArray = eval('(' + customers + ')');
108
109       custnum_obj.disabled = false;
110       custnum_obj.style.backgroundColor = '#ffffff';
111       customer.disabled = false;
112       customer.style.backgroundColor = '#ffffff';
113
114       if ( customerArray.length == 0 ) {
115
116         custnum_obj.value = 'Not found';
117         customer.value = 'Not found';
118         custnum_obj.style.color = '#ff0000';
119         customer.style.color = '#ff0000';
120
121         customer.style.display = '';
122         customer_select.style.display = 'none';
123
124       } else if ( customerArray.length == 3 ) {
125
126         custnum_obj.value = customerArray[0];
127         custnum_obj.style.color = '#000000';
128         customer.value = customerArray[1];
129         balance.value = customerArray[2];
130
131         customer.style.display = '';
132         customer_select.style.display = 'none';
133
134 % if ( $opt{invnum_update_callback} ) {
135         <% $opt{invnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
136 % }
137
138       }
139
140     }
141
142     invnum_search( invnum, search_invnum_update );
143
144   }
145
146
147   function <% $opt{prefix} %>search_custnum() {
148
149     this.style.color = '#000000'
150
151     var custnum_obj = this;
152     var searchrow = this.getAttribute('rownum');
153     var custnum = this.value;
154
155     if ( custnum == 'searching...' || custnum == 'Not found' || custnum == '' )
156       return;
157
158     if ( this.getAttribute('magic') == 'nosearch' ) {
159       this.setAttribute('magic', '');
160       return;
161     }
162
163     if ( ( <% $opt{prefix} %>rownum - searchrow ) == 1 ) {
164       <% $opt{prefix} %>addRow();
165     }
166     var customer = document.getElementById('customer'+searchrow);
167     customer.value = 'searching...';
168     customer.disabled = true;
169     customer.style.color = '#000000';
170     customer.style.backgroundColor = '#dddddd';
171
172     var customer_select = document.getElementById('cust_select'+searchrow);
173
174     customer.style.display = '';
175     customer_select.style.display = 'none';
176
177     var invnum = document.getElementById('invnum'+searchrow);
178     invnum.value = '';
179     
180     var balance = document.getElementById('balance'+searchrow);
181     balance.value = '';
182
183     function search_custnum_update(name) {
184
185       var name = eval('(' + name + ')' );
186
187       customer.disabled = false;
188       customer.style.backgroundColor = '#ffffff';
189
190       if ( name.length > 0 ) {
191         customer.value = name;
192         customer.setAttribute('magic', 'nosearch');
193 % if ( $opt{custnum_update_callback} ) {
194         <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
195 % }
196       } else {
197         customer.value = 'Not found';
198         customer.style.color = '#ff0000';
199         custnum_obj.style.color = '#ff0000';
200
201       }
202
203     }
204
205     custnum_search( custnum, search_custnum_update );
206
207   }
208
209   function <% $opt{prefix} %>search_customer() {
210
211     var customer_obj = this;
212     var searchrow = this.getAttribute('rownum');
213     var customer = this.value;
214
215     if ( customer == 'searching...' || customer == 'Not found' || customer == '' )
216       return;
217
218     if ( this.getAttribute('magic') == 'nosearch' ) {
219       this.setAttribute('magic', '');
220       return;
221     }
222
223     if ( ( <% $opt{prefix} %>rownum - searchrow ) == 1 ) {
224       <% $opt{prefix} %>addRow();
225     }
226     
227     var invnum = document.getElementById('invnum'+searchrow);
228     invnum.value = '';
229
230     var custnum_obj = document.getElementById('custnum'+searchrow);
231     custnum_obj.value = 'searching...';
232     custnum_obj.disabled = true;
233     custnum_obj.style.color = '#000000';
234     custnum_obj.style.backgroundColor = '#dddddd';
235
236     var customer_select = document.getElementById('cust_select'+searchrow);
237     
238     var balance = document.getElementById('balance'+searchrow);
239     balance.value = '';
240
241     function search_customer_update(customers) {
242
243       var customerArray = eval('(' + customers + ')');
244
245       custnum_obj.disabled = false;
246       custnum_obj.style.backgroundColor = '#ffffff';
247
248       if ( customerArray.length == 0 ) {
249
250         custnum_obj.value = 'Not found';
251         custnum_obj.style.color = '#ff0000';
252         customer_obj.style.color = '#ff0000';
253
254         customer_obj.style.display = '';
255         customer_select.style.display = 'none';
256
257       } else if ( customerArray.length == 1 ) {
258
259         custnum_obj.value = customerArray[0][0];
260         customer_obj.value = customerArray[0][1];
261         balance.value = customerArray[0][2];
262
263         customer_obj.style.display = '';
264         customer_select.style.display = 'none';
265
266 % if ( $opt{custnum_update_callback} ) {
267         <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
268 % }
269
270       } else {
271
272         custnum_obj.value = 'Multiple'; // or something
273         custnum_obj.style.color = '#ff0000';
274
275         //blank the current list
276         for ( var i = customer_select.length; i >= 0; i-- )
277           customer_select.options[i] = null;
278
279         opt(customer_select, '', 'Multiple customers match "' + customer + '" - select one', '#ff0000');
280
281         //add the multiple customers
282         for ( var s = 0; s < customerArray.length; s++ )
283           opt(customer_select, customerArray[s][0], customerArray[s][1], '#000000');
284
285         opt(customer_select, 'cancel', '(Edit search string)', '#000000');
286
287         customer_obj.style.display = 'none';
288
289         customer_select.style.display = '';
290
291       }
292
293     }
294
295     smart_search( customer, search_customer_update );
296
297   }
298
299   function select_customer() {
300
301     var custnum = this.options[this.selectedIndex].value;
302     var customer = this.options[this.selectedIndex].text;
303
304     var searchrow = this.getAttribute('rownum');
305     var custnum_obj = document.getElementById('custnum'+searchrow);
306     var customer_obj = document.getElementById('customer'+searchrow);
307
308     if ( custnum == '' ) {
309
310     } else if ( custnum == 'cancel' ) {
311
312       custnum_obj.value = '';
313       custnum_obj.style.color = '#000000';
314
315       this.style.display = 'none';
316       customer_obj.style.display = '';
317       customer_obj.focus();
318
319     } else {
320
321       custnum_obj.value = custnum;
322       custnum_obj.style.color = '#000000';
323
324       customer_obj.value = customer;
325       customer_obj.style.color = '#000000';
326
327       this.style.display = 'none';
328       customer_obj.style.display = '';
329
330 % if ( $opt{custnum_update_callback} ) {
331       <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
332 % }
333
334     }
335
336   }
337
338   function opt(what,value,text,color) {
339     var optionName = new Option(text, value, false, false);
340     optionName.style.color = color;
341     var length = what.length;
342     what.options[length] = optionName;
343   }
344
345 </SCRIPT>
346
347 <TABLE ID="<% $opt{prefix} %>OneTrueTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
348
349 <TR>
350   <TH>Inv #</TH>
351   <TH>Cust #</TH>
352   <TH>Customer</TH>
353 % foreach my $header ( @{$opt{header}} ) {
354     <TH><% $header %></TH>
355 % }
356   <TH>Balance</TH>
357 </TR>
358 % my $row = 0;
359 % for ( $row = 0; exists($param->{"custnum$row"}); $row++ ) { 
360
361     <TR>
362       <TD>
363         <INPUT TYPE      = "text"
364                NAME      = "invnum<% $row %>"
365                ID        = "invnum<% $row %>"
366                SIZE      = 8
367                MAXLENGTH = 12
368                STYLE     = "text-align:right;"
369                VALUE     = "<% $param->{"invnum$row"} %>"
370                rownum    = "<% $row %>"
371         >
372         <SCRIPT TYPE="text/javascript">
373           var invnum_input<% $row %> = document.getElementById("invnum<% $row %>");
374           invnum_input<% $row %>.onfocus = clearhint_invnum;
375           invnum_input<% $row %>.onchange = <% $opt{prefix} %>search_invnum;
376         </SCRIPT>
377       </TD>
378
379       <TD>
380         <INPUT TYPE      = "text"
381                NAME      = "custnum<% $row %>"
382                ID        = "custnum<% $row %>"
383                SIZE      = 8
384                MAXLENGTH = 12
385                STYLE     = "text-align:right;"
386                VALUE     = "<% $param->{"custnum$row"} %>"
387                rownum    = "<% $row %>"
388         >
389         <SCRIPT TYPE="text/javascript">
390           var custnum_input<% $row %> = document.getElementById("custnum<% $row %>");
391           custnum_input<% $row %>.onfocus = clearhint_custnum;
392           custnum_input<% $row %>.onchange = <% $opt{prefix} %>search_custnum;
393         </SCRIPT>
394       </TD>
395
396       <TD>
397         <INPUT TYPE="text" NAME="customer<% $row %>" ID="customer<% $row %>" SIZE=64 VALUE="<% $param->{"customer$row"} %>" rownum="<% $row %>">
398           <SCRIPT TYPE="text/javascript">
399             var customer_input<% $row %> = document.getElementById("customer<% $row %>");
400             customer_input<% $row %>.onfocus = clearhint_customer;
401             customer_input<% $row %>.onclick = clearhint_customer;
402             customer_input<% $row %>.onchange = <% $opt{prefix} %>search_customer;
403           </SCRIPT>
404         <SELECT NAME="cust_select<% $row %>" ID="cust_select<% $row %>" rownum="<% $row %>" STYLE="color:#ff0000; display:none">
405         </SELECT>
406           <SCRIPT TYPE="text/javascript">
407             var customer_select<% $row %> = document.getElementById("cust_select<% $row %>");
408             customer_select<% $row %>.onchange = select_customer;
409           </SCRIPT>
410       </TD>
411
412 %   my $col = 0;
413 %   foreach my $field ( @{$opt{fields}} ) {
414 %     my $value;
415 %     if ( ref($field) eq 'CODE' ) {
416 %       $value = &{$field}($row,$param);
417 %     } else {
418 %       $value = $param->{"$field$row"}; 
419 %     }
420 %     my $name  = (ref($field) eq 'CODE') ? "column${col}_$row" : "$field$row";
421 %     my $align = $align{ $opt{align}->[$col] || 'l' };
422 %     my $size  = $sizes->[$col]  || 10;
423 %     my $color = $opt{color}->[$col];
424 %     my $font = $color ? qq(<FONT COLOR="$color">) : '';
425 %     my $onchange = '';
426 %     if ( $opt{footer}->[$col] eq '_TOTAL' ) {
427 %       $total[$col] += $value;
428 %       $onchange = $opt{prefix}. "calc_total$col();";
429 %       $onchange = qq(onchange="$onchange" onkeyup="$onchange");
430 %     }
431       <TD ALIGN="<% $align %>">
432 %     if (! $types->[$col] || $types->[$col] eq 'text') {
433         <INPUT TYPE  = "text"
434                NAME  = "<% $name %>"
435                ID    = "<% $name %>"
436                SIZE  = "<% $size %>"
437                STYLE = "text-align: <% $align %>;"
438                VALUE = "<% $value %>"
439                <% $onchange %>
440         >
441 %     } elsif ($types->[$col] eq 'immutable') {
442         <% $font %><% $value %><% $font ? '</FONT>' : '' %>
443         <INPUT TYPE="hidden" ID="<% $name %>" NAME="<% $name %>" VALUE="<% $value %>" >
444 %     } else {
445         Cannot represent unknown type: <% $types->[$col] %>
446 %     }
447       </TD>
448 %     $col++;
449 %   }
450       <TD>
451         <INPUT TYPE      = "text"
452                NAME      = "balance<% $row %>"
453                ID        = "balance<% $row %>"
454                SIZE      = 8
455                MAXLENGTH = 12
456                STYLE     = "text-align:right;"
457                VALUE     = "<% $param->{"balance$row"} %>"
458                rownum    = "<% $row %>"
459         >
460       </TD>
461     </TR>
462 % } 
463
464 <TR>
465   <TH COLSPAN=3 ID="<% $opt{'prefix'} %>_TOTAL_TOTAL">
466     Total <% $row ? $row-1 : 0 %>
467     <% PL($opt{name_singular} || 'customer', ( $row ? $row-1 : 0 ) ) %>
468   </TH>
469 % my $col = 0;
470 % foreach my $footer ( @{$opt{footer}} ) {
471 %   my $align = $align{ $opt{'footer_align'}->[$col] || 'c' };
472 %   if ($footer eq '_TOTAL' ) {
473 %     my $id = $opt{'fields'}->[$col];
474 %     $id = ref($id) ? "column${col}_TOTAL" : "${id}_TOTAL";
475       <TH ALIGN="<% $align %>" ID="<% $id %>">&nbsp;<% sprintf('%.2f', $total[$col] ) %></TH>
476 %   } else {
477       <TH ALIGN="<% $align %>"><% $footer %></TH>
478 %   }
479 %   $col++;
480 % }
481 </TR>
482
483 </TABLE>
484
485 <SCRIPT TYPE="text/javascript">
486 % my $col = 0;
487 % foreach my $footer ( @{$opt{footer}} ) {
488 %   if ($footer eq '_TOTAL' ) {
489 %     my $name = $opt{fields}->[$col];
490 %     $name = ref($name) ? "column$col" : $name;
491       var <% $opt{prefix}.$name %>_CACHE = new Array ();
492       var <% $opt{prefix} %>th_el = document.getElementById("<%$name%>_TOTAL");
493       function <% $opt{prefix} %>calc_total<% $col %>() {
494         var row = 0;
495         var total = 0;
496         for ( var row = 0;
497               
498               ( <% $opt{prefix}.$name%>_CACHE[row] =
499                                         <% $opt{prefix}.$name%>_CACHE[row]
500                                      || document.getElementById("<%$name%>"+row)
501               ) != null;
502               
503               row++
504             )
505         {
506           var value = <%$name%>_CACHE[row].value;
507           value = parseFloat(value);
508           if ( ! isNaN(value) ) {
509             total = total + value;
510           }
511         }
512         <% $opt{prefix} %>th_el.innerHTML = '&nbsp;' + total.toFixed(2);
513
514       }
515 %   }
516 %   $col++;
517 % }
518 </SCRIPT>
519
520 <% include('/elements/xmlhttp.html',
521               'url'  => $p. 'misc/xmlhttp-cust_main-search.cgi',
522               'subs' => [qw( custnum_search smart_search invnum_search )],
523            )
524 %>
525
526 <SCRIPT TYPE="text/javascript">
527
528   var <% $opt{prefix} %>total_el =
529     document.getElementById("<% $opt{'prefix'} %>_TOTAL_TOTAL");
530
531   var <% $opt{prefix} %>rownum = <% $row %>;
532
533   function <% $opt{prefix} %>addRow() {
534
535     var table = document.getElementById('<% $opt{prefix} %>OneTrueTable');
536     var tablebody = table.getElementsByTagName('tbody').item(0);
537
538     var row = table.insertRow(rownum+1);
539     
540     var invnum_cell = document.createElement('TD');
541
542       var invnum_input = document.createElement('INPUT');
543       invnum_input.setAttribute('name', 'invnum'+<% $opt{prefix} %>rownum);
544       invnum_input.setAttribute('id',   'invnum'+<% $opt{prefix} %>rownum);
545       invnum_input.style.textAlign = 'right';
546       invnum_input.setAttribute('size', 8);
547       invnum_input.setAttribute('maxlength', 12);
548       invnum_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
549       invnum_input.onfocus = clearhint_invnum;
550       invnum_input.onchange = <% $opt{prefix} %>search_invnum;
551       invnum_cell.appendChild(invnum_input);
552
553     row.appendChild(invnum_cell);
554
555     var custnum_cell = document.createElement('TD');
556
557       var custnum_input = document.createElement('INPUT');
558       custnum_input.setAttribute('name', 'custnum'+<% $opt{prefix} %>rownum);
559       custnum_input.setAttribute('id',   'custnum'+<% $opt{prefix} %>rownum);
560       custnum_input.style.textAlign = 'right';
561       custnum_input.setAttribute('size', 8);
562       custnum_input.setAttribute('maxlength', 12);
563       custnum_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
564       custnum_input.onfocus = clearhint_custnum;
565       custnum_input.onchange = <% $opt{prefix} %>search_custnum;
566       custnum_cell.appendChild(custnum_input);
567
568     row.appendChild(custnum_cell);
569
570     var customer_cell = document.createElement('TD');
571
572       var customer_input = document.createElement('INPUT');
573       customer_input.setAttribute('name', 'customer'+<% $opt{prefix} %>rownum);
574       customer_input.setAttribute('id',   'customer'+<% $opt{prefix} %>rownum);
575       customer_input.setAttribute('size', 64);
576       customer_input.setAttribute('value', '(last name or company)' );
577       customer_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
578       customer_input.onfocus = clearhint_customer;
579       customer_input.onclick = clearhint_customer;
580       customer_input.onchange = <% $opt{prefix} %>search_customer;
581       customer_cell.appendChild(customer_input);
582
583       var customer_select = document.createElement('SELECT');
584       customer_select.setAttribute('name', 'cust_select'+<% $opt{prefix} %>rownum);
585       customer_select.setAttribute('id',   'cust_select'+<% $opt{prefix} %>rownum);
586       customer_select.setAttribute('rownum', <% $opt{prefix} %>rownum);
587       customer_select.style.color = '#ff0000';
588       customer_select.style.display = 'none';
589       customer_select.onchange = select_customer;
590       customer_cell.appendChild(customer_select);
591
592     row.appendChild(customer_cell);
593
594 %   my $col = 0;
595 %   foreach my $field ( @{$opt{fields}} ) {
596
597       var my_cell = document.createElement('TD');
598       my_cell.setAttribute('align', '<% $align{ $opt{align}->[$col] || 'l' } %>');
599
600 %     if ($types->[$col] eq 'immutable') {
601 %       my $value;
602 %       if ( ref($field) eq 'CODE' ) {
603 %         $value = &{$field}($row,$param);
604 %       } else {
605 %         $value = $param->{"$field$row"}; 
606 %       }
607         var my_text = document.createTextNode('<% $value %>');
608         my_cell.appendChild(my_text);
609 %     }
610
611       var my_input = document.createElement('INPUT');
612       my_input.setAttribute('name', '<% $field %>'+<% $opt{prefix} %>rownum);
613       my_input.setAttribute('id',   '<% $field %>'+<% $opt{prefix} %>rownum);
614       my_input.style.textAlign = '<% $align{ $opt{align}->[$col] || 'l' } %>';
615       my_input.setAttribute('size', <% $sizes->[$col] || 10 %>);
616 %     if ($types->[$col] eq 'immutable') {
617         my_input.setAttribute('type', 'hidden');
618 %     }
619 %     if ( $opt{footer}->[$col] eq '_TOTAL' ) {
620         my_input.onchange   = <% $opt{prefix} %>calc_total<%$col%>;
621         my_input.onkeyup    = <% $opt{prefix} %>calc_total<%$col%>;
622 %     }
623       my_cell.appendChild(my_input);
624
625     row.appendChild(my_cell);
626
627 %     $col++;
628 %   }
629
630     var balance_cell = document.createElement('TD');
631
632       var balance_input = document.createElement('INPUT');
633       balance_input.setAttribute('name', 'balance'+<% $opt{prefix} %>rownum);
634       balance_input.setAttribute('id',   'balance'+<% $opt{prefix} %>rownum);
635       balance_input.style.textAlign = 'right';
636       balance_input.setAttribute('size', 8);
637       balance_input.setAttribute('maxlength', 12);
638       balance_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
639       balance_input.disabled = true;
640       balance_input.style.backgroundColor = '#dddddd';
641       balance_cell.appendChild(balance_input);
642
643     row.appendChild(balance_cell);
644
645     //update the total # of rows display
646     if ( <% $opt{prefix} %>rownum == 1 ) {
647       <% $opt{prefix} %>total_el.innerHTML =
648         'Total '
649           + <% $opt{prefix} %>rownum
650           + ' <% $opt{name_singular} || 'customer' %>';
651     } else {
652       <% $opt{prefix} %>total_el.innerHTML =
653         'Total '
654           + <% $opt{prefix} %>rownum
655           + ' <% PL($opt{name_singular} || 'customer') %>';
656     }
657
658     <% $opt{prefix} %>rownum++;
659
660   }
661
662 % unless ($cgi->param('error')) {
663   <% $opt{prefix} %>addRow();
664 % }
665 </SCRIPT>
666
667 <%init>
668
669 my(%opt) = @_;
670
671 $opt{prefix} = '' unless defined $opt{prefix};
672 $opt{prefix} .= '_' if $opt{prefix};
673
674 my $types = $opt{'types'} ? [ @{$opt{'types'}} ] : [];
675 my $sizes = $opt{'size'} ? [ @{$opt{'size'}} ] : [];
676
677 my $param = $opt{param};
678 $param = $cgi->Vars if $cgi->param('error');
679
680 $opt{$_} ||= [] foreach qw(align color footer footer_align);
681
682 my @total = map 0, @{$opt{footer}};
683
684 my %align = (
685   'l' => 'left',
686   'r' => 'right',
687   'c' => 'center',
688 );
689
690 </%init>