proper use of date_format config for international date formats, RT#7009
[freeside.git] / httemplate / edit / elements / ApplicationCommon.html
index a485d37..7b1050a 100644 (file)
@@ -39,10 +39,12 @@ Examples:
   )
 
 </%doc>
-<% include('/elements/header-popup.html', "Apply $src_thing$to" ) %>
+
+<% include('/elements/header-popup.html', "Apply $src_thing$to", '', 'onLoad="myOnLoadFunction();"') %>
 
 <% include('/elements/error.html') %>
 
+<P ID="ErrorMessage"></P>
 <FORM ACTION="<% $p1. $opt{'form_action'} %>" NAME="ApplicationForm" ID="ApplicationForm" METHOD=POST>
 
 <% $src_thing %> #<B><% $src_pkeyvalue %></B><BR>
@@ -52,23 +54,31 @@ Examples:
 
 <TR>
   <TD ALIGN="right">Date: </TD>
-  <TD><B><% time2str("%D", $src->_date) %></B></TD>
+  <TD><B><% time2str($date_format, $src->_date) %></B></TD>
 </TR>
 
 <TR>
   <TD ALIGN="right">Amount: </TD>
-  <TD><B><% $money_char %><% $src->amount %></B></TD>
+  <TD ID="original_amount"><B><% $money_char %><% $src_amount %></B>
+  </TD>
+  <TD>
+% if ($use_sub_dst_thing && $can_change_credit) {
+    <INPUT TYPE="hidden" NAME="src_amount" VALUE="<% $src_amount %>" >
+    <BUTTON TYPE="button" NAME="expand_button" ID="expand_button" onClick="do_change_amount(this);">Change</BUTTON>
+% }
+  </TD>
+
 </TR>
 
 <TR>
   <TD ALIGN="right">Unapplied amount: </TD>
-  <TD><B><% $money_char %><% $unapplied %></B></TD>
+  <TD ID="unapplied_amount"><B><% $money_char %><% $unapplied %></B></TD>
 </TR>
 
 % if ( $src_table eq 'cust_credit' ) {
     <TR>
       <TD ALIGN="right">Reason: </TD>
-      <TD><B><% $src->reason %></B></TD>
+      <TD COLSPAN=2><B><% $src->reason %></B></TD>
     </TR>
 % }
 
@@ -76,30 +86,267 @@ Examples:
 <BR>
 
 <SCRIPT TYPE="text/javascript">
+function clear_amounts() {
+  var rownum=0
+  var table = document.getElementById('ApplicationTable');
+  for (var row = 2; table.rows[row]; row++)
+  {
+    var inputs = table.rows[row].getElementsByTagName('input');
+    if ( !inputs.length ) {
+      break;
+    }
+    inputs.item(0).value = ''; // amount
+  }
+
+}
+
 function changed(what) {
   dst = what.options[what.selectedIndex].value;
 
   if ( dst == '' ) {
     what.form.submit.disabled=true;
+%if ($use_sub_dst_thing && $src_pkey eq 'crednum') {
+    what.form.tax_button.disabled=true;
+    what.form.clear_button.disabled=true;
+%}
     return true;
   }
 
   what.form.submit.disabled=false;
+%if ($use_sub_dst_thing && $src_pkey eq 'crednum') {
+  what.form.tax_button.disabled=false;
+  what.form.clear_button.disabled=false;
+%}
 
 % foreach my $dst ( @dst ) {
 
     if ( dst == <% $dst->$dst_pkey %> ) {
       what.form.amount.value = "<% min($dst->$dst_unapplied, $unapplied) %>";
+%     if ($use_sub_dst_thing) {
+        what.form.display_amount.value = "<% min($dst->$dst_unapplied, $unapplied) %>";
+
+        var rownum=0
+        var table = document.getElementById('ApplicationTable');
+        while(table.rows[2]) {
+          table.deleteRow(2);
+        }
+%       my $app_class = "FS::$link_table";
+%       my $temp_app = $app_class->new(
+%         { $src_pkey => $src_pkeyvalue,
+%           $dst_pkey => $dst->$dst_pkey,
+%           'amount'  => min($dst->$dst_unapplied, $unapplied),
+%         }
+%       );
+%       my %apphash = ();
+%       my $listref_or_error = $temp_app->calculate_applications;
+%       %apphash = map { &{$key_generator}($_), $_ } @$listref_or_error
+%         if ref($listref_or_error);
+%       foreach my $cbp ( $dst->open_cust_bill_pkg ) {
+%         my $desc = $cbp->desc;
+%         my $total_owed = $cbp->owed_setup + $cbp->owed_recur;
+%         my $key = &{$key_generator}([ $cbp, 0, {} ]);
+%         my $amount = exists($apphash{ $key }) ? $apphash{ $key }->[1] : 0;
+%         unless ( $cbp->pkgnum ) {
+%           foreach my $taxX ( $cbp->cust_bill_pkg_tax_Xlocation ) {
+%             my $pkey = $taxX->primary_key;
+%             my $owed = $taxX->owed;
+%             my $key = &{$key_generator}([ $cbp, 0, { $pkey => $taxX->$pkey } ]);
+%             my $toapp = exists($apphash{ $key }) ? $apphash{ $key }->[1] : 0;
+              <% &{$row_generator}( $key, $cbp, $taxX->desc, $owed, $toapp, $taxX->$pkey ) %>
+%             $total_owed -= $owed;
+%             $amount -= $toapp;
+%           }
+%           $desc .= ' (default)';
+%         }
+%         if ( $total_owed > 0 ) {
+            <% &{$row_generator}($key, $cbp, $desc, $total_owed, $amount, '') %>
+%         }
+%       }
+%     }
     }
 
 % } 
 
 }
