From: Chris Sharp Date: Wed, 22 Oct 2014 18:42:39 +0000 (-0400) Subject: PINES has already applied 0873. X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=b3349422caaf74441c6a54d968287c8c4eab773b;p=evergreen%2Fpines.git PINES has already applied 0873. --- diff --git a/Open-ILS/src/sql/Pg/version-upgrade/2.5.3-2.6.0-upgrade-db.sql b/Open-ILS/src/sql/Pg/version-upgrade/2.5.3-2.6.0-upgrade-db.sql index 78c8c63663..90aa0d3ea0 100644 --- a/Open-ILS/src/sql/Pg/version-upgrade/2.5.3-2.6.0-upgrade-db.sql +++ b/Open-ILS/src/sql/Pg/version-upgrade/2.5.3-2.6.0-upgrade-db.sql @@ -4587,388 +4587,388 @@ SELECT count(metabib.remap_metarecord_for_bib( bre.id, bre.fingerprint, TRUE, C -SELECT evergreen.upgrade_deps_block_check('0873', :eg_version); - -CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint(pickup_ou integer, request_ou integer, match_item bigint, match_user integer, match_requestor integer) - RETURNS integer AS -$func$ -DECLARE - requestor_object actor.usr%ROWTYPE; - user_object actor.usr%ROWTYPE; - item_object asset.copy%ROWTYPE; - item_cn_object asset.call_number%ROWTYPE; - my_item_age INTERVAL; - rec_descriptor metabib.rec_descriptor%ROWTYPE; - matchpoint config.hold_matrix_matchpoint%ROWTYPE; - weights config.hold_matrix_weights%ROWTYPE; - denominator NUMERIC(6,2); - v_pickup_ou ALIAS FOR pickup_ou; - v_request_ou ALIAS FOR request_ou; -BEGIN - SELECT INTO user_object * FROM actor.usr WHERE id = match_user; - SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor; - SELECT INTO item_object * FROM asset.copy WHERE id = match_item; - SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number; - SELECT INTO rec_descriptor * FROM metabib.rec_descriptor WHERE record = item_cn_object.record; - - SELECT INTO my_item_age age(coalesce(item_object.active_date, now())); - - -- The item's owner should probably be the one determining if the item is holdable - -- How to decide that is debatable. Decided to default to the circ library (where the item lives) - -- This flag will allow for setting it to the owning library (where the call number "lives") - PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.weight_owner_not_circ' AND enabled; - - -- Grab the closest set circ weight setting. - IF NOT FOUND THEN - -- Default to circ library - SELECT INTO weights hw.* - FROM config.weight_assoc wa - JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights) - JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) d ON (wa.org_unit = d.id) - WHERE active - ORDER BY d.distance - LIMIT 1; - ELSE - -- Flag is set, use owning library - SELECT INTO weights hw.* - FROM config.weight_assoc wa - JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights) - JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) d ON (wa.org_unit = d.id) - WHERE active - ORDER BY d.distance - LIMIT 1; - END IF; - - -- No weights? Bad admin! Defaults to handle that anyway. - IF weights.id IS NULL THEN - weights.user_home_ou := 5.0; - weights.request_ou := 5.0; - weights.pickup_ou := 5.0; - weights.item_owning_ou := 5.0; - weights.item_circ_ou := 5.0; - weights.usr_grp := 7.0; - weights.requestor_grp := 8.0; - weights.circ_modifier := 4.0; - weights.marc_type := 3.0; - weights.marc_form := 2.0; - weights.marc_bib_level := 1.0; - weights.marc_vr_format := 1.0; - weights.juvenile_flag := 4.0; - weights.ref_flag := 0.0; - weights.item_age := 0.0; - END IF; - - -- Determine the max (expected) depth (+1) of the org tree and max depth of the permisson tree - -- If you break your org tree with funky parenting this may be wrong - -- Note: This CTE is duplicated in the find_circ_matrix_matchpoint function, and it may be a good idea to split it off to a function - -- We use one denominator for all tree-based checks for when permission groups and org units have the same weighting - WITH all_distance(distance) AS ( - SELECT depth AS distance FROM actor.org_unit_type - UNION - SELECT distance AS distance FROM permission.grp_ancestors_distance((SELECT id FROM permission.grp_tree WHERE parent IS NULL)) - ) - SELECT INTO denominator MAX(distance) + 1 FROM all_distance; - - -- To ATTEMPT to make this work like it used to, make it reverse the user/requestor profile ids. - -- This may be better implemented as part of the upgrade script? - -- Set usr_grp = requestor_grp, requestor_grp = 1 or something when this flag is already set - -- Then remove this flag, of course. - PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.usr_not_requestor' AND enabled; - - IF FOUND THEN - -- Note: This, to me, is REALLY hacky. I put it in anyway. - -- If you can't tell, this is a single call swap on two variables. - SELECT INTO user_object.profile, requestor_object.profile - requestor_object.profile, user_object.profile; - END IF; - - -- Select the winning matchpoint into the matchpoint variable for returning - SELECT INTO matchpoint m.* - FROM config.hold_matrix_matchpoint m - /*LEFT*/ JOIN permission.grp_ancestors_distance( requestor_object.profile ) rpgad ON m.requestor_grp = rpgad.id - LEFT JOIN permission.grp_ancestors_distance( user_object.profile ) upgad ON m.usr_grp = upgad.id - LEFT JOIN actor.org_unit_ancestors_distance( v_pickup_ou ) puoua ON m.pickup_ou = puoua.id - LEFT JOIN actor.org_unit_ancestors_distance( v_request_ou ) rqoua ON m.request_ou = rqoua.id - LEFT JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) cnoua ON m.item_owning_ou = cnoua.id - LEFT JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) iooua ON m.item_circ_ou = iooua.id - LEFT JOIN actor.org_unit_ancestors_distance( user_object.home_ou ) uhoua ON m.user_home_ou = uhoua.id - WHERE m.active - -- Permission Groups - -- AND (m.requestor_grp IS NULL OR upgad.id IS NOT NULL) -- Optional Requestor Group? - AND (m.usr_grp IS NULL OR upgad.id IS NOT NULL) - -- Org Units - AND (m.pickup_ou IS NULL OR (puoua.id IS NOT NULL AND (puoua.distance = 0 OR NOT m.strict_ou_match))) - AND (m.request_ou IS NULL OR (rqoua.id IS NOT NULL AND (rqoua.distance = 0 OR NOT m.strict_ou_match))) - AND (m.item_owning_ou IS NULL OR (cnoua.id IS NOT NULL AND (cnoua.distance = 0 OR NOT m.strict_ou_match))) - AND (m.item_circ_ou IS NULL OR (iooua.id IS NOT NULL AND (iooua.distance = 0 OR NOT m.strict_ou_match))) - AND (m.user_home_ou IS NULL OR (uhoua.id IS NOT NULL AND (uhoua.distance = 0 OR NOT m.strict_ou_match))) - -- Static User Checks - AND (m.juvenile_flag IS NULL OR m.juvenile_flag = user_object.juvenile) - -- Static Item Checks - AND (m.circ_modifier IS NULL OR m.circ_modifier = item_object.circ_modifier) - AND (m.marc_type IS NULL OR m.marc_type = COALESCE(item_object.circ_as_type, rec_descriptor.item_type)) - AND (m.marc_form IS NULL OR m.marc_form = rec_descriptor.item_form) - AND (m.marc_bib_level IS NULL OR m.marc_bib_level = rec_descriptor.bib_level) - AND (m.marc_vr_format IS NULL OR m.marc_vr_format = rec_descriptor.vr_format) - AND (m.ref_flag IS NULL OR m.ref_flag = item_object.ref) - AND (m.item_age IS NULL OR (my_item_age IS NOT NULL AND m.item_age > my_item_age)) - ORDER BY - -- Permission Groups - CASE WHEN rpgad.distance IS NOT NULL THEN 2^(2*weights.requestor_grp - (rpgad.distance/denominator)) ELSE 0.0 END + - CASE WHEN upgad.distance IS NOT NULL THEN 2^(2*weights.usr_grp - (upgad.distance/denominator)) ELSE 0.0 END + - -- Org Units - CASE WHEN puoua.distance IS NOT NULL THEN 2^(2*weights.pickup_ou - (puoua.distance/denominator)) ELSE 0.0 END + - CASE WHEN rqoua.distance IS NOT NULL THEN 2^(2*weights.request_ou - (rqoua.distance/denominator)) ELSE 0.0 END + - CASE WHEN cnoua.distance IS NOT NULL THEN 2^(2*weights.item_owning_ou - (cnoua.distance/denominator)) ELSE 0.0 END + - CASE WHEN iooua.distance IS NOT NULL THEN 2^(2*weights.item_circ_ou - (iooua.distance/denominator)) ELSE 0.0 END + - CASE WHEN uhoua.distance IS NOT NULL THEN 2^(2*weights.user_home_ou - (uhoua.distance/denominator)) ELSE 0.0 END + - -- Static User Checks -- Note: 4^x is equiv to 2^(2*x) - CASE WHEN m.juvenile_flag IS NOT NULL THEN 4^weights.juvenile_flag ELSE 0.0 END + - -- Static Item Checks - CASE WHEN m.circ_modifier IS NOT NULL THEN 4^weights.circ_modifier ELSE 0.0 END + - CASE WHEN m.marc_type IS NOT NULL THEN 4^weights.marc_type ELSE 0.0 END + - CASE WHEN m.marc_form IS NOT NULL THEN 4^weights.marc_form ELSE 0.0 END + - CASE WHEN m.marc_vr_format IS NOT NULL THEN 4^weights.marc_vr_format ELSE 0.0 END + - CASE WHEN m.ref_flag IS NOT NULL THEN 4^weights.ref_flag ELSE 0.0 END + - -- Item age has a slight adjustment to weight based on value. - -- This should ensure that a shorter age limit comes first when all else is equal. - -- NOTE: This assumes that intervals will normally be in days. - CASE WHEN m.item_age IS NOT NULL THEN 4^weights.item_age - 86400/EXTRACT(EPOCH FROM m.item_age) ELSE 0.0 END DESC, - -- Final sort on id, so that if two rules have the same sorting in the previous sort they have a defined order - -- This prevents "we changed the table order by updating a rule, and we started getting different results" - m.id; - - -- Return just the ID for now - RETURN matchpoint.id; -END; -$func$ LANGUAGE 'plpgsql'; - -CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$ -DECLARE - matchpoint_id INT; - user_object actor.usr%ROWTYPE; - age_protect_object config.rule_age_hold_protect%ROWTYPE; - standing_penalty config.standing_penalty%ROWTYPE; - transit_range_ou_type actor.org_unit_type%ROWTYPE; - transit_source actor.org_unit%ROWTYPE; - item_object asset.copy%ROWTYPE; - item_cn_object asset.call_number%ROWTYPE; - item_status_object config.copy_status%ROWTYPE; - item_location_object asset.copy_location%ROWTYPE; - ou_skip actor.org_unit_setting%ROWTYPE; - result action.matrix_test_result; - hold_test config.hold_matrix_matchpoint%ROWTYPE; - use_active_date TEXT; - age_protect_date TIMESTAMP WITH TIME ZONE; - hold_count INT; - hold_transit_prox INT; - frozen_hold_count INT; - context_org_list INT[]; - done BOOL := FALSE; - hold_penalty TEXT; - v_pickup_ou ALIAS FOR pickup_ou; - v_request_ou ALIAS FOR request_ou; -BEGIN - SELECT INTO user_object * FROM actor.usr WHERE id = match_user; - SELECT INTO context_org_list ARRAY_AGG(id) FROM actor.org_unit_full_path( v_pickup_ou ); - - result.success := TRUE; - - -- The HOLD penalty block only applies to new holds. - -- The CAPTURE penalty block applies to existing holds. - hold_penalty := 'HOLD'; - IF retargetting THEN - hold_penalty := 'CAPTURE'; - END IF; - - -- Fail if we couldn't find a user - IF user_object.id IS NULL THEN - result.fail_part := 'no_user'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - RETURN; - END IF; - - SELECT INTO item_object * FROM asset.copy WHERE id = match_item; - - -- Fail if we couldn't find a copy - IF item_object.id IS NULL THEN - result.fail_part := 'no_item'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - RETURN; - END IF; - - SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(v_pickup_ou, v_request_ou, match_item, match_user, match_requestor); - result.matchpoint := matchpoint_id; - - SELECT INTO ou_skip * FROM actor.org_unit_setting WHERE name = 'circ.holds.target_skip_me' AND org_unit = item_object.circ_lib; - - -- Fail if the circ_lib for the item has circ.holds.target_skip_me set to true - IF ou_skip.id IS NOT NULL AND ou_skip.value = 'true' THEN - result.fail_part := 'circ.holds.target_skip_me'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - RETURN; - END IF; - - -- Fail if user is barred - IF user_object.barred IS TRUE THEN - result.fail_part := 'actor.usr.barred'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - RETURN; - END IF; - - SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number; - SELECT INTO item_status_object * FROM config.copy_status WHERE id = item_object.status; - SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location; - - -- Fail if we couldn't find any matchpoint (requires a default) - IF matchpoint_id IS NULL THEN - result.fail_part := 'no_matchpoint'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - RETURN; - END IF; - - SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id; - - IF hold_test.holdable IS FALSE THEN - result.fail_part := 'config.hold_matrix_test.holdable'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - - IF item_object.holdable IS FALSE THEN - result.fail_part := 'item.holdable'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - - IF item_status_object.holdable IS FALSE THEN - result.fail_part := 'status.holdable'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - - IF item_location_object.holdable IS FALSE THEN - result.fail_part := 'location.holdable'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - - IF hold_test.transit_range IS NOT NULL THEN - SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range; - IF hold_test.distance_is_from_owner THEN - SELECT INTO transit_source ou.* FROM actor.org_unit ou JOIN asset.call_number cn ON (cn.owning_lib = ou.id) WHERE cn.id = item_object.call_number; - ELSE - SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib; - END IF; - - PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = v_pickup_ou; - - IF NOT FOUND THEN - result.fail_part := 'transit_range'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - END IF; - - FOR standing_penalty IN - SELECT DISTINCT csp.* - FROM actor.usr_standing_penalty usp - JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty) - WHERE usr = match_user - AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) ) - AND (usp.stop_date IS NULL or usp.stop_date > NOW()) - AND csp.block_list LIKE '%' || hold_penalty || '%' LOOP - - result.fail_part := standing_penalty.name; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END LOOP; - - IF hold_test.stop_blocked_user IS TRUE THEN - FOR standing_penalty IN - SELECT DISTINCT csp.* - FROM actor.usr_standing_penalty usp - JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty) - WHERE usr = match_user - AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) ) - AND (usp.stop_date IS NULL or usp.stop_date > NOW()) - AND csp.block_list LIKE '%CIRC%' LOOP - - result.fail_part := standing_penalty.name; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END LOOP; - END IF; - - IF hold_test.max_holds IS NOT NULL AND NOT retargetting THEN - SELECT INTO hold_count COUNT(*) - FROM action.hold_request - WHERE usr = match_user - AND fulfillment_time IS NULL - AND cancel_time IS NULL - AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END; - - IF hold_count >= hold_test.max_holds THEN - result.fail_part := 'config.hold_matrix_test.max_holds'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - END IF; - - IF item_object.age_protect IS NOT NULL THEN - SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect; - IF hold_test.distance_is_from_owner THEN - SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_cn_object.owning_lib); - ELSE - SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_object.circ_lib); - END IF; - IF use_active_date = 'true' THEN - age_protect_date := COALESCE(item_object.active_date, NOW()); - ELSE - age_protect_date := item_object.create_date; - END IF; - IF age_protect_date + age_protect_object.age > NOW() THEN - IF hold_test.distance_is_from_owner THEN - SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number; - SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_cn_object.owning_lib AND to_org = v_pickup_ou; - ELSE - SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_object.circ_lib AND to_org = v_pickup_ou; - END IF; - - IF hold_transit_prox > age_protect_object.prox THEN - result.fail_part := 'config.rule_age_hold_protect.prox'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - END IF; - END IF; - - IF NOT done THEN - RETURN NEXT result; - END IF; - - RETURN; -END; -$func$ LANGUAGE plpgsql; +--SELECT evergreen.upgrade_deps_block_check('0873', :eg_version); +-- +--CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint(pickup_ou integer, request_ou integer, match_item bigint, match_user integer, match_requestor integer) +-- RETURNS integer AS +--$func$ +--DECLARE +-- requestor_object actor.usr%ROWTYPE; +-- user_object actor.usr%ROWTYPE; +-- item_object asset.copy%ROWTYPE; +-- item_cn_object asset.call_number%ROWTYPE; +-- my_item_age INTERVAL; +-- rec_descriptor metabib.rec_descriptor%ROWTYPE; +-- matchpoint config.hold_matrix_matchpoint%ROWTYPE; +-- weights config.hold_matrix_weights%ROWTYPE; +-- denominator NUMERIC(6,2); +-- v_pickup_ou ALIAS FOR pickup_ou; +-- v_request_ou ALIAS FOR request_ou; +--BEGIN +-- SELECT INTO user_object * FROM actor.usr WHERE id = match_user; +-- SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor; +-- SELECT INTO item_object * FROM asset.copy WHERE id = match_item; +-- SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number; +-- SELECT INTO rec_descriptor * FROM metabib.rec_descriptor WHERE record = item_cn_object.record; +-- +-- SELECT INTO my_item_age age(coalesce(item_object.active_date, now())); +-- +-- -- The item's owner should probably be the one determining if the item is holdable +-- -- How to decide that is debatable. Decided to default to the circ library (where the item lives) +-- -- This flag will allow for setting it to the owning library (where the call number "lives") +-- PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.weight_owner_not_circ' AND enabled; +-- +-- -- Grab the closest set circ weight setting. +-- IF NOT FOUND THEN +-- -- Default to circ library +-- SELECT INTO weights hw.* +-- FROM config.weight_assoc wa +-- JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights) +-- JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) d ON (wa.org_unit = d.id) +-- WHERE active +-- ORDER BY d.distance +-- LIMIT 1; +-- ELSE +-- -- Flag is set, use owning library +-- SELECT INTO weights hw.* +-- FROM config.weight_assoc wa +-- JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights) +-- JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) d ON (wa.org_unit = d.id) +-- WHERE active +-- ORDER BY d.distance +-- LIMIT 1; +-- END IF; +-- +-- -- No weights? Bad admin! Defaults to handle that anyway. +-- IF weights.id IS NULL THEN +-- weights.user_home_ou := 5.0; +-- weights.request_ou := 5.0; +-- weights.pickup_ou := 5.0; +-- weights.item_owning_ou := 5.0; +-- weights.item_circ_ou := 5.0; +-- weights.usr_grp := 7.0; +-- weights.requestor_grp := 8.0; +-- weights.circ_modifier := 4.0; +-- weights.marc_type := 3.0; +-- weights.marc_form := 2.0; +-- weights.marc_bib_level := 1.0; +-- weights.marc_vr_format := 1.0; +-- weights.juvenile_flag := 4.0; +-- weights.ref_flag := 0.0; +-- weights.item_age := 0.0; +-- END IF; +-- +-- -- Determine the max (expected) depth (+1) of the org tree and max depth of the permisson tree +-- -- If you break your org tree with funky parenting this may be wrong +-- -- Note: This CTE is duplicated in the find_circ_matrix_matchpoint function, and it may be a good idea to split it off to a function +-- -- We use one denominator for all tree-based checks for when permission groups and org units have the same weighting +-- WITH all_distance(distance) AS ( +-- SELECT depth AS distance FROM actor.org_unit_type +-- UNION +-- SELECT distance AS distance FROM permission.grp_ancestors_distance((SELECT id FROM permission.grp_tree WHERE parent IS NULL)) +-- ) +-- SELECT INTO denominator MAX(distance) + 1 FROM all_distance; +-- +-- -- To ATTEMPT to make this work like it used to, make it reverse the user/requestor profile ids. +-- -- This may be better implemented as part of the upgrade script? +-- -- Set usr_grp = requestor_grp, requestor_grp = 1 or something when this flag is already set +-- -- Then remove this flag, of course. +-- PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.usr_not_requestor' AND enabled; +-- +-- IF FOUND THEN +-- -- Note: This, to me, is REALLY hacky. I put it in anyway. +-- -- If you can't tell, this is a single call swap on two variables. +-- SELECT INTO user_object.profile, requestor_object.profile +-- requestor_object.profile, user_object.profile; +-- END IF; +-- +-- -- Select the winning matchpoint into the matchpoint variable for returning +-- SELECT INTO matchpoint m.* +-- FROM config.hold_matrix_matchpoint m +-- /*LEFT*/ JOIN permission.grp_ancestors_distance( requestor_object.profile ) rpgad ON m.requestor_grp = rpgad.id +-- LEFT JOIN permission.grp_ancestors_distance( user_object.profile ) upgad ON m.usr_grp = upgad.id +-- LEFT JOIN actor.org_unit_ancestors_distance( v_pickup_ou ) puoua ON m.pickup_ou = puoua.id +-- LEFT JOIN actor.org_unit_ancestors_distance( v_request_ou ) rqoua ON m.request_ou = rqoua.id +-- LEFT JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) cnoua ON m.item_owning_ou = cnoua.id +-- LEFT JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) iooua ON m.item_circ_ou = iooua.id +-- LEFT JOIN actor.org_unit_ancestors_distance( user_object.home_ou ) uhoua ON m.user_home_ou = uhoua.id +-- WHERE m.active +-- -- Permission Groups +-- -- AND (m.requestor_grp IS NULL OR upgad.id IS NOT NULL) -- Optional Requestor Group? +-- AND (m.usr_grp IS NULL OR upgad.id IS NOT NULL) +-- -- Org Units +-- AND (m.pickup_ou IS NULL OR (puoua.id IS NOT NULL AND (puoua.distance = 0 OR NOT m.strict_ou_match))) +-- AND (m.request_ou IS NULL OR (rqoua.id IS NOT NULL AND (rqoua.distance = 0 OR NOT m.strict_ou_match))) +-- AND (m.item_owning_ou IS NULL OR (cnoua.id IS NOT NULL AND (cnoua.distance = 0 OR NOT m.strict_ou_match))) +-- AND (m.item_circ_ou IS NULL OR (iooua.id IS NOT NULL AND (iooua.distance = 0 OR NOT m.strict_ou_match))) +-- AND (m.user_home_ou IS NULL OR (uhoua.id IS NOT NULL AND (uhoua.distance = 0 OR NOT m.strict_ou_match))) +-- -- Static User Checks +-- AND (m.juvenile_flag IS NULL OR m.juvenile_flag = user_object.juvenile) +-- -- Static Item Checks +-- AND (m.circ_modifier IS NULL OR m.circ_modifier = item_object.circ_modifier) +-- AND (m.marc_type IS NULL OR m.marc_type = COALESCE(item_object.circ_as_type, rec_descriptor.item_type)) +-- AND (m.marc_form IS NULL OR m.marc_form = rec_descriptor.item_form) +-- AND (m.marc_bib_level IS NULL OR m.marc_bib_level = rec_descriptor.bib_level) +-- AND (m.marc_vr_format IS NULL OR m.marc_vr_format = rec_descriptor.vr_format) +-- AND (m.ref_flag IS NULL OR m.ref_flag = item_object.ref) +-- AND (m.item_age IS NULL OR (my_item_age IS NOT NULL AND m.item_age > my_item_age)) +-- ORDER BY +-- -- Permission Groups +-- CASE WHEN rpgad.distance IS NOT NULL THEN 2^(2*weights.requestor_grp - (rpgad.distance/denominator)) ELSE 0.0 END + +-- CASE WHEN upgad.distance IS NOT NULL THEN 2^(2*weights.usr_grp - (upgad.distance/denominator)) ELSE 0.0 END + +-- -- Org Units +-- CASE WHEN puoua.distance IS NOT NULL THEN 2^(2*weights.pickup_ou - (puoua.distance/denominator)) ELSE 0.0 END + +-- CASE WHEN rqoua.distance IS NOT NULL THEN 2^(2*weights.request_ou - (rqoua.distance/denominator)) ELSE 0.0 END + +-- CASE WHEN cnoua.distance IS NOT NULL THEN 2^(2*weights.item_owning_ou - (cnoua.distance/denominator)) ELSE 0.0 END + +-- CASE WHEN iooua.distance IS NOT NULL THEN 2^(2*weights.item_circ_ou - (iooua.distance/denominator)) ELSE 0.0 END + +-- CASE WHEN uhoua.distance IS NOT NULL THEN 2^(2*weights.user_home_ou - (uhoua.distance/denominator)) ELSE 0.0 END + +-- -- Static User Checks -- Note: 4^x is equiv to 2^(2*x) +-- CASE WHEN m.juvenile_flag IS NOT NULL THEN 4^weights.juvenile_flag ELSE 0.0 END + +-- -- Static Item Checks +-- CASE WHEN m.circ_modifier IS NOT NULL THEN 4^weights.circ_modifier ELSE 0.0 END + +-- CASE WHEN m.marc_type IS NOT NULL THEN 4^weights.marc_type ELSE 0.0 END + +-- CASE WHEN m.marc_form IS NOT NULL THEN 4^weights.marc_form ELSE 0.0 END + +-- CASE WHEN m.marc_vr_format IS NOT NULL THEN 4^weights.marc_vr_format ELSE 0.0 END + +-- CASE WHEN m.ref_flag IS NOT NULL THEN 4^weights.ref_flag ELSE 0.0 END + +-- -- Item age has a slight adjustment to weight based on value. +-- -- This should ensure that a shorter age limit comes first when all else is equal. +-- -- NOTE: This assumes that intervals will normally be in days. +-- CASE WHEN m.item_age IS NOT NULL THEN 4^weights.item_age - 86400/EXTRACT(EPOCH FROM m.item_age) ELSE 0.0 END DESC, +-- -- Final sort on id, so that if two rules have the same sorting in the previous sort they have a defined order +-- -- This prevents "we changed the table order by updating a rule, and we started getting different results" +-- m.id; +-- +-- -- Return just the ID for now +-- RETURN matchpoint.id; +--END; +--$func$ LANGUAGE 'plpgsql'; +-- +--CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$ +--DECLARE +-- matchpoint_id INT; +-- user_object actor.usr%ROWTYPE; +-- age_protect_object config.rule_age_hold_protect%ROWTYPE; +-- standing_penalty config.standing_penalty%ROWTYPE; +-- transit_range_ou_type actor.org_unit_type%ROWTYPE; +-- transit_source actor.org_unit%ROWTYPE; +-- item_object asset.copy%ROWTYPE; +-- item_cn_object asset.call_number%ROWTYPE; +-- item_status_object config.copy_status%ROWTYPE; +-- item_location_object asset.copy_location%ROWTYPE; +-- ou_skip actor.org_unit_setting%ROWTYPE; +-- result action.matrix_test_result; +-- hold_test config.hold_matrix_matchpoint%ROWTYPE; +-- use_active_date TEXT; +-- age_protect_date TIMESTAMP WITH TIME ZONE; +-- hold_count INT; +-- hold_transit_prox INT; +-- frozen_hold_count INT; +-- context_org_list INT[]; +-- done BOOL := FALSE; +-- hold_penalty TEXT; +-- v_pickup_ou ALIAS FOR pickup_ou; +-- v_request_ou ALIAS FOR request_ou; +--BEGIN +-- SELECT INTO user_object * FROM actor.usr WHERE id = match_user; +-- SELECT INTO context_org_list ARRAY_AGG(id) FROM actor.org_unit_full_path( v_pickup_ou ); +-- +-- result.success := TRUE; +-- +-- -- The HOLD penalty block only applies to new holds. +-- -- The CAPTURE penalty block applies to existing holds. +-- hold_penalty := 'HOLD'; +-- IF retargetting THEN +-- hold_penalty := 'CAPTURE'; +-- END IF; +-- +-- -- Fail if we couldn't find a user +-- IF user_object.id IS NULL THEN +-- result.fail_part := 'no_user'; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- RETURN; +-- END IF; +-- +-- SELECT INTO item_object * FROM asset.copy WHERE id = match_item; +-- +-- -- Fail if we couldn't find a copy +-- IF item_object.id IS NULL THEN +-- result.fail_part := 'no_item'; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- RETURN; +-- END IF; +-- +-- SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(v_pickup_ou, v_request_ou, match_item, match_user, match_requestor); +-- result.matchpoint := matchpoint_id; +-- +-- SELECT INTO ou_skip * FROM actor.org_unit_setting WHERE name = 'circ.holds.target_skip_me' AND org_unit = item_object.circ_lib; +-- +-- -- Fail if the circ_lib for the item has circ.holds.target_skip_me set to true +-- IF ou_skip.id IS NOT NULL AND ou_skip.value = 'true' THEN +-- result.fail_part := 'circ.holds.target_skip_me'; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- RETURN; +-- END IF; +-- +-- -- Fail if user is barred +-- IF user_object.barred IS TRUE THEN +-- result.fail_part := 'actor.usr.barred'; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- RETURN; +-- END IF; +-- +-- SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number; +-- SELECT INTO item_status_object * FROM config.copy_status WHERE id = item_object.status; +-- SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location; +-- +-- -- Fail if we couldn't find any matchpoint (requires a default) +-- IF matchpoint_id IS NULL THEN +-- result.fail_part := 'no_matchpoint'; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- RETURN; +-- END IF; +-- +-- SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id; +-- +-- IF hold_test.holdable IS FALSE THEN +-- result.fail_part := 'config.hold_matrix_test.holdable'; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- END IF; +-- +-- IF item_object.holdable IS FALSE THEN +-- result.fail_part := 'item.holdable'; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- END IF; +-- +-- IF item_status_object.holdable IS FALSE THEN +-- result.fail_part := 'status.holdable'; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- END IF; +-- +-- IF item_location_object.holdable IS FALSE THEN +-- result.fail_part := 'location.holdable'; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- END IF; +-- +-- IF hold_test.transit_range IS NOT NULL THEN +-- SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range; +-- IF hold_test.distance_is_from_owner THEN +-- SELECT INTO transit_source ou.* FROM actor.org_unit ou JOIN asset.call_number cn ON (cn.owning_lib = ou.id) WHERE cn.id = item_object.call_number; +-- ELSE +-- SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib; +-- END IF; +-- +-- PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = v_pickup_ou; +-- +-- IF NOT FOUND THEN +-- result.fail_part := 'transit_range'; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- END IF; +-- END IF; +-- +-- FOR standing_penalty IN +-- SELECT DISTINCT csp.* +-- FROM actor.usr_standing_penalty usp +-- JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty) +-- WHERE usr = match_user +-- AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) ) +-- AND (usp.stop_date IS NULL or usp.stop_date > NOW()) +-- AND csp.block_list LIKE '%' || hold_penalty || '%' LOOP +-- +-- result.fail_part := standing_penalty.name; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- END LOOP; +-- +-- IF hold_test.stop_blocked_user IS TRUE THEN +-- FOR standing_penalty IN +-- SELECT DISTINCT csp.* +-- FROM actor.usr_standing_penalty usp +-- JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty) +-- WHERE usr = match_user +-- AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) ) +-- AND (usp.stop_date IS NULL or usp.stop_date > NOW()) +-- AND csp.block_list LIKE '%CIRC%' LOOP +-- +-- result.fail_part := standing_penalty.name; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- END LOOP; +-- END IF; +-- +-- IF hold_test.max_holds IS NOT NULL AND NOT retargetting THEN +-- SELECT INTO hold_count COUNT(*) +-- FROM action.hold_request +-- WHERE usr = match_user +-- AND fulfillment_time IS NULL +-- AND cancel_time IS NULL +-- AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END; +-- +-- IF hold_count >= hold_test.max_holds THEN +-- result.fail_part := 'config.hold_matrix_test.max_holds'; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- END IF; +-- END IF; +-- +-- IF item_object.age_protect IS NOT NULL THEN +-- SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect; +-- IF hold_test.distance_is_from_owner THEN +-- SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_cn_object.owning_lib); +-- ELSE +-- SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_object.circ_lib); +-- END IF; +-- IF use_active_date = 'true' THEN +-- age_protect_date := COALESCE(item_object.active_date, NOW()); +-- ELSE +-- age_protect_date := item_object.create_date; +-- END IF; +-- IF age_protect_date + age_protect_object.age > NOW() THEN +-- IF hold_test.distance_is_from_owner THEN +-- SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number; +-- SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_cn_object.owning_lib AND to_org = v_pickup_ou; +-- ELSE +-- SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_object.circ_lib AND to_org = v_pickup_ou; +-- END IF; +-- +-- IF hold_transit_prox > age_protect_object.prox THEN +-- result.fail_part := 'config.rule_age_hold_protect.prox'; +-- result.success := FALSE; +-- done := TRUE; +-- RETURN NEXT result; +-- END IF; +-- END IF; +-- END IF; +-- +-- IF NOT done THEN +-- RETURN NEXT result; +-- END IF; +-- +-- RETURN; +--END; +--$func$ LANGUAGE plpgsql;