]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/acq/po/view_po.js
added selector column for edi_account; added edi messages page for POs; allow editpan...
[working/Evergreen.git] / Open-ILS / web / js / ui / default / acq / po / view_po.js
1 dojo.require("dojo.string");
2 dojo.require('dijit.layout.ContentPane');
3 dojo.require('openils.PermaCrud');
4
5 var pcrud = new openils.PermaCrud();
6 var PO = null;
7 var liTable;
8 var poItemTable;
9 var poNoteTable;
10 var invoiceLinkDialogManager;
11
12 function AcqPoNoteTable() {
13     var self = this;
14
15     this.notesTbody = dojo.byId("acq-po-notes-tbody");
16     this.notesRow = this.notesTbody.removeChild(dojo.byId("acq-po-notes-row"));
17
18     dojo.byId("acq-po-notes-back-button").onclick = function() { self.hide(); };
19     dojo.byId("acq-po-view-notes").onclick = function() { self.show(); };
20
21     /* widgets' event properties are cased likeThis */
22     acqPoCreateNoteSubmit.onClick = function() {
23         if (!acqPoCreateNoteText.attr("value")) return;
24
25         /* prep new note */
26         var note = new acqpon();
27         note.vendor_public(
28             Boolean(acqPoCreateNoteVendorPublic.attr('checked'))
29         );
30         note.value(acqPoCreateNoteText.attr("value"));
31         note.purchase_order(PO.id());
32         note.isnew(true);
33
34         /* save it */
35         self.updatePoNotes(note);
36
37         /* reset fields for next use */
38         acqPoCreateNoteText.attr("value", "");
39         acqPoCreateNoteVendorPublic.attr("checked", false);
40     };
41
42     this.drawPoNote = function(note) {
43         if (note.isdeleted())
44             return;
45
46         var row = dojo.clone(this.notesRow);
47
48         nodeByName("value", row).innerHTML = note.value();
49
50         if (openils.Util.isTrue(note.vendor_public()))
51             nodeByName("vendor_public", row).innerHTML =
52                 localeStrings.VENDOR_PUBLIC;
53
54         nodeByName("delete", row).onclick = function() {
55             note.isdeleted(true);
56             self.notesTbody.removeChild(row);
57             self.updatePoNotes();
58         };
59
60         if (note.edit_time()) {
61             nodeByName("edit_time", row).innerHTML =
62                 dojo.date.locale.format(
63                     dojo.date.stamp.fromISOString(note.edit_time()),
64                     {"formatLength": "short"}
65                 );
66         }
67
68         self.notesTbody.appendChild(row);
69     };
70
71     this.drawPoNotes = function() {
72         /* sort */
73         PO.notes(
74             PO.notes().sort(
75                 function(a, b) {
76                     return (a.edit_time() < b.edit_time()) ? 1 : -1;
77                 }
78             )
79         );
80
81         /* remove old renderings of notes */
82         dojo.empty(this.notesTbody);
83
84         PO.notes().forEach(function(o) { self.drawPoNote(o); });
85     };
86
87     this.updatePoNotesCount = function() {
88         dojo.byId("acq-po-view-notes").innerHTML = PO.notes().length;
89     };
90
91     this.updatePoNotes = function(newNote) {
92         var notes = newNote ?
93             [newNote] :
94             PO.notes().filter(
95                 function(o) {
96                     if (o.ischanged() || o.isnew() || o.isdeleted())
97                         return o;
98                 }
99             );
100
101         if (notes.length < 1)
102             return;
103
104         progressDialog.show();
105
106         fieldmapper.standardRequest(
107             ["open-ils.acq", "open-ils.acq.po_note.cud.batch"], {
108                 "async": true,
109                 "params": [openils.User.authtoken, notes],
110                 "onresponse": function(r) {
111                     var resp = openils.Util.readResponse(r);
112                     if (resp) {
113                         progressDialog.update(resp);
114
115                         if (!resp.note.isdeleted()) {
116                             resp.note.isnew(false);
117                             resp.note.ischanged(false);
118                             PO.notes().push(resp.note);
119                         }
120                     }
121                 },
122                 "oncomplete": function() {
123                     if (!newNote) {
124                         /* remove the old changed notes */
125                         var list = [];
126                         PO.notes(
127                             PO.notes().filter(
128                                 function(o) {
129                                     return (!(
130                                         o.ischanged() || o.isnew() ||
131                                         o.isdeleted()
132                                     ));
133                                 }
134                             )
135                         );
136                     }
137
138                     progressDialog.hide();
139                     self.updatePoNotesCount();
140                     self.drawPoNotes();
141                 }
142             }
143         );
144     };
145
146     this.hide = function() {
147         openils.Util.hide("acq-po-notes-div");
148         liTable.show("list");
149         poItemTable.show();
150     };
151
152     this.show = function() {
153         liTable.hide();
154         poItemTable.hide();
155         self.drawPoNotes();
156         openils.Util.show("acq-po-notes-div");
157     };
158 }
159
160 function updatePoState(po_info) {
161     var data = po_info[PO.id()];
162     if (data) {
163         for (var key in data)
164             PO[key](data[key]);
165         renderPo();
166     }
167 }
168
169 function cancellationUpdater(r) {
170     var r = openils.Util.readResponse(r);
171     if (r) {
172         if (r.po) updatePoState(r.po);
173         if (r.li) {
174             for (var id in r.li) {
175                 liTable.liCache[id].state(r.li[id].state);
176                 liTable.liCache[id].cancel_reason(r.li[id].cancel_reason);
177                 liTable.updateLiState(liTable.liCache[id]);
178             }
179         }
180         if (r.lid && liTable.copyCache) {
181             for (var id in r.lid) {
182                 if (liTable.copyCache[id]) {
183                     liTable.copyCache[id].cancel_reason(
184                         r.lid[id].cancel_reason
185                     );
186                     liTable.updateLidState(liTable.copyCache[id]);
187                 }
188             }
189         }
190     }
191 }
192
193 function makeProviderLink(node, provider) {
194     return dojo.create(
195         "a", {
196             "href": oilsBasePath + "/conify/global/acq/provider/" + provider.id(),
197             "innerHTML": provider.name() + " (" + provider.code() + ")",
198         },
199         node,
200         "only"
201     );
202 }
203 function makePrepayWidget(node, prepay) {
204     if (prepay) {
205         openils.Util.addCSSClass(node, "oils-acq-po-prepay");
206         node.innerHTML = localeStrings.YES;
207     } else {
208         openils.Util.removeCSSClass(node, "oils-acq-po-prepay");
209         node.innerHTML = localeStrings.NO;
210     }
211 }
212
213 function makeCancelWidget(node, labelnode) {
214     openils.Util.hide("acq-po-choose-cancel-reason");
215
216     if (PO.cancel_reason()) {
217         labelnode.innerHTML = localeStrings.CANCEL_REASON;
218         node.innerHTML = PO.cancel_reason().description() + " (" +
219             PO.cancel_reason().label() + ")";
220     } else if (["on-order", "pending"].indexOf(PO.state()) == -1) {
221         dojo.destroy(this.oldTip);
222         labelnode.innerHTML = "";
223         node.innerHTML = "";
224     } else {
225         dojo.destroy(this.oldTip);
226         labelnode.innerHTML = localeStrings.CANCEL;
227         node.innerHTML = "";
228         if (!acqPoCancelReasonSubmit._prepared) {
229             var widget = new openils.widget.AutoFieldWidget({
230                 "fmField": "cancel_reason",
231                 "fmClass": "acqpo",
232                 "parentNode": dojo.byId("acq-po-cancel-reason"),
233                 "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],
234                 "forceSync": true
235             });
236             widget.build(
237                 function(w, ww) {
238                     acqPoCancelReasonSubmit.onClick = function() {
239                         if (w.attr("value")) {
240                             if (confirm(localeStrings.PO_CANCEL_CONFIRM)) {
241                                 fieldmapper.standardRequest(
242                                     ["open-ils.acq",
243                                         "open-ils.acq.purchase_order.cancel"],
244                                     {
245                                         "params": [
246                                             openils.User.authtoken,
247                                             PO.id(), 
248                                             w.attr("value")
249                                         ],
250                                         "async": true,
251                                         "oncomplete": cancellationUpdater
252                                     }
253                                 );
254                             }
255                         }
256                     };
257                     acqPoCancelReasonSubmit._prepared = true;
258                 }
259             );
260         }
261         openils.Util.show("acq-po-choose-cancel-reason", "inline");
262     }
263 }
264
265 function prepareInvoiceFeatures() {
266     /* show the count of related invoices on the "view invoices" button */
267     fieldmapper.standardRequest(
268         ["open-ils.acq", "open-ils.acq.invoice.unified_search.atomic"], {
269             "params": [
270                 openils.User.authtoken,
271                 {"acqpo":[{"id": PO.id()}]},
272                 null,
273                 null,
274                 {"id_list": true}
275             ],
276             "async": true,
277             "oncomplete": function(r) {
278                 dojo.byId("acq-po-view-invoice-count").innerHTML =
279                     openils.Util.readResponse(r).length;
280             }
281         }
282     );
283
284     /* view invoices button */
285     dijit.byId("acq-po-view-invoice-link").onClick = function() {
286         location.href = oilsBasePath + "/acq/search/unified?so=" +
287             base64Encode({"jub":[{"purchase_order": PO.id()}]}) +
288             "&rt=invoice";
289     };
290
291     /* create invoice button */
292     dijit.byId("acq-po-create-invoice-link").onClick = function() {
293         location.href = oilsBasePath +
294             "/acq/invoice/view?create=1&attach_po=" + PO.id();
295     };
296
297     if (!invoiceLinkDialogManager)
298         invoiceLinkDialogManager = new InvoiceLinkDialogManager("po", PO);
299
300     openils.Util.show("acq-po-view-invoices", "table-row");
301 }
302
303 function renderPo() {
304     dojo.byId("acq-po-view-id").innerHTML = PO.id();
305     dojo.byId("acq-po-view-name").innerHTML = PO.name();
306     makeProviderLink(
307         dojo.byId("acq-po-view-provider"),
308         PO.provider()
309     );
310     dojo.byId("acq-po-view-total-li").innerHTML = PO.lineitem_count();
311     dojo.byId("acq-po-view-total-enc").innerHTML = PO.amount_encumbered().toFixed(2);
312     dojo.byId("acq-po-view-total-spent").innerHTML = PO.amount_spent().toFixed(2);
313     dojo.byId("acq-po-view-state").innerHTML = PO.state(); // TODO i18n
314     makePrepayWidget(
315         dojo.byId("acq-po-view-prepay"),
316         openils.Util.isTrue(PO.prepayment_required())
317     );
318     makeCancelWidget(
319         dojo.byId("acq-po-view-cancel-reason"),
320         dojo.byId("acq-po-cancel-label")
321     );
322     dojo.byId("acq-po-view-notes").innerHTML = PO.notes().length;
323
324     if(PO.state() == "pending") {
325         openils.Util.show("acq-po-activate", "table-row");
326         checkCouldActivatePo();
327         if (PO.lineitem_count() > 1)
328             openils.Util.show("acq-po-split");
329     }
330
331     // XXX we probably don't *always* need to do this...
332     poItemTable.reset();
333     PO.po_items().forEach(
334         function(po_item) { poItemTable.addItem(po_item); }
335     );
336     poItemTable.show();
337
338     prepareInvoiceFeatures();
339 }
340
341
342 function init() {
343     /* set up li table */
344     liTable = new AcqLiTable();
345     liTable.reset();
346     liTable.isPO = poId;
347     liTable.poUpdateCallback = updatePoState;
348
349     /* set up po notes table */
350     poNoteTable = new AcqPoNoteTable();
351
352     /* retrieve data and populate */
353     fieldmapper.standardRequest(
354         ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
355         {   async: true,
356             params: [openils.User.authtoken, poId, {
357                 "flesh_provider": true,
358                 "flesh_price_summary": true,
359                 "flesh_lineitem_count": true,
360                 "flesh_notes": true,
361                 "flesh_po_items": true
362             }],
363             oncomplete: function(r) {
364                 PO = openils.Util.readResponse(r); /* save PO globally */
365
366                 /* po item table */
367                 poItemTable = new PoItemTable(PO, pcrud);
368
369                 renderPo();
370             }
371         }
372     );
373
374     var totalEstimated = 0;
375     fieldmapper.standardRequest(
376         ['open-ils.acq', 'open-ils.acq.lineitem.search'],
377         {   async: true,
378 params: [openils.User.authtoken, {purchase_order:poId}, {flesh_attrs:true, flesh_notes:true, flesh_cancel_reason:true}],
379             onresponse: function(r) {
380                 liTable.show('list');
381                 var li = openils.Util.readResponse(r);
382                 // TODO: Add po_item's to total estimated amount
383                 totalEstimated += (Number(li.item_count() || 0) * Number(li.estimated_unit_price() || 0));
384                 liTable.addLineitem(li);
385             },
386
387             oncomplete : function() {
388                 dojo.byId("acq-po-view-total-estimated").innerHTML = totalEstimated.toFixed(2);
389                 if (liFocus) liTable.drawCopies(liFocus);
390             }
391         }
392     );
393
394     pcrud.search(
395         'acqedim', 
396         {purchase_order : poId}, 
397         {
398             order_by : {acqedim : 'create_time'},
399             oncomplete : function(r) {
400                 var resp = openils.Util.readResponse(r);
401                 // TODO: I18n
402                 if(resp) {
403                     dojo.byId('acq-po-view-edi-messages').innerHTML = '(' + resp.length + ')';
404                     dojo.byId('acq-po-view-edi-messages').setAttribute('href', oilsBasePath + '/acq/po/edi_messages/' + poId);
405                 } else {
406                     dojo.byId('acq-po-view-edi-messages').innerHTML = '0';
407                     dojo.byId('acq-po-view-edi-messages').setAttribute('href', '');
408                 }
409             }
410         }
411     );
412 }
413
414 function checkCouldActivatePo() {
415     var d = dojo.byId("acq-po-activate-checking");
416     var a = dojo.byId("acq-po-activate-link");
417     d.innerHTML = localeStrings.PO_CHECKING;
418     var warnings = [];
419     var stops = [];
420
421     fieldmapper.standardRequest(
422         ["open-ils.acq", "open-ils.acq.purchase_order.activate.dry_run"], {
423             "params": [openils.User.authtoken, PO.id()],
424             "async": true,
425             "onresponse": function(r) {
426                 if ((r = openils.Util.readResponse(r, true /* eventOk */))) {
427                     if (typeof(r.textcode) != "undefined") {
428                         switch(r.textcode) {
429                             case "ACQ_FUND_EXCEEDS_STOP_PERCENT":
430                                 stops.push(r);
431                                 break;
432                             case "ACQ_FUND_EXCEEDS_WARN_PERCENT":
433                                 warnings.push(r);
434                                 break;
435                         }
436                     }
437                 }
438             },
439             "oncomplete": function() {
440                 /* XXX in the future, this might be tweaked to display info
441                  * about more than one stop or warning event from the ML. */
442                 if (!(warnings.length || stops.length)) {
443                     d.innerHTML = localeStrings.PO_COULD_ACTIVATE;
444                     openils.Util.show(a, "inline");
445                 } else {
446                     if (stops.length) {
447                         d.innerHTML =
448                             dojo.string.substitute(
449                                 localeStrings.PO_STOP_BLOCKS_ACTIVATION, [
450                                     stops[0].payload.fund.code(),
451                                     stops[0].payload.fund.year()
452                                 ]
453                             );
454                         openils.Util.hide(a);
455                     } else {
456                         PO._warning_hack = true;
457                         d.innerHTML =
458                             dojo.string.substitute(
459                                 localeStrings.PO_WARNING_NO_BLOCK_ACTIVATION, [
460                                     warnings[0].payload.fund.code(),
461                                     warnings[0].payload.fund.year()
462                                 ]
463                             );
464                         openils.Util.show(a, "inline");
465                     }
466                 }
467             }
468         }
469     );
470 }
471
472 function activatePo() {
473     if (openils.Util.isTrue(PO.prepayment_required())) {
474         if (!confirm(localeStrings.PREPAYMENT_REQUIRED_REMINDER))
475             return false;
476     }
477
478     if (PO._warning_hack) {
479         if (!confirm(localeStrings.PO_FUND_WARNING_CONFIRM))
480             return false;
481     }
482
483     var want_refresh = false;
484     progressDialog.show(true);
485     fieldmapper.standardRequest(
486         ["open-ils.acq", "open-ils.acq.purchase_order.activate"], {
487             "async": true,
488             "params": [openils.User.authtoken, PO.id()],
489             "onresponse": function(r) {
490                 want_refresh = Boolean(openils.Util.readResponse(r));
491             },
492             "oncomplete": function() {
493                 progressDialog.hide();
494                 if (want_refresh)
495                     location.href = location.href;
496             }
497         }
498     );
499 }
500
501 function splitPo() {
502     progressDialog.show(true);
503     try {
504         var list;
505         fieldmapper.standardRequest(
506             ['open-ils.acq', 'open-ils.acq.purchase_order.split_by_lineitems'],
507             {   async: true,
508                 params: [openils.User.authtoken, PO.id()],
509                 onresponse : function(r) {
510                     list = openils.Util.readResponse(r);
511                 },
512                 oncomplete : function() {
513                     progressDialog.hide();
514                     if (list) {
515                         location.href = oilsBasePath + '/acq/po/search/' +
516                             list.join(",");
517                     }
518                 }
519             }
520         );
521     } catch(E) {
522         progressDialog.hide();
523         alert(E);
524     }
525 }
526
527 function updatePoName() {
528     var value = prompt('Enter new purchase order name:', PO.name()); // TODO i18n
529     if(!value || value == PO.name()) return;
530     PO.name(value);
531     pcrud.update(PO, {
532         oncomplete : function(r, cudResults) {
533             var stat = cudResults[0];
534             if(stat)
535                 dojo.byId('acq-po-view-name').innerHTML = value;
536         }
537     });
538 }
539
540 openils.Util.addOnLoad(init);