[freeside-commits] branch master updated. 93e3a52f23c3473207f29f36cda06adfe221353f

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


The branch, master has been updated
       via  93e3a52f23c3473207f29f36cda06adfe221353f (commit)
      from  b62f98268b17471c7b195d7d193b33c4a6915892 (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 93e3a52f23c3473207f29f36cda06adfe221353f
Author: Ivan Kohler <ivan at freeside.biz>
Date:   Sun Dec 9 10:30:54 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 826569b..a83af13 100644
--- a/FS/FS/cust_bill_pkg.pm
+++ b/FS/FS/cust_bill_pkg.pm
@@ -665,8 +665,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
 
@@ -843,6 +844,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 776112a..245f31a 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 166addb..6630d12 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