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