'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 ) = @_;
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(
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,
)
);
+-- 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'
+ )
+;
--- /dev/null
+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;
+
<!ENTITY staff.server.admin.index.testing "(Testing)">
<!ENTITY staff.server.admin.index.hold_pull_list_classic "Pull List for Hold Requests (Classic)">
<!ENTITY staff.server.admin.index.reports "Reports">
+<!ENTITY staff.server.admin.index.age_overdue_circulations_to_lost.label "Age Overdue Circs to Lost">
+<!ENTITY staff.server.admin.index.age_overdue_circulations_to_lost.accesskey "">
+<!ENTITY staff.server.admin.index.age_overdue_circulations_to_lost.description "Choose the user profile and circulation library for the overdue circulations you wish to age to a Lost status. Note that descendents of these values (sub-groups, sub-libraries) will also be affected.">
+<!ENTITY staff.server.admin.index.age_overdue_circulations_to_lost.user_profile "User Profile:">
+<!ENTITY staff.server.admin.index.age_overdue_circulations_to_lost.circ_lib "Circulation Library:">
+<!ENTITY staff.server.admin.index.age_overdue_circulations_to_lost.confirm "Are you sure?">
+<!ENTITY staff.server.admin.index.age_overdue_circulations_to_lost.action "Queue for Aging">
<!ENTITY staff.server.admin.index.cash_reports "Cash Reports">
<!ENTITY staff.server.admin.index.transits "Transits">
<!ENTITY staff.server.admin.index.transit_list "Transit List">
'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' },
['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); }
<command id="cmd_local_admin_action_trigger"/>
<command id="cmd_local_admin_survey"/>
<command id="cmd_local_admin_reports"/>
+ <command id="cmd_local_admin_age_overdue_circulations_to_lost"
+ label="&staff.server.admin.index.age_overdue_circulations_to_lost.label;"
+ accesskey="&staff.server.admin.index.age_overdue_circulations_to_lost.accesskey;"/>
<command id="cmd_local_admin_cash_reports"/>
<command id="cmd_local_admin_transit_list"/>
<command id="cmd_local_admin_circ_matrix_matchpoint"/>
<menu id="main.menu.admin.local" label="&staff.main.menu.admin.local_admin.label;">
<menupopup id="main.menu.admin.local.popup">
+ <menuitem command="cmd_local_admin_age_overdue_circulations_to_lost"/>
<menuitem label="&staff.server.admin.index.cash_reports;" command="cmd_local_admin_cash_reports"/>
<menuitem label="&staff.main.menu.admin.local_admin.circ_matrix_matchpoint.label;" command="cmd_local_admin_circ_matrix_matchpoint"/>
<menuitem label="&staff.server.admin.index.closed_dates;" command="cmd_local_admin_closed_dates"/>
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
--- /dev/null
+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()');
+ }
+}
+
--- /dev/null
+<?xml version="1.0"?>
+<!-- Application: Evergreen Staff Client -->
+<!-- Screen: Example Template for remote xul -->
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- STYLESHEETS -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="/xul/server/skin/global.css" type="text/css"?>
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- LOCALIZATION -->
+<!DOCTYPE window PUBLIC "" ""[
+ <!--#include virtual="/opac/locale/${locale}/lang.dtd"-->
+]>
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- OVERLAYS -->
+<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
+
+<window id="circ_age_to_lost_win"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+ <!-- BEHAVIOR -->
+ <script type="text/javascript">
+ var myPackageDir = 'open_ils_staff_client'; var IAMXUL = true;
+ </script>
+ <scripts id="openils_util_scripts"/>
+
+ <script type="text/javascript" src="/xul/server/main/JSAN.js"/>
+ <script type="text/javascript" src="circ_age_to_lost.js"/>
+
+ <messagecatalog id="adminStrings" src="/xul/server/locale/<!--#echo var='locale'-->/admin.properties"/>
+
+ <vbox flex="1">
+ <grid>
+ <columns>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <spacer/>
+ <description>&staff.server.admin.index.age_overdue_circulations_to_lost.description;</description>
+ </row>
+ <row>
+ <label value="&staff.server.admin.index.age_overdue_circulations_to_lost.user_profile;" /><vbox id="x_profile" />
+ </row>
+ <row>
+ <label value="&staff.server.admin.index.age_overdue_circulations_to_lost.circ_lib;" /><vbox id="x_circ_lib" />
+ </row>
+ <row>
+ <checkbox id="checkbox" label="&staff.server.admin.index.age_overdue_circulations_to_lost.confirm;" oncommand="$('doit').disabled = ! this.checked"/>
+ <deck id="deck">
+ <button id="doit" label="&staff.server.admin.index.age_overdue_circulations_to_lost.action;" disabled="true"/>
+ <progressmeter mode="undetermined" />
+ </deck>
+ </row>
+ </rows>
+ </grid>
+ <label id="results_label" style="font-size: x-large;"/>
+ </vbox>
+</window>
+
+staff.admin.age_overdue_circulations_to_lost.completed_so_far=Completed: %1$s
+staff.admin.age_overdue_circulations_to_lost.completed_total=Total Completed: %1$s
staff.admin.font_settings.sound=Sound preference saved to file system.
staff.admin.font_settings.save=Global Font saved to file system.
staff.admin.font_settings.sound.disabled=Sound is now disabled.