[freeside-commits] branch FREESIDE_2_3_BRANCH updated. 076d8c9e55781a4ad292914d30eed4d954cecb13

Ivan ivan at 420.am
Sun Dec 9 10:30:58 PST 2012


The branch, FREESIDE_2_3_BRANCH has been updated
       via  076d8c9e55781a4ad292914d30eed4d954cecb13 (commit)
      from  25560f9267a56a81a5e198187e9d40e077ee7655 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 076d8c9e55781a4ad292914d30eed4d954cecb13
Author: Ivan Kohler <ivan at freeside.biz>
Date:   Sun Dec 9 10:30:57 2012 -0800

    create credits by selecting line items, RT#18676

diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm
index ca421c3..131f969 100644
--- a/FS/FS/cust_bill_pkg.pm
+++ b/FS/FS/cust_bill_pkg.pm
@@ -797,8 +797,9 @@ sub set_display {
 
 =item disintegrate
 
-Returns a list of cust_bill_pkg objects each with no more than a single class
-(including setup or recur) of charge.
+Returns a hash: keys are "setup", "recur" or usage classnum, values are
+FS::cust_bill_pkg objects, each with no more than a single class (setup or
+recur) of charge.
 
 =cut
 
@@ -1077,6 +1078,18 @@ sub _X_show_zero {
   $self->cust_pkg->_X_show_zero($what);
 }
 
+=item credited [ BEFORE, AFTER, OPTIONS ]
+
+Returns the sum of credits applied to this item.  Arguments are the same as
+owed_sql/paid_sql/credited_sql.
+
+=cut
+
+sub credited {
+  my $self = shift;
+  $self->scalar_sql('SELECT '. $self->credited_sql(@_).' FROM cust_bill_pkg WHERE billpkgnum = ?', $self->billpkgnum);
+}
+
 =back
 
 =head1 CLASS METHODS
diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm
index 6185fc4..f7f3758 100644
--- a/FS/FS/cust_credit.pm
+++ b/FS/FS/cust_credit.pm
@@ -172,7 +172,7 @@ sub insert {
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
 
-  #false laziness w/ cust_credit::insert
+  #false laziness w/ cust_pay::insert
   if ( $unsuspendauto && $old_balance && $cust_main->balance <= 0 ) {
     my @errors = $cust_main->unsuspend;
     #return 
@@ -618,6 +618,259 @@ sub credited_sql {
   unapplied_sql();
 }
 
+=item credit_lineitems
+
+Example:
+
+  my $error = FS::cust_credit->credit_lineitems(
+    #the lineitems
+    'billpkgnums'       => \@billpkgnums,
+
+    #the credit
+    'newreasonnum'      => scalar($cgi->param('newreasonnum')),
+    'newreasonnum_type' => scalar($cgi->param('newreasonnumT')),
+    map { $_ => scalar($cgi->param($_)) }
+      fields('cust_credit')  
+  );
+
+=cut
+
+#maybe i should just be an insert with extra args instead of a class method
+use FS::cust_bill_pkg;
+sub credit_lineitems {
+  my( $class, %arg ) = @_;
+
+  my $curuser = $FS::CurrentUser::CurrentUser;
+
+  #some false laziness w/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html
+
+  my $cust_main = qsearchs({
+    'table'     => 'cust_main',
+    'hashref'   => { 'custnum' => $arg{custnum} },
+    'extra_sql' => ' AND '. $curuser->agentnums_sql,
+  }) or return 'unknown customer';
+
+
+  local $SIG{HUP} = 'IGNORE';
+  local $SIG{INT} = 'IGNORE';
+  local $SIG{QUIT} = 'IGNORE';
+  local $SIG{TERM} = 'IGNORE';
+  local $SIG{TSTP} = 'IGNORE';
+  local $SIG{PIPE} = 'IGNORE';
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  #my @cust_bill_pkg = qsearch({
+  #  'select'    => 'cust_bill_pkg.*',
+  #  'table'     => 'cust_bill_pkg',
+  #  'addl_from' => ' LEFT JOIN cust_bill USING (invnum)  '.
+  #                 ' LEFT JOIN cust_main USING (custnum) ',
+  #  'extra_sql' => ' WHERE custnum = $custnum AND billpkgnum IN ('.
+  #                     join( ',', @{$arg{billpkgnums}} ). ')',
+  #  'order_by'  => 'ORDER BY invnum ASC, billpkgnum ASC',
+  #});
+
+  my $error = '';
+  if ($arg{reasonnum} == -1) {
+
+    $error = 'Enter a new reason (or select an existing one)'
+      unless $arg{newreasonnum} !~ /^\s*$/;
+    my $reason = new FS::reason {
+                   'reason'      => $arg{newreasonnum},
+                   'reason_type' => $arg{newreasonnum_type},
+                 };
+    $error ||= $reason->insert;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return "Error inserting reason: $error";
+    }
+    $arg{reasonnum} = $reason->reasonnum;
+  }
+
+  my $cust_credit = new FS::cust_credit ( {
+    map { $_ => $arg{$_} }
+      #fields('cust_credit')
+      qw( custnum _date amount reason reasonnum addlinfo ), #pkgnum eventnum
+  } );
+  $error = $cust_credit->insert;
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return "Error inserting credit: $error";
+  }
+
+  #my $subtotal = 0;
+  my $taxlisthash = {};
+  my %cust_credit_bill = ();
+  my %cust_bill_pkg = ();
+  my %cust_credit_bill_pkg = ();
+  foreach my $billpkgnum ( @{$arg{billpkgnums}} ) {
+    my $setuprecur = shift @{$arg{setuprecurs}};
+    my $amount = shift @{$arg{amounts}};
+
+    my $cust_bill_pkg = qsearchs({
+      'table'     => 'cust_bill_pkg',
+      'hashref'   => { 'billpkgnum' => $billpkgnum },
+      'addl_from' => 'LEFT JOIN cust_bill USING (invnum)',
+      'extra_sql' => 'AND custnum = '. $cust_main->custnum,
+    }) or die "unknown billpkgnum $billpkgnum";
+
+    if ( $setuprecur eq 'setup' ) {
+      $cust_bill_pkg->setup($amount);
+      $cust_bill_pkg->recur(0);
+      $cust_bill_pkg->unitrecur(0);
+      $cust_bill_pkg->type('');
+    } else {
+      $setuprecur = 'recur'; #in case its a usage classnum?
+      $cust_bill_pkg->recur($amount);
+      $cust_bill_pkg->setup(0);
+      $cust_bill_pkg->unitsetup(0);
+    }
+
+    push @{$cust_bill_pkg{$cust_bill_pkg->invnum}}, $cust_bill_pkg;
+
+    #unapply any payments applied to this line item (other credits too?)
+    foreach my $cust_bill_pay_pkg ( $cust_bill_pkg->cust_bill_pay_pkg($setuprecur) ) {
+      $error = $cust_bill_pay_pkg->delete;
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "Error unapplying payment: $error";
+      }
+    }
+
+    #$subtotal += $amount;
+    $cust_credit_bill{$cust_bill_pkg->invnum} += $amount;
+    push @{ $cust_credit_bill_pkg{$cust_bill_pkg->invnum} },
+      new FS::cust_credit_bill_pkg {
+        'billpkgnum' => $cust_bill_pkg->billpkgnum,
+        'amount'     => $amount,
+        'setuprecur' => $setuprecur,
+        'sdate'      => $cust_bill_pkg->sdate,
+        'edate'      => $cust_bill_pkg->edate,
+      };
+
+    my $part_pkg = $cust_bill_pkg->part_pkg;
+    $cust_main->_handle_taxes( $part_pkg,
+                               $taxlisthash,
+                               $cust_bill_pkg,
+                               $cust_bill_pkg->cust_pkg,
+                               $cust_bill_pkg->cust_bill->_date,
+                               $cust_bill_pkg->cust_pkg->pkgpart,
+                             );
+  }
+
+  ###
+  # now loop through %cust_credit_bill and insert those
+  ###
+
+  # (hack to prevent cust_credit_bill_pkg insertion)
+  local($FS::cust_bill_ApplicationCommon::skip_apply_to_lineitems_hack) = 1;
+
+  foreach my $invnum ( sort { $a <=> $b } keys %cust_credit_bill ) {
+
+    #taxes
+
+    if ( @{ $cust_bill_pkg{$invnum} } ) {
+
+      my $listref_or_error = 
+        $cust_main->calculate_taxes( $cust_bill_pkg{$invnum}, $taxlisthash, $cust_bill_pkg{$invnum}->[0]->cust_bill->_date );
+
+      unless ( ref( $listref_or_error ) ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "Error calculating taxes: $listref_or_error";
+      }
+
+      # so, loop through the taxlines, apply just that amount to the tax line
+      #  item (save for later insert) & add to $
+
+      #my @taxlines = ();
+      #my $taxtotal = 0;
+      foreach my $taxline ( @$listref_or_error ) {
+
+        #find equivalent tax line items on the existing invoice
+        # (XXX need a more specific/deterministic way to find these than itemdesc..)
+        my $tax_cust_bill_pkg = qsearchs('cust_bill_pkg', {
+          'invnum'   => $invnum,
+          'pkgnum'   => 0, #$taxline->invnum
+          'itemdesc' => $taxline->desc,
+        });
+
+        my $amount = $taxline->setup;
+        my $desc = $taxline->desc;
+
+        foreach my $location ( $tax_cust_bill_pkg->cust_bill_pkg_tax_Xlocation ) {
+
+          $location->cust_bill_pkg_desc($taxline->desc); #ugh @ that kludge
+
+          #$taxtotal += $location->amount;
+          $amount -= $location->amount;
+
+          #push @taxlines,
+          #  #[ $location->desc, $taxline->setup, $taxlocnum, $taxratelocnum ];
+          #  [ $location->desc, $location->amount, $taxlocnum, $taxratelocnum ];
+          $cust_credit_bill{$invnum} += $location->amount;
+          push @{ $cust_credit_bill_pkg{$invnum} },
+            new FS::cust_credit_bill_pkg {
+              'billpkgnum'                => $tax_cust_bill_pkg->billpkgnum,
+              'amount'                    => $location->amount,
+              'setuprecur'                => 'setup',
+              'billpkgtaxlocationnum'     => $location->billpkgtaxlocationnum,
+              'billpkgtaxratelocationnum' => $location->billpkgtaxratelocationnum,
+            };
+
+        }
+        if ($amount > 0) {
+          #$taxtotal += $amount;
+          #push @taxlines,
+          #  [ $taxline->itemdesc. ' (default)', sprintf('%.2f', $amount), '', '' ];
+
+          $cust_credit_bill{$invnum} += $amount;
+          push @{ $cust_credit_bill_pkg{$invnum} },
+            new FS::cust_credit_bill_pkg {
+              'billpkgnum' => $tax_cust_bill_pkg->billpkgnum,
+              'amount'     => $amount,
+              'setuprecur' => 'setup',
+            };
+
+        }
+      }
+
+    }
+
+    #insert cust_credit_bill
+
+    my $cust_credit_bill = new FS::cust_credit_bill {
+      'crednum' => $cust_credit->crednum,
+      'invnum'  => $invnum,
+      'amount'  => $cust_credit_bill{$invnum},
+    };
+    $error = $cust_credit_bill->insert;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return "Error applying credit of $cust_credit_bill{$invnum} ".
+             " to invoice $invnum: $error";
+    }
+
+    #and then insert cust_credit_bill_pkg for each cust_bill_pkg
+    foreach my $cust_credit_bill_pkg ( @{$cust_credit_bill_pkg{$invnum}} ) {
+      $cust_credit_bill_pkg->creditbillnum( $cust_credit_bill->creditbillnum );
+      $error = $cust_credit_bill_pkg->insert;
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "Error applying credit to line item: $error";
+      }
+    }
+
+  }
+
+  #$return->{taxlines} = \@taxlines;
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  '';
+
+}
+
 =back
 
 =head1 BUGS