+
+function sub_changed(what) {
+
+  var amount = 0;
+  var table = document.getElementById('ApplicationTable');
+  var i = table.rows.length;
+  while(i-- > 2) {
+    var inputs = table.rows[i].getElementsByTagName('input');
+    if (! inputs.length) {
+      continue;
+    }
+    amount += parseFloat( inputs.item(0).value ) || 0; 
+  }
+  what.form.amount.value = parseFloat(amount).toFixed(2);
+  what.form.display_amount.value = parseFloat(amount).toFixed(2);
+  set_amount_color(what);
+
+}
+
+function set_amount_color(what) {
+  if (what.form.src_amount.value < what.form.amount.value) {
+    what.form.display_amount.style.color = '#ff0000';
+  } else {
+    what.form.display_amount.style.color = '#00ff00';
+  }
+}
+
 </SCRIPT>
 
 Apply to:
 
-<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+% if ($use_sub_dst_thing && $src_pkey eq 'crednum') {
+<CENTER>
+  <TABLE>
+    <TR>
+      <TD>
+        <BUTTON TYPE="button" NAME="tax_button" ID="tax_button" onClick="do_calculate_tax(this);" DISABLED>Calculate Tax</BUTTON>
+      </TD>
+      <TD>
+        <BUTTON TYPE="button" NAME="clear_button" ID="clear_button" onClick="clear_amounts(this);" DISABLED>Clear Amounts</BUTTON>
+      </TD>
+    </TR>
+  </TABLE>
+</CENTER>
+<% include( '/elements/xmlhttp.html',
+            'url' =>  $p.'misc/xmlhttp-calculate_taxes.html',
+            'subs' => [ 'calculate_taxes' ],
+          )
+ %>
+<SCRIPT TYPE="text/javascript">
+
+function show_taxes(arg) {
+  var argsHash = eval('(' + arg + ')');
+
+  var button = document.getElementById('tax_button');
+  button.disabled = false;
+  button.innerHTML = 'Calculate Tax';
+  button = document.getElementById('clear_button');
+  button.disabled = false;
+
+  var error = argsHash['error'];
+
+  var paragraph = document.getElementById('ErrorMessage');
+  if (error) {
+    paragraph.innerHTML = 'Error: ' + error;
+    paragraph.style.color = '#ff0000';
+  } else {
+    paragraph.innerHTML = '';
+  }
+  var taxlines = argsHash['taxlines'];
+
+  var table = document.getElementById('ApplicationTable');
+
+  var aFoundRow = 0;
+  for (i = 0; taxlines[i]; i++) {
+    var itemdesc = taxlines[i][0];
+    var locnum   = taxlines[i][2];
+    if (taxlines[i][3]) {
+      locnum  = taxlines[i][3];
+    }
+
+    var found = 0;
+    for (var row = 2; table.rows[row]; row++) {
+      var inputs = table.rows[row].getElementsByTagName('input');
+      if (! inputs.length) {
+        while ( table.rows[row] ) {
+           table.deleteRow(row);
+        }
+        break;
+      }
+      if ( inputs.item(4).value == itemdesc && inputs.item(2).value == locnum )
+      {
+        inputs.item(0).value = taxlines[i][1];
+        aFoundRow = found = row;
+        break;
+      }
+    }
+    if (! found) {
+      var row = table.insertRow(table.rows.length);
+      var warning_cell = document.createElement('TD');
+      warning_cell.style.color = '#ff0000';
+      warning_cell.colSpan = 2;
+      warning_cell.innerHTML = 'Calculated Tax - ' + itemdesc + ' - ' +
+                               taxlines[i][1] + ' will not be applied';
+      row.appendChild(warning_cell);
+    }
+  }
+
+  if (aFoundRow) {
+    sub_changed(table.rows[aFoundRow].getElementsByTagName('input').item(0));
+  }
+    
+}
+
+function do_calculate_tax (what) {
+  what.innerHTML = 'Calculating....';
+  what.disabled = true;
+  var button = document.getElementById('clear_button');
+  button.disabled = true;
+  var taxed_items = new Array();
+  var table = document.getElementById('ApplicationTable');
+  for (var row = 2; table.rows[row]; row++)
+  {
+    var inputs = table.rows[row].getElementsByTagName('input');
+    if ( !inputs.length ) {
+      break;
+    }
+    var taxed_item = new Array(
+      inputs.item(1).value, // billpkgnum
+      inputs.item(3).value, // s_or_r
+      inputs.item(0).value || 0  // amount
+    );
+    taxed_items.push(taxed_item);
+  }
+
+  var args = new Array(
+    'crednum', '<% $src_pkeyvalue %>',
+    'items', taxed_items
+  );
+  calculate_taxes ( args, show_taxes );
+}
+
+function do_change_amount (what) {
+  var amount_cell = document.getElementById('original_amount');
+  var inputs = amount_cell.getElementsByTagName('input');
+  if (inputs.length) {
+    src_amount_changed();
+    amount_cell.innerHTML = '<B><% $money_char %></B>' + inputs.item(0).value;
+  } else {
+    amount_cell.innerHTML = '<% $money_char %>';
+    var amount_input = document.createElement('INPUT');
+    amount_input.setAttribute('name', 'entered_amount');
+    amount_input.setAttribute('id',   'entered_amount');
+    amount_input.style.textAlign = 'right';
+    amount_input.setAttribute('size', 8);
+    amount_input.setAttribute('maxlength', 8);
+    amount_input.setAttribute('value', what.form.src_amount.value);
+    amount_input.setAttribute('onChange', "src_amount_changed(this);");
+    amount_cell.appendChild(amount_input);
+  }
+}
+
+function src_amount_changed () {
+  //alert('src_amount_changed called');
+  var entered_amount = document.getElementById('entered_amount');
+  if ( entered_amount ) {
+    entered_amount.form.src_amount.value = entered_amount.value;
+    var unapplied_cell = document.getElementById('unapplied_amount');
+    unapplied_cell.innerHTML = '<B><% $money_char %>' + entered_amount.value + '</B>';
+    set_amount_color(entered_amount);
+  }
+  return true;
+}
+
+</SCRIPT>
+
+%}
+
+<TABLE ID="ApplicationTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
 
 <TR>
   <TD ALIGN="right"><% $dst_thing %>: </TD>
