[open-ils-commits] r15600 - in trunk/Open-ILS: src/perlmods/OpenILS/Application/Acq web/js/dojo/openils/acq/nls web/js/ui/default/acq/common web/js/ui/default/acq/po web/templates/default/acq/common (senator)
svn at svn.open-ils.org
svn at svn.open-ils.org
Fri Feb 19 15:00:39 EST 2010
Author: senator
Date: 2010-02-19 15:00:35 -0500 (Fri, 19 Feb 2010)
New Revision: 15600
Modified:
trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Financials.pm
trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Order.pm
trunk/Open-ILS/web/js/dojo/openils/acq/nls/acq.js
trunk/Open-ILS/web/js/ui/default/acq/common/li_table.js
trunk/Open-ILS/web/js/ui/default/acq/po/view_po.js
trunk/Open-ILS/web/templates/default/acq/common/li_table.tt2
Log:
Acq: complete improvements of receive/unreceive for lineitems and copies
When viewing a purchase order, you can now recieve and un-receive individual
lineitems or even individual copies, and the PO viewing interface will
properly reflect all changes (to the states of the PO, the lineitems, and the
copies; to the amounts spent and encumbered; and to the state of widgets that
should appear or disappear based on PO/LI/copy state).
Modified: trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Financials.pm
===================================================================
--- trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Financials.pm 2010-02-19 17:08:37 UTC (rev 15599)
+++ trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Financials.pm 2010-02-19 20:00:35 UTC (rev 15600)
@@ -1003,6 +1003,41 @@
return undef;
}
+sub build_price_summary {
+ my ($e, $po_id) = @_;
+
+ # fetch the fund debits for this purchase order
+ my $debits = $e->json_query({
+ "select" => {"acqfdeb" => [qw/encumbrance amount/]},
+ "from" => {
+ "acqlid" => {
+ "jub" => {
+ "fkey" => "lineitem",
+ "field" => "id",
+ "join" => {
+ "acqpo" => {
+ "fkey" => "purchase_order", "field" => "id"
+ }
+ }
+ },
+ "acqfdeb" => {"fkey" => "fund_debit", "field" => "id"}
+ }
+ },
+ "where" => {"+acqpo" => {"id" => $po_id}}
+ });
+
+ my ($enc, $spent) = (0, 0);
+ for my $deb (@$debits) {
+ if($U->is_true($deb->{encumbrance})) {
+ $enc += $deb->{amount};
+ } else {
+ $spent += $deb->{amount};
+ }
+ }
+ ($enc, $spent);
+}
+
+
sub retrieve_purchase_order_impl {
my($e, $po_id, $options) = @_;
@@ -1038,31 +1073,7 @@
}
if($$options{flesh_price_summary}) {
-
- # fetch the fund debits for this purchase order
- my $debits = $e->json_query({
- select => {acqfdeb => ["encumbrance", "amount"]},
- from => {
- acqlid => {
- jub => {fkey => "lineitem", field => "id",
- join => {acqpo => {fkey => "purchase_order", field => "id"}}
- },
- acqfdeb => {fkey => "fund_debit", field =>"id"}
- }
- },
- where => {'+acqpo' => {id => $po_id}}
- });
-
- my $enc = 0;
- my $spent = 0;
- for my $deb (@$debits) {
- if($U->is_true($deb->{encumbrance})) {
- $enc += $deb->{amount};
- } else {
- $spent += $deb->{amount};
- }
- }
-
+ my ($enc, $spent) = build_price_summary($e, $po_id);
$po->amount_encumbered($enc);
$po->amount_spent($spent);
}
Modified: trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Order.pm
===================================================================
--- trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Order.pm 2010-02-19 17:08:37 UTC (rev 15599)
+++ trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Order.pm 2010-02-19 20:00:35 UTC (rev 15600)
@@ -1,4 +1,5 @@
package OpenILS::Application::Acq::BatchManager;
+use OpenILS::Application::Acq::Financials;
use OpenSRF::AppSession;
use OpenSRF::EX qw/:try/;
use strict; use warnings;
@@ -328,6 +329,23 @@
# been received, mark the lineitem as received
# returns 1 on non-received, li on received, 0 on error
# ----------------------------------------------------------------------------
+
+sub describe_affected_po {
+ my ($e, $po) = @_;
+
+ my ($enc, $spent) =
+ OpenILS::Application::Acq::Financials::build_price_summary(
+ $e, $po->id
+ );
+
+ +{$po->id => {
+ "state" => $po->state,
+ "amount_encumbered" => $enc,
+ "amount_spent" => $spent
+ }
+ };
+}
+
sub check_lineitem_received {
my($mgr, $li_id) = @_;
@@ -359,15 +377,13 @@
$mgr->post_process( sub { create_lineitem_status_events($mgr, $li_id, 'aur.received'); });
my $po;
- my $result = {"li" => {$li->id => {"state" => $li->state}}};
return 0 unless
$skip_complete_check or (
$po = check_purchase_order_received($mgr, $li->purchase_order)
);
- if (ref $po) {
- $result->{"po"} = {$po->id => {"state" => $li->state}};
- }
+ my $result = {"li" => {$li->id => {"state" => $li->state}}};
+ $result->{"po"} = describe_affected_po($mgr->editor, $po) if ref $po;
return $result;
}
@@ -497,9 +513,7 @@
my $li = check_lineitem_received($mgr, $lid->lineitem) or return 0;
return 1 if $li == 1; # li not received
- my $po = check_purchase_order_received($mgr, $li->purchase_order) or return 0;
- return $li if $po == 1;
- return $po;
+ return check_purchase_order_received($mgr, $li->purchase_order) or return 0;
}
@@ -844,9 +858,9 @@
state => {'!=' => 'received'}
}, {idlist=>1});
- return 1 if @$non_recv_li;
+ my $po = $mgr->editor->retrieve_acq_purchase_order($po_id);
+ return $po if @$non_recv_li;
- my $po = $mgr->editor->retrieve_acq_purchase_order($po_id);
$po->state('received');
return update_purchase_order($mgr, $po);
}
@@ -1555,6 +1569,22 @@
}
+# At the moment there's a lack of parallelism between the receive and unreceive
+# API methods for POs and the API methods for LIs and LIDs. The methods for
+# POs stream back objects as they act, whereas the methods for LIs and LIDs
+# atomically return an object that describes only what changed (in LIs and LIDs
+# themselves or in the objects to which to LIs and LIDs belong).
+#
+# The methods for LIs and LIDs work the way they do to faciliate the UI's
+# maintaining correct information about the state of these things when a user
+# wants to receive or unreceive these objects without refreshing their whole
+# display. The UI feature for receiving and un-receiving a whole PO just
+# refreshes the whole display, so this absence of parallelism in the UI is also
+# relected in this module.
+#
+# This could be neatened in the future by making POs receive and unreceive in
+# the same way the LIs and LIDs do.
+
__PACKAGE__->register_method(
method => 'receive_lineitem_detail_api',
api_name => 'open-ils.acq.lineitem_detail.receive',
@@ -1564,7 +1594,10 @@
{desc => 'Authentication token', type => 'string'},
{desc => 'lineitem detail ID', type => 'number'}
],
- return => {desc => '1 on success, Event on error'}
+ return => {desc =>
+ "on success, object describing changes to LID and possibly " .
+ "to LI and PO; on error, Event"
+ }
}
);
@@ -1575,34 +1608,29 @@
return $e->die_event unless $e->checkauth;
my $mgr = OpenILS::Application::Acq::BatchManager->new(editor => $e, conn => $conn);
- my $lid = $e->retrieve_acq_lineitem_detail([
- $lid_id, {
- flesh => 2,
- flesh_fields => {
- acqlid => ['lineitem'],
- jub => ['purchase_order']
- }
+ my $fleshing = {
+ "flesh" => 2, "flesh_fields" => {
+ "acqlid" => ["lineitem"], "jub" => ["purchase_order"]
}
- ]);
+ };
+ my $lid = $e->retrieve_acq_lineitem_detail([$lid_id, $fleshing]);
+
return $e->die_event unless $e->allowed(
'RECEIVE_PURCHASE_ORDER', $lid->lineitem->purchase_order->ordering_agency);
+ # update ...
my $recvd = receive_lineitem_detail($mgr, $lid_id) or return $e->die_event;
- # What's this business, you ask? We basically want to return a minimal
- # set of information about what has changed as a result of the "receive
- # lineitem detail" operation; remember: not only does the lineitem detail
- # change state, but so might an LI and even a PO, and a good UI will want
- # to reflect those changes.
- $lid = $e->retrieve_acq_lineitem_detail(
- [$lid_id, {"flesh" => 1, "flesh_fields" => {"acqlid" => ["lineitem"]}}]
- );
+ # .. and re-retrieve
+ $lid = $e->retrieve_acq_lineitem_detail([$lid_id, $fleshing]);
+
+ # Now build result data structure.
my $result = {"lid" => {$lid->id => {"recv_time" => $lid->recv_time}}};
if (ref $recvd) {
if ($recvd->class_name =~ /::purchase_order/) {
- $result->{"po"} = {"id" => $recvd->id, "state" => $recvd->state};
+ $result->{"po"} = describe_affected_po($e, $recvd);
$result->{"li"} = {
$lid->lineitem->id => {"state" => $lid->lineitem->state}
};
@@ -1610,6 +1638,8 @@
$result->{"li"} = {$recvd->id => {"state" => $recvd->state}};
}
}
+ $result->{"po"} ||=
+ describe_affected_po($e, $lid->lineitem->purchase_order);
$e->commit;
return $result;
@@ -1622,11 +1652,11 @@
desc => 'Mark a lineitem as received',
params => [
{desc => 'Authentication token', type => 'string'},
- {desc => 'lineitem detail ID', type => 'number'}
+ {desc => 'lineitem ID', type => 'number'}
],
return => {desc =>
- "on success, object containing an LI and possibly a PO; " .
- "on error, event"
+ "on success, object describing changes to LI and possibly PO; " .
+ "on error, Event"
}
}
);
@@ -1695,7 +1725,10 @@
{desc => 'Authentication token', type => 'string'},
{desc => 'lineitem detail ID', type => 'number'}
],
- return => {desc => '1 on success, Event on error'}
+ return => {desc =>
+ "on success, object describing changes to LID and possibly " .
+ "to LI and PO; on error, Event"
+ }
}
);
@@ -1740,8 +1773,8 @@
if ($po->state eq "received") {
$po->state("on-order");
$po = update_purchase_order($mgr, $po) or return $e->die_event;
- $result->{"po"} = {$po->id => {"state" => $po->state}};
}
+ $result->{"po"} = describe_affected_po($e, $po);
$e->commit and return $result or return $e->die_event;
}
@@ -1750,12 +1783,15 @@
method => 'rollback_receive_lineitem_api',
api_name => 'open-ils.acq.lineitem.receive.rollback',
signature => {
- desc => 'Mark a lineitem as received',
+ desc => 'Mark a lineitem as Un-received',
params => [
{desc => 'Authentication token', type => 'string'},
- {desc => 'lineitem detail ID', type => 'number'}
+ {desc => 'lineitem ID', type => 'number'}
],
- return => {desc => 'altered objects on success, event on error'}
+ return => {desc =>
+ "on success, object describing changes to LI and possibly PO; " .
+ "on error, Event"
+ }
}
);
@@ -1781,8 +1817,8 @@
if ($po->state eq "received") {
$po->state("on-order");
$po = update_purchase_order($mgr, $po) or return $e->die_event;
- $result->{"po"} = {$po->id => {"state" => $po->state}};
}
+ $result->{"po"} = describe_affected_po($e, $po);
$e->commit and return $result or return $e->die_event;
}
Modified: trunk/Open-ILS/web/js/dojo/openils/acq/nls/acq.js
===================================================================
--- trunk/Open-ILS/web/js/dojo/openils/acq/nls/acq.js 2010-02-19 17:08:37 UTC (rev 15599)
+++ trunk/Open-ILS/web/js/dojo/openils/acq/nls/acq.js 2010-02-19 20:00:35 UTC (rev 15600)
@@ -18,5 +18,7 @@
'EXPORT_SAVE_DIALOG_TITLE': "Save field values to a file",
'EXPORT_SHORT_LIST': "Not all of the selected items had the attribute '${0}'.\nChoose OK to save those values that could be found.",
'EXPORT_EMPTY_LIST': "No values for attribute '${0}' found.",
- 'UNRECEIVE_LI': "Are you sure you want to mark this lineitem as UN-received?"
+ 'UNRECEIVE_LI': "Are you sure you want to mark this lineitem as UN-received?",
+
+ 'UNRECEIVE_LID': "Are you sure you want to mark this copy as UN-received?",
}
Modified: trunk/Open-ILS/web/js/ui/default/acq/common/li_table.js
===================================================================
--- trunk/Open-ILS/web/js/ui/default/acq/common/li_table.js 2010-02-19 17:08:37 UTC (rev 15599)
+++ trunk/Open-ILS/web/js/ui/default/acq/common/li_table.js 2010-02-19 20:00:35 UTC (rev 15600)
@@ -251,7 +251,7 @@
priceInput.onchange = function() { self.updateLiPrice(priceInput, li) };
// show either "mark received" or "unreceive" as appropriate
- this.updateReceivedness(li, row);
+ this.updateLiReceivedness(li, row);
if (!skip_final_placement) {
self.tbody.appendChild(row);
@@ -261,7 +261,7 @@
}
};
- this.updateReceivedness = function(li, row) {
+ this.updateLiReceivedness = function(li, row) {
if (typeof(row) == "undefined")
row = dojo.query('tr[li="' + li.id() + '"]', "acq-lit-tbody")[0];
@@ -281,18 +281,15 @@
openils.Util.hide(real_copies_link);
openils.Util.hide(unrecv_link);
openils.Util.show(recv_link, "inline");
- if (typeof(recv_link.onclick) != "function")
- recv_link.onclick = function() { self.receiveLi(li); };
+ recv_link.onclick = function() { self.issueReceive(li); };
return;
case "received":
openils.Util.hide(recv_link);
openils.Util.show(unrecv_link, "inline");
- if (typeof(unrecv_link.onclick) != "function") {
- unrecv_link.onclick = function() {
- if (confirm(localeStrings.UNRECEIVE_LI))
- self.unReceiveLi(li);
- };
- }
+ unrecv_link.onclick = function() {
+ if (confirm(localeStrings.UNRECEIVE_LI))
+ self.issueReceive(li, /* rollback */ true);
+ };
// TODO we should allow editing before receipt, in which case the
// test should be "if 1 or more real (acp) copies exist
openils.Util.show(real_copies_link);
@@ -902,23 +899,50 @@
}
);
- var recv_link = dojo.query('[name=receive]', row)[0];
- if(copy.recv_time()) {
- openils.Util.hide(recv_link);
- } else {
- recv_link.onclick = function() {
- self.receiveLid(copy);
+ this.updateLidReceivedness(copy, row);
+ };
+
+ this.updateLidReceivedness = function(copy, row) {
+ if (typeof(row) == "undefined") {
+ row = dojo.query(
+ 'tr[copy_id="' + copy.id() + '"]', this.copyTbody
+ )[0];
+ }
+
+ var self = this;
+ var recv_link = nodeByName("receive", row);
+ var unrecv_link = nodeByName("unreceive", row);
+ var del_link = nodeByName("delete", row);
+
+ if (this.isPO) {
+ openils.Util.hide(del_link.parentNode);
+
+ /* Avoid showing (un)receive links for virtual copies */
+ if (copy.id() > 0) {
+ if(copy.recv_time()) {
+ openils.Util.hide(recv_link);
+ openils.Util.show(unrecv_link);
+ unrecv_link.onclick = function() {
+ if (confirm(localeStrings.UNRECEIVE_LID))
+ self.issueReceive(copy, /* rollback */ true);
+ };
+ } else {
+ openils.Util.hide(unrecv_link);
+ openils.Util.show(recv_link);
+ recv_link.onclick = function() { self.issueReceive(copy); };
+ }
+ } else {
+ openils.Util.hide(unrecv_link);
openils.Util.hide(recv_link);
}
- }
+ } else {
+ openils.Util.hide(unrecv_link);
+ openils.Util.hide(recv_link);
- if(this.isPO) {
- openils.Util.hide(dojo.query('[name=delete]', row)[0].parentNode);
- } else {
- dojo.query('[name=delete]', row)[0].onclick =
- function() { self.deleteCopy(row) };
+ del_link.onclick = function() { self.deleteCopy(row) };
+ openils.Util.show(del_link.parentNode);
}
- };
+ }
this.deleteCopy = function(row) {
var copy = this.copyCache[row.getAttribute('copy_id')];
@@ -1175,79 +1199,55 @@
);
}
- this.receiveLi = function(li) {
- /* (For now) there shall be no marking LIs received except from the
- * actual "view PO" interface. */
+ this.issueReceive = function(obj, rollback) {
+ /* (For now) there shall be no marking LI or LIDs (un)received
+ * except from the actual "view PO" interface. */
if (!this.isPO) return;
- var self = this;
+ var part =
+ {"jub": "lineitem", "acqlid": "lineitem_detail"}[obj.classname];
+ var method =
+ "open-ils.acq." + part + ".receive" + (rollback ? ".rollback" : "");
+
progressDialog.show(true);
fieldmapper.standardRequest(
- ["open-ils.acq", "open-ils.acq.lineitem.receive"], {
+ ["open-ils.acq", method], {
"async": true,
- "params": [this.authtoken, li.id()],
+ "params": [this.authtoken, obj.id()],
"onresponse": function(r) {
- self.handleReceiveOrRollback(openils.Util.readResponse(r));
+ self.handleReceive(openils.Util.readResponse(r));
},
- "oncomplete": function() {
- progressDialog.hide();
- }
+ "oncomplete": function() { progressDialog.hide(); }
}
);
- }
+ };
- this.handleReceiveOrRollback = function(resp) {
+ /**
+ * Handles the responses from receive and rollback ML calls.
+ */
+ this.handleReceive = function(resp) {
if (resp) {
if (resp.li) {
for (var li_id in resp.li) {
for (var key in resp.li[li_id])
self.liCache[li_id][key](resp.li[li_id][key]);
- self.updateReceivedness(self.liCache[li_id]);
+ self.updateLiReceivedness(self.liCache[li_id]);
}
}
if (resp.po) {
if (typeof(self.poUpdateCallback) == "function")
self.poUpdateCallback(resp.po);
}
- }
- }
-
- this.unReceiveLi = function(li) {
- /* (For now) there shall be no marking LIs un-received except from the
- * actual "view PO" interface. */
- if (!this.isPO) return;
-
- var self = this;
- progressDialog.show(true);
- fieldmapper.standardRequest(
- ["open-ils.acq", "open-ils.acq.lineitem.receive.rollback"], {
- "async": true,
- "params": [this.authtoken, li.id()],
- "onresponse": function(r) {
- self.handleReceiveOrRollback(openils.Util.readResponse(r));
- },
- "oncomplete": function() {
- progressDialog.hide();
+ if (resp.lid) {
+ for (var lid_id in resp.lid) {
+ for (var key in resp.lid[lid_id])
+ self.copyCache[lid_id][key](resp.lid[lid_id][key]);
+ self.updateLidReceivedness(self.copyCache[lid_id]);
}
}
- );
- }
+ }
+ };
- this.receiveLid = function(li) {
- if(!this.isPO) return;
- progressDialog.show(true);
- fieldmapper.standardRequest(
- ['open-ils.acq', 'open-ils.acq.lineitem_detail.receive'],
- { async: true,
- params: [this.authtoken, li.id()],
- onresponse : function(r) {
- var resp = openils.Util.readResponse(r);
- progressDialog.hide();
- },
- }
- );
- }
-
this.rollbackPoReceive = function() {
if(!this.isPO) return;
if(!confirm(localeStrings.ROLLBACK_PO_RECEIVE_CONFIRM)) return;
Modified: trunk/Open-ILS/web/js/ui/default/acq/po/view_po.js
===================================================================
--- trunk/Open-ILS/web/js/ui/default/acq/po/view_po.js 2010-02-19 17:08:37 UTC (rev 15599)
+++ trunk/Open-ILS/web/js/ui/default/acq/po/view_po.js 2010-02-19 20:00:35 UTC (rev 15600)
@@ -8,12 +8,28 @@
function updatePoState(po_info) {
var data = po_info[PO.id()];
- if (data && data.state) {
- PO.state(data.state);
- dojo.byId("acq-po-view-state").innerHTML = PO.state(); // TODO i18n
+ if (data) {
+ for (var key in data)
+ PO[key](data[key]);
+ renderPo();
}
}
+function renderPo() {
+ dojo.byId("acq-po-view-id").innerHTML = PO.id();
+ dojo.byId("acq-po-view-name").innerHTML = PO.name();
+ dojo.byId("acq-po-view-total-li").innerHTML = PO.lineitem_count();
+ dojo.byId("acq-po-view-total-enc").innerHTML = PO.amount_encumbered();
+ dojo.byId("acq-po-view-total-spent").innerHTML = PO.amount_spent();
+ dojo.byId("acq-po-view-state").innerHTML = PO.state(); // TODO i18n
+
+ if(PO.state() == "pending") {
+ openils.Util.show("acq-po-activate");
+ if (PO.lineitem_count() > 1)
+ openils.Util.show("acq-po-split");
+ }
+}
+
function init() {
liTable = new AcqLiTable();
liTable.reset();
@@ -25,20 +41,8 @@
{ async: true,
params: [openils.User.authtoken, poId, {flesh_price_summary:true, flesh_lineitem_count:true}],
oncomplete: function(r) {
- PO = openils.Util.readResponse(r);
- dojo.byId('acq-po-view-id').innerHTML = PO.id();
- dojo.byId('acq-po-view-name').innerHTML = PO.name();
- dojo.byId('acq-po-view-total-li').innerHTML = PO.lineitem_count();
- dojo.byId('acq-po-view-total-enc').innerHTML = PO.amount_encumbered();
- dojo.byId('acq-po-view-total-spent').innerHTML = PO.amount_spent();
- dojo.byId('acq-po-view-state').innerHTML = PO.state(); // TODO i18n
-
- if(PO.state() == 'pending') {
- openils.Util.show('acq-po-activate');
- if (PO.lineitem_count() > 1) {
- openils.Util.show('acq-po-split');
- }
- }
+ PO = openils.Util.readResponse(r); /* save PO globally */
+ renderPo();
}
}
);
Modified: trunk/Open-ILS/web/templates/default/acq/common/li_table.tt2
===================================================================
--- trunk/Open-ILS/web/templates/default/acq/common/li_table.tt2 2010-02-19 17:08:37 UTC (rev 15599)
+++ trunk/Open-ILS/web/templates/default/acq/common/li_table.tt2 2010-02-19 20:00:35 UTC (rev 15600)
@@ -92,7 +92,7 @@
</table>
</td>
<td><a class='hidden' name='real_copies_link' href='javascript:void(0);'>Update Barcodes</a></td>
- <td><a name='receive_link' href='javascript:void(0);'>Mark Received</a><a name='unreceive_link' href='javascript:void(0);'>Unreceive</a></td>
+ <td><a name='receive_link' href='javascript:void(0);'>Mark Received</a><a name='unreceive_link' href='javascript:void(0);'>Un-Receive</a></td>
</td>
<td><a name='copieslink' href='javascript:void(0);'>Copies(<span name='count'>0</span>)</a></td>
<td>
@@ -249,7 +249,7 @@
<td><div name='cn_label'/></td>
<td><div name='barcode'/></td>
<td><div name='note'/></td>
- <td><a href='javascript:void(0);' name='receive'>Mark Received</a></td>
+ <td><a href='javascript:void(0);' name='receive'>Mark Received</a><a href='javascript:void(0);' name='unreceive'>Un-Receive</a></td>
<td><div name='delete' dojoType='dijit.form.Button' style='color:red;'>X</div></td>
</tr>
</tbody>
More information about the open-ils-commits
mailing list