]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/upgrade/XXXX.fix_indb_hold_permit.sql
TPac messages for item/location/status not holdable
[working/Evergreen.git] / Open-ILS / src / sql / Pg / upgrade / XXXX.fix_indb_hold_permit.sql
1 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$
2 DECLARE
3     matchpoint_id        INT;
4     user_object        actor.usr%ROWTYPE;
5     age_protect_object    config.rule_age_hold_protect%ROWTYPE;
6     standing_penalty    config.standing_penalty%ROWTYPE;
7     transit_range_ou_type    actor.org_unit_type%ROWTYPE;
8     transit_source        actor.org_unit%ROWTYPE;
9     item_object        asset.copy%ROWTYPE;
10     item_cn_object     asset.call_number%ROWTYPE;
11     item_status_object  config.copy_status%ROWTYPE;
12     item_location_object    asset.copy_location%ROWTYPE;
13     ou_skip              actor.org_unit_setting%ROWTYPE;
14     result            action.matrix_test_result;
15     hold_test        config.hold_matrix_matchpoint%ROWTYPE;
16     use_active_date   TEXT;
17     age_protect_date  TIMESTAMP WITH TIME ZONE;
18     hold_count        INT;
19     hold_transit_prox    INT;
20     frozen_hold_count    INT;
21     context_org_list    INT[];
22     done            BOOL := FALSE;
23 BEGIN
24     SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
25     SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( pickup_ou );
26
27     result.success := TRUE;
28
29     -- Fail if we couldn't find a user
30     IF user_object.id IS NULL THEN
31         result.fail_part := 'no_user';
32         result.success := FALSE;
33         done := TRUE;
34         RETURN NEXT result;
35         RETURN;
36     END IF;
37
38     SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
39
40     -- Fail if we couldn't find a copy
41     IF item_object.id IS NULL THEN
42         result.fail_part := 'no_item';
43         result.success := FALSE;
44         done := TRUE;
45         RETURN NEXT result;
46         RETURN;
47     END IF;
48
49     SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(pickup_ou, request_ou, match_item, match_user, match_requestor);
50     result.matchpoint := matchpoint_id;
51
52     SELECT INTO ou_skip * FROM actor.org_unit_setting WHERE name = 'circ.holds.target_skip_me' AND org_unit = item_object.circ_lib;
53
54     -- Fail if the circ_lib for the item has circ.holds.target_skip_me set to true
55     IF ou_skip.id IS NOT NULL AND ou_skip.value = 'true' THEN
56         result.fail_part := 'circ.holds.target_skip_me';
57         result.success := FALSE;
58         done := TRUE;
59         RETURN NEXT result;
60         RETURN;
61     END IF;
62
63     -- Fail if user is barred
64     IF user_object.barred IS TRUE THEN
65         result.fail_part := 'actor.usr.barred';
66         result.success := FALSE;
67         done := TRUE;
68         RETURN NEXT result;
69         RETURN;
70     END IF;
71
72     SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
73     SELECT INTO item_status_object * FROM config.copy_status WHERE id = item_object.status;
74     SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location;
75
76     -- Fail if we couldn't find any matchpoint (requires a default)
77     IF matchpoint_id IS NULL THEN
78         result.fail_part := 'no_matchpoint';
79         result.success := FALSE;
80         done := TRUE;
81         RETURN NEXT result;
82         RETURN;
83     END IF;
84
85     SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id;
86
87     IF hold_test.holdable IS FALSE THEN
88         result.fail_part := 'config.hold_matrix_test.holdable';
89         result.success := FALSE;
90         done := TRUE;
91         RETURN NEXT result;
92     END IF;
93
94     IF item_object.holdable IS FALSE THEN
95         result.fail_part := 'item.holdable';
96         result.success := FALSE;
97         done := TRUE;
98         RETURN NEXT result;
99     END IF;
100
101     IF item_status_object.holdable IS FALSE THEN
102         result.fail_part := 'status.holdable';
103         result.success := FALSE;
104         done := TRUE;
105         RETURN NEXT result;
106     END IF;
107
108     IF item_location_object.holdable IS FALSE THEN
109         result.fail_part := 'location.holdable';
110         result.success := FALSE;
111         done := TRUE;
112         RETURN NEXT result;
113     END IF;
114
115     IF hold_test.transit_range IS NOT NULL THEN
116         SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range;
117         IF hold_test.distance_is_from_owner THEN
118             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;
119         ELSE
120             SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib;
121         END IF;
122
123         PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = pickup_ou;
124
125         IF NOT FOUND THEN
126             result.fail_part := 'transit_range';
127             result.success := FALSE;
128             done := TRUE;
129             RETURN NEXT result;
130         END IF;
131     END IF;
132  
133     FOR standing_penalty IN
134         SELECT  DISTINCT csp.*
135           FROM  actor.usr_standing_penalty usp
136                 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
137           WHERE usr = match_user
138                 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
139                 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
140                 AND csp.block_list LIKE '%HOLD%' LOOP
141
142         result.fail_part := standing_penalty.name;
143         result.success := FALSE;
144         done := TRUE;
145         RETURN NEXT result;
146     END LOOP;
147
148     IF hold_test.stop_blocked_user IS TRUE THEN
149         FOR standing_penalty IN
150             SELECT  DISTINCT csp.*
151               FROM  actor.usr_standing_penalty usp
152                     JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
153               WHERE usr = match_user
154                     AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
155                     AND (usp.stop_date IS NULL or usp.stop_date > NOW())
156                     AND csp.block_list LIKE '%CIRC%' LOOP
157     
158             result.fail_part := standing_penalty.name;
159             result.success := FALSE;
160             done := TRUE;
161             RETURN NEXT result;
162         END LOOP;
163     END IF;
164
165     IF hold_test.max_holds IS NOT NULL AND NOT retargetting THEN
166         SELECT    INTO hold_count COUNT(*)
167           FROM    action.hold_request
168           WHERE    usr = match_user
169             AND fulfillment_time IS NULL
170             AND cancel_time IS NULL
171             AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
172
173         IF hold_count >= hold_test.max_holds THEN
174             result.fail_part := 'config.hold_matrix_test.max_holds';
175             result.success := FALSE;
176             done := TRUE;
177             RETURN NEXT result;
178         END IF;
179     END IF;
180
181     IF item_object.age_protect IS NOT NULL THEN
182         SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect;
183         IF hold_test.distance_is_from_owner THEN
184             SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_cn_object.owning_lib);
185         ELSE
186             SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_object.circ_lib);
187         END IF;
188         IF use_active_date = 'true' THEN
189             age_protect_date := COALESCE(item_object.active_date, NOW());
190         ELSE
191             age_protect_date := item_object.create_date;
192         END IF;
193         IF age_protect_date + age_protect_object.age > NOW() THEN
194             IF hold_test.distance_is_from_owner THEN
195                 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
196                 SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_cn_object.owning_lib AND to_org = pickup_ou;
197             ELSE
198                 SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_object.circ_lib AND to_org = pickup_ou;
199             END IF;
200
201             IF hold_transit_prox > age_protect_object.prox THEN
202                 result.fail_part := 'config.rule_age_hold_protect.prox';
203                 result.success := FALSE;
204                 done := TRUE;
205                 RETURN NEXT result;
206             END IF;
207         END IF;
208     END IF;
209
210     IF NOT done THEN
211         RETURN NEXT result;
212     END IF;
213
214     RETURN;
215 END;
216 $func$ LANGUAGE plpgsql;