diff --git a/httemplate/edit/credit-cust_bill_pkg.html b/httemplate/edit/credit-cust_bill_pkg.html
new file mode 100644
index 0000000..e317936
--- /dev/null
+++ b/httemplate/edit/credit-cust_bill_pkg.html
@@ -0,0 +1,249 @@
+<& /elements/header-popup.html, 'Credit line items' &>
+
+<FORM ACTION="process/credit-cust_bill_pkg.html" METHOD="POST">
+<INPUT TYPE="hidden" NAME="crednum" VALUE="">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum |h %>">
+<INPUT TYPE="hidden" NAME="paybatch" VALUE="">
+<INPUT TYPE="hidden" NAME="_date" VALUE="<% time %>">
+<table>
+
+% my $old_invnum = 0; 
+%# foreach my $cust_bill_pkg ( @cust_bill_pkg ) {
+% foreach my $item ( @items ) {
+%   my( $setuprecur, $cust_bill_pkg ) = @$item;
+
+%   my $method = $setuprecur eq 'setup' ? 'setup' : 'recur';
+%   my $amount = $cust_bill_pkg->$method();
+%   my $credited = $cust_bill_pkg->credited('', '', 'setuprecur'=>$method);
+%   $amount -= $credited;
+%   $amount = sprintf('%.2f', $amount);
+%   next unless $amount > 0;
+
+%   if ( $cust_bill_pkg->invnum ne $old_invnum ) {
+      <TR><TD COLSPAN=3 BGCOLOR="#f8f8f8"> </TD></TR>
+      <TR><TH COLSPAN=3 BGCOLOR="#f8f8f8" ALIGN="left">Invoice #<% $cust_bill_pkg->invnum %> - <% time2str($date_format, $cust_bill_pkg->cust_bill->_date) %></TD></TR>
+%     $old_invnum = $cust_bill_pkg->invnum;
+%   }
+
+    <TR>
+      <TD>
+        <INPUT TYPE            = "checkbox"
+               NAME            = "billpkgnum<% $cust_bill_pkg->billpkgnum.'-'. $setuprecur %>"
+               VALUE           = "<% $amount %>"
+               onClick         = "calc_total(this)"
+               data-amount     = "<% $amount %>"
+               data-billpkgnum = "<% $cust_bill_pkg->billpkgnum %>"
+               data-setuprecur = "<% $setuprecur %>"
+        >
+      </TD>
+      <TD BGCOLOR="#ffffff"><% $cust_bill_pkg->desc |h %></TD>
+%#    show one-time/setup vs recur vs usage?
+      <TD BGCOLOR="#ffffff" ALIGN="right"><% $money_char. $amount %></TD>
+    </TR>
+
+% }
+
+<TR><TD COLSPAN=3 BGCOLOR="#f8f8f8"> </TD></TR>
+<TR>
+  <TD></TD>
+  <TD ALIGN="right">Subtotal: </TD>
+  <TD ALIGN="right" ID="subtotal_td"><% $money_char %><% sprintf('%.2f', 0) %></TD>
+</TR>
+<TR>
+  <TD></TD>
+  <TD ALIGN="right">Taxes: </TD>
+  <TD ALIGN="right" ID="taxtotal_td"><% $money_char %><% sprintf('%.2f', 0) %></TD>
+</TR>
+<TR>
+  <TD></TD>
+  <TH ALIGN="right">Total credit amount: </TD>
+  <TH ALIGN="right" ID="total_td"><% $money_char %><% sprintf('%.2f', 0) %></TD>
+</TR>
+<INPUT TYPE="hidden" NAME="amount" ID="total_el" VALUE="0.00">
+
+</table>
+
+<table>
+
+<& /elements/tr-select-reason.html,
+              'field'          => 'reasonnum',
+              'reason_class'   => 'R',
+              #XXX reconcile both this and show_taxes wanteding to enable this
+              'control_button' => "document.getElementById('credit_button')",
+              'cgi'            => $cgi,
+&>
+
+<TR>
+  <TD ALIGN="right"><% mt('Additional info') |h %></TD>
+  <TD>
+    <INPUT TYPE="text" NAME="addlinfo" VALUE="<% $cgi->param('addlinfo') |h %>">
+  </TD>
+</TR>
+
+</table>
+
+<BR>
+<INPUT TYPE="submit" ID="credit_button" VALUE="Credit" DISABLED>
+
+</FORM>
+
+<% include( '/elements/xmlhttp.html',
+            'url' =>  $p.'misc/xmlhttp-cust_bill_pkg-calculate_taxes.html',
+            'subs' => [ 'calculate_taxes' ],
+          )
+%>
+<SCRIPT TYPE="text/javascript">
+
+function show_taxes(arg) {
+  var argsHash = eval('(' + arg + ')');
+
+  //XXX add an 'ErrorMessage' section to the HTML and re-enable
+  //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'];
+
+//XXX display the tax lines? just a total will do for now
+//
+//  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));
+//  }
+
+  var subtotal = parseFloat( argsHash['subtotal'] );
+
+  var taxtotal = parseFloat( argsHash['taxtotal'] );
+  document.getElementById('taxtotal_td').innerHTML =
+    '<% $money_char %>' + taxtotal.toFixed(2);
+
+  var total = subtotal + taxtotal;
+  document.getElementById('total_td').innerHTML =
+    '<% $money_char %>' + total.toFixed(2);
+  document.getElementById('total_el').value = total.toFixed(2);
+
+  //XXX reconcile both this and the reason selector wanteding to enable this
+  if ( total > 0 ) {
+    document.getElementById('credit_button').disabled = false;
+  }
+    
+}
+
+function calc_total(what) {
+
+  document.getElementById('credit_button').disabled = true;
+
+  var subtotal = 0;
+  // bah, a pain, just using an attribute var re = /^billpkgnum(\d+)$/;
+
+  var el = what.form.elements;
+  var billpkgnums = [];
+  var setuprecurs = [];
+  var amounts = [];
+  for (var i=0; i<el.length; i++) {
+    if ( el[i].type == 'checkbox' && el[i].checked ) {
+      subtotal += parseFloat( el[i].getAttribute('data-amount') );
+      amounts.push(     el[i].getAttribute('data-amount') );
+      billpkgnums.push( el[i].getAttribute('data-billpkgnum') );
+      setuprecurs.push( el[i].getAttribute('data-setuprecur') );
+    }
+  }
+
+  document.getElementById('subtotal_td').innerHTML =
+    '<% $money_char %>' + subtotal.toFixed(2);
+
+  var args = new Array(
+    'custnum',     '<% $custnum %>',
+    'subtotal',    subtotal,
+    'billpkgnums', billpkgnums.join(),
+    'setuprecurs', setuprecurs.join(),
+    'amounts',     amounts.join()
+  );
+
+  calculate_taxes( args, show_taxes );
+
+}
+</SCRIPT>
+
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+die "access denied" unless $curuser->access_right('Post credit');
+
+#a tiny bit of false laziness w/search/cust_bill_pkg.cgi, but we're pretty
+# specialized and a piece of UI, not a report
+#slightly more false laziness w/httemplate/edit/elements/ApplicationCommon.html
+# show_taxes & calc_total here/do_calculate_tax there
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+my $date_format = $conf->config('date_format') || '%m/%d/%Y';
+
+$cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum';
+my $custnum = $1;
+
+my $cust_main = qsearchs({
+  'table'     => 'cust_main',
+  'hashref'   => { 'custnum' => $custnum },
+  'extra_sql' => ' AND '. $curuser->agentnums_sql,
+}) or die 'unknown customer';
+
+my @cust_bill_pkg = qsearch({
+  'select'    => 'cust_bill_pkg.*',
+  'table'     => 'cust_bill_pkg',
+  'addl_from' => 'LEFT JOIN cust_bill USING (invnum)',
+  'extra_sql' => "WHERE custnum = $custnum AND pkgnum != 0",
+  'order_by'  => 'ORDER BY invnum ASC, billpkgnum ASC',
+});
+
+my @items = map { my %hash = $_->disintegrate;
+                  map [ $_, $hash{$_} ],
+                    keys(%hash);
+                }
+              @cust_bill_pkg;
+
+#omit line items which have been previously credited?  would be nice
+
+</%init>
diff --git a/httemplate/edit/cust_credit.cgi b/httemplate/edit/cust_credit.cgi
index 6e8a9c9..4dba1e7 100755
--- a/httemplate/edit/cust_credit.cgi
+++ b/httemplate/edit/cust_credit.cgi
@@ -34,6 +34,7 @@
     <TD>
       <INPUT TYPE="text" NAME="addlinfo" VALUE="<% $cgi->param('addlinfo') |h %>">
     </TD>
