<& /elements/error.html &>
+<STYLE TYPE="text/css">
+.select_invnum {
+ text-align: right;
+ width: 220px;
+}
+.select_invnum * {
+ font-family: monospace;
+}
+</STYLE>
<SCRIPT TYPE="text/javascript">
function warnUnload() {
if(document.getElementById("OneTrueTable").rows.length > 3 &&
}
window.onbeforeunload = warnUnload;
-function add_row_callback(rownum, prefix) {
- document.getElementById('enable_app'+rownum).disabled = true;
+function add_row_callback(rownum, values) {
+ if (values) {
+ custnum_update_callback(rownum);
+ } else {
+ document.getElementById('enable_app'+rownum).disabled = true;
+ }
}
-function custnum_update_callback(rownum, prefix) {
+function delete_row_callback(rownum) {
+ var i = 0;
+ var delbutton = document.getElementById('delete'+rownum+'.'+i);
+ var delrows = [];
+ while (delbutton) {
+ delrows[i] = delbutton;
+ i++;
+ delbutton = document.getElementById('delete'+rownum+'.'+i);
+ }
+ delrows = delrows.reverse();
+ for (i = 0; i < delrows.length; i++) {
+ delrows[i].onclick();
+ }
+}
+
+function custnum_update_callback(rownum) {
var custnum = document.getElementById('custnum'+rownum).value;
- document.getElementById('enable_app'+rownum).disabled = (
- custnum == 0 ||
- num_open_invoices[rownum] < 2
- );
+ // if there is a custnum and more than one open invoice, enable
+ // (and check) the box
+ var show_applications = !(custnum > 0 && num_open_invoices[rownum] > 1);
+ var enable_app_checkbox = document.getElementById('enable_app'+rownum);
+ enable_app_checkbox.disabled = show_applications;
+
% if ( $use_discounts ) {
- select_discount_term(rownum, prefix);
+ select_discount_term(rownum);
% }
}
-function select_discount_term(row, prefix) {
- var custnum_obj = document.getElementById('custnum'+prefix+row);
- var select_obj = document.getElementById('discount_term'+prefix+row);
+function invnum_update_callback(rownum) {
+ custnum_update_callback(rownum);
+}
+
+function select_discount_term(row) {
+ var custnum_obj = document.getElementById('custnum'+row);
+ var select_obj = document.getElementById('discount_term'+row);
var value = '';
if (select_obj.type == 'hidden') {
var invoices_for_row = new Object;
+var preloading = 0; // the number of preloading threads currently running
+
+// callback from toggle_application_row: we've received a list of
+// the customer's open invoices. store them.
function update_invoices(rownum, invoices) {
invoices_for_row[rownum] = new Object;
// only called before create_application_row
if (!next) next = function(){}; //optional continuation
var rownum = this.getAttribute('rownum');
if ( this.checked ) {
+ // the user has opted to apply the payment to specific invoices.
+ // - lock the customer
+ // - fetch the list of open invoices
+ // - create a row to select an invoice
+ // - then optionally call "next", with this as the invocant
+ // and the rownum as argument; we use this to preload rows.
var custnum = document.getElementById('custnum'+rownum).value;
if (!custnum) return;
lock_payment_row(rownum, true);
next.call(this, rownum);
}
);
+ } else {
+ // the user has opted not to do that.
+ // - remove all application rows
+ // - unlock the customer
+ var row = document.getElementById('row'+rownum);
+ var table_rows = row.parentNode.rows;
+ for (i = row.sectionRowIndex; i < table_rows.count; i++) {
+ if ( table_rows[i].id.indexof('row'+rownum+'.') > -1 ) {
+ table_rows.removeChild(table_rows[i]);
+ } else {
+ break;
+ }
+ }
+ lock_payment_row(rownum, false);
}
}
var change_app_amount;
+// the user has chosen an invoice. the previously chosen invoice is still
+// in curr_invoice
+// - if there is a value there, put it back on the invoices_for_row list for
+// this customer.
+// - then _remove_ the newly chosen invoice from that list.
+// - find the "owed" element for this application row and set its value to the
+// amount owed on that invoice.
+// - find the "amount" element for this application row and set its value to
+// either "owed" or the remaining payment amount, whichever is less.
+// - call change_app_amount() on that element.
function choose_app_invnum() {
var rownum = this.getAttribute('rownum');
var appnum = this.getAttribute('appnum');
}
}
+// the invoice selector has gained focus. clear its list of options, and
+// replace them with the list of open invoices (from invoices_for_row).
+// if there's already a selected invoice, prepend that to the list.
function focus_app_invnum() {
-% # invoice numbers just display as invoice numbers
var rownum = this.getAttribute('rownum');
- var add_opt = function(obj, value) {
+ var add_opt = function(obj, value, label) {
var o = document.createElement('OPTION');
- o.text = value;
+ o.text = label;
o.value = value;
obj.add(o);
}
this.options.length = 0;
var this_invoice = this.curr_invoice;
if ( this_invoice ) {
- add_opt(this, this_invoice.invnum);
+ add_opt(this, this_invoice.invnum, this_invoice.label);
} else {
- add_opt(this, '');
+ add_opt(this, '', '');
}
for ( var x in invoices_for_row[rownum] ) {
- add_opt(this, invoices_for_row[rownum][x].invnum);
+ add_opt(this,
+ invoices_for_row[rownum][x].invnum,
+ invoices_for_row[rownum][x].label);
}
}
+// an application amount has been changed. if there's any unapplied payment
+// amount, and any remaining invoices_for_row, add a blank application row.
+// (but don't do this while preloading; it will unconditionally add enough
+// rows to show all the attempted applications)
function change_app_amount() {
var rownum = this.getAttribute('rownum');
var appnum = this.getAttribute('appnum');
-%# maybe some kind of warning if amount_unapplied < 0?
-%# only spawn a new application row if there are open invoices left,
-%# and this is the highest-numbered application row for the customer,
-%# and the sum of the applied amounts is < the amount of the payment,
- if ( Object.keys(invoices_for_row[rownum]).length > 0
+ if ( preloading == 0
+ && Object.keys(invoices_for_row[rownum]).length > 0
&& !document.getElementById( 'row'+rownum+'.'+(parseInt(appnum) + 1) )
&& amount_unapplied(rownum) > 0 ) {
create_application_row(rownum, parseInt(appnum) + 1);
-
}
}
+// we're creating a payment application row.
+// create the following elements: <TR>, <TD>s, "Apply to invoice" caption,
+// invnum selector, "owed" display, amount input box, delete button.
function create_application_row(rownum, appnum) {
var payment_row = document.getElementById('row'+rownum);
var tr_app = document.createElement('TR');
td_invnum.setAttribute('colspan', 4);
td_invnum.style.textAlign = 'right';
td_invnum.appendChild(
- document.createTextNode('<% mt('Apply to Invoice ') %>')
+ document.createTextNode(<% mt('Apply to Invoice ') |js_string %>)
);
var select_invnum = document.createElement('SELECT');
select_invnum.setAttribute('rownum', rownum);
select_invnum.setAttribute('appnum', appnum);
select_invnum.setAttribute('id', 'invnum'+rownum+'.'+appnum);
select_invnum.setAttribute('name', 'invnum'+rownum+'.'+appnum);
- select_invnum.style.textAlign = 'right';
- select_invnum.style.width = '50px';
+ select_invnum.className = 'select_invnum';
select_invnum.onchange = choose_app_invnum;
select_invnum.onfocus = focus_app_invnum;
%# for error handling--ugly, but the alternative is translating the whole
%# process of creating rows into Mason
-var row_array = <% encode_json(\@rows) %>;
+var row_obj = <% encode_json(\%rows) %>;
function preload() {
var rownum;
var appnum;
- for (rownum=0; rownum < row_array.length; rownum++) {
- if ( row_array[rownum].length ) {
+ for (rownum in row_obj) {
+ if ( row_obj[rownum].length ) {
var enable = document.getElementById('enable_app'+rownum);
enable.checked = true;
var preload_row = function(r) {//continuation from toggle_application_row
- for (appnum=0; appnum < row_array[r].length; appnum++) {
- this_app = row_array[r][appnum];
- var x = r + '.' + appnum;
- //set invnum
- var select_invnum = document.getElementById('invnum'+x);
- focus_app_invnum.call(select_invnum);
- for (i=0; i<select_invnum.options.length; i++) {
- if (select_invnum.options[i].value == this_app.invnum) {
- select_invnum.selectedIndex = i;
+
+ preloading++;
+
+ try {
+ for (appnum=0; appnum < row_obj[r].length; appnum++) {
+ this_app = row_obj[r][appnum];
+ var x = r + '.' + appnum;
+ //set invnum
+ var select_invnum = document.getElementById('invnum'+x);
+ focus_app_invnum.call(select_invnum);
+ for (i=0; i<select_invnum.options.length; i++) {
+ if (select_invnum.options[i].value == this_app.invnum) {
+ select_invnum.selectedIndex = i;
+ }
}
- }
- choose_app_invnum.call(select_invnum);
- //set amount
- var input_amount = document.getElementById('amount'+x);
- input_amount.value = this_app.amount;
-
- //set error
- var span_error = document.getElementById('error'+x);
- span_error.innerHTML = this_app.error;
- change_app_amount.call(input_amount); //creates next row
- } //for appnum
+ choose_app_invnum.call(select_invnum);
+ //set amount
+ var input_amount = document.getElementById('amount'+x);
+ input_amount.value = this_app.amount;
+
+ //set error
+ var span_error = document.getElementById('error'+x);
+ span_error.innerHTML = this_app.error;
+
+ // create another row (unconditionally)
+ create_application_row(r, appnum + 1);
+
+ } //for appnum
+
+ } finally {
+ preloading--;
+ }
+
}; //preload_row function
+
+ // enable application rows on the selected customer. this creates
+ // the first row, then kicks off preloading.
toggle_application_row.call(enable, null, preload_row);
- } // if row_array[rownum].length
+
+ } // if (row_obj[rownum].length
} //for rownum
}
footer_align => \@footer_align,
onchange => \@onchange,
custnum_update_callback => 'custnum_update_callback',
+ invnum_update_callback => 'invnum_update_callback',
add_row_callback => 'add_row_callback',
+ delete_row_callback => 'delete_row_callback',
&>
<BR>
-<INPUT TYPE="button" VALUE="Post payment batch" name="btnsubmit" onclick="window.onbeforeunload = null; document.OneTrueForm.submit(); this.disabled = true;">
+<INPUT TYPE="button" VALUE="Post payment batch" name="btnsubmit" id="btnsubmit" onclick="window.onbeforeunload = null; document.OneTrueForm.submit(); this.disabled = true;">
</FORM>
-%if ( $cgi->param('error') ) {
-<SCRIPT TYPE="text/javascript">
-% for ( my $row = 0; defined($cgi->param("custnum$row")); $row++ ) {
- select_discount_term(<% $row %>, '');
-% }
-</SCRIPT>
-%}
+% #XXX I think this can go away completely, but need to test with $use_discount
+% ###not perl <SCRIPT TYPE="text/javascript">
+% #foreach my $row ( keys %rows ) {
+% ###not perl select_discount_term(<% $row %>, '');
+% #}
+% ###not perl </SCRIPT>
<% include('/elements/footer.html') %>
my @onchange = ( '', '' );;
my $use_discounts = '';
+# Not entirely sure this works anymore...
if ( FS::Record->scalar_sql('SELECT COUNT(*) FROM part_pkg_discount') ) {
#push @header, 'Discount';
push @header, '';
push @footer_align, '';
push @onchange, 'toggle_application_row';
+push @header, 'No Auto Allocate';
+push @fields, 'no_auto_apply';
+push @types, 'checkbox';
+push @align, 'c';
+push @sizes, '0';
+push @colors, '';
+push @footer, '';
+push @footer_align, '';
+push @onchange, '';
+
#push @header, 'Error';
push @header, '';
push @fields, 'error';
$m->comp('/elements/handle_uri_query');
# set up for preloading
-my @rows;
-my @row_errors;
+my %rows;
+my %row_errors;
if ( $cgi->param('error') ) {
my $param = $cgi->Vars;
my $enum = 0; #errors numbered separately
- for( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
- $rows[$row] = [];
- $row_errors[$row] = $param->{"error$enum"};
+ my @invrows = grep /^invnum\d+\.\d+$/, keys %$param; #pare down possibilities
+ foreach my $row ( sort { $a <=> $b } map /^custnum(\d+)$/, keys %$param ) {
+# for( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
+ $rows{$row} = [];
+ $row_errors{$row} = $param->{"error$enum"};
$enum++;
- for( my $app = 0; exists($param->{"invnum$row.$app"}); $app++ ) {
+ foreach my $app ( map /^invnum$row\.(\d+)$/, @invrows ) {
next if !$param->{"invnum$row.$app"};
my %this_app = map { $_ => ($param->{$_.$row.'.'.$app} || '') }
qw( invnum amount );
$this_app{'error'} = $param->{"error$enum"} || '';
$param->{"error$enum"} = ''; # don't pass this error through
- $rows[$row][$app] = \%this_app;
+ $rows{$row}[$app] = \%this_app;
$enum++;
}
}
- for( my $row = 0; $row < @row_errors; $row++ ) {
- $param->{"error$row"} = $row_errors[$row];
+ foreach my $row (keys %rows) {
+ $param->{"error$row"} = $row_errors{$row};
}
}
-#warn Dumper {rows => \@rows, row_errors => \@row_errors };
+#warn Dumper {rows => \%rows, row_errors => \%row_errors };
</%init>