[open-ils-commits] [GIT] Evergreen ILS branch master updated. cc0ef7448c8a92f1715dd1726aead4409a8b0e08

Evergreen Git git at git.evergreen-ils.org
Fri Jun 7 15:35:14 EDT 2013


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, master has been updated
       via  cc0ef7448c8a92f1715dd1726aead4409a8b0e08 (commit)
       via  eb4fc15d55d1a74a9fafe24a033955a6a757a2c6 (commit)
       via  246fff2d71f640954194f999af0a59f06a416e9a (commit)
       via  dc40c1c748cfbb0c4b14d94b5fe72825cb110a08 (commit)
       via  765aea78207f879e81eeb9f3d7614699399e585f (commit)
       via  d43e92f682f6e06a61d508944cf340093168e77a (commit)
       via  86995bbb791f3ba0771e92921f756f6680a22571 (commit)
       via  0771f7c6ea249a336ec7e68bd6e1949311ce3856 (commit)
       via  1523a8b6c8512cac58d8ecbe5a5456265eb6fcf3 (commit)
      from  288c1d1c5f23e991bbaf74c42ed4fba7ef5c1151 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit cc0ef7448c8a92f1715dd1726aead4409a8b0e08
Author: Dan Wells <dbw2 at calvin.edu>
Date:   Fri Jun 7 15:31:37 2013 -0400

    Stamping upgrade script for purge holds functionality
    
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql
index 769ad25..e7f467f 100644
--- a/Open-ILS/src/sql/Pg/002.schema.config.sql
+++ b/Open-ILS/src/sql/Pg/002.schema.config.sql
@@ -91,7 +91,7 @@ CREATE TRIGGER no_overlapping_deps
     BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
     FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
 
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0796', :eg_version); -- berick/dbwells
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0797', :eg_version); -- tsbere/Dyrcona/dbwells
 
 CREATE TABLE config.bib_source (
 	id		SERIAL	PRIMARY KEY,
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.action.purge_holds.sql b/Open-ILS/src/sql/Pg/upgrade/0797.schema.action.purge_holds.sql
similarity index 99%
rename from Open-ILS/src/sql/Pg/upgrade/XXXX.schema.action.purge_holds.sql
rename to Open-ILS/src/sql/Pg/upgrade/0797.schema.action.purge_holds.sql
index 5389856..dc50c92 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.action.purge_holds.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/0797.schema.action.purge_holds.sql
@@ -1,3 +1,6 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0797', :eg_version); -- tsbere/Dyrcona/dbwells
 
 -- New global flags for the purge function
 INSERT INTO config.global_flag  (name, label, enabled)
@@ -366,3 +369,4 @@ CREATE TRIGGER action_hold_request_aging_tgr
 	FOR EACH ROW
 	EXECUTE PROCEDURE action.age_hold_on_delete ();
 
+COMMIT;

commit eb4fc15d55d1a74a9fafe24a033955a6a757a2c6
Author: Jason Stephenson <jason at sigio.com>
Date:   Wed Apr 10 17:29:28 2013 -0400

    Update the purge_holds.txt release notes.
    
    Signed-off-by: Jason Stephenson <jstephenson at mvlc.org>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/docs/RELEASE_NOTES_NEXT/purge_holds.txt b/docs/RELEASE_NOTES_NEXT/purge_holds.txt
index f015f6d..75d2371 100644
--- a/docs/RELEASE_NOTES_NEXT/purge_holds.txt
+++ b/docs/RELEASE_NOTES_NEXT/purge_holds.txt
@@ -2,6 +2,8 @@ New Feature: "Purge Holds"
 ==========================
 Similar to purging circulations one may wish to purge old (filled or canceled) hold information. This feature adds a database function and settings for doing so.
 
+Purged holds are moved to the action.aged_hold_request table with patron identifying information scrubbed, much like circulations are moved to action.aged_circulation.
+
 The settings allow for a default retention age as well as filled, canceled, and canceled by cancel cause ages. The most specific one wins unless a patron is retaining their hold history. In the latter case the patron's holds are retained either way.
 
-Note that the function still needs to be called, which could be set up as a cron job or done more manually, say after statistics collection.
+Note that the function still needs to be called, which could be set up as a cron job or done more manually, say after statistics collection. A new script, purge_holds.srfsh, is added that can be used to purge holds from cron.

commit 246fff2d71f640954194f999af0a59f06a416e9a
Author: Jason Stephenson <jstephenson at mvlc.org>
Date:   Thu Apr 4 11:30:25 2013 -0400

    Add table, view and trigger for "aging" hold requests on delete.
    
    This creates the action.aged_hold_request table, the
    action.all_hold_request view, the action.age_hold_on_delete
    function, and the action_hold_request_aging_tgr on action.hold_request.
    
    Add fieldmapper entries for action.all_hold_request view and
    action.aged_hold_request table.
    
    Signed-off-by: Jason Stephenson <jstephenson at mvlc.org>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index ec6525e..b4614ee 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -5421,6 +5421,123 @@ SELECT  usr,
 			</actions>
 		</permacrud>
 	</class>
+	<class id="combahr" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action::all_hold_request" oils_persist:tablename="action.all_hold_request" reporter:core="true" reporter:label="Combined (Active &amp; Aged) Hold Request">
+		<fields oils_persist:primary="id" oils_persist:sequence="action.hold_request_id_seq">
+			<field reporter:label="Status" name="status" oils_persist:virtual="true" />
+			<field reporter:label="Capture Date/Time" name="capture_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Currently Targeted Copy" name="current_copy" />
+			<field reporter:label="Notify by Email?" name="email_notify" reporter:datatype="bool"/>
+			<field reporter:label="Hold Expire Date/Time" name="expire_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Fulfilling Library" name="fulfillment_lib" reporter:datatype="org_unit"/>
+			<field reporter:label="Fulfilling Staff" name="fulfillment_staff" />
+			<field reporter:label="Fulfillment Date/Time" name="fulfillment_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Hold Type" name="hold_type" reporter:datatype="text"/>
+			<field reporter:label="Holdable Formats (for M-type hold)" name="holdable_formats" reporter:datatype="text"/>
+			<field reporter:label="Hold ID" name="id" reporter:datatype="id" />
+			<field reporter:label="Notify by Phone?" name="phone_notify" reporter:datatype="bool"/>
+			<field reporter:label="Notify by SMS?" name="sms_notify" reporter:datatype="bool"/>
+			<field reporter:label="Pickup Library" name="pickup_lib" reporter:datatype="org_unit"/>
+			<field reporter:label="Last Targeting Date/Time" name="prev_check_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Requesting Library" name="request_lib" reporter:datatype="org_unit"/>
+			<field reporter:label="Request Date/Time" name="request_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Patron ZIP" name="usr_post_code" reporter:datatype="text"/>
+			<field reporter:label="Patron Home Library" name="usr_home_ou" reporter:datatype="link"/>
+			<field reporter:label="Patron Profile Group" name="usr_profile" reporter:datatype="link"/>
+			<field reporter:label="Patron Birth Year" name="usr_birth_year" reporter:datatype="int"/>
+			<field reporter:label="Staff Placed?" name="staff_placed" reporter:datatype="bool"/>
+			<field reporter:label="Item Selection Depth" name="selection_depth" />
+			<field reporter:label="Selection Locus" name="selection_ou" reporter:datatype="org_unit"/>
+			<field reporter:label="Target Object ID" name="target" reporter:datatype="link"/>
+			<field reporter:label="Hold Cancel Date/Time" name="cancel_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Bib Record link" name="bib_rec" oils_persist:virtual="true" reporter:datatype="link"/>
+			<field reporter:label="Currently Frozen" name="frozen" reporter:datatype="bool"/>
+			<field reporter:label="Thaw Date (if frozen)" name="thaw_date" reporter:datatype="timestamp"/>
+			<field reporter:label="Shelf Time" name="shelf_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Cancelation cause" name="cancel_cause" reporter:datatype="link" />
+			<field reporter:label="Cancelation note" name="cancel_note" reporter:datatype="text" />
+			<field reporter:label="Top of Queue" name="cut_in_line" reporter:datatype="bool" />
+			<field reporter:label="Is Mint Condition" name="mint_condition" reporter:datatype="bool" />
+			<field reporter:label="Shelf Expire Time" name="shelf_expire_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Current Shelf Lib" name="current_shelf_lib" reporter:datatype="org_unit"/>
+		</fields>
+		<links>
+			<link field="fulfillment_lib" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="fulfillment_staff" reltype="has_a" key="id" map="" class="au"/>
+			<link field="pickup_lib" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="selection_ou" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="current_copy" reltype="has_a" key="id" map="" class="acp"/>
+			<link field="request_lib" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="usr_home_ou" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="bib_rec" reltype="might_have" key="id" map="" class="rhrr"/>
+			<link field="cancel_cause" reltype="might_have" key="id" map="" class="ahrcc"/>
+			<link field="current_shelf_lib" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="usr_profile" reltype="has_a" key="id" map="" class="pgt"/>
+		</links>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<retrieve permission="VIEW_HOLD" context_field="pickup_lib" />
+			</actions>
+		</permacrud>
+	</class>
+	<class id="aahr" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action::aged_hold_request" oils_persist:tablename="action.aged_hold_request" reporter:core="true" reporter:label="Aged Hold Request">
+		<fields oils_persist:primary="id" oils_persist:sequence="action.hold_request_id_seq">
+			<field reporter:label="Status" name="status" oils_persist:virtual="true" />
+			<field reporter:label="Capture Date/Time" name="capture_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Currently Targeted Copy" name="current_copy" />
+			<field reporter:label="Notify by Email?" name="email_notify" reporter:datatype="bool"/>
+			<field reporter:label="Hold Expire Date/Time" name="expire_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Fulfilling Library" name="fulfillment_lib" reporter:datatype="org_unit"/>
+			<field reporter:label="Fulfilling Staff" name="fulfillment_staff" />
+			<field reporter:label="Fulfillment Date/Time" name="fulfillment_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Hold Type" name="hold_type" reporter:datatype="text"/>
+			<field reporter:label="Holdable Formats (for M-type hold)" name="holdable_formats" reporter:datatype="text"/>
+			<field reporter:label="Hold ID" name="id" reporter:datatype="id" />
+			<field reporter:label="Notify by Phone?" name="phone_notify" reporter:datatype="bool"/>
+			<field reporter:label="Notify by SMS?" name="sms_notify" reporter:datatype="bool"/>
+			<field reporter:label="Pickup Library" name="pickup_lib" reporter:datatype="org_unit"/>
+			<field reporter:label="Last Targeting Date/Time" name="prev_check_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Requesting Library" name="request_lib" reporter:datatype="org_unit"/>
+			<field reporter:label="Request Date/Time" name="request_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Patron ZIP" name="usr_post_code" reporter:datatype="text"/>
+			<field reporter:label="Patron Home Library" name="usr_home_ou" reporter:datatype="link"/>
+			<field reporter:label="Patron Profile Group" name="usr_profile" reporter:datatype="link"/>
+			<field reporter:label="Patron Birth Year" name="usr_birth_year" reporter:datatype="int"/>
+			<field reporter:label="Staff Placed?" name="staff_placed" reporter:datatype="bool"/>
+			<field reporter:label="Item Selection Depth" name="selection_depth" />
+			<field reporter:label="Selection Locus" name="selection_ou" reporter:datatype="org_unit"/>
+			<field reporter:label="Target Object ID" name="target" reporter:datatype="link"/>
+			<field reporter:label="Hold Cancel Date/Time" name="cancel_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Bib Record link" name="bib_rec" oils_persist:virtual="true" reporter:datatype="link"/>
+			<field reporter:label="Currently Frozen" name="frozen" reporter:datatype="bool"/>
+			<field reporter:label="Thaw Date (if frozen)" name="thaw_date" reporter:datatype="timestamp"/>
+			<field reporter:label="Shelf Time" name="shelf_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Cancelation cause" name="cancel_cause" reporter:datatype="link" />
+			<field reporter:label="Cancelation note" name="cancel_note" reporter:datatype="text" />
+			<field reporter:label="Top of Queue" name="cut_in_line" reporter:datatype="bool" />
+			<field reporter:label="Is Mint Condition" name="mint_condition" reporter:datatype="bool" />
+			<field reporter:label="Shelf Expire Time" name="shelf_expire_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Current Shelf Lib" name="current_shelf_lib" reporter:datatype="org_unit"/>
+		</fields>
+		<links>
+			<link field="fulfillment_lib" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="fulfillment_staff" reltype="has_a" key="id" map="" class="au"/>
+			<link field="pickup_lib" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="selection_ou" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="current_copy" reltype="has_a" key="id" map="" class="acp"/>
+			<link field="request_lib" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="usr_home_ou" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="bib_rec" reltype="might_have" key="id" map="" class="rhrr"/>
+			<link field="cancel_cause" reltype="might_have" key="id" map="" class="ahrcc"/>
+			<link field="current_shelf_lib" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="usr_profile" reltype="has_a" key="id" map="" class="pgt"/>
+		</links>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<retrieve permission="VIEW_HOLD" context_field="pickup_lib" />
+			</actions>
+		</permacrud>
+	</class>
+
 	<class id="aou" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::org_unit" oils_persist:tablename="actor.org_unit" reporter:label="Organizational Unit" oils_persist:field_safe="true">
 		<fields oils_persist:primary="id" oils_persist:sequence="actor.org_unit_id_seq">
 			<field reporter:label="Subordinate Organizational Units" name="children" oils_persist:virtual="true" reporter:datatype="org_unit"/>
diff --git a/Open-ILS/src/sql/Pg/090.schema.action.sql b/Open-ILS/src/sql/Pg/090.schema.action.sql
index d07f07f..bb5c66d 100644
--- a/Open-ILS/src/sql/Pg/090.schema.action.sql
+++ b/Open-ILS/src/sql/Pg/090.schema.action.sql
@@ -524,6 +524,210 @@ CREATE VIEW action.unfulfilled_hold_max_loop AS
       GROUP BY 1;
 
 
+CREATE TABLE action.aged_hold_request (
+    usr_post_code		TEXT,
+    usr_home_ou		INT	NOT NULL,
+    usr_profile		INT	NOT NULL,
+    usr_birth_year		INT,
+    staff_placed        BOOLEAN NOT NULL,
+    LIKE action.hold_request
+);
+ALTER TABLE action.aged_hold_request
+      ADD PRIMARY KEY (id),
+      DROP COLUMN usr,
+      DROP COLUMN requestor,
+      DROP COLUMN sms_carrier,
+      ALTER COLUMN phone_notify TYPE BOOLEAN
+            USING CASE WHEN phone_notify IS NULL OR phone_notify = '' THEN FALSE ELSE TRUE END,
+      ALTER COLUMN sms_notify TYPE BOOLEAN
+            USING CASE WHEN sms_notify IS NULL OR sms_notify = '' THEN FALSE ELSE TRUE END,
+      ALTER COLUMN phone_notify SET NOT NULL,
+      ALTER COLUMN sms_notify SET NOT NULL;
+CREATE INDEX aged_hold_request_target_idx ON action.aged_hold_request (target);
+CREATE INDEX aged_hold_request_pickup_lib_idx ON action.aged_hold_request (pickup_lib);
+CREATE INDEX aged_hold_request_current_copy_idx ON action.aged_hold_request (current_copy);
+CREATE INDEX aged_hold_request_fulfillment_staff_idx ON action.aged_hold_request ( fulfillment_staff );
+
+CREATE OR REPLACE VIEW action.all_hold_request AS
+    SELECT DISTINCT
+           COALESCE(a.post_code, b.post_code) AS usr_post_code,
+           p.home_ou AS usr_home_ou,
+           p.profile AS usr_profile,
+           EXTRACT(YEAR FROM p.dob)::INT AS usr_birth_year,
+           CAST(ahr.requestor <> ahr.usr AS BOOLEAN) AS staff_placed,
+           ahr.id,
+           ahr.request_time,
+           ahr.capture_time,
+           ahr.fulfillment_time,
+           ahr.checkin_time,
+           ahr.return_time,
+           ahr.prev_check_time,
+           ahr.expire_time,
+           ahr.cancel_time,
+           ahr.cancel_cause,
+           ahr.cancel_note,
+           ahr.target,
+           ahr.current_copy,
+           ahr.fulfillment_staff,
+           ahr.fulfillment_lib,
+           ahr.request_lib,
+           ahr.selection_ou,
+           ahr.selection_depth,
+           ahr.pickup_lib,
+           ahr.hold_type,
+           ahr.holdable_formats,
+           CASE
+           WHEN ahr.phone_notify IS NULL THEN FALSE
+           WHEN ahr.phone_notify = '' THEN FALSE
+           ELSE TRUE
+           END AS phone_notify,
+           ahr.email_notify,
+           CASE
+           WHEN ahr.sms_notify IS NULL THEN FALSE
+           WHEN ahr.sms_notify = '' THEN FALSE
+           ELSE TRUE
+           END AS sms_notify,
+           ahr.frozen,
+           ahr.thaw_date,
+           ahr.shelf_time,
+           ahr.cut_in_line,
+           ahr.mint_condition,
+           ahr.shelf_expire_time,
+           ahr.current_shelf_lib
+    FROM action.hold_request ahr
+         JOIN actor.usr p ON (ahr.usr = p.id)
+         LEFT JOIN actor.usr_address a ON (p.mailing_address = a.id)
+         LEFT JOIN actor.usr_address b ON (p.billing_address = b.id)
+    UNION ALL
+    SELECT 
+           usr_post_code,
+           usr_home_ou,
+           usr_profile,
+           usr_birth_year,
+           staff_placed,
+           id,
+           request_time,
+           capture_time,
+           fulfillment_time,
+           checkin_time,
+           return_time,
+           prev_check_time,
+           expire_time,
+           cancel_time,
+           cancel_cause,
+           cancel_note,
+           target,
+           current_copy,
+           fulfillment_staff,
+           fulfillment_lib,
+           request_lib,
+           selection_ou,
+           selection_depth,
+           pickup_lib,
+           hold_type,
+           holdable_formats,
+           phone_notify,
+           email_notify,
+           sms_notify,
+           frozen,
+           thaw_date,
+           shelf_time,
+           cut_in_line,
+           mint_condition,
+           shelf_expire_time,
+           current_shelf_lib
+    FROM action.aged_hold_request;
+
+CREATE OR REPLACE FUNCTION action.age_hold_on_delete () RETURNS TRIGGER AS $$
+DECLARE
+BEGIN
+    -- Archive a copy of the old row to action.aged_hold_request
+
+    INSERT INTO action.aged_hold_request
+           (usr_post_code,
+            usr_home_ou,
+            usr_profile,
+            usr_birth_year,
+            staff_placed,
+            id,
+            request_time,
+            capture_time,
+            fulfillment_time,
+            checkin_time,
+            return_time,
+            prev_check_time,
+            expire_time,
+            cancel_time,
+            cancel_cause,
+            cancel_note,
+            target,
+            current_copy,
+            fulfillment_staff,
+            fulfillment_lib,
+            request_lib,
+            selection_ou,
+            selection_depth,
+            pickup_lib,
+            hold_type,
+            holdable_formats,
+            phone_notify,
+            email_notify,
+            sms_notify,
+            frozen,
+            thaw_date,
+            shelf_time,
+            cut_in_line,
+            mint_condition,
+            shelf_expire_time,
+            current_shelf_lib)
+      SELECT 
+           usr_post_code,
+           usr_home_ou,
+           usr_profile,
+           usr_birth_year,
+           staff_placed,
+           id,
+           request_time,
+           capture_time,
+           fulfillment_time,
+           checkin_time,
+           return_time,
+           prev_check_time,
+           expire_time,
+           cancel_time,
+           cancel_cause,
+           cancel_note,
+           target,
+           current_copy,
+           fulfillment_staff,
+           fulfillment_lib,
+           request_lib,
+           selection_ou,
+           selection_depth,
+           pickup_lib,
+           hold_type,
+           holdable_formats,
+           phone_notify,
+           email_notify,
+           sms_notify,
+           frozen,
+           thaw_date,
+           shelf_time,
+           cut_in_line,
+           mint_condition,
+           shelf_expire_time,
+           current_shelf_lib
+        FROM action.all_hold_request WHERE id = OLD.id;
+
+    RETURN OLD;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE TRIGGER action_hold_request_aging_tgr
+	BEFORE DELETE ON action.hold_request
+	FOR EACH ROW
+	EXECUTE PROCEDURE action.age_hold_on_delete ();
+
 CREATE TABLE action.fieldset (
     id              SERIAL          PRIMARY KEY,
     owner           INT             NOT NULL REFERENCES actor.usr (id)
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql
deleted file mode 100644
index 693167a..0000000
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql
+++ /dev/null
@@ -1,163 +0,0 @@
-
--- New global flags for the purge function
-INSERT INTO config.global_flag  (name, label, enabled)
-    VALUES (
-        'history.hold.retention_age',
-        oils_i18n_gettext('history.hold.retention_age', 'Historical Hold Retention Age', 'cgf', 'label'),
-        TRUE
-    ),(
-        'history.hold.retention_age_fulfilled',
-        oils_i18n_gettext('history.hold.retention_age_fulfilled', 'Historical Hold Retention Age - Fulfilled', 'cgf', 'label'),
-        FALSE
-    ),(
-        'history.hold.retention_age_canceled',
-        oils_i18n_gettext('history.hold.retention_age_canceled', 'Historical Hold Retention Age - Canceled (Default)', 'cgf', 'label'),
-        FALSE
-    ),(
-        'history.hold.retention_age_canceled_1',
-        oils_i18n_gettext('history.hold.retention_age_canceled_1', 'Historical Hold Retention Age - Canceled (Untarged expiration)', 'cgf', 'label'),
-        FALSE
-    ),(
-        'history.hold.retention_age_canceled_2',
-        oils_i18n_gettext('history.hold.retention_age_canceled_2', 'Historical Hold Retention Age - Canceled (Hold Shelf expiration)', 'cgf', 'label'),
-        FALSE
-    ),(
-        'history.hold.retention_age_canceled_3',
-        oils_i18n_gettext('history.hold.retention_age_canceled_3', 'Historical Hold Retention Age - Canceled (Patron via phone)', 'cgf', 'label'),
-        TRUE
-    ),(
-        'history.hold.retention_age_canceled_4',
-        oils_i18n_gettext('history.hold.retention_age_canceled_4', 'Historical Hold Retention Age - Canceled (Patron in person)', 'cgf', 'label'),
-        TRUE
-    ),(
-        'history.hold.retention_age_canceled_5',
-        oils_i18n_gettext('history.hold.retention_age_canceled_5', 'Historical Hold Retention Age - Canceled (Staff forced)', 'cgf', 'label'),
-        TRUE
-    ),(
-        'history.hold.retention_age_canceled_6',
-        oils_i18n_gettext('history.hold.retention_age_canceled_6', 'Historical Hold Retention Age - Canceled (Patron via OPAC)', 'cgf', 'label'),
-        FALSE
-    );
-
-CREATE OR REPLACE FUNCTION action.purge_holds() RETURNS INT AS $func$
-DECLARE
-  current_hold RECORD;
-  purged_holds INT;
-  cgf_d INTERVAL;
-  cgf_f INTERVAL;
-  cgf_c INTERVAL;
-  prev_usr INT;
-  user_start TIMESTAMPTZ;
-  user_age INTERVAL;
-  user_count INT;
-BEGIN
-  purged_holds := 0;
-  SELECT INTO cgf_d value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age' AND enabled;
-  SELECT INTO cgf_f value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age_fulfilled' AND enabled;
-  SELECT INTO cgf_c value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age_canceled' AND enabled;
-  FOR current_hold IN
-    SELECT
-      rank() OVER (PARTITION BY usr ORDER BY COALESCE(fulfillment_time, cancel_time) DESC),
-      cgf_cs.value::INTERVAL as cgf_cs,
-      ahr.*
-    FROM
-      action.hold_request ahr
-      LEFT JOIN config.global_flag cgf_cs ON (ahr.cancel_cause IS NOT NULL AND cgf_cs.name = 'history.hold.retention_age_canceled_' || ahr.cancel_cause AND cgf_cs.enabled)
-    WHERE
-      (fulfillment_time IS NOT NULL OR cancel_time IS NOT NULL)
-  LOOP
-    IF prev_usr IS NULL OR prev_usr != current_hold.usr THEN
-      prev_usr := current_hold.usr;
-      SELECT INTO user_start oils_json_to_text(value)::TIMESTAMPTZ FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_start';
-      SELECT INTO user_age oils_json_to_text(value)::INTERVAL FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_age';
-      SELECT INTO user_count oils_json_to_text(value)::INT FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_count';
-      IF user_start IS NOT NULL THEN
-        user_age := LEAST(user_age, AGE(NOW(), user_start));
-      END IF;
-      IF user_count IS NULL THEN
-        user_count := 1000; -- Assumption based on the user visible holds routine
-      END IF;
-    END IF;
-    -- Library keep age trumps user keep anything, for purposes of being able to hold on to things when staff canceled and such.
-    IF current_hold.fulfillment_time IS NOT NULL AND current_hold.fulfillment_time > NOW() - COALESCE(cgf_f, cgf_d) THEN
-      CONTINUE;
-    END IF;
-    IF current_hold.cancel_time IS NOT NULL AND current_hold.cancel_time > NOW() - COALESCE(current_hold.cgf_cs, cgf_c, cgf_d) THEN
-      CONTINUE;
-    END IF;
-
-    -- User keep age needs combining with count. If too old AND within the count, keep!
-    IF user_start IS NOT NULL AND COALESCE(current_hold.fulfillment_time, current_hold.cancel_time) > NOW() - user_age AND current_hold.rank <= user_count THEN
-      CONTINUE;
-    END IF;
-
-    -- All checks should have passed, delete!
-    DELETE FROM action.hold_request WHERE id = current_hold.id;
-    purged_holds := purged_holds + 1;
-  END LOOP;
-  RETURN purged_holds;
-END;
-$func$ LANGUAGE plpgsql;
-
-CREATE OR REPLACE FUNCTION action.usr_visible_holds (usr_id INT) RETURNS SETOF action.hold_request AS $func$
-DECLARE
-    h               action.hold_request%ROWTYPE;
-    view_age        INTERVAL;
-    view_count      INT;
-    usr_view_count  actor.usr_setting%ROWTYPE;
-    usr_view_age    actor.usr_setting%ROWTYPE;
-    usr_view_start  actor.usr_setting%ROWTYPE;
-BEGIN
-    SELECT * INTO usr_view_count FROM actor.usr_setting WHERE usr = usr_id AND name = 'history.hold.retention_count';
-    SELECT * INTO usr_view_age FROM actor.usr_setting WHERE usr = usr_id AND name = 'history.hold.retention_age';
-    SELECT * INTO usr_view_start FROM actor.usr_setting WHERE usr = usr_id AND name = 'history.hold.retention_start';
-
-    FOR h IN
-        SELECT  *
-          FROM  action.hold_request
-          WHERE usr = usr_id
-                AND fulfillment_time IS NULL
-                AND cancel_time IS NULL
-          ORDER BY request_time DESC
-    LOOP
-        RETURN NEXT h;
-    END LOOP;
-
-    IF usr_view_start.value IS NULL THEN
-        RETURN;
-    END IF;
-
-    IF usr_view_age.value IS NOT NULL THEN
-        -- User opted in and supplied a retention age
-        IF oils_json_to_text(usr_view_age.value)::INTERVAL > AGE(NOW(), oils_json_to_text(usr_view_start.value)::TIMESTAMPTZ) THEN
-            view_age := AGE(NOW(), oils_json_to_text(usr_view_start.value)::TIMESTAMPTZ);
-        ELSE
-            view_age := oils_json_to_text(usr_view_age.value)::INTERVAL;
-        END IF;
-    ELSE
-        -- User opted in
-        view_age := AGE(NOW(), oils_json_to_text(usr_view_start.value)::TIMESTAMPTZ);
-    END IF;
-
-    IF usr_view_count.value IS NOT NULL THEN
-        view_count := oils_json_to_text(usr_view_count.value)::INT;
-    ELSE
-        view_count := 1000;
-    END IF;
-
-    -- show some fulfilled/canceled holds
-    FOR h IN
-        SELECT  *
-          FROM  action.hold_request
-          WHERE usr = usr_id
-                AND ( fulfillment_time IS NOT NULL OR cancel_time IS NOT NULL )
-                AND COALESCE(fulfillment_time, cancel_time) > NOW() - view_age
-          ORDER BY COALESCE(fulfillment_time, cancel_time) DESC
-          LIMIT view_count
-    LOOP
-        RETURN NEXT h;
-    END LOOP;
-
-    RETURN;
-END;
-$func$ LANGUAGE PLPGSQL;
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.action.purge_holds.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.action.purge_holds.sql
new file mode 100644
index 0000000..5389856
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.action.purge_holds.sql
@@ -0,0 +1,368 @@
+
+-- New global flags for the purge function
+INSERT INTO config.global_flag  (name, label, enabled)
+    VALUES (
+        'history.hold.retention_age',
+        oils_i18n_gettext('history.hold.retention_age', 'Historical Hold Retention Age', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_fulfilled',
+        oils_i18n_gettext('history.hold.retention_age_fulfilled', 'Historical Hold Retention Age - Fulfilled', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled',
+        oils_i18n_gettext('history.hold.retention_age_canceled', 'Historical Hold Retention Age - Canceled (Default)', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled_1',
+        oils_i18n_gettext('history.hold.retention_age_canceled_1', 'Historical Hold Retention Age - Canceled (Untarged expiration)', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled_2',
+        oils_i18n_gettext('history.hold.retention_age_canceled_2', 'Historical Hold Retention Age - Canceled (Hold Shelf expiration)', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled_3',
+        oils_i18n_gettext('history.hold.retention_age_canceled_3', 'Historical Hold Retention Age - Canceled (Patron via phone)', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_canceled_4',
+        oils_i18n_gettext('history.hold.retention_age_canceled_4', 'Historical Hold Retention Age - Canceled (Patron in person)', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_canceled_5',
+        oils_i18n_gettext('history.hold.retention_age_canceled_5', 'Historical Hold Retention Age - Canceled (Staff forced)', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_canceled_6',
+        oils_i18n_gettext('history.hold.retention_age_canceled_6', 'Historical Hold Retention Age - Canceled (Patron via OPAC)', 'cgf', 'label'),
+        FALSE
+    );
+
+CREATE OR REPLACE FUNCTION action.purge_holds() RETURNS INT AS $func$
+DECLARE
+  current_hold RECORD;
+  purged_holds INT;
+  cgf_d INTERVAL;
+  cgf_f INTERVAL;
+  cgf_c INTERVAL;
+  prev_usr INT;
+  user_start TIMESTAMPTZ;
+  user_age INTERVAL;
+  user_count INT;
+BEGIN
+  purged_holds := 0;
+  SELECT INTO cgf_d value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age' AND enabled;
+  SELECT INTO cgf_f value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age_fulfilled' AND enabled;
+  SELECT INTO cgf_c value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age_canceled' AND enabled;
+  FOR current_hold IN
+    SELECT
+      rank() OVER (PARTITION BY usr ORDER BY COALESCE(fulfillment_time, cancel_time) DESC),
+      cgf_cs.value::INTERVAL as cgf_cs,
+      ahr.*
+    FROM
+      action.hold_request ahr
+      LEFT JOIN config.global_flag cgf_cs ON (ahr.cancel_cause IS NOT NULL AND cgf_cs.name = 'history.hold.retention_age_canceled_' || ahr.cancel_cause AND cgf_cs.enabled)
+    WHERE
+      (fulfillment_time IS NOT NULL OR cancel_time IS NOT NULL)
+  LOOP
+    IF prev_usr IS NULL OR prev_usr != current_hold.usr THEN
+      prev_usr := current_hold.usr;
+      SELECT INTO user_start oils_json_to_text(value)::TIMESTAMPTZ FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_start';
+      SELECT INTO user_age oils_json_to_text(value)::INTERVAL FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_age';
+      SELECT INTO user_count oils_json_to_text(value)::INT FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_count';
+      IF user_start IS NOT NULL THEN
+        user_age := LEAST(user_age, AGE(NOW(), user_start));
+      END IF;
+      IF user_count IS NULL THEN
+        user_count := 1000; -- Assumption based on the user visible holds routine
+      END IF;
+    END IF;
+    -- Library keep age trumps user keep anything, for purposes of being able to hold on to things when staff canceled and such.
+    IF current_hold.fulfillment_time IS NOT NULL AND current_hold.fulfillment_time > NOW() - COALESCE(cgf_f, cgf_d) THEN
+      CONTINUE;
+    END IF;
+    IF current_hold.cancel_time IS NOT NULL AND current_hold.cancel_time > NOW() - COALESCE(current_hold.cgf_cs, cgf_c, cgf_d) THEN
+      CONTINUE;
+    END IF;
+
+    -- User keep age needs combining with count. If too old AND within the count, keep!
+    IF user_start IS NOT NULL AND COALESCE(current_hold.fulfillment_time, current_hold.cancel_time) > NOW() - user_age AND current_hold.rank <= user_count THEN
+      CONTINUE;
+    END IF;
+
+    -- All checks should have passed, delete!
+    DELETE FROM action.hold_request WHERE id = current_hold.id;
+    purged_holds := purged_holds + 1;
+  END LOOP;
+  RETURN purged_holds;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION action.usr_visible_holds (usr_id INT) RETURNS SETOF action.hold_request AS $func$
+DECLARE
+    h               action.hold_request%ROWTYPE;
+    view_age        INTERVAL;
+    view_count      INT;
+    usr_view_count  actor.usr_setting%ROWTYPE;
+    usr_view_age    actor.usr_setting%ROWTYPE;
+    usr_view_start  actor.usr_setting%ROWTYPE;
+BEGIN
+    SELECT * INTO usr_view_count FROM actor.usr_setting WHERE usr = usr_id AND name = 'history.hold.retention_count';
+    SELECT * INTO usr_view_age FROM actor.usr_setting WHERE usr = usr_id AND name = 'history.hold.retention_age';
+    SELECT * INTO usr_view_start FROM actor.usr_setting WHERE usr = usr_id AND name = 'history.hold.retention_start';
+
+    FOR h IN
+        SELECT  *
+          FROM  action.hold_request
+          WHERE usr = usr_id
+                AND fulfillment_time IS NULL
+                AND cancel_time IS NULL
+          ORDER BY request_time DESC
+    LOOP
+        RETURN NEXT h;
+    END LOOP;
+
+    IF usr_view_start.value IS NULL THEN
+        RETURN;
+    END IF;
+
+    IF usr_view_age.value IS NOT NULL THEN
+        -- User opted in and supplied a retention age
+        IF oils_json_to_text(usr_view_age.value)::INTERVAL > AGE(NOW(), oils_json_to_text(usr_view_start.value)::TIMESTAMPTZ) THEN
+            view_age := AGE(NOW(), oils_json_to_text(usr_view_start.value)::TIMESTAMPTZ);
+        ELSE
+            view_age := oils_json_to_text(usr_view_age.value)::INTERVAL;
+        END IF;
+    ELSE
+        -- User opted in
+        view_age := AGE(NOW(), oils_json_to_text(usr_view_start.value)::TIMESTAMPTZ);
+    END IF;
+
+    IF usr_view_count.value IS NOT NULL THEN
+        view_count := oils_json_to_text(usr_view_count.value)::INT;
+    ELSE
+        view_count := 1000;
+    END IF;
+
+    -- show some fulfilled/canceled holds
+    FOR h IN
+        SELECT  *
+          FROM  action.hold_request
+          WHERE usr = usr_id
+                AND ( fulfillment_time IS NOT NULL OR cancel_time IS NOT NULL )
+                AND COALESCE(fulfillment_time, cancel_time) > NOW() - view_age
+          ORDER BY COALESCE(fulfillment_time, cancel_time) DESC
+          LIMIT view_count
+    LOOP
+        RETURN NEXT h;
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE TABLE action.aged_hold_request (
+    usr_post_code		TEXT,
+    usr_home_ou		INT	NOT NULL,
+    usr_profile		INT	NOT NULL,
+    usr_birth_year		INT,
+    staff_placed        BOOLEAN NOT NULL,
+    LIKE action.hold_request
+);
+ALTER TABLE action.aged_hold_request
+      ADD PRIMARY KEY (id),
+      DROP COLUMN usr,
+      DROP COLUMN requestor,
+      DROP COLUMN sms_carrier,
+      ALTER COLUMN phone_notify TYPE BOOLEAN
+            USING CASE WHEN phone_notify IS NULL OR phone_notify = '' THEN FALSE ELSE TRUE END,
+      ALTER COLUMN sms_notify TYPE BOOLEAN
+            USING CASE WHEN sms_notify IS NULL OR sms_notify = '' THEN FALSE ELSE TRUE END,
+      ALTER COLUMN phone_notify SET NOT NULL,
+      ALTER COLUMN sms_notify SET NOT NULL;
+CREATE INDEX aged_hold_request_target_idx ON action.aged_hold_request (target);
+CREATE INDEX aged_hold_request_pickup_lib_idx ON action.aged_hold_request (pickup_lib);
+CREATE INDEX aged_hold_request_current_copy_idx ON action.aged_hold_request (current_copy);
+CREATE INDEX aged_hold_request_fulfillment_staff_idx ON action.aged_hold_request ( fulfillment_staff );
+
+CREATE OR REPLACE VIEW action.all_hold_request AS
+    SELECT DISTINCT
+           COALESCE(a.post_code, b.post_code) AS usr_post_code,
+           p.home_ou AS usr_home_ou,
+           p.profile AS usr_profile,
+           EXTRACT(YEAR FROM p.dob)::INT AS usr_birth_year,
+           CAST(ahr.requestor <> ahr.usr AS BOOLEAN) AS staff_placed,
+           ahr.id,
+           ahr.request_time,
+           ahr.capture_time,
+           ahr.fulfillment_time,
+           ahr.checkin_time,
+           ahr.return_time,
+           ahr.prev_check_time,
+           ahr.expire_time,
+           ahr.cancel_time,
+           ahr.cancel_cause,
+           ahr.cancel_note,
+           ahr.target,
+           ahr.current_copy,
+           ahr.fulfillment_staff,
+           ahr.fulfillment_lib,
+           ahr.request_lib,
+           ahr.selection_ou,
+           ahr.selection_depth,
+           ahr.pickup_lib,
+           ahr.hold_type,
+           ahr.holdable_formats,
+           CASE
+           WHEN ahr.phone_notify IS NULL THEN FALSE
+           WHEN ahr.phone_notify = '' THEN FALSE
+           ELSE TRUE
+           END AS phone_notify,
+           ahr.email_notify,
+           CASE
+           WHEN ahr.sms_notify IS NULL THEN FALSE
+           WHEN ahr.sms_notify = '' THEN FALSE
+           ELSE TRUE
+           END AS sms_notify,
+           ahr.frozen,
+           ahr.thaw_date,
+           ahr.shelf_time,
+           ahr.cut_in_line,
+           ahr.mint_condition,
+           ahr.shelf_expire_time,
+           ahr.current_shelf_lib
+    FROM action.hold_request ahr
+         JOIN actor.usr p ON (ahr.usr = p.id)
+         LEFT JOIN actor.usr_address a ON (p.mailing_address = a.id)
+         LEFT JOIN actor.usr_address b ON (p.billing_address = b.id)
+    UNION ALL
+    SELECT 
+           usr_post_code,
+           usr_home_ou,
+           usr_profile,
+           usr_birth_year,
+           staff_placed,
+           id,
+           request_time,
+           capture_time,
+           fulfillment_time,
+           checkin_time,
+           return_time,
+           prev_check_time,
+           expire_time,
+           cancel_time,
+           cancel_cause,
+           cancel_note,
+           target,
+           current_copy,
+           fulfillment_staff,
+           fulfillment_lib,
+           request_lib,
+           selection_ou,
+           selection_depth,
+           pickup_lib,
+           hold_type,
+           holdable_formats,
+           phone_notify,
+           email_notify,
+           sms_notify,
+           frozen,
+           thaw_date,
+           shelf_time,
+           cut_in_line,
+           mint_condition,
+           shelf_expire_time,
+           current_shelf_lib
+    FROM action.aged_hold_request;
+
+CREATE OR REPLACE FUNCTION action.age_hold_on_delete () RETURNS TRIGGER AS $$
+DECLARE
+BEGIN
+    -- Archive a copy of the old row to action.aged_hold_request
+
+    INSERT INTO action.aged_hold_request
+           (usr_post_code,
+            usr_home_ou,
+            usr_profile,
+            usr_birth_year,
+            staff_placed,
+            id,
+            request_time,
+            capture_time,
+            fulfillment_time,
+            checkin_time,
+            return_time,
+            prev_check_time,
+            expire_time,
+            cancel_time,
+            cancel_cause,
+            cancel_note,
+            target,
+            current_copy,
+            fulfillment_staff,
+            fulfillment_lib,
+            request_lib,
+            selection_ou,
+            selection_depth,
+            pickup_lib,
+            hold_type,
+            holdable_formats,
+            phone_notify,
+            email_notify,
+            sms_notify,
+            frozen,
+            thaw_date,
+            shelf_time,
+            cut_in_line,
+            mint_condition,
+            shelf_expire_time,
+            current_shelf_lib)
+      SELECT 
+           usr_post_code,
+           usr_home_ou,
+           usr_profile,
+           usr_birth_year,
+           staff_placed,
+           id,
+           request_time,
+           capture_time,
+           fulfillment_time,
+           checkin_time,
+           return_time,
+           prev_check_time,
+           expire_time,
+           cancel_time,
+           cancel_cause,
+           cancel_note,
+           target,
+           current_copy,
+           fulfillment_staff,
+           fulfillment_lib,
+           request_lib,
+           selection_ou,
+           selection_depth,
+           pickup_lib,
+           hold_type,
+           holdable_formats,
+           phone_notify,
+           email_notify,
+           sms_notify,
+           frozen,
+           thaw_date,
+           shelf_time,
+           cut_in_line,
+           mint_condition,
+           shelf_expire_time,
+           current_shelf_lib
+        FROM action.all_hold_request WHERE id = OLD.id;
+
+    RETURN OLD;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE TRIGGER action_hold_request_aging_tgr
+	BEFORE DELETE ON action.hold_request
+	FOR EACH ROW
+	EXECUTE PROCEDURE action.age_hold_on_delete ();
+

commit dc40c1c748cfbb0c4b14d94b5fe72825cb110a08
Author: Jason Stephenson <jstephenson at mvlc.org>
Date:   Mon Apr 1 16:59:59 2013 -0400

    Add a purge_holds.srfsh script so it can be run from cron.
    
    Signed-off-by: Jason Stephenson <jstephenson at mvlc.org>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/Makefile.am b/Open-ILS/src/Makefile.am
index 0363c4b..d96d1bd 100644
--- a/Open-ILS/src/Makefile.am
+++ b/Open-ILS/src/Makefile.am
@@ -72,6 +72,7 @@ core_scripts =   $(examples)/oils_ctl.sh \
 		 $(supportscr)/thaw_expired_frozen_holds.srfsh \
 		 $(supportscr)/long-overdue-status-update.pl \
 		 $(supportscr)/action_trigger_runner.pl \
+		 $(supportscr)/purge_holds.srfsh \
 		 $(srcdir)/extras/eg_config \
 		 $(srcdir)/extras/openurl_map.pl \
 		 $(srcdir)/extras/import/marc_add_ids
diff --git a/Open-ILS/src/support-scripts/purge_holds.srfsh b/Open-ILS/src/support-scripts/purge_holds.srfsh
new file mode 100755
index 0000000..bce78e2
--- /dev/null
+++ b/Open-ILS/src/support-scripts/purge_holds.srfsh
@@ -0,0 +1,7 @@
+#!/openils/bin/srfsh
+open open-ils.cstore
+request open-ils.cstore open-ils.cstore.transaction.begin
+request open-ils.cstore open-ils.cstore.json_query {"from":["action.purge_holds"]}
+request open-ils.cstore open-ils.cstore.transaction.commit
+close open-ils.cstore
+

commit 765aea78207f879e81eeb9f3d7614699399e585f
Author: Thomas Berezansky <tsbere at mvlc.org>
Date:   Tue Apr 2 10:18:55 2013 -0400

    Fix typo in purge holds function
    
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>
    Signed-off-by: Jason Stephenson <jstephenson at mvlc.org>

diff --git a/Open-ILS/src/sql/Pg/090.schema.action.sql b/Open-ILS/src/sql/Pg/090.schema.action.sql
index 639becd..d07f07f 100644
--- a/Open-ILS/src/sql/Pg/090.schema.action.sql
+++ b/Open-ILS/src/sql/Pg/090.schema.action.sql
@@ -879,7 +879,7 @@ BEGIN
       ahr.*
     FROM
       action.hold_request ahr
-      LEFT JOIN config.global_flag cgf_cs ON (ahr.cancel_cause IS NOT NULL AND cgf_cs.name = 'history.hold.retenetion_age_canceled_' || ahr.cancel_cause AND cgf_cs.enabled)
+      LEFT JOIN config.global_flag cgf_cs ON (ahr.cancel_cause IS NOT NULL AND cgf_cs.name = 'history.hold.retention_age_canceled_' || ahr.cancel_cause AND cgf_cs.enabled)
     WHERE
       (fulfillment_time IS NOT NULL OR cancel_time IS NOT NULL)
   LOOP
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql
index 3a6714c..693167a 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql
@@ -62,7 +62,7 @@ BEGIN
       ahr.*
     FROM
       action.hold_request ahr
-      LEFT JOIN config.global_flag cgf_cs ON (ahr.cancel_cause IS NOT NULL AND cgf_cs.name = 'history.hold.retenetion_age_canceled_' || ahr.cancel_cause AND cgf_cs.enabled)
+      LEFT JOIN config.global_flag cgf_cs ON (ahr.cancel_cause IS NOT NULL AND cgf_cs.name = 'history.hold.retention_age_canceled_' || ahr.cancel_cause AND cgf_cs.enabled)
     WHERE
       (fulfillment_time IS NOT NULL OR cancel_time IS NOT NULL)
   LOOP

commit d43e92f682f6e06a61d508944cf340093168e77a
Author: Thomas Berezansky <tsbere at mvlc.org>
Date:   Wed Feb 13 16:08:01 2013 -0500

    Release notes for Purge Holds routine
    
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>
    Signed-off-by: Jason Stephenson <jstephenson at mvlc.org>

diff --git a/docs/RELEASE_NOTES_NEXT/purge_holds.txt b/docs/RELEASE_NOTES_NEXT/purge_holds.txt
new file mode 100644
index 0000000..f015f6d
--- /dev/null
+++ b/docs/RELEASE_NOTES_NEXT/purge_holds.txt
@@ -0,0 +1,7 @@
+New Feature: "Purge Holds"
+==========================
+Similar to purging circulations one may wish to purge old (filled or canceled) hold information. This feature adds a database function and settings for doing so.
+
+The settings allow for a default retention age as well as filled, canceled, and canceled by cancel cause ages. The most specific one wins unless a patron is retaining their hold history. In the latter case the patron's holds are retained either way.
+
+Note that the function still needs to be called, which could be set up as a cron job or done more manually, say after statistics collection.

commit 86995bbb791f3ba0771e92921f756f6680a22571
Author: Thomas Berezansky <tsbere at mvlc.org>
Date:   Wed Feb 13 15:41:44 2013 -0500

    Fix seed values and upgrade script
    
    enabled and label were out of order.
    
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>
    Signed-off-by: Jason Stephenson <jstephenson at mvlc.org>

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 e5ae671..43bc29a 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -9004,7 +9004,7 @@ INSERT INTO config.global_flag (name,label,enabled)
         TRUE
     );
 
-INSERT INTO config.global_flag  (name, enabled, label)
+INSERT INTO config.global_flag  (name, label, enabled)
     VALUES (
         'history.hold.retention_age',
         oils_i18n_gettext('history.hold.retention_age', 'Historical Hold Retention Age', 'cgf', 'label'),
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql
index 69ae1df..3a6714c 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql
@@ -1,6 +1,6 @@
 
 -- New global flags for the purge function
-INSERT INTO config.global_flag  (name, enabled, label)
+INSERT INTO config.global_flag  (name, label, enabled)
     VALUES (
         'history.hold.retention_age',
         oils_i18n_gettext('history.hold.retention_age', 'Historical Hold Retention Age', 'cgf', 'label'),

commit 0771f7c6ea249a336ec7e68bd6e1949311ce3856
Author: Thomas Berezansky <tsbere at mvlc.org>
Date:   Thu Jun 21 09:55:05 2012 -0400

    Make user visible holds based on fill/cancel time
    
    Instead of request time. That way holds don't vanish after filling when
    keeping history was turned on after hold placement.
    
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>
    Signed-off-by: Jason Stephenson <jstephenson at mvlc.org>

diff --git a/Open-ILS/src/sql/Pg/090.schema.action.sql b/Open-ILS/src/sql/Pg/090.schema.action.sql
index f309498..639becd 100644
--- a/Open-ILS/src/sql/Pg/090.schema.action.sql
+++ b/Open-ILS/src/sql/Pg/090.schema.action.sql
@@ -757,8 +757,8 @@ BEGIN
           FROM  action.hold_request
           WHERE usr = usr_id
                 AND ( fulfillment_time IS NOT NULL OR cancel_time IS NOT NULL )
-                AND request_time > NOW() - view_age
-          ORDER BY request_time DESC
+                AND COALESCE(fulfillment_time, cancel_time) > NOW() - view_age
+          ORDER BY COALESCE(fulfillment_time, cancel_time) DESC
           LIMIT view_count
     LOOP
         RETURN NEXT h;
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql
index 7db1607..69ae1df 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql
@@ -98,3 +98,66 @@ BEGIN
   RETURN purged_holds;
 END;
 $func$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION action.usr_visible_holds (usr_id INT) RETURNS SETOF action.hold_request AS $func$
+DECLARE
+    h               action.hold_request%ROWTYPE;
+    view_age        INTERVAL;
+    view_count      INT;
+    usr_view_count  actor.usr_setting%ROWTYPE;
+    usr_view_age    actor.usr_setting%ROWTYPE;
+    usr_view_start  actor.usr_setting%ROWTYPE;
+BEGIN
+    SELECT * INTO usr_view_count FROM actor.usr_setting WHERE usr = usr_id AND name = 'history.hold.retention_count';
+    SELECT * INTO usr_view_age FROM actor.usr_setting WHERE usr = usr_id AND name = 'history.hold.retention_age';
+    SELECT * INTO usr_view_start FROM actor.usr_setting WHERE usr = usr_id AND name = 'history.hold.retention_start';
+
+    FOR h IN
+        SELECT  *
+          FROM  action.hold_request
+          WHERE usr = usr_id
+                AND fulfillment_time IS NULL
+                AND cancel_time IS NULL
+          ORDER BY request_time DESC
+    LOOP
+        RETURN NEXT h;
+    END LOOP;
+
+    IF usr_view_start.value IS NULL THEN
+        RETURN;
+    END IF;
+
+    IF usr_view_age.value IS NOT NULL THEN
+        -- User opted in and supplied a retention age
+        IF oils_json_to_text(usr_view_age.value)::INTERVAL > AGE(NOW(), oils_json_to_text(usr_view_start.value)::TIMESTAMPTZ) THEN
+            view_age := AGE(NOW(), oils_json_to_text(usr_view_start.value)::TIMESTAMPTZ);
+        ELSE
+            view_age := oils_json_to_text(usr_view_age.value)::INTERVAL;
+        END IF;
+    ELSE
+        -- User opted in
+        view_age := AGE(NOW(), oils_json_to_text(usr_view_start.value)::TIMESTAMPTZ);
+    END IF;
+
+    IF usr_view_count.value IS NOT NULL THEN
+        view_count := oils_json_to_text(usr_view_count.value)::INT;
+    ELSE
+        view_count := 1000;
+    END IF;
+
+    -- show some fulfilled/canceled holds
+    FOR h IN
+        SELECT  *
+          FROM  action.hold_request
+          WHERE usr = usr_id
+                AND ( fulfillment_time IS NOT NULL OR cancel_time IS NOT NULL )
+                AND COALESCE(fulfillment_time, cancel_time) > NOW() - view_age
+          ORDER BY COALESCE(fulfillment_time, cancel_time) DESC
+          LIMIT view_count
+    LOOP
+        RETURN NEXT h;
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;

commit 1523a8b6c8512cac58d8ecbe5a5456265eb6fcf3
Author: Thomas Berezansky <tsbere at mvlc.org>
Date:   Thu Jun 21 09:44:11 2012 -0400

    Add purge_holds DB function
    
    This allows removing of holds based on patron and library preferences.
    
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>
    Signed-off-by: Jason Stephenson <jstephenson at mvlc.org>

diff --git a/Open-ILS/src/sql/Pg/090.schema.action.sql b/Open-ILS/src/sql/Pg/090.schema.action.sql
index 9dec15f..f309498 100644
--- a/Open-ILS/src/sql/Pg/090.schema.action.sql
+++ b/Open-ILS/src/sql/Pg/090.schema.action.sql
@@ -856,6 +856,65 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL;
 
+CREATE OR REPLACE FUNCTION action.purge_holds() RETURNS INT AS $func$
+DECLARE
+  current_hold RECORD;
+  purged_holds INT;
+  cgf_d INTERVAL;
+  cgf_f INTERVAL;
+  cgf_c INTERVAL;
+  prev_usr INT;
+  user_start TIMESTAMPTZ;
+  user_age INTERVAL;
+  user_count INT;
+BEGIN
+  purged_holds := 0;
+  SELECT INTO cgf_d value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age' AND enabled;
+  SELECT INTO cgf_f value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age_fulfilled' AND enabled;
+  SELECT INTO cgf_c value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age_canceled' AND enabled;
+  FOR current_hold IN
+    SELECT
+      rank() OVER (PARTITION BY usr ORDER BY COALESCE(fulfillment_time, cancel_time) DESC),
+      cgf_cs.value::INTERVAL as cgf_cs,
+      ahr.*
+    FROM
+      action.hold_request ahr
+      LEFT JOIN config.global_flag cgf_cs ON (ahr.cancel_cause IS NOT NULL AND cgf_cs.name = 'history.hold.retenetion_age_canceled_' || ahr.cancel_cause AND cgf_cs.enabled)
+    WHERE
+      (fulfillment_time IS NOT NULL OR cancel_time IS NOT NULL)
+  LOOP
+    IF prev_usr IS NULL OR prev_usr != current_hold.usr THEN
+      prev_usr := current_hold.usr;
+      SELECT INTO user_start oils_json_to_text(value)::TIMESTAMPTZ FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_start';
+      SELECT INTO user_age oils_json_to_text(value)::INTERVAL FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_age';
+      SELECT INTO user_count oils_json_to_text(value)::INT FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_count';
+      IF user_start IS NOT NULL THEN
+        user_age := LEAST(user_age, AGE(NOW(), user_start));
+      END IF;
+      IF user_count IS NULL THEN
+        user_count := 1000; -- Assumption based on the user visible holds routine
+      END IF;
+    END IF;
+    -- Library keep age trumps user keep anything, for purposes of being able to hold on to things when staff canceled and such.
+    IF current_hold.fulfillment_time IS NOT NULL AND current_hold.fulfillment_time > NOW() - COALESCE(cgf_f, cgf_d) THEN
+      CONTINUE;
+    END IF;
+    IF current_hold.cancel_time IS NOT NULL AND current_hold.cancel_time > NOW() - COALESCE(current_hold.cgf_cs, cgf_c, cgf_d) THEN
+      CONTINUE;
+    END IF;
+
+    -- User keep age needs combining with count. If too old AND within the count, keep!
+    IF user_start IS NOT NULL AND COALESCE(current_hold.fulfillment_time, current_hold.cancel_time) > NOW() - user_age AND current_hold.rank <= user_count THEN
+      CONTINUE;
+    END IF;
+
+    -- All checks should have passed, delete!
+    DELETE FROM action.hold_request WHERE id = current_hold.id;
+    purged_holds := purged_holds + 1;
+  END LOOP;
+  RETURN purged_holds;
+END;
+$func$ LANGUAGE plpgsql;
 
 CREATE OR REPLACE FUNCTION action.apply_fieldset(
 	fieldset_id IN INT,        -- id from action.fieldset
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 125a4b2..e5ae671 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -9004,6 +9004,45 @@ INSERT INTO config.global_flag (name,label,enabled)
         TRUE
     );
 
+INSERT INTO config.global_flag  (name, enabled, label)
+    VALUES (
+        'history.hold.retention_age',
+        oils_i18n_gettext('history.hold.retention_age', 'Historical Hold Retention Age', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_fulfilled',
+        oils_i18n_gettext('history.hold.retention_age_fulfilled', 'Historical Hold Retention Age - Fulfilled', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled',
+        oils_i18n_gettext('history.hold.retention_age_canceled', 'Historical Hold Retention Age - Canceled (Default)', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled_1',
+        oils_i18n_gettext('history.hold.retention_age_canceled_1', 'Historical Hold Retention Age - Canceled (Untarged expiration)', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled_2',
+        oils_i18n_gettext('history.hold.retention_age_canceled_2', 'Historical Hold Retention Age - Canceled (Hold Shelf expiration)', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled_3',
+        oils_i18n_gettext('history.hold.retention_age_canceled_3', 'Historical Hold Retention Age - Canceled (Patron via phone)', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_canceled_4',
+        oils_i18n_gettext('history.hold.retention_age_canceled_4', 'Historical Hold Retention Age - Canceled (Patron in person)', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_canceled_5',
+        oils_i18n_gettext('history.hold.retention_age_canceled_5', 'Historical Hold Retention Age - Canceled (Staff forced)', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_canceled_6',
+        oils_i18n_gettext('history.hold.retention_age_canceled_6', 'Historical Hold Retention Age - Canceled (Patron via OPAC)', 'cgf', 'label'),
+        FALSE
+    );
+
 INSERT INTO config.global_flag (name, label, enabled)
     VALUES (
         'cat.maintain_control_numbers',
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql
new file mode 100644
index 0000000..7db1607
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.purge_holds.sql
@@ -0,0 +1,100 @@
+
+-- New global flags for the purge function
+INSERT INTO config.global_flag  (name, enabled, label)
+    VALUES (
+        'history.hold.retention_age',
+        oils_i18n_gettext('history.hold.retention_age', 'Historical Hold Retention Age', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_fulfilled',
+        oils_i18n_gettext('history.hold.retention_age_fulfilled', 'Historical Hold Retention Age - Fulfilled', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled',
+        oils_i18n_gettext('history.hold.retention_age_canceled', 'Historical Hold Retention Age - Canceled (Default)', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled_1',
+        oils_i18n_gettext('history.hold.retention_age_canceled_1', 'Historical Hold Retention Age - Canceled (Untarged expiration)', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled_2',
+        oils_i18n_gettext('history.hold.retention_age_canceled_2', 'Historical Hold Retention Age - Canceled (Hold Shelf expiration)', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled_3',
+        oils_i18n_gettext('history.hold.retention_age_canceled_3', 'Historical Hold Retention Age - Canceled (Patron via phone)', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_canceled_4',
+        oils_i18n_gettext('history.hold.retention_age_canceled_4', 'Historical Hold Retention Age - Canceled (Patron in person)', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_canceled_5',
+        oils_i18n_gettext('history.hold.retention_age_canceled_5', 'Historical Hold Retention Age - Canceled (Staff forced)', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_canceled_6',
+        oils_i18n_gettext('history.hold.retention_age_canceled_6', 'Historical Hold Retention Age - Canceled (Patron via OPAC)', 'cgf', 'label'),
+        FALSE
+    );
+
+CREATE OR REPLACE FUNCTION action.purge_holds() RETURNS INT AS $func$
+DECLARE
+  current_hold RECORD;
+  purged_holds INT;
+  cgf_d INTERVAL;
+  cgf_f INTERVAL;
+  cgf_c INTERVAL;
+  prev_usr INT;
+  user_start TIMESTAMPTZ;
+  user_age INTERVAL;
+  user_count INT;
+BEGIN
+  purged_holds := 0;
+  SELECT INTO cgf_d value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age' AND enabled;
+  SELECT INTO cgf_f value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age_fulfilled' AND enabled;
+  SELECT INTO cgf_c value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age_canceled' AND enabled;
+  FOR current_hold IN
+    SELECT
+      rank() OVER (PARTITION BY usr ORDER BY COALESCE(fulfillment_time, cancel_time) DESC),
+      cgf_cs.value::INTERVAL as cgf_cs,
+      ahr.*
+    FROM
+      action.hold_request ahr
+      LEFT JOIN config.global_flag cgf_cs ON (ahr.cancel_cause IS NOT NULL AND cgf_cs.name = 'history.hold.retenetion_age_canceled_' || ahr.cancel_cause AND cgf_cs.enabled)
+    WHERE
+      (fulfillment_time IS NOT NULL OR cancel_time IS NOT NULL)
+  LOOP
+    IF prev_usr IS NULL OR prev_usr != current_hold.usr THEN
+      prev_usr := current_hold.usr;
+      SELECT INTO user_start oils_json_to_text(value)::TIMESTAMPTZ FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_start';
+      SELECT INTO user_age oils_json_to_text(value)::INTERVAL FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_age';
+      SELECT INTO user_count oils_json_to_text(value)::INT FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_count';
+      IF user_start IS NOT NULL THEN
+        user_age := LEAST(user_age, AGE(NOW(), user_start));
+      END IF;
+      IF user_count IS NULL THEN
+        user_count := 1000; -- Assumption based on the user visible holds routine
+      END IF;
+    END IF;
+    -- Library keep age trumps user keep anything, for purposes of being able to hold on to things when staff canceled and such.
+    IF current_hold.fulfillment_time IS NOT NULL AND current_hold.fulfillment_time > NOW() - COALESCE(cgf_f, cgf_d) THEN
+      CONTINUE;
+    END IF;
+    IF current_hold.cancel_time IS NOT NULL AND current_hold.cancel_time > NOW() - COALESCE(current_hold.cgf_cs, cgf_c, cgf_d) THEN
+      CONTINUE;
+    END IF;
+
+    -- User keep age needs combining with count. If too old AND within the count, keep!
+    IF user_start IS NOT NULL AND COALESCE(current_hold.fulfillment_time, current_hold.cancel_time) > NOW() - user_age AND current_hold.rank <= user_count THEN
+      CONTINUE;
+    END IF;
+
+    -- All checks should have passed, delete!
+    DELETE FROM action.hold_request WHERE id = current_hold.id;
+    purged_holds := purged_holds + 1;
+  END LOOP;
+  RETURN purged_holds;
+END;
+$func$ LANGUAGE plpgsql;

-----------------------------------------------------------------------

Summary of changes:
 Open-ILS/examples/fm_IDL.xml                       |  117 ++++++
 Open-ILS/src/Makefile.am                           |    1 +
 Open-ILS/src/sql/Pg/002.schema.config.sql          |    2 +-
 Open-ILS/src/sql/Pg/090.schema.action.sql          |  267 ++++++++++++++-
 Open-ILS/src/sql/Pg/950.data.seed-values.sql       |   39 ++
 .../Pg/upgrade/0797.schema.action.purge_holds.sql  |  372 ++++++++++++++++++++
 ...date_hard_due_dates.srfsh => purge_holds.srfsh} |    2 +-
 docs/RELEASE_NOTES_NEXT/purge_holds.txt            |    9 +
 8 files changed, 805 insertions(+), 4 deletions(-)
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/0797.schema.action.purge_holds.sql
 copy Open-ILS/src/support-scripts/{update_hard_due_dates.srfsh => purge_holds.srfsh} (66%)
 create mode 100644 docs/RELEASE_NOTES_NEXT/purge_holds.txt


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list