+  </TR>
 
 % if ( $conf->exists('credits-auto-apply-disable') ) {
         <INPUT TYPE="HIDDEN" NAME="apply" VALUE="no">
diff --git a/httemplate/edit/process/credit-cust_bill_pkg.html b/httemplate/edit/process/credit-cust_bill_pkg.html
new file mode 100644
index 0000000..d3323e6
--- /dev/null
+++ b/httemplate/edit/process/credit-cust_bill_pkg.html
@@ -0,0 +1,41 @@
+%if ($error) {
+%  errorpage_popup($error); #XXX redirect back for correction...
+%} else {
+<& /elements/header-popup.html, 'Credit successful' &>
+  <SCRIPT TYPE="text/javascript">
+    window.top.location.reload();
+  </SCRIPT>
+  </BODY></HTML>
+% }
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Post credit');
+
+my @billpkgnum_setuprecurs =
+  map { $_ =~ /^billpkgnum(\d+\-\w*)$/ or die 'gm#23'; $1; } 
+  grep { $_ =~ /^billpkgnum\d+\-\w*$/ && $cgi->param($_) } $cgi->param;
+
+my @billpkgnums = ();
+my @setuprecurs = ();
+my @amounts = ();
+foreach my $billpkgnum_setuprecur (@billpkgnum_setuprecurs) {
+  my $amount = $cgi->param("billpkgnum$billpkgnum_setuprecur");
+  my( $billpkgnum, $setuprecur ) = split('-', $billpkgnum_setuprecur);
+  push @billpkgnums, $billpkgnum;
+  push @setuprecurs, $setuprecur;
+  push @amounts,     $amount;
+}
+
+my $error = FS::cust_credit->credit_lineitems(
+  'newreasonnum'      => scalar($cgi->param('newreasonnum')),
+  'newreasonnum_type' => scalar($cgi->param('newreasonnumT')),
+  'billpkgnums'       => \@billpkgnums,
+  'setuprecurs'       => \@setuprecurs,
+  'amounts'           => \@amounts,
+  map { $_ => scalar($cgi->param($_)) }
+    #fields('cust_credit')  
+    qw( custnum _date amount reason reasonnum addlinfo ), #pkgnum eventnum
+);
+
+</%init>
diff --git a/httemplate/edit/process/cust_credit.cgi b/httemplate/edit/process/cust_credit.cgi
index a4330dc..6795b42 100755
--- a/httemplate/edit/process/cust_credit.cgi
+++ b/httemplate/edit/process/cust_credit.cgi
@@ -15,7 +15,7 @@
 %
 %  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
 %  
-<% header(emt('Credit sucessful')) %>
+<% header(emt('Credit successful')) %>
   <SCRIPT TYPE="text/javascript">
     window.top.location.reload();
   </SCRIPT>
@@ -27,7 +27,7 @@
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Post credit');
 
-$cgi->param('custnum') =~ /^(\d*)$/ or die "Illegal custnum!";
+$cgi->param('custnum') =~ /^(\d+)$/ or die "Illegal custnum!";
 my $custnum = $1;
 
 $cgi->param('reasonnum') =~ /^(-?\d+)$/ or die "Illegal reasonnum";
diff --git a/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html b/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html
new file mode 100644
index 0000000..9935046
--- /dev/null
+++ b/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html
@@ -0,0 +1,123 @@
+<% to_json($return) %>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+die "access denied" unless $curuser->access_right('Post credit');
+
+my $DEBUG = 0;
+
+my $conf = new FS::Conf;
+
+my $sub = $cgi->param('sub');
+
+my $return = {};
+
+if ( $sub eq 'calculate_taxes' ) {
+
+  {
+
+    my %arg = $cgi->param('arg');
+    $return = \%arg;
+    warn join('', map "$_: $arg{$_}\n", keys %arg )
+      if $DEBUG;
+
+    #some false laziness w/cust_credit::credit_lineitems
+
+    my $cust_main = qsearchs({
+      'table'     => 'cust_main',
+      'hashref'   => { 'custnum' => $arg{custnum} },
+      'extra_sql' => ' AND '. $curuser->agentnums_sql,
+    }) or die 'unknown customer';
+
+    my @billpkgnums = split(',', $arg{billpkgnums});
+    my @setuprecurs = split(',', $arg{setuprecurs});
+    my @amounts =     split(',', $arg{amounts});
+
+    my @cust_bill_pkg = ();
+    my $taxlisthash = {};
+    while ( @billpkgnums ) {
+      my $billpkgnum = shift @billpkgnums;
+      my $setuprecur = shift @setuprecurs;
+      my $amount     = shift @amounts;
+
+      my $cust_bill_pkg = qsearchs({
+        'table'     => 'cust_bill_pkg',
+        'hashref'   => { 'billpkgnum' => $billpkgnum },
+        'addl_from' => 'LEFT JOIN cust_bill USING (invnum)',
+        'extra_sql' => 'AND custnum = '. $cust_main->custnum,
+      }) or die "unknown billpkgnum $billpkgnum";
+
+      #shouldn't be passed# next if $cust_bill_pkg->pkgnum == 0;
+
+      if ( $setuprecur eq 'setup' ) {
+        $cust_bill_pkg->setup($amount);
+        $cust_bill_pkg->recur(0);
+        $cust_bill_pkg->unitrecur(0);
+        $cust_bill_pkg->type('');
+      } else {
+        $cust_bill_pkg->recur($amount);
+        $cust_bill_pkg->setup(0);
+        $cust_bill_pkg->unitsetup(0);
+      }
+
+      push @cust_bill_pkg, $cust_bill_pkg;
+
+      my $part_pkg = $cust_bill_pkg->part_pkg;
+      $cust_main->_handle_taxes( $part_pkg,
+                                 $taxlisthash,
+                                 $cust_bill_pkg,
+                                 $cust_bill_pkg->cust_pkg,
+                                 $cust_bill_pkg->cust_bill->_date,
+                                 $cust_bill_pkg->cust_pkg->pkgpart,
+                               );
+
+    }
+
+    if ( @cust_bill_pkg ) {
+
+      my $listref_or_error = 
+        $cust_main->calculate_taxes( \@cust_bill_pkg, $taxlisthash, $cust_bill_pkg[0]->cust_bill->_date );
+
+      unless ( ref( $listref_or_error ) ) {
+        $return->{error} = $listref_or_error;
+        last;
+      }
+
+      my @taxlines = ();
+      my $taxtotal = 0;
+      $return->{taxlines} = \@taxlines;
+      foreach my $taxline ( @$listref_or_error ) {
+        my $amount = $taxline->setup;
+        my $desc = $taxline->desc;
+        foreach my $location ( @{$taxline->cust_bill_pkg_tax_location}, @{$taxline->cust_bill_pkg_tax_rate_location} ) {
+          my $taxlocnum = $location->locationnum || '';
+          my $taxratelocnum = $location->taxratelocationnum || '';
+          $location->cust_bill_pkg_desc($taxline->desc); #ugh @ that kludge
+          $taxtotal += $location->amount;
+          push @taxlines,
+            #[ $location->desc, $taxline->setup, $taxlocnum, $taxratelocnum ];
+            [ $location->desc, $location->amount, $taxlocnum, $taxratelocnum ];
+          $amount -= $location->amount;
+        }
+        if ($amount > 0) {
+          $taxtotal += $amount;
+          push @taxlines,
+            [ $taxline->itemdesc. ' (default)', sprintf('%.2f', $amount), '', '' ];
+        }
+      }
+
+      $return->{taxlines} = \@taxlines;
+      $return->{taxtotal} = sprintf('%.2f', $taxtotal);
+
+    } else {
+
+      $return->{taxlines} = [];
+      $return->{taxtotal} = '0.00';
+
+    }
+
+  }
+
+}
+
+</%init>
diff --git a/httemplate/view/cust_main/payment_history.html b/httemplate/view/cust_main/payment_history.html
index b6e49b6..ed07577 100644
--- a/httemplate/view/cust_main/payment_history.html
+++ b/httemplate/view/cust_main/payment_history.html
@@ -70,6 +70,16 @@
                'actionlabel' => emt('Enter credit'),
                'width'       => 616, #make room for reasons #540 default
   &>
+  |
+  <& /elements/popup_link-cust_main.html,
+               'label'       => emt('Credit line items'),
+               #'action'      => "${p}search/cust_bill_pkg.cgi?nottax=1;type=select",
+               'action'      => "${p}edit/credit-cust_bill_pkg.html",
+               'cust_main'   => $cust_main,
+               'actionlabel' => emt('Credit line items'),
+               'width'       => 884, #763,
+               'height'      => 575,
+  &>
   <BR>
 % } 
 

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/cust_bill_pkg.pm                             |   17 ++-
 FS/FS/cust_credit.pm                               |  255 +++++++++++++++++++-
 httemplate/edit/credit-cust_bill_pkg.html          |  249 +++++++++++++++++++
 httemplate/edit/cust_credit.cgi                    |    1 +
 httemplate/edit/process/credit-cust_bill_pkg.html  |   41 +++
 httemplate/edit/process/cust_credit.cgi            |    4 +-
 .../xmlhttp-cust_bill_pkg-calculate_taxes.html     |  123 ++++++++++
 httemplate/view/cust_main/payment_history.html     |   10 +
 8 files changed, 695 insertions(+), 5 deletions(-)
 create mode 100644 httemplate/edit/credit-cust_bill_pkg.html
 create mode 100644 httemplate/edit/process/credit-cust_bill_pkg.html
 create mode 100644 httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html




More information about the freeside-commits mailing list