]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/xul/staff_client/server/patron/bill2.js
Improve Firefox/XULRunner Support
[Evergreen.git] / Open-ILS / xul / staff_client / server / patron / bill2.js
1 function my_init() {
2     try {
3         if (typeof JSAN == 'undefined') { throw( $("commonStrings").getString('common.jsan.missing') ); }
4         JSAN.errorLevel = "die"; // none, warn, or die
5         JSAN.addRepository('/xul/server/');
6
7         JSAN.use('util.error'); g.error = new util.error();
8         JSAN.use('util.network'); g.network = new util.network();
9         JSAN.use('util.date');
10         JSAN.use('util.money');
11         JSAN.use('util.widgets');
12         JSAN.use('patron.util');
13         JSAN.use('OpenILS.data'); g.data = new OpenILS.data(); g.data.init({'via':'stash'});
14         g.data.voided_billings = []; g.data.stash('voided_billings');
15
16         g.error.sdump('D_TRACE','my_init() for bill2.xul');
17
18         document.title = $("patronStrings").getString('staff.patron.bill_history.my_init.current_bills');
19
20         g.funcs = []; g.bill_map = {}; g.row_map = {}; g.check_map = {};
21
22         g.patron_id = xul_param('patron_id');
23
24         $('circulating_hint').hidden = true;
25
26         init_lists();
27
28         retrieve_mbts_for_list();
29
30         event_listeners();
31
32         JSAN.use('util.exec'); var exec = new util.exec(20); 
33         exec.on_error = function(E) { alert(E); return true; }
34         exec.timer(g.funcs,100);
35
36         $('credit_forward').setAttribute('value','???');
37         if (!g.patron) {
38             refresh_patron();
39         } else {
40             $('credit_forward').setAttribute('value',util.money.sanitize( g.patron.credit_forward_balance() ));
41         }
42
43         if (g.data.hash.aous['ui.circ.billing.uncheck_bills_and_unfocus_payment_box']) {
44             g.funcs.push(
45                 function() {
46                     $('uncheck_all').focus();
47                     tally_all();
48                 }
49             );
50         } else {
51             g.funcs.push(
52                 function() {
53                     default_focus();
54                     tally_all();
55                 }
56             );
57         }
58
59     } catch(E) {
60         var err_msg = $("commonStrings").getFormattedString('common.exception', ['patron/bill2.xul', E]);
61         try { g.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); }
62         alert(err_msg);
63     }
64 }
65
66 function event_listeners() {
67     try {
68         $('details').addEventListener(
69             'command',
70             handle_details,
71             false
72         );
73
74         $('add').addEventListener(
75             'command',
76             handle_add,
77             false
78         );
79
80         $('voidall').addEventListener(
81             'command',
82             handle_void_all,
83             false
84         );
85
86         $('refund').addEventListener(
87             'command',
88             handle_refund,
89             false
90         );
91
92         $('opac').addEventListener(
93             'command',
94             handle_opac,
95             false
96         );
97
98         $('copy_details').addEventListener(
99             'command',
100             handle_copy_details,
101             false
102         );
103
104         $('payment').addEventListener(
105             'change',
106             function(ev) {
107                 if ($('payment_type').value == 'credit_payment') {
108                     JSAN.use('util.money');
109                     JSAN.use('patron.util'); g.patron = patron.util.retrieve_fleshed_au_via_id(ses(),g.patron_id,null);
110                     var proposed = util.money.dollars_float_to_cents_integer(ev.target.value);
111                     var available = util.money.dollars_float_to_cents_integer(g.patron.credit_forward_balance());
112                     if (proposed > available) {
113                         alert($("patronStrings").getFormattedString('staff.patron.bills.bill_payment_amount.credit_amount', [g.patron.credit_forward_balance()]));
114                         ev.target.value = util.money.cents_as_dollars( available );
115                         ev.target.setAttribute('value',ev.target.value);
116                     }
117                 }
118                 distribute_payment(); 
119             },
120             false
121         );
122
123         $('payment').addEventListener(
124             'focus',
125             function(ev) { ev.target.select(); },
126             false
127         );
128
129         $('payment').addEventListener(
130             'keypress',
131             function(ev) {
132                 if (! (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) ) { return; }
133                 distribute_payment();
134                 $('apply_payment_btn').focus();
135             },
136             false
137         );
138
139         $('bill_patron_btn').addEventListener(
140             'command',
141             function() {
142                 JSAN.use('util.window'); var win = new util.window();
143                 var my_xulG = win.open(
144                     urls.XUL_PATRON_BILL_WIZARD,
145                     'billwizard',
146                     'chrome,resizable,modal',
147                     { 'patron_id' : g.patron_id }
148                 );
149                 if (my_xulG.xact_id) { g.funcs.push( gen_list_append_func( my_xulG.xact_id ) ); /* FIXME: do something to update summary sidebar */ }
150             },
151             false
152         );
153
154         $('bill_history_btn').addEventListener(
155             'command',
156             function() {
157                 xulG.display_window.g.patron.right_deck.reset_iframe( 
158                     urls.XUL_PATRON_BILL_HISTORY,
159                     {},
160                     {
161                         'patron_id' : g.patron_id,
162                         'refresh' : function() { refresh(); },
163                         'new_tab' : xulG.new_tab,
164                         'url_prefix' : xulG.url_prefix
165                     }
166                 );
167             },
168             false
169         );
170
171         $('convert_change_to_credit').addEventListener(
172             'command',
173             function(ev) {
174                 if (ev.target.checked) {
175                     addCSSClass( $('change_due'), 'change_to_credit' );
176                 } else {
177                     removeCSSClass( $('change_due'), 'change_to_credit' );
178                 }
179             },
180             false
181         );
182
183         $('apply_payment_btn').addEventListener(
184             'command',
185             function(ev) {
186                 try {
187                     $('apply_payment_btn').disabled = true;
188                     apply_payment();
189                     tally_all();
190                     $('apply_payment_btn').disabled = false;
191                 } catch(E) {
192                     alert('Error in bill2.js, apply_payment_btn: ' + E);
193                 }
194             },
195             false
196         );
197
198     } catch(E) {
199         alert('Error in bill2.js, event_listeners(): ' + E);
200     }
201 }
202
203 function $(id) { return document.getElementById(id); }
204
205 function default_focus() {
206     try { $('payment').focus(); } catch(E) { alert('Error in default_focus(): ' + E); }
207 }
208
209 function tally_pending() {
210     try {
211         var payments = [];
212         JSAN.use('util.money');
213         var tb = $('payment');
214         var payment_tendered = util.money.dollars_float_to_cents_integer( tb.value );
215         var payment_pending = 0;
216         var retrieve_ids = g.bill_list.dump_retrieve_ids();
217         for (var i = 0; i < retrieve_ids.length; i++) {
218             var row_params = g.row_map[retrieve_ids[i]];
219             if (g.check_map[retrieve_ids[i]]) { 
220                 var value = util.money.dollars_float_to_cents_integer( row_params.row.my.payment_pending );
221                 payment_pending += value;
222                 if (value != '0.00') { payments.push( [ retrieve_ids[i], util.money.cents_as_dollars(value) ] ); }
223             }
224         }
225         var change_pending = payment_tendered - payment_pending;
226         $('pending_payment').value = util.money.cents_as_dollars( payment_pending );
227         $('pending_change').value = util.money.cents_as_dollars( change_pending );
228         $('change_due').value = util.money.cents_as_dollars( change_pending );
229         return { 'payments' : payments, 'change' : util.money.cents_as_dollars( change_pending ) };
230     } catch(E) {
231         alert('Error in bill2.js, tally_pending(): ' + E);
232     }
233 }
234
235 function tally_selected() {
236     try {
237         JSAN.use('util.money');
238         var selected_billed = 0;
239         var selected_paid = 0;
240         var selected_balance = 0;
241
242         for (var i = 0; i < g.bill_list_selection.length; i++) {
243             var bill = g.bill_map[g.bill_list_selection[i]];
244             if (!bill) {
245                 //$('checked_owed').setAttribute('value', '???');
246                 //$('checked_billed').setAttribute('value', '???');
247                 //$('checked_paid').setAttribute('value', '???');
248                 return;
249             }
250             var to = util.money.dollars_float_to_cents_integer( bill.transaction.total_owed() );
251             var tp = util.money.dollars_float_to_cents_integer( bill.transaction.total_paid() );
252             var bo = util.money.dollars_float_to_cents_integer( bill.transaction.balance_owed() );
253             selected_billed += to;
254             selected_paid += tp;
255             selected_balance += bo;
256         }
257         //$('checked_billed').setAttribute('value', util.money.cents_as_dollars( selected_billed ) );
258         //$('checked_paid').setAttribute('value', util.money.cents_as_dollars( selected_paid ) );
259         //$('checked_owed').setAttribute('value', util.money.cents_as_dollars( selected_balance ) );
260     } catch(E) {
261         alert('Error in bill2.js, tally_selected(): ' + E);
262     }
263 }
264
265 function tally_voided() {
266     try {
267         JSAN.use('util.money');
268         var voided_total = 0;
269
270         g.data.stash_retrieve();
271
272         for (var i = 0; i < g.data.voided_billings.length; i++) {
273             var billing = g.data.voided_billings[i];
274             var bv = util.money.dollars_float_to_cents_integer( billing.amount() );
275             voided_total += bv;
276         }
277         $('currently_voided').setAttribute('value', util.money.cents_as_dollars( voided_total ) );
278     } catch(E) {
279         alert('Error in bill2.js, tally_voided(): ' + E);
280     }
281 }
282
283 function tally_all() {
284     try {
285         JSAN.use('util.money');
286         var checked_billed = 0;
287         var checked_paid = 0;
288         var checked_balance = 0;
289         var total_billed = 0;
290         var total_paid = 0;
291         var total_balance = 0;
292         var refunds_owed = 0;
293
294         var retrieve_ids = g.bill_list.dump_retrieve_ids();
295         for (var i = 0; i < retrieve_ids.length; i++) {
296             var bill = g.bill_map[retrieve_ids[i]];
297             if (!bill) {
298                 $('checked_owed').value = '???';
299                 $('checked_owed2').setAttribute('value', '???');
300                 $('checked_billed').value = '???';
301                 $('checked_paid').value = '???';
302                 $('tb_total_owed').value = '???';
303                 $('total_owed2').setAttribute('value', '???');
304                 $('total_billed').value = '???';
305                 $('tb_total_paid').value = '???';
306                 $('refunds_owed').setAttribute('value', '???');
307                 return;
308             }
309             var to = util.money.dollars_float_to_cents_integer( bill.transaction.total_owed() );
310             var tp = util.money.dollars_float_to_cents_integer( bill.transaction.total_paid() );
311             var bo = util.money.dollars_float_to_cents_integer( bill.transaction.balance_owed() );
312             total_billed += to;
313             total_paid += tp;
314             total_balance += bo;
315             if ( bo < 0 ) refunds_owed += bo;
316             if (g.check_map[retrieve_ids[i]]) {
317                 checked_billed += to;
318                 checked_paid += tp;
319                 checked_balance += bo;
320             }
321         }
322         $('checked_billed').value = util.money.cents_as_dollars( checked_billed );
323         $('checked_paid').value = util.money.cents_as_dollars( checked_paid );
324         $('checked_owed').value = util.money.cents_as_dollars( checked_balance );
325         $('checked_owed2').setAttribute('value', util.money.cents_as_dollars( checked_balance ) );
326         $('total_billed').value = util.money.cents_as_dollars( total_billed );
327         $('tb_total_paid').value = util.money.cents_as_dollars( total_paid );
328         $('tb_total_owed').value = util.money.cents_as_dollars( total_balance );
329         $('total_owed2').setAttribute('value', util.money.cents_as_dollars( total_balance ) );
330         $('refunds_owed').setAttribute('value', util.money.cents_as_dollars( Math.abs( refunds_owed ) ) );
331         // tally_selected();
332     } catch(E) {
333         alert('Error in bill2.js, tally_all(): ' + E);
334     }
335 }
336
337 function handle_refund() {
338     if(g.bill_list_selection.length > 1) {
339         var msg = $("patronStrings").getFormattedString('staff.patron.bills.handle_refund.message_plural', [g.bill_list_selection]);
340     } else {
341         var msg = $("patronStrings").getFormattedString('staff.patron.bills.handle_refund.message_singular', [g.bill_list_selection]);
342     }
343         
344     var r = g.error.yns_alert(msg,
345         $("patronStrings").getString('staff.patron.bills.handle_refund.title'),
346         $("patronStrings").getString('staff.patron.bills.handle_refund.btn_yes'),
347         $("patronStrings").getString('staff.patron.bills.handle_refund.btn_no'),null,
348         $("patronStrings").getString('staff.patron.bills.handle_refund.confirm_message'));
349     if (r == 0) {
350         for (var i = 0; i < g.bill_list_selection.length; i++) {
351             var bill_id = g.bill_list_selection[i];
352             //alert('g.check_map['+bill_id+'] = '+g.check_map[bill_id]+' bill_map['+bill_id+'] = ' + js2JSON(g.bill_map[bill_id]));
353             g.check_map[bill_id] = true;
354             var row_params = g.row_map[bill_id];
355             row_params.row.my.checked = true;
356             g.bill_list.refresh_row(row_params);
357         }
358     }
359     tally_all();
360     distribute_payment();
361 }
362
363
364 function check_all() {
365     try {
366         for (var i in g.bill_map) {
367             g.check_map[i] = true;
368             var row_params = g.row_map[i];
369             row_params.row.my.checked = true;
370             g.bill_list.refresh_row(row_params);
371         }
372         tally_all();
373         distribute_payment();
374     } catch(E) {
375         alert('Error in bill2.js, check_all(): ' + E);
376     }
377
378 }
379
380 function uncheck_all() {
381     try {
382         for (var i in g.bill_map) {
383             g.check_map[i] = false;
384             var row_params = g.row_map[i];
385             row_params.row.my.checked = false;
386             g.bill_list.refresh_row(row_params);
387         }
388         tally_all();
389         distribute_payment();
390     } catch(E) {
391         alert('Error in bill2.js, check_all(): ' + E);
392     }
393
394 }
395
396 function check_all_refunds() {
397     try {
398         for (var i in g.bill_map) {
399             if ( Number( g.bill_map[i].transaction.balance_owed() ) < 0 ) {
400                 g.check_map[i] = true;
401                 var row_params = g.row_map[i];
402                 row_params.row.my.checked = true;
403                 g.bill_list.refresh_row(row_params);
404             }
405         }
406         tally_all();
407         distribute_payment();
408     } catch(E) {
409         alert('Error in bill2.js, check_all_refunds(): ' + E);
410     }
411 }
412
413 function gen_list_append_func(r) {
414     return function() {
415         var default_check_state = g.data.hash.aous[
416             'ui.circ.billing.uncheck_bills_and_unfocus_payment_box'
417         ] ? false : true;
418         if (typeof r == 'object') {
419             g.row_map[ r.id() ] = g.bill_list.append( {
420                 'retrieve_id' : r.id(),
421                 'flesh_immediately' : true,
422                 'row' : {
423                     'my' : {
424                         'checked' : default_check_state,
425                         'mbts' : r
426                     }
427                 }
428             } );
429         } else {
430             g.row_map[r] = g.bill_list.append( {
431                 'retrieve_id' : r,
432                 'flesh_immediately' : true,
433                 'row' : {
434                     'my' : {
435                         'checked' : default_check_state
436                     }
437                 }
438             } );
439         }
440     }
441 }
442
443 function retrieve_mbts_for_list() {
444     var method = 'FM_MBTS_IDS_RETRIEVE_ALL_HAVING_BALANCE.authoritative';
445     g.mbts_ids = g.network.simple_request(method,[ses(),g.patron_id]);
446     if (g.mbts_ids.ilsevent) {
447         switch(Number(g.mbts_ids.ilsevent)) {
448             case -1: g.error.standard_network_error_alert($("patronStrings").getString('staff.patron.bill_history.retrieve_mbts_for_list.close_win_try_again')); break;
449             default: g.error.standard_unexpected_error_alert($("patronStrings").getString('staff.patron.bill_history.retrieve_mbts_for_list.close_win_try_again'),g.mbts_ids); break;
450         }
451     } else if (g.mbts_ids == null) {
452         g.error.standard_unexpected_error_alert($("patronStrings").getString('staff.patron.bill_history.retrieve_mbts_for_list.close_win_try_again'),null);
453     } else {
454    
455         g.mbts_ids.reverse();
456  
457         for (var i = 0; i < g.mbts_ids.length; i++) {
458             dump('i = ' + i + ' g.mbts_ids[i] = ' + g.mbts_ids[i] + '\n');
459             g.funcs.push( gen_list_append_func(g.mbts_ids[i]) );
460         }
461     }
462 }
463
464 function init_lists() {
465     JSAN.use('util.list'); JSAN.use('circ.util'); 
466
467     g.bill_list_selection = [];
468
469     g.bill_list = new util.list('bill_tree');
470
471     g.bill_list.init( {
472         'columns' : 
473             [
474                 {
475                     'id' : 'select', 'primary' : true, 'type' : 'checkbox', 'editable' : true, 'label' : '', 'style' : 'min-width: 3em;',
476                     'render' : function(my) { return String( my.checked ) == 'true'; }, 
477                 }
478             ].concat(
479                 patron.util.mbts_columns({
480                     'mbts_xact_finish' : { 'hidden' : true }
481                 }
482             ).concat( 
483                 circ.util.columns({ 
484                     'title' : { 'hidden' : false, 'flex' : '3' }
485                 }
486             ).concat( 
487                 [
488                     {
489                         'id' : 'payment_pending', 'editable' : false, 'sort_type' : 'money', 
490                         'label' : $('patronStrings').getString('staff.patron.bill_interface.payment_pending.column_header'),
491                         'render' : function(my) { return my.payment_pending || '0.00'; }, 
492                     }
493                 ]
494             ))),
495         'on_select' : function(ev) {
496             JSAN.use('util.functional');
497             g.bill_list_selection = util.functional.map_list(
498                 g.bill_list.retrieve_selection(),
499                 function(o) { return o.getAttribute('retrieve_id'); }
500             );
501             //tally_selected();
502             $('details').setAttribute('disabled', g.bill_list_selection.length == 0);
503             $('add').setAttribute('disabled', g.bill_list_selection.length == 0);
504             $('voidall').setAttribute('disabled', g.bill_list_selection.length == 0);
505             $('refund').setAttribute('disabled', g.bill_list_selection.length == 0);
506             $('opac').setAttribute('disabled', g.bill_list_selection.length == 0);
507             $('copy_details').setAttribute('disabled', g.bill_list_selection.length == 0);
508         },
509         'on_click' : function(ev) {
510             var row = {}; var col = {}; var nobj = {};
511             g.bill_list.node.treeBoxObject.getCellAt(ev.clientX,ev.clientY,row,col,nobj);
512             if (row.value == -1) return;
513             var treeItem = g.bill_list.node.contentView.getItemAtIndex(row.value);
514             if (treeItem.nodeName != 'treeitem') return;
515             var treeRow = treeItem.firstChild;
516             var treeCell = treeRow.firstChild.nextSibling;
517             if (g.check_map[ treeItem.getAttribute('retrieve_id') ] != (treeCell.getAttribute('value') == 'true')) {
518                 g.check_map[ treeItem.getAttribute('retrieve_id') ] = treeCell.getAttribute('value') == 'true';
519                 g.row_map[ treeItem.getAttribute('retrieve_id') ].row.my.checked = treeCell.getAttribute('value') == 'true';
520                 tally_all();
521                 distribute_payment();
522             }
523         },
524         'on_sort' : function() {
525             tally_all();
526         },
527         'on_checkbox_toggle' : function(toggle) {
528             try {
529                 var retrieve_ids = g.bill_list.dump_retrieve_ids();
530                 for (var i = 0; i < retrieve_ids.length; i++) {
531                     g.check_map[ retrieve_ids[i] ] = (toggle=='on');
532                     g.row_map[ retrieve_ids[i] ].row.my.checked = (toggle=='on');
533                 }
534                 tally_all();
535             } catch(E) {
536                 alert('error in on_checkbox_toggle(): ' + E);
537             }
538         },
539         'retrieve_row' : function(params) {
540             try {
541                 var id = params.retrieve_id;
542                 var row = params.row;
543
544                 function handle_props(row) {
545                     try {
546                         if ( row && row.my && row.my.mbts && Number( row.my.mbts.balance_owed() ) < 0 ) {
547                             util.widgets.addProperty(params.treeitem_node.firstChild,'refundable');
548                             util.widgets.addProperty(params.treeitem_node.firstChild.childNodes[ g.payment_pending_column_idx ],'refundable');
549                         }
550                         if ( row && row.my && row.my.circ && ! row.my.circ.checkin_time() ) {
551                             $('circulating_hint').hidden = false;
552                             util.widgets.addProperty(params.treeitem_node.firstChild,'circulating');
553                             util.widgets.addProperty(params.treeitem_node.firstChild.childNodes[ g.title_column_idx ],'circulating');
554                         }
555                     } catch(E) {
556                         g.error.sdump('D_WARN','Error setting list properties in bill2.js: ' + E);
557                         alert('Error setting list properties in bill2.js: ' + E);
558                     }
559                 }
560
561                 if (id) {
562                     if (typeof row.my == 'undefined') row.my = {};
563                     if (typeof row.my.mbts == 'undefined' ) {
564                         g.network.simple_request('BLOB_MBTS_DETAILS_RETRIEVE',[ses(),id], function(req) {
565                             var blob = req.getResultObject();
566                             row.my.mbts = blob.transaction;
567                             row.my.circ = blob.circ;
568                             row.my.acp = blob.copy;
569                             row.my.mvr = blob.record;
570                             if (typeof params.on_retrieve == 'function') {
571                                 if ( row.my.mbts && Number( row.my.mbts.balance_owed() ) < 0 ) {
572                                     row.my.checked = false;
573                                 }
574                                 handle_props(row);
575                                 params.on_retrieve(row);
576                             };
577                             g.bill_map[ id ] = blob;
578                             g.check_map[ id ] = row.my.checked;
579                             tally_all();
580                         } );
581                     } else {
582                         if (typeof params.on_retrieve == 'function') { 
583                             handle_props(row);
584                             params.on_retrieve(row); 
585                         }
586                     }
587                 } else {
588                     if (typeof params.on_retrieve == 'function') { 
589                         params.on_retrieve(row); 
590                     }
591                 }
592
593                 return row;
594             } catch(E) {
595                 alert('Error in bill2.js, retrieve_row(): ' + E);
596             }
597         }
598     } );
599
600     g.title_column_idx = util.functional.map_list( g.bill_list.columns, function(o) { return o.id; } ).indexOf( 'title' );
601     g.payment_pending_column_idx = util.functional.map_list( g.bill_list.columns, function(o) { return o.id; } ).indexOf( 'payment_pending' );
602     $('bill_list_actions').appendChild( g.bill_list.render_list_actions() );
603     g.bill_list.set_list_actions();
604 }
605
606 function handle_add() {
607     if(g.bill_list_selection.length > 1) {
608         var msg = $("patronStrings").getFormattedString('staff.patron.bill_history.handle_add.message_plural', [g.bill_list_selection]);
609     } else {
610         var msg = $("patronStrings").getFormattedString('staff.patron.bill_history.handle_add.message_singular', [g.bill_list_selection]);
611     }
612         
613     var r = g.error.yns_alert(msg,
614         $("patronStrings").getString('staff.patron.bill_history.handle_add.title'),
615         $("patronStrings").getString('staff.patron.bill_history.handle_add.btn_yes'),
616         $("patronStrings").getString('staff.patron.bill_history.handle_add.btn_no'),null,
617         $("patronStrings").getString('staff.patron.bill_history.handle_add.confirm_message'));
618     if (r == 0) {
619         JSAN.use('util.window');
620         var win = new util.window();
621         for (var i = 0; i < g.bill_list_selection.length; i++) {
622             var w = win.open(
623                 urls.XUL_PATRON_BILL_WIZARD,
624                 'billwizard',
625                 'chrome,resizable,modal',
626                 { 'patron_id' : g.patron_id, 'xact_id' : g.bill_list_selection[i] }
627             );
628         }
629         refresh();
630         if (typeof window.xulG == 'object' && typeof window.xulG.refresh == 'function') window.xulG.refresh();
631     }
632 }
633
634 function handle_void_all() {
635     if(g.bill_list_selection.length > 1) {
636         var msg = $("patronStrings").getFormattedString('staff.patron.bill_history.handle_void.message_plural', [g.bill_list_selection]);
637     } else {
638         var msg = $("patronStrings").getFormattedString('staff.patron.bill_history.handle_void.message_singular', [g.bill_list_selection]);
639     }
640         
641     var r = g.error.yns_alert(msg,
642         $("patronStrings").getString('staff.patron.bill_history.handle_void.title'),
643         $("patronStrings").getString('staff.patron.bill_history.handle_void.btn_yes'),
644         $("patronStrings").getString('staff.patron.bill_history.handle_void.btn_no'),null,
645         $("patronStrings").getString('staff.patron.bill_history.handle_void.confirm_message'));
646     if (r == 0) {
647         for (var i = 0; i < g.bill_list_selection.length; i++) {
648             void_all_billings( g.bill_list_selection[i] );
649         }
650         refresh();
651         if (typeof window.xulG == 'object' && typeof window.xulG.refresh == 'function') window.xulG.refresh();
652         if (typeof window.xulG == 'object' && typeof window.xulG.on_money_change == 'function') window.xulG.on_money_change();
653     }
654 }
655
656 function handle_opac() {
657     try {
658         var ids = [];
659         for (var i = 0; i < g.bill_list_selection.length; i++) {
660             var my_mvr = g.bill_map[ g.bill_list_selection[i] ].record;
661             var my_acp = g.bill_map[ g.bill_list_selection[i] ].copy;
662             if (typeof my_mvr != 'undefined' && my_mvr != null) {
663                 ids.push( { 'barcode' : my_acp.barcode(), 'doc_id' : my_mvr.doc_id() } );
664             }
665         }
666         JSAN.use('cat.util');
667         cat.util.show_in_opac( ids );
668     } catch(E) {
669         alert('Error in bill2.js, handle_opac: ' + E);
670     }
671 }
672
673 function handle_copy_details() {
674     try {
675         var ids = [];
676         for (var i = 0; i < g.bill_list_selection.length; i++) {
677             var my_acp = g.bill_map[ g.bill_list_selection[i] ].copy;
678             if (typeof my_acp != 'undefined' && my_acp != null) {
679                 ids.push( my_acp.barcode() );
680             }
681         }
682         JSAN.use('circ.util');
683         circ.util.item_details_new( ids );
684     } catch(E) {
685         alert('Error in bill2.js, handle_opac: ' + E);
686     }
687 }
688
689 function handle_details() {
690     JSAN.use('util.window'); var win = new util.window();
691     for (var i = 0; i < g.bill_list_selection.length; i++) {
692         var my_xulG = win.open(
693             urls.XUL_PATRON_BILL_DETAILS,
694             'test_billdetails_' + g.bill_list_selection[i],
695             'chrome,resizable',
696             {
697                 'patron_id' : g.patron_id,
698                 'mbts_id' : g.bill_list_selection[i],
699                 'refresh' : function() {
700                     refresh(); 
701                     if (typeof window.xulG == 'object' && typeof window.xulG.refresh == 'function') window.xulG.refresh();
702                 }, 
703                 'new_tab' : xulG.new_tab,
704                 'url_prefix' : xulG.url_prefix
705             }
706         );
707     }
708 }
709
710 function print_bills() {
711     try {
712         var template = 'bills_current';
713         JSAN.use('patron.util');
714         g.patron = patron.util.retrieve_fleshed_au_via_id(ses(),g.patron_id,null); 
715         g.bill_list.print({ 
716               'patron' : g.patron
717             , 'printer_context' : 'receipt'
718             , 'template' : template
719             , 'data' : {
720                   grand_total_owed:   $('tb_total_owed').value
721                 , grand_total_billed: $('total_billed').value
722                 , grand_total_paid:   $('tb_total_paid').value
723             }
724          });
725     } catch(E) {
726         g.error.standard_unexpected_error_alert($("patronStrings").getString('staff.patron.bill_history.print_bills.print_error'), E);
727     }
728 }
729
730 function distribute_payment() {
731     try {
732         JSAN.use('util.money');
733         var tb = $('payment');
734         tb.value = util.money.cents_as_dollars( util.money.dollars_float_to_cents_integer( tb.value ) );
735         tb.setAttribute('value', tb.value );
736         var total = util.money.dollars_float_to_cents_integer( tb.value );
737         if (total < 0) { tb.value = '0.00'; tb.setAttribute('value','0.00'); total = 0; }
738         var retrieve_ids = g.bill_list.dump_retrieve_ids();
739         for (var i = 0; i < retrieve_ids.length; i++) {
740             var row_params = g.row_map[retrieve_ids[i]];
741             if (g.check_map[retrieve_ids[i]]) { 
742                 var bill = g.bill_map[retrieve_ids[i]].transaction;
743                 var bo = util.money.dollars_float_to_cents_integer( bill.balance_owed() );
744                 if ( bo > total ) {
745                     row_params.row.my.payment_pending = util.money.cents_as_dollars( total );
746                     total = 0;
747                 } else {
748                     row_params.row.my.payment_pending = util.money.cents_as_dollars( bo );
749                     total = total - bo;
750                 }
751             } else {
752                 row_params.row.my.payment_pending = '0.00';
753             }
754             g.bill_list.refresh_row(row_params);
755         }
756         tally_pending();
757     } catch(E) {
758         alert('Error in bill2.js, distribute_payment(): ' + E);
759     }
760 }
761
762 function apply_payment() {
763     try {
764         var payment_blob = {};
765         JSAN.use('util.window');
766         var win = new util.window();
767         switch($('payment_type').value) {
768             case 'credit_card_payment' :
769                 g.data.temp = '';
770                 g.data.stash('temp');
771                 var my_xulG = win.open(
772                     urls.XUL_PATRON_BILL_CC_INFO,
773                     'billccinfo',
774                     'chrome,resizable,modal',
775                     {'patron_id': g.patron_id}
776                 );
777                 g.data.stash_retrieve();
778                 payment_blob = JSON2js( g.data.temp ); // FIXME - replace with my_xulG and update_modal_xulG, though it looks like we were using that before and moved away from it
779             break;
780             case 'check_payment' :
781                 g.data.temp = '';
782                 g.data.stash('temp');
783                 var my_xulG = win.open(
784                     urls.XUL_PATRON_BILL_CHECK_INFO,
785                     'billcheckinfo',
786                     'chrome,resizable,modal'
787                 );
788                 g.data.stash_retrieve();
789                 payment_blob = JSON2js( g.data.temp );
790             break;
791         }
792         if (
793             (typeof payment_blob == 'undefined') || 
794             payment_blob=='' || 
795             payment_blob.cancelled=='true'
796         ) { 
797             alert( $('commonStrings').getString('common.cancelled') ); 
798             return; 
799         }
800         payment_blob.userid = g.patron_id;
801         payment_blob.note = payment_blob.note || '';
802         //payment_blob.cash_drawer = 1; // FIXME: get new Config() to work
803         payment_blob.payment_type = $('payment_type').value;
804         var tally_blob = tally_pending();
805         payment_blob.payments = tally_blob.payments;
806         // Handle patron credit
807         if ( payment_blob.payment_type == 'credit_payment' ) { // paying with patron credit
808             if ( $('convert_change_to_credit').checked ) {
809                 // No need to convert credit into credit, handled automatically
810                 payment_blob.patron_credit = '0.00';
811             } else {
812                 // Cashing out extra credit as change
813                 payment_blob.patron_credit = 0 - tally_blob.change;
814             }
815         } else if ( $('convert_change_to_credit').checked ) {
816             // Saving change from a non-credit payment as patron credit on server
817             payment_blob.patron_credit = tally_blob.change;
818         } else {
819             payment_blob.patron_credit = '0.00';
820         }
821         if ( payment_blob.payments.length == 0 && payment_blob.patron_credit == '0.00' ) {
822             alert($("patronStrings").getString('staff.patron.bills.apply_payment.nothing_applied'));
823             return;
824         }
825         if ( pay( payment_blob ) ) {
826
827             $('payment').value = ''; $('payment').select(); $('payment').focus();
828             refresh({'clear_voided_summary':true});
829             if (typeof window.xulG == 'object' && typeof window.xulG.refresh == 'function') window.xulG.refresh();
830             if (typeof window.xulG == 'object' && typeof window.xulG.on_money_change == 'function') window.xulG.on_money_change();
831             if ( $('payment_type').value == 'credit_payment' || $('convert_change_to_credit').checked ) {
832                 refresh_patron();
833             }
834             try {
835                 if ( ! $('receipt_upon_payment').hasAttribute('checked') ) { return; } // Skip print attempt
836                 if ( ! $('receipt_upon_payment').getAttribute('checked') ) { return; } // Skip print attempt
837                 var no_print_prompting = g.data.hash.aous['circ.staff_client.do_not_auto_attempt_print'];
838                 if (no_print_prompting) {
839                     if (no_print_prompting.indexOf( "Bill Pay" ) > -1) { return; } // Skip print attempt
840                 }
841                 g.data.stash_retrieve();
842                 var template = 'bill_payment';
843                 JSAN.use('patron.util'); JSAN.use('util.functional');
844                 var params = { 
845                     'patron' : g.patron,
846                     'lib' : g.data.hash.aou[ ses('ws_ou') ],
847                     'staff' : ses('staff'),
848                     'header' : g.data.print_list_templates[template].header,
849                     'line_item' : g.data.print_list_templates[template].line_item,
850                     'footer' : g.data.print_list_templates[template].footer,
851                     'type' : g.data.print_list_templates[template].type,
852                     'list' : util.functional.map_list(
853                         payment_blob.payments,
854                         function(o) {
855                             return {
856                                 'bill_id' : o[0],
857                                 'payment' : o[1],
858                                 'last_billing_type' : g.bill_map[ o[0] ].transaction.last_billing_type(),
859                                 'last_billing_note' : g.bill_map[ o[0] ].transaction.last_billing_note(),
860                                 'title' : typeof g.bill_map[ o[0] ].record != 'undefined' ? g.bill_map[ o[0] ].record.title() : '', 
861                                 'barcode' : typeof g.bill_map[ o[0] ].copy != 'undefined' ? g.bill_map[ o[0] ].copy.barcode() : ''
862                             };
863                         }
864                     ),
865                     'data' : g.previous_summary,
866                     'context' : g.data.print_list_templates[template].context,
867                 };
868                 g.error.sdump('D_DEBUG',js2JSON(params));
869                 if ($('printer_prompt').hasAttribute('checked')) {
870                     if ($('printer_prompt').getAttribute('checked')) {
871                             params.no_prompt = false;
872                     } else {
873                             params.no_prompt = true;
874                     }
875                 } else {
876                     params.no_prompt = true;
877                 }
878                 JSAN.use('util.print'); var print = new util.print('receipt');
879                 for (var i = 0; i < $('num_of_receipts').value; i++) {
880                     print.tree_list( params );
881                 }
882             } catch(E) {
883                 g.error.standard_unexpected_error_alert('bill receipt', E);
884             }
885         }
886     } catch(E) {
887         alert('Error in bill2.js, apply_payment(): ' + E);
888     }
889 }
890
891 function pay(payment_blob) {
892     try {
893         var x = $('annotate_payment');
894         if (x && x.checked && (! payment_blob.note)) {
895             payment_blob.note = window.prompt(
896                 $("patronStrings").getString('staff.patron.bills.pay.annotate_payment'),
897                 '', 
898                 $("patronStrings").getString('staff.patron.bills.pay.annotate_payment.title')
899             );
900         }
901         g.previous_summary = {
902             original_balance : $('tb_total_owed').value,
903             voided_balance : $('currently_voided').value,
904             payment_received : $('payment').value,
905             payment_applied : $('pending_payment').value,
906             change_given : $('convert_change_to_credit').checked ? 0 : $('pending_change').value,
907             credit_given : $('convert_change_to_credit').checked ? $('pending_change').value : 0,
908             new_balance : util.money.cents_as_dollars( 
909                 util.money.dollars_float_to_cents_integer( $('tb_total_owed').value ) - 
910                 util.money.dollars_float_to_cents_integer( $('pending_payment').value )
911             ),
912             payment_type : $('payment_type').getAttribute('label'),
913             note : payment_blob.note
914         }
915         var robj = g.network.simple_request( 'BILL_PAY', [ ses(), payment_blob, g.patron.last_xact_id() ]);
916
917         try {
918             g.error.work_log(
919                 $('circStrings').getFormattedString(
920                     robj && robj.payments
921                         ? 'staff.circ.work_log_payment_attempt.success.message'
922                         : 'staff.circ.work_log_payment_attempt.failure.message',
923                     [
924                         ses('staff_usrname'), // 1 - Staff Username
925                         g.patron.family_name(), // 2 - Patron Family
926                         g.patron.card().barcode(), // 3 - Patron Barcode
927                         g.previous_summary.original_balance, // 4 - Original Balance
928                         g.previous_summary.voided_balance, // 5 - Voided Balance
929                         g.previous_summary.payment_received, // 6 - Payment Received
930                         g.previous_summary.payment_applied, // 7 - Payment Applied
931                         g.previous_summary.change_given, // 8 - Change Given
932                         g.previous_summary.credit_given, // 9 - Credit Given
933                         g.previous_summary.new_balance, // 10 - New Balance
934                         g.previous_summary.payment_type, // 11 - Payment Type
935                         g.previous_summary.note, // 12 - Note
936                         robj && robj.textcode ? robj.textcode : robj // 13 - API call result
937                     ]
938                 ), {
939                     'au_id' : g.patron.id(),
940                     'au_family_name' : g.patron.family_name(),
941                     'au_barcode' : g.patron.card().barcode()
942                 }
943             );
944         } catch(E) {
945             alert('Error logging payment in bill2.js: ' + E);
946         }
947
948         if (typeof robj.ilsevent != 'undefined') {
949             switch(robj.textcode) {
950                 case 'SUCCESS' : return true; break;
951                 case 'REFUND_EXCEEDS_DESK_PAYMENTS' : alert($("patronStrings").getFormattedString('staff.patron.bills.pay.refund_exceeds_desk_payment', [robj.desc])); return false; break;
952                 case 'INVALID_USER_XACT_ID' :
953                     refresh(); default_focus();
954                     alert($("patronStrings").getFormattedString('staff.patron.bills.pay.invalid_user_xact_id', [robj.desc])); return false; break;
955                 default: throw(robj); break;
956             }
957         }
958         return true;
959     } catch(E) {
960         g.error.standard_unexpected_error_alert($("patronStrings").getString('staff.patron.bills.pay.payment_failed'),E);
961         return false;
962     }
963 }
964
965 function refresh(params) {
966     try {
967         if (params && params.clear_voided_summary) {
968             g.data.voided_billings = []; g.data.stash('voided_billings');
969         }
970         refresh_patron();
971         g.bill_list.clear();
972         retrieve_mbts_for_list();
973         tally_voided();
974         distribute_payment(); 
975     } catch(E) {
976         alert('Error in bill2.js, refresh(): ' + E);
977     }
978 }
979
980 function void_all_billings(mobts_id) {
981     try {
982         JSAN.use('util.functional');
983         
984         var mb_list = g.network.simple_request( 'FM_MB_RETRIEVE_VIA_MBTS_ID.authoritative', [ ses(), mobts_id ] );
985         if (typeof mb_list.ilsevent != 'undefined') throw(mb_list);
986
987         mb_list = util.functional.filter_list( mb_list, function(o) { return ! get_bool( o.voided() ) });
988
989         if (mb_list.length == 0) { alert($("patronStrings").getString('staff.patron.bills.void_all_billings.all_voided')); return; }
990
991         var sum = 0;
992         for (var i = 0; i < mb_list.length; i++) sum += util.money.dollars_float_to_cents_integer( mb_list[i].amount() );
993         sum = util.money.cents_as_dollars( sum );
994
995         var msg = $("patronStrings").getFormattedString('staff.patron.bills.void_all_billings.void.message', [sum]);
996         var r = g.error.yns_alert(msg,
997             $("patronStrings").getString('staff.patron.bills.void_all_billings.void.title'),
998             $("patronStrings").getString('staff.patron.bills.void_all_billings.void.yes'),
999             $("patronStrings").getString('staff.patron.bills.void_all_billings.void.no'), null,
1000             $("patronStrings").getString('staff.patron.bills.void_all_billings.void.confirm_message'));
1001         if (r == 0) {
1002             var robj = g.network.simple_request('FM_MB_VOID',[ses()].concat(util.functional.map_list(mb_list,function(o){return o.id();})));
1003             if (robj.ilsevent) {
1004                 switch(Number(robj.ilsevent)) {
1005                     case 5000 /* PERM_FAILURE */:
1006                         return;
1007                     break;
1008                     default: 
1009                         g.error.standard_unexpected_error_alert($("patronStrings").getString('staff.patron.bills.void_all_billings.error_voiding_bills'),robj); 
1010                         return; 
1011                     break;
1012                 }
1013             }
1014
1015             g.data.stash_retrieve(); if (! g.data.voided_billings ) g.data.voided_billings = []; 
1016             for (var i = 0; i < mb_list.length; i++) {
1017                     g.data.voided_billings.push( mb_list[i] );
1018             }
1019             g.data.stash('voided_billings');
1020         }
1021     } catch(E) {
1022         try { g.error.standard_unexpected_error_alert('bill2.js, void_all_billings():',E); } catch(F) { alert(E); }
1023     }
1024 }
1025
1026 function refresh_patron() {
1027     JSAN.use('patron.util'); JSAN.use('util.money');
1028     patron.util.retrieve_fleshed_au_via_id(ses(),g.patron_id,null,function(req) {
1029         var au_obj = req.getResultObject();
1030         if (typeof au_obj.ilsevent == 'undefined') {
1031             g.patron = au_obj;
1032             $('credit_forward').setAttribute('value',util.money.sanitize( g.patron.credit_forward_balance() ));
1033         }
1034     });
1035 }