[open-ils-commits] r11297 - in trunk/Open-ILS: examples src/sql/Pg
svn at svn.open-ils.org
svn at svn.open-ils.org
Fri Nov 21 00:58:17 EST 2008
Author: miker
Date: 2008-11-21 00:58:14 -0500 (Fri, 21 Nov 2008)
New Revision: 11297
Modified:
trunk/Open-ILS/examples/fm_IDL.xml
trunk/Open-ILS/src/sql/Pg/002.schema.config.sql
trunk/Open-ILS/src/sql/Pg/100.circ_matrix.sql
trunk/Open-ILS/src/sql/Pg/110.hold_matrix.sql
trunk/Open-ILS/src/sql/Pg/950.data.seed-values.sql
Log:
in-db circ/hold schema clean up; new stored proc to return generated standing penalties; ranged standing penalties
Modified: trunk/Open-ILS/examples/fm_IDL.xml
===================================================================
--- trunk/Open-ILS/examples/fm_IDL.xml 2008-11-20 22:28:13 UTC (rev 11296)
+++ trunk/Open-ILS/examples/fm_IDL.xml 2008-11-21 05:58:14 UTC (rev 11297)
@@ -646,6 +646,12 @@
<field reporter:label="MARC Form" name="marc_form" oils_obj:array_position="14" oils_persist:virtual="false" oils_persist:primitive="string" reporter:datatype="link"/>
<field reporter:label="Videorecording Format" name="marc_vr_format" oils_obj:array_position="15" oils_persist:virtual="false" oils_persist:primitive="string" reporter:datatype="link"/>
<field reporter:label="Reference?" name="ref_flag" oils_obj:array_position="16" oils_persist:virtual="false" reporter:datatype="bool"/>
+ <field reporter:label="Holdable?" name="holdable" oils_obj:array_position="17" oils_persist:virtual="false" reporter:datatype="bool"/>
+ <field reporter:label="Range is from Owning Lib?" name="distance_is_from_owner" oils_obj:array_position="18" oils_persist:virtual="false" reporter:datatype="bool"/>
+ <field reporter:label="Transit Range" name="transit_range" oils_obj:array_position="19" oils_persist:virtual="false" reporter:datatype="int"/>
+ <field reporter:label="Max Holds" name="max_holds" oils_obj:array_position="20" oils_persist:virtual="false" reporter:datatype="int"/>
+ <field reporter:label="Max includes Frozen" name="include_frozen_holds" oils_obj:array_position="21" oils_persist:virtual="false" reporter:datatype="bool"/>
+ <field reporter:label="Copy Age Hold Protection Rule" name="age_hold_protect_rule" oils_obj:array_position="22" oils_persist:virtual="false" reporter:datatype="link"/>
</fields>
<links>
<link field="user_home_ou" reltype="has_a" key="id" map="" class="aou"/>
@@ -660,6 +666,7 @@
<link field="marc_type" reltype="has_a" key="code" map="" class="citm"/>
<link field="marc_form" reltype="has_a" key="code" map="" class="cifm"/>
<link field="marc_vr_format" reltype="has_a" key="code" map="" class="cvrfm"/>
+ <link field="age_hold_protect_rule" reltype="has_a" key="id" map="" class="crahp"/>
</links>
</class>
@@ -679,6 +686,11 @@
<field reporter:label="Reference?" name="ref_flag" oils_obj:array_position="11" oils_persist:virtual="false" reporter:datatype="bool"/>
<field reporter:label="User Age: Lower Bound" name="usr_age_lower_bound" oils_obj:array_position="12" oils_persist:virtual="false" reporter:datatype="text"/>
<field reporter:label="User Age: Upper Bound" name="usr_age_upper_bound" oils_obj:array_position="13" oils_persist:virtual="false" reporter:datatype="text"/>
+ <field reporter:label="Circulate?" name="circulate" oils_obj:array_position="14" oils_persist:virtual="false" reporter:datatype="bool"/>
+ <field reporter:label="Duration Rule" name="duration_rule" oils_obj:array_position="15" oils_persist:virtual="false" reporter:datatype="link"/>
+ <field reporter:label="Recurring Fine Rule" name="recurring_fine_rule" oils_obj:array_position="16" oils_persist:virtual="false" reporter:datatype="link"/>
+ <field reporter:label="Max Fine Rule" name="max_fine_rule" oils_obj:array_position="17" oils_persist:virtual="false" reporter:datatype="link"/>
+ <field reporter:label="Script Test" name="script_test" oils_obj:array_position="18" oils_persist:virtual="false" reporter:datatype="text"/>
</fields>
<links>
<link field="org_unit" reltype="has_a" key="id" map="" class="aou"/>
@@ -688,46 +700,12 @@
<link field="marc_type" reltype="has_a" key="code" map="" class="citm"/>
<link field="marc_form" reltype="has_a" key="code" map="" class="cifm"/>
<link field="marc_vr_format" reltype="has_a" key="code" map="" class="cvrfm"/>
+ <link field="duration_rule" reltype="has_a" key="id" map="" class="crcd"/>
+ <link field="max_fine_rule" reltype="has_a" key="id" map="" class="crmf"/>
+ <link field="recurring_fine_rule" reltype="has_a" key="id" map="" class="crrf"/>
</links>
</class>
- <class id="chmt" controller="open-ils.cstore" oils_obj:fieldmapper="config::hold_matrix_test" oils_persist:tablename="config.hold_matrix_test" reporter:label="Hold Matrix Test Set">
- <fields oils_persist:primary="matchpoint">
- <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
- <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
- <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
- <field reporter:label="Matchpoint ID" name="matchpoint" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="link"/>
- <field reporter:label="Holdable?" name="holdable" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="bool"/>
- <field reporter:label="Range is from Owning Lib?" name="distance_is_from_owner" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="bool"/>
- <field reporter:label="Transit Range" name="transit_range" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="int"/>
- <field reporter:label="Max Holds" name="max_holds" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="int"/>
- <field reporter:label="Max includes Frozen" name="include_frozen_holds" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="bool"/>
- <field reporter:label="Copy Age Hold Protection Rule" name="age_hold_protect_rule" oils_obj:array_position="9" oils_persist:virtual="false" reporter:datatype="link"/>
- </fields>
- <links>
- <link field="matchpoint" reltype="has_a" key="id" map="" class="ccmm"/>
- <link field="age_hold_protect_rule" reltype="has_a" key="id" map="" class="crahp"/>
- </links>
- </class>
-
- <class id="ccmt" controller="open-ils.cstore" oils_obj:fieldmapper="config::circ_matrix_test" oils_persist:tablename="config.circ_matrix_test" reporter:label="Circulation Matrix Test Set">
- <fields oils_persist:primary="matchpoint">
- <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
- <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
- <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
- <field reporter:label="Matchpoint ID" name="matchpoint" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id"/>
- <field reporter:label="Circulate?" name="circulate" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="bool"/>
- <field reporter:label="Max Items Out" name="max_items_out" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="int"/>
- <field reporter:label="Max Overdue" name="max_overdue" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="int"/>
- <field reporter:label="Max Fines" name="max_fines" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="money"/>
- <field reporter:label="Test Script" name="script_test" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="text"/>
- <field reporter:label="Test Context Location" name="org_unit" oils_obj:array_position="9" oils_persist:virtual="false" reporter:datatype="int"/>
- </fields>
- <links>
- <link field="matchpoint" reltype="has_a" key="id" map="" class="ccmm"/>
- </links>
- </class>
-
<class id="ccmcmt" controller="open-ils.cstore" oils_obj:fieldmapper="config::circ_matrix_circ_mod_test" oils_persist:tablename="config.circ_matrix_circ_mod_test" reporter:label="Circulation Matrix Circulation Modifier Subtest">
<fields oils_persist:primary="id" oils_persist:sequence="config.circ_matrix_circ_mod_test_id_seq">
<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
@@ -744,24 +722,6 @@
</links>
</class>
- <class id="ccmrs" controller="open-ils.cstore" oils_obj:fieldmapper="config::circ_matrix_ruleset" oils_persist:tablename="config.circ_matrix_ruleset" reporter:label="Circulation Matrix Rule Set">
- <fields oils_persist:primary="matchpoint">
- <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
- <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
- <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
- <field reporter:label="Matchpoint ID" name="matchpoint" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id"/>
- <field reporter:label="Duration Rule" name="duration_rule" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link"/>
- <field reporter:label="Recurring Fine Rule" name="recurring_fine_rule" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="link"/>
- <field reporter:label="Max Fine Rule" name="max_fine_rule" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="link"/>
- </fields>
- <links>
- <link field="matchpoint" reltype="has_a" key="id" map="" class="ccmm"/>
- <link field="duration_rule" reltype="has_a" key="id" map="" class="crcd"/>
- <link field="max_fine_rule" reltype="has_a" key="id" map="" class="crmf"/>
- <link field="recurring_fine_rule" reltype="has_a" key="id" map="" class="crrf"/>
- </links>
- </class>
-
<class id="cit" controller="open-ils.cstore" oils_obj:fieldmapper="config::identification_type" oils_persist:tablename="config.identification_type" reporter:label="Identification Type">
<fields oils_persist:primary="id" oils_persist:sequence="config.identification_type_id_seq">
<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
Modified: trunk/Open-ILS/src/sql/Pg/002.schema.config.sql
===================================================================
--- trunk/Open-ILS/src/sql/Pg/002.schema.config.sql 2008-11-20 22:28:13 UTC (rev 11296)
+++ trunk/Open-ILS/src/sql/Pg/002.schema.config.sql 2008-11-21 05:58:14 UTC (rev 11297)
@@ -121,6 +121,8 @@
VALUES (1,'PATRON_EXCEEDS_FINES','Patron exceeds fine threshold','CIRC|HOLD|RENEW');
INSERT INTO config.standing_penalty (id,name,label,block_list)
VALUES (2,'PATRON_EXCEEDS_OVERDUE_COUNT','Patron exceeds max overdue item threshold','CIRC|HOLD|RENEW');
+INSERT INTO config.standing_penalty (id,name,label,block_list)
+ VALUES (3,'PATRON_EXCEEDS_CHECKOUT_COUNT','Patron exceeds max checked out item threshold','CIRC');
SELECT SETVAL('config.standing_penalty_id_seq', 100);
CREATE TABLE config.xml_transform (
Modified: trunk/Open-ILS/src/sql/Pg/100.circ_matrix.sql
===================================================================
--- trunk/Open-ILS/src/sql/Pg/100.circ_matrix.sql 2008-11-20 22:28:13 UTC (rev 11296)
+++ trunk/Open-ILS/src/sql/Pg/100.circ_matrix.sql 2008-11-21 05:58:14 UTC (rev 11297)
@@ -96,31 +96,27 @@
--
CREATE TABLE config.circ_matrix_matchpoint (
- id SERIAL PRIMARY KEY,
- active BOOL NOT NULL DEFAULT TRUE,
- org_unit INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
- grp INT NOT NULL REFERENCES permission.grp_tree (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top applicable group from the group tree; will need descendents and prox functions for filtering
- circ_modifier TEXT REFERENCES config.circ_modifier (code) DEFERRABLE INITIALLY DEFERRED,
- marc_type TEXT REFERENCES config.item_type_map (code) DEFERRABLE INITIALLY DEFERRED,
- marc_form TEXT REFERENCES config.item_form_map (code) DEFERRABLE INITIALLY DEFERRED,
- marc_vr_format TEXT REFERENCES config.videorecording_format_map (code) DEFERRABLE INITIALLY DEFERRED,
- ref_flag BOOL,
- is_renewal BOOL,
+ id SERIAL PRIMARY KEY,
+ active BOOL NOT NULL DEFAULT TRUE,
+ org_unit INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
+ grp INT NOT NULL REFERENCES permission.grp_tree (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top applicable group from the group tree; will need descendents and prox functions for filtering
+ circ_modifier TEXT REFERENCES config.circ_modifier (code) DEFERRABLE INITIALLY DEFERRED,
+ marc_type TEXT REFERENCES config.item_type_map (code) DEFERRABLE INITIALLY DEFERRED,
+ marc_form TEXT REFERENCES config.item_form_map (code) DEFERRABLE INITIALLY DEFERRED,
+ marc_vr_format TEXT REFERENCES config.videorecording_format_map (code) DEFERRABLE INITIALLY DEFERRED,
+ ref_flag BOOL,
+ is_renewal BOOL,
usr_age_lower_bound INTERVAL,
usr_age_upper_bound INTERVAL,
+ circulate BOOL NOT NULL DEFAULT TRUE, -- Hard "can't circ" flag requiring an override
+ duration_rule INT NOT NULL REFERENCES config.rule_circ_duration (id) DEFERRABLE INITIALLY DEFERRED,
+ recurring_fine_rule INT NOT NULL REFERENCES config.rule_recuring_fine (id) DEFERRABLE INITIALLY DEFERRED,
+ max_fine_rule INT NOT NULL REFERENCES config.rule_max_fine (id) DEFERRABLE INITIALLY DEFERRED,
+ script_test TEXT, -- javascript source
CONSTRAINT ep_once_per_grp_loc_mod_marc UNIQUE (grp, org_unit, circ_modifier, marc_type, marc_form, marc_vr_format, ref_flag, usr_age_lower_bound, usr_age_upper_bound, is_renewal)
);
--- Tests to determine if circ can occur for this item at this location for this patron
-CREATE TABLE config.circ_matrix_test (
- matchpoint INT PRIMARY KEY NOT NULL REFERENCES config.circ_matrix_matchpoint (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
- circulate BOOL NOT NULL DEFAULT TRUE, -- Hard "can't circ" flag requiring an override
- max_items_out INT, -- Total current active circulations must be less than this, NULL means skip (always pass)
- org_depth INT, -- Set to the top OU for the max-out applicability range
- script_test TEXT -- filename or javascript source ??
-);
-
-- Tests for max items out by circ_modifier
CREATE TABLE config.circ_matrix_circ_mod_test (
id SERIAL PRIMARY KEY,
@@ -130,15 +126,7 @@
);
--- How to circ, assuming tests pass
-CREATE TABLE config.circ_matrix_ruleset (
- matchpoint INT PRIMARY KEY REFERENCES config.circ_matrix_matchpoint (id) DEFERRABLE INITIALLY DEFERRED,
- duration_rule INT NOT NULL REFERENCES config.rule_circ_duration (id) DEFERRABLE INITIALLY DEFERRED,
- recurring_fine_rule INT NOT NULL REFERENCES config.rule_recuring_fine (id) DEFERRABLE INITIALLY DEFERRED,
- max_fine_rule INT NOT NULL REFERENCES config.rule_max_fine (id) DEFERRABLE INITIALLY DEFERRED
-);
-
-CREATE OR REPLACE FUNCTION action.find_circ_matrix_matchpoint( context_ou INT, match_item BIGINT, match_user INT, renewal BOOL ) RETURNS INT AS $func$
+CREATE OR REPLACE FUNCTION action.find_circ_matrix_matchpoint( context_ou INT, match_item BIGINT, match_user INT, renewal BOOL ) RETURNS config.circ_matrix_matchpoint AS $func$
DECLARE
current_group permission.grp_tree%ROWTYPE;
user_object actor.usr%ROWTYPE;
@@ -214,7 +202,7 @@
SELECT INTO current_group * FROM permission.grp_tree WHERE id = current_group.parent;
END LOOP;
- RETURN matchpoint.id;
+ RETURN matchpoint;
END;
$func$ LANGUAGE plpgsql;
@@ -222,25 +210,18 @@
CREATE TYPE action.matrix_test_result AS ( success BOOL, matchpoint INT, fail_part TEXT );
CREATE OR REPLACE FUNCTION action.item_user_circ_test( circ_ou INT, match_item BIGINT, match_user INT, renewal BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
DECLARE
- matchpoint_id INT;
user_object actor.usr%ROWTYPE;
+ standing_penalty config.standing_penalty%ROWTYPE;
item_object asset.copy%ROWTYPE;
item_status_object config.copy_status%ROWTYPE;
item_location_object asset.copy_location%ROWTYPE;
result action.matrix_test_result;
- circ_test config.circ_matrix_test%ROWTYPE;
+ circ_test config.circ_matrix_matchpoint%ROWTYPE;
out_by_circ_mod config.circ_matrix_circ_mod_test%ROWTYPE;
- patron_penalties INT;
+ penalty_type TEXT;
tmp_grp INT;
items_out INT;
- max_overdue INT;
- items_overdue INT;
- overdue_orgs INT[];
- max_fines NUMERIC(8,2) := 0.0;
- current_fines NUMERIC(8,2) := 0.0;
- tmp_fines NUMERIC(8,2);
- tmp_groc RECORD;
- tmp_circ RECORD;
+ context_org_list INT[];
done BOOL := FALSE;
BEGIN
result.success := TRUE;
@@ -295,15 +276,11 @@
RETURN NEXT result;
END IF;
- SELECT INTO matchpoint_id action.find_circ_matrix_matchpoint(circ_ou, match_item, match_user, renewal);
- result.matchpoint := matchpoint_id;
+ SELECT INTO circ_test * FROM action.find_circ_matrix_matchpoint(circ_ou, match_item, match_user, renewal);
+ result.matchpoint := circ_test.id;
- SELECT INTO circ_test * from config.circ_matrix_test WHERE matchpoint = result.matchpoint;
+ SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( circ_test.org_unit );
- IF circ_test.org_depth IS NOT NULL THEN
- SELECT INTO overdue_orgs ARRAY_ACCUM(id) FROM actor.org_unit_full_path( circ_ou, circ_test.org_depth );
- END IF;
-
-- Fail if we couldn't find a set of tests
IF result.matchpoint IS NULL THEN
result.fail_part := 'no_matchpoint';
@@ -320,59 +297,33 @@
RETURN NEXT result;
END IF;
- SELECT INTO patron_penalties COUNT(*)
- FROM actor.usr_standing_penalty usp
- JOIN config.standing_penalty csp ON (csp.id = usp.penalty)
- WHERE usr = match_user
- AND usp.org_unit IN ( SELECT * FROM explode_array(overdue_orgs) )
- AND csp.block_list LIKE '%RENEW%';
-
- IF patron_penalties > 0 THEN
- result.fail_part := 'config.circ_matrix_test.stop_blocked_user.circ';
- result.success := FALSE;
- done := TRUE;
- RETURN NEXT result;
+ IF renewal THEN
+ penalty_type = '%RENEW%';
+ ELSE
+ penalty_type = '%CIRC%';
END IF;
- patron_penalties := 0;
+ FOR standing_penalty IN
+ SELECT DISTINCT csp.*
+ FROM actor.usr_standing_penalty usp
+ JOIN config.standing_penalty csp ON (csp.id = usp.penalty)
+ WHERE usr = match_user
+ AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
+ AND csp.block_list LIKE penalty_type LOOP
- SELECT INTO patron_penalties COUNT(*)
- FROM actor.usr_standing_penalty usp
- JOIN config.standing_penalty csp ON (csp.id = usp.penalty)
- WHERE usr = match_user
- AND usp.org_unit IN ( SELECT * FROM explode_array(overdue_orgs) )
- AND csp.block_list LIKE '%CIRC%';
-
- IF patron_penalties > 0 THEN
- result.fail_part := 'config.circ_matrix_test.stop_blocked_user.renew';
+ result.fail_part := standing_penalty.name;
result.success := FALSE;
done := TRUE;
RETURN NEXT result;
- END IF;
+ END LOOP;
- -- Fail if the user has too many items checked out
- IF circ_test.max_items_out IS NOT NULL THEN
- SELECT INTO items_out COUNT(*)
- FROM action.circulation
- WHERE usr = match_user
- AND (circ_test.org_depth IS NULL OR (circ_test.org_depth IS NOT NULL AND circ_lib IN ( SELECT * FROM explode_array(overdue_orgs) )))
- AND checkin_time IS NULL
- AND (stop_fines NOT IN ('LOST','CLAIMSRETURNED','LONGOVERDUE') OR stop_fines IS NULL);
- IF items_out >= circ_test.max_items_out THEN
- result.fail_part := 'config.circ_matrix_test.max_items_out';
- result.success := FALSE;
- done := TRUE;
- RETURN NEXT result;
- END IF;
- END IF;
-
-- Fail if the user has too many items with specific circ_modifiers checked out
- FOR out_by_circ_mod IN SELECT * FROM config.circ_matrix_circ_mod_test WHERE matchpoint = matchpoint_id LOOP
+ FOR out_by_circ_mod IN SELECT * FROM config.circ_matrix_circ_mod_test WHERE matchpoint = circ_test.id LOOP
SELECT INTO items_out COUNT(*)
FROM action.circulation circ
JOIN asset.copy cp ON (cp.id = circ.target_copy)
WHERE circ.usr = match_user
- AND (circ_test.org_depth IS NULL OR (circ_test.org_depth IS NOT NULL AND circ_lib IN ( SELECT * FROM explode_array(overdue_orgs) )))
+ AND circ_lib IN ( SELECT * FROM explode_array(context_org_list) )
AND circ.checkin_time IS NULL
AND (circ.stop_fines NOT IN ('LOST','CLAIMSRETURNED','LONGOVERDUE') OR circ.stop_fines IS NULL)
AND cp.circ_modifier = out_by_circ_mod.circ_mod;
@@ -384,97 +335,200 @@
END IF;
END LOOP;
- -- Fail if the user has too many overdue items
- tmp_grp := user_object.profile;
- LOOP
- SELECT pgpt.threshold::INT INTO max_overdue FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 2;
- IF max_overdue IS NULL THEN
- SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
- ELSE
- EXIT;
- END IF;
+ -- If we passed everything, return the successful matchpoint id
+ IF NOT done THEN
+ RETURN NEXT result;
+ END IF;
- IF tmp_grp IS NULL THEN
- EXIT;
- END IF;
- END LOOP;
+ RETURN;
+END;
+$func$ LANGUAGE plpgsql;
- IF max_overdue IS NOT NULL THEN
- SELECT INTO items_overdue COUNT(*)
- FROM action.circulation
- WHERE usr = match_user
- AND (circ_test.org_depth IS NULL OR (circ_test.org_depth IS NOT NULL AND circ_lib IN ( SELECT * FROM explode_array(overdue_orgs) )))
- AND checkin_time IS NULL
- AND due_date < NOW()
- AND (stop_fines NOT IN ('LOST','CLAIMSRETURNED','LONGOVERDUE') OR stop_fines IS NULL);
- IF items_overdue >= max_overdue THEN
- DELETE FROM actor.usr_standing_penalty WHERE usr = match_usr AND standing_penalty = 2 AND org_unit = circ_ou;
- INSERT INTO actor.usr_standing_penalty (usr, standing_penalty, org_unit) VALUES (match_usr, 2, circ_ou);
- result.fail_part := 'config.circ_matrix_test.max_overdue';
- result.success := FALSE;
- done := TRUE;
- RETURN NEXT result;
- END IF;
- END IF;
+CREATE OR REPLACE FUNCTION action.item_user_circ_test( INT, BIGINT, INT ) RETURNS SETOF action.matrix_test_result AS $func$
+ SELECT * FROM action.item_user_circ_test( $1, $2, $3, FALSE );
+$func$ LANGUAGE SQL;
+CREATE OR REPLACE FUNCTION action.item_user_renew_test( INT, BIGINT, INT ) RETURNS SETOF action.matrix_test_result AS $func$
+ SELECT * FROM action.item_user_circ_test( $1, $2, $3, TRUE );
+$func$ LANGUAGE SQL;
+
+
+CREATE OR REPLACE FUNCTION actor.calculate_system_penalties( match_user INT, context_org INT ) RETURNS SETOF actor.usr_standing_penalty AS $func$
+DECLARE
+ user_object actor.usr%ROWTYPE;
+ sp_row actor.usr_standing_penalty%ROWTYPE;
+ max_fines permission.grp_penalty_threshold%ROWTYPE;
+ max_overdue permission.grp_penalty_threshold%ROWTYPE;
+ max_items_out permission.grp_penalty_threshold%ROWTYPE;
+ tmp_grp INT;
+ items_overdue INT;
+ items_out INT;
+ context_org_list INT[];
+ current_fines NUMERIC(8,2) := 0.0;
+ tmp_fines NUMERIC(8,2);
+ tmp_groc RECORD;
+ tmp_circ RECORD;
+ tmp_org actor.org_unit%ROWTYPE;
+BEGIN
+ SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+
+ -- Max fines
+ SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
-- Fail if the user has a high fine balance
- tmp_grp := user_object.profile;
LOOP
- SELECT pgpt.threshold INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 1;
- IF max_overdue IS NULL THEN
- SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
- ELSE
+ tmp_grp := user_object.profile;
+ LOOP
+ SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 1 AND org_unit = tmp_org.id;
+
+ IF max_fines.threshold IS NULL THEN
+ SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+ ELSE
+ EXIT;
+ END IF;
+
+ IF tmp_grp IS NULL THEN
+ EXIT;
+ END IF;
+ END LOOP;
+
+ IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
EXIT;
END IF;
- IF tmp_grp IS NULL THEN
- EXIT;
- END IF;
+ SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
END LOOP;
- IF max_fines IS NOT NULL THEN
- FOR tmp_groc IN SELECT * FROM money.grocery WHERE usr = match_usr AND xact_finish IS NULL AND (circ_test.org_depth IS NULL OR (circ_test.org_depth IS NOT NULL AND billing_location IN ( SELECT * FROM explode_array(overdue_orgs) ))) LOOP
+ IF max_fines.threshold IS NOT NULL THEN
+ FOR tmp_groc IN
+ SELECT *
+ FROM money.grocery g
+ JOIN actor.org_unit_full_path( max_fines.org_unit ) fp ON (g.billing_location = fp.id)
+ WHERE usr = match_user
+ AND xact_finish IS NULL
+ LOOP
SELECT INTO tmp_fines SUM( amount ) FROM money.billing WHERE xact = tmp_groc.id AND NOT voided;
current_fines = current_fines + COALESCE(tmp_fines, 0.0);
SELECT INTO tmp_fines SUM( amount ) FROM money.payment WHERE xact = tmp_groc.id AND NOT voided;
current_fines = current_fines - COALESCE(tmp_fines, 0.0);
END LOOP;
- FOR tmp_circ IN SELECT * FROM action.circulation WHERE usr = match_usr AND xact_finish IS NULL AND (circ_test.org_depth IS NULL OR (circ_test.org_depth IS NOT NULL AND circ_lib IN ( SELECT * FROM explode_array(overdue_orgs) ))) LOOP
+ FOR tmp_circ IN
+ SELECT *
+ FROM action.circulation circ
+ JOIN actor.org_unit_full_path( max_fines.org_unit ) fp ON (circ.circ_lib = fp.id)
+ WHERE usr = match_user
+ AND xact_finish IS NULL
+ LOOP
SELECT INTO tmp_fines SUM( amount ) FROM money.billing WHERE xact = tmp_circ.id AND NOT voided;
current_fines = current_fines + COALESCE(tmp_fines, 0.0);
SELECT INTO tmp_fines SUM( amount ) FROM money.payment WHERE xact = tmp_circ.id AND NOT voided;
current_fines = current_fines - COALESCE(tmp_fines, 0.0);
END LOOP;
- IF current_fines >= max_fines THEN
- DELETE FROM actor.usr_standing_penalty WHERE usr = match_usr AND standing_penalty = 1 AND org_unit = circ_ou;
- INSERT INTO actor.usr_standing_penalty (usr, standing_penalty, org_unit) VALUES (match_usr, 1, circ_ou);
- result.fail_part := 'config.circ_matrix_test.max_fines';
- result.success := FALSE;
- RETURN NEXT result;
- done := TRUE;
+ IF current_fines >= max_fines.threshold THEN
+ sp_row.usr := match_user;
+ sp_row.org_unit := max_fines.org_unit;
+ sp_row.standing_penalty := 1;
+ RETURN NEXT sp_row;
END IF;
END IF;
- -- If we passed everything, return the successful matchpoint id
- IF NOT done THEN
- RETURN NEXT result;
+ -- Start over for max overdue
+ SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+ -- Fail if the user has too many overdue items
+ LOOP
+ tmp_grp := user_object.profile;
+ LOOP
+
+ SELECT * INTO max_overdue FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 2 AND org_unit = tmp_org.id;
+
+ IF max_overdue.threshold IS NULL THEN
+ SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+ ELSE
+ EXIT;
+ END IF;
+
+ IF tmp_grp IS NULL THEN
+ EXIT;
+ END IF;
+ END LOOP;
+
+ IF max_overdue.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+ EXIT;
+ END IF;
+
+ SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+ END LOOP;
+
+ IF max_overdue.threshold IS NOT NULL THEN
+ SELECT INTO items_overdue COUNT(*)
+ FROM action.circulation circ
+ JOIN actor.org_unit_full_path( max_overdue.org_unit ) fp ON (circ.circ_lib = fp.id)
+ WHERE circ.usr = match_user
+ AND circ.checkin_time IS NULL
+ AND circ.due_date < NOW()
+ AND (circ.stop_fines NOT IN ('LOST','CLAIMSRETURNED','LONGOVERDUE') OR circ.stop_fines IS NULL);
+
+ IF items_overdue >= max_overdue.threshold::INT THEN
+ sp_row.usr := match_user;
+ sp_row.org_unit := max_overdue.org_unit;
+ sp_row.standing_penalty := 2;
+ RETURN NEXT sp_row;
+ END IF;
END IF;
- RETURN;
-END;
-$func$ LANGUAGE plpgsql;
+ -- Start over for max out
+ SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
-CREATE OR REPLACE FUNCTION action.item_user_circ_test( INT, BIGINT, INT ) RETURNS SETOF action.matrix_test_result AS $func$
- SELECT * FROM action.item_user_circ_test( $1, $2, $3, FALSE );
-$func$ LANGUAGE SQL;
+ -- Fail if the user has too many overdue items
+ LOOP
+ tmp_grp := user_object.profile;
+ LOOP
+ SELECT * INTO max_items_out FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 3 AND org_unit = tmp_org.id;
-CREATE OR REPLACE FUNCTION action.item_user_renew_test( INT, BIGINT, INT ) RETURNS SETOF action.matrix_test_result AS $func$
- SELECT * FROM action.item_user_circ_test( $1, $2, $3, TRUE );
-$func$ LANGUAGE SQL;
+ IF max_items_out.threshold IS NULL THEN
+ SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+ ELSE
+ EXIT;
+ END IF;
-CREATE OR REPLACE FUNCTION actor.refresh_auto_penalties( user INT ) RETURNS INT AS $func$
+ IF tmp_grp IS NULL THEN
+ EXIT;
+ END IF;
+ END LOOP;
+
+ IF max_items_out.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+ EXIT;
+ END IF;
+
+ SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+ END LOOP;
+
+
+ -- Fail if the user has too many items checked out
+ IF max_items_out.threshold IS NOT NULL THEN
+ SELECT INTO items_out COUNT(*)
+ FROM action.circulation circ
+ JOIN actor.org_unit_full_path( max_items_out.org_unit ) fp ON (circ.circ_lib = fp.id)
+ WHERE circ.usr = match_user
+ AND circ.checkin_time IS NULL
+ AND (circ.stop_fines NOT IN ('LOST','CLAIMSRETURNED','LONGOVERDUE') OR circ.stop_fines IS NULL);
+
+ IF items_out >= max_items_out.threshold::INT THEN
+ sp_row.usr := match_user;
+ sp_row.org_unit := max_items_out.org_unit;
+ sp_row.standing_penalty := 3;
+ RETURN NEXT sp_row;
+ END IF;
+ END IF;
+
+ RETURN;
+END;
$func$ LANGUAGE plpgsql;
COMMIT;
Modified: trunk/Open-ILS/src/sql/Pg/110.hold_matrix.sql
===================================================================
--- trunk/Open-ILS/src/sql/Pg/110.hold_matrix.sql 2008-11-20 22:28:13 UTC (rev 11296)
+++ trunk/Open-ILS/src/sql/Pg/110.hold_matrix.sql 2008-11-21 05:58:14 UTC (rev 11297)
@@ -29,33 +29,28 @@
CREATE TABLE config.hold_matrix_matchpoint (
- id SERIAL PRIMARY KEY,
- active BOOL NOT NULL DEFAULT TRUE,
- user_home_ou INT REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
- request_ou INT REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
- pickup_ou INT REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
- item_owning_ou INT REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
- item_circ_ou INT REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
- usr_grp INT REFERENCES permission.grp_tree (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top applicable group from the group tree; will need descendents and prox functions for filtering
- requestor_grp INT NOT NULL REFERENCES permission.grp_tree (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top applicable group from the group tree; will need descendents and prox functions for filtering
- circ_modifier TEXT REFERENCES config.circ_modifier (code) DEFERRABLE INITIALLY DEFERRED,
- marc_type TEXT REFERENCES config.item_type_map (code) DEFERRABLE INITIALLY DEFERRED,
- marc_form TEXT REFERENCES config.item_form_map (code) DEFERRABLE INITIALLY DEFERRED,
- marc_vr_format TEXT REFERENCES config.videorecording_format_map (code) DEFERRABLE INITIALLY DEFERRED,
- ref_flag BOOL,
- CONSTRAINT hous_once_per_grp_loc_mod_marc UNIQUE (user_home_ou, request_ou, pickup_ou, item_owning_ou, item_circ_ou, requestor_grp, usr_grp, circ_modifier, marc_type, marc_form, marc_vr_format)
-);
-
--- Tests to determine if hold against a specific copy is possible for a user at (and from) a location
-CREATE TABLE config.hold_matrix_test (
- matchpoint INT PRIMARY KEY REFERENCES config.hold_matrix_matchpoint (id) DEFERRABLE INITIALLY DEFERRED,
+ id SERIAL PRIMARY KEY,
+ active BOOL NOT NULL DEFAULT TRUE,
+ user_home_ou INT REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
+ request_ou INT REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
+ pickup_ou INT REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
+ item_owning_ou INT REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
+ item_circ_ou INT REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
+ usr_grp INT REFERENCES permission.grp_tree (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top applicable group from the group tree; will need descendents and prox functions for filtering
+ requestor_grp INT NOT NULL REFERENCES permission.grp_tree (id) DEFERRABLE INITIALLY DEFERRED, -- Set to the top applicable group from the group tree; will need descendents and prox functions for filtering
+ circ_modifier TEXT REFERENCES config.circ_modifier (code) DEFERRABLE INITIALLY DEFERRED,
+ marc_type TEXT REFERENCES config.item_type_map (code) DEFERRABLE INITIALLY DEFERRED,
+ marc_form TEXT REFERENCES config.item_form_map (code) DEFERRABLE INITIALLY DEFERRED,
+ marc_vr_format TEXT REFERENCES config.videorecording_format_map (code) DEFERRABLE INITIALLY DEFERRED,
+ ref_flag BOOL,
holdable BOOL NOT NULL DEFAULT TRUE, -- Hard "can't hold" flag requiring an override
distance_is_from_owner BOOL NOT NULL DEFAULT FALSE, -- How to calculate transit_range. True means owning lib, false means copy circ lib
- transit_range INT REFERENCES actor.org_unit_type (id) DEFERRABLE INITIALLY DEFERRED, -- Can circ inside range of cn.owner/cp.circ_lib at depth of the org_unit_type specified here
+ transit_range INT REFERENCES actor.org_unit_type (id) DEFERRABLE INITIALLY DEFERRED, -- Can circ inside range of cn.owner/cp.circ_lib at depth of the org_unit_type specified here
max_holds INT, -- Total hold requests must be less than this, NULL means skip (always pass)
include_frozen_holds BOOL NOT NULL DEFAULT TRUE, -- Include frozen hold requests in the count for max_holds test
stop_blocked_user BOOL NOT NULL DEFAULT FALSE, -- Stop users who cannot check out items from placing holds
- age_hold_protect_rule INT REFERENCES config.rule_age_hold_protect (id) DEFERRABLE INITIALLY DEFERRED -- still not sure we want to move this off the copy
+ age_hold_protect_rule INT REFERENCES config.rule_age_hold_protect (id) DEFERRABLE INITIALLY DEFERRED, -- still not sure we want to move this off the copy
+ CONSTRAINT hous_once_per_grp_loc_mod_marc UNIQUE (user_home_ou, request_ou, pickup_ou, item_owning_ou, item_circ_ou, requestor_grp, usr_grp, circ_modifier, marc_type, marc_form, marc_vr_format)
);
CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS INT AS $func$
@@ -173,7 +168,7 @@
transit_source actor.org_unit%ROWTYPE;
item_object asset.copy%ROWTYPE;
result action.matrix_test_result;
- hold_test config.hold_matrix_test%ROWTYPE;
+ hold_test config.hold_matrix_matchpoint%ROWTYPE;
hold_count INT;
hold_transit_prox INT;
frozen_hold_count INT;
@@ -222,9 +217,9 @@
RETURN;
END IF;
- SELECT INTO hold_test * FROM config.hold_matrix_test WHERE matchpoint = matchpoint_id;
+ SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id;
- result.matchpoint := matchpoint_id;
+ result.matchpoint := hold_test.id;
result.success := TRUE;
IF hold_test.holdable IS FALSE THEN
@@ -254,7 +249,7 @@
SELECT INTO patron_penalties COUNT(*)
FROM actor.usr_standing_penalty usp
- JOIN config.standing_penalty csp ON (csp.id = usp.penalty)
+ JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
WHERE usr = match_user
AND csp.block_list LIKE '%HOLD%';
@@ -270,7 +265,7 @@
IF hold_test.stop_blocked_user IS TRUE THEN
SELECT INTO patron_penalties COUNT(*)
FROM actor.usr_standing_penalty usp
- JOIN config.standing_penalty csp ON (csp.id = usp.penalty)
+ JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
WHERE usr = match_user
AND csp.block_list LIKE '%CIRC%';
Modified: trunk/Open-ILS/src/sql/Pg/950.data.seed-values.sql
===================================================================
--- trunk/Open-ILS/src/sql/Pg/950.data.seed-values.sql 2008-11-20 22:28:13 UTC (rev 11296)
+++ trunk/Open-ILS/src/sql/Pg/950.data.seed-values.sql 2008-11-21 05:58:14 UTC (rev 11297)
@@ -1297,6 +1297,8 @@
VALUES (1,1,1,10.0);
INSERT INTO permission.grp_penalty_threshold (grp,org_unit,penalty,threshold)
VALUES (1,1,2,10.0);
+INSERT INTO permission.grp_penalty_threshold (grp,org_unit,penalty,threshold)
+ VALUES (1,1,3,10.0);
SELECT SETVAL('permission.grp_penalty_threshold_id_seq'::TEXT, (SELECT MAX(id) FROM permission.grp_penalty_threshold));
@@ -1440,8 +1442,7 @@
INSERT INTO config.xml_transform VALUES ( 'mods33', 'http://www.loc.gov/mods/v3', 'mods33', '');
-- circ matrix
-INSERT INTO config.circ_matrix_matchpoint (org_unit,grp) VALUES (1,1);
-INSERT INTO config.circ_matrix_ruleset (matchpoint,duration_rule,recurring_fine_rule,max_fine_rule) VALUES (1,11,1,1);
+INSERT INTO config.circ_matrix_matchpoint (org_unit,grp,duration_rule,recurring_fine_rule,max_fine_rule) VALUES (1,1,1,11,1,1);
-- hold matrix - 110.hold_matrix.sql:
More information about the open-ils-commits
mailing list