]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/acq/po/view_po.js
ACQ : lineitem recovers focus from various interfaces
[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 =
89             "(" + PO.notes().length + ")";
90     };
91
92     this.updatePoNotes = function(newNote) {
93         var notes = newNote ?
94             [newNote] :
95             PO.notes().filter(
96                 function(o) {
97                     if (o.ischanged() || o.isnew() || o.isdeleted())
98                         return o;
99                 }
100             );
101
102         if (notes.length < 1)
103             return;
104
105         progressDialog.show();
106
107         fieldmapper.standardRequest(
108             ["open-ils.acq", "open-ils.acq.po_note.cud.batch"], {
109                 "async": true,
110                 "params": [openils.User.authtoken, notes],
111                 "onresponse": function(r) {
112                     var resp = openils.Util.readResponse(r);
113                     if (resp) {
114                         progressDialog.update(resp);
115
116                         if (!resp.note.isdeleted()) {
117                             resp.note.isnew(false);
118                             resp.note.ischanged(false);
119                             PO.notes().push(resp.note);
120                         }
121                     }
122                 },
123                 "oncomplete": function() {
124                     if (!newNote) {
125                         /* remove the old changed notes */
126                         var list = [];
127                         PO.notes(
128                             PO.notes().filter(
129                                 function(o) {
130                                     return (!(
131                                         o.ischanged() || o.isnew() ||
132                                         o.isdeleted()
133                                     ));
134                                 }
135                             )
136                         );
137                     }
138
139                     progressDialog.hide();
140                     self.updatePoNotesCount();
141                     self.drawPoNotes();
142                 }
143             }
144         );
145     };
146
147     this.hide = function() {
148         openils.Util.hide("acq-po-notes-div");
149         liTable.show("list");
150         poItemTable.show();
151     };
152
153     this.show = function() {
154         liTable.hide();
155         poItemTable.hide();
156         self.drawPoNotes();
157         openils.Util.show("acq-po-notes-div");
158     };
159 }
160
161 function updatePoState(po_info) {
162     var data = po_info[PO.id()];
163     if (data) {
164         for (var key in data)
165             PO[key](data[key]);
166         renderPo();
167     }
168 }
169
170 function cancellationUpdater(r) {
171     var r = openils.Util.readResponse(r);
172     if (r) {
173         if (r.po) updatePoState(r.po);
174         if (r.li) {
175             for (var id in r.li) {
176                 liTable.liCache[id].state(r.li[id].state);
177                 liTable.liCache[id].cancel_reason(r.li[id].cancel_reason);
178                 liTable.updateLiState(liTable.liCache[id]);
179             }
180         }
181         if (r.lid && liTable.copyCache) {
182             for (var id in r.lid) {
183                 if (liTable.copyCache[id]) {
184                     liTable.copyCache[id].cancel_reason(
185                         r.lid[id].cancel_reason
186                     );
187                     liTable.updateLidState(liTable.copyCache[id]);
188                 }
189             }
190         }
191     }
192 }
193
194 function makeProviderLink(node, provider) {
195     return dojo.create(
196         "a", {
197             "href": oilsBasePath + "/conify/global/acq/provider/" + provider.id(),
198             "innerHTML": provider.name() + " (" + provider.code() + ")",
199         },
200         node,
201         "only"
202     );
203 }
204 function makePrepayWidget(node, prepay) {
205     if (prepay) {
206         openils.Util.addCSSClass(node, "oils-acq-po-prepay");
207         node.innerHTML = localeStrings.YES;
208     } else {
209         openils.Util.removeCSSClass(node, "oils-acq-po-prepay");
210         node.innerHTML = localeStrings.NO;
211     }
212 }
213
214 function makeCancelWidget(node, labelnode) {
215     openils.Util.hide("acq-po-choose-cancel-reason");
216
217     if (PO.cancel_reason()) {
218         labelnode.innerHTML = localeStrings.CANCEL_REASON;
219         node.innerHTML = PO.cancel_reason().description() + " (" +
220             PO.cancel_reason().label() + ")";
221     } else if (["on-order", "pending"].indexOf(PO.state()) == -1) {
222         dojo.destroy(this.oldTip);
223         labelnode.innerHTML = "";
224         node.innerHTML = "";
225     } else {
226         dojo.destroy(this.oldTip);
227         labelnode.innerHTML = localeStrings.CANCEL;
228         node.innerHTML = "";
229         if (!acqPoCancelReasonSubmit._prepared) {
230             var widget = new openils.widget.AutoFieldWidget({
231                 "fmField": "cancel_reason",
232                 "fmClass": "acqpo",
233                 "parentNode": dojo.byId("acq-po-cancel-reason"),
234                 "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],
235                 "forceSync": true
236             });
237             widget.build(
238                 function(w, ww) {
239                     acqPoCancelReasonSubmit.onClick = function() {
240                         if (w.attr("value")) {
241                             if (confirm(localeStrings.PO_CANCEL_CONFIRM)) {
242                                 fieldmapper.standardRequest(
243                                     ["open-ils.acq",
244                                         "open-ils.acq.purchase_order.cancel"],
245                                     {
246                                         "params": [
247                                             openils.User.authtoken,
248                                             PO.id(), 
249                                             w.attr("value")
250                                         ],
251                                         "async": true,
252                                         "oncomplete": cancellationUpdater
253                                     }
254                                 );
255                             }
256                         }
257                     };
258                     acqPoCancelReasonSubmit._prepared = true;
259                 }
260             );
261         }
262         openils.Util.show("acq-po-choose-cancel-reason", "inline");
263     }
264 }
265
266 function prepareInvoiceFeatures() {
267     /* show the count of related invoices on the "view invoices" button */
268     fieldmapper.standardRequest(
269         ["open-ils.acq", "open-ils.acq.invoice.unified_search.atomic"], {
270             "params": [
271                 openils.User.authtoken,
272                 {"acqpo":[{"id": PO.id()}]},
273                 null,
274                 null,
275                 {"id_list": true}
276             ],
277             "async": true,
278             "oncomplete": function(r) {
279                 dojo.byId("acq-po-view-invoice-count").innerHTML =
280                     openils.Util.readResponse(r).length;
281             }
282         }
283     );
284
285     /* view invoices button */
286     dijit.byId("acq-po-view-invoice-link").onClick = function() {
287         location.href = oilsBasePath + "/acq/search/unified?so=" +
288             base64Encode({"acqpo":[{"id": PO.id()}]}) +
289             "&rt=invoice";
290     };
291
292     /* create invoice button */
293     dijit.byId("acq-po-create-invoice-link").onClick = function() {
294         location.href = oilsBasePath +
295             "/acq/invoice/view?create=1&attach_po=" + PO.id();
296     };
297
298     openils.Util.show("acq-po-invoice-stuff", "table-cell");
299 }
300
301 function renderPo() {
302     dojo.byId("acq-po-view-id").innerHTML = PO.id();
303     dojo.byId("acq-po-view-name").innerHTML = PO.name();
304     makeProviderLink(
305         dojo.byId("acq-po-view-provider"),
306         PO.provider()
307     );
308     dojo.byId("acq-po-view-total-li").innerHTML = PO.lineitem_count();
309     dojo.byId("acq-po-view-total-enc").innerHTML = PO.amount_encumbered().toFixed(2);
310     dojo.byId("acq-po-view-total-spent").innerHTML = PO.amount_spent().toFixed(2);
311     dojo.byId("acq-po-view-state").innerHTML = PO.state(); // TODO i18n
312
313     if(PO.order_date()) {
314         openils.Util.show('acq-po-activated-on', 'inline');
315         dojo.byId('acq-po-activated-on').innerHTML = 
316             dojo.string.substitute(
317                 localeStrings.PO_ACTIVATED_ON, [
318                     openils.Util.timeStamp(PO.order_date(), {formatLength:'short'})
319                 ]
320             );
321
322     }
323
324     makePrepayWidget(
325         dojo.byId("acq-po-view-prepay"),
326         openils.Util.isTrue(PO.prepayment_required())
327     );
328     makeCancelWidget(
329         dojo.byId("acq-po-view-cancel-reason"),
330         dojo.byId("acq-po-cancel-label")
331     );
332     // dojo.byId("acq-po-view-notes").innerHTML = PO.notes().length;
333     poNoteTable.updatePoNotesCount();
334
335     if (PO.state() == "pending") {
336         checkCouldActivatePo();
337         if (PO.lineitem_count() > 1)
338             openils.Util.show("acq-po-split");
339     } else {
340         dojo.byId("acq-po-activate-checking").innerHTML = localeStrings.NO;
341     }
342
343     // XXX we probably don't *always* need to do this...
344     poItemTable.reset();
345     PO.po_items().forEach(
346         function(po_item) { poItemTable.addItem(po_item); }
347     );
348     poItemTable.show();
349
350     dojo.attr(
351         "acq-po-view-history", "href",
352         oilsBasePath + "/acq/po/history/" + PO.id()
353     );
354     openils.Util.show("acq-po-view-history", "inline");
355
356     
357     /* if we got here from the search/invoice page with a focused LI,
358      * return to the previous page with the same LI focused */
359     var cgi = new openils.CGI();
360     if (cgi.param('focus_li')) {
361         dojo.forEach(
362             ['search', 'invoice'], // perhaps a wee bit too loose
363             function(source) {
364                 if (document.referrer.match(new RegExp(source))) {
365                     openils.Util.show('acq-po-return-to-' + source);
366                     var newCgi = new openils.CGI({url : document.referrer});
367                     newCgi.param('focus_li', cgi.param('focus_li'));
368                     dojo.byId('acq-po-return-to-' + source + '-button').onclick = function() {
369                         location.href = newCgi.url();
370                     }
371                 }
372             }
373         );
374     }
375
376     prepareInvoiceFeatures();
377 }
378
379
380 function init() {
381     /* set up li table */
382     liTable = new AcqLiTable();
383     liTable.reset();
384     liTable.isPO = poId;
385     liTable.poUpdateCallback = updatePoState;
386
387     /* set up po notes table */
388     poNoteTable = new AcqPoNoteTable();
389
390     /* retrieve data and populate */
391     fieldmapper.standardRequest(
392         ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
393         {   async: true,
394             params: [openils.User.authtoken, poId, {
395                 "flesh_provider": true,
396                 "flesh_price_summary": true,
397                 "flesh_lineitem_count": true,
398                 "flesh_notes": true,
399                 "flesh_po_items": true
400             }],
401             oncomplete: function(r) {
402                 PO = openils.Util.readResponse(r); /* save PO globally */
403
404                 /* po item table */
405                 poItemTable = new PoItemTable(PO, pcrud);
406
407                 renderPo();
408             }
409         }
410     );
411
412     var totalEstimated = 0;
413     var zeroLi = true;
414     fieldmapper.standardRequest(
415         ['open-ils.acq', 'open-ils.acq.lineitem.search'],
416         {   async: true,
417             params: [
418                 openils.User.authtoken, 
419                 [{purchase_order:poId}, {"order_by": {"jub": "id ASC"}}], 
420                 {flesh_attrs:true, flesh_notes:true, flesh_cancel_reason:true, clear_marc:true}
421             ],
422             onresponse: function(r) {
423                 zeroLi = false;
424                 liTable.show('list');
425                 var li = openils.Util.readResponse(r);
426                 // TODO: Add po_item's to total estimated amount
427                 totalEstimated += (Number(li.item_count() || 0) * Number(li.estimated_unit_price() || 0));
428                 liTable.addLineitem(li);
429             },
430
431             oncomplete : function() {
432                 dojo.byId("acq-po-view-total-estimated").innerHTML = totalEstimated.toFixed(2);
433                 if (liFocus) liTable.drawCopies(liFocus);
434                 if(zeroLi) openils.Util.show('acq-po-no-lineitems');
435             }
436         }
437     );
438
439     pcrud.search(
440         'acqedim', 
441         {purchase_order : poId}, 
442         {
443             id_list : true,
444             oncomplete : function(r) {
445                 var resp = openils.Util.readResponse(r);
446                 // TODO: I18n
447                 if(resp) {
448                     dojo.byId('acq-po-view-edi-messages').innerHTML = '(' + resp.length + ')';
449                     dojo.byId('acq-po-view-edi-messages').setAttribute('href', oilsBasePath + '/acq/po/edi_messages/' + poId);
450                 } else {
451                     dojo.byId('acq-po-view-edi-messages').innerHTML = '0';
452                     dojo.byId('acq-po-view-edi-messages').setAttribute('href', '');
453                 }
454             }
455         }
456     );
457 }
458
459 function checkCouldActivatePo() {
460     var d = dojo.byId("acq-po-activate-checking");
461     var a = dojo.byId("acq-po-activate-link");
462     d.innerHTML = localeStrings.PO_CHECKING;
463     var warnings = [];
464     var stops = [];
465     var other = [];
466
467     fieldmapper.standardRequest(
468         ["open-ils.acq", "open-ils.acq.purchase_order.activate.dry_run"], {
469             "params": [
470                 openils.User.authtoken,
471                 PO.id(),
472                 null,  // vandelay options
473                 {zero_copy_activate : dojo.byId('acq-po-activate-zero-copies').checked}
474             ],
475             "async": true,
476             "onresponse": function(r) {
477                 if ((r = openils.Util.readResponse(r, true /* eventOk */))) {
478                     if (typeof(r.textcode) != "undefined") {
479                         switch(r.textcode) {
480                             case "ACQ_FUND_EXCEEDS_STOP_PERCENT":
481                                 stops.push(r);
482                                 break;
483                             case "ACQ_FUND_EXCEEDS_WARN_PERCENT":
484                                 warnings.push(r);
485                                 break;
486                             default:
487                                 other.push(r);
488                         }
489                     }
490                 }
491             },
492             "oncomplete": function() {
493                 /* XXX in the future, this might be tweaked to display info
494                  * about more than one stop or warning event from the ML. */
495                 if (!(warnings.length || stops.length || other.length)) {
496                     d.innerHTML = localeStrings.PO_COULD_ACTIVATE;
497                     openils.Util.show(a, "inline");
498                 } else {
499                     if (other.length) {
500                         /* XXX make the textcode part a tooltip one day */
501                         d.innerHTML = localeStrings.NO + ": " +
502                             other[0].desc + " (" + other[0].textcode + ")";
503                         openils.Util.hide(a);
504                         
505                         if (other[0].textcode == 'ACQ_LINEITEM_NO_COPIES') {
506                             // when LIs w/ zero LIDs are present, list them
507                             fieldmapper.standardRequest(
508                                 [   'open-ils.acq', 
509                                     'open-ils.acq.purchase_order.no_copy_lineitems.id_list.authoritative.atomic' ],
510                                 {   async : true, 
511                                     params : [openils.User.authtoken, poId],
512                                     oncomplete : function(r) {
513                                         var ids = openils.Util.readResponse(r);
514                                         d.innerHTML += ' (' + ids + ')';
515                                     }
516                                 }
517                             );
518                         }
519                     } else if (stops.length) {
520                         d.innerHTML =
521                             dojo.string.substitute(
522                                 localeStrings.PO_STOP_BLOCKS_ACTIVATION, [
523                                     stops[0].payload.fund.code(),
524                                     stops[0].payload.fund.year()
525                                 ]
526                             );
527                         openils.Util.hide(a);
528                     } else {
529                         PO._warning_hack = true;
530                         d.innerHTML =
531                             dojo.string.substitute(
532                                 localeStrings.PO_WARNING_NO_BLOCK_ACTIVATION, [
533                                     warnings[0].payload.fund.code(),
534                                     warnings[0].payload.fund.year()
535                                 ]
536                             );
537                         openils.Util.show(a, "inline");
538                     }
539                 }
540             }
541         }
542     );
543 }
544
545 function activatePo() {
546     if (openils.Util.isTrue(PO.prepayment_required())) {
547         if (!confirm(localeStrings.PREPAYMENT_REQUIRED_REMINDER))
548             return false;
549     }
550
551     if (PO._warning_hack) {
552         if (!confirm(localeStrings.PO_FUND_WARNING_CONFIRM))
553             return false;
554     }
555
556     liTable.showAssetCreator(activatePoStage2);
557 }
558
559 function activatePoStage2() {
560
561     var want_refresh = false;
562     progressDialog.show(true);
563     fieldmapper.standardRequest(
564         ["open-ils.acq", "open-ils.acq.purchase_order.activate"], {
565             "async": true,
566             "params": [
567                 openils.User.authtoken,
568                 PO.id(),
569                 null,  // vandelay options
570                 {zero_copy_activate : dojo.byId('acq-po-activate-zero-copies').checked}
571             ],
572             "onresponse": function(r) {
573                 want_refresh = Boolean(openils.Util.readResponse(r));
574             },
575             "oncomplete": function() {
576                 progressDialog.hide();
577                 if (want_refresh)
578                     location.href = location.href;
579             }
580         }
581     );
582 }
583
584 function splitPo() {
585     progressDialog.show(true);
586     try {
587         var list;
588         fieldmapper.standardRequest(
589             ['open-ils.acq', 'open-ils.acq.purchase_order.split_by_lineitems'],
590             {   async: true,
591                 params: [openils.User.authtoken, PO.id()],
592                 onresponse : function(r) {
593                     list = openils.Util.readResponse(r);
594                 },
595                 oncomplete : function() {
596                     progressDialog.hide();
597                     if (list) {
598                         location.href = oilsBasePath + '/acq/po/search/' +
599                             list.join(",");
600                     }
601                 }
602             }
603         );
604     } catch(E) {
605         progressDialog.hide();
606         alert(E);
607     }
608 }
609
610 function updatePoName() {
611     var value = prompt('Enter new purchase order name:', PO.name()); // TODO i18n
612     if(!value || value == PO.name()) return;
613     PO.name(value);
614     pcrud.update(PO, {
615         oncomplete : function(r, cudResults) {
616             var stat = cudResults[0];
617             if(stat)
618                 dojo.byId('acq-po-view-name').innerHTML = value;
619         }
620     });
621 }
622
623 openils.Util.addOnLoad(init);