1 dojo.require('dojo.date.locale');
2 dojo.require('dojo.date.stamp');
3 dojo.require('dojo.cookie');
4 dojo.require('dijit.form.CheckBox');
5 dojo.require('dijit.form.Button');
6 dojo.require('dijit.form.CurrencyTextBox');
7 dojo.require('dijit.form.NumberTextBox');
8 dojo.require('openils.User');
9 dojo.require('openils.Util');
10 dojo.require('openils.CGI');
11 dojo.require('openils.PermaCrud');
12 dojo.require('openils.widget.EditPane');
13 dojo.require('openils.widget.AutoFieldWidget');
14 dojo.require('openils.widget.ProgressDialog');
15 dojo.require('openils.acq.Lineitem');
17 dojo.requireLocalization('openils.acq', 'acq');
18 var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
20 var fundLabelFormat = ['${0} (${1})', 'code', 'year'];
21 var fundSearchFormat = ['${0} (${1})', 'code', 'year'];
23 var cgi = new openils.CGI();
24 var pcrud = new openils.PermaCrud();
39 var widgetRegistry = {acqie : {}, acqii : {}};
41 var searchInitDone = false;
45 function nodeByName(name, context) {
46 return dojo.query('[name='+name+']', context)[0];
51 attachLi = cgi.param('attach_li') || [];
52 if (!dojo.isArray(attachLi))
53 attachLi = [attachLi];
55 attachPo = cgi.param('attach_po') || [];
56 if (!dojo.isArray(attachPo))
57 attachPo = [attachPo];
59 focusLineitem = new openils.CGI().param('focus_li');
61 totalInvoicedBox = dojo.byId('acq-total-invoiced-box');
62 totalPaidBox = dojo.byId('acq-total-paid-box');
63 balanceOwedBox = dojo.byId('acq-total-balance-box');
65 itemTypes = pcrud.retrieveAll('aiit');
67 dojo.byId('acq-invoice-summary-toggle-off').onclick = function() {
68 openils.Util.hide(dojo.byId('acq-invoice-summary'));
69 openils.Util.show(dojo.byId('acq-invoice-summary-small'));
72 dojo.byId('acq-invoice-summary-toggle-on').onclick = function() {
73 openils.Util.show(dojo.byId('acq-invoice-summary'));
74 openils.Util.hide(dojo.byId('acq-invoice-summary-small'));
77 if(cgi.param('create')) {
80 // show summary info by default for new invoices
81 dojo.byId('acq-invoice-summary-toggle-on').onclick();
84 dojo.byId('acq-invoice-summary-toggle-off').onclick();
85 fieldmapper.standardRequest(
86 ['open-ils.acq', 'open-ils.acq.invoice.retrieve.authoritative'],
88 params : [openils.User.authtoken, invoiceId],
89 oncomplete : function(r) {
90 invoice = openils.Util.readResponse(r);
97 extraCopiesFund = new openils.widget.AutoFieldWidget({
100 searchFilter : {active : 't'},
101 labelFormat : fundLabelFormat,
102 searchFormat : fundSearchFormat,
103 dijitArgs : {required : true},
104 parentNode : dojo.byId('acq-invoice-extra-copies-fund')
106 extraCopiesFund.build();
109 function renderInvoice() {
111 // in create mode, let the LI or PO render the invoice with seed data
112 if( !(cgi.param('create') && (attachPo.length || attachLi.length)) ) {
113 invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), invoice);
116 dojo.byId('acq-invoice-new-item').onclick = function() {
117 var item = new fieldmapper.acqii();
118 item.id(virtualId--);
120 addInvoiceItem(item);
125 if(invoice && openils.Util.isTrue(invoice.complete())) {
127 dojo.forEach( // hide widgets that should not be visible for a completed invoice
128 dojo.query('.hide-complete'),
129 function(node) { openils.Util.hide(node); }
132 new openils.User().getPermOrgList(
133 'ACQ_INVOICE_REOPEN',
135 if(orgs.indexOf(invoice.receiver()) >= 0)
136 openils.Util.show('acq-invoice-reopen-button-wrapper', 'inline');
143 // display items and entries in ID order
144 // which effectively equates to add order.
145 function idsort(a, b) { return a.id() < b.id() ? -1 : 1 }
149 invoice.items().sort(idsort),
151 addInvoiceItem(item);
156 invoice.entries().sort(idsort),
158 addInvoiceEntry(entry);
163 if(attachLi.length) doAttachLi();
164 if(attachPo.length) doAttachPo(0);
167 function doAttachLi(skipInit) {
169 //var invoiceArgs = {provider : lineitem.provider(), shipper : lineitem.provider()};
170 if(cgi.param('create') && !skipInit) {
172 // use the first LI in the list to determine the default provider
173 fieldmapper.standardRequest(
174 ['open-ils.acq', 'open-ils.acq.lineitem.retrieve.authoritative'],
176 params : [openils.User.authtoken, attachLi[0], {clear_marc:1}],
177 oncomplete : function(r) {
178 var li = openils.Util.readResponse(r);
179 invoicePane = drawInvoicePane(
180 dojo.byId('acq-view-invoice-div'), null,
181 {provider : li.provider(), shipper : li.provider()}
188 dojo.forEach(attachLi,
190 var entry = new fieldmapper.acqie();
191 entry.id(virtualId--);
194 addInvoiceEntry(entry);
199 function doAttachPo(idx) {
201 if (idx == attachPo.length) return;
202 var poId = attachPo[idx];
204 fieldmapper.standardRequest(
205 ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
208 openils.User.authtoken, poId,
209 {flesh_lineitem_ids : true, flesh_po_items : true}
211 oncomplete: function(r) {
212 var po = openils.Util.readResponse(r);
214 if(cgi.param('create') && idx == 0) {
215 // render the invoice using some seed data from the first PO
216 var invoiceArgs = {provider : po.provider(), shipper : po.provider()};
217 invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), null, invoiceArgs);
220 dojo.forEach(po.lineitems(),
222 var entry = new fieldmapper.acqie();
223 entry.id(virtualId--);
225 entry.lineitem(lineitem);
226 entry.purchase_order(po);
227 addInvoiceEntry(entry);
231 dojo.forEach(po.po_items(),
233 var item = new fieldmapper.acqii();
234 item.id(virtualId--);
236 item.fund(poItem.fund());
237 item.title(poItem.title());
238 item.author(poItem.author());
239 item.note(poItem.note());
240 item.inv_item_type(poItem.inv_item_type());
241 item.purchase_order(po);
242 item.po_item(poItem);
243 addInvoiceItem(item);
253 function performSearch(pageDir, clearFirst) {
255 clearSearchResTable();
257 var searchObject = termManager.buildSearchObject();
258 dojo.cookie('invs', base64Encode(searchObject));
259 dojo.cookie('invc', dojo.byId("acq-unified-conjunction").getValue());
261 if (pageDir == 0) { // new search
262 resultsLoader.displayOffset = 0;
264 resultsLoader.displayOffset += pageDir * resultsLoader.displayLimit;
267 if (resultsLoader.displayOffset == 0) {
268 openils.Util.hide('acq-inv-search-prev');
270 openils.Util.show('acq-inv-search-prev', 'inline');
273 if (dojo.byId('acq-invoice-search-limit-invoiceable').checked) {
274 if (!searchObject.jub)
275 searchObject.jub = [];
277 // exclude lineitems that are "cancelled" (sidebar: 'Mericans spell it 'canceled')
278 searchObject.jub.push({state : 'cancelled', '__not' : true});
280 // exclude lineitems already linked to this invoice
281 if (invoice && invoice.id() > 0) {
282 if (!searchObject.acqinv)
283 searchObject.acqinv = [];
284 searchObject.acqinv.push({id : invoice.id(), '__not' : true});
287 // limit to lineitems that have invoiceable copies
288 searchObject.acqlisumi = [{item_count : 1, '_gte' : true}];
290 // limit to provider if a provider is selected
291 var provider = invoicePane.getFieldValue('provider');
293 if (!searchObject.jub.filter(function(i) { return i.provider != null }).length)
294 searchObject.jub.push({provider : provider});
298 if (dojo.byId('acq-invoice-search-sort-title').checked) {
299 uriManager.order_by =
300 [ {"class": "acqlia", "field":"attr_value", "transform":"first"} ];
303 resultsLoader.lastSearch = searchObject;
304 resultManager.go(searchObject)
305 console.log('Lineitem Search: ' + js2JSON(searchObject));
306 focusLastSearchInput();
310 function renderUnifiedSearch() {
312 if (!searchInitDone) {
314 searchInitDone = true;
315 termManager = new TermManager();
316 resultManager = new ResultManager();
317 resultsLoader = new searchResultsLoader();
318 uriManager = new URIManager();
320 // define custom lineitem result handler
321 resultManager.result_types = {
323 "search_options": { "id_list": true },
324 "revealer": function() { },
325 "finisher": function() {
326 resultsLoader.batch_length = resultManager.count_results;
328 "adder": function(li) {
329 resultsLoader.addLineitem(li);
331 "interface": resultsLoader
334 "revealer": function() { }
339 resultManager.no_results_popup = true;
340 resultManager.submitter = smartSearchSubmitter;
342 var searchObject = dojo.cookie('invs');
343 console.log('loaded ' + searchObject);
345 // if there is a search object cookie, populate the search form
346 termManager.reflect(base64Decode(searchObject));
347 dojo.byId("acq-unified-conjunction").setValue(dojo.cookie('invc'));
349 console.log('adding row');
350 termManager.addRow();
354 dojo.addClass(dojo.byId('oils-acq-invoice-table'), 'hidden');
355 dojo.removeClass(dojo.byId('oils-acq-invoice-search'), 'hidden');
356 focusLastSearchInput();
359 function focusLastSearchInput() {
360 // TODO: see about making this better and moving it into search/unified.js
361 var wnodes = dojo.query('[name=widget]');
362 var inputNode = wnodes.item(wnodes.length - 1).firstChild;
372 var resultsTbody, resultsRow;
373 function searchResultsLoader() {
374 this.displayOffset = 0;
375 this.displayLimit = 10;
378 resultsTbody = dojo.byId('acq-invoice-search-results-tbody');
379 resultsRow = resultsTbody.removeChild(dojo.byId('acq-invoice-search-results-tr'));
382 this.addLineitem = function(li_id) {
383 console.log('Adding search result lineitem ' + li_id);
384 var row = resultsRow.cloneNode(true);
385 resultsTbody.appendChild(row);
386 var checkbox = dojo.query('[name=search-results-checkbox]', row)[0];
387 checkbox.setAttribute('lineitem', li_id);
389 // this lineitem is already part of the invoice
390 if (dojo.query('[entry_lineitem_row=' + li_id + ']')[0]) {
391 checkbox.disabled = true;
392 dojo.addClass(checkbox.parentNode, 'search-results-already-invoiced');
395 openils.acq.Lineitem.fetchAndRender(
398 dojo.query('[name=search-results-content-div]', row)[0].innerHTML = html;
404 function addSelectedToInvoice() {
405 var inputs = dojo.query('[name=search-results-checkbox]');
409 if (checkbox.checked) {
410 attachLi.push(checkbox.getAttribute('lineitem'));
411 checkbox.disabled = true;
412 checkbox.checked = false;
413 dojo.addClass(checkbox.parentNode, 'search-results-already-invoiced');
420 allResSelected = false;
421 function clearSearchResTable() {
422 allResSelected = false;
423 while (resultsTbody.childNodes[0])
424 resultsTbody.removeChild(resultsTbody.childNodes[0]);
427 function selectSearchResults() {
428 allResSelected = !allResSelected;
429 dojo.query('[name=search-results-checkbox]').forEach(
430 function(input) { input.checked = allResSelected });
433 function updateTotalCost() {
436 for(var id in widgetRegistry.acqii)
437 if(!widgetRegistry.acqii[id]._object.isdeleted())
438 totalCost += Number(widgetRegistry.acqii[id].cost_billed.getFormattedValue());
439 for(var id in widgetRegistry.acqie)
440 if(!widgetRegistry.acqie[id]._object.isdeleted())
441 totalCost += Number(widgetRegistry.acqie[id].cost_billed.getFormattedValue());
442 totalInvoicedBox.innerHTML = totalCost.toFixed(2);
445 for(var id in widgetRegistry.acqii)
446 if(!widgetRegistry.acqii[id]._object.isdeleted())
447 totalPaid += Number(widgetRegistry.acqii[id].amount_paid.getFormattedValue());
448 for(var id in widgetRegistry.acqie)
449 if(!widgetRegistry.acqie[id]._object.isdeleted())
450 totalPaid += Number(widgetRegistry.acqie[id].amount_paid.getFormattedValue());
451 totalPaidBox.innerHTML = totalPaid.toFixed(2);
453 var buttonsDisabled = false;
455 if(totalPaid > totalCost || totalPaid < 0) {
456 openils.Util.addCSSClass(totalPaidBox, 'acq-invoice-invalid-amount');
457 invoiceSaveButton.attr('disabled', true);
458 invoiceProrateButton.attr('disabled', true);
459 buttonsDisabled = true;
461 openils.Util.removeCSSClass(totalPaidBox, 'acq-invoice-invalid-amount');
462 invoiceSaveButton.attr('disabled', false);
463 invoiceProrateButton.attr('disabled', false);
467 openils.Util.addCSSClass(totalInvoicedBox, 'acq-invoice-invalid-amount');
468 invoiceSaveButton.attr('disabled', true);
469 invoiceProrateButton.attr('disabled', true);
471 openils.Util.removeCSSClass(totalInvoicedBox, 'acq-invoice-invalid-amount');
472 if(!buttonsDisabled) {
473 invoiceSaveButton.attr('disabled', false);
474 invoiceProrateButton.attr('disabled', false);
478 if(totalPaid == totalCost) { // XXX: too rigid?
479 invoiceCloseButton.attr('disabled', false);
481 invoiceCloseButton.attr('disabled', true);
484 balanceOwedBox.innerHTML = (totalCost - totalPaid).toFixed(2);
486 updateExpectedCost();
490 function registerWidget(obj, field, widget, callback) {
491 var blob = widgetRegistry[obj.classname];
493 blob[obj.id()] = {_object : obj};
494 blob[obj.id()][field] = widget;
497 dojo.connect(w, 'onChange',
503 if(callback) callback(w, ww);
509 function addInvoiceItem(item) {
510 itemTbody = dojo.byId('acq-invoice-item-tbody');
511 if(itemTemplate == null) {
512 itemTemplate = itemTbody.removeChild(dojo.byId('acq-invoice-item-template'));
515 var row = itemTemplate.cloneNode(true);
516 var itemType = itemTypes.filter(function(t) { return (t.code() == item.inv_item_type()) })[0];
519 ['title', 'author', 'cost_billed', 'amount_paid'],
523 if(field == 'title' || field == 'author') {
524 //args = {style : 'width:10em'};
525 } else if(field == 'cost_billed' || field == 'amount_paid') {
526 args = {required : true, style : 'width: 8em'};
532 new openils.widget.AutoFieldWidget({
536 readOnly : invoice && openils.Util.isTrue(invoice.complete()),
538 parentNode : nodeByName(field, row)
545 /* ----------- fund -------------- */
550 labelFormat : fundLabelFormat,
551 searchFormat : fundSearchFormat,
552 readOnly : invoice && openils.Util.isTrue(invoice.complete()),
553 dijitArgs : {required : true},
554 parentNode : nodeByName('fund', row)
557 if(item.fund_debit()) {
558 fundArgs.searchFilter = {'-or' : [{active : 't'}, {id : item.fund()}]};
560 fundArgs.searchFilter = {active : 't'}
561 if(itemType && openils.Util.isTrue(itemType.prorate()))
562 fundArgs.dijitArgs = {disabled : true};
565 var fundWidget = new openils.widget.AutoFieldWidget(fundArgs);
566 registerWidget(item, 'fund', fundWidget);
568 /* ---------- inv_item_type ------------- */
572 // read-only item view for items that were the result of a po-item
573 var po = item.purchase_order();
574 var po_item = item.po_item();
575 var node = nodeByName('inv_item_type', row);
576 var itemType = itemTypes.filter(function(t) { return (t.code() == item.inv_item_type()) })[0];
577 orderDate = (!po.order_date()) ? '' :
578 dojo.date.locale.format(dojo.date.stamp.fromISOString(po.order_date()), {selector:'date'});
580 node.innerHTML = dojo.string.substitute(
581 localeStrings.INVOICE_ITEM_PO_DETAILS,
588 po_item.estimated_cost()
597 new openils.widget.AutoFieldWidget({
599 fmField : 'inv_item_type',
600 parentNode : nodeByName('inv_item_type', row),
601 readOnly : invoice && openils.Util.isTrue(invoice.complete()),
602 dijitArgs : {required : true}
605 // When the inv_item_type is set to prorate=true, don't allow the user the edit the fund
606 // since this charge will be prorated against (potentially) multiple funds
607 dojo.connect(w, 'onChange',
609 if(!item.fund_debit()) {
610 var itemType = itemTypes.filter(function(t) { return (t.code() == w.attr('value')) })[0];
611 if(!itemType) return;
612 if(openils.Util.isTrue(itemType.prorate())) {
613 fundWidget.widget.attr('disabled', true);
614 fundWidget.widget.attr('value', '');
616 fundWidget.widget.attr('disabled', false);
625 nodeByName('delete', row).onclick = function() {
626 var cost = widgetRegistry.acqii[item.id()].cost_billed.getFormattedValue();
627 var msg = dojo.string.substitute(
628 localeStrings.INVOICE_CONFIRM_ITEM_DELETE, [
630 widgetRegistry.acqii[item.id()].inv_item_type.getFormattedValue() || ''
633 if(!confirm(msg)) return;
634 itemTbody.removeChild(row);
635 item.isdeleted(true);
637 delete widgetRegistry.acqii[item.id()];
641 itemTbody.appendChild(row);
645 function updateReceiveLink(li) {
647 return; /* can't do this with unsaved invoices */
649 var link = dojo.byId("acq-view-invoice-receive-link");
650 if (link.onclick) return; /* only need to do this once */
652 /* don't do this if there's nothing receivable on the lineitem */
653 if (li.order_summary().recv_count() + li.order_summary().cancel_count() >=
654 li.order_summary().item_count())
657 openils.Util.show("acq-view-invoice-receive");
658 link.onclick = function() { location.href = oilsBasePath + '/acq/invoice/receive/' + invoiceId; };
662 * Ensures focusLineitem is in view and causes a brief
663 * border around the lineitem to come to life then fade.
666 if (!focusLineitem) return;
668 // set during addLineitem()
669 var node = dojo.byId('li-title-ref-' + focusLineitem);
671 console.log('focus: li-title-ref-' + focusLineitem + ' : ' + node);
673 // LI may not yet be rendered
676 console.log('focusing ' + focusLineitem);
678 // prevent numerous re-focuses
679 focusLineitem = null;
681 // causes the full row to be visible
682 dijit.scrollIntoView(node);
684 dojo.require('dojox.fx');
688 dojox.fx.highlight({color : '#BB4433', node : node, duration : 2000}).play();
694 // expected cost is totalCostInvoiced + totalCostNotYetInvoiced
695 function updateExpectedCost() {
697 var cost = Number(totalInvoicedBox.innerHTML || 0);
699 // for any LI's that are not yet billed (i.e. filled in)
700 // use the total expected cost for that lineitem.
701 for(var id in widgetRegistry.acqie) {
702 var entry = widgetRegistry.acqie[id]._object;
703 if(!entry.isdeleted()) {
704 if (Number(widgetRegistry.acqie[id].cost_billed.getFormattedValue()) == 0) {
705 var li = entry.lineitem();
707 Number(li.order_summary().estimated_amount()) -
708 Number(li.order_summary().paid_amount());
713 dojo.byId('acq-invoice-summary-cost').innerHTML = cost.toFixed(2);
716 var invoicEntryWidgets = {};
717 function addInvoiceEntry(entry) {
718 console.log('Adding new entry for lineitem ' + entry.lineitem());
720 openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-header'), 'hidden');
721 openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-thead'), 'hidden');
722 openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-tbody'), 'hidden');
724 dojo.byId('acq-invoice-summary-count').innerHTML =
725 Number(dojo.byId('acq-invoice-summary-count').innerHTML) + 1;
727 entryTbody = dojo.byId('acq-invoice-entry-tbody');
728 if(entryTemplate == null) {
729 entryTemplate = entryTbody.removeChild(dojo.byId('acq-invoice-entry-template'));
732 if(dojo.query('[lineitem=' + entry.lineitem() +']', entryTbody)[0])
733 // Is it ever valid to have multiple entries for 1 lineitem in a single invoice?
736 var row = entryTemplate.cloneNode(true);
737 row.setAttribute('lineitem', entry.lineitem());
738 row.setAttribute('entry_lineitem_row', entry.lineitem());
740 openils.acq.Lineitem.fetchAndRender(
741 entry.lineitem(), {},
744 entry.purchase_order(li.purchase_order());
745 nodeByName('title_details', row).innerHTML = html;
747 nodeByName('title_details', row).parentNode.id = 'li-title-ref-' + li.id();
748 console.log(dojo.byId('li-title-ref-' + li.id()));
750 updateReceiveLink(li);
752 // set some default values if otherwise unset
753 if (!invoicePane.getFieldValue('receiver')) {
754 invoicePane.setFieldValue('receiver', li.purchase_order().ordering_agency());
756 if (!invoicePane.getFieldValue('provider')) {
757 invoicePane.setFieldValue('provider', li.purchase_order().provider());
761 ['inv_item_count', 'phys_item_count', 'cost_billed', 'amount_paid'],
763 var dijitArgs = {required : true, constraints : {min: 0}, style : 'width:6em'};
764 if(field.match(/count/)) {
765 dijitArgs.style = 'width:4em;';
767 dijitArgs.style = 'width:9em;';
769 if(entry.isnew() && field == 'phys_item_count') {
770 // by default, attempt to pay for all non-canceled and as-of-yet-un-invoiced items
771 var count = Number(li.order_summary().item_count() || 0) -
772 Number(li.order_summary().cancel_count() || 0) -
773 Number(li.order_summary().invoice_count() || 0);
774 if(count < 0) count = 0;
775 dijitArgs.value = count;
780 new openils.widget.AutoFieldWidget({
784 dijitArgs : dijitArgs,
785 readOnly : invoice && openils.Util.isTrue(invoice.complete()),
786 parentNode : nodeByName(field, row)
790 if(field == 'phys_item_count') {
791 dojo.connect(w, 'onChange',
793 // staff entered a higher number in the receive field than was originally ordered
794 // taking into account already invoiced items
795 var extra = Number(this.attr('value')) -
796 (Number(entry.lineitem().item_count()) - Number(entry.lineitem().order_summary().invoice_count()));
798 storeExtraCopies(entry, extra);
804 if(field == 'inv_item_count' || field == 'cost_billed') {
805 setPerCopyPrice(row, entry);
806 // update the per-copy count as invoice count and cost billed change
807 dojo.connect(w, 'onChange', function() { setPerCopyPrice(row, entry) } );
816 if (focusLineitem == li.id())
821 nodeByName('detach', row).onclick = function() {
822 var cost = widgetRegistry.acqie[entry.id()].cost_billed.getFormattedValue();
824 dojo.forEach(['isbn', 'upc', 'issn'],
826 var val = liMarcAttr(entry.lineitem(), ident);
827 if(val) idents.push(val);
831 var msg = dojo.string.substitute(
832 localeStrings.INVOICE_CONFIRM_ENTRY_DETACH, [
834 liMarcAttr(entry.lineitem(), 'title'),
835 liMarcAttr(entry.lineitem(), 'author'),
839 if(!confirm(msg)) return;
840 entryTbody.removeChild(row);
841 entry.isdeleted(true);
843 delete widgetRegistry.acqie[entry.id()];
847 entryTbody.appendChild(row);
850 function setPerCopyPrice(row, entry) {
851 var inv_w = widgetRegistry.acqie[entry.id()].inv_item_count;
852 var bill_w = widgetRegistry.acqie[entry.id()].cost_billed;
854 if (inv_w && bill_w) {
855 var invoiced = Number(inv_w.getFormattedValue());
856 var billed = Number(bill_w.getFormattedValue());
857 console.log(invoiced + ' : ' + billed);
859 nodeByName('amount_paid_per_copy', row).innerHTML = (billed / invoiced).toFixed(2);
861 nodeByName('amount_paid_per_copy', row).innerHTML = '0.00';
866 function liMarcAttr(lineitem, name) {
867 var attr = lineitem.attributes().filter(
870 attr.attr_type() == 'lineitem_marc_attr_definition' &&
871 attr.attr_name() == name)
875 return (attr) ? attr.attr_value() : '';
878 function saveChanges(args) {
880 createExtraCopies(function() { saveChangesPartTwo(args); });
883 // Define a helper function to 'unflesh' sub-objects from an fmclass object.
884 // 'this' specifies the object; the arguments specify a list of names of
888 dojo.forEach(arguments, function (n) {
890 if (_ !== null && typeof _ === 'object')
895 function saveChangesPartTwo(args) {
899 invoice.complete('f');
903 // Prepare an invoice for submission
905 invoice = new fieldmapper.acqinv();
908 invoice.ischanged(true); // for now, just always update
911 var e = invoicePane.mapValues(function (n, v) { invoice[n](v); });
912 if (e instanceof Error) {
918 invoice.complete('t');
921 // Prepare any charge items
922 var updateItems = [];
923 for(var id in widgetRegistry.acqii) {
924 var reg = widgetRegistry.acqii[id];
925 var item = reg._object;
926 if(item.ischanged() || item.isnew() || item.isdeleted()) {
927 updateItems.push(item);
928 if(item.isnew()) item.id(null);
929 for(var field in reg) {
930 if(field != '_object')
931 item[field]( reg[field].getFormattedValue() );
934 unflesh.call(item, 'purchase_order');
939 // Prepare any line items
940 var updateEntries = [];
941 for(var id in widgetRegistry.acqie) {
942 var reg = widgetRegistry.acqie[id];
943 var entry = reg._object;
944 if(entry.ischanged() || entry.isnew() || entry.isdeleted()) {
945 updateEntries.push(entry);
946 if(entry.isnew()) entry.id(null);
948 for(var field in reg) {
949 if(field != '_object')
950 entry[field]( reg[field].getFormattedValue() );
953 unflesh.call(entry, 'purchase_order', 'lineitem');
958 progressDialog.show(true);
959 fieldmapper.standardRequest(
960 ['open-ils.acq', 'open-ils.acq.invoice.update'],
962 params : [openils.User.authtoken, invoice, updateEntries, updateItems],
963 oncomplete : function(r) {
964 progressDialog.hide();
965 var invoice = openils.Util.readResponse(r);
968 return prorateInvoice(invoice);
970 location.href = oilsBasePath + '/acq/invoice/view?create=1';
972 location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
980 function prorateInvoice(invoice) {
981 if(!confirm(localeStrings.INVOICE_CONFIRM_PRORATE)) return;
982 progressDialog.show(true);
984 fieldmapper.standardRequest(
985 ['open-ils.acq', 'open-ils.acq.invoice.apply_prorate'],
987 params : [openils.User.authtoken, invoice.id()],
988 oncomplete : function(r) {
989 progressDialog.hide();
990 var invoice = openils.Util.readResponse(r);
992 location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
999 function storeExtraCopies(entry, numExtra) {
1001 dojo.byId('acq-invoice-extra-copies-message').innerHTML =
1002 dojo.string.substitute(
1003 localeStrings.INVOICE_EXTRA_COPIES, [numExtra]);
1006 addCopyHandler = dojo.connect(
1010 extraCopies[entry.lineitem().id()] = {
1011 numExtra : numExtra,
1012 fund : extraCopiesFund.widget.attr('value')
1014 extraItemsDialog.hide();
1015 dojo.disconnect(addCopyHandler);
1023 widgetRegistry.acqie[entry.id()].phys_item_count.widget.attr('value', '');
1024 extraItemsDialog.hide()
1028 extraItemsDialog.show();
1031 function validateInvIdent(inv_ident, provider, receiver) {
1032 if (!(inv_ident && provider && receiver)) {
1033 console.info("not enough information to pre-validate inv_ident");
1037 openils.Util.show("ident-validation-spinner", "inline");
1038 var pcrud = new openils.PermaCrud();
1040 "acqinv", {"inv_ident": inv_ident, "provider": provider}, {
1041 "oncomplete": function(r) {
1042 openils.Util.hide("ident-validation-spinner");
1044 /* This could throw an event about the user not having perms,
1045 * but in such a case the whole interface is already busted
1047 r = openils.Util.readResponse(r);
1049 var w = invoicePane.getFieldWidget("inv_ident").widget;
1051 alert(localeStrings.INVOICE_IDENT_COLLIDE);
1052 w.validator = function() { return false; };
1055 w.validator = function() { return true; };
1065 function drawInvoicePane(parentNode, inv, args) {
1072 recv_date : {widgetValue : dojo.date.stamp.toISOString(new Date())},
1073 //receiver : {widgetValue : openils.User.user.ws_ou()},
1076 "onChange": function(val) {
1078 invoicePane && invoicePane.getFieldValue("inv_ident"),
1079 invoicePane && invoicePane.getFieldValue("provider"),
1085 recv_method : {widgetValue : 'PPR'}
1089 dojo.mixin(override, {
1092 store_options : { base_filter : { active :"t" } },
1093 onChange : function(val) {
1094 pane.setFieldValue('shipper', val);
1096 invoicePane && invoicePane.getFieldValue("inv_ident"),
1098 invoicePane && invoicePane.getFieldValue("receiver")
1103 shipper : { dijitArgs : { store_options : { base_filter : { active :"t" } } } }
1106 for(var field in args) {
1107 override[field] = {widgetValue : args[field]};
1110 // push the name of the invoice into the name display field after update
1111 override.inv_ident = dojo.mixin(
1113 {dijitArgs : {onChange :
1117 invoicePane && invoicePane.getFieldValue("provider"),
1118 invoicePane && invoicePane.getFieldValue("receiver")
1121 if (dojo.byId('acq-invoice-summary-name'))
1122 dojo.byId('acq-invoice-summary-name').innerHTML = newVal;
1128 pane = new openils.widget.EditPane({
1132 mode : (inv) ? 'edit' : 'create',
1133 hideActionButtons : true,
1134 overrideWidgetArgs : override,
1135 readOnly : (inv) && openils.Util.isTrue(inv.complete()),
1150 suppressFields : ['id', 'complete']
1154 parentNode.appendChild(pane.domNode);
1159 function createExtraCopies(oncomplete) {
1162 for(var liId in extraCopies) {
1163 var data = extraCopies[liId];
1164 for(var i = 0; i < data.numExtra; i++) {
1165 var lid = new fieldmapper.acqlid();
1168 lid.fund(data.fund);
1169 lid.recv_time('now');
1174 if(lids.length == 0)
1175 return oncomplete();
1177 fieldmapper.standardRequest(
1178 ['open-ils.acq', 'open-ils.acq.lineitem_detail.cud.batch'],
1180 params : [openils.User.authtoken, lids, true],
1181 oncomplete : function(r) {
1182 if(openils.Util.readResponse(r))
1190 function smartSearchSubmitter() {
1191 performSearch(0, !dojo.byId('acq-unified-build-progressively').checked);
1195 openils.Util.addOnLoad(init);