1 dojo.require("dijit.form.Button");
2 dojo.require("dojo.string");
3 dojo.require('dijit.layout.ContentPane');
4 dojo.require('openils.PermaCrud');
6 var pcrud = new openils.PermaCrud();
11 var invoiceLinkDialogManager;
13 function AcqPoNoteTable() {
16 this.notesTbody = dojo.byId("acq-po-notes-tbody");
17 this.notesRow = this.notesTbody.removeChild(dojo.byId("acq-po-notes-row"));
19 dojo.byId("acq-po-notes-back-button").onclick = function() { self.hide(); };
20 dojo.byId("acq-po-view-notes").onclick = function() { self.show(); };
22 /* widgets' event properties are cased likeThis */
23 acqPoCreateNoteSubmit.onClick = function() {
24 if (!acqPoCreateNoteText.attr("value")) return;
27 var note = new acqpon();
29 Boolean(acqPoCreateNoteVendorPublic.attr('checked'))
31 note.value(acqPoCreateNoteText.attr("value"));
32 note.purchase_order(PO.id());
36 self.updatePoNotes(note);
38 /* reset fields for next use */
39 acqPoCreateNoteText.attr("value", "");
40 acqPoCreateNoteVendorPublic.attr("checked", false);
43 this.drawPoNote = function(note) {
47 var row = dojo.clone(this.notesRow);
49 nodeByName("value", row).innerHTML = note.value();
51 if (openils.Util.isTrue(note.vendor_public()))
52 nodeByName("vendor_public", row).innerHTML =
53 localeStrings.VENDOR_PUBLIC;
55 nodeByName("delete", row).onclick = function() {
57 self.notesTbody.removeChild(row);
61 if (note.edit_time()) {
62 nodeByName("edit_time", row).innerHTML =
63 dojo.date.locale.format(
64 dojo.date.stamp.fromISOString(note.edit_time()),
65 {"formatLength": "short"}
69 self.notesTbody.appendChild(row);
72 this.drawPoNotes = function() {
77 return (a.edit_time() < b.edit_time()) ? 1 : -1;
82 /* remove old renderings of notes */
83 dojo.empty(this.notesTbody);
85 PO.notes().forEach(function(o) { self.drawPoNote(o); });
88 this.updatePoNotesCount = function() {
89 dojo.byId("acq-po-view-notes").innerHTML =
90 "(" + PO.notes().length + ")";
93 this.updatePoNotes = function(newNote) {
98 if (o.ischanged() || o.isnew() || o.isdeleted())
103 if (notes.length < 1)
106 progressDialog.show();
108 fieldmapper.standardRequest(
109 ["open-ils.acq", "open-ils.acq.po_note.cud.batch"], {
111 "params": [openils.User.authtoken, notes],
112 "onresponse": function(r) {
113 var resp = openils.Util.readResponse(r);
115 progressDialog.update(resp);
117 if (!resp.note.isdeleted()) {
118 resp.note.isnew(false);
119 resp.note.ischanged(false);
120 PO.notes().push(resp.note);
124 "oncomplete": function() {
126 /* remove the old changed notes */
132 o.ischanged() || o.isnew() ||
140 progressDialog.hide();
141 self.updatePoNotesCount();
148 this.hide = function() {
149 openils.Util.hide("acq-po-notes-div");
150 liTable.show("list");
154 this.show = function() {
158 openils.Util.show("acq-po-notes-div");
162 function updatePoState(po_info) {
163 var data = po_info[PO.id()];
165 for (var key in data)
171 function cancellationUpdater(r) {
172 var r = openils.Util.readResponse(r);
174 if (r.po) updatePoState(r.po);
176 for (var id in r.li) {
177 liTable.liCache[id].state(r.li[id].state);
178 liTable.liCache[id].cancel_reason(r.li[id].cancel_reason);
179 liTable.updateLiState(liTable.liCache[id]);
182 if (r.lid && liTable.copyCache) {
183 for (var id in r.lid) {
184 if (liTable.copyCache[id]) {
185 liTable.copyCache[id].cancel_reason(
186 r.lid[id].cancel_reason
188 liTable.updateLidState(liTable.copyCache[id]);
195 function makeProviderLink(node, provider) {
198 "href": oilsBasePath + "/conify/global/acq/provider/" + provider.id(),
199 "innerHTML": provider.name() + " (" + provider.code() + ")",
205 function makePrepayWidget(node, prepay) {
207 openils.Util.addCSSClass(node, "oils-acq-po-prepay");
208 node.innerHTML = localeStrings.YES;
210 openils.Util.removeCSSClass(node, "oils-acq-po-prepay");
211 node.innerHTML = localeStrings.NO;
215 function makeCancelWidget(node, labelnode) {
216 openils.Util.hide("acq-po-choose-cancel-reason");
218 if (PO.cancel_reason()) {
219 labelnode.innerHTML = localeStrings.CANCEL_REASON;
220 node.innerHTML = PO.cancel_reason().description() + " (" +
221 PO.cancel_reason().label() + ")";
222 } else if (["on-order", "pending"].indexOf(PO.state()) == -1) {
223 dojo.destroy(this.oldTip);
224 labelnode.innerHTML = "";
227 dojo.destroy(this.oldTip);
228 labelnode.innerHTML = localeStrings.CANCEL;
230 if (!acqPoCancelReasonSubmit._prepared) {
231 var widget = new openils.widget.AutoFieldWidget({
232 "fmField": "cancel_reason",
234 "parentNode": dojo.byId("acq-po-cancel-reason"),
235 "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],
237 "searchOptions" : {order_by : {"acqcr":"label"}}
241 acqPoCancelReasonSubmit.onClick = function() {
242 if (w.attr("value")) {
243 if (confirm(localeStrings.PO_CANCEL_CONFIRM)) {
244 fieldmapper.standardRequest(
246 "open-ils.acq.purchase_order.cancel"],
249 openils.User.authtoken,
254 "oncomplete": cancellationUpdater
260 acqPoCancelReasonSubmit._prepared = true;
264 openils.Util.show("acq-po-choose-cancel-reason", "inline");
268 function prepareInvoiceFeatures() {
269 /* show the count of related invoices on the "view invoices" button */
270 fieldmapper.standardRequest(
271 ["open-ils.acq", "open-ils.acq.invoice.unified_search.atomic"], {
273 openils.User.authtoken,
274 {"acqpo":[{"id": PO.id()}]},
280 "oncomplete": function(r) {
281 dojo.byId("acq-po-view-invoice-count").innerHTML =
282 openils.Util.readResponse(r).length;
287 /* view invoices button */
288 dijit.byId("acq-po-view-invoice-link").onClick = function() {
289 location.href = oilsBasePath + "/acq/search/unified?so=" +
290 base64Encode({"acqpo":[{"id": PO.id()}]}) +
294 /* create invoice button */
295 dijit.byId("acq-po-create-invoice-link").onClick = function() {
296 location.href = oilsBasePath +
297 "/acq/invoice/view?create=1&attach_po=" + PO.id();
300 openils.Util.show("acq-po-invoice-stuff", "table-cell");
303 function setSummaryAmounts() {
304 dojo.byId("acq-po-view-total-enc").innerHTML = PO.amount_encumbered().toFixed(2);
305 dojo.byId("acq-po-view-total-spent").innerHTML = PO.amount_spent().toFixed(2);
306 dojo.byId("acq-po-view-total-estimated").innerHTML = PO.amount_estimated().toFixed(2);
309 function refreshPOSummaryAmounts() {
310 fieldmapper.standardRequest(
312 'open-ils.acq.purchase_order.retrieve.authoritative'],
314 params: [openils.User.authtoken, poId, {
315 "flesh_price_summary": true
317 oncomplete: function(r) {
318 // update the global PO instead of replacing it, since other
319 // code outside our control may be referencing it.
320 var po = openils.Util.readResponse(r);
321 PO.amount_encumbered(po.amount_encumbered());
322 PO.amount_spent(po.amount_spent());
323 PO.amount_estimated(po.amount_estimated());
330 /* renderPo() is the best place to add tests that depend on PO-state
331 * (or simple ordered-or-not? checks) to enable/disable UI elements
332 * across the whole interface. */
333 function renderPo() {
334 var po_state = PO.state();
335 dojo.byId("acq-po-view-id").innerHTML = PO.id();
336 dojo.byId("acq-po-view-name").innerHTML = PO.name();
338 dojo.byId("acq-po-view-provider"),
343 dojo.byId("acq-po-view-total-li").innerHTML = PO.lineitem_count();
344 dojo.byId("acq-po-view-state").innerHTML = po_state; // TODO i18n
346 if(PO.order_date()) {
347 openils.Util.show('acq-po-activated-on', 'inline');
348 liTable.enableActionsDropdownOptions("ao"); /* activated */
349 liTable.initBatchUpdater(["item_count", "distribution_formula"]);
351 dojo.byId('acq-po-activated-on').innerHTML =
352 dojo.string.substitute(
353 localeStrings.PO_ACTIVATED_ON, [
354 openils.Util.timeStamp(PO.order_date(), {formatLength:'short'})
357 /* These are handled another way now */
358 // if (po_state == "on-order" || po_state == "cancelled") {
359 // dojo.removeAttr('receive_lineitems', 'disabled');
360 // } else if(po_state == "received") {
361 // dojo.removeAttr('rollback_receive_lineitems', 'disabled');
364 /* cancel widgets only make sense for activate (ordered) POs */
366 dojo.byId("acq-po-view-cancel-reason"),
367 dojo.byId("acq-po-cancel-label")
370 /* likewise for invoice features */
371 openils.Util.show("acq-po-invoice-label", "table-cell");
372 prepareInvoiceFeatures();
374 /* These things only make sense for not-ordered-yet POs */
376 liTable.initBatchUpdater();
377 liTable.enableActionsDropdownOptions("po");
379 openils.Util.show("acq-po-zero-activate-label", "table-cell");
380 openils.Util.show("acq-po-zero-activate", "table-cell");
382 openils.Util.show("acq-po-item-table-controls");
386 dojo.byId("acq-po-view-prepay"),
387 openils.Util.isTrue(PO.prepayment_required())
389 // dojo.byId("acq-po-view-notes").innerHTML = PO.notes().length;
390 poNoteTable.updatePoNotesCount();
392 if (po_state == "pending") {
393 checkCouldActivatePo();
394 if (PO.lineitem_count() > 1)
395 openils.Util.show("acq-po-split");
397 if (PO.order_date()) {
398 dojo.byId("acq-po-activate-checking").innerHTML = localeStrings.PO_ALREADY_ACTIVATED;
399 checkCouldBlanketFinalize();
401 dojo.byId("acq-po-activate-checking").innerHTML = localeStrings.NO;
405 // XXX we probably don't *always* need to do this...
407 PO.po_items().forEach(
408 function(po_item) { poItemTable.addItem(po_item); }
413 "acq-po-view-history", "href",
414 oilsBasePath + "/acq/po/history/" + PO.id()
416 openils.Util.show("acq-po-view-history", "inline");
419 /* if we got here from the search/invoice page with a focused LI,
420 * return to the previous page with the same LI focused */
421 var cgi = new openils.CGI();
422 var source = cgi.param('source');
423 var focus_li = cgi.param('focus_li');
424 if (focus_li && source) {
426 ['search', 'invoice'], // perhaps a wee bit too loose
428 if (source.match(new RegExp(srcType))) {
429 openils.Util.show('acq-po-return-to-' + srcType);
430 var newCgi = new openils.CGI({url : source});
431 newCgi.param('focus_li', focus_li);
432 dojo.byId('acq-po-return-to-' + srcType + '-button').onclick = function() {
433 location.href = newCgi.url();
443 /* set up li table */
444 liTable = new AcqLiTable();
447 liTable.poUpdateCallback = updatePoState;
449 /* set up po notes table */
450 poNoteTable = new AcqPoNoteTable();
452 /* retrieve data and populate */
453 fieldmapper.standardRequest(
454 ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
456 params: [openils.User.authtoken, poId, {
457 "flesh_provider": true,
458 "flesh_price_summary": true,
459 "flesh_lineitem_count": true,
461 "flesh_po_items": true
463 oncomplete: function(r) {
464 PO = openils.Util.readResponse(r); /* save PO globally */
467 poItemTable = new PoItemTable(PO, pcrud);
469 liTable.testOrderIdentPerms( PO.ordering_agency(), init2);
480 fieldmapper.standardRequest(
481 ['open-ils.acq', 'open-ils.acq.lineitem.search'],
484 openils.User.authtoken,
485 [{purchase_order:poId}, {"order_by": {"jub": "id ASC"}}],
486 { flesh_attrs : true,
488 flesh_cancel_reason : true,
489 flesh_order_summary : true,
493 onresponse: function(r) {
494 liTable.show('list');
495 var li = openils.Util.readResponse(r);
496 liTable.addLineitem(li);
499 oncomplete : function() {
500 if (liFocus) liTable.drawCopies(liFocus);
507 {purchase_order : poId},
510 oncomplete : function(r) {
511 var resp = openils.Util.readResponse(r);
514 dojo.byId('acq-po-view-edi-messages').innerHTML = '(' + resp.length + ')';
515 dojo.byId('acq-po-view-edi-messages').setAttribute('href', oilsBasePath + '/acq/po/edi_messages/' + poId);
517 dojo.byId('acq-po-view-edi-messages').innerHTML = '0';
518 dojo.byId('acq-po-view-edi-messages').setAttribute('href', '');
525 function checkCouldBlanketFinalize() {
527 if (PO.state() == 'received') return;
531 // get the unique set of invoice item type IDs
532 PO.po_items().forEach(function(item) {
533 if (inv_types.indexOf(item.inv_item_type()) == -1)
534 inv_types.push(item.inv_item_type());
537 if (inv_types.length == 0) return;
540 {code : inv_types, blanket : 't'}, {
541 oncomplete : function(r) {
542 r = openils.Util.readResponse(r);
543 if (r.length == 0) return;
544 openils.Util.show(dojo.byId('acq-po-finalize-links'), 'inline');
549 function checkCouldActivatePo() {
550 var d = dojo.byId("acq-po-activate-checking");
551 var a = dojo.byId("acq-po-activate-links"); /* <span> not <a> now, but no diff */
552 d.innerHTML = localeStrings.PO_CHECKING;
557 fieldmapper.standardRequest(
558 ["open-ils.acq", "open-ils.acq.purchase_order.activate.dry_run"], {
560 openils.User.authtoken,
562 null, // vandelay options
563 {zero_copy_activate : dojo.byId('acq-po-activate-zero-copies').checked}
566 "onresponse": function(r) {
567 if ((r = openils.Util.readResponse(r, true /* eventOk */))) {
568 if (typeof(r.textcode) != "undefined") {
570 case "ACQ_FUND_EXCEEDS_STOP_PERCENT":
573 case "ACQ_FUND_EXCEEDS_WARN_PERCENT":
582 "oncomplete": function() {
583 /* XXX in the future, this might be tweaked to display info
584 * about more than one stop or warning event from the ML. */
585 if (!(warnings.length || stops.length || other.length)) {
586 d.innerHTML = localeStrings.PO_COULD_ACTIVATE;
587 openils.Util.show(a, "inline");
588 activatePoButton.attr("disabled", false);
591 /* XXX make the textcode part a tooltip one day */
592 d.innerHTML = localeStrings.NO + ": " +
593 other[0].desc + " (" + other[0].textcode + ")";
594 openils.Util.hide(a);
596 if (other[0].textcode == 'ACQ_LINEITEM_NO_COPIES') {
597 // when LIs w/ zero LIDs are present, list them
598 fieldmapper.standardRequest(
600 'open-ils.acq.purchase_order.no_copy_lineitems.id_list.authoritative.atomic' ],
602 params : [openils.User.authtoken, poId],
603 oncomplete : function(r) {
604 var ids = openils.Util.readResponse(r);
605 d.innerHTML += ' (' + ids + ')';
610 } else if (stops.length) {
612 dojo.string.substitute(
613 localeStrings.PO_STOP_BLOCKS_ACTIVATION, [
614 stops[0].payload.fund.code(),
615 stops[0].payload.fund.year()
618 openils.Util.hide(a);
620 PO._warning_hack = true;
622 dojo.string.substitute(
623 localeStrings.PO_WARNING_NO_BLOCK_ACTIVATION, [
624 warnings[0].payload.fund.code(),
625 warnings[0].payload.fund.year()
628 openils.Util.show(a, "inline");
629 activatePoButton.attr("disabled", false);
637 function finalizePo() {
639 if (!confirm(localeStrings.FINALIZE_PO)) return;
641 finalizePoButton.attr('disabled', true);
643 fieldmapper.standardRequest(
644 ['open-ils.acq', 'open-ils.acq.purchase_order.blanket.finalize'],
646 params : [openils.User.authtoken, PO.id()],
647 oncomplete : function(r) {
648 if (openils.Util.readResponse(r) == 1)
649 location.href = location.href;
655 function activatePo(noAssets) {
656 activatePoButton.attr("disabled", true);
657 activatePoNoAssetsButton.attr("disabled", true);
659 if (openils.Util.isTrue(PO.prepayment_required())) {
660 if (!confirm(localeStrings.PREPAYMENT_REQUIRED_REMINDER)) {
661 activatePoButton.attr("disabled", false);
666 if (PO._warning_hack) {
667 if (!confirm(localeStrings.PO_FUND_WARNING_CONFIRM)) {
668 activatePoButton.attr("disabled", false);
674 // no need for AssetCreator when assets are not desired
675 activatePoStage2(true);
677 liTable.showAssetCreator(activatePoStage2);
681 function activatePoStage2(noAssets) {
683 var want_refresh = false;
684 progressDialog.show(true);
685 progressDialog.attr("title", localeStrings.PO_ACTIVATING);
686 fieldmapper.standardRequest(
687 ["open-ils.acq", "open-ils.acq.purchase_order.activate"], {
690 openils.User.authtoken,
692 null, // vandelay options
694 no_assets : noAssets, // no bibs/volumes/copies required
695 zero_copy_activate : dojo.byId('acq-po-activate-zero-copies').checked
698 "onresponse": function(r) {
699 activatePoButton.attr("disabled", false);
700 want_refresh = Boolean(openils.Util.readResponse(r));
702 "oncomplete": function() {
703 progressDialog.hide();
704 progressDialog.attr("title", "");
706 location.href = location.href;
713 progressDialog.show(true);
716 fieldmapper.standardRequest(
717 ['open-ils.acq', 'open-ils.acq.purchase_order.split_by_lineitems'],
719 params: [openils.User.authtoken, PO.id()],
720 onresponse : function(r) {
721 list = openils.Util.readResponse(r);
723 oncomplete : function() {
724 progressDialog.hide();
726 location.href = oilsBasePath + '/acq/po/search/' +
733 progressDialog.hide();
738 function updatePoName() {
739 var value = prompt('Enter new purchase order name:', PO.name()); // TODO i18n
740 if(!value || value == PO.name()) return;
742 var orgs = fieldmapper.aou.descendantNodeList(PO.ordering_agency(), true);
743 new openils.PermaCrud().search('acqpo',
745 id : {'<>' : PO.id()},
747 ordering_agency : orgs
749 {async : true, oncomplete : function(r) {
750 var po = openils.Util.readResponse(r);
751 openils.Util.hide('acq-dupe-po-name');
753 if (po && (po = po[0])) {
754 var link = dojo.byId('acq-dupe-po-link');
755 openils.Util.show('acq-dupe-po-name');
756 var dupe_path = '/acq/po/view/' + po.id();
760 if (window.IAMBROWSER) {
765 link.onclick = function() {
767 var loc = xulG.url_prefix('XUL_BROWSER?url=') +
768 window.encodeURIComponent(
769 xulG.url_prefix('EG_WEB_BASE' + dupe_path)
773 {tab_name: '', browser:false},
776 show_nav_buttons : true,
777 show_print_button : true,
784 link.onclick = function() {
785 window.open(oilsBasePath + dupe_path, '_blank').focus();
790 updatePoName2(value);
797 function updatePoName2(value) {
800 oncomplete : function(r, cudResults) {
801 var stat = cudResults[0];
803 dojo.byId('acq-po-view-name').innerHTML = value;
808 openils.Util.addOnLoad(init);