]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/acq/invoice/view.js
implemented create/save logic
[working/Evergreen.git] / Open-ILS / web / js / ui / default / acq / invoice / view.js
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
14 dojo.requireLocalization('openils.acq', 'acq');
15 var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
16
17 var fundLabelFormat = ['${0} (${1})', 'code', 'year'];
18 var fundSearchFormat = ['${0} (${1})', 'code', 'year'];
19
20 var cgi = new openils.CGI();
21 var pcrud = new openils.PermaCrud();
22 var attachLi;
23 var attachPo;
24 var invoice;
25 var itemTbody;
26 var itemTemplate;
27 var entryTemplate;
28 var totalAmountBox;
29 var invoicePane;
30 var itemTypes;
31 var virtualId = -1;
32 var widgetRegistry = {acqie : {}, acqii : {}};
33
34 function nodeByName(name, context) {
35     return dojo.query('[name='+name+']', context)[0];
36 }
37
38 function init() {
39
40     attachLi = cgi.param('attach_li');
41     attachPo = cgi.param('attach_po');
42
43     itemTypes = pcrud.retrieveAll('aiit');
44
45     if(cgi.param('create')) {
46         renderInvoice();
47
48     } else {
49         fieldmapper.standardRequest(
50             ['open-ils.acq', 'open-ils.acq.invoice.retrieve'],
51             {
52                 params : [openils.User.authtoken, invoiceId],
53                 oncomplete : function(r) {
54                     invoice = openils.Util.readResponse(r);     
55                     renderInvoice();
56                 }
57             }
58         );
59     }
60 }
61
62 function renderInvoice() {
63
64     // in create mode, let the LI or PO render the invoice with seed data
65     if( !(cgi.param('create') && (attachPo || attachLi)) ) {
66         invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), invoice);
67     }
68
69     dojo.byId('acq-invoice-new-item').onclick = function() {
70         var item = new fieldmapper.acqii();
71         item.id(virtualId--);
72         item.isnew(true);
73         addInvoiceItem(item);
74     }
75
76     updateTotalCost();
77
78     if(invoice) {
79         dojo.forEach(
80             invoice.items(),
81             function(item) {
82                 addInvoiceItem(item);
83             }
84         );
85
86         dojo.forEach(
87             invoice.entries(),
88             function(entry) {
89                 addInvoiceEntry(entry);
90             }
91         );
92     }
93
94     if(attachLi) doAttachLi();
95     if(attachPo) doAttachPo();
96 }
97
98 function doAttachLi() {
99
100     fieldmapper.standardRequest(
101         ["open-ils.acq", "open-ils.acq.lineitem.retrieve"], {
102             async: true,
103             params: [openils.User.authtoken, attachLi, {
104                 clear_marc : true,
105                 flesh_attrs : true,
106                 flesh_po : true,
107             }],
108             oncomplete: function(r) { 
109                 lineitem = openils.Util.readResponse(r);
110
111                 if(cgi.param('create')) {
112                     // render the invoice using some seed data from the Lineitem
113                     var invoiceArgs = {provider : lineitem.provider(), shipper : lineitem.provider()}; 
114                     invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), null, invoiceArgs);
115                 }
116
117                 var entry = new fieldmapper.acqie();
118                 entry.id(virtualId--);
119                 entry.isnew(true);
120                 entry.lineitem(lineitem);
121                 entry.purchase_order(lineitem.purchase_order());
122                 addInvoiceEntry(entry);
123             }
124         }
125     );
126 }
127
128 function doAttachPo() {
129     fieldmapper.standardRequest(
130         ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
131         {   async: true,
132             params: [openils.User.authtoken, attachPo, {
133                 flesh_lineitems : true,
134                 clear_marc : true
135             }],
136             oncomplete: function(r) {
137                 var po = openils.Util.readResponse(r);
138
139                 if(cgi.param('create')) {
140                     // render the invoice using some seed data from the PO
141                     var invoiceArgs = {provider : po.provider(), shipper : po.provider()}; 
142                     invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), null, invoiceArgs);
143                 }
144
145                 dojo.forEach(po.lineitems(), 
146                     function(lineitem) {
147                         var entry = new fieldmapper.acqie();
148                         entry.id(virtualId--);
149                         entry.isnew(true);
150                         entry.lineitem(lineitem);
151                         entry.purchase_order(po);
152                         lineitem.purchase_order(po);
153                         addInvoiceEntry(entry);
154                     }
155                 );
156             }
157         }
158     );
159 }
160
161 function updateTotalCost() {
162     var total = 0;    
163     if(!totalAmountBox) {
164         totalAmountBox = new dijit.form.CurrencyTextBox(
165             {style : 'width: 5em'}, dojo.byId('acq-invoice-total-invoiced'));
166     }
167     for(var id in widgetRegistry.acqii) 
168         if(!widgetRegistry.acqii[id]._object.isdeleted())
169             total += widgetRegistry.acqii[id].cost_billed.getFormattedValue();
170     for(var id in widgetRegistry.acqie) 
171         if(!widgetRegistry.acqie[id]._object.isdeleted())
172             total += widgetRegistry.acqie[id].cost_billed.getFormattedValue();
173     totalAmountBox.attr('value', total);
174 }
175
176
177 function registerWidget(obj, field, widget, callback) {
178     var blob = widgetRegistry[obj.classname];
179     if(!blob[obj.id()]) 
180         blob[obj.id()] = {_object : obj};
181     blob[obj.id()][field] = widget;
182     widget.build(
183         function(w, ww) {
184             dojo.connect(w, 'onChange', 
185                 function(newVal) { 
186                     obj.ischanged(true); 
187                     updateTotalCost();
188                 }
189             );
190             if(callback) callback(w, ww);
191         }
192     );
193     return widget;
194 }
195
196 function addInvoiceItem(item) {
197     itemTbody = dojo.byId('acq-invoice-item-tbody');
198     if(itemTemplate == null) {
199         itemTemplate = itemTbody.removeChild(dojo.byId('acq-invoice-item-template'));
200     }
201
202     var row = itemTemplate.cloneNode(true);
203     var itemType = itemTypes.filter(function(t) { return (t.code() == item.inv_item_type()) })[0];
204
205     dojo.forEach(
206         ['title', 'author', 'cost_billed'], 
207         function(field) {
208             registerWidget(
209                 item,
210                 field,
211                 new openils.widget.AutoFieldWidget({
212                     fmClass : 'acqii',
213                     fmObject : item,
214                     fmField : field,
215                     dijitArgs : (field == 'cost_billed') ? {required : true, style : 'width: 5em'} : null,
216                     parentNode : nodeByName(field, row)
217                 })
218             )
219         }
220     );
221
222
223     /* ----------- fund -------------- */
224     var itemType = itemTypes.filter(function(t) { return (t.code() == item.inv_item_type()) })[0];
225
226     var fundArgs = {
227         fmClass : 'acqii',
228         fmObject : item,
229         fmField : 'fund',
230         labelFormat : fundLabelFormat,
231         searchFormat : fundSearchFormat,
232         parentNode : nodeByName('fund', row)
233     }
234
235     if(item.fund_debit()) {
236         fundArgs.readOnly = true;
237     } else {
238         fundArgs.searchFilter = {active : 't'}
239         if(itemType && openils.Util.isTrue(itemType.prorate()))
240             fundArgs.disabled = true;
241     }
242
243     var fundWidget = new openils.widget.AutoFieldWidget(fundArgs);
244     registerWidget(item, 'fund', fundWidget);
245
246     /* ---------- inv_item_type ------------- */
247
248     registerWidget(
249         item,
250         'inv_item_type',
251         new openils.widget.AutoFieldWidget({
252             fmObject : item,
253             fmField : 'inv_item_type',
254             parentNode : nodeByName('inv_item_type', row),
255             dijitArgs : {required : true}
256         }),
257         function(w, ww) {
258             // When the inv_item_type is set to prorate=true, don't allow the user the edit the fund
259             // since this charge will be prorated against (potentially) multiple funds
260             dojo.connect(w, 'onChange', 
261                 function() {
262                     if(!item.fund_debit()) {
263                         var itemType = itemTypes.filter(function(t) { return (t.code() == w.attr('value')) })[0];
264                         if(!itemType) return;
265                         if(openils.Util.isTrue(itemType.prorate())) {
266                             fundWidget.widget.attr('disabled', true);
267                             fundWidget.widget.attr('value', '');
268                         } else {
269                             fundWidget.widget.attr('disabled', false);
270                         }
271                     }
272                 }
273             );
274         }
275     );
276
277     nodeByName('delete', row).onclick = function() {
278         var cost = widgetRegistry.acqii[item.id()].cost_billed.getFormattedValue();
279         var msg = dojo.string.substitute(
280             localeStrings.INVOICE_CONFIRM_ITEM_DELETE, [
281                 cost,
282                 widgetRegistry.acqii[item.id()].inv_item_type.getFormattedValue()
283             ]
284         );
285         if(!confirm(msg)) return;
286         itemTbody.removeChild(row);
287         item.isdeleted(true);
288         if(item.isnew())
289             delete widgetRegistry.acqii[item.id()];
290         updateTotalCost();
291     }
292
293     itemTbody.appendChild(row);
294     updateTotalCost();
295 }
296
297 function addInvoiceEntry(entry) {
298     entryTbody = dojo.byId('acq-invoice-entry-tbody');
299     if(entryTemplate == null) {
300         entryTemplate = entryTbody.removeChild(dojo.byId('acq-invoice-entry-template'));
301     }
302
303     if(dojo.query('[lineitem=' + entry.lineitem().id() +']', entryTbody)[0])
304         // Is it ever valid to have multiple entries for 1 lineitem in a single invoice?
305         return;
306
307     var row = entryTemplate.cloneNode(true);
308     row.setAttribute('lineitem', entry.lineitem().id());
309     var lineitem = entry.lineitem();
310
311     var idents = [];
312     if(liMarcAttr(lineitem, 'isbn')) idents.push(liMarcAttr(lineitem, 'isbn'));
313     if(liMarcAttr(lineitem, 'upc')) idents.push(liMarcAttr(lineitem, 'upc'));
314     if(liMarcAttr(lineitem, 'issn')) idents.push(liMarcAttr(lineitem, 'issn'));
315
316     nodeByName('title', row).innerHTML = liMarcAttr(lineitem, 'title');
317     nodeByName('author', row).innerHTML = liMarcAttr(lineitem, 'author');
318     nodeByName('idents', row).innerHTML = idents.join(',');
319
320     var po = entry.purchase_order();
321     if(po) {
322         openils.Util.show(nodeByName('purchase_order_span', row), 'inline');
323         nodeByName('purchase_order', row).innerHTML = po.name();
324         nodeByName('purchase_order', row).onclick = function() {
325             location.href = oilsBasePath + '/acq/po/view/ ' + po.id();
326         }
327     }
328
329     dojo.forEach(
330         ['inv_item_count', 'phys_item_count', 'cost_billed'],
331         function(field) {
332             registerWidget(
333                 entry, 
334                 field,
335                 new openils.widget.AutoFieldWidget({
336                     fmObject : entry,
337                     fmClass : 'acqie',
338                     fmField : field,
339                     dijitArgs : {required : true, constraints : {min: 0}, style : 'width:5em'}, 
340                     parentNode : nodeByName(field, row)
341                 })
342             );
343         }
344     );
345
346     nodeByName('detach', row).onclick = function() {
347         var cost = widgetRegistry.acqie[entry.id()].cost_billed.getFormattedValue();
348         var msg = dojo.string.substitute(
349             localeStrings.INVOICE_CONFIRM_ENTRY_DETACH, [
350                 cost || 0,
351                 liMarcAttr(lineitem, 'title'),
352                 liMarcAttr(lineitem, 'author'),
353                 idents.join(',')
354             ]
355         );
356         if(!confirm(msg)) return;
357         entryTbody.removeChild(row);
358         entry.isdeleted(true);
359         if(entry.isnew())
360             delete widgetRegistry.acqie[entry.id()];
361         updateTotalCost();
362     }
363
364
365     entryTbody.appendChild(row);
366     updateTotalCost();
367 }
368
369 function liMarcAttr(lineitem, name) {
370     var attr = lineitem.attributes().filter(
371         function(attr) { 
372             if(
373                 attr.attr_type() == 'lineitem_marc_attr_definition' && 
374                 attr.attr_name() == name) 
375                     return attr 
376         } 
377     )[0];
378     return (attr) ? attr.attr_value() : '';
379 }
380
381 function saveChanges() {
382     
383     progressDialog.show(true);
384
385     var updateItems = [];
386     for(var id in widgetRegistry.acqii) {
387         var reg = widgetRegistry.acqii[id];
388         var item = reg._object;
389         if(item.ischanged() || item.isnew() || item.isdeleted()) {
390             updateItems.push(item);
391             if(item.isnew()) item.id(null);
392             for(var field in reg) {
393                 if(field != '_object')
394                     item[field]( reg[field].getFormattedValue() );
395             }
396             
397             // unflesh
398             if(item.purchase_order() != null && typeof item.purchase_order() == 'object')
399                 item.purchase_order( item.purchase_order().id() );
400         }
401     }
402
403     var updateEntries = [];
404     for(var id in widgetRegistry.acqie) {
405         var reg = widgetRegistry.acqie[id];
406         var entry = reg._object;
407         if(entry.ischanged() || entry.isnew() || entry.isdeleted()) {
408             entry.lineitem(entry.lineitem().id());
409             entry.purchase_order(entry.purchase_order().id());
410             updateEntries.push(entry);
411             if(entry.isnew()) entry.id(null);
412
413             for(var field in reg) {
414                 if(field != '_object')
415                     entry[field]( reg[field].getFormattedValue() );
416             }
417             
418             // unflesh
419             dojo.forEach(['purchase_order', 'lineitem'],
420                 function(field) {
421                     if(entry[field]() != null && typeof entry[field]() == 'object')
422                         entry[field]( entry[field]().id() );
423                 }
424             );
425         }
426     }
427
428     if(!invoice) {
429         invoice = new fieldmapper.acqinv();
430         invoice.isnew(true);
431     } else {
432         invoice.ischanged(true); // for now, just always update
433     }
434
435     dojo.forEach(invoicePane.fieldList, 
436         function(field) {
437             invoice[field.name]( field.widget.getFormattedValue() );
438         }
439     );
440
441     fieldmapper.standardRequest(
442         ['open-ils.acq', 'open-ils.acq.invoice.update'],
443         {
444             params : [openils.User.authtoken, invoice, updateEntries, updateItems],
445             oncomplete : function(r) {
446                 progressDialog.hide();
447                 var invoice = openils.Util.readResponse(r);
448                 if(invoice) {
449                     location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
450                 }
451             }
452         }
453     );
454 }
455
456
457
458 openils.Util.addOnLoad(init);
459
460