From 42f970e342d468122c6810911760eb20bfd7c1a0 Mon Sep 17 00:00:00 2001 From: senator Date: Wed, 12 May 2010 22:14:49 +0000 Subject: [PATCH] Acq: import file of bib IDs, get paginated LI table from which make new orders This is most of the way to being usable, but isn't all there, as it probably still needs: 1) trivial parsing of the input file so that we can look for a specific column in a CSV file to treat as a bib ID instead of a whole line, 2) a way to select and act on lineitems not currently displayed on the page git-svn-id: svn://svn.open-ils.org/ILS/trunk@16424 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- .../src/perlmods/OpenILS/Application/Acq/Search.pm | 118 ++++++++++++++++----- Open-ILS/web/css/skin/default/acq.css | 2 + Open-ILS/web/js/dojo/openils/acq/nls/acq.js | 3 +- .../web/js/ui/default/acq/common/li_table_pager.js | 64 +++++++++++ .../web/js/ui/default/acq/picklist/from_bib.js | 67 ++++++++++++ Open-ILS/web/opac/locale/en-US/lang.dtd | 2 + .../default/acq/common/li_table_pager.tt2 | 17 +++ .../templates/default/acq/picklist/from_bib.tt2 | 25 +++++ .../xul/staff_client/chrome/content/main/menu.js | 4 + .../chrome/content/main/menu_frame_menus.xul | 2 + .../chrome/locale/en-US/offline.properties | 1 + 11 files changed, 280 insertions(+), 25 deletions(-) create mode 100644 Open-ILS/web/js/ui/default/acq/common/li_table_pager.js create mode 100644 Open-ILS/web/js/ui/default/acq/picklist/from_bib.js create mode 100644 Open-ILS/web/templates/default/acq/common/li_table_pager.tt2 create mode 100644 Open-ILS/web/templates/default/acq/picklist/from_bib.tt2 diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Acq/Search.pm b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Search.pm index 6d6e4891c4..e81977be20 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Acq/Search.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Search.pm @@ -5,6 +5,7 @@ use strict; use warnings; use OpenSRF::AppSession; +use OpenSRF::Utils::Logger qw/:logger/; use OpenILS::Event; use OpenILS::Utils::CStoreEditor q/:funcs/; use OpenILS::Utils::Fieldmapper; @@ -455,9 +456,24 @@ __PACKAGE__->register_method( } ); +__PACKAGE__->register_method( + method => "bib_search", + api_name => "open-ils.acq.biblio.create_by_id", + stream => 1, + signature => { + desc => q/Returns new lineitems for each matching bib record/, + params => [ + {desc => "Authentication token", type => "string"}, + {desc => "list of bib IDs", type => "array"}, + {desc => "options (for lineitem fleshing)", type => "object"} + ], + return => {desc => "A stream of LIs on success, Event on failure"} + } +); + # This is very similar to zsearch() in Order.pm sub bib_search { - my ($self, $conn, $auth, $search, $options) = @_; + my ($self, $conn, $auth, $search, $opts) = @_; my $e = new_editor("authtoken" => $auth, "xact" => 1); return $e->die_event unless $e->checkauth; @@ -467,47 +483,101 @@ sub bib_search { "editor" => $e, "conn" => $conn ); - $options ||= {}; - $options->{"limit"} ||= 10; - - my $ses = create OpenSRF::AppSession("open-ils.search"); - my $req = $ses->request( - "open-ils.search.biblio.multiclass.query.staff", $options, $search - ); + $opts ||= {}; - my $count = 0; my $picklist; my @li_ids = (); - while (my $resp = $req->recv("timeout" => 60)) { - $picklist = OpenILS::Application::Acq::Order::zsearch_build_pl( - $mgr, undef # XXX could have per-user name for temp picklist here? - ) unless $count++; + if ($self->api_name =~ /create_by_id/) { + $search = [ sort @$search ]; # for consitency + my $bibs = $e->search_biblio_record_entry( + {"id" => $search}, {"order_by" => {"bre" => ["id"]}} + ) or return $e->die_event; + + if ($opts->{"reuse_picklist"}) { + $picklist = $e->retrieve_acq_picklist($opts->{"reuse_picklist"}) or + return $e->die_event; + return $e->die_event unless + $e->allowed("UPDATE_PICKLIST", $picklist->org_unit); + + # If we're reusing an existing picklist, we don't need to + # create new lineitems for any bib records for which we already + + my $already_have = $e->search_acq_lineitem({ + "picklist" => $picklist->id, + "eg_bib_id" => [ map { $_->id } @$bibs ] + }) or return $e->die_event; + + # So in that case we a) save the lineitem id's of the relevant + # items that already exist so that we can return those items later, + # and b) remove the bib id's in question from our list of bib + # id's to lineitemize. + if (@$already_have) { + push @li_ids, $_->id foreach (@$already_have); + my @new_bibs = (); + foreach my $bib (@$bibs) { + push @new_bibs, $bib unless + grep { $_->eg_bib_id == $bib->id } @$already_have; + } + $bibs = [ @new_bibs ]; + } + } else { + $picklist = + OpenILS::Application::Acq::Order::zsearch_build_pl($mgr, undef); + } - my $result = $resp->content; - next if not ref $result; + $conn->respond($picklist->id); - # The result object contains a whole heck of a lot more information - # than just bib IDs, so maybe we could tell the client something - # useful (progress meter at least) in the future... push @li_ids, map { - my $bib = $_->[0]; OpenILS::Application::Acq::Order::create_lineitem( $mgr, "picklist" => $picklist->id, "source_label" => "native-evergreen-catalog", - "marc" => $e->retrieve_biblio_record_entry($bib)->marc, - "eg_bib_id" => $bib + "marc" => $_->marc, + "eg_bib_id" => $_->id )->id; - } (@{$result->{"ids"}}); + } (@$bibs); + } else { + $opts->{"limit"} ||= 10; + + my $ses = create OpenSRF::AppSession("open-ils.search"); + my $req = $ses->request( + "open-ils.search.biblio.multiclass.query.staff", $opts, $search + ); + + my $count = 0; + while (my $resp = $req->recv("timeout" => 60)) { + $picklist = OpenILS::Application::Acq::Order::zsearch_build_pl( + $mgr, undef + ) unless $count++; + + my $result = $resp->content; + next if not ref $result; + + # The result object contains a whole heck of a lot more information + # than just bib IDs, so maybe we could tell the client something + # useful (progress meter at least) in the future... + push @li_ids, map { + my $bib = $_->[0]; + OpenILS::Application::Acq::Order::create_lineitem( + $mgr, + "picklist" => $picklist->id, + "source_label" => "native-evergreen-catalog", + "marc" => $e->retrieve_biblio_record_entry($bib)->marc, + "eg_bib_id" => $bib + )->id; + } (@{$result->{"ids"}}); + } + $ses->disconnect; } $e->commit; - $ses->disconnect; + + $logger->info("created @li_ids new lineitems for picklist $picklist"); # new editor, no transaction needed this time $e = new_editor("authtoken" => $auth) or return $e->die_event; return $e->die_event unless $e->checkauth; - $conn->respond($RETRIEVERS{"lineitem"}->($e, $_, $options)) foreach @li_ids; + $conn->respond($RETRIEVERS{"lineitem"}->($e, $_, $opts)) foreach @li_ids; $e->disconnect; undef; diff --git a/Open-ILS/web/css/skin/default/acq.css b/Open-ILS/web/css/skin/default/acq.css index 74d7e16ea3..b8c5fe6609 100644 --- a/Open-ILS/web/css/skin/default/acq.css +++ b/Open-ILS/web/css/skin/default/acq.css @@ -243,3 +243,5 @@ span[name="bib_origin"] img { vertical-align: middle; } #acq-po-item-table-items tr td button[name="delete"] { color: #c00; } #acq-po-item-table-items tr { margin: 6px 0; } #acq-po-item-table-controls { margin-top: 8px; } + +#acq-litpager-controls[disabled="true"] { color: #ccc; } diff --git a/Open-ILS/web/js/dojo/openils/acq/nls/acq.js b/Open-ILS/web/js/dojo/openils/acq/nls/acq.js index 1177fd690c..6f877b2a60 100644 --- a/Open-ILS/web/js/dojo/openils/acq/nls/acq.js +++ b/Open-ILS/web/js/dojo/openils/acq/nls/acq.js @@ -80,5 +80,6 @@ 'NOT_RECVD': "Not recv'd", 'PRINT': "Print", 'INVOICES': "Invoices", - 'NUM_CLAIMS_EXISTING': "Claims (${0} existing)" + 'NUM_CLAIMS_EXISTING': "Claims (${0} existing)", + 'LOAD_TERMS_FIRST': "You can't retrieve records until you've loaded a CSV file\nwith bibliographic IDs in the first column." } diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table_pager.js b/Open-ILS/web/js/ui/default/acq/common/li_table_pager.js new file mode 100644 index 0000000000..d352e5258a --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/common/li_table_pager.js @@ -0,0 +1,64 @@ +function LiTablePager() { + var self = this; + + this.init = function(fetcher, liTable, offset, limit) { + this.fetcher = fetcher; + this.liTable = liTable; + this.limit = limit || 10; + this.offset = offset || 0; + + dojo.byId("acq-litpager-controls-prev").onclick = + function() { self.go(-1); } + dojo.byId("acq-litpager-controls-next").onclick = + function() { self.go(1); } + }; + + this.go = function(n /* generally (-1, 0, 1) */) { + if (n) this.offset += n * this.limit; + + this.enableControls(false); + + [this.batch, this.total] = this.fetcher(this.offset, this.limit); + + if (this.batch.length) { + this.liTable.reset(); + this.liTable.show("list"); + this.batch.forEach(function(li) { self.liTable.addLineitem(li); }); + } + + this.relabelControls(); + this.enableControls(true); + }; + + this.enableControls = function(yes) { + dojo.byId("acq-litpager-controls-prev").disabled = + (!yes) || this.offset < 1; + dojo.byId("acq-litpager-controls-next").disabled = + (!yes) || ( + (typeof(this.total) != "undefined") && + this.offset + this.limit >= this.total + ); + dojo.attr("acq-litpager-controls", "disabled", String(!yes)); + } + + this.relabelControls = function() { + if (typeof(this.total) != "undefined") { + dojo.byId("acq-litpager-controls-total").innerHTML = this.total; + openils.Util.show("acq-litpager-controls-total-holder", "inline"); + } else { + openils.Util.hide("acq-litpager-controls-total-holder"); + } + + if (this.batch && this.batch.length) { + dojo.byId("acq-litpager-controls-batch-start").innerHTML = + this.offset + 1; + dojo.byId("acq-litpager-controls-batch-end").innerHTML = + this.offset + this.batch.length; + openils.Util.show("acq-litpager-controls-batch-range", "inline"); + } else { + openils.Util.hide("acq-litpager-controls-batch-range"); + } + }; + + this.init.apply(this, arguments); +} diff --git a/Open-ILS/web/js/ui/default/acq/picklist/from_bib.js b/Open-ILS/web/js/ui/default/acq/picklist/from_bib.js new file mode 100644 index 0000000000..64befd5c38 --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/picklist/from_bib.js @@ -0,0 +1,67 @@ +dojo.require("dijit.form.Button"); +dojo.require("openils.widget.XULTermLoader"); + +var termLoader = null; +var liTable = null; +var pager = null; +var usingPl = null; + +function fetchRecords(offset, limit) { + var data = termLoader.attr("value"); + var results = []; + var total = data.length; + if (offset < 0 || offset >= data.length) return [results, total]; + + progressDialog.show(true); + /* notice this call is synchronous */ + fieldmapper.standardRequest( + ["open-ils.acq", "open-ils.acq.biblio.create_by_id"], { + "params": [ + openils.User.authtoken, data.slice(offset, offset + limit), { + "flesh_attrs": true, + "flesh_cancel_reason": true, + "flesh_notes": true, + "reuse_picklist": usingPl + } + ], + "onresponse": function(r) { + if (r = openils.Util.readResponse(r)) { + if (typeof(r) != "object") + usingPl = r; + else if (r.classname && r.classname == "jub") + results.push(r); + /* XXX the ML method is buggy and sometimes responds with + * more objects that we don't want, hence the specific + * conditionals above that don't necesarily consume all + * responses. */ + } + } + } + ); + progressDialog.hide(); + return [results, total]; +} + +function beginSearch() { + var data = termLoader.attr("value"); + if (!data || !data.length) { + alert(localeStrings.LOAD_TERMS_FIRST); + return; + } + + pager.go(0); + openils.Util.hide("acq-frombib-upload-box"); + openils.Util.show("acq-frombib-reload-box"); +} + +function init() { + new openils.widget.XULTermLoader( + {"parentNode": "acq-frombib-upload"} + ).build(function(w) { termLoader = w; }); + liTable = new AcqLiTable(); + pager = new LiTablePager(fetchRecords, liTable); + + openils.Util.show("acq-frombib-begin-holder"); +} + +openils.Util.addOnLoad(init); diff --git a/Open-ILS/web/opac/locale/en-US/lang.dtd b/Open-ILS/web/opac/locale/en-US/lang.dtd index 63f9cc17ea..0093f5535a 100644 --- a/Open-ILS/web/opac/locale/en-US/lang.dtd +++ b/Open-ILS/web/opac/locale/en-US/lang.dtd @@ -801,6 +801,8 @@ + + diff --git a/Open-ILS/web/templates/default/acq/common/li_table_pager.tt2 b/Open-ILS/web/templates/default/acq/common/li_table_pager.tt2 new file mode 100644 index 0000000000..6e30aaae73 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/common/li_table_pager.tt2 @@ -0,0 +1,17 @@ + +
+ + + +
diff --git a/Open-ILS/web/templates/default/acq/picklist/from_bib.tt2 b/Open-ILS/web/templates/default/acq/picklist/from_bib.tt2 new file mode 100644 index 0000000000..f916d69285 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/picklist/from_bib.tt2 @@ -0,0 +1,25 @@ +[% WRAPPER "default/base.tt2" %] +[% ctx.page_title = "Items by Bibliographic ID" %] + +
+
+ Provide one or more CSV files whose first columns + contain Evergreen bibliographic record IDs. +
+
+ +
+ +
+ +
+[% INCLUDE "default/acq/common/li_table_pager.tt2" %] +[% INCLUDE "default/acq/common/li_table.tt2" %] +[% END %] diff --git a/Open-ILS/xul/staff_client/chrome/content/main/menu.js b/Open-ILS/xul/staff_client/chrome/content/main/menu.js index 137c9ed4a9..533a6aef5c 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/menu.js +++ b/Open-ILS/xul/staff_client/chrome/content/main/menu.js @@ -768,6 +768,10 @@ main.menu.prototype = { ['oncommand'], function() { open_eg_web_page('acq/search/unified', 'menu.cmd_acq_unified_search.tab'); } ], + 'cmd_acq_from_bib' : [ + ['oncommand'], + function() { open_eg_web_page('acq/picklist/from_bib', 'menu.cmd_acq_from_bib.tab'); } + ], 'cmd_acq_new_brief_record' : [ ['oncommand'], function() { open_eg_web_page('acq/picklist/brief_record', 'menu.cmd_acq_new_brief_record.tab'); } diff --git a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul index 014e08ba13..231c7fe117 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul +++ b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul @@ -85,6 +85,7 @@ + @@ -266,6 +267,7 @@ + diff --git a/Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties b/Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties index c2d7127f37..f592087a40 100644 --- a/Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties +++ b/Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties @@ -227,6 +227,7 @@ menu.cmd_local_admin_transit_list.tab=Transits menu.cmd_acq_create_invoice.tab=New Invoice menu.cmd_acq_view_picklist.tab=Selection Lists menu.cmd_acq_bib_search.tab=Title Search +menu.cmd_acq_from_bib.tab=Import Catalog Records menu.cmd_acq_unified_search.tab=Acquisitions Search menu.cmd_acq_upload.tab=Load Order Record menu.cmd_acq_new_brief_record.tab=New Brief Record -- 2.11.0