]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/xul/staff_client/server/serial/batch_receive.js
Serials: dojo/autogrid-based scaffolding for building serials objects
[working/Evergreen.git] / Open-ILS / xul / staff_client / server / serial / batch_receive.js
1 /* The code in this file relies on common.js */
2
3 dojo.require("dojo.cookie");
4 dojo.require("openils.Util");
5 dojo.require("openils.User");
6 dojo.require("openils.CGI");
7 dojo.require("openils.PermaCrud");
8
9 var batch_receiver;
10
11 function S(k) {
12     return dojo.byId("serialStrings").getString("batch_receive." + k).
13         replace("\\n", "\n");
14 }
15
16 function F(k, args) {
17     return dojo.byId("serialStrings").
18         getFormattedString("batch_receive." + k, args).replace("\\n", "\n");
19 }
20
21 function BatchReceiver() {
22     var self = this;
23
24     this.init = function(authtoken, bib_id, sub_id) {
25         if (authtoken) {
26             this.user = new openils.User({"authtoken": authtoken});
27             this.pcrud = new openils.PermaCrud({"authtoken": authtoken});
28             this.authtoken = authtoken;
29         }
30
31         hide("batch_receive_sub");
32         hide("batch_receive_entry");
33         hide("batch_receive_bibdata_bits");
34         hide("batch_receive_sub_bits");
35         hide("batch_receive_issuance_bits");
36         hide("batch_receive_issuance");
37
38         dojo.byId("bib_lookup_submit").disabled = false;
39         dojo.byId("bib_search_term").value = "";
40
41         if (!bib_id) {
42             show("batch_receive_bib");
43             dojo.byId("bib_search_term").focus();
44         }
45
46         if (!this.entry_tbody) {
47             this.entry_tbody = dojo.byId("entry_tbody");
48             this.template = this.entry_tbody.removeChild(
49                 dojo.byId("entry_template")
50             );
51         }
52
53         this._clear_entry_batch_row();
54
55         this._call_number_cache = null;
56         this._prepared_call_number_controls = {};
57         this._location_by_lib = {};
58
59         /* empty the entry receiving table if we're starting over */
60         if (this.item_cache) {
61             for (var id in this.item_cache) {
62                 this.finish_receipt(this.item_cache[id]);
63                 hard_empty(this.entry_tbody);
64             }
65             /* XXX incredibly, running hard_empty() more than once seems to be
66              * good and necessary.  There's a bug under the covers somewhere,
67              * but this keeps it out of sight for the moment. */
68              hard_empty(this.entry_tbody);
69         }
70         hard_empty(this.entry_tbody);
71
72         this.rows = {};
73         this.item_cache = {};
74
75         if (bib_id)
76             this.bib_lookup(bib_id, null, true, sub_id);
77
78         busy(false);
79     };
80
81     this._clear_entry_batch_row = function() {
82         dojo.forEach(
83             dojo.byId("entry_batch_row").childNodes,
84             function(node) {
85                 if (node.nodeType == 1 &&
86                     node.getAttribute("name") != "barcode")
87                     hard_empty(node);
88             }
89         );
90     };
91
92     this._show_bibdata_bits = function() {
93         hard_empty("title_here");
94         dojo.byId("title_here").appendChild(T(this.bibdata.mvr.title()));
95         hard_empty("author_here");
96
97         if (this.bibdata.mvr.author()) {
98             dojo.byId("author_here").appendChild(T(this.bibdata.mvr.author()));
99             show("author_here_holder");
100         } else {
101             hide("author_here_holder");
102         }
103
104         show("batch_receive_bibdata_bits");
105     };
106
107     this._sub_label = function(sub) {
108         /* XXX use a formatting string from serial.properties */
109         return sub.id() + ": (" + sub.owning_lib().shortname() + ") " +
110             D(sub.start_date()) + " - " + D(sub.end_date());
111     };
112
113     this._show_sub_bits = function() {
114         hard_empty("sublabel_here");
115         dojo.place(
116             T(this._sub_label(this.sub)),
117             "sublabel_here",
118             "only"
119         );
120         hide("batch_receive_sub");
121         show("batch_receive_sub_bits");
122     };
123
124     this._show_issuance_bits = function() {
125         hide("batch_receive_issuance");
126         hard_empty("issuance_label_here");
127         dojo.place(
128             T(this.issuance.label()),
129             "issuance_label_here",
130             "only"
131         );
132         show("batch_receive_issuance_bits");
133     }
134
135     this._get_receivable_issuances = function() {
136         var issuances = [];
137
138         busy(true);
139         try {
140             fieldmapper.standardRequest(
141                 ["open-ils.serial", "open-ils.serial.issuances.receivable"], {
142                     "params": [this.authtoken, this.sub.id()],
143                     "async": false,
144                     "onresponse": function(r) {
145                         if (r = openils.Util.readResponse(r))
146                             issuances.push(r);
147                     }
148                 }
149             );
150         } catch (E) {
151             alert(E);
152         }
153         busy(false);
154
155         return issuances;
156     };
157
158     this._build_circ_modifier_dropdown = function() {
159         if (!this._built_circ_modifier_dropdown) {
160             var menulist = dojo.create("menulist");
161             var menupopup = dojo.create("menupopup", null, menulist, "only");
162             dojo.create(
163                 "menuitem", {"value": 0, "label": S("none")},
164                 menupopup, "first"
165             );
166
167             var mods = [];
168             fieldmapper.standardRequest(
169                 ["open-ils.circ", "open-ils.circ.circ_modifier.retrieve.all"],{
170                     "params": [{"full": true}],
171                     "async": false,
172                     "onresponse": function(r) {
173                         if (mods = openils.Util.readResponse(r)) {
174                             mods.sort(
175                                 function(a,b) {
176                                     return a.code() > b.code() ? 1 :
177                                         b.code() > a.code() ? -1 :
178                                         0;
179                                 }
180                             ).forEach(
181                                 function(mod) {
182                                     dojo.create(
183                                         "menuitem", {
184                                             "value": mod.code(),
185                                             "label": document.getElementById('commonStrings').getFormattedString('staff.circ_modifier.display',[mod.code(),mod.name(),mod.description()]) 
186                                         }, menupopup, "last"
187                                     );
188                                 }
189                             );
190                         }
191                     }
192                 }
193             );
194             if (!mods.length) {
195                 /* in this case, discard menulist and menupopup */
196                 this._built_circ_modifier_dropdown =
197                     dojo.create("description", {"value": "-"});
198             } else {
199                 this._built_circ_modifier_dropdown = menulist;
200             }
201         }
202
203         return dojo.clone(this._built_circ_modifier_dropdown);
204     };
205
206     this._extend_circ_modifier_for_batch = function(control) {
207         dojo.create(
208             "menuitem", {"value": -1, "label": "---"},
209             dojo.query("menupopup", control)[0],
210             "first"
211         );
212         return control;
213     };
214
215     this._build_location_dropdown = function(locs, add_unset_value) {
216         var menulist = dojo.create("menulist");
217         var menupopup = dojo.create("menupopup", null, menulist, "only");
218
219         if (add_unset_value) {
220             dojo.create(
221                 "menuitem", {"value": -1, "label": "---"}, menupopup, "first"
222             );
223         }
224
225         locs.forEach(
226             function(loc) {
227                 dojo.create(
228                     "menuitem", {
229                         "value": loc.id(),
230                         "label": "(" + loc.owning_lib().shortname() + ") " +
231                             loc.name() /* XXX i18n */
232                     }, menupopup, "last"
233                 );
234             }
235         );
236
237         return menulist;
238     };
239
240     this._get_locations_for_lib = function(lib) {
241         if (!this._location_by_lib[lib]) {
242             fieldmapper.standardRequest(
243                 ["open-ils.circ", "open-ils.circ.copy_location.retrieve.all"],{
244                     "params": [lib, false, true],
245                     "async": false,
246                     "onresponse": function(r) {
247                         if (locs = openils.Util.readResponse(r))
248                             self._location_by_lib[lib] = locs;
249                     }
250                 }
251             );
252         }
253
254         return this._location_by_lib[lib];
255     };
256
257     this._build_call_number_control = function(item) {
258         /* In any case, give a dropdown of call numbers related to the
259          * same bre as the subscription relates to. */
260         if (!this._call_number_cache) {
261             this._call_number_cache = this.pcrud.search(
262                 "acn", {
263                     "record": this.sub.record_entry()
264                 }, {
265                     "order_by": {"acn": "label"},   /* XXX wrong sorting? */
266                 }
267             );
268         }
269
270         if (typeof item == "undefined") {
271             /* In this case, no further limiting of call numbers for now,
272              * although ideally it might be nice to limit to call numbers
273              * with owning_lib matching the holding_lib of the distribs
274              * that ultimately relate to the items. */
275
276             var menulist = dojo.create("menulist", {
277                 "editable": "true", "className": "cn"
278             });
279             var menupopup = dojo.create("menupopup", null, menulist, "only");
280             this._call_number_cache.forEach(
281                 function(cn) {
282                     dojo.create(
283                         "menuitem", {
284                             "value": cn.id(), "label": cn.label()
285                         }, menupopup, "last"
286                     );
287                 }
288             );
289             return menulist;
290         } else {
291             /* In this case, limit call numbers by owning_lib matching
292              * distributions's holding_lib. */
293
294             var lib = item.stream().distribution().holding_lib().id();
295             if (!this._prepared_call_number_controls[lib]) {
296                 var menulist = dojo.create("menulist", {
297                     "editable": "true", "className": "cn"
298                 });
299                 var menupopup = dojo.create("menupopup", null, menulist,"only");
300                 this._call_number_cache.filter(
301                     function(cn) { return cn.owning_lib() == lib; }
302                 ).forEach(
303                     function(cn) {
304                         dojo.create(
305                             "menuitem", {
306                                 "value": cn.id(), "label": cn.label()
307                             }, menupopup, "last"
308                         );
309                     }
310                 );
311                 this._prepared_call_number_controls[lib] = menulist;
312             }
313             return dojo.clone(this._prepared_call_number_controls[lib]);
314         }
315     };
316
317     this._build_receive_toggle = function(item) {
318         return dojo.create(
319             "checkbox", {
320                 "oncommand": function(ev) {
321                         self._disable_row(item.id(), !ev.target.checked);
322                 },
323                 "checked": "true"
324             }
325         );
326     }
327
328     this._disable_row = function(item_id, disabled) {
329         var row = this.rows[item_id];
330         dojo.query("textbox,menulist", row).forEach(
331             function(element) { element.disabled = disabled; }
332         );
333         this._row_disabled(row, disabled);
334     };
335
336     this._row_disabled = function(row, disabled) {
337         if (typeof(row) == "string") row = this.rows[row];
338
339         var checkbox = dojo.query("checkbox", row)[0];
340
341         if (typeof(disabled) != "undefined")
342             checkbox.checked = !disabled;
343
344         return !checkbox.checked;
345     };
346
347     this._row_field_value = function(row, field, value) {
348         if (typeof(row) == "string") row = this.rows[row];
349
350         var node = dojo.query("*", node_by_name(field, row))[0];
351
352         if (typeof(value) == "undefined")
353             return node.value;
354         else
355             node.value = value;
356     }
357
358         this._user_wants_autogen = function() {
359         return dojo.byId("autogen_barcodes").checked;
360     };
361
362     this._get_autogen_potentials = function(item_id) {
363         var hit_a_wall = false;
364
365         return [openils.Util.objectProperties(this.rows).sort(num_sort).filter(
366             function(id) {
367                 if (hit_a_wall) {
368                     return false;
369                 } else if (id <= item_id || self._row_disabled(id)) {
370                     return false;
371                 } else if (self._row_field_value(id, "barcode")) {
372                     hit_a_wall = true;
373                     return false;
374                 } else {
375                     return true;
376                 }
377             }
378         ), hit_a_wall];
379     };
380
381     this._prepare_autogen_control = function() {
382         dojo.attr("autogen_barcodes",
383             "command", function(ev) {
384                 if (!ev.target.checked) {
385                     var list = self._have_autogen_barcodes();
386                     if (list.length && confirm(S("autogen_barcodes.remove"))) {
387                         list.forEach(
388                             function(id) {
389                                 self._row_field_value(id, "barcode", "");
390                                 self.rows[id]._has_autogen_barcode = false;
391                             }
392                         );
393                     }
394                 }
395             }
396         );
397     };
398
399     this._have_autogen_barcodes = function() {
400         var list = [];
401         for (var id in this.rows)
402             if (this.rows[id]._has_autogen_barcode) list.push(id);
403         return list;
404     };
405
406     this._cn_exists_but_not_for_lib = function(lib, value) {
407         var exists = this._call_number_cache.filter(
408             function(cn) { return cn.label() == value }
409         );
410         var for_lib = exists.filter(
411             function(cn) { return cn.owning_lib() == lib; }
412         );
413         return (exists.length && !for_lib.length);
414     };
415
416     this._call_number_confirm_for_lib = function(lib, value) {
417         if (!this._has_confirmed_cn_for)
418             this._has_confirmed_cn_for = {};
419
420         if (typeof(this._has_confirmed_cn_for[lib.id()]) == "undefined") {
421             if (this._cn_exists_but_not_for_lib(lib.id(), value)) {
422                 this._has_confirmed_cn_for[lib.id()] = confirm(
423                     F("cn_for_lib", [lib.shortname()])
424                 );
425             } else {
426                 this._has_confirmed_cn_for[lib.id()] = true;
427             }
428         }
429
430         return this._has_confirmed_cn_for[lib.id()];
431     }
432
433     this._confirm_row_field_application = function(id, key, value) {
434         if (key == "call_number") { /* XXX make a dispatch table so we can do
435                                        this for other fields too */
436             return this._call_number_confirm_for_lib(
437                 this.item_cache[id].stream().distribution().holding_lib(),
438                 value
439             );
440         } else {
441             return true;
442         }
443     };
444
445     this._set_all_enabled_rows = function(key, value) {
446         /* do NOT do trimming here, set whitespace as is. */
447         for (var id in this.rows) {
448             if (!this._row_disabled(id)) {
449                 if (this._confirm_row_field_application(id, key, value)) {
450                     this._row_field_value(id, key, value);
451                 }
452             }
453         }
454     };
455
456     this.bib_lookup = function(bib_search_term, evt, is_actual_id, sub_id) {
457         if (evt && evt.keyCode != 13) return;
458
459         if (!bib_search_term) {
460             var bib_search_term = dojo.byId("bib_search_term").value.trim();
461             if (!bib_search_term.length) {
462                 alert(S("bib_lookup.empty"));
463                 return;
464             }
465         }
466
467         hide("batch_receive_sub");
468         hide("batch_receive_entry");
469
470         busy(true);
471         dojo.byId("bib_lookup_submit").disabled = true;
472         fieldmapper.standardRequest(
473             ["open-ils.serial",
474                 "open-ils.serial.biblio.record_entry.by_identifier.atomic"], {
475                 "params": [
476                     bib_search_term, {
477                         "require_subscriptions": true,
478                         "add_mvr": true,
479                         "is_actual_id": is_actual_id
480                     }
481                 ],
482                 "async": false,
483                 "oncomplete": function(r) {
484                     /* These two things better come before readResponse(),
485                      * which can throw exceptions. */
486                     busy(false);
487                     dojo.byId("bib_lookup_submit").disabled = false;
488
489                     var list = openils.Util.readResponse(r, false, true);
490                     if (list && list.length) {
491                         if (list.length > 1) {
492                             /* XXX TODO just let the user pick one from a list,
493                              * although this circumstance seems really
494                              * unlikely.  It just can't happen for TCN, and
495                              * wouldn't be likely for ISxN or UPC... ? */
496                             alert(S("bib_lookup.multiple"));
497                         } else {
498                             self.bibdata = list[0];
499                             self._show_bibdata_bits();
500                             self.choose_subscription(sub_id);
501                         }
502                     } else {
503                         alert(S("bib_lookup.not_found"));
504                         if (is_actual_id) {
505                             self.init();
506                         } else {
507                             dojo.byId("bib_search_term").reset();
508                             dojo.byId("bib_search_term").focus();
509                         }
510                     }
511                 }
512             }
513         );
514     };
515
516     this.choose_subscription = function(sub_id) {
517         hide("batch_receive_bib");
518         hide("batch_receive_entry");
519         hide("batch_receive_sub_bits");
520         hide("batch_receive_issuance");
521
522         var subs = this.bibdata.bre.subscriptions();
523
524         if (sub_id) {
525             this.choose_issuance(
526                 subs.filter(function(o) { return o.id() == sub_id; })[0]
527             );
528         } else if (subs.length > 1) {
529             var menulist = dojo.create("menulist", {"id": "sub_chooser"});
530             var menupopup = dojo.create("menupopup", {}, menulist, "only");
531
532             this.bibdata.bre.subscriptions().forEach(
533                 function(sub) {
534                     dojo.create(
535                         "menuitem", {
536                             "label": self._sub_label(sub),
537                             "value": sub.id()
538                         }, menupopup, "last"
539                     );
540                 }
541             );
542
543             hard_empty(dojo.byId("sub_chooser_here"));
544
545             dojo.place(menulist, dojo.byId("sub_chooser_here"), "only");
546             show("batch_receive_sub");
547         } else {
548             this.choose_issuance(subs[0]);
549         }
550     };
551
552     this.choose_issuance = function(sub) {
553         hide("batch_receive_bib");
554         hide("batch_receive_entry");
555         hide("batch_receive_sub");
556
557         if (typeof(sub) == "undefined") {   /* sub chosen from menu */
558             var sub_id = dojo.byId("sub_chooser").value;
559             this.sub = this.bibdata.bre.subscriptions().filter(
560                 function(o) { return o.id() == sub_id; }
561             )[0];
562         } else {    /* only one sub possible, passed in directly */
563             this.sub = sub;
564         }
565
566         this._show_sub_bits();
567
568         this.issuances = this._get_receivable_issuances();   /* sync */
569
570         if (this.issuances.length > 1) {
571             var menulist = dojo.create("menulist", {"id": "issuance_chooser"});
572             var menupopup = dojo.create("menupopup", {}, menulist, "only");
573
574             this.issuances.sort(
575                 function(a, b) {
576                     if (a.date_published()>b.date_published()) return 1;
577                     else if (b.date_published()>a.date_published()) return -1;
578                     else return 0;
579                 }
580             ).forEach(
581                 function(issuance) {
582                     dojo.create(
583                         "menuitem", {
584                             "label": issuance.label(),
585                             "value": issuance.id()
586                         }, menupopup, "last"
587                     );
588                 }
589             );
590
591             hard_empty("issuance_chooser_here");
592             dojo.place(menulist, dojo.byId("issuance_chooser_here"), "only");
593
594             show("batch_receive_issuance");
595         } else if (this.issuances.length) {
596             this.load_entry_form(this.issuances[0]);
597         } else {
598             alert(S("issuance_lookup.none"));
599             this.init();
600         }
601
602     };
603
604     this.load_entry_form = function(issuance) {
605         if (typeof(issuance) == "undefined") {
606             var issuance_id = dojo.byId("issuance_chooser").value;
607             this.issuance = this.issuances.filter(
608                 function(o) { return o.id() == issuance_id; }
609             )[0];
610         } else {
611             this.issuance = issuance;
612         }
613
614         this._show_issuance_bits();
615         this._prepare_autogen_control();
616
617         busy(true);
618
619         fieldmapper.standardRequest(
620             ["open-ils.serial",
621                 "open-ils.serial.items.receivable.by_issuance.atomic"], {
622                 "params": [this.authtoken, this.issuance.id()],
623                 "async": true,
624                 "onresponse": function(r) {
625                     busy(false);
626
627                     if (list = openils.Util.readResponse(r, false, true)) {
628
629                         if (list.length) {
630                             busy(true);
631                             show("form_holder");
632
633                             list.forEach(function(o) {self.add_entry_row(o);});
634
635                             self.build_batch_entry_row();
636                             dojo.byId("batch_receive_with_units").doCommand();
637
638                             show("batch_receive_entry");
639                             busy(false);
640                         } else {
641                             alert(S("item_lookup.none"));
642                             if (self.issuances.length) self.choose_issuance();
643                             else self.init();
644                         }
645                     }
646                 }
647             }
648         );
649     };
650
651     this.toggle_receive_with_units = function(ev) {
652         var head_row = dojo.byId("batch_receive_entry_thead");
653         var batch_row = dojo.byId("entry_batch_row");
654
655         var fields = [
656             "barcode", "call_number", "price", "location", "circ_modifier"
657         ];
658
659         var table_cell_func = ev.target.checked ?
660             show_table_cell : hide_table_cell;
661         fields.forEach(
662             function(key) {
663                 if (batch_row) table_cell_func(node_by_name(key, batch_row));
664                 if (head_row) table_cell_func(node_by_name(key, head_row));
665
666                 for (var id in self.rows) {
667                     table_cell_func(node_by_name(key, self.rows[id]));
668                 }
669             }
670         );
671
672         if (!ev.target.checked) {
673             /* XXX As of the time of this writing, a blank barcode field will
674              * avoid unit creation */
675             this._set_all_enabled_rows("barcode", "");
676         }
677     };
678
679     this.toggle_all_receive = function(checked) {
680         for (var id in this.rows) {
681             this._disable_row(id, !checked);
682         }
683     };
684
685     this.build_batch_entry_row = function() {
686         var row = dojo.byId("entry_batch_row");
687
688         this.batch_controls = {};
689
690         node_by_name("note", row).appendChild(
691             this.batch_controls.note = dojo.create("textbox", {"size": 20})
692         );
693
694         node_by_name("location", row).appendChild(
695             this.batch_controls.location = this._build_location_dropdown(
696                 /* XXX TODO build a smarter list. rather than all copy locs
697                  * under OU #1, try building a list of copy locs available to
698                  * all OUs represented in actual items */
699                 this._get_locations_for_lib(1),
700                 true /* add_unset_value */
701             )
702         );
703
704         node_by_name("circ_modifier", row).appendChild(
705             this.batch_controls.circ_modifier =
706                 this._extend_circ_modifier_for_batch(
707                     this._build_circ_modifier_dropdown() /* for all OUs */
708                 )
709         );
710
711         node_by_name("call_number", row).appendChild(
712             this.batch_controls.call_number = this._build_call_number_control()
713         );
714
715         node_by_name("price", row).appendChild(
716             this.batch_controls.price = dojo.create("textbox", {"size": 9})
717         );
718
719         node_by_name("receive", row).appendChild(
720             dojo.create(
721                 "checkbox", {
722                     "oncommand": function(ev) {
723                         self.toggle_all_receive(ev.target.checked);
724                     },
725                     "checked": "true"
726                 }
727             )
728         );
729
730         node_by_name("apply", row).appendChild(
731             dojo.create("button", {
732                 "label": S("apply"),
733                 "oncommand": function() { self.apply_batch_values(); }
734             })
735         );
736     };
737
738     this.apply_batch_values = function() {
739         var row = dojo.byId("entry_batch_row");
740
741         for (var key in this.batch_controls) {
742             var value = this.batch_controls[key].value;
743             if (value != "" && value != -1)
744                 this._set_all_enabled_rows(key, value);
745         }
746
747         /* XXX genericize for all fields? */
748         delete this._has_confirmed_cn_for;
749     };
750
751     this.add_entry_row = function(item) {
752         this.item_cache[item.id()] = item;
753         var row = this.rows[item.id()] = dojo.clone(this.template);
754
755         function n(s) { return node_by_name(s, row); }    /* typing saver */
756
757         n("holding_lib").appendChild(
758             T(item.stream().distribution().holding_lib().shortname())
759         );
760
761         n("barcode").appendChild(
762             dojo.create(
763                 "textbox", {
764                     "size": 15,
765                     "tabindex": 10000 + Number(item.id()), /* is this right? */
766                     "onchange": function() {
767                         self.autogen_if_appropriate(this, item.id());
768                     }
769                 }
770             )
771         );
772
773         n("location").appendChild(
774             this._build_location_dropdown(
775                 this._get_locations_for_lib(
776                     item.stream().distribution().holding_lib().id()
777                 )
778             )
779         );
780
781         n("note").appendChild(dojo.create("textbox", {"size": 20}));
782         n("circ_modifier").appendChild(this._build_circ_modifier_dropdown());
783         n("call_number").appendChild(this._build_call_number_control(item));
784         n("price").appendChild(dojo.create("textbox", {"size": 9}));
785         n("receive").appendChild(this._build_receive_toggle(item));
786
787         this.entry_tbody.appendChild(row);
788     };
789
790     this.receive = function() {
791         var items = [];
792         var confirmed_missing_units = false;
793
794         for (var id in this.rows) {
795             if (this._row_disabled(id))
796                 continue;
797
798             var item = this.item_cache[id];
799
800             /* Don't trim() call_number field, as existing call numbers
801              * are yielded by their label field, not by id, and if
802              * they start or end in spaces, we'll unintentionally create
803              * a new, different CN if we trim that */
804             var cn_string = this._row_field_value(id, "call_number");
805             var barcode = this._row_field_value(id, "barcode").trim();
806
807             if (barcode && cn_string.length) {
808                 var unit = new sunit();
809                 unit.barcode(barcode);
810
811                 ["price", "location", "circ_modifier"].forEach(
812                     function(field) {
813                         var value = self._row_field_value(id, field).trim();
814                         if (value) unit[field](value);
815                     }
816                 );
817
818                 unit.call_number(cn_string);
819                 item.unit(unit);
820             } else if (barcode && !cn_string.length) {
821                 alert(S("missing_cn"));
822                 return;
823             } else if (!confirmed_missing_units) {
824                 if (
825                     (!dojo.byId("batch_receive_with_units").checked) ||
826                     confirm(S("missing_units"))
827                 ) {
828                     confirmed_missing_units = true;
829                 } else {
830                     return;
831                 }
832             }
833
834             var note_value = this._row_field_value(id, "note").trim();
835             if (note_value) {
836                 var note = new sin();
837                 note.item(id);
838                 note.pub(false);
839                 note.title(S("receive_time_note"));
840                 note.value(note_value);
841
842                 item.notes([note]);
843             }
844
845             items.push(item);
846         }
847
848         busy(true);
849         fieldmapper.standardRequest(
850             ["open-ils.serial", "open-ils.serial.receive_items.one_unit_per"],{
851                 "params": [this.authtoken, items, this.sub.record_entry()],
852                 "async": true,
853                 "oncomplete": function(r) {
854                     try {
855                         while (item_id = openils.Util.readResponse(r))
856                             self.finish_receipt(item_id);
857                     } catch (E) {
858                         alert(E);
859                     }
860                     busy(false);
861                 }
862             }
863         );
864     };
865
866     this.finish_receipt = function(item_id) {
867         hard_empty(this.rows[item_id]);
868         dojo.destroy(this.rows[item_id]);
869         delete this.rows[item_id];
870         delete this.item_cache[item_id];
871     };
872
873     this.autogen_if_appropriate = function(textbox, item_id) {
874         if (this._user_wants_autogen() && textbox.value) {
875             var [list, question] = this._get_autogen_potentials(item_id);
876             if (list.length) {
877                 if (question && !confirm(S("autogen_barcodes.questionable")))
878                     return;
879
880                 busy(true);
881                 try {
882                     fieldmapper.standardRequest(
883                         ["open-ils.cat", "open-ils.cat.item.barcode.autogen"], {
884                             "params": [
885                                 this.authtoken, textbox.value, list.length
886                             ],
887                             "async": false,
888                             "onresponse": function(r) {
889                                 r = openils.Util.readResponse(r, false, true);
890                                 if (r) {
891                                     for (var i = 0; i < r.length; i++) {
892                                         var row = self.rows[list[i]];
893                                         self._row_field_value(
894                                             row, "barcode", r[i]
895                                         );
896                                         row._has_autogen_barcode = true;
897                                     }
898                                 }
899                             }
900                         }
901                     );
902                 } catch (E) {
903                     alert(E);
904                 }
905                 busy(false);
906             } /* do nothing for empty list */
907         }
908     };
909
910     this.init.apply(this, arguments);
911 }
912
913 function my_init() {
914     var cgi = new openils.CGI();
915
916     batch_receiver = new BatchReceiver(
917         (typeof ses == "function" ? ses() : 0) ||
918             cgi.param("ses") || dojo.cookie("ses"),
919         cgi.param("docid") || null, cgi.param("subid") || null
920     );
921 }