]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/booking/populator.js
1eeb7cbfe7b9d89e0f553389029cc3fd831591cc
[Evergreen.git] / Open-ILS / web / js / ui / default / booking / populator.js
1 /* This module depends on common.js being loaded, as well as the
2  * localization (Dojo/nls) for pickup and return . */
3
4 dojo.require("dojo.data.ItemFileReadStore");
5 dojo.require("dojo.date.locale");
6 dojo.require("openils.PermaCrud");
7 dojo.require("dojo.string");
8
9 function Populator(widgets, primary_input) {
10     this.widgets = widgets;
11
12     this.all = [];
13     for (var k in widgets) this.all.push(k);
14
15     if (primary_input) this.primary_input = primary_input;
16
17     this.prepare_cache();
18     this.prepare_empty_stores();
19     this.reset();
20 }
21 Populator.prototype.prepare_cache = function(data) {
22     this.cache = {};
23     for (var k in this.all) this.cache[this.all[k]] = {};
24 };
25 Populator.prototype.prepare_empty_stores = function(data) {
26     this.empty_stores = {};
27
28     for (var i in this.all) {
29         var name = this.all[i];
30
31         if (this.widgets[name] && this["flatten_" + name]) {
32             this.empty_stores[name] =
33                 new dojo.data.ItemFileReadStore({
34                     "data": this["flatten_" + name]([])
35                 });
36             this.widgets[name].setStore(this.empty_stores[name]);
37         }
38     }
39 };
40 Populator.prototype.flatten_ready = function(data) {
41     return {
42         "label": "id",
43         "identifier": "id",
44         "items": data.map(function(o) {
45             return {
46                 "id": o.id(),
47                 "type": o.target_resource_type().name(),
48                 "resource": o.current_resource().barcode(),
49                 "start_time": humanize_timestamp_string(o.start_time()),
50                 "end_time": humanize_timestamp_string(o.end_time())
51             };
52         })
53     };
54 };
55 Populator.prototype.flatten_out = function(data) {
56     return {
57         "label": "id",
58         "identifier": "id",
59         "items": data.map(function(o) {
60             return {
61                 "id": o.id(),
62                 "type": o.target_resource_type().name(),
63                 "resource": o.current_resource().barcode(),
64                 "pickup_time": humanize_timestamp_string(o.pickup_time()),
65                 "end_time": humanize_timestamp_string(o.end_time())
66             };
67         })
68     };
69 };
70 Populator.prototype.flatten_in = function(data) {
71     return {
72         "label": "id",
73         "identifier": "id",
74         "items": data.map(function(o) {
75             return {
76                 "id": o.id(),
77                 "type": o.target_resource_type().name(),
78                 "resource": o.current_resource().barcode(),
79                 "due_time": humanize_timestamp_string(o.end_time()),
80                 "return_time": humanize_timestamp_string(o.return_time())
81             };
82         })
83     };
84 };
85 Populator.prototype.reveal_container = function(widget) {
86     var el = document.getElementById("contains_" + widget.id);
87     if (el) reveal_dom_element(el);
88 };
89 Populator.prototype.hide_container = function(widget) {
90     var el = document.getElementById("contains_" + widget.id);
91     if (el) hide_dom_element(el);
92 };
93 Populator.prototype.populate_ready = function(data) {
94     return this._populate_any_resv_grid(data, "ready");
95 };
96 Populator.prototype.populate_out = function(data) {
97     return this._populate_any_resv_grid(data, "out");
98 };
99 Populator.prototype.populate_in = function(data) {
100     return this._populate_any_resv_grid(data, "in");
101 };
102 Populator.prototype._populate_any_resv_grid = function(data, which) {
103     var flattener = this["flatten_" + which];
104     var widget = this.widgets[which];
105     var cache = this.cache[which];
106     var empty_store = this.empty_stores[which];
107
108     this.reveal_container(widget);
109
110     if (!data || !data.length) {
111         widget.setStore(empty_store);
112         this.toggle_anyness(false, which);
113     } else {
114         for (var i in data) cache[data[i].id()] = data[i];
115
116         widget.setStore(
117             new dojo.data.ItemFileReadStore({"data": flattener(data)})
118         );
119
120         this.toggle_anyness(true, which);
121
122         /* Arrrgh! Horrid but necessary: */
123         setTimeout(function() { widget.sort(); }, 100);
124     }
125 };
126 Populator.prototype.populate_patron = function(data) {
127     var h2 = document.createElement("h2");
128     h2.setAttribute("class", "booking");
129     h2.appendChild(document.createTextNode(formal_name(data)));
130
131     this.widgets.patron.innerHTML = "";
132     this.widgets.patron.appendChild(h2);
133
134     this.reveal_container(this.widgets.patron);
135     /* Maybe add patron's home OU or something here later... */
136 };
137 Populator.prototype.return_by_resource = function(barcode, override) {
138     /* XXX instead of talking to the server every time we do this, we could
139      * also check the "out" cache, iff we have one.  */
140     var r = fieldmapper.standardRequest(
141         ["open-ils.booking",
142         "open-ils.booking.reservations.by_returnable_resource_barcode"],
143         [openils.User.authtoken, barcode]
144     );
145     if (!r || r.length < 1) {
146         alert(localeStrings.NO_SUCH_RETURNABLE_RESOURCE);
147     } else if (is_ils_event(r)) {
148         alert(my_ils_error(localeStrings.RETURNABLE_RESOURCE_ERROR, r));
149     } else {
150         try {
151             var new_barcode = r.usr().card().barcode();
152         } catch (E) {
153             alert(localeStrings.RETURN_ERROR + "\nr: " + js2JSON(r) + "\n" + E);
154             return;
155         }
156         if (this.patron_barcode && this.patron_barcode != new_barcode) {
157             /* XXX make this more subtle, i.e. flash something in background */
158             alert(localeStrings.NOTICE_CHANGE_OF_PATRON);
159         }
160         this.patron_barcode = new_barcode;
161         var ret = this.return(r, override);
162         if (!ret) {
163             alert(localeStrings.RETURN_NO_RESPONSE);
164         } else if (is_ils_event(ret) && ret.textcode != "SUCCESS") {
165             if (ret.textcode == "ROUTE_ITEM") {
166                 display_transit_slip(ret);
167             } else if (ret.textcode == "COPY_ALERT_MESSAGE") {
168                 if (
169                     confirm(
170                         dojo.string.substitute(
171                             localeStrings.COPY_ALERT, [ret.desc, ret.payload]
172                        )
173                     )
174                 ) {
175                     this.return_by_resource(barcode, true /*override*/);
176                     return;
177                 }
178             } else {
179                 alert(my_ils_error(localeStrings.RETURN_ERROR, ret));
180             }
181         } else {
182             /* XXX speedbump should go, but something has to happen else
183              * there's no indication to staff that anything happened when
184              * starting from a fresh (blank) return interface.
185              */
186             alert(localeStrings.RETURN_SUCCESS);
187         }
188         this.populate(); /* Won't recurse with no args. All is well. */
189     }
190 };
191 Populator.prototype.populate = function(barcode, which) {
192     if (barcode) {
193         if (barcode.patron) {
194             this.patron_barcode = barcode.patron;
195         }
196         else if (barcode.resource) { /* resource OR patron, not both */
197             if (!this.return_by_resource(barcode.resource))
198                 return;
199         }
200     }
201     if (!this.patron_barcode) {
202         alert(localeStrings.NO_PATRON_BARCODE);
203         return;
204     }
205
206     if (!which) which = this.all;
207
208     var result = fieldmapper.standardRequest(
209         ["open-ils.booking", "open-ils.booking.reservations.get_captured"],
210         [openils.User.authtoken, this.patron_barcode, which]
211     );
212
213     if (!result) {
214         this.patron_barcode = undefined;
215         alert(localeStrings.RESERVATIONS_NO_RESPONSE);
216     } else if (is_ils_event(result)) {
217         this.patron_barcode = undefined;
218         alert(my_ils_error(localeStrings.RESERVATIONS_ERROR, result));
219     } else {
220         for (var k in result)
221             this["populate_" + k](result[k]);
222     }
223 };
224 Populator.prototype.toggle_anyness = function(any, which) {
225     var widget = this.widgets[which].domNode;
226     var empty_alternate = document.getElementById("no_" + widget.id);
227     var controls = document.getElementById("controls_" + widget.id);
228     if (any) {
229         reveal_dom_element(widget);
230         if (empty_alternate) hide_dom_element(empty_alternate);
231         if (controls) reveal_dom_element(controls);
232     } else {
233         hide_dom_element(widget);
234         if (empty_alternate) reveal_dom_element(empty_alternate);
235         if (controls) hide_dom_element(controls);
236     }
237 };
238 Populator.prototype.pickup = function(reservation) {
239     return fieldmapper.standardRequest(
240         ["open-ils.circ", "open-ils.circ.reservation.pickup"],
241         [openils.User.authtoken, {
242             "patron_barcode": this.patron_barcode,
243             "reservation": reservation
244         }]
245     );
246 };
247 Populator.prototype.return = function(reservation, override) {
248     var method = "open-ils.circ.reservation.return";
249     if (override) method += ".override";
250     return fieldmapper.standardRequest(
251         ["open-ils.circ", method],
252         [openils.User.authtoken, {
253             "patron_barcode": this.patron_barcode,
254             "reservation": reservation.id()
255             /* yeah just id here ------^; lack of parallelism */
256         }]
257     );
258 };
259 Populator.prototype.act_on_selected = function(how, which) {
260     var widget = this.widgets[which];
261     var cache = this.cache[which];
262     var no_response_msg = localeStrings[how.toUpperCase() + "_NO_RESPONSE"];
263     var error_msg = localeStrings[how.toUpperCase() + "_ERROR"];
264
265     var selected_id_list =
266         widget.selection.getSelected().map(function(o) { return o.id[0]; });
267
268     if (!selected_id_list || !selected_id_list.length) {
269         alert(localeStrings.SELECT_SOMETHING);
270         return;
271     }
272
273     var reservations = selected_id_list.map(function(o) { return cache[o]; });
274
275     /* Do we have to process these one at a time?  I think so... */
276     var self = this;
277     function looper(reservation, override) {
278         if (looper._done) return;
279         var result = self[how](reservation, override);
280         if (!result) {
281             alert(no_response_msg);
282         } else if (is_ils_event(result) && result.textcode != "SUCCESS") {
283             if (result.textcode == "ROUTE_ITEM") {
284                 display_transit_slip(result);
285             } else if (result.textcode == "COPY_ALERT_MESSAGE") {
286                 if (confirm(
287                     dojo.string.substitute(
288                         localeStrings.COPY_ALERT, [result.desc, result.payload]
289                    )
290                 )) {
291                     looper(reservation, true);
292                 }
293                 return; // continues processing other resvs
294             } else {
295                 alert(my_ils_error(error_msg, result));
296             }
297         } else {
298             return;
299         }
300         looper._done = true;
301     }
302     dojo.forEach(reservations, looper);
303
304     this.populate();
305 };
306 Populator.prototype.reset = function() {
307     for (var k in this.widgets) {
308         this.hide_container(this.widgets[k]);
309     }
310     this.patron_barcode = undefined;
311
312     if (typeof(this._extra_resetting) == "function")
313         this._extra_resetting();
314
315     if (this.primary_input) {
316         this.primary_input.value = "";
317         this.primary_input.focus();
318     }
319 };
320
321 /* XXX needs to be combined with the code that shows transit slips in the
322  * booking capture interface. */
323 function display_transit_slip(e) {
324     var ou = fieldmapper.aou.findOrgUnit(e.org, /* slim_ok */false);
325     var ma = (new openils.PermaCrud()).retrieve("aoa", ou.mailing_address());
326     var mas = ma ?
327         dojo.string.substitute(
328             localeStrings.ADDRESS,
329             [ma.street1(),ma.street2(),ma.city(),ma.state(),ma.post_code()].map(
330                 function(o) { return o ? o : ""; }
331             )
332         ).replace("\n\n", "\n").replace("\n", "<br />") : "[Unknown address]";
333     /* XXX i18n and/or template */
334     try {
335         var win = window.open(
336             "","","resizeable,width=600,height=400,scrollbars=1"
337         );
338         win.document.body.innerHTML =
339             "<h1>Transit Slip</h1>\n" +
340             //"<img src='/xul/server/skin/media/images/turtle.gif' />\n" +
341             "<p>Destination: <strong>" + ou.name() + "</strong></p>\n" +
342             "<p>" + mas + "</p>\n" +
343             "<p>Barcode: " + e.payload.copy.barcode() + "<br />\n" +
344             "Title: <span id='title'></span><br />\n" +
345             "Author: <span id='author'></span><br />\n" +
346             "Slip Date: " +
347                 dojo.date.locale.format(new Date(), {"formatLength": "short"}) +
348             "</p>";
349         fieldmapper.standardRequest(
350             ["open-ils.search", "open-ils.search.biblio.mods_from_copy"], {
351                 "params": [e.payload.copy.id()],
352                 "async": true,
353                 "onresponse": function(r) {
354                     var mvr = openils.Util.readResponse(r);
355                     dojo.byId("title", win.document).innerHTML = mvr.title();
356                     dojo.byId("author", win.document).innerHTML = mvr.author();
357                 },
358                 "oncomplete": function() {
359                     win[confirm("Print transit slip?") ? "print" : "close"]();
360                 }
361             }
362         );
363     } catch (E) {
364         alert("exception rendering transit slip: " + E); // XXX
365     }
366 }