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