]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/xul/staff_client/server/patron/bill2.js
Fix: Current Bills thinks it is Bill History
[working/Evergreen.git] / Open-ILS / xul / staff_client / server / patron / bill2.js
1 function my_init() {
2     try {
3         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
4         if (typeof JSAN == 'undefined') { throw( $("commonStrings").getString('common.jsan.missing') ); }
5         JSAN.errorLevel = "die"; // none, warn, or die
6         JSAN.addRepository('/xul/server/');
7
8         JSAN.use('util.error'); g.error = new util.error();
9         JSAN.use('util.network'); g.network = new util.network();
10         JSAN.use('util.date');
11         JSAN.use('util.money');
12         JSAN.use('util.widgets');
13         JSAN.use('patron.util');
14         JSAN.use('OpenILS.data'); g.data = new OpenILS.data(); g.data.init({'via':'stash'});
15         g.data.voided_billings = []; g.data.stash('voided_billings');
16
17         g.error.sdump('D_TRACE','my_init() for bill2.xul');
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 event_listeners() {
68     try {
69         $('details').addEventListener(
70             'command',
71             handle_details,
72             false
73         );
74
75         $('add').addEventListener(
76             'command',
77             handle_add,
78             false
79         );
80
81         $('voidall').addEventListener(
82             'command',
83             handle_void_all,
84             false
85         );
86
87         $('refund').addEventListener(
88             'command',
89             handle_refund,
90             false
91         );
92
93         $('opac').addEventListener(
94             'command',
95             handle_opac,
96             false
97         );
98
99         $('copy_details').addEventListener(
100             'command',
101             handle_copy_details,
102             false
103         );
104
105         $('payment').addEventListener(
106             'change',
107             function(ev) {
108                 if ($('payment_type').value == 'credit_payment') {
109                     JSAN.use('util.money');
110                     JSAN.use('patron.util'); g.patron = patron.util.retrieve_fleshed_au_via_id(ses(),g.patron_id,null);
111                     var proposed = util.money.dollars_float_to_cents_integer(ev.target.value);
112                     var available = util.money.dollars_float_to_cents_integer(g.patron.credit_forward_balance());
113                     if (proposed > available) {
114                         alert($("patronStrings").getFormattedString('staff.patron.bills.bill_payment_amount.credit_amount', [g.patron.credit_forward_balance()]));
115                         ev.target.value = util.money.cents_as_dollars( available );
116                         ev.target.setAttribute('value',ev.target.value);
117                     }
118                 }
119                 distribute_payment(); 
120             },
121             false
122         );
123
124         $('payment').addEventListener(
125             'focus',
126             function(ev) { ev.target.select(); },
127             false
128         );
129
130         $('payment').addEventListener(
131             'keypress',
132             function(ev) {
133                 if (! (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) ) { return; }
134                 distribute_payment();
135                 $('apply_payment_btn').focus();
136             },
137             false
138         );
139
140         $('bill_patron_btn').addEventListener(
141             'command',
142             function() {
143                 JSAN.use('util.window'); var win = new util.window();
144                 var my_xulG = win.open(
145                     urls.XUL_PATRON_BILL_WIZARD,
146                     'billwizard',
147                     'chrome,resizable,modal',
148                     { 'patron_id' : g.patron_id }
149                 );
150                 if (my_xulG.xact_id) { g.funcs.push( gen_list_append_func( my_xulG.xact_id ) ); /* FIXME: do something to update summary sidebar */ }
151             },
152             false
153         );
154
155         $('bill_history_btn').addEventListener(
156             'command',
157             function() {
158                 xulG.display_window.g.patron.right_deck.reset_iframe( 
159                     urls.XUL_PATRON_BILL_HISTORY,
160                     {},
161                     {
162                         'patron_id' : g.patron_id,
163                         'refresh' : function() { refresh(); },
164                         'new_tab' : xulG.new_tab
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         'map_row_to_columns' : patron.util.std_map_row_to_columns(' '),
496         'on_select' : function(ev) {
497             JSAN.use('util.functional');
498             g.bill_list_selection = util.functional.map_list(
499                 g.bill_list.retrieve_selection(),
500                 function(o) { return o.getAttribute('retrieve_id'); }
501             );
502             //tally_selected();
503             $('details').setAttribute('disabled', g.bill_list_selection.length == 0);
504             $('add').setAttribute('disabled', g.bill_list_selection.length == 0);
505             $('voidall').setAttribute('disabled', g.bill_list_selection.length == 0);
506             $('refund').setAttribute('disabled', g.bill_list_selection.length == 0);
507             $('opac').setAttribute('disabled', g.bill_list_selection.length == 0);
508             $('copy_details').setAttribute('disabled', g.bill_list_selection.length == 0);
509         },
510         'on_click' : function(ev) {
511             netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect UniversalBrowserRead');
512             var row = {}; var col = {}; var nobj = {};
513             g.bill_list.node.treeBoxObject.getCellAt(ev.clientX,ev.clientY,row,col,nobj);
514             if (row.value == -1) return;
515             var treeItem = g.bill_list.node.contentView.getItemAtIndex(row.value);
516             if (treeItem.nodeName != 'treeitem') return;
517             var treeRow = treeItem.firstChild;
518             var treeCell = treeRow.firstChild;
519             if (g.check_map[ treeItem.getAttribute('retrieve_id') ] != (treeCell.getAttribute('value') == 'true')) {
520                 g.check_map[ treeItem.getAttribute('retrieve_id') ] = treeCell.getAttribute('value') == 'true';
521                 g.row_map[ treeItem.getAttribute('retrieve_id') ].row.my.checked = treeCell.getAttribute('value') == 'true';
522                 tally_all();
523                 distribute_payment();
524             }
525         },
526         'on_sort' : function() {
527             tally_all();
528         },
529         'on_checkbox_toggle' : function(toggle) {
530             try {
531                 var retrieve_ids = g.bill_list.dump_retrieve_ids();
532                 for (var i = 0; i < retrieve_ids.length; i++) {
533                     g.check_map[ retrieve_ids[i] ] = (toggle=='on');
534                     g.row_map[ retrieve_ids[i] ].row.my.checked = (toggle=='on');
535                 }
536                 tally_all();
537             } catch(E) {
538                 alert('error in on_checkbox_toggle(): ' + E);
539             }
540         },
541         'retrieve_row' : function(params) {
542             try {
543                 var id = params.retrieve_id;
544                 var row = params.row;
545
546                 function handle_props(row) {
547                     try {
548                         if ( row && row.my && row.my.mbts && Number( row.my.mbts.balance_owed() ) < 0 ) {
549                             util.widgets.addProperty(params.row_node.firstChild,'refundable');
550                             util.widgets.addProperty(params.row_node.firstChild.childNodes[ g.payment_pending_column_idx ],'refundable');
551                         }
552                         if ( row && row.my && row.my.circ && ! row.my.circ.checkin_time() ) {
553                             $('circulating_hint').hidden = false;
554                             util.widgets.addProperty(params.row_node.firstChild,'circulating');
555                             util.widgets.addProperty(params.row_node.firstChild.childNodes[ g.title_column_idx ],'circulating');
556                         }
557                     } catch(E) {
558                         g.error.sdump('D_WARN','Error setting list properties in bill2.js: ' + E);
559                         alert('Error setting list properties in bill2.js: ' + E);
560                     }
561                 }
562
563                 if (id) {
564                     if (typeof row.my == 'undefined') row.my = {};
565                     if (typeof row.my.mbts == 'undefined' ) {
566                         g.network.simple_request('BLOB_MBTS_DETAILS_RETRIEVE',[ses(),id], function(req) {
567                             var blob = req.getResultObject();
568                             row.my.mbts = blob.transaction;
569                             row.my.circ = blob.circ;
570                             row.my.acp = blob.copy;
571                             row.my.mvr = blob.record;
572                             if (typeof params.on_retrieve == 'function') {
573                                 if ( row.my.mbts && Number( row.my.mbts.balance_owed() ) < 0 ) {
574                                     row.my.checked = false;
575                                 }
576                                 handle_props(row);
577                                 params.on_retrieve(row);
578                             };
579                             g.bill_map[ id ] = blob;
580                             g.check_map[ id ] = row.my.checked;
581                             tally_all();
582                         } );
583                     } else {
584                         if (typeof params.on_retrieve == 'function') { 
585                             handle_props(row);
586                             params.on_retrieve(row); 
587                         }
588                     }
589                 } else {
590                     if (typeof params.on_retrieve == 'function') { 
591                         params.on_retrieve(row); 
592                     }
593                 }
594
595                 return row;
596             } catch(E) {
597                 alert('Error in bill2.js, retrieve_row(): ' + E);
598             }
599         }
600     } );
601
602     g.title_column_idx = util.functional.map_list( g.bill_list.columns, function(o) { return o.id; } ).indexOf( 'title' );
603     g.payment_pending_column_idx = util.functional.map_list( g.bill_list.columns, function(o) { return o.id; } ).indexOf( 'payment_pending' );
604     $('bill_list_actions').appendChild( g.bill_list.render_list_actions() );
605     g.bill_list.set_list_actions();
606 }
607
608 function handle_add() {
609     if(g.bill_list_selection.length > 1) {
610         var msg = $("patronStrings").getFormattedString('staff.patron.bill_history.handle_add.message_plural', [g.bill_list_selection]);
611     } else {
612         var msg = $("patronStrings").getFormattedString('staff.patron.bill_history.handle_add.message_singular', [g.bill_list_selection]);
613     }
614         
615     var r = g.error.yns_alert(msg,
616         $("patronStrings").getString('staff.patron.bill_history.handle_add.title'),
617         $("patronStrings").getString('staff.patron.bill_history.handle_add.btn_yes'),
618         $("patronStrings").getString('staff.patron.bill_history.handle_add.btn_no'),null,
619         $("patronStrings").getString('staff.patron.bill_history.handle_add.confirm_message'));
620     if (r == 0) {
621         JSAN.use('util.window');
622         var win = new util.window();
623         for (var i = 0; i < g.bill_list_selection.length; i++) {
624             var w = win.open(
625                 urls.XUL_PATRON_BILL_WIZARD,
626                 'billwizard',
627                 'chrome,resizable,modal',
628                 { 'patron_id' : g.patron_id, 'xact_id' : g.bill_list_selection[i] }
629             );
630         }
631         refresh();
632         if (typeof window.xulG == 'object' && typeof window.xulG.refresh == 'function') window.xulG.refresh();
633     }
634 }
635
636 function handle_void_all() {
637     if(g.bill_list_selection.length > 1) {
638         var msg = $("patronStrings").getFormattedString('staff.patron.bill_history.handle_void.message_plural', [g.bill_list_selection]);
639     } else {
640         var msg = $("patronStrings").getFormattedString('staff.patron.bill_history.handle_void.message_singular', [g.bill_list_selection]);
641     }
642         
643     var r = g.error.yns_alert(msg,
644         $("patronStrings").getString('staff.patron.bill_history.handle_void.title'),
645         $("patronStrings").getString('staff.patron.bill_history.handle_void.btn_yes'),
646         $("patronStrings").getString('staff.patron.bill_history.handle_void.btn_no'),null,
647         $("patronStrings").getString('staff.patron.bill_history.handle_void.confirm_message'));
648     if (r == 0) {
649         for (var i = 0; i < g.bill_list_selection.length; i++) {
650             void_all_billings( g.bill_list_selection[i] );
651         }
652         refresh();
653         if (typeof window.xulG == 'object' && typeof window.xulG.refresh == 'function') window.xulG.refresh();
654         if (typeof window.xulG == 'object' && typeof window.xulG.on_money_change == 'function') window.xulG.on_money_change();
655     }
656 }
657
658 function handle_opac() {
659     try {
660         var ids = [];
661         for (var i = 0; i < g.bill_list_selection.length; i++) {
662             var my_mvr = g.bill_map[ g.bill_list_selection[i] ].record;
663             var my_acp = g.bill_map[ g.bill_list_selection[i] ].copy;
664             if (typeof my_mvr != 'undefined' && my_mvr != null) {
665                 ids.push( { 'barcode' : my_acp.barcode(), 'doc_id' : my_mvr.doc_id() } );
666             }
667         }
668         JSAN.use('cat.util');
669         cat.util.show_in_opac( ids );
670     } catch(E) {
671         alert('Error in bill2.js, handle_opac: ' + E);
672     }
673 }
674
675 function handle_copy_details() {
676     try {
677         var ids = [];
678         for (var i = 0; i < g.bill_list_selection.length; i++) {
679             var my_acp = g.bill_map[ g.bill_list_selection[i] ].copy;
680             if (typeof my_acp != 'undefined' && my_acp != null) {
681                 ids.push( my_acp.barcode() );
682             }
683         }
684         JSAN.use('circ.util');
685         circ.util.item_details_new( ids );
686     } catch(E) {
687         alert('Error in bill2.js, handle_opac: ' + E);
688     }
689 }
690
691 function handle_details() {
692     JSAN.use('util.window'); var win = new util.window();
693     for (var i = 0; i < g.bill_list_selection.length; i++) {
694         var my_xulG = win.open(
695             urls.XUL_PATRON_BILL_DETAILS,
696             'test_billdetails_' + g.bill_list_selection[i],
697             'chrome,resizable',
698             {
699                 'patron_id' : g.patron_id,
700                 'mbts_id' : g.bill_list_selection[i],
701                 'refresh' : function() {
702                     refresh(); 
703                     if (typeof window.xulG == 'object' && typeof window.xulG.refresh == 'function') window.xulG.refresh();
704                 }, 
705                 'new_tab' : xulG.new_tab,
706                 'url_prefix' : xulG.url_prefix
707             }
708         );
709     }
710 }
711
712 function print_bills() {
713     try {
714         var template = 'bills_current';
715         JSAN.use('patron.util');
716         g.patron = patron.util.retrieve_fleshed_au_via_id(ses(),g.patron_id,null); 
717         g.bill_list.print({ 
718               'patron' : g.patron
719             , 'printer_context' : 'receipt'
720             , 'template' : template
721             , 'data' : {
722                   grand_total_owed:   $('tb_total_owed').value
723                 , grand_total_billed: $('total_billed').value
724                 , grand_total_paid:   $('tb_total_paid').value
725             }
726          });
727     } catch(E) {
728         g.error.standard_unexpected_error_alert($("patronStrings").getString('staff.patron.bill_history.print_bills.print_error'), E);
729     }
730 }
731
732 function distribute_payment() {
733     try {
734         JSAN.use('util.money');
735         var tb = $('payment');
736         tb.value = util.money.cents_as_dollars( util.money.dollars_float_to_cents_integer( tb.value ) );
737         tb.setAttribute('value', tb.value );
738         var total = util.money.dollars_float_to_cents_integer( tb.value );
739         if (total < 0) { tb.value = '0.00'; tb.setAttribute('value','0.00'); total = 0; }
740         var retrieve_ids = g.bill_list.dump_retrieve_ids();
741         for (var i = 0; i < retrieve_ids.length; i++) {
742             var row_params = g.row_map[retrieve_ids[i]];
743             if (g.check_map[retrieve_ids[i]]) { 
744                 var bill = g.bill_map[retrieve_ids[i]].transaction;
745                 var bo = util.money.dollars_float_to_cents_integer( bill.balance_owed() );
746                 if ( bo > total ) {
747                     row_params.row.my.payment_pending = util.money.cents_as_dollars( total );
748                     total = 0;
749                 } else {
750                     row_params.row.my.payment_pending = util.money.cents_as_dollars( bo );
751                     total = total - bo;
752                 }
753             } else {
754                 row_params.row.my.payment_pending = '0.00';
755             }
756             g.bill_list.refresh_row(row_params);
757         }
758         tally_pending();
759     } catch(E) {
760         alert('Error in bill2.js, distribute_payment(): ' + E);
761     }
762 }
763
764 function apply_payment() {
765     try {
766         var payment_blob = {};
767         JSAN.use('util.window');
768         var win = new util.window();
769         switch($('payment_type').value) {
770             case 'credit_card_payment' :
771                 g.data.temp = '';
772                 g.data.stash('temp');
773                 var my_xulG = win.open(
774                     urls.XUL_PATRON_BILL_CC_INFO,
775                     'billccinfo',
776                     'chrome,resizable,modal',
777                     {'patron_id': g.patron_id}
778                 );
779                 g.data.stash_retrieve();
780                 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
781             break;
782             case 'check_payment' :
783                 g.data.temp = '';
784                 g.data.stash('temp');
785                 var my_xulG = win.open(
786                     urls.XUL_PATRON_BILL_CHECK_INFO,
787                     'billcheckinfo',
788                     'chrome,resizable,modal'
789                 );
790                 g.data.stash_retrieve();
791                 payment_blob = JSON2js( g.data.temp );
792             break;
793         }
794         if (
795             (typeof payment_blob == 'undefined') || 
796             payment_blob=='' || 
797             payment_blob.cancelled=='true'
798         ) { 
799             alert( $('commonStrings').getString('common.cancelled') ); 
800             return; 
801         }
802         payment_blob.userid = g.patron_id;
803         payment_blob.note = payment_blob.note || '';
804         //payment_blob.cash_drawer = 1; // FIXME: get new Config() to work
805         payment_blob.payment_type = $('payment_type').value;
806         var tally_blob = tally_pending();
807         payment_blob.payments = tally_blob.payments;
808         // Handle patron credit
809         if ( payment_blob.payment_type == 'credit_payment' ) { // paying with patron credit
810             if ( $('convert_change_to_credit').checked ) {
811                 // No need to convert credit into credit, handled automatically
812                 payment_blob.patron_credit = '0.00';
813             } else {
814                 // Cashing out extra credit as change
815                 payment_blob.patron_credit = 0 - tally_blob.change;
816             }
817         } else if ( $('convert_change_to_credit').checked ) {
818             // Saving change from a non-credit payment as patron credit on server
819             payment_blob.patron_credit = tally_blob.change;
820         } else {
821             payment_blob.patron_credit = '0.00';
822         }
823         if ( payment_blob.payments.length == 0 && payment_blob.patron_credit == '0.00' ) {
824             alert($("patronStrings").getString('staff.patron.bills.apply_payment.nothing_applied'));
825             return;
826         }
827         if ( pay( payment_blob ) ) {
828
829             $('payment').value = ''; $('payment').select(); $('payment').focus();
830             refresh({'clear_voided_summary':true});
831             if (typeof window.xulG == 'object' && typeof window.xulG.refresh == 'function') window.xulG.refresh();
832             if (typeof window.xulG == 'object' && typeof window.xulG.on_money_change == 'function') window.xulG.on_money_change();
833             if ( $('payment_type').value == 'credit_payment' || $('convert_change_to_credit').checked ) {
834                 refresh_patron();
835             }
836             try {
837                 if ( ! $('receipt_upon_payment').hasAttribute('checked') ) { return; } // Skip print attempt
838                 if ( ! $('receipt_upon_payment').getAttribute('checked') ) { return; } // Skip print attempt
839                 var no_print_prompting = g.data.hash.aous['circ.staff_client.do_not_auto_attempt_print'];
840                 if (no_print_prompting) {
841                     if (no_print_prompting.indexOf( "Bill Pay" ) > -1) { return; } // Skip print attempt
842                 }
843                 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
844                 g.data.stash_retrieve();
845                 var template = 'bill_payment';
846                 JSAN.use('patron.util'); JSAN.use('util.functional');
847                 var params = { 
848                     'patron' : g.patron,
849                     'lib' : g.data.hash.aou[ ses('ws_ou') ],
850                     'staff' : ses('staff'),
851                     'header' : g.data.print_list_templates[template].header,
852                     'line_item' : g.data.print_list_templates[template].line_item,
853                     'footer' : g.data.print_list_templates[template].footer,
854                     'type' : g.data.print_list_templates[template].type,
855                     'list' : util.functional.map_list(
856                         payment_blob.payments,
857                         function(o) {
858                             return {
859                                 'bill_id' : o[0],
860                                 'payment' : o[1],
861                                 'last_billing_type' : g.bill_map[ o[0] ].transaction.last_billing_type(),
862                                 'last_billing_note' : g.bill_map[ o[0] ].transaction.last_billing_note(),
863                                 'title' : typeof g.bill_map[ o[0] ].record != 'undefined' ? g.bill_map[ o[0] ].record.title() : '', 
864                                 'barcode' : typeof g.bill_map[ o[0] ].copy != 'undefined' ? g.bill_map[ o[0] ].copy.barcode() : ''
865                             };
866                         }
867                     ),
868                     'data' : g.previous_summary
869                 };
870                 g.error.sdump('D_DEBUG',js2JSON(params));
871                 if ($('printer_prompt').hasAttribute('checked')) {
872                     if ($('printer_prompt').getAttribute('checked')) {
873                             params.no_prompt = false;
874                     } else {
875                             params.no_prompt = true;
876                     }
877                 } else {
878                     params.no_prompt = true;
879                 }
880                 JSAN.use('util.print'); var print = new util.print('receipt');
881                 for (var i = 0; i < $('num_of_receipts').value; i++) {
882                     print.tree_list( params );
883                 }
884             } catch(E) {
885                 g.error.standard_unexpected_error_alert('bill receipt', E);
886             }
887         }
888     } catch(E) {
889         alert('Error in bill2.js, apply_payment(): ' + E);
890     }
891 }
892
893 function pay(payment_blob) {
894     try {
895         var x = $('annotate_payment');
896         if (x && x.checked && (! payment_blob.note)) {
897             payment_blob.note = window.prompt(
898                 $("patronStrings").getString('staff.patron.bills.pay.annotate_payment'),
899                 '', 
900                 $("patronStrings").getString('staff.patron.bills.pay.annotate_payment.title')
901             );
902         }
903         g.previous_summary = {
904             original_balance : $('tb_total_owed').value,
905             voided_balance : $('currently_voided').value,
906             payment_received : $('payment').value,
907             payment_applied : $('pending_payment').value,
908             change_given : $('convert_change_to_credit').checked ? 0 : $('pending_change').value,
909             credit_given : $('convert_change_to_credit').checked ? $('pending_change').value : 0,
910             new_balance : util.money.cents_as_dollars( 
911                 util.money.dollars_float_to_cents_integer( $('tb_total_owed').value ) - 
912                 util.money.dollars_float_to_cents_integer( $('pending_payment').value )
913             ),
914             payment_type : $('payment_type').getAttribute('label'),
915             note : payment_blob.note
916         }
917         var robj = g.network.simple_request( 'BILL_PAY', [ ses(), payment_blob, g.patron.last_xact_id() ]);
918         if (typeof robj.ilsevent != 'undefined') {
919             switch(robj.textcode) {
920                 case 'SUCCESS' : return true; break;
921                 case 'REFUND_EXCEEDS_DESK_PAYMENTS' : alert($("patronStrings").getFormattedString('staff.patron.bills.pay.refund_exceeds_desk_payment', [robj.desc])); return false; break;
922                 case 'INVALID_USER_XACT_ID' :
923                     refresh(); default_focus();
924                     alert($("patronStrings").getFormattedString('staff.patron.bills.pay.invalid_user_xact_id', [robj.desc])); return false; break;
925                 default: throw(robj); break;
926             }
927         }
928         return true;
929     } catch(E) {
930         g.error.standard_unexpected_error_alert($("patronStrings").getString('staff.patron.bills.pay.payment_failed'),E);
931         return false;
932     }
933 }
934
935 function refresh(params) {
936     try {
937         if (params && params.clear_voided_summary) {
938             g.data.voided_billings = []; g.data.stash('voided_billings');
939         }
940         refresh_patron();
941         g.bill_list.clear();
942         retrieve_mbts_for_list();
943         tally_voided();
944         distribute_payment(); 
945     } catch(E) {
946         alert('Error in bill2.js, refresh(): ' + E);
947     }
948 }
949
950 function void_all_billings(mobts_id) {
951     try {
952         JSAN.use('util.functional');
953         
954         var mb_list = g.network.simple_request( 'FM_MB_RETRIEVE_VIA_MBTS_ID.authoritative', [ ses(), mobts_id ] );
955         if (typeof mb_list.ilsevent != 'undefined') throw(mb_list);
956
957         mb_list = util.functional.filter_list( mb_list, function(o) { return ! get_bool( o.voided() ) });
958
959         if (mb_list.length == 0) { alert($("patronStrings").getString('staff.patron.bills.void_all_billings.all_voided')); return; }
960
961         var sum = 0;
962         for (var i = 0; i < mb_list.length; i++) sum += util.money.dollars_float_to_cents_integer( mb_list[i].amount() );
963         sum = util.money.cents_as_dollars( sum );
964
965         var msg = $("patronStrings").getFormattedString('staff.patron.bills.void_all_billings.void.message', [sum]);
966         var r = g.error.yns_alert(msg,
967             $("patronStrings").getString('staff.patron.bills.void_all_billings.void.title'),
968             $("patronStrings").getString('staff.patron.bills.void_all_billings.void.yes'),
969             $("patronStrings").getString('staff.patron.bills.void_all_billings.void.no'), null,
970             $("patronStrings").getString('staff.patron.bills.void_all_billings.void.confirm_message'));
971         if (r == 0) {
972             var robj = g.network.simple_request('FM_MB_VOID',[ses()].concat(util.functional.map_list(mb_list,function(o){return o.id();})));
973             if (robj.ilsevent) {
974                 switch(Number(robj.ilsevent)) {
975                     case 5000 /* PERM_FAILURE */:
976                         return;
977                     break;
978                     default: 
979                         g.error.standard_unexpected_error_alert($("patronStrings").getString('staff.patron.bills.void_all_billings.error_voiding_bills'),robj); 
980                         return; 
981                     break;
982                 }
983             }
984
985             g.data.stash_retrieve(); if (! g.data.voided_billings ) g.data.voided_billings = []; 
986             for (var i = 0; i < mb_list.length; i++) {
987                     g.data.voided_billings.push( mb_list[i] );
988             }
989             g.data.stash('voided_billings');
990         }
991     } catch(E) {
992         try { g.error.standard_unexpected_error_alert('bill2.js, void_all_billings():',E); } catch(F) { alert(E); }
993     }
994 }
995
996 function refresh_patron() {
997     JSAN.use('patron.util'); JSAN.use('util.money');
998     patron.util.retrieve_fleshed_au_via_id(ses(),g.patron_id,null,function(req) {
999         var au_obj = req.getResultObject();
1000         if (typeof au_obj.ilsevent == 'undefined') {
1001             g.patron = au_obj;
1002             $('credit_forward').setAttribute('value',util.money.sanitize( g.patron.credit_forward_balance() ));
1003         }
1004     });
1005 }