Give staff the ability to select a circ lib and user group and mark all associated...
authorphasefx <phasefx@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Wed, 27 Oct 2010 18:10:45 +0000 (18:10 +0000)
committerphasefx <phasefx@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Wed, 27 Oct 2010 18:10:45 +0000 (18:10 +0000)
git-svn-id: svn://svn.open-ils.org/ILS/trunk@18498 dcc99617-32d9-48b4-a31d-7c20da2025e4

13 files changed:
Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm
Open-ILS/src/perlmods/OpenILS/Application/Circ.pm
Open-ILS/src/sql/Pg/002.schema.config.sql
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/upgrade/0448.data.trigger.circ.staff_age_to_lost.sql [new file with mode: 0644]
Open-ILS/web/opac/locale/en-US/lang.dtd
Open-ILS/xul/staff_client/chrome/content/main/constants.js
Open-ILS/xul/staff_client/chrome/content/main/menu.js
Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul
Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties
Open-ILS/xul/staff_client/server/admin/circ_age_to_lost.js [new file with mode: 0644]
Open-ILS/xul/staff_client/server/admin/circ_age_to_lost.xul [new file with mode: 0644]
Open-ILS/xul/staff_client/server/locale/en-US/admin.properties

index 6bf3c4c..c3c7ae0 100644 (file)
@@ -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 ) = @_;
index 89a06fa..52e76bc 100644 (file)
@@ -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(
index 6c4baf9..09f9d83 100644 (file)
@@ -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,
index 3fbc6cd..f58aaf4 100644 (file)
@@ -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 (file)
index 0000000..1eeb59d
--- /dev/null
@@ -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;
+
index a1d3dc3..c40c7c9 100644 (file)
 <!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">
index e95db3d..0cb3882 100644 (file)
@@ -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' },
index fa37d37..95d121e 100644 (file)
@@ -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); }
index ec4d000..ff1d29f 100644 (file)
     <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"/>
index 8c2d949..ba7a470 100644 (file)
@@ -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 (file)
index 0000000..b875cc3
--- /dev/null
@@ -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 (file)
index 0000000..b9664fc
--- /dev/null
@@ -0,0 +1,65 @@
+<?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>
+
index 53fbaca..a1ae73a 100644 (file)
@@ -1,3 +1,5 @@
+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.