+function add_row_callback(rownum, values) {
+ if (values) {
+ custnum_update_callback(rownum);
+ } else {
+ document.getElementById('enable_app'+rownum).disabled = true;
+ }
+}
+
+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;
+ // 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);
+% }
+}
+
+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') {
+ value = select_obj.value;
+ }
+
+ var term_select = document.createElement('SELECT');
+ term_select.setAttribute('name', 'discount_term'+row);
+ term_select.setAttribute('id', 'discount_term'+row);
+ term_select.setAttribute('rownum', row);
+ term_select.style.display = '';
+ select_obj.parentNode.replaceChild(term_select, select_obj);
+ opt(term_select, '', '1 month');
+
+ function select_discount_term_update(discount_terms) {
+
+ var termArray = eval('(' + discount_terms + ')');
+ for ( var t = 0; t < termArray.length; t++ ) {
+ opt(term_select, termArray[t][0], termArray[t][1]);
+ if (termArray[t][0] == value) {
+ term_select.selectedIndex = t+1;
+ }
+ }
+
+ }
+
+ discount_terms(custnum_obj.value, select_discount_term_update);
+
+}
+
+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
+ for ( var i=0; i<invoices.length; i++ ) {
+ invoices_for_row[rownum][ invoices[i].invnum ] = invoices[i];
+ }
+}
+
+function toggle_application_row(ev, next) {
+ 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);
+ custnum_search_open( custnum,
+ function(returned) {
+ update_invoices(rownum, JSON.parse(returned));
+ create_application_row(rownum, 0);
+ 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);
+ }
+}
+
+function lock_payment_row(rownum, flag) {
+% foreach (qw(invnum custnum customer)) {
+ obj = document.getElementById('<% $_ %>'+rownum);
+ obj.readOnly = flag;
+% }
+ document.getElementById('enable_app'+rownum).disabled = flag;
+}
+
+function delete_application_row() {
+ var rownum = this.getAttribute('rownum');
+ var appnum = this.getAttribute('appnum');
+ var tr_app = document.getElementById('row'+rownum+'.'+appnum);
+ var select_invnum = document.getElementById('invnum'+rownum+'.'+appnum);
+ if ( select_invnum.value ) {
+ invoices_for_row[rownum][ select_invnum.value ] = select_invnum.curr_invoice;
+ }
+
+ tr_app.parentNode.removeChild(tr_app);
+ if ( appnum > 0 ) {
+ document.getElementById('delete'+rownum+'.'+(appnum-1)).style.display = '';
+ }
+ else {
+ lock_payment_row(rownum, false);
+ document.getElementById('enable_app'+rownum).checked = false;
+ }
+}
+
+function amount_unapplied(rownum) {
+ var appnum = 0;
+ var total = 0;
+ var payment_amount = parseFloat(document.getElementById('paid'+rownum).value)
+ || 0;
+ while (true) {
+ var input_amount = document.getElementById('amount'+rownum+'.'+appnum);
+ if ( input_amount ) {
+ total += parseFloat(input_amount.value || 0);
+ appnum++;
+ }
+ else {
+ return payment_amount - total;
+ }
+ }
+}
+
+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');
+ var last_invoice = this.curr_invoice;
+ if ( last_invoice ) {
+ invoices_for_row[rownum][ last_invoice['invnum'] ] = last_invoice;
+ }
+
+ if ( this.value ) {
+ var this_invoice = invoices_for_row[rownum][this.value];
+ this.curr_invoice = invoices_for_row[rownum][this.value];
+ var span_owed = document.getElementById('owed'+rownum+'.'+appnum);
+ span_owed.innerHTML = this_invoice['owed'] + ' ';
+ delete invoices_for_row[rownum][this.value];
+
+ var input_amount = document.getElementById('amount'+rownum+'.'+appnum);
+ if ( input_amount.value == '' ) {
+ input_amount.value =
+ Math.max(
+ 0, Math.min( amount_unapplied(rownum), this_invoice['owed'])
+ ).toFixed(2);
+ // trigger onchange
+ change_app_amount.call(input_amount);
+ }
+ }
+}
+
+// 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() {
+ var rownum = this.getAttribute('rownum');
+ var add_opt = function(obj, value, label) {
+ var o = document.createElement('OPTION');
+ 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, this_invoice.label);
+ } else {
+ add_opt(this, '', '');
+ }
+ for ( var x in invoices_for_row[rownum] ) {
+ 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');
+ 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');
+ tr_app.setAttribute('rownum', rownum);
+ tr_app.setAttribute('appnum', appnum);
+ tr_app.setAttribute('id', 'row'+rownum+'.'+appnum);
+
+ var td_invnum = document.createElement('TD');
+ td_invnum.setAttribute('colspan', 4);
+ td_invnum.style.textAlign = 'right';
+ td_invnum.appendChild(
+ 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.className = 'select_invnum';
+ select_invnum.onchange = choose_app_invnum;
+ select_invnum.onfocus = focus_app_invnum;
+
+ td_invnum.appendChild(select_invnum);
+ tr_app.appendChild(td_invnum);
+
+ var td_owed = document.createElement('TD');
+ td_owed.style.textAlign= 'right';
+ var span_owed = document.createElement('SPAN');
+ span_owed.setAttribute('rownum', rownum);
+ span_owed.setAttribute('appnum', appnum);
+ span_owed.setAttribute('id', 'owed'+rownum+'.'+appnum);
+ td_owed.appendChild(span_owed);
+ tr_app.appendChild(td_owed);