work around ie7 javascript issues
[freeside.git] / httemplate / edit / elements / ApplicationCommon.html
1 <%doc>
2
3 Examples:
4
5   #cust_bill_pay
6   include('elements/ApplicationCommon.html',
7     'form_action' => 'process/cust_bill_pay.cgi', 
8     'src_table'   => 'cust_pay',
9     'src_thing'   => 'payment',
10     'dst_table'   => 'cust_bill',
11     'dst_thing'   => 'invoice',
12   )
13
14   #cust_credit_bill
15   include('elements/ApplicationCommon.html',
16     'form_action' => 'process/cust_credit_bill.cgi',
17     'src_table'   => 'cust_credit',
18     'src_thing'   => 'credit',
19     'dst_table'   => 'cust_bill',
20     'dst_thing'   => 'invoice',
21   )
22
23   #cust_pay_refund
24   include('elements/ApplicationCommon.html',
25     'form_action' => 'process/cust_pay_refund.cgi',
26     'src_table'   => 'cust_pay',
27     'src_thing'   => 'payment',
28     'dst_table'   => 'cust_refund',
29     'dst_thing'   => 'refund',
30   )
31
32   #cust_credit_refund
33   include('elements/ApplicationCommon.html',
34     'form_action' => 'process/cust_credit_refund.cgi',
35     'src_table'   => 'cust_credit',
36     'src_thing'   => 'credit',
37     'dst_table'   => 'cust_refund',
38     'dst_thing'   => 'refund',
39   )
40
41 </%doc>
42
43 <% include('/elements/header-popup.html', "Apply $src_thing$to", '', 'onLoad="myOnLoadFunction();"') %>
44
45 <% include('/elements/error.html') %>
46
47 <P ID="ErrorMessage"></P>
48 <FORM ACTION="<% $p1. $opt{'form_action'} %>" NAME="ApplicationForm" ID="ApplicationForm" METHOD=POST>
49
50 <% $src_thing %> #<B><% $src_pkeyvalue %></B><BR>
51 <INPUT TYPE="hidden" NAME="<% $src_pkey %>" VALUE="<% $src_pkeyvalue %>">
52
53 <TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
54
55 <TR>
56   <TD ALIGN="right">Date: </TD>
57   <TD><B><% time2str("%D", $src->_date) %></B></TD>
58 </TR>
59
60 <TR>
61   <TD ALIGN="right">Amount: </TD>
62   <TD ID="original_amount"><B><% $money_char %><% $src_amount %></B>
63   </TD>
64   <TD>
65 % if ($use_sub_dst_thing && $can_change_credit) {
66     <INPUT TYPE="hidden" NAME="src_amount" VALUE="<% $src_amount %>" >
67     <BUTTON TYPE="button" NAME="expand_button" ID="expand_button" onClick="do_change_amount(this);">Change</BUTTON>
68 % }
69   </TD>
70
71 </TR>
72
73 <TR>
74   <TD ALIGN="right">Unapplied amount: </TD>
75   <TD ID="unapplied_amount"><B><% $money_char %><% $unapplied %></B></TD>
76 </TR>
77
78 % if ( $src_table eq 'cust_credit' ) {
79     <TR>
80       <TD ALIGN="right">Reason: </TD>
81       <TD COLSPAN=2><B><% $src->reason %></B></TD>
82     </TR>
83 % }
84
85 </TABLE>
86 <BR>
87
88 <SCRIPT TYPE="text/javascript">
89 function changed(what) {
90   dst = what.options[what.selectedIndex].value;
91
92   if ( dst == '' ) {
93     what.form.submit.disabled=true;
94 %if ($src_pkey eq 'crednum') {
95     what.form.tax_button.disabled=true;
96 %}
97     return true;
98   }
99
100   what.form.submit.disabled=false;
101 %if ($src_pkey eq 'crednum') {
102   what.form.tax_button.disabled=false;
103 %}
104
105 % foreach my $dst ( @dst ) {
106
107     if ( dst == <% $dst->$dst_pkey %> ) {
108       what.form.amount.value = "<% min($dst->$dst_unapplied, $unapplied) %>";
109 %     if ($use_sub_dst_thing) {
110         what.form.display_amount.value = "<% min($dst->$dst_unapplied, $unapplied) %>";
111
112         var rownum=0
113         var table = document.getElementById('ApplicationTable');
114         while(table.rows[2]) {
115           table.deleteRow(2);
116         }
117 %       my $app_class = "FS::$link_table";
118 %       my $temp_app = $app_class->new(
119 %         { $src_pkey => $src_pkeyvalue,
120 %           $dst_pkey => $dst->$dst_pkey,
121 %           'amount'  => min($dst->$dst_unapplied, $unapplied),
122 %         }
123 %       );
124 %       my %apphash = ();
125 %       my $listref_or_error = $temp_app->calculate_applications;
126 %       %apphash = map { &{$key_generator}($_), $_ } @$listref_or_error
127 %         if ref($listref_or_error);
128 %       foreach my $cbp ( $dst->open_cust_bill_pkg ) {
129 %         my $desc = $cbp->desc;
130 %         my $total_owed = $cbp->owed_setup + $cbp->owed_recur;
131 %         my $key = &{$key_generator}([ $cbp, 0, {} ]);
132 %         my $amount = exists($apphash{ $key }) ? $apphash{ $key }->[1] : 0;
133 %         unless ( $cbp->pkgnum ) {
134 %           foreach my $taxX ( $cbp->cust_bill_pkg_tax_Xlocation ) {
135 %             my $pkey = $taxX->primary_key;
136 %             my $owed = $taxX->owed;
137 %             my $key = &{$key_generator}([ $cbp, 0, { $pkey => $taxX->$pkey } ]);
138 %             my $toapp = exists($apphash{ $key }) ? $apphash{ $key }->[1] : 0;
139               <% &{$row_generator}( $key, $cbp, $taxX->desc, $owed, $toapp, $taxX->$pkey ) %>
140 %             $total_owed -= $owed;
141 %             $amount -= $toapp;
142 %           }
143 %           $desc .= ' (default)';
144 %         }
145 %         if ( $total_owed > 0 ) {
146             <% &{$row_generator}($key, $cbp, $desc, $total_owed, $amount, '') %>
147 %         }
148 %       }
149 %     }
150     }
151
152 % } 
153
154 }
155
156 function sub_changed(what) {
157
158   var amount = 0;
159   var table = document.getElementById('ApplicationTable');
160   var i = table.rows.length;
161   while(i-- > 2) {
162     var inputs = table.rows[i].getElementsByTagName('input');
163     if (! inputs.length) {
164       continue;
165     }
166     amount += parseFloat( inputs.item(0).value ) || 0; 
167   }
168   what.form.amount.value = parseFloat(amount).toFixed(2);
169   what.form.display_amount.value = parseFloat(amount).toFixed(2);
170   set_amount_color(what);
171
172 }
173
174 function set_amount_color(what) {
175   if (what.form.src_amount.value < what.form.amount.value) {
176     what.form.display_amount.style.color = '#ff0000';
177   } else {
178     what.form.display_amount.style.color = '#00ff00';
179   }
180 }
181
182 </SCRIPT>
183
184 Apply to:
185
186 % if ($use_sub_dst_thing && $src_pkey eq 'crednum') {
187 <CENTER><BUTTON TYPE="button" NAME="tax_button" ID="tax_button" onClick="do_calculate_tax(this);" DISABLED>Calculate Tax</BUTTON></CENTER>
188 <% include( '/elements/xmlhttp.html',
189             'url' =>  $p.'misc/xmlhttp-calculate_taxes.html',
190             'subs' => [ 'calculate_taxes' ],
191           )
192  %>
193 <SCRIPT TYPE="text/javascript">
194
195 function show_taxes(arg) {
196   var argsHash = eval('(' + arg + ')');
197
198   var button = document.getElementById('tax_button');
199   button.disabled = false;
200   button.innerHTML = 'Calculate Tax';
201
202   var error = argsHash['error'];
203
204   var paragraph = document.getElementById('ErrorMessage');
205   if (error) {
206     paragraph.innerHTML = 'Error: ' + error;
207     paragraph.style.color = '#ff0000';
208   } else {
209     paragraph.innerHTML = '';
210   }
211   var taxlines = argsHash['taxlines'];
212
213   var table = document.getElementById('ApplicationTable');
214
215   var aFoundRow = 0;
216   for (i = 0; taxlines[i]; i++) {
217     var itemdesc = taxlines[i][0];
218     var locnum   = taxlines[i][2];
219     if (taxlines[i][3]) {
220       locnum  = taxlines[i][3];
221     }
222
223     var found = 0;
224     for (var row = 2; table.rows[row]; row++) {
225       var inputs = table.rows[row].getElementsByTagName('input');
226       if (! inputs.length) {
227         while ( table.rows[row] ) {
228            table.deleteRow(row);
229         }
230         break;
231       }
232       if ( inputs.item(4).value == itemdesc && inputs.item(2).value == locnum )
233       {
234         inputs.item(0).value = taxlines[i][1];
235         aFoundRow = found = row;
236         break;
237       }
238     }
239     if (! found) {
240       var row = table.insertRow(table.rows.length);
241       var warning_cell = document.createElement('TD');
242       warning_cell.style.color = '#ff0000';
243       warning_cell.colSpan = 2;
244       warning_cell.innerHTML = 'Calculated Tax - ' + itemdesc + ' - ' +
245                                taxlines[i][1] + ' will not be applied';
246       row.appendChild(warning_cell);
247     }
248   }
249
250   if (aFoundRow) {
251     sub_changed(table.rows[aFoundRow].getElementsByTagName('input').item(0));
252   }
253     
254 }
255
256 function do_calculate_tax (what) {
257   what.innerHTML = 'Calculating....';
258   what.disabled = true;
259   var taxed_items = new Array();
260   var table = document.getElementById('ApplicationTable');
261   for (var row = 2; table.rows[row]; row++)
262   {
263     var inputs = table.rows[row].getElementsByTagName('input');
264     if ( !inputs.length ) {
265       break;
266     }
267     var taxed_item = new Array(
268       inputs.item(1).value, // billpkgnum
269       inputs.item(3).value, // s_or_r
270       inputs.item(0).value  // amount
271     );
272     taxed_items.push(taxed_item);
273   }
274
275   var args = new Array(
276     'crednum', '<% $src_pkeyvalue %>',
277     'items', taxed_items
278   );
279   calculate_taxes ( args, show_taxes );
280 }
281
282 function do_change_amount (what) {
283   var amount_cell = document.getElementById('original_amount');
284   var inputs = amount_cell.getElementsByTagName('input');
285   if (inputs.length) {
286     src_amount_changed();
287     amount_cell.innerHTML = '<B><% $money_char %></B>' + inputs.item(0).value;
288   } else {
289     amount_cell.innerHTML = '<% $money_char %>';
290     var amount_input = document.createElement('INPUT');
291     amount_input.setAttribute('name', 'entered_amount');
292     amount_input.setAttribute('id',   'entered_amount');
293     amount_input.style.textAlign = 'right';
294     amount_input.setAttribute('size', 8);
295     amount_input.setAttribute('maxlength', 8);
296     amount_input.setAttribute('value', what.form.src_amount.value);
297     amount_input.setAttribute('onChange', "src_amount_changed(this);");
298     amount_cell.appendChild(amount_input);
299   }
300 }
301
302 function src_amount_changed () {
303   //alert('src_amount_changed called');
304   var entered_amount = document.getElementById('entered_amount');
305   if ( entered_amount ) {
306     entered_amount.form.src_amount.value = entered_amount.value;
307     var unapplied_cell = document.getElementById('unapplied_amount');
308     unapplied_cell.innerHTML = '<B><% $money_char %>' + entered_amount.value + '</B>';
309     set_amount_color(entered_amount);
310   }
311   return true;
312 }
313
314 </SCRIPT>
315
316 %}
317
318 <TABLE ID="ApplicationTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
319
320 <TR>
321   <TD ALIGN="right"><% $dst_thing %>: </TD>
322   <TD><SELECT NAME="<% $dst_pkey %>" SIZE=1 onChange="changed(this)">
323 <OPTION VALUE="">Select <% $dst_thing %>
324
325 % foreach my $dst ( @dst ) { 
326   <OPTION<% $dst->$dst_pkey eq $dst_pkeyvalue ? ' SELECTED' : '' %> VALUE="<% $dst->$dst_pkey %>">#<% $dst->$dst_pkey %> - <% time2str("%D", $dst->_date) %> - $<% $dst->$dst_unapplied %>
327 % } 
328
329 </SELECT>
330   </TD>
331 </TR>
332
333 <TR>
334   <TD ALIGN="right">Amount: </TD>
335   <TD><% $money_char %><INPUT TYPE="text" NAME="<% $use_sub_dst_thing ? 'display_' : '' %>amount" VALUE="<% $amount %>" SIZE=8 MAXLENGTH=8 <% $use_sub_dst_thing ? 'DISABLED' : '' %> STYLE="text-align:right;"></TD>
336 % if ($use_sub_dst_thing) {
337     <INPUT TYPE="hidden" NAME="amount" VALUE="<% $amount %>" >
338 % }
339 </TR>
340
341 </TABLE>
342
343 <BR>
344 <CENTER><INPUT TYPE="submit"
345                VALUE="Apply"
346                NAME="submit"
347                ID="submit"
348 % if ($use_sub_dst_thing && $can_change_credit) {
349                onClick="src_amount_changed()"
350 % }
351                DISABLED
352 ></CENTER>
353
354 </FORM>
355
356 <SCRIPT TYPE="text/javascript">
357
358 function myOnLoadFunction () {
359   <% $onload %>
360 }
361
362 </SCRIPT>
363
364 <% include('/elements/footer.html') %>
365
366 <%init>
367
368 my %opt = @_;
369
370 my $conf = new FS::Conf;
371 my $money_char = $conf->config('money_char') || '$';
372
373 my $src_thing = ucfirst($opt{'src_thing'});
374 my $src_table = $opt{'src_table'};
375 my $src_pkey = dbdef->table($src_table)->primary_key;
376
377 my $dst_thing = ucfirst($opt{'dst_thing'});
378 my $dst_table = $opt{'dst_table'};
379 my $dst_pkey = dbdef->table($dst_table)->primary_key;
380 my $dst_unapplied = $dst_table eq 'cust_bill' ? 'owed' : 'unapplied';
381
382 $opt{form_action} =~ /^process\/(.*)\./ or die "bad form action";
383 my $link_table = $1;
384
385 my $use_sub_dst_thing = 0;
386 $use_sub_dst_thing = 1
387   if ( $dst_table eq 'cust_bill' && $conf->exists("${link_table}_pkg-manual") );
388
389 my $can_change_credit = 0;
390 $can_change_credit = 1
391   if ( $src_table eq 'cust_credit' && 
392        $FS::CurrentUser::CurrentUser->access_right('Post credit') &&
393        $FS::CurrentUser::CurrentUser->access_right('Delete credit')
394      );
395
396 my $to = $dst_table eq 'cust_refund' ? ' to Refund' : '';
397
398 my($src_pkeyvalue, $amount, $dst_pkeyvalue, $src_amount);
399 if ( $cgi->param('error') ) {
400   $src_pkeyvalue = $cgi->param($src_pkey);
401   $amount    = $cgi->param('amount');
402   $dst_pkeyvalue    = $cgi->param($dst_pkey);
403   $src_amount = $cgi->param('src_amount');
404 } else {
405   my($query) = $cgi->keywords;
406   $query =~ /^(\d+)$/;
407   $src_pkeyvalue = $1;
408   $amount = '';
409   $dst_pkeyvalue = '';
410 }
411
412 my $otaker = getotaker;
413
414 my $p1 = popurl(1);
415
416 my $src = qsearchs($src_table, { $src_pkey => $src_pkeyvalue } );
417 die "$src_thing $src_pkeyvalue not found!" unless $src;
418
419 $src_amount = $src->amount unless $cgi->param('error');
420
421 my $unapplied = $src->unapplied;
422
423 my @dst = sort {    $a->_date     <=> $b->_date
424                  or $a->$dst_pkey <=> $b->$dst_pkey
425                }
426           grep { $_->$dst_unapplied != 0 }
427           qsearch($dst_table, { 'custnum' => $src->custnum } );
428
429 my $row_generator = sub {
430   my ($key, $cust_bill_pkg, $desc, $owed, $amount, $taxXnum) = @_;
431   my ($num, $s_or_r, $taxlinenum) = split(':', $key);
432   my $id = $cust_bill_pkg->pkgnum || 'Tax';
433   my $billpkgnum = $cust_bill_pkg->billpkgnum;
434   my $s_or_r = $cust_bill_pkg->setup > 0 ? 'setup' : 'recur';
435
436   $amount = sprintf("%.2f", $amount);
437   qq!
438       var tablebody = document.getElementsByTagName('tbody').item(0);
439       var row = table.insertRow(rownum+2);
440       var pkg_cell = document.createElement('TD');
441       pkg_cell.style.textAlign = 'right';
442       pkg_cell.innerHTML = "$id - $desc - $owed:";
443       var amount_cell = document.createElement('TD');
444       amount_cell.innerHTML = "$money_char";
445       var amount_input = document.createElement('INPUT');
446       amount_input.setAttribute('name', 'subamount'+rownum);
447       amount_input.setAttribute('id',   'subamount'+rownum);
448       amount_input.style.textAlign = 'right';
449       amount_input.setAttribute('size', 8);
450       amount_input.setAttribute('maxlength', 8);
451       amount_input.setAttribute('rownum', rownum);
452       amount_input.setAttribute('value', "$amount");
453       amount_input.setAttribute('onChange', "sub_changed(this);");
454       amount_cell.appendChild(amount_input);
455       var subnum_input = document.createElement('INPUT');
456       subnum_input.setAttribute('name', 'subnum'+rownum);
457       subnum_input.setAttribute('id',   'subnum'+rownum);
458       subnum_input.setAttribute('type', 'hidden');
459       subnum_input.setAttribute('rownum', rownum);
460       subnum_input.setAttribute('value', "$billpkgnum");
461       amount_cell.appendChild(subnum_input);
462       var taxnum_input = document.createElement('INPUT');
463       taxnum_input.setAttribute('name', 'taxXlocationnum'+rownum);
464       taxnum_input.setAttribute('id',   'taxXlocationnum'+rownum);
465       taxnum_input.setAttribute('type', 'hidden');
466       taxnum_input.setAttribute('rownum', rownum);
467       taxnum_input.setAttribute('value', "$taxXnum");
468       amount_cell.appendChild(taxnum_input);
469       var s_or_r_input = document.createElement('INPUT');
470       s_or_r_input.setAttribute('name', 's_or_r'+rownum);
471       s_or_r_input.setAttribute('id',   's_or_r'+rownum);
472       s_or_r_input.setAttribute('type', 'hidden');
473       s_or_r_input.setAttribute('rownum', rownum);
474       s_or_r_input.setAttribute('value', "$s_or_r");
475       amount_cell.appendChild(s_or_r_input);
476       var itemdesc_input = document.createElement('INPUT');
477       itemdesc_input.setAttribute('name', 'itemdesc'+rownum);
478       itemdesc_input.setAttribute('id',   'itemdesc'+rownum);
479       itemdesc_input.setAttribute('type', 'hidden');
480       itemdesc_input.setAttribute('rownum', rownum);
481       itemdesc_input.setAttribute('value', "$desc");
482       amount_cell.appendChild(itemdesc_input);
483       row.appendChild(pkg_cell);
484       row.appendChild(amount_cell);
485       rownum++;
486     !;
487 };
488
489 my $key_generator = sub {
490   my $listref = shift;
491   my ($cust_bill_pkg, $amount, $hashref) = @$listref;
492   my $setup_or_recur = $cust_bill_pkg->setup > 0 ? 'setup' : 'recur';
493   my $taxlinenum = $hashref->{billpkgtaxlocationnum} ||
494                    $hashref->{billpkgtaxratelocationnum} ||
495                    '';
496                    
497   join(':', $cust_bill_pkg->billpkgnum, $setup_or_recur, $taxlinenum);
498 };
499
500 my $onload = 'return true;';
501
502 if ($cgi->param('error')) {
503
504   my $set_sub_amounts =
505     join(';', map { "myform.subamount$_.value = ". $cgi->param("subamount$_") }
506                grep { /.+/ }
507                map { /^subnum(\d+)$/ ? $1 : '' }
508                $cgi->param
509   );
510   $set_sub_amounts &&= "$set_sub_amounts;sub_changed(myform.subamount0)";
511
512   $onload = qq!
513     var myform = document.getElementById('ApplicationForm');
514     changed(myform.elements['$dst_pkey']);
515     $set_sub_amounts;
516     return true;
517   !;
518 }
519
520 </%init>