]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/acq/invoice/receive.js
It's a lineitem detail (copy) batch receiver
[Evergreen.git] / Open-ILS / web / js / ui / default / acq / invoice / receive.js
1 dojo.require("dijit.form.Button");
2 dojo.require("dijit.form.NumberSpinner");
3 dojo.require("openils.PermaCrud");
4 dojo.require("openils.acq.Lineitem");
5 dojo.require("openils.widget.AutoFieldWidget");
6 dojo.require("openils.widget.ProgressDialog");
7 dojo.requireLocalization("openils.acq", "acq");
8
9 var copy_table;
10 var localeStrings = dojo.i18n.getLocalization("openils.acq", "acq");
11
12 function ReceivableCopyTable() {
13     var self = this;
14
15     this._init = function() {
16         this.columns = ["owning_lib", "location", "collection_code",
17             "circ_modifier", "fund", "cn_label", "barcode"];
18
19         this.tbody = dojo.byId("rows-here");
20         this.pcrud = new openils.PermaCrud();
21
22         this.mode = "number";   /* can be "number" or "list" */
23         this.some_receiving_done = false;
24
25         this._init_select_all();
26     };
27
28     this._init_select_all = function() {
29         dojo.byId("select_all").onchange = function() {
30             var checked = this.checked;
31             dojo.query("input[type='checkbox']", self.tbody).forEach(
32                 function(cb) { cb.checked = checked; }
33             );
34         };
35     };
36
37     this._set_invoice_header = function() {
38         dojo.byId("inv-header").innerHTML = dojo.string.substitute(
39             localeStrings.INVOICE_NUMBER, [this.invoice.inv_ident()]
40         );
41     };
42
43     this._configure_for_mode = function() {
44         if (this.mode == "list") {
45             openils.Util.show("list-mode-headings", "table-header-group");
46             openils.Util.hide("set-list-mode");
47             openils.Util.show("set-number-mode");
48             dojo.byId("set-number-mode-link").onclick = function() {
49                 self.reset("number");
50                 self.load();
51             };
52         } else { /* number */
53             openils.Util.hide("list-mode-headings");
54             openils.Util.show("set-list-mode");
55             openils.Util.hide("set-number-mode");
56             dojo.byId("set-list-mode-link").onclick = function() {
57                 self.reset("list");
58                 self.load();
59             };
60         }
61     };
62
63     this._get_receivable_details = function(li) {
64         return li.lineitem_details().filter(
65             function(lid) { return (!lid.recv_time() && !lid.cancel_reason()); }
66         );
67     };
68
69     this._create_receiver = function(lid, tr, precheck) {
70         var args = {
71             "type": "checkbox",
72             "name": "receive",
73             "value": lid.id()
74         };
75
76         if (precheck) args.checked = "checked";
77
78         dojo.create("input", args, dojo.create("td", null, tr));
79     };
80
81     this._get_selected_list_mode = function() {
82         return dojo.query("input[type=checkbox]", this.tbody).filter(
83             function(cb) { return cb.checked; }
84         ).map(
85             function(cb) { return cb.value; }
86         );
87     };
88
89     this._get_selected_number_mode = function() {
90         var list = [];
91         for (var li_id in this.spinners) {
92             var spinner = this.spinners[li_id];
93             var li = spinner._li;
94
95             var number = spinner.attr("value");
96             list = list.concat(
97                 this._get_receivable_details(li).slice(0, number)
98             );
99         }
100         return list.map(function(lid) { return lid.id(); });
101     };
102
103     /* The first time this interface is loaded, use the phys_item_count field
104      * (the "# paid" column on an invoice) to determing how man items to
105      * preselect.  Otherwise use 0.
106      */
107     this._number_to_preselect = function(ie, li) {
108         return (this.some_receiving_done) ? 0 :
109             Number(ie.phys_item_count() || 0);
110
111 //        var n = Number(ie.phys_item_count() || 0) -
112 //            li.lineitem_details().filter(
113 //                function(lid) {
114 //                    return lid.recv_time() || lid.cancel_reason()
115 //                }
116 //            ).length;
117 //
118 //        return n > 0 ? n : 0;
119     };
120
121     this._add_lineitem_number_mode = function(details, li, preselect_count) {
122         var tr = dojo.create("tr", null, this.tbody);
123         var td = dojo.create("td", {
124             "colspan": 1 + this.columns.length,
125             "className": "spinner-cell"
126         }, tr);
127
128         var span_id = "number-mode-li-" + li.id();
129
130         td.innerHTML = localeStrings.COPIES_TO_RECEIVE;
131         dojo.create("span", {"id": span_id}, td);
132
133         var max = details.length;
134         var value = (preselect_count <= max ? preselect_count : max);
135
136         this.spinners[li.id()] = new dijit.form.NumberSpinner({
137             "constraints": {"min": 0, "max": max},
138             "value": value
139         }, span_id);
140         this.spinners[li.id()]._li = li;
141     };
142
143     this._add_lineitem_list_mode = function(details, li, preselect_count) {
144         details.forEach(
145             function(lid) {
146                 dump("preselect_count "+ preselect_count+"\n");
147                 self.add_lineitem_detail(
148                     lid, li, Boolean(preselect_count-- > 0)
149                 );
150             }
151         );
152     };
153
154     this.add_lineitem_detail = function(lid, li, precheck) {
155         var tr = dojo.create(
156             "tr", {"className": "copy-row"}, this.tbody
157         );
158
159         /* Make receive checkbox cell. */
160         this._create_receiver(lid, tr, precheck);
161
162         /* Make cells for all the other columns.  Using a read-only
163          * AutoFieldWidget to show the value of each field on a lineitem
164          * detail is much easier than worrying about fleshing enough
165          * information to do the same ourselves. */
166         this.columns.forEach(
167             function(column) {
168                 var td = dojo.create("td", null, tr);
169                 new openils.widget.AutoFieldWidget({
170                     "parentNode": dojo.create("div", null, td),
171                     "fmField": column,
172                     "fmObject": lid,
173                     "readOnly": true,
174                     "dijitArgs": {"labelType": (column=='fund') ? "html" : null}
175                 }).build();
176             }
177         );
178     };
179
180     /* /maybe/ add a lineitem to the table, if it has any lineitem details
181      * that are still receivable, and preselect lineitem details up to the
182      * number specified in ie.phys_item_count() */
183     this.add_lineitem = function(ie, li, displayHTML) {
184         var receivable_details = this._get_receivable_details(li);
185         if (!receivable_details.length) return;
186
187         /* show lineitem overall description */
188         /* add rows for copies (lineitem details) */
189         dojo.create(
190             "td", {
191                 "colspan": 1 + this.columns.length,
192                 "innerHTML": displayHTML
193             }, dojo.create("tr", null, this.tbody)
194         );
195
196         /* build look-up table */
197         receivable_details.forEach(
198             function(lid) { self.li_by_lid[lid.id()] = li; }
199         );
200
201         /* Render something for receiving the lineitem details, depending
202          * on mode. */
203         this["_add_lineitem_" + this.mode + "_mode"](
204             receivable_details, li, this._number_to_preselect(ie, li)
205         );
206     };
207
208     this.reset = function(mode) {
209         if (mode)
210             this.mode = mode;
211
212         this.user_has_acked = [];
213         this.li_by_lid = {};
214
215         if (this.spinners) {
216             for (var key in this.spinners)
217                 this.spinners[key].destroy();
218         }
219
220         this.spinners = {};
221
222         this._configure_for_mode();
223
224         dojo.empty(this.tbody);
225     };
226
227     /* It's important to remember that an invoice doesn't actually have
228      * lineitems, but rather is made up of invoice entries and invoice items.
229      * Invoice entries usually link to lineitems, though (invoice items
230      * usually link to po_items).
231      */
232     this.load = function(inv_id) {
233         if (inv_id)
234             this.inv_id = inv_id;
235
236         this.reset();
237         progress_dialog.show(true);
238
239         if (!this.invoice) {
240             this.invoice = this.pcrud.retrieve("acqinv", this.inv_id);
241             this._set_invoice_header();
242         }
243
244         this.pcrud.search("acqie", {"invoice": this.inv_id}).forEach(
245             function(entry) {
246                 if (entry.lineitem()) {
247                     openils.acq.Lineitem.fetchAndRender(
248                         entry.lineitem(),
249                         {"flesh_li_details": true, "flesh_notes": true},
250                         function(li, str) { self.add_lineitem(entry, li, str); }
251                     );
252                 }
253             }
254         );
255
256         if (openils.Util.objectProperties(this.li_by_lid).length) {
257             openils.Util.show("non-empty");
258             openils.Util.hide("empty");
259         } else {
260             openils.Util.hide("non-empty");
261             openils.Util.show("empty");
262         }
263         progress_dialog.hide();
264     };
265
266     /* returns an array of lineitem_detail IDs */
267     this.get_selected = function() {
268         return this["_get_selected_" + this.mode + "_mode"]();
269     };
270
271     this.receive_lineitem_detail = function(id_list, index) {
272         if (index >= id_list.length) {
273             progress_dialog.hide();
274             this.load();
275
276             return;
277         }
278
279         var lid_id = id_list[index];
280         var li = this.li_by_lid[lid_id];
281
282         if (!this.check_lineitem_alerts(li)) {
283             self.receive_lineitem_detail(id_list, ++index);
284             return;
285         }
286
287         fieldmapper.standardRequest(
288             ["open-ils.acq", "open-ils.acq.lineitem_detail.receive"], {
289                 "async": false,
290                 "params": [openils.User.authtoken, lid_id],
291                 "oncomplete": function(r) {
292                     if (r = openils.Util.readResponse(r)) {
293                         self.some_receiving_done = true;
294                         /* receive the next lid in our list */
295                         self.receive_lineitem_detail(id_list, ++index);
296                     }
297                 }
298             }
299         );
300     };
301
302     this.receive_selected = function() {
303         var lid_ids = this.get_selected();
304
305         progress_dialog.show(true);
306
307         this.receive_lineitem_detail(lid_ids, 0);
308     };
309
310     /* 1st of 2 functions all but copied from li_table.js. Refactor this and
311      * that to share code from a 3rd place.
312      */
313     this.check_lineitem_alerts = function(lineitem) {
314         var alert_notes = lineitem.lineitem_notes().filter(
315             function(o) { return Boolean(o.alert_text()); }
316         );
317
318         for (var i = 0; i < alert_notes.length; i++) {
319             if (this.user_has_acked[alert_notes[i].id()])
320                 continue;
321             else if (!this.confirm_alert(li, alert_notes[i]))
322                 return false;
323             else
324                 this.user_has_acked[alert_notes[i].id()] = true;
325         }
326
327         return true;
328     };
329
330     /* 2nd of 2 functions all but copied from li_table.js. Refactor this and
331      * that to share code from a 3rd place.
332      */
333     this.confirm_alert = function(lineitem, note) {
334         return confirm(
335             dojo.string.substitute(
336                 localeStrings.CONFIRM_LI_ALERT, [
337                     (new openils.acq.Lineitem({"lineitem": lineitem})).findAttr(
338                         "title", "lineitem_marc_attr_definition"
339                     ),
340                     note.alert_text().code(),
341                     note.alert_text().description() || "",
342                     note.value()
343                 ]
344             )
345         );
346     };
347
348     this._init.apply(this, arguments);
349 }
350
351 function my_init() {
352     copy_table = new ReceivableCopyTable();
353     copy_table.load(inv_id);
354 }
355
356 openils.Util.addOnLoad(my_init);