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