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