@@ -107,7 +354,7 @@ Apply to:
 <OPTION VALUE="">Select <% $dst_thing %>
 
 % foreach my $dst ( @dst ) { 
-  <OPTION<% $dst->$dst_pkey eq $dst_pkeyvalue ? ' SELECTED' : '' %> VALUE="<% $dst->$dst_pkey %>">#<% $dst->$dst_pkey %> - <% time2str("%D", $dst->_date) %> - $<% $dst->$dst_unapplied %>
+  <OPTION<% $dst->$dst_pkey eq $dst_pkeyvalue ? ' SELECTED' : '' %> VALUE="<% $dst->$dst_pkey %>">#<% $dst->$dst_pkey %> - <% time2str($date_format, $dst->_date) %> - $<% $dst->$dst_unapplied %>
 % } 
 
 </SELECT>
@@ -116,16 +363,35 @@ Apply to:
 
 <TR>
   <TD ALIGN="right">Amount: </TD>
-  <TD><% $money_char %><INPUT TYPE="text" NAME="amount" VALUE="<% $amount %>" SIZE=8 MAXLENGTH=8></TD>
+  <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>
+% if ($use_sub_dst_thing) {
+    <INPUT TYPE="hidden" NAME="amount" VALUE="<% $amount %>" >
+% }
 </TR>
 
 </TABLE>
 
 <BR>
