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