summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjeff <jeff>2009-10-28 19:01:18 +0000
committerjeff <jeff>2009-10-28 19:01:18 +0000
commit2cb70470a8e5c3287146008e4ce2c4eb9f242373 (patch)
treee8d161557d4420f988245f3bf00d25d80b1d73d4
parent57f5975a062022e280680feed1f692f3e937414b (diff)
UI changes for credit applications include on the fly tax calculations #4729
-rw-r--r--FS/FS/cust_bill_pkg.pm3
-rw-r--r--FS/FS/cust_credit.pm3
-rw-r--r--FS/FS/cust_main.pm241
-rw-r--r--httemplate/edit/elements/ApplicationCommon.html231
-rwxr-xr-xhttemplate/edit/process/cust_credit_bill.cgi6
-rw-r--r--httemplate/edit/process/elements/ApplicationCommon.html18
6 files changed, 395 insertions, 107 deletions
diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm
index 4058f1f38..016b8bf66 100644
--- a/FS/FS/cust_bill_pkg.pm
+++ b/FS/FS/cust_bill_pkg.pm
@@ -627,7 +627,8 @@ sub disintegrate {
}
#split usage from recur
- my $usage = sprintf( "%.2f", $cust_bill_pkg{recur}->usage );
+ my $usage = sprintf( "%.2f", $cust_bill_pkg{recur}->usage )
+ if exists($cust_bill_pkg{recur});
warn "usage is $usage\n" if $DEBUG > 1;
if ($usage) {
my $cust_bill_pkg_usage =
diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm
index 6c3effa13..fda10decf 100644
--- a/FS/FS/cust_credit.pm
+++ b/FS/FS/cust_credit.pm
@@ -306,6 +306,9 @@ sub check {
return "amount must be > 0 " if $self->amount <= 0;
+ return "amount must be greater or equal to amount applied"
+ if $self->unapplied < 0;
+
return "Unknown customer"
unless qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index 1c6284976..700e15a79 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -2623,6 +2623,141 @@ sub bill {
}
+ my $listref_or_error =
+ $self->calculate_taxes( \@cust_bill_pkg, \%taxlisthash, $invoice_time);
+
+ unless ( ref( $listref_or_error ) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $listref_or_error;
+ }
+
+ foreach my $taxline ( @$listref_or_error ) {
+ $total_setup = sprintf('%.2f', $total_setup+$taxline->setup );
+ push @cust_bill_pkg, $taxline;
+ }
+
+ #add tax adjustments
+ warn "adding tax adjustments...\n" if $DEBUG > 2;
+ foreach my $cust_tax_adjustment (
+ qsearch('cust_tax_adjustment', { 'custnum' => $self->custnum,
+ 'billpkgnum' => '',
+ }
+ )
+ ) {
+
+ my $tax = sprintf('%.2f', $cust_tax_adjustment->amount );
+
+ my $itemdesc = $cust_tax_adjustment->taxname;
+ $itemdesc = '' if $itemdesc eq 'Tax';
+
+ push @cust_bill_pkg, new FS::cust_bill_pkg {
+ 'pkgnum' => 0,
+ 'setup' => $tax,
+ 'recur' => 0,
+ 'sdate' => '',
+ 'edate' => '',
+ 'itemdesc' => $itemdesc,
+ 'itemcomment' => $cust_tax_adjustment->comment,
+ 'cust_tax_adjustment' => $cust_tax_adjustment,
+ #'cust_bill_pkg_tax_location' => \@cust_bill_pkg_tax_location,
+ };
+
+ }
+
+ my $charged = sprintf('%.2f', $total_setup + $total_recur );
+
+ my @cust_bill = $self->cust_bill;
+ my $balance = $self->balance;
+ my $previous_balance = scalar(@cust_bill)
+ ? ( $cust_bill[$#cust_bill]->billing_balance || 0 )
+ : 0;
+
+ $previous_balance += $cust_bill[$#cust_bill]->charged
+ if scalar(@cust_bill);
+ #my $balance_adjustments =
+ # sprintf('%.2f', $balance - $prior_prior_balance - $prior_charged);
+
+ #create the new invoice
+ my $cust_bill = new FS::cust_bill ( {
+ 'custnum' => $self->custnum,
+ '_date' => ( $invoice_time ),
+ 'charged' => $charged,
+ 'billing_balance' => $balance,
+ 'previous_balance' => $previous_balance,
+ 'invoice_terms' => $options{'invoice_terms'},
+ } );
+ $error = $cust_bill->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't create invoice for customer #". $self->custnum. ": $error";
+ }
+
+ foreach my $cust_bill_pkg ( @cust_bill_pkg ) {
+ $cust_bill_pkg->invnum($cust_bill->invnum);
+ my $error = $cust_bill_pkg->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't create invoice line item: $error";
+ }
+ }
+
+
+ foreach my $hook ( @precommit_hooks ) {
+ eval {
+ &{$hook}; #($self) ?
+ };
+ if ( $@ ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "$@ running precommit hook $hook\n";
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ ''; #no error
+}
+
+=item calculate_taxes LINEITEMREF TAXHASHREF INVOICE_TIME
+
+This is a weird one. Perhaps it should not even be exposed.
+
+Generates tax line items (see L<FS::cust_bill_pkg>) for this customer.
+Usually used internally by bill method B<bill>.
+
+If there is an error, returns the error, otherwise returns reference to a
+list of line items suitable for insertion.
+
+=over 4
+
+=item LINEITEMREF
+
+An array ref of the line items being billed.
+
+=item TAXHASHREF
+
+A strange beast. The keys to this hash are internal identifiers consisting
+of the name of the tax object type, a space, and its unique identifier ( e.g.
+ 'cust_main_county 23' ). The values of the hash are listrefs. The first
+item in the list is the tax object. The remaining items are either line
+items or floating point values (currency amounts).
+
+The taxes are calculated on this entity. Calculated exemption records are
+transferred to the LINEITEMREF items on the assumption that they are related.
+
+Read the source.
+
+=item INVOICE_TIME
+
+This specifies the date appearing on the associated invoice. Some
+jurisdictions (i.e. Texas) have tax exemptions which are date sensitive.
+
+=back
+
+=cut
+sub calculate_taxes {
+ my ($self, $cust_bill_pkg, $taxlisthash, $invoice_time) = @_;
+
+ my @tax_line_items = ();
+
warn "having a look at the taxes we found...\n" if $DEBUG > 2;
# keys are tax names (as printed on invoices / itemdesc )
@@ -2641,20 +2776,18 @@ sub bill {
# values are listrefs of cust_bill_pkg_tax_rate_location hashrefs
my %tax_rate_location = ();
- foreach my $tax ( keys %taxlisthash ) {
- my $tax_object = shift @{ $taxlisthash{$tax} };
+ foreach my $tax ( keys %$taxlisthash ) {
+ my $tax_object = shift @{ $taxlisthash->{$tax} };
warn "found ". $tax_object->taxname. " as $tax\n" if $DEBUG > 2;
- warn " ". join('/', @{ $taxlisthash{$tax} } ). "\n" if $DEBUG > 2;
+ warn " ". join('/', @{ $taxlisthash->{$tax} } ). "\n" if $DEBUG > 2;
my $hashref_or_error =
- $tax_object->taxline( $taxlisthash{$tax},
+ $tax_object->taxline( $taxlisthash->{$tax},
'custnum' => $self->custnum,
'invoice_time' => $invoice_time
);
- unless ( ref($hashref_or_error) ) {
- $dbh->rollback if $oldAutoCommit;
- return $hashref_or_error;
- }
- unshift @{ $taxlisthash{$tax} }, $tax_object;
+ return $hashref_or_error unless ref($hashref_or_error);
+
+ unshift @{ $taxlisthash->{$tax} }, $tax_object;
my $name = $hashref_or_error->{'name'};
my $amount = $hashref_or_error->{'amount'};
@@ -2694,9 +2827,9 @@ sub bill {
}
#move the cust_tax_exempt_pkg records to the cust_bill_pkgs we will commit
- my %packagemap = map { $_->pkgnum => $_ } @cust_bill_pkg;
- foreach my $tax ( keys %taxlisthash ) {
- foreach ( @{ $taxlisthash{$tax} }[1 ... scalar(@{ $taxlisthash{$tax} })] ) {
+ my %packagemap = map { $_->pkgnum => $_ } @$cust_bill_pkg;
+ foreach my $tax ( keys %$taxlisthash ) {
+ foreach ( @{ $taxlisthash->{$tax} }[1 ... scalar(@{ $taxlisthash->{$tax} })] ) {
next unless ref($_) eq 'FS::cust_bill_pkg';
push @{ $packagemap{$_->pkgnum}->_cust_tax_exempt_pkg },
@@ -2726,7 +2859,6 @@ sub bill {
next unless $tax;
$tax = sprintf('%.2f', $tax );
- $total_setup = sprintf('%.2f', $total_setup+$tax );
my $pkg_category = qsearchs( 'pkg_category', { 'categoryname' => $taxname,
'disabled' => '',
@@ -2745,7 +2877,7 @@ sub bill {
}
- push @cust_bill_pkg, new FS::cust_bill_pkg {
+ push @tax_line_items, new FS::cust_bill_pkg {
'pkgnum' => 0,
'setup' => $tax,
'recur' => 0,
@@ -2759,88 +2891,9 @@ sub bill {
}
- #add tax adjustments
- warn "adding tax adjustments...\n" if $DEBUG > 2;
- foreach my $cust_tax_adjustment (
- qsearch('cust_tax_adjustment', { 'custnum' => $self->custnum,
- 'billpkgnum' => '',
- }
- )
- ) {
-
- my $tax = sprintf('%.2f', $cust_tax_adjustment->amount );
- $total_setup = sprintf('%.2f', $total_setup+$tax );
-
- my $itemdesc = $cust_tax_adjustment->taxname;
- $itemdesc = '' if $itemdesc eq 'Tax';
-
- push @cust_bill_pkg, new FS::cust_bill_pkg {
- 'pkgnum' => 0,
- 'setup' => $tax,
- 'recur' => 0,
- 'sdate' => '',
- 'edate' => '',
- 'itemdesc' => $itemdesc,
- 'itemcomment' => $cust_tax_adjustment->comment,
- 'cust_tax_adjustment' => $cust_tax_adjustment,
- #'cust_bill_pkg_tax_location' => \@cust_bill_pkg_tax_location,
- };
-
- }
-
- my $charged = sprintf('%.2f', $total_setup + $total_recur );
-
- my @cust_bill = $self->cust_bill;
- my $balance = $self->balance;
- my $previous_balance = scalar(@cust_bill)
- ? ( $cust_bill[$#cust_bill]->billing_balance || 0 )
- : 0;
-
- $previous_balance += $cust_bill[$#cust_bill]->charged
- if scalar(@cust_bill);
- #my $balance_adjustments =
- # sprintf('%.2f', $balance - $prior_prior_balance - $prior_charged);
-
- #create the new invoice
- my $cust_bill = new FS::cust_bill ( {
- 'custnum' => $self->custnum,
- '_date' => ( $invoice_time ),
- 'charged' => $charged,
- 'billing_balance' => $balance,
- 'previous_balance' => $previous_balance,
- 'invoice_terms' => $options{'invoice_terms'},
- } );
- $error = $cust_bill->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "can't create invoice for customer #". $self->custnum. ": $error";
- }
-
- foreach my $cust_bill_pkg ( @cust_bill_pkg ) {
- $cust_bill_pkg->invnum($cust_bill->invnum);
- my $error = $cust_bill_pkg->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "can't create invoice line item: $error";
- }
- }
-
-
- foreach my $hook ( @precommit_hooks ) {
- eval {
- &{$hook}; #($self) ?
- };
- if ( $@ ) {
- $dbh->rollback if $oldAutoCommit;
- return "$@ running precommit hook $hook\n";
- }
- }
-
- $dbh->commit or die $dbh->errstr if $oldAutoCommit;
- ''; #no error
+ \@tax_line_items;
}
-
sub _make_lines {
my ($self, %params) = @_;
diff --git a/httemplate/edit/elements/ApplicationCommon.html b/httemplate/edit/elements/ApplicationCommon.html
index b46a3c8fe..181430c5a 100644
--- a/httemplate/edit/elements/ApplicationCommon.html
+++ b/httemplate/edit/elements/ApplicationCommon.html
@@ -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>
@@ -57,18 +59,26 @@ Examples:
<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>
% }
@@ -81,10 +91,16 @@ function changed(what) {
if ( dst == '' ) {
what.form.submit.disabled=true;
+%if ($src_pkey eq 'crednum') {
+ what.form.tax_button.disabled=true;
+%}
return true;
}
what.form.submit.disabled=false;
+%if ($src_pkey eq 'crednum') {
+ what.form.tax_button.disabled=false;
+%}
% foreach my $dst ( @dst ) {
@@ -120,14 +136,14 @@ function changed(what) {
% my $owed = $taxX->owed;
% my $key = &{$key_generator}([ $cbp, 0, { $pkey => $taxX->$pkey } ]);
% my $toapp = exists($apphash{ $key }) ? $apphash{ $key }->[1] : 0;
- <% &{$row_generator}( $cbp, $taxX->desc, $owed, $toapp, $taxX->$pkey ) %>
+ <% &{$row_generator}( $key, $cbp, $taxX->desc, $owed, $toapp, $taxX->$pkey ) %>
% $total_owed -= $owed;
% $amount -= $toapp;
% }
% $desc .= ' (default)';
% }
% if ( $total_owed > 0 ) {
- <% &{$row_generator}($cbp, $desc, $total_owed, $amount, '') %>
+ <% &{$row_generator}($key, $cbp, $desc, $total_owed, $amount, '') %>
% }
% }
% }
@@ -143,17 +159,156 @@ function sub_changed(what) {
var table = document.getElementById('ApplicationTable');
var i = table.rows.length;
while(i-- > 2) {
- var amount_input = table.rows[i].getElementsByTagName('input').item(0);
- amount += parseFloat( amount_input.value ) || 0;
+ 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:
+% if ($use_sub_dst_thing && $src_pkey eq 'crednum') {
+<CENTER><BUTTON TYPE="button" NAME="tax_button" ID="tax_button" onClick="do_calculate_tax(this);" DISABLED>Calculate Tax</BUTTON></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';
+
+ 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 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 // 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) {
+ 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 (what) {
+ what.form.src_amount.value = what.value;
+ var unapplied_cell = document.getElementById('unapplied_amount');
+ unapplied_cell.innerHTML = '<B><% $money_char %>' + what.value + '</B>';
+ set_amount_color(what);
+}
+
+</SCRIPT>
+
+%}
+
<TABLE ID="ApplicationTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
<TR>
@@ -173,7 +328,7 @@ Apply to:
<TD ALIGN="right">Amount: </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 %>" >
+ <INPUT TYPE="hidden" NAME="amount" VALUE="<% $amount %>" >
% }
</TR>
@@ -184,6 +339,14 @@ Apply to:
</FORM>
+<SCRIPT TYPE="text/javascript">
+
+function myOnLoadFunction () {
+ <% $onload %>
+}
+
+</SCRIPT>
+
<% include('/elements/footer.html') %>
<%init>
@@ -209,13 +372,21 @@ 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+)$/;
@@ -231,6 +402,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
@@ -240,9 +413,11 @@ my @dst = sort { $a->_date <=> $b->_date
qsearch($dst_table, { 'custnum' => $src->custnum } );
my $row_generator = sub {
- my ($cust_bill_pkg, $desc, $owed, $amount, $taxXnum) = @_;
+ 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!
@@ -277,6 +452,20 @@ my $row_generator = sub {
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++;
@@ -294,4 +483,24 @@ my $key_generator = sub {
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>
diff --git a/httemplate/edit/process/cust_credit_bill.cgi b/httemplate/edit/process/cust_credit_bill.cgi
index c0f34ae5b..d3847dc40 100755
--- a/httemplate/edit/process/cust_credit_bill.cgi
+++ b/httemplate/edit/process/cust_credit_bill.cgi
@@ -10,4 +10,10 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Apply credit');
+if ( $cgi->param('src_amount') ) {
+ die "access denied"
+ unless ( $FS::CurrentUser::CurrentUser->access_right('Post credit') &&
+ $FS::CurrentUser::CurrentUser->access_right('Delete credit') );
+}
+
</%init>
diff --git a/httemplate/edit/process/elements/ApplicationCommon.html b/httemplate/edit/process/elements/ApplicationCommon.html
index 3cb7ae6bf..c7bdd3ea2 100644
--- a/httemplate/edit/process/elements/ApplicationCommon.html
+++ b/httemplate/edit/process/elements/ApplicationCommon.html
@@ -34,6 +34,8 @@ Examples:
my %opt = @_;
+my $error = '';
+
my $src_thing = ucfirst($opt{'src_thing'});
my $src_table = $opt{'src_table'};
my $src_pkey = dbdef->table($src_table)->primary_key;
@@ -58,6 +60,10 @@ my @subitems = map { [ $cgi->param("subnum$_"), $cgi->param("subamount$_"), $cgi
my %options = ();
$options{subitems} = \@subitems if scalar(@subitems);
+
+my $oldAutoCommit = $FS::UID::AutoCommit;
+local $FS::UID::AutoCommit = 0;
+my $dbh = dbh;
my $new;
# $new = new FS::cust_refund ( {
@@ -70,6 +76,11 @@ my $new;
# } );
#} else {
+ if ($src->amount != $cgi->param('src_amount')) {
+ $src->amount($cgi->param('src_amount'));
+ $error = $src->replace;
+ }
+
my $class = 'FS::'. $opt{link_table};
$new = $class->new( {
@@ -82,6 +93,11 @@ my $new;
$options{manual} = 1;
-my $error = $new->insert( %options );
+$error ||= $new->insert( %options );
+if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+} else {
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+}
</%init>