From: phasefx Date: Wed, 27 Oct 2010 18:10:45 +0000 (+0000) Subject: Give staff the ability to select a circ lib and user group and mark all associated... X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=92bd9092229a7b2ba345018e596d7ad64c139e85;p=evergreen%2Ftadl.git Give staff the ability to select a circ lib and user group and mark all associated overdue circulations/items as Lost. Admin->Local System Administration->Age Overdue Circs to Lost. This may be used, for example, by academic libraries after the end of a semester. Must remember to activate the template and to give it an editor param. git-svn-id: svn://svn.open-ils.org/ILS/trunk@18498 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- diff --git a/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm b/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm index 6bf3c4c28b..c3c7ae0c7e 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm @@ -640,6 +640,27 @@ sub fetch_permission_group_tree { 'open-ils.actor.groups.tree.retrieve' ); } +sub fetch_permission_group_descendants { + my( $self, $profile ) = @_; + my $group_tree = $self->fetch_permission_group_tree(); + my $start_here; + my @groups; + + # FIXME: okay, so it's not an org tree, but it is compatible + $self->walk_org_tree($group_tree, sub { + my $g = shift; + if ($g->id == $profile) { + $start_here = $g; + } + }); + + $self->walk_org_tree($start_here, sub { + my $g = shift; + push(@groups,$g->id); + }); + + return \@groups; +} sub fetch_patron_circ_summary { my( $self, $userid ) = @_; diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Circ.pm b/Open-ILS/src/perlmods/OpenILS/Application/Circ.pm index 89a06fa31c..52e76bc900 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Circ.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Circ.pm @@ -252,6 +252,92 @@ sub title_from_transaction { return $apputils->record_to_mvr($title); } +__PACKAGE__->register_method( + method => "staff_age_to_lost", + api_name => "open-ils.circ.circulation.age_to_lost", + stream => 1, + signature => q/ + This fires a circ.staff_age_to_lost Action-Trigger event against all + overdue circulations in scope of the specified context library and + user profile, which effectively marks the associated items as Lost. + This is likely to be done at the end of a semester in an academic + library, etc. + @param auth + @param args : circ_lib, user_profile + / +); + +sub staff_age_to_lost { + my( $self, $conn, $auth, $args ) = @_; + + my $orgs = $U->get_org_descendants($args->{'circ_lib'}); + my $profiles = $U->fetch_permission_group_descendants($args->{'user_profile'}); + + my $ses = OpenSRF::AppSession->create('open-ils.trigger'); + + my $method = 'open-ils.trigger.passive.event.autocreate.batch'; + my $hook = 'circ.staff_age_to_lost'; + my $context_org = 'circ_lib'; + my $opt_granularity = undef; + my $filter = { + "checkin_time" => undef, + "due_date" => { "<" => "now" }, + "-or" => [ + { "stop_fines" => ["MAXFINES", "LONGOVERDUE"] }, # FIXME: CLAIMSRETURNED also? + { "stop_fines" => undef } + ], + "-and" => [ + {"-exists" => { + "select" => {"au" => ["id"]}, + "from" => "au", + "where" => { + "profile" => $profiles, + "id" => { "=" => {"+circ" => "usr"} } + } + }}, + {"-exists" => { + "select" => {"aou" => ["id"]}, + "from" => "aou", + "where" => { + "-and" => [ + {"id" => { "=" => {"+circ" => "circ_lib"} }}, + {"id" => $orgs} + ] + } + }} + ] + }; + my $req_timeout = 10800; + my $chunk_size = 100; + my $progress = 1; + + my $req = $ses->request($method, $hook, $context_org, $filter, $opt_granularity); + my @event_ids; my @chunked_ids; + while (my $resp = $req->recv(timeout => $req_timeout)) { + push(@event_ids, $resp->content); + push(@chunked_ids, $resp->content); + if (scalar(@chunked_ids) > $chunk_size) { + $conn->respond({'progress'=>$progress++}); # 'event_ids'=>@chunked_ids + @chunked_ids = (); + } + } + if (scalar(@chunked_ids) > 0) { + $conn->respond({'progress'=>$progress++}); # 'event_ids'=>@chunked_ids + } + + if(@event_ids) { + $logger->info("staff_age_to_lost: created ".scalar(@event_ids)." events for circ.staff_age_to_lost"); + $conn->respond_complete({'total_progress'=>$progress-1,'created'=>scalar(@event_ids)}); + } elsif($req->complete) { + $logger->info("staff_age_to_lost: no events to create for circ.staff_age_to_lost"); + $conn->respond_complete({'total_progress'=>$progress-1,'created'=>0}); + } else { + $logger->warn("staff_age_to_lost: timeout occurred during event creation for circ.staff_age_to_lost"); + $conn->respond_complete({'total_progress'=>$progress-1,'error'=>'timeout'}); + } + + return undef; +} __PACKAGE__->register_method( diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql index 6c4baf98df..09f9d83929 100644 --- a/Open-ILS/src/sql/Pg/002.schema.config.sql +++ b/Open-ILS/src/sql/Pg/002.schema.config.sql @@ -70,7 +70,7 @@ CREATE TABLE config.upgrade_log ( install_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() ); -INSERT INTO config.upgrade_log (version) VALUES ('0447'); -- gmc +INSERT INTO config.upgrade_log (version) VALUES ('0448'); -- phasefx CREATE TABLE config.bib_source ( id SERIAL PRIMARY KEY, diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql index 3fbc6cd0dd..f58aaf463d 100644 --- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql +++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql @@ -7030,4 +7030,39 @@ INSERT INTO action_trigger.validator (module, description) VALUES ( ) ); +-- 0448.data.trigger.circ.staff_age_to_lost.sql + +INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES + ( 'circ.staff_age_to_lost', + 'circ', + oils_i18n_gettext( + 'circ.staff_age_to_lost', + 'An overdue circulation should be aged to a Lost status.', + 'ath', + 'description' + ), + TRUE + ) +; + +INSERT INTO action_trigger.event_definition ( + id, + active, + owner, + name, + hook, + validator, + reactor, + delay_field + ) VALUES ( + 36, + FALSE, + 1, + 'circ.staff_age_to_lost', + 'circ.staff_age_to_lost', + 'CircIsOverdue', + 'MarkItemLost', + 'due_date' + ) +; diff --git a/Open-ILS/src/sql/Pg/upgrade/0448.data.trigger.circ.staff_age_to_lost.sql b/Open-ILS/src/sql/Pg/upgrade/0448.data.trigger.circ.staff_age_to_lost.sql new file mode 100644 index 0000000000..1eeb59de01 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/0448.data.trigger.circ.staff_age_to_lost.sql @@ -0,0 +1,42 @@ +BEGIN; + +INSERT INTO config.upgrade_log (version) VALUES ('0448'); -- phasefx + +INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES + ( 'circ.staff_age_to_lost', + 'circ', + oils_i18n_gettext( + 'circ.staff_age_to_lost', + 'An overdue circulation should be aged to a Lost status.', + 'ath', + 'description' + ), + TRUE + ) +; + +INSERT INTO action_trigger.event_definition ( + id, + active, + owner, + name, + hook, + validator, + reactor, + delay_field + ) VALUES ( + 36, + FALSE, + 1, + 'circ.staff_age_to_lost', + 'circ.staff_age_to_lost', + 'CircIsOverdue', + 'MarkItemLost', + 'due_date' + ) +; + +-- DELETE FROM config.upgrade_log WHERE version = '0448'; DELETE FROM action_trigger.event WHERE event_def = 36; DELETE FROM action_trigger.event_params WHERE event_def = 36; DELETE FROM action_trigger.event_definition WHERE id = 36; DELETE FROM action_trigger.hook WHERE key = 'circ.staff_age_to_lost'; + +COMMIT; + diff --git a/Open-ILS/web/opac/locale/en-US/lang.dtd b/Open-ILS/web/opac/locale/en-US/lang.dtd index a1d3dc3145..c40c7c9325 100644 --- a/Open-ILS/web/opac/locale/en-US/lang.dtd +++ b/Open-ILS/web/opac/locale/en-US/lang.dtd @@ -1920,6 +1920,13 @@ + + + + + + + diff --git a/Open-ILS/xul/staff_client/chrome/content/main/constants.js b/Open-ILS/xul/staff_client/chrome/content/main/constants.js index e95db3d3c6..0cb38829b4 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/constants.js +++ b/Open-ILS/xul/staff_client/chrome/content/main/constants.js @@ -214,6 +214,7 @@ var api = { 'FM_BRN_FROM_MARCXML' : { 'app' : 'open-ils.search', 'method' : 'open-ils.search.z3950.marcxml_to_brn', 'secure' : false }, 'FM_CBT_RETRIEVE' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.billing_type.ranged.retrieve.all', 'secure' : false }, 'FM_CCS_RETRIEVE' : { 'app' : 'open-ils.search', 'method' : 'open-ils.search.config.copy_status.retrieve.all', 'secure' : false }, + 'FM_CIRC_AGE_TO_LOST' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.circulation.age_to_lost' }, 'FM_CIRC_CHAIN' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.renewal_chain.retrieve_by_circ.atomic' }, 'FM_CIRC_CHAIN_SUMMARY' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.renewal_chain.retrieve_by_circ.summary' }, 'FM_CIRC_PREV_CHAIN' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.prev_renewal_chain.retrieve_by_circ.atomic' }, 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 fa37d37fe4..95d121edad 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/menu.js +++ b/Open-ILS/xul/staff_client/chrome/content/main/menu.js @@ -556,6 +556,10 @@ main.menu.prototype = { ['oncommand'], function() { open_admin_page('transit_list.xul', 'menu.cmd_local_admin_transit_list.tab'); } ], + 'cmd_local_admin_age_overdue_circulations_to_lost' : [ + ['oncommand'], + function() { open_admin_page('circ_age_to_lost.xul', 'menu.cmd_local_admin_age_overdue_circulations_to_lost.tab', true); } + ], 'cmd_local_admin_cash_reports' : [ ['oncommand'], function() { open_admin_page('cash_reports.xhtml', 'menu.cmd_local_admin_cash_reports.tab', true); } 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 ec4d000e29..ff1d29f5ea 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 @@ -121,6 +121,9 @@ + @@ -349,6 +352,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 8c2d949582..ba7a470e31 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 @@ -224,6 +224,7 @@ menu.cmd_local_admin_lib_settings.tab=Library Settings Editor menu.cmd_local_admin_non_cat_types.tab=Non-cataloged Types Editor menu.cmd_local_admin_stat_cats.tab=Statistical Categories Editor menu.cmd_local_admin_reports.tab=Reports +menu.cmd_local_admin_age_overdue_circulations_to_lost.tab=Age to Lost menu.cmd_local_admin_cash_reports.tab=Cash Reports menu.cmd_local_admin_transit_list.tab=Transits menu.cmd_acq_create_invoice.tab=New Invoice diff --git a/Open-ILS/xul/staff_client/server/admin/circ_age_to_lost.js b/Open-ILS/xul/staff_client/server/admin/circ_age_to_lost.js new file mode 100644 index 0000000000..b875cc32d3 --- /dev/null +++ b/Open-ILS/xul/staff_client/server/admin/circ_age_to_lost.js @@ -0,0 +1,121 @@ +var error; +var data; + +function my_init() { + try { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + if (typeof JSAN == 'undefined') { throw( "The JSAN library object is missing."); } + JSAN.errorLevel = "die"; // none, warn, or die + JSAN.addRepository('/xul/server/'); + JSAN.use('util.error'); error = new util.error(); + error.sdump('D_TRACE','my_init() for circ_age_to_lost.xul'); + + JSAN.use('OpenILS.data'); data = new OpenILS.data(); data.stash_retrieve(); + + build_pgt_list(); + build_ou_list(); + + $('doit').addEventListener('command',doit,false); + + if (typeof window.xulG == 'object' && typeof window.xulG.set_tab_name == 'function') { + try { window.xulG.set_tab_name( $('offlineStrings').getString('menu.cmd_local_admin_age_overdue_circulations_to_lost.tab') ); } catch(E) { alert(E); } + } + + } catch(E) { + alert('Error in admin/circ_age_to_lost.xul, my_init(): ' + E); + } +} + +function doit(ev) { + try { + $('checkbox').disabled = true; + ev.target.disabled = true; + $('deck').selectedIndex = 1; + var profile = $('profile').value; + var circ_lib = $('circ_lib').value; + + function response_handler(e,r,list) { + try { + var result; + switch(e) { + case 'oncomplete' : return; break; + default: result = r.recv().content(); break; + } + dump(e + ' result = ' + js2JSON(result) + '\n'); + if (typeof result.progress != 'undefined') { + + $('results_label').setAttribute('value', $('adminStrings').getFormattedString('staff.admin.age_overdue_circulations_to_lost.completed_so_far',[result.progress]) ); + + } else if (typeof result.created != 'undefined') { + + $('results_label').setAttribute('value', $('adminStrings').getFormattedString('staff.admin.age_overdue_circulations_to_lost.completed_total',[result.created]) ); + $('deck').selectedIndex = 0; + + } else if (typeof result.error != 'undefined') { + + $('deck').selectedIndex = 0; + throw(result.error); + + } else { + throw(result); + } + } catch(E) { + $('deck').selectedIndex = 0; + alert('Error in admin/circ_age_to_lost.js, doit(), ' + e + ': ' + r + ' => ' + E); + } + } + dump('firing ' + api.FM_CIRC_AGE_TO_LOST.method + ' with profile ' + profile + ' and circ_lib ' + circ_lib + '\n'); + fieldmapper.standardRequest( + [ api.FM_CIRC_AGE_TO_LOST.app, api.FM_CIRC_AGE_TO_LOST.method ], + { async: true, + params: [ses(), { 'user_profile' : profile, 'circ_lib' : circ_lib } ], + onresponse: function(r) { response_handler('onresponse',r); }, + oncomplete: function(r) { response_handler('oncomplete',r); }, + onerror: function(r) { response_handler('onerror',r); } + } + ); + + } catch(E) { + alert('Error in admin/circ_age_to_lost.js, doit(): ' + E); + } +} + +function build_pgt_list() { + JSAN.use('util.functional'); JSAN.use('util.widgets'); + var default_profile = data.tree.pgt.id(); + var menu_data = util.functional.map_list( + data.list.pgt, + function(obj) { + var sname = obj.name(); + for (i = sname.length; i < 20; i++) { + sname += ' '; + } + var depth = 0; var p = obj; + while (p = data.hash.pgt[ p.parent() ]) { depth++; } + return [ + obj.description() ? sname + ' : ' + obj.description() : obj.name(), + obj.id(), + false, // disable menuentry? + ( depth * 2) // spaces of indentation + ]; + } + ); + var ml = util.widgets.make_menulist( menu_data, default_profile ); + ml.setAttribute('id','profile'); $('x_profile').appendChild(ml); +} + +function build_ou_list() { + JSAN.use('util.file'); JSAN.use('util.widgets'); + var file = new util.file('offline_ou_list'); + if (file._file.exists()) { + var menu_data = file.get_object(); file.close(); + for (var i = 0; i < menu_data[0].length; i++) { // make sure all entries are enabled + menu_data[0][i][2] = false; + } + ml = util.widgets.make_menulist( menu_data[0], menu_data[1] ); + ml.setAttribute('id','circ_lib'); $('x_circ_lib').appendChild(ml); + } else { + throw('Missing file offline_ou_list in build_ou_list()'); + } +} + diff --git a/Open-ILS/xul/staff_client/server/admin/circ_age_to_lost.xul b/Open-ILS/xul/staff_client/server/admin/circ_age_to_lost.xul new file mode 100644 index 0000000000..b9664fc1c7 --- /dev/null +++ b/Open-ILS/xul/staff_client/server/admin/circ_age_to_lost.xul @@ -0,0 +1,65 @@ + + + + + + + + + + + + +]> + + + + + + + + + + + + +