-<CENTER><INPUT TYPE="submit" VALUE="Apply" NAME="submit" ID="submit" DISABLED></CENTER>
+<CENTER><INPUT TYPE="submit"
+               VALUE="Apply"
+               NAME="submit"
+               ID="submit"
+% if ($use_sub_dst_thing && $can_change_credit) {
+               onClick="src_amount_changed()"
+% }
+               DISABLED
+></CENTER>
 
 </FORM>
 
+<SCRIPT TYPE="text/javascript">
+
+function myOnLoadFunction () {
+  <% $onload %>
+}
+
+</SCRIPT>
+
 <% include('/elements/footer.html') %>
 
 <%init>
@@ -133,7 +399,8 @@ Apply to:
 my %opt = @_;
 
 my $conf = new FS::Conf;
-my $money_char = $conf->config('money_char') || '$';
+my $money_char  = $conf->config('money_char')  || '$';
+my $date_format = $conf->config('date_format') || '%m/%d/%Y';
 
 my $src_thing = ucfirst($opt{'src_thing'});
 my $src_table = $opt{'src_table'};
@@ -144,13 +411,28 @@ my $dst_table = $opt{'dst_table'};
 my $dst_pkey = dbdef->table($dst_table)->primary_key;
 my $dst_unapplied = $dst_table eq 'cust_bill' ? 'owed' : 'unapplied';
 
+$opt{form_action} =~ /^process\/(.*)\./ or die "bad form action";
+my $link_table = $1;
+
+my $use_sub_dst_thing = 0;
+$use_sub_dst_thing = 1
+  if ( $dst_table eq 'cust_bill' && $conf->exists("${link_table}_pkg-manual") );
+
+my $can_change_credit = 0;
+$can_change_credit = 1
+  if ( $src_table eq 'cust_credit' && 
+       $FS::CurrentUser::CurrentUser->access_right('Post credit') &&
+       $FS::CurrentUser::CurrentUser->access_right('Delete credit')
+     );
+
 my $to = $dst_table eq 'cust_refund' ? ' to Refund' : '';
 
