--- /dev/null
+dojo.require('dojo.date.locale');
+dojo.require('dojo.date.stamp');
+dojo.require('dijit.form.CheckBox');
+dojo.require('dijit.form.CurrencyTextBox');
+dojo.require('dijit.form.NumberTextBox');
+dojo.require('openils.User');
+dojo.require('openils.Util');
+dojo.require('openils.CGI');
+dojo.require('openils.PermaCrud');
+dojo.require('openils.widget.EditPane');
+dojo.require('openils.widget.AutoFieldWidget');
+dojo.require('openils.widget.ProgressDialog');
+dojo.require('openils.acq.Lineitem');
+
+dojo.requireLocalization('openils.acq', 'acq');
+var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
+
+var fundLabelFormat = ['${0} (${1})', 'code', 'year'];
+var fundSearchFormat = ['${0} (${1})', 'code', 'year'];
+
+var cgi = new openils.CGI();
+var pcrud = new openils.PermaCrud();
+var attachLi;
+var attachPo;
+var invoice;
+var itemTbody;
+var itemTemplate;
+var entryTemplate;
+var totalInvoicedBox;
+var totalPaidBox;
+var balanceOwedBox;
+var invoicePane;
+var itemTypes;
+var virtualId = -1;
+var extraCopies = {};
+var extraCopiesFund;
+var widgetRegistry = {acqie : {}, acqii : {}};
+
+function nodeByName(name, context) {
+ return dojo.query('[name='+name+']', context)[0];
+}
+
+function init() {
+
+ attachLi = cgi.param('attach_li');
+ attachPo = cgi.param('attach_po');
+
+ itemTypes = pcrud.retrieveAll('aiit');
+
+ if(cgi.param('create')) {
+ renderInvoice();
+
+ } else {
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.invoice.retrieve.authoritative'],
+ {
+ params : [openils.User.authtoken, invoiceId],
+ oncomplete : function(r) {
+ invoice = openils.Util.readResponse(r);
+ renderInvoice();
+ }
+ }
+ );
+ }
+
+ extraCopiesFund = new openils.widget.AutoFieldWidget({
+ fmField : 'fund',
+ fmClass : 'acqlid',
+ searchFilter : {active : 't'},
+ labelFormat : fundLabelFormat,
+ searchFormat : fundSearchFormat,
+ dijitArgs : {required : true},
+ parentNode : dojo.byId('acq-invoice-extra-copies-fund')
+ });
+ extraCopiesFund.build();
+}
+
+function renderInvoice() {
+
+ // in create mode, let the LI or PO render the invoice with seed data
+ if( !(cgi.param('create') && (attachPo || attachLi)) ) {
+ invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), invoice);
+ }
+
+ dojo.byId('acq-invoice-new-item').onclick = function() {
+ var item = new fieldmapper.acqii();
+ item.id(virtualId--);
+ item.isnew(true);
+ addInvoiceItem(item);
+ }
+
+ updateTotalCost();
+
+ if(invoice && openils.Util.isTrue(invoice.complete())) {
+
+ dojo.forEach( // hide widgets that should not be visible for a completed invoice
+ dojo.query('.hide-complete'),
+ function(node) { openils.Util.hide(node); }
+ );
+
+ new openils.User().getPermOrgList(
+ 'ACQ_INVOICE_REOPEN',
+ function (orgs) {
+ if(orgs.indexOf(invoice.receiver()) >= 0)
+ openils.Util.show('acq-invoice-reopen-button-wrapper', 'inline');
+ },
+ true,
+ true
+ );
+ }
+
+ if(invoice) {
+ dojo.forEach(
+ invoice.items(),
+ function(item) {
+ addInvoiceItem(item);
+ }
+ );
+
+ dojo.forEach(
+ invoice.entries(),
+ function(entry) {
+ addInvoiceEntry(entry);
+ }
+ );
+ }
+
+ if(attachLi) doAttachLi();
+ if(attachPo) doAttachPo();
+}
+
+function doAttachLi() {
+
+ //var invoiceArgs = {provider : lineitem.provider(), shipper : lineitem.provider()};
+ if(cgi.param('create')) {
+ var invoiceArgs = {};
+ invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), null, invoiceArgs);
+ }
+ var entry = new fieldmapper.acqie();
+ entry.id(virtualId--);
+ entry.isnew(true);
+ entry.lineitem(attachLi);
+ addInvoiceEntry(entry);
+}
+
+function doAttachPo() {
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
+ { async: true,
+ params: [
+ openils.User.authtoken, attachPo,
+ {flesh_lineitem_ids : true, flesh_po_items : true}
+ ],
+ oncomplete: function(r) {
+ var po = openils.Util.readResponse(r);
+
+ if(cgi.param('create')) {
+ // render the invoice using some seed data from the PO
+ var invoiceArgs = {provider : po.provider(), shipper : po.provider()};
+ invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), null, invoiceArgs);
+ }
+
+ dojo.forEach(po.lineitems(),
+ function(lineitem) {
+ var entry = new fieldmapper.acqie();
+ entry.id(virtualId--);
+ entry.isnew(true);
+ entry.lineitem(lineitem);
+ entry.purchase_order(po);
+ addInvoiceEntry(entry);
+ }
+ );
+
+ dojo.forEach(po.po_items(),
+ function(poItem) {
+ var item = new fieldmapper.acqii();
+ item.id(virtualId--);
+ item.isnew(true);
+ item.fund(poItem.fund());
+ item.title(poItem.title());
+ item.author(poItem.author());
+ item.note(poItem.note());
+ item.inv_item_type(poItem.inv_item_type());
+ item.purchase_order(po);
+ item.po_item(poItem);
+ addInvoiceItem(item);
+ }
+ );
+ }
+ }
+ );
+}
+
+function updateTotalCost() {
+
+ var totalCost = 0;
+ for(var id in widgetRegistry.acqii)
+ if(!widgetRegistry.acqii[id]._object.isdeleted())
+ totalCost += Number(widgetRegistry.acqii[id].cost_billed.getFormattedValue());
+ for(var id in widgetRegistry.acqie)
+ if(!widgetRegistry.acqie[id]._object.isdeleted())
+ totalCost += Number(widgetRegistry.acqie[id].cost_billed.getFormattedValue());
+ totalInvoicedBox.attr('value', totalCost);
+
+ totalPaid = 0;
+ for(var id in widgetRegistry.acqii)
+ if(!widgetRegistry.acqii[id]._object.isdeleted())
+ totalPaid += Number(widgetRegistry.acqii[id].amount_paid.getFormattedValue());
+ for(var id in widgetRegistry.acqie)
+ if(!widgetRegistry.acqie[id]._object.isdeleted())
+ totalPaid += Number(widgetRegistry.acqie[id].amount_paid.getFormattedValue());
+ totalPaidBox.attr('value', totalPaid);
+
+ var buttonsDisabled = false;
+
+ if(totalPaid > totalCost || totalPaid < 0) {
+ openils.Util.addCSSClass(totalPaidBox.domNode, 'acq-invoice-invalid-amount');
+ invoiceSaveButton.attr('disabled', true);
+ invoiceProrateButton.attr('disabled', true);
+ buttonsDisabled = true;
+ } else {
+ openils.Util.removeCSSClass(totalPaidBox.domNode, 'acq-invoice-invalid-amount');
+ invoiceSaveButton.attr('disabled', false);
+ invoiceProrateButton.attr('disabled', false);
+ }
+
+ if(totalCost < 0) {
+ openils.Util.addCSSClass(totalInvoicedBox.domNode, 'acq-invoice-invalid-amount');
+ invoiceSaveButton.attr('disabled', true);
+ invoiceProrateButton.attr('disabled', true);
+ } else {
+ openils.Util.removeCSSClass(totalInvoicedBox.domNode, 'acq-invoice-invalid-amount');
+ if(!buttonsDisabled) {
+ invoiceSaveButton.attr('disabled', false);
+ invoiceProrateButton.attr('disabled', false);
+ }
+ }
+
+ if(totalPaid == totalCost) { // XXX: too rigid?
+ invoiceCloseButton.attr('disabled', false);
+ } else {
+ invoiceCloseButton.attr('disabled', true);
+ }
+
+ balanceOwedBox.attr('value', (totalCost - totalPaid));
+}
+
+
+function registerWidget(obj, field, widget, callback) {
+ var blob = widgetRegistry[obj.classname];
+ if(!blob[obj.id()])
+ blob[obj.id()] = {_object : obj};
+ blob[obj.id()][field] = widget;
+ widget.build(
+ function(w, ww) {
+ dojo.connect(w, 'onChange',
+ function(newVal) {
+ obj.ischanged(true);
+ updateTotalCost();
+ }
+ );
+ if(callback) callback(w, ww);
+ }
+ );
+ return widget;
+}
+
+function addInvoiceItem(item) {
+ itemTbody = dojo.byId('acq-invoice-item-tbody');
+ if(itemTemplate == null) {
+ itemTemplate = itemTbody.removeChild(dojo.byId('acq-invoice-item-template'));
+ }
+
+ var row = itemTemplate.cloneNode(true);
+ var itemType = itemTypes.filter(function(t) { return (t.code() == item.inv_item_type()) })[0];
+
+ dojo.forEach(
+ ['title', 'author', 'cost_billed', 'amount_paid'],
+ function(field) {
+
+ var args;
+ if(field == 'title' || field == 'author') {
+ //args = {style : 'width:10em'};
+ } else if(field == 'cost_billed' || field == 'amount_paid') {
+ args = {required : true, style : 'width: 6em'};
+ }
+ registerWidget(
+ item,
+ field,
+ new openils.widget.AutoFieldWidget({
+ fmClass : 'acqii',
+ fmObject : item,
+ fmField : field,
+ readOnly : invoice && openils.Util.isTrue(invoice.complete()),
+ dijitArgs : args,
+ parentNode : nodeByName(field, row)
+ })
+ )
+ }
+ );
+
+
+ /* ----------- fund -------------- */
+ var fundArgs = {
+ fmClass : 'acqii',
+ fmObject : item,
+ fmField : 'fund',
+ labelFormat : fundLabelFormat,
+ searchFormat : fundSearchFormat,
+ readOnly : invoice && openils.Util.isTrue(invoice.complete()),
+ dijitArgs : {required : true},
+ parentNode : nodeByName('fund', row)
+ }
+
+ if(item.fund_debit()) {
+ fundArgs.searchFilter = {'-or' : [{active : 't'}, {id : item.fund()}]};
+ } else {
+ fundArgs.searchFilter = {active : 't'}
+ if(itemType && openils.Util.isTrue(itemType.prorate()))
+ fundArgs.dijitArgs = {disabled : true};
+ }
+
+ var fundWidget = new openils.widget.AutoFieldWidget(fundArgs);
+ registerWidget(item, 'fund', fundWidget);
+
+ /* ---------- inv_item_type ------------- */
+
+ if(item.po_item()) {
+
+ // read-only item view for items that were the result of a po-item
+ var po = item.purchase_order();
+ var po_item = item.po_item();
+ var node = nodeByName('inv_item_type', row);
+ var itemType = itemTypes.filter(function(t) { return (t.code() == item.inv_item_type()) })[0];
+ orderDate = (!po.order_date()) ? '' :
+ dojo.date.locale.format(dojo.date.stamp.fromISOString(po.order_date()), {selector:'date'});
+
+ node.innerHTML = dojo.string.substitute(
+ localeStrings.INVOICE_ITEM_PO_DETAILS,
+ [
+ itemType.name(),
+ oilsBasePath,
+ po.id(),
+ po.name(),
+ orderDate,
+ po_item.estimated_cost()
+ ]
+ );
+
+ } else {
+
+ registerWidget(
+ item,
+ 'inv_item_type',
+ new openils.widget.AutoFieldWidget({
+ fmObject : item,
+ fmField : 'inv_item_type',
+ parentNode : nodeByName('inv_item_type', row),
+ readOnly : invoice && openils.Util.isTrue(invoice.complete()),
+ dijitArgs : {required : true}
+ }),
+ function(w, ww) {
+ // When the inv_item_type is set to prorate=true, don't allow the user the edit the fund
+ // since this charge will be prorated against (potentially) multiple funds
+ dojo.connect(w, 'onChange',
+ function() {
+ if(!item.fund_debit()) {
+ var itemType = itemTypes.filter(function(t) { return (t.code() == w.attr('value')) })[0];
+ if(!itemType) return;
+ if(openils.Util.isTrue(itemType.prorate())) {
+ fundWidget.widget.attr('disabled', true);
+ fundWidget.widget.attr('value', '');
+ } else {
+ fundWidget.widget.attr('disabled', false);
+ }
+ }
+ }
+ );
+ }
+ );
+ }
+
+ nodeByName('delete', row).onclick = function() {
+ var cost = widgetRegistry.acqii[item.id()].cost_billed.getFormattedValue();
+ var msg = dojo.string.substitute(
+ localeStrings.INVOICE_CONFIRM_ITEM_DELETE, [
+ cost,
+ widgetRegistry.acqii[item.id()].inv_item_type.getFormattedValue()
+ ]
+ );
+ if(!confirm(msg)) return;
+ itemTbody.removeChild(row);
+ item.isdeleted(true);
+ if(item.isnew())
+ delete widgetRegistry.acqii[item.id()];
+ updateTotalCost();
+ }
+
+ itemTbody.appendChild(row);
+ updateTotalCost();
+}
+
+function addInvoiceEntry(entry) {
+
+ openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-header'), 'hidden');
+ openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-thead'), 'hidden');
+ openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-tbody'), 'hidden');
+
+ entryTbody = dojo.byId('acq-invoice-entry-tbody');
+ if(entryTemplate == null) {
+ entryTemplate = entryTbody.removeChild(dojo.byId('acq-invoice-entry-template'));
+ }
+
+ if(dojo.query('[lineitem=' + entry.lineitem() +']', entryTbody)[0])
+ // Is it ever valid to have multiple entries for 1 lineitem in a single invoice?
+ return;
+
+ var row = entryTemplate.cloneNode(true);
+ row.setAttribute('lineitem', entry.lineitem());
+
+ openils.acq.Lineitem.fetchAndRender(
+ entry.lineitem(), {},
+ function(li, html) {
+ entry.lineitem(li);
+ entry.purchase_order(li.purchase_order());
+ nodeByName('title_details', row).innerHTML = html;
+
+ dojo.forEach(
+ ['inv_item_count', 'phys_item_count', 'cost_billed', 'amount_paid'],
+ function(field) {
+ var dijitArgs = {required : true, constraints : {min: 0}, style : 'width:6em'};
+ if(entry.isnew() && field == 'phys_item_count') {
+ // by default, attempt to pay for all non-canceled and as-of-yet-un-invoiced items
+ var count = Number(li.order_summary().item_count() || 0) -
+ Number(li.order_summary().cancel_count() || 0) -
+ Number(li.order_summary().invoice_count() || 0);
+ if(count < 0) count = 0;
+ dijitArgs.value = count;
+ }
+ registerWidget(
+ entry,
+ field,
+ new openils.widget.AutoFieldWidget({
+ fmObject : entry,
+ fmClass : 'acqie',
+ fmField : field,
+ dijitArgs : dijitArgs,
+ readOnly : invoice && openils.Util.isTrue(invoice.complete()),
+ parentNode : nodeByName(field, row)
+ }),
+ function(w) {
+ if(field == 'phys_item_count') {
+ dojo.connect(w, 'onChange',
+ function() {
+ // staff entered a higher number in the receive field than was originally ordered
+ // taking into account already invoiced items
+ var extra = Number(this.attr('value')) -
+ (Number(entry.lineitem().item_count()) - Number(entry.lineitem().order_summary().invoice_count()));
+ if(extra > 0) {
+ storeExtraCopies(entry, extra);
+ }
+ }
+ )
+ }
+ }
+ );
+ }
+ );
+ }
+ );
+
+ nodeByName('detach', row).onclick = function() {
+ var cost = widgetRegistry.acqie[entry.id()].cost_billed.getFormattedValue();
+ var idents = [];
+ dojo.forEach(['isbn', 'upc', 'issn'],
+ function(ident) {
+ var val = liMarcAttr(entry.lineitem(), ident);
+ if(val) idents.push(val);
+ }
+ );
+
+ var msg = dojo.string.substitute(
+ localeStrings.INVOICE_CONFIRM_ENTRY_DETACH, [
+ cost || 0,
+ liMarcAttr(entry.lineitem(), 'title'),
+ liMarcAttr(entry.lineitem(), 'author'),
+ idents.join(',')
+ ]
+ );
+ if(!confirm(msg)) return;
+ entryTbody.removeChild(row);
+ entry.isdeleted(true);
+ if(entry.isnew())
+ delete widgetRegistry.acqie[entry.id()];
+ updateTotalCost();
+ }
+
+ entryTbody.appendChild(row);
+ updateTotalCost();
+}
+
+function liMarcAttr(lineitem, name) {
+ var attr = lineitem.attributes().filter(
+ function(attr) {
+ if(
+ attr.attr_type() == 'lineitem_marc_attr_definition' &&
+ attr.attr_name() == name)
+ return attr
+ }
+ )[0];
+ return (attr) ? attr.attr_value() : '';
+}
+
+function saveChanges(doProrate, doClose, doReopen) {
+ createExtraCopies(
+ function() {
+ saveChangesPartTwo(doProrate, doClose, doReopen);
+ }
+ );
+}
+
+function saveChangesPartTwo(doProrate, doClose, doReopen) {
+
+ progressDialog.show(true);
+
+ if(doReopen) {
+ invoice.complete('f');
+
+ } else {
+
+
+ var updateItems = [];
+ for(var id in widgetRegistry.acqii) {
+ var reg = widgetRegistry.acqii[id];
+ var item = reg._object;
+ if(item.ischanged() || item.isnew() || item.isdeleted()) {
+ updateItems.push(item);
+ if(item.isnew()) item.id(null);
+ for(var field in reg) {
+ if(field != '_object')
+ item[field]( reg[field].getFormattedValue() );
+ }
+
+ // unflesh
+ if(item.purchase_order() != null && typeof item.purchase_order() == 'object')
+ item.purchase_order( item.purchase_order().id() );
+ }
+ }
+
+ var updateEntries = [];
+ for(var id in widgetRegistry.acqie) {
+ var reg = widgetRegistry.acqie[id];
+ var entry = reg._object;
+ if(entry.ischanged() || entry.isnew() || entry.isdeleted()) {
+ entry.lineitem(entry.lineitem().id());
+ entry.purchase_order(entry.purchase_order().id());
+ updateEntries.push(entry);
+ if(entry.isnew()) entry.id(null);
+
+ for(var field in reg) {
+ if(field != '_object')
+ entry[field]( reg[field].getFormattedValue() );
+ }
+
+ // unflesh
+ dojo.forEach(['purchase_order', 'lineitem'],
+ function(field) {
+ if(entry[field]() != null && typeof entry[field]() == 'object')
+ entry[field]( entry[field]().id() );
+ }
+ );
+ }
+ }
+
+ if(!invoice) {
+ invoice = new fieldmapper.acqinv();
+ invoice.isnew(true);
+ } else {
+ invoice.ischanged(true); // for now, just always update
+ }
+
+ dojo.forEach(invoicePane.fieldList,
+ function(field) {
+ invoice[field.name]( field.widget.getFormattedValue() );
+ }
+ );
+
+ if(doClose)
+ invoice.complete('t');
+ }
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.invoice.update'],
+ {
+ params : [openils.User.authtoken, invoice, updateEntries, updateItems],
+ oncomplete : function(r) {
+ progressDialog.hide();
+ var invoice = openils.Util.readResponse(r);
+ if(invoice) {
+ if(doProrate)
+ return prorateInvoice(invoice);
+ location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
+ }
+ }
+ }
+ );
+}
+
+function prorateInvoice(invoice) {
+ if(!confirm(localeStrings.INVOICE_CONFIRM_PRORATE)) return;
+ progressDialog.show(true);
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.invoice.apply_prorate'],
+ {
+ params : [openils.User.authtoken, invoice.id()],
+ oncomplete : function(r) {
+ progressDialog.hide();
+ var invoice = openils.Util.readResponse(r);
+ if(invoice) {
+ location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
+ }
+ }
+ }
+ );
+}
+
+function storeExtraCopies(entry, numExtra) {
+
+ dojo.byId('acq-invoice-extra-copies-message').innerHTML =
+ dojo.string.substitute(
+ localeStrings.INVOICE_EXTRA_COPIES, [numExtra]);
+
+ var addCopyHandler;
+ addCopyHandler = dojo.connect(
+ extraCopiesGo,
+ 'onClick',
+ function() {
+ extraCopies[entry.lineitem().id()] = {
+ numExtra : numExtra,
+ fund : extraCopiesFund.widget.attr('value')
+ }
+ extraItemsDialog.hide();
+ dojo.disconnect(addCopyHandler);
+ }
+ );
+
+ dojo.connect(
+ extraCopiesCancel,
+ 'onClick',
+ function() {
+ widgetRegistry.acqie[entry.id()].phys_item_count.widget.attr('value', '');
+ extraItemsDialog.hide()
+ }
+ );
+
+ extraItemsDialog.show();
+}
+
+function createExtraCopies(oncomplete) {
+
+ var lids = [];
+ for(var liId in extraCopies) {
+ var data = extraCopies[liId];
+ for(var i = 0; i < data.numExtra; i++) {
+ var lid = new fieldmapper.acqlid();
+ lid.isnew(true);
+ lid.lineitem(liId);
+ lid.fund(data.fund);
+ lid.recv_time('now');
+ lids.push(lid);
+ }
+ }
+
+ if(lids.length == 0)
+ return oncomplete();
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem_detail.cud.batch'],
+ {
+ params : [openils.User.authtoken, lids, true],
+ oncomplete : function(r) {
+ if(openils.Util.readResponse(r))
+ oncomplete();
+ }
+ }
+ );
+
+}
+
+
+openils.Util.addOnLoad(init);
+
+