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');
14 dojo.requireLocalization('openils.acq', 'acq');
15 var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
17 var fundLabelFormat = ['${0} (${1})', 'code', 'year'];
18 var fundSearchFormat = ['${0} (${1})', 'code', 'year'];
20 var cgi = new openils.CGI();
21 var pcrud = new openils.PermaCrud();
33 var widgetRegistry = {acqie : {}, acqii : {}};
35 function nodeByName(name, context) {
36 return dojo.query('[name='+name+']', context)[0];
41 attachLi = cgi.param('attach_li');
42 attachPo = cgi.param('attach_po');
44 itemTypes = pcrud.retrieveAll('aiit');
46 if(cgi.param('create')) {
50 fieldmapper.standardRequest(
51 ['open-ils.acq', 'open-ils.acq.invoice.retrieve'],
53 params : [openils.User.authtoken, invoiceId],
54 oncomplete : function(r) {
55 invoice = openils.Util.readResponse(r);
63 function renderInvoice() {
65 // in create mode, let the LI or PO render the invoice with seed data
66 if( !(cgi.param('create') && (attachPo || attachLi)) ) {
67 invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), invoice);
70 dojo.byId('acq-invoice-new-item').onclick = function() {
71 var item = new fieldmapper.acqii();
90 addInvoiceEntry(entry);
95 if(attachLi) doAttachLi();
96 if(attachPo) doAttachPo();
99 function doAttachLi() {
101 fieldmapper.standardRequest(
102 ["open-ils.acq", "open-ils.acq.lineitem.retrieve"], {
104 params: [openils.User.authtoken, attachLi, {
108 flesh_li_details : true,
109 flesh_fund_debit : true
111 oncomplete: function(r) {
112 lineitem = openils.Util.readResponse(r);
114 if(cgi.param('create')) {
115 // render the invoice using some seed data from the Lineitem
116 var invoiceArgs = {provider : lineitem.provider(), shipper : lineitem.provider()};
117 invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), null, invoiceArgs);
120 var entry = new fieldmapper.acqie();
121 entry.id(virtualId--);
123 entry.lineitem(lineitem);
124 entry.purchase_order(lineitem.purchase_order());
125 addInvoiceEntry(entry);
131 function doAttachPo() {
132 fieldmapper.standardRequest(
133 ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
135 params: [openils.User.authtoken, attachPo, {
136 flesh_lineitems : true,
138 flesh_lineitem_details : true,
139 flesh_fund_debit : true
141 oncomplete: function(r) {
142 var po = openils.Util.readResponse(r);
144 if(cgi.param('create')) {
145 // render the invoice using some seed data from the PO
146 var invoiceArgs = {provider : po.provider(), shipper : po.provider()};
147 invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), null, invoiceArgs);
150 dojo.forEach(po.lineitems(),
152 var entry = new fieldmapper.acqie();
153 entry.id(virtualId--);
155 entry.lineitem(lineitem);
156 entry.purchase_order(po);
157 lineitem.purchase_order(po);
158 addInvoiceEntry(entry);
166 function updateTotalCost() {
168 if(!totalInvoicedBox) {
169 totalInvoicedBox = new dijit.form.CurrencyTextBox(
170 {style : 'width: 5em'}, dojo.byId('acq-invoice-total-invoiced'));
172 for(var id in widgetRegistry.acqii)
173 if(!widgetRegistry.acqii[id]._object.isdeleted())
174 total += widgetRegistry.acqii[id].cost_billed.getFormattedValue();
175 for(var id in widgetRegistry.acqie)
176 if(!widgetRegistry.acqie[id]._object.isdeleted())
177 total += widgetRegistry.acqie[id].cost_billed.getFormattedValue();
178 totalInvoicedBox.attr('value', total);
182 totalPaidBox = new dijit.form.CurrencyTextBox(
183 {style : 'width: 5em'}, dojo.byId('acq-invoice-total-paid'));
185 for(var id in widgetRegistry.acqii)
186 if(!widgetRegistry.acqii[id]._object.isdeleted())
187 total += widgetRegistry.acqii[id].amount_paid.getFormattedValue();
188 for(var id in widgetRegistry.acqie)
189 if(!widgetRegistry.acqie[id]._object.isdeleted())
190 total += widgetRegistry.acqie[id].amount_paid.getFormattedValue();
191 totalPaidBox.attr('value', total);
195 function registerWidget(obj, field, widget, callback) {
196 var blob = widgetRegistry[obj.classname];
198 blob[obj.id()] = {_object : obj};
199 blob[obj.id()][field] = widget;
202 dojo.connect(w, 'onChange',
208 if(callback) callback(w, ww);
214 function addInvoiceItem(item) {
215 itemTbody = dojo.byId('acq-invoice-item-tbody');
216 if(itemTemplate == null) {
217 itemTemplate = itemTbody.removeChild(dojo.byId('acq-invoice-item-template'));
220 var row = itemTemplate.cloneNode(true);
221 var itemType = itemTypes.filter(function(t) { return (t.code() == item.inv_item_type()) })[0];
224 ['title', 'author', 'cost_billed', 'amount_paid'],
229 new openils.widget.AutoFieldWidget({
233 dijitArgs : (field == 'cost_billed' || field == 'amount_paid') ? {required : true, style : 'width: 5em'} : null,
234 parentNode : nodeByName(field, row)
241 /* ----------- fund -------------- */
246 labelFormat : fundLabelFormat,
247 searchFormat : fundSearchFormat,
248 parentNode : nodeByName('fund', row)
251 if(item.fund_debit()) {
252 fundArgs.searchFilter = {'-or' : [{active : 't'}, {id : item.fund()}]};
254 fundArgs.searchFilter = {active : 't'}
255 if(itemType && openils.Util.isTrue(itemType.prorate()))
256 fundArgs.dijitArgs = {disabled : true};
259 var fundWidget = new openils.widget.AutoFieldWidget(fundArgs);
260 registerWidget(item, 'fund', fundWidget);
262 /* ---------- inv_item_type ------------- */
267 new openils.widget.AutoFieldWidget({
269 fmField : 'inv_item_type',
270 parentNode : nodeByName('inv_item_type', row),
271 dijitArgs : {required : true}
274 // When the inv_item_type is set to prorate=true, don't allow the user the edit the fund
275 // since this charge will be prorated against (potentially) multiple funds
276 dojo.connect(w, 'onChange',
278 if(!item.fund_debit()) {
279 var itemType = itemTypes.filter(function(t) { return (t.code() == w.attr('value')) })[0];
280 if(!itemType) return;
281 if(openils.Util.isTrue(itemType.prorate())) {
282 fundWidget.widget.attr('disabled', true);
283 fundWidget.widget.attr('value', '');
285 fundWidget.widget.attr('disabled', false);
293 nodeByName('delete', row).onclick = function() {
294 var cost = widgetRegistry.acqii[item.id()].cost_billed.getFormattedValue();
295 var msg = dojo.string.substitute(
296 localeStrings.INVOICE_CONFIRM_ITEM_DELETE, [
298 widgetRegistry.acqii[item.id()].inv_item_type.getFormattedValue()
301 if(!confirm(msg)) return;
302 itemTbody.removeChild(row);
303 item.isdeleted(true);
305 delete widgetRegistry.acqii[item.id()];
309 itemTbody.appendChild(row);
313 function addInvoiceEntry(entry) {
314 entryTbody = dojo.byId('acq-invoice-entry-tbody');
315 if(entryTemplate == null) {
316 entryTemplate = entryTbody.removeChild(dojo.byId('acq-invoice-entry-template'));
319 if(dojo.query('[lineitem=' + entry.lineitem().id() +']', entryTbody)[0])
320 // Is it ever valid to have multiple entries for 1 lineitem in a single invoice?
323 var row = entryTemplate.cloneNode(true);
324 row.setAttribute('lineitem', entry.lineitem().id());
325 var lineitem = entry.lineitem();
328 if(liMarcAttr(lineitem, 'isbn')) idents.push(liMarcAttr(lineitem, 'isbn'));
329 if(liMarcAttr(lineitem, 'upc')) idents.push(liMarcAttr(lineitem, 'upc'));
330 if(liMarcAttr(lineitem, 'issn')) idents.push(liMarcAttr(lineitem, 'issn'));
332 var lids = lineitem.lineitem_details();
333 var numOrdered = lids.length;
334 var numReceived = lids.filter(function(lid) { return (lid.recv_time() != null) }).length;
335 var numInvoiced = lids.filter(function(lid) { return !openils.Util.isTrue(lid.fund_debit().encumbrance()) }).length;
339 var po = entry.purchase_order();
345 nodeByName('title_details', row).innerHTML =
346 dojo.string.substitute(
347 localeStrings.INVOICE_TITLE_DETAILS, [
348 liMarcAttr(lineitem, 'title'),
349 liMarcAttr(lineitem, 'author'),
353 Number(lineitem.estimated_unit_price()).toFixed(2),
354 (Number(lineitem.estimated_unit_price()) * numOrdered).toFixed(2),
365 ['inv_item_count', 'phys_item_count', 'cost_billed', 'amount_paid'],
367 var dijitArgs = {required : true, constraints : {min: 0}, style : 'width:5em'};
368 if(entry.isnew() && field == 'phys_item_count') dijitArgs.value = numReceived;
372 new openils.widget.AutoFieldWidget({
376 dijitArgs : dijitArgs,
377 parentNode : nodeByName(field, row)
383 nodeByName('detach', row).onclick = function() {
384 var cost = widgetRegistry.acqie[entry.id()].cost_billed.getFormattedValue();
385 var msg = dojo.string.substitute(
386 localeStrings.INVOICE_CONFIRM_ENTRY_DETACH, [
388 liMarcAttr(lineitem, 'title'),
389 liMarcAttr(lineitem, 'author'),
393 if(!confirm(msg)) return;
394 entryTbody.removeChild(row);
395 entry.isdeleted(true);
397 delete widgetRegistry.acqie[entry.id()];
401 entryTbody.appendChild(row);
405 function liMarcAttr(lineitem, name) {
406 var attr = lineitem.attributes().filter(
409 attr.attr_type() == 'lineitem_marc_attr_definition' &&
410 attr.attr_name() == name)
414 return (attr) ? attr.attr_value() : '';
417 function saveChanges(doProrate) {
419 progressDialog.show(true);
421 var updateItems = [];
422 for(var id in widgetRegistry.acqii) {
423 var reg = widgetRegistry.acqii[id];
424 var item = reg._object;
425 if(item.ischanged() || item.isnew() || item.isdeleted()) {
426 updateItems.push(item);
427 if(item.isnew()) item.id(null);
428 for(var field in reg) {
429 if(field != '_object')
430 item[field]( reg[field].getFormattedValue() );
434 if(item.purchase_order() != null && typeof item.purchase_order() == 'object')
435 item.purchase_order( item.purchase_order().id() );
439 var updateEntries = [];
440 for(var id in widgetRegistry.acqie) {
441 var reg = widgetRegistry.acqie[id];
442 var entry = reg._object;
443 if(entry.ischanged() || entry.isnew() || entry.isdeleted()) {
444 entry.lineitem(entry.lineitem().id());
445 entry.purchase_order(entry.purchase_order().id());
446 updateEntries.push(entry);
447 if(entry.isnew()) entry.id(null);
449 for(var field in reg) {
450 if(field != '_object')
451 entry[field]( reg[field].getFormattedValue() );
455 dojo.forEach(['purchase_order', 'lineitem'],
457 if(entry[field]() != null && typeof entry[field]() == 'object')
458 entry[field]( entry[field]().id() );
465 invoice = new fieldmapper.acqinv();
468 invoice.ischanged(true); // for now, just always update
471 dojo.forEach(invoicePane.fieldList,
473 invoice[field.name]( field.widget.getFormattedValue() );
477 fieldmapper.standardRequest(
478 ['open-ils.acq', 'open-ils.acq.invoice.update'],
480 params : [openils.User.authtoken, invoice, updateEntries, updateItems],
481 oncomplete : function(r) {
482 progressDialog.hide();
483 var invoice = openils.Util.readResponse(r);
486 return prorateInvoice();
487 location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
494 function prorateInvoice() {
495 if(!confirm(localeStrings.INVOICE_CONFIRM_PRORATE)) return;
496 progressDialog.show(true);
498 fieldmapper.standardRequest(
499 ['open-ils.acq', 'open-ils.acq.invoice.apply_prorate'],
501 params : [openils.User.authtoken, invoice.id()],
502 oncomplete : function(r) {
503 progressDialog.hide();
504 var invoice = openils.Util.readResponse(r);
506 location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
516 openils.Util.addOnLoad(init);