-my($src_pkeyvalue, $amount, $dst_pkeyvalue);
+my($src_pkeyvalue, $amount, $dst_pkeyvalue, $src_amount);
 if ( $cgi->param('error') ) {
   $src_pkeyvalue = $cgi->param($src_pkey);
   $amount    = $cgi->param('amount');
   $dst_pkeyvalue    = $cgi->param($dst_pkey);
+  $src_amount = $cgi->param('src_amount');
 } else {
   my($query) = $cgi->keywords;
   $query =~ /^(\d+)$/;
@@ -166,6 +448,8 @@ my $p1 = popurl(1);
 my $src = qsearchs($src_table, { $src_pkey => $src_pkeyvalue } );
 die "$src_thing $src_pkeyvalue not found!" unless $src;
 
+$src_amount = $src->amount unless $cgi->param('error');
+
 my $unapplied = $src->unapplied;
 
 my @dst = sort {    $a->_date     <=> $b->_date
@@ -174,4 +458,95 @@ my @dst = sort {    $a->_date     <=> $b->_date
           grep { $_->$dst_unapplied != 0 }
           qsearch($dst_table, { 'custnum' => $src->custnum } );
 
+my $row_generator = sub {
+  my ($key, $cust_bill_pkg, $desc, $owed, $amount, $taxXnum) = @_;
+  my ($num, $s_or_r, $taxlinenum) = split(':', $key);
+  my $id = $cust_bill_pkg->pkgnum || 'Tax';
+  my $billpkgnum = $cust_bill_pkg->billpkgnum;
+  my $s_or_r = $cust_bill_pkg->setup > 0 ? 'setup' : 'recur';
+
+  $amount = sprintf("%.2f", $amount);
+  qq!
+      var tablebody = document.getElementsByTagName('tbody').item(0);
+      var row = table.insertRow(rownum+2);
+      var pkg_cell = document.createElement('TD');
+      pkg_cell.style.textAlign = 'right';
+      pkg_cell.innerHTML = "$id - $desc - $owed:";
+      var amount_cell = document.createElement('TD');
+      amount_cell.innerHTML = "$money_char";
+      var amount_input = document.createElement('INPUT');
+      amount_input.setAttribute('name', 'subamount'+rownum);
+      amount_input.setAttribute('id',   'subamount'+rownum);
+      amount_input.style.textAlign = 'right';
+      amount_input.setAttribute('size', 8);
+      amount_input.setAttribute('maxlength', 8);
+      amount_input.setAttribute('rownum', rownum);
+      amount_input.setAttribute('value', "$amount");
+      amount_input.setAttribute('onChange', "sub_changed(this);");
+      amount_cell.appendChild(amount_input);
+      var subnum_input = document.createElement('INPUT');
+      subnum_input.setAttribute('name', 'subnum'+rownum);
+      subnum_input.setAttribute('id',   'subnum'+rownum);
+      subnum_input.setAttribute('type', 'hidden');
+      subnum_input.setAttribute('rownum', rownum);
+      subnum_input.setAttribute('value', "$billpkgnum");
+      amount_cell.appendChild(subnum_input);
+      var taxnum_input = document.createElement('INPUT');
+      taxnum_input.setAttribute('name', 'taxXlocationnum'+rownum);
+      taxnum_input.setAttribute('id',   'taxXlocationnum'+rownum);
+      taxnum_input.setAttribute('type', 'hidden');
+      taxnum_input.setAttribute('rownum', rownum);
+      taxnum_input.setAttribute('value', "$taxXnum");
+      amount_cell.appendChild(taxnum_input);
+      var s_or_r_input = document.createElement('INPUT');
+      s_or_r_input.setAttribute('name', 's_or_r'+rownum);
+      s_or_r_input.setAttribute('id',   's_or_r'+rownum);
+      s_or_r_input.setAttribute('type', 'hidden');
+      s_or_r_input.setAttribute('rownum', rownum);
+      s_or_r_input.setAttribute('value', "$s_or_r");
+      amount_cell.appendChild(s_or_r_input);
+      var itemdesc_input = document.createElement('INPUT');
+      itemdesc_input.setAttribute('name', 'itemdesc'+rownum);
+      itemdesc_input.setAttribute('id',   'itemdesc'+rownum);
+      itemdesc_input.setAttribute('type', 'hidden');
+      itemdesc_input.setAttribute('rownum', rownum);
+      itemdesc_input.setAttribute('value', "$desc");
+      amount_cell.appendChild(itemdesc_input);
+      row.appendChild(pkg_cell);
+      row.appendChild(amount_cell);
+      rownum++;
+    !;
+};
+
+my $key_generator = sub {
+  my $listref = shift;
+  my ($cust_bill_pkg, $amount, $hashref) = @$listref;
+  my $setup_or_recur = $cust_bill_pkg->setup > 0 ? 'setup' : 'recur';
+  my $taxlinenum = $hashref->{billpkgtaxlocationnum} ||
+                   $hashref->{billpkgtaxratelocationnum} ||
+                   '';
+                   
+  join(':', $cust_bill_pkg->billpkgnum, $setup_or_recur, $taxlinenum);
+};
+
+my $onload = 'return true;';
+
+if ($cgi->param('error')) {
+
+  my $set_sub_amounts =
+    join(';', map { "myform.subamount$_.value = ". $cgi->param("subamount$_") }
+               grep { /.+/ }
+               map { /^subnum(\d+)$/ ? $1 : '' }
+               $cgi->param
+  );
+  $set_sub_amounts &&= "$set_sub_amounts;sub_changed(myform.subamount0)";
+
+  $onload = qq!
+    var myform = document.getElementById('ApplicationForm');
+    changed(myform.elements['$dst_pkey']);
+    $set_sub_amounts;
+    return true;
+  !;
+}
+
 </%init>