1 dojo.require('dojo.date.locale');
2 dojo.require('dojo.date.stamp');
3 dojo.require('dijit.form.Button');
4 dojo.require('dijit.form.TextBox');
5 dojo.require('dijit.form.FilteringSelect');
6 dojo.require('dijit.form.Textarea');
7 dojo.require('dijit.ProgressBar');
8 dojo.require('openils.User');
9 dojo.require('openils.Util');
10 dojo.require('openils.acq.Lineitem');
11 dojo.require('openils.acq.PO');
12 dojo.require('openils.acq.Picklist');
13 dojo.require('openils.widget.AutoFieldWidget');
14 dojo.require('dojo.data.ItemFileReadStore');
15 dojo.require('openils.widget.ProgressDialog');
16 dojo.requireLocalization('openils.acq', 'acq');
17 var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
19 function AcqLiTable() {
23 this.toggleState = false;
24 this.tbody = dojo.byId('acq-lit-tbody');
26 this.authtoken = openils.User.authtoken;
27 this.rowTemplate = this.tbody.removeChild(dojo.byId('acq-lit-row'));
28 this.copyTbody = dojo.byId('acq-lit-li-details-tbody');
29 this.copyRow = this.copyTbody.removeChild(dojo.byId('acq-lit-li-details-row'));
30 this.copyBatchRow = dojo.byId('acq-lit-li-details-batch-row');
31 this.copyBatchWidgets = {};
32 this.liNotesTbody = dojo.byId('acq-lit-notes-tbody');
33 this.liNotesRow = this.liNotesTbody.removeChild(dojo.byId('acq-lit-notes-row'));
35 dojo.connect(acqLitLiActionsSelector, 'onChange',
37 self.applySelectedLiAction(this.attr('value'))
38 acqLitLiActionsSelector.attr('value', '_');
41 acqLitCreatePoSubmit.onClick = function() {
42 acqLitPoCreateDialog.hide();
43 self._createPO(acqLitPoCreateDialog.getValues());
46 acqLitSavePlButton.onClick = function() {
47 acqLitSavePlDialog.hide();
48 self._savePl(acqLitSavePlDialog.getValues());
51 dojo.byId('acq-lit-notes-new-button').onclick = function(){acqLitCreateLiNoteDialog.show();}
53 dojo.byId('acq-lit-select-toggle').onclick = function(){self.toggleSelect()};
54 dojo.byId('acq-lit-info-back-button').onclick = function(){self.show('list')};
55 dojo.byId('acq-lit-copies-back-button').onclick = function(){self.show('list')};
56 dojo.byId('acq-lit-notes-back-button').onclick = function(){self.show('list')};
58 this.reset = function() {
59 while(self.tbody.childNodes[0])
60 self.tbody.removeChild(self.tbody.childNodes[0]);
64 this.setNext = function(handler) {
65 var link = dojo.byId('acq-lit-next');
67 dojo.style(link, 'visibility', 'visible');
68 link.onclick = handler;
70 dojo.style(link, 'visibility', 'hidden');
74 this.setPrev = function(handler) {
75 var link = dojo.byId('acq-lit-prev');
77 dojo.style(link, 'visibility', 'visible');
78 link.onclick = handler;
80 dojo.style(link, 'visibility', 'hidden');
84 this.show = function(div) {
85 openils.Util.hide('acq-lit-table-div');
86 openils.Util.hide('acq-lit-info-div');
87 openils.Util.hide('acq-lit-li-details');
88 openils.Util.hide('acq-lit-notes-div');
91 openils.Util.show('acq-lit-table-div');
94 openils.Util.show('acq-lit-info-div');
97 openils.Util.show('acq-lit-li-details');
100 openils.Util.show('acq-lit-notes-div');
104 openils.Util.show(div);
108 this.hide = function() {
112 this.toggleSelect = function() {
114 dojo.forEach(self.selectors, function(i){i.checked = false});
116 dojo.forEach(self.selectors, function(i){i.checked = true});
117 self.toggleState = !self.toggleState;
121 /** @param all If true, assume all are selected */
122 this.getSelected = function(all) {
124 dojo.forEach(self.selectors,
127 selected.push(self.liCache[i.parentNode.parentNode.getAttribute('li')]);
133 this.setRowAttr = function(td, liWrapper, field, type) {
134 var val = liWrapper.findAttr(field, type || 'lineitem_marc_attr_definition') || '';
135 td.appendChild(document.createTextNode(val));
138 this.addLineitem = function(li) {
139 this.liCache[li.id()] = li;
141 // sort the lineitem notes on edit_time
142 if(!li.lineitem_notes()) li.lineitem_notes([]);
144 var liWrapper = new openils.acq.Lineitem({lineitem:li});
145 var row = self.rowTemplate.cloneNode(true);
146 row.setAttribute('li', li.id());
147 var tds = dojo.query('[attr]', row);
148 dojo.forEach(tds, function(td) {self.setRowAttr(td, liWrapper, td.getAttribute('attr'), td.getAttribute('attr_type'));});
149 dojo.query('[name=source_label]', row)[0].appendChild(document.createTextNode(li.source_label()));
151 var isbn = liWrapper.findAttr('isbn', 'lineitem_marc_attr_definition');
153 // XXX media prefix for added content
154 dojo.query('[name=jacket]', row)[0].setAttribute('src', '/opac/extras/ac/jacket/small/' + isbn);
157 dojo.query('[attr=title]', row)[0].onclick = function() {self.drawInfo(li.id())};
158 dojo.query('[name=copieslink]', row)[0].onclick = function() {self.drawCopies(li.id())};
159 dojo.query('[name=count]', row)[0].innerHTML = li.item_count();
160 dojo.query('[name=notes_count]', row)[0].innerHTML = li.lineitem_notes().length;
161 dojo.query('[name=noteslink]', row)[0].onclick = function() {self.drawLiNotes(li)};
163 var priceInput = dojo.query('[name=estimated_price]', row)[0];
164 var priceData = liWrapper.getPrice();
165 priceInput.value = (priceData) ? priceData.price : '';
166 priceInput.onchange = function() { self.updateLiPrice(priceInput, li) };
168 self.tbody.appendChild(row);
169 self.selectors.push(dojo.query('[name=selectbox]', row)[0]);
172 this.drawLiNotes = function(li) {
176 li.lineitem_notes().sort(
178 if(a.edit_time() < b.edit_time()) return 1;
184 while(this.liNotesTbody.childNodes[0])
185 this.liNotesTbody.removeChild(this.liNotesTbody.childNodes[0]);
188 acqLitCreateLiNoteSubmit.onClick = function() {
189 acqLitCreateLiNoteDialog.hide();
190 var value = acqLitCreateLiNoteDialog.getValues().note;
192 var note = new fieldmapper.acqlin();
195 li.lineitem_notes().push(note);
196 note.lineitem(li.id());
197 self.addLiNote(li, note);
200 dojo.byId('acq-lit-notes-save-button').onclick = function() {
201 self.updateLiNotes(li);
204 dojo.forEach(li.lineitem_notes(), function(note) { self.addLiNote(li, note) });
207 this.addLiNote = function(li, note) {
209 var row = self.liNotesRow.cloneNode(true);
210 dojo.query('[name=creator]', row)[0].innerHTML = note.creator() || ''; /* XXX */
211 dojo.query('[name=editor]', row)[0].innerHTML = note.editor() || ''; /* XXX */
213 if(note.create_time()) {
214 dojo.query('[name=create_time]', row)[0].innerHTML =
215 dojo.date.locale.format(
216 dojo.date.stamp.fromISOString(note.create_time()),
217 {formatLength:'short'});
220 if(note.edit_time()) {
221 dojo.query('[name=edit_time]', row)[0].innerHTML =
222 dojo.date.locale.format(
223 dojo.date.stamp.fromISOString(note.edit_time()),
224 {formatLength:'short'});
227 new openils.widget.AutoFieldWidget({
230 parentNode : dojo.query('[name=value]', row)[0],
231 widgetClass : 'dijit.form.Textarea',
234 dojo.connect(w, 'onChange',
236 note.value(this.attr('value'));
237 note.ischanged(true);
243 dojo.query('[name=delete]', row)[0].onclick = function() {
244 note.isdeleted(true);
245 self.liNotesTbody.removeChild(row);
248 self.liNotesTbody.appendChild(row);
251 this.updateLiNotes = function(li) {
252 progressDialog.show();
254 var notes = li.lineitem_notes().filter(
256 if(note.ischanged() || note.isnew() || note.isdeleted())
261 fieldmapper.standardRequest(
262 ['open-ils.acq', 'open-ils.acq.lineitem_note.cud.batch'],
264 params : [this.authtoken, notes],
265 onresponse : function(r) {
266 var resp = openils.Util.readResponse(r);
269 progressDialog.hide();
270 self.drawLiNotes(li);
274 progressDialog.update(resp);
276 var newnote = resp.note;
277 var oldnote = li.lineitem_notes().filter(function(n) { return (n.value() == newnote.value()) })[0];
278 var notes = li.lineitem_notes().filter(function(n) { return (n.value() != newnote.value()) });
280 if(!openils.Util.isTrue(oldnote.isdeleted()))
282 li.lineitem_notes(notes);
288 this.updateLiPrice = function(input, li) {
290 var price = input.value;
291 var liWrapper = new openils.acq.Lineitem({lineitem:li});
292 var oldPrice = liWrapper.getPrice() || null;
294 if(oldPrice) oldPrice = oldPrice.price;
295 if(price == oldPrice) return;
297 fieldmapper.standardRequest(
298 ['open-ils.acq', 'open-ils.acq.lineitem_local_attr.set'],
300 params : [this.authtoken, li.id(), 'estimated_price', price],
301 oncomplete : function(r) {
302 openils.Util.readResponse(r);
308 this.removeLineitem = function(liId) {
309 this.tbody.removeChild(dojo.query('[li='+liId+']', this.tbody)[0]);
310 delete this.liCache[liId];
313 this.drawInfo = function(liId) {
315 openils.acq.Lineitem.fetchAttrDefs(
317 self._fetchLineitem(liId, function(li){self._drawInfo(li);});
322 this._fetchLineitem = function(liId, handler) {
324 var li = this.liCache[liId];
325 if(li && li.marc() && li.lineitem_details())
328 fieldmapper.standardRequest(
329 ['open-ils.acq', 'open-ils.acq.lineitem.retrieve'],
332 params: [self.authtoken, liId, {
334 flesh_li_details: true,
335 flesh_fund_debit: true }],
337 oncomplete: function(r) {
338 var li = openils.Util.readResponse(r);
345 this._drawInfo = function(li) {
347 acqLitEditMarc.onClick = function() { self.editMarc(li); }
348 this.drawMarcHTML(li);
349 this.infoTbody = dojo.byId('acq-lit-info-tbody');
352 this.infoRow = this.infoTbody.removeChild(dojo.byId('acq-lit-info-row'));
353 while(this.infoTbody.childNodes[0])
354 this.infoTbody.removeChild(this.infoTbody.childNodes[0]);
356 for(var i = 0; i < li.attributes().length; i++) {
357 var attr = li.attributes()[i];
358 var row = this.infoRow.cloneNode(true);
360 var type = attr.attr_type().replace(/lineitem_(.*)_attr_definition/, '$1');
361 var name = openils.acq.Lineitem.attrDefs[type].filter(
363 return (a.code() == attr.attr_name());
365 ).pop().description();
367 dojo.query('[name=label]', row)[0].appendChild(document.createTextNode(name));
368 dojo.query('[name=value]', row)[0].appendChild(document.createTextNode(attr.attr_value()));
369 this.infoTbody.appendChild(row);
373 openils.Util.show('acq-lit-info-cat-link');
374 var link = dojo.byId('acq-lit-info-cat-link').getElementsByTagName('a')[0];
375 var href = link.getAttribute('href');
377 link.setAttribute('href', href + li.eg_bib_id());
379 openils.Util.hide('acq-lit-info-cat-link');
383 this.drawMarcHTML = function(li) {
384 fieldmapper.standardRequest(
385 ['open-ils.search', 'open-ils.search.biblio.record.html'],
387 params: [null, true, li.marc()],
388 oncomplete: function(r) {
389 dojo.byId('acq-lit-marc-div').innerHTML =
390 openils.Util.readResponse(r);
396 this.drawCopies = function(liId) {
400 this.copyWidgetCache = {};
402 acqLitSaveCopies.onClick = function() { self.saveCopyChanges(liId) };
403 acqLitBatchUpdateCopies.onClick = function() { self.batchCopyUpdate() };
405 while(this.copyTbody.childNodes[0])
406 this.copyTbody.removeChild(this.copyTbody.childNodes[0]);
408 this._drawBatchCopyWidgets();
410 this._fetchDistribFormulas(
412 openils.acq.Lineitem.fetchAttrDefs(
414 self._fetchLineitem(liId, function(li){self._drawCopies(li);});
421 this._fetchDistribFormulas = function(onload) {
422 if(this.distribForms) {
426 fieldmapper.standardRequest(
427 ['open-ils.acq', 'open-ils.acq.distribution_formula.ranged.retrieve.atomic'],
429 params: [openils.User.authtoken],
430 oncomplete: function(r) {
431 self.distribForms = openils.Util.readResponse(r);
432 self.distribFormulaStore =
433 new dojo.data.ItemFileReadStore(
434 {data:acqdf.toStoreData(self.distribForms)});
442 this._drawBatchCopyWidgets = function() {
443 var row = this.copyBatchRow;
444 dojo.forEach(['fund', 'owning_lib', 'location', 'circ_modifier', 'cn_label'],
446 if(self.copyBatchRowDrawn) {
447 self.copyBatchWidgets[field].attr('value', null);
449 var widget = new openils.widget.AutoFieldWidget({
452 parentNode : dojo.query('[name='+field+']', row)[0],
453 orgLimitPerms : ['CREATE_PICKLIST'],
454 dijitArgs : {required:false}
457 self.copyBatchWidgets[field] = widget.widget;
461 this.copyBatchRowDrawn = true;
464 this.batchCopyUpdate = function() {
466 var fields = ['fund', 'owning_lib', 'location', 'circ_modifier', 'cn_label'];
467 for(var k in this.copyWidgetCache) {
468 var cache = this.copyWidgetCache[k];
469 dojo.forEach(fields, function(f) {
470 var newval = self.copyBatchWidgets[f].attr('value');
471 if(newval) cache[f].attr('value', newval);
476 this._drawCopies = function(li) {
477 acqLitAddCopyCount.onClick = function() {
478 var count = acqLitCopyCountInput.attr('value');
479 for(var i = 0; i < count; i++)
482 if(li.lineitem_details().length > 0) {
483 dojo.forEach(li.lineitem_details(),
485 self.addCopy(li, copy);
493 this.virtCopyId = -1;
494 this.addCopy = function(li, copy) {
495 var row = this.copyRow.cloneNode(true);
496 this.copyTbody.appendChild(row);
500 copy = new fieldmapper.acqlid();
502 copy.id(this.virtCopyId--);
503 copy.lineitem(li.id());
506 this.copyCache[copy.id()] = copy;
507 row.setAttribute('copy_id', copy.id());
508 self.copyWidgetCache[copy.id()] = {};
510 dojo.forEach(['fund', 'owning_lib', 'location', 'barcode', 'cn_label', 'circ_modifier', 'note'],
512 var widget = new openils.widget.AutoFieldWidget({
516 parentNode : dojo.query('[name='+field+']', row)[0],
517 orgLimitPerms : ['CREATE_PICKLIST'],
521 // make sure we capture the value from any async widgets
522 function(w, ww) { copy[field](ww.getFormattedValue()) }
524 dojo.connect(widget.widget, 'onChange',
526 if(copy.isnew() || val != copy[field]()) {
527 // prevent setting ischanged() automatically on widget load for existing copies
528 copy[field](widget.getFormattedValue())
529 copy.ischanged(true);
533 self.copyWidgetCache[copy.id()][field] = widget.widget;
538 openils.Util.hide(dojo.query('[name=delete]', row)[0].parentNode);
540 dojo.query('[name=delete]', row)[0].onclick =
541 function() { self.deleteCopy(row) };
545 this.deleteCopy = function(row) {
546 var copy = this.copyCache[row.getAttribute('copy_id')];
547 copy.isdeleted(true);
549 delete this.copyCache[copy.id()];
550 this.copyTbody.removeChild(row);
553 this.saveCopyChanges = function(liId) {
557 openils.Util.show('acq-lit-update-copies-progress');
559 for(var id in this.copyCache) {
560 var c = this.copyCache[id];
561 if(c.isnew() || c.ischanged() || c.isdeleted()) {
562 if(c.id() < 0) c.id(null);
567 if(copies.length == 0)
570 fieldmapper.standardRequest(
571 ['open-ils.acq', 'open-ils.acq.lineitem_detail.cud.batch'],
573 params: [openils.User.authtoken, copies],
574 onresponse: function(r) {
575 var res = openils.Util.readResponse(r);
576 litUpdateCopiesProgress.update(res);
578 oncomplete: function() {
579 openils.Util.hide('acq-lit-update-copies-progress');
580 self.drawCopies(liId);
586 this.applySelectedLiAction = function(action) {
590 case 'delete_selected':
591 this._deleteLiList(self.getSelected());
596 if(!this.createPoProviderSelector) {
597 var widget = new openils.widget.AutoFieldWidget({
598 fmField : 'provider',
600 parentNode : dojo.byId('acq-lit-po-provider'),
601 orgLimitPerms : ['CREATE_PURCHASE_ORDER'],
604 function(w) { self.createPoProviderSelector = w; }
608 acqLitPoCreateDialog.show();
611 case 'save_picklist':
612 this._loadPLSelect();
613 acqLitSavePlDialog.show();
624 case 'rollback_receive_po':
625 this.rollbackPoReceive();
628 case 'create_assets':
634 this.createAssets = function() {
635 if(!this.isPO) return;
636 if(!confirm(localeStrings.CREATE_PO_ASSETS_CONFIRM)) return;
637 this.show('acq-lit-progress-numbers');
639 fieldmapper.standardRequest(
640 ['open-ils.acq', 'open-ils.acq.purchase_order.assets.create'],
642 params: [this.authtoken, this.isPO],
643 onresponse: function(r) {
644 var resp = openils.Util.readResponse(r);
645 self._updateProgressNumbers(resp, true);
651 this.printPO = function() {
652 if(!this.isPO) return;
653 progressDialogInd.show();
654 fieldmapper.standardRequest(
655 ['open-ils.acq', 'open-ils.acq.purchase_order.format'],
657 params: [this.authtoken, this.isPO, 'html'],
658 oncomplete: function(r) {
659 progressDialogInd.hide();
660 var evt = openils.Util.readResponse(r);
661 if(evt && evt.template_output()) {
662 win = window.open('','', 'resizable,width=800,height=600,scrollbars=1');
663 win.document.body.innerHTML = evt.template_output().data();
671 this.receivePO = function() {
672 if(!this.isPO) return;
673 this.show('acq-lit-progress-numbers');
675 fieldmapper.standardRequest(
676 ['open-ils.acq', 'open-ils.acq.purchase_order.receive'],
678 params: [this.authtoken, this.isPO],
679 onresponse : function(r) {
680 var resp = openils.Util.readResponse(r);
681 self._updateProgressNumbers(resp, true);
687 this.rollbackPoReceive = function() {
688 if(!this.isPO) return;
689 if(!confirm(localeStrings.ROLLBACK_PO_RECEIVE_CONFIRM)) return;
690 this.show('acq-lit-progress-numbers');
692 fieldmapper.standardRequest(
693 ['open-ils.acq', 'open-ils.acq.purchase_order.receive.rollback'],
695 params: [this.authtoken, this.isPO],
696 onresponse : function(r) {
697 var resp = openils.Util.readResponse(r);
698 self._updateProgressNumbers(resp, true);
704 this._updateProgressNumbers = function(resp, reloadOnComplete) {
706 dojo.byId('acq-pl-lit-li-processed').innerHTML = resp.li;
707 dojo.byId('acq-pl-lit-lid-processed').innerHTML = resp.lid;
708 dojo.byId('acq-pl-lit-debits-processed').innerHTML = resp.debits_accrued;
709 dojo.byId('acq-pl-lit-bibs-processed').innerHTML = resp.bibs;
710 dojo.byId('acq-pl-lit-indexed-processed').innerHTML = resp.indexed;
711 dojo.byId('acq-pl-lit-copies-processed').innerHTML = resp.copies;
712 if(resp.complete && reloadOnComplete)
713 location.href = location.href;
717 this._createPO = function(fields) {
718 this.show('acq-lit-progress-numbers');
719 var po = new fieldmapper.acqpo();
720 po.provider(this.createPoProviderSelector.attr('value'));
722 var selected = this.getSelected( (fields.create_from == 'all') );
723 if(selected.length == 0) return;
725 var max = selected.length * 3;
728 fieldmapper.standardRequest(
729 ['open-ils.acq', 'open-ils.acq.purchase_order.create'],
732 openils.User.authtoken,
735 lineitems : selected.map(function(li) { return li.id() }),
736 create_assets : fields.create_assets[0],
740 onresponse : function(r) {
741 var resp = openils.Util.readResponse(r);
742 self._updateProgressNumbers(resp);
744 location.href = oilsBasePath + '/eg/acq/po/view/' + resp.purchase_order.id();
750 this._deleteLiList = function(list, idx) {
751 if(idx == null) idx = 0;
752 if(idx >= list.length) return;
753 var liId = list[idx].id();
754 fieldmapper.standardRequest(
755 ['open-ils.acq', 'open-ils.acq.lineitem.delete'],
757 params: [openils.User.authtoken, liId],
758 oncomplete: function(r) {
759 self.removeLineitem(liId);
760 self._deleteLiList(list, ++idx);
766 this.editMarc = function(li) {
768 /* To run in Firefox directly, must set signed.applets.codebase_principal_support
769 to true in about:config */
771 netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
772 win = window.open('/xul/server/cat/marcedit.xul'); // XXX version?
776 record : {marc : li.marc()},
778 label: 'Save Record', // XXX I18N
779 func: function(xmlString) {
781 fieldmapper.standardRequest(
782 ['open-ils.acq', 'open-ils.acq.lineitem.update'],
784 params: [openils.User.authtoken, li],
785 oncomplete: function(r) {
786 openils.Util.readResponse(r);
788 self.drawInfo(li.id())
798 this._savePl = function(values) {
800 var selected = this.getSelected( (values.which == 'all') );
801 openils.Util.show('acq-lit-generic-progress');
803 if(values.new_name) {
804 openils.acq.Picklist.create(
805 {name: values.new_name},
807 self._updateLiList(id, selected, 0,
809 location.href = oilsBasePath + '/eg/acq/picklist/view/' + id;
813 } else if(values.existing_pl) {
814 // update lineitems to use an existing picklist
815 self._updateLiList(values.existing_pl, selected, 0,
817 location.href = oilsBasePath + '/eg/acq/picklist/view/' + values.existing_pl;
822 this._updateLiList = function(pl, list, idx, oncomplete) {
823 if(idx >= list.length) return oncomplete();
826 litGenericProgress.update({maximum: list.length, progress: idx});
827 new openils.acq.Lineitem({lineitem:li}).update(
829 self._updateLiList(pl, list, ++idx, oncomplete);
834 this._loadPLSelect = function() {
835 if(this._plSelectLoaded) return;
837 function handleResponse(r) {
838 plList.push(r.recv().content());
840 var method = 'open-ils.acq.picklist.user.retrieve';
841 fieldmapper.standardRequest(
842 ['open-ils.acq', method],
844 params: [this.authtoken],
845 onresponse: handleResponse,
846 oncomplete: function() {
847 self._plSelectLoaded = true;
848 acqLitAddExistingSelect.store =
849 new dojo.data.ItemFileReadStore({data:acqpl.toStoreData(plList)});
850 acqLitAddExistingSelect.setValue();