1 dojo.require('dojo.date.locale');
2 dojo.require('dojo.date.stamp');
3 dojo.require('dijit.form.CheckBox');
4 dojo.require('dijit.form.CurrencyTextBox');
5 dojo.require('dijit.form.NumberTextBox');
6 dojo.require('openils.User');
7 dojo.require('openils.Util');
8 dojo.require('openils.CGI');
9 dojo.require('openils.PermaCrud');
10 dojo.require('openils.widget.EditPane');
11 dojo.require('openils.widget.AutoFieldWidget');
12 dojo.require('openils.widget.ProgressDialog');
13 dojo.require('openils.acq.Lineitem');
15 dojo.requireLocalization('openils.acq', 'acq');
16 var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
18 var fundLabelFormat = ['${0} (${1})', 'code', 'year'];
19 var fundSearchFormat = ['${0} (${1})', 'code', 'year'];
21 var cgi = new openils.CGI();
22 var pcrud = new openils.PermaCrud();
35 var widgetRegistry = {acqie : {}, acqii : {}};
37 function nodeByName(name, context) {
38 return dojo.query('[name='+name+']', context)[0];
43 attachLi = cgi.param('attach_li');
44 attachPo = cgi.param('attach_po');
46 itemTypes = pcrud.retrieveAll('aiit');
48 if(cgi.param('create')) {
52 fieldmapper.standardRequest(
53 ['open-ils.acq', 'open-ils.acq.invoice.retrieve'],
55 params : [openils.User.authtoken, invoiceId],
56 oncomplete : function(r) {
57 invoice = openils.Util.readResponse(r);
65 function renderInvoice() {
67 // in create mode, let the LI or PO render the invoice with seed data
68 if( !(cgi.param('create') && (attachPo || attachLi)) ) {
69 invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), invoice);
72 dojo.byId('acq-invoice-new-item').onclick = function() {
73 var item = new fieldmapper.acqii();
81 if(invoice && openils.Util.isTrue(invoice.complete())) {
83 dojo.forEach( // hide widgets that should not be visible for a completed invoice
84 dojo.query('.hide-complete'),
85 function(node) { openils.Util.hide(node); }
88 new openils.User().getPermOrgList(
91 if(orgs.indexOf(invoice.receiver()) >= 0)
92 openils.Util.show('acq-invoice-reopen-button-wrapper', 'inline');
103 addInvoiceItem(item);
110 addInvoiceEntry(entry);
115 if(attachLi) doAttachLi();
116 if(attachPo) doAttachPo();
119 function doAttachLi() {
121 //var invoiceArgs = {provider : lineitem.provider(), shipper : lineitem.provider()};
122 if(cgi.param('create')) {
123 var invoiceArgs = {};
124 invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), null, invoiceArgs);
126 var entry = new fieldmapper.acqie();
127 entry.id(virtualId--);
129 entry.lineitem(attachLi);
130 addInvoiceEntry(entry);
133 function doAttachPo() {
135 fieldmapper.standardRequest(
136 ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
138 params: [openils.User.authtoken, attachPo, {flesh_lineitem_ids : true}],
139 oncomplete: function(r) {
140 var po = openils.Util.readResponse(r);
142 if(cgi.param('create')) {
143 // render the invoice using some seed data from the PO
144 var invoiceArgs = {provider : po.provider(), shipper : po.provider()};
145 invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), null, invoiceArgs);
148 dojo.forEach(po.lineitems(),
150 var entry = new fieldmapper.acqie();
151 entry.id(virtualId--);
153 entry.lineitem(lineitem);
154 entry.purchase_order(po);
155 addInvoiceEntry(entry);
163 function updateTotalCost() {
166 for(var id in widgetRegistry.acqii)
167 if(!widgetRegistry.acqii[id]._object.isdeleted())
168 totalCost += Number(widgetRegistry.acqii[id].cost_billed.getFormattedValue());
169 for(var id in widgetRegistry.acqie)
170 if(!widgetRegistry.acqie[id]._object.isdeleted())
171 totalCost += Number(widgetRegistry.acqie[id].cost_billed.getFormattedValue());
172 totalInvoicedBox.attr('value', totalCost);
175 for(var id in widgetRegistry.acqii)
176 if(!widgetRegistry.acqii[id]._object.isdeleted())
177 totalPaid += Number(widgetRegistry.acqii[id].amount_paid.getFormattedValue());
178 for(var id in widgetRegistry.acqie)
179 if(!widgetRegistry.acqie[id]._object.isdeleted())
180 totalPaid += Number(widgetRegistry.acqie[id].amount_paid.getFormattedValue());
181 totalPaidBox.attr('value', totalPaid);
183 var buttonsDisabled = false;
185 if(totalPaid > totalCost || totalPaid < 0) {
186 openils.Util.addCSSClass(totalPaidBox.domNode, 'acq-invoice-invalid-amount');
187 invoiceSaveButton.attr('disabled', true);
188 invoiceProrateButton.attr('disabled', true);
189 buttonsDisabled = true;
191 openils.Util.removeCSSClass(totalPaidBox.domNode, 'acq-invoice-invalid-amount');
192 invoiceSaveButton.attr('disabled', false);
193 invoiceProrateButton.attr('disabled', false);
197 openils.Util.addCSSClass(totalInvoicedBox.domNode, 'acq-invoice-invalid-amount');
198 invoiceSaveButton.attr('disabled', true);
199 invoiceProrateButton.attr('disabled', true);
201 openils.Util.removeCSSClass(totalInvoicedBox.domNode, 'acq-invoice-invalid-amount');
202 if(!buttonsDisabled) {
203 invoiceSaveButton.attr('disabled', false);
204 invoiceProrateButton.attr('disabled', false);
208 if(totalPaid == totalCost) { // XXX: too rigid?
209 invoiceCloseButton.attr('disabled', false);
211 invoiceCloseButton.attr('disabled', true);
214 balanceOwedBox.attr('value', (totalCost - totalPaid));
218 function registerWidget(obj, field, widget, callback) {
219 var blob = widgetRegistry[obj.classname];
221 blob[obj.id()] = {_object : obj};
222 blob[obj.id()][field] = widget;
225 dojo.connect(w, 'onChange',
231 if(callback) callback(w, ww);
237 function addInvoiceItem(item) {
238 itemTbody = dojo.byId('acq-invoice-item-tbody');
239 if(itemTemplate == null) {
240 itemTemplate = itemTbody.removeChild(dojo.byId('acq-invoice-item-template'));
243 var row = itemTemplate.cloneNode(true);
244 var itemType = itemTypes.filter(function(t) { return (t.code() == item.inv_item_type()) })[0];
247 ['title', 'author', 'cost_billed', 'amount_paid'],
251 if(field == 'title' || field == 'author') {
252 args = {style : 'width:10em'};
253 } else if(field == 'cost_billed' || field == 'amount_paid') {
254 args = {required : true, style : 'width: 6em'};
259 new openils.widget.AutoFieldWidget({
263 readOnly : invoice && openils.Util.isTrue(invoice.complete()),
265 parentNode : nodeByName(field, row)
272 /* ----------- fund -------------- */
277 labelFormat : fundLabelFormat,
278 searchFormat : fundSearchFormat,
279 readOnly : invoice && openils.Util.isTrue(invoice.complete()),
280 parentNode : nodeByName('fund', row)
283 if(item.fund_debit()) {
284 fundArgs.searchFilter = {'-or' : [{active : 't'}, {id : item.fund()}]};
286 fundArgs.searchFilter = {active : 't'}
287 if(itemType && openils.Util.isTrue(itemType.prorate()))
288 fundArgs.dijitArgs = {disabled : true};
291 var fundWidget = new openils.widget.AutoFieldWidget(fundArgs);
292 registerWidget(item, 'fund', fundWidget);
294 /* ---------- inv_item_type ------------- */
299 new openils.widget.AutoFieldWidget({
301 fmField : 'inv_item_type',
302 parentNode : nodeByName('inv_item_type', row),
303 readOnly : invoice && openils.Util.isTrue(invoice.complete()),
304 dijitArgs : {required : true}
307 // When the inv_item_type is set to prorate=true, don't allow the user the edit the fund
308 // since this charge will be prorated against (potentially) multiple funds
309 dojo.connect(w, 'onChange',
311 if(!item.fund_debit()) {
312 var itemType = itemTypes.filter(function(t) { return (t.code() == w.attr('value')) })[0];
313 if(!itemType) return;
314 if(openils.Util.isTrue(itemType.prorate())) {
315 fundWidget.widget.attr('disabled', true);
316 fundWidget.widget.attr('value', '');
318 fundWidget.widget.attr('disabled', false);
326 nodeByName('delete', row).onclick = function() {
327 var cost = widgetRegistry.acqii[item.id()].cost_billed.getFormattedValue();
328 var msg = dojo.string.substitute(
329 localeStrings.INVOICE_CONFIRM_ITEM_DELETE, [
331 widgetRegistry.acqii[item.id()].inv_item_type.getFormattedValue()
334 if(!confirm(msg)) return;
335 itemTbody.removeChild(row);
336 item.isdeleted(true);
338 delete widgetRegistry.acqii[item.id()];
342 itemTbody.appendChild(row);
346 function addInvoiceEntry(entry) {
348 openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-header'), 'hidden');
349 openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-thead'), 'hidden');
350 openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-tbody'), 'hidden');
352 entryTbody = dojo.byId('acq-invoice-entry-tbody');
353 if(entryTemplate == null) {
354 entryTemplate = entryTbody.removeChild(dojo.byId('acq-invoice-entry-template'));
357 if(dojo.query('[lineitem=' + entry.lineitem() +']', entryTbody)[0])
358 // Is it ever valid to have multiple entries for 1 lineitem in a single invoice?
361 var row = entryTemplate.cloneNode(true);
362 row.setAttribute('lineitem', entry.lineitem());
364 openils.acq.Lineitem.fetchAndRender(
365 entry.lineitem(), {},
368 entry.purchase_order(li.purchase_order());
369 nodeByName('title_details', row).innerHTML = html;
374 ['inv_item_count', 'phys_item_count', 'cost_billed', 'amount_paid'],
376 var dijitArgs = {required : true, constraints : {min: 0}, style : 'width:6em'};
377 if(entry.isnew() && field == 'phys_item_count') dijitArgs.value = numReceived;
381 new openils.widget.AutoFieldWidget({
385 dijitArgs : dijitArgs,
386 readOnly : invoice && openils.Util.isTrue(invoice.complete()),
387 parentNode : nodeByName(field, row)
393 nodeByName('detach', row).onclick = function() {
394 var cost = widgetRegistry.acqie[entry.id()].cost_billed.getFormattedValue();
395 var msg = dojo.string.substitute(
396 localeStrings.INVOICE_CONFIRM_ENTRY_DETACH, [
398 liMarcAttr(lineitem, 'title'),
399 liMarcAttr(lineitem, 'author'),
403 if(!confirm(msg)) return;
404 entryTbody.removeChild(row);
405 entry.isdeleted(true);
407 delete widgetRegistry.acqie[entry.id()];
411 entryTbody.appendChild(row);
415 function liMarcAttr(lineitem, name) {
416 var attr = lineitem.attributes().filter(
419 attr.attr_type() == 'lineitem_marc_attr_definition' &&
420 attr.attr_name() == name)
424 return (attr) ? attr.attr_value() : '';
427 function saveChanges(doProrate, doClose, doReopen) {
429 progressDialog.show(true);
432 invoice.complete('f');
437 var updateItems = [];
438 for(var id in widgetRegistry.acqii) {
439 var reg = widgetRegistry.acqii[id];
440 var item = reg._object;
441 if(item.ischanged() || item.isnew() || item.isdeleted()) {
442 updateItems.push(item);
443 if(item.isnew()) item.id(null);
444 for(var field in reg) {
445 if(field != '_object')
446 item[field]( reg[field].getFormattedValue() );
450 if(item.purchase_order() != null && typeof item.purchase_order() == 'object')
451 item.purchase_order( item.purchase_order().id() );
455 var updateEntries = [];
456 for(var id in widgetRegistry.acqie) {
457 var reg = widgetRegistry.acqie[id];
458 var entry = reg._object;
459 if(entry.ischanged() || entry.isnew() || entry.isdeleted()) {
460 entry.lineitem(entry.lineitem().id());
461 entry.purchase_order(entry.purchase_order().id());
462 updateEntries.push(entry);
463 if(entry.isnew()) entry.id(null);
465 for(var field in reg) {
466 if(field != '_object')
467 entry[field]( reg[field].getFormattedValue() );
471 dojo.forEach(['purchase_order', 'lineitem'],
473 if(entry[field]() != null && typeof entry[field]() == 'object')
474 entry[field]( entry[field]().id() );
481 invoice = new fieldmapper.acqinv();
484 invoice.ischanged(true); // for now, just always update
487 dojo.forEach(invoicePane.fieldList,
489 invoice[field.name]( field.widget.getFormattedValue() );
494 invoice.complete('t');
497 fieldmapper.standardRequest(
498 ['open-ils.acq', 'open-ils.acq.invoice.update'],
500 params : [openils.User.authtoken, invoice, updateEntries, updateItems],
501 oncomplete : function(r) {
502 progressDialog.hide();
503 var invoice = openils.Util.readResponse(r);
506 return prorateInvoice(invoice);
507 location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
514 function prorateInvoice(invoice) {
515 if(!confirm(localeStrings.INVOICE_CONFIRM_PRORATE)) return;
516 progressDialog.show(true);
518 fieldmapper.standardRequest(
519 ['open-ils.acq', 'open-ils.acq.invoice.apply_prorate'],
521 params : [openils.User.authtoken, invoice.id()],
522 oncomplete : function(r) {
523 progressDialog.hide();
524 var invoice = openils.Util.readResponse(r);
526 location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
534 openils.Util.addOnLoad(init);