]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/upgrade/XXXX.penalty_fixes.sql
Fix some standing penalty issues
[working/Evergreen.git] / Open-ILS / src / sql / Pg / upgrade / XXXX.penalty_fixes.sql
1 CREATE OR REPLACE FUNCTION action.item_user_circ_test( circ_ou INT, match_item BIGINT, match_user INT, renewal BOOL ) RETURNS SETOF action.circ_matrix_test_result AS $func$
2 DECLARE
3     user_object             actor.usr%ROWTYPE;
4     standing_penalty        config.standing_penalty%ROWTYPE;
5     item_object             asset.copy%ROWTYPE;
6     item_status_object      config.copy_status%ROWTYPE;
7     item_location_object    asset.copy_location%ROWTYPE;
8     result                  action.circ_matrix_test_result;
9     circ_test               action.found_circ_matrix_matchpoint;
10     circ_matchpoint         config.circ_matrix_matchpoint%ROWTYPE;
11     out_by_circ_mod         config.circ_matrix_circ_mod_test%ROWTYPE;
12     circ_mod_map            config.circ_matrix_circ_mod_test_map%ROWTYPE;
13     hold_ratio              action.hold_stats%ROWTYPE;
14     penalty_type            TEXT;
15     items_out               INT;
16     context_org_list        INT[];
17     done                    BOOL := FALSE;
18 BEGIN
19     -- Assume success unless we hit a failure condition
20     result.success := TRUE;
21
22     -- Need user info to look up matchpoints
23     SELECT INTO user_object * FROM actor.usr WHERE id = match_user AND NOT deleted;
24
25     -- (Insta)Fail if we couldn't find the user
26     IF user_object.id IS NULL THEN
27         result.fail_part := 'no_user';
28         result.success := FALSE;
29         done := TRUE;
30         RETURN NEXT result;
31         RETURN;
32     END IF;
33
34     -- Need item info to look up matchpoints
35     SELECT INTO item_object * FROM asset.copy WHERE id = match_item AND NOT deleted;
36
37     -- (Insta)Fail if we couldn't find the item 
38     IF item_object.id IS NULL THEN
39         result.fail_part := 'no_item';
40         result.success := FALSE;
41         done := TRUE;
42         RETURN NEXT result;
43         RETURN;
44     END IF;
45
46     SELECT INTO circ_test * FROM action.find_circ_matrix_matchpoint(circ_ou, item_object, user_object, renewal);
47
48     circ_matchpoint             := circ_test.matchpoint;
49     result.matchpoint           := circ_matchpoint.id;
50     result.circulate            := circ_matchpoint.circulate;
51     result.duration_rule        := circ_matchpoint.duration_rule;
52     result.recurring_fine_rule  := circ_matchpoint.recurring_fine_rule;
53     result.max_fine_rule        := circ_matchpoint.max_fine_rule;
54     result.hard_due_date        := circ_matchpoint.hard_due_date;
55     result.renewals             := circ_matchpoint.renewals;
56     result.grace_period         := circ_matchpoint.grace_period;
57     result.buildrows            := circ_test.buildrows;
58
59     -- (Insta)Fail if we couldn't find a matchpoint
60     IF circ_test.success = false THEN
61         result.fail_part := 'no_matchpoint';
62         result.success := FALSE;
63         done := TRUE;
64         RETURN NEXT result;
65         RETURN;
66     END IF;
67
68     -- All failures before this point are non-recoverable
69     -- Below this point are possibly overridable failures
70
71     -- Fail if the user is barred
72     IF user_object.barred IS TRUE THEN
73         result.fail_part := 'actor.usr.barred';
74         result.success := FALSE;
75         done := TRUE;
76         RETURN NEXT result;
77     END IF;
78
79     -- Fail if the item can't circulate
80     IF item_object.circulate IS FALSE THEN
81         result.fail_part := 'asset.copy.circulate';
82         result.success := FALSE;
83         done := TRUE;
84         RETURN NEXT result;
85     END IF;
86
87     -- Fail if the item isn't in a circulateable status on a non-renewal
88     IF NOT renewal AND item_object.status NOT IN ( 0, 7, 8 ) THEN 
89         result.fail_part := 'asset.copy.status';
90         result.success := FALSE;
91         done := TRUE;
92         RETURN NEXT result;
93     -- Alternately, fail if the item isn't checked out on a renewal
94     ELSIF renewal AND item_object.status <> 1 THEN
95         result.fail_part := 'asset.copy.status';
96         result.success := FALSE;
97         done := TRUE;
98         RETURN NEXT result;
99     END IF;
100
101     -- Fail if the item can't circulate because of the shelving location
102     SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location;
103     IF item_location_object.circulate IS FALSE THEN
104         result.fail_part := 'asset.copy_location.circulate';
105         result.success := FALSE;
106         done := TRUE;
107         RETURN NEXT result;
108     END IF;
109
110     -- Use Circ OU for penalties and such
111     SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( circ_ou );
112
113     IF renewal THEN
114         penalty_type = '%RENEW%';
115     ELSE
116         penalty_type = '%CIRC%';
117     END IF;
118
119     FOR standing_penalty IN
120         SELECT  DISTINCT csp.*
121           FROM  actor.usr_standing_penalty usp
122                 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
123           WHERE usr = match_user
124                 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
125                 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
126                 AND csp.block_list LIKE penalty_type LOOP
127
128         result.fail_part := standing_penalty.name;
129         result.success := FALSE;
130         done := TRUE;
131         RETURN NEXT result;
132     END LOOP;
133
134     -- Fail if the test is set to hard non-circulating
135     IF circ_matchpoint.circulate IS FALSE THEN
136         result.fail_part := 'config.circ_matrix_test.circulate';
137         result.success := FALSE;
138         done := TRUE;
139         RETURN NEXT result;
140     END IF;
141
142     -- Fail if the total copy-hold ratio is too low
143     IF circ_matchpoint.total_copy_hold_ratio IS NOT NULL THEN
144         SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item);
145         IF hold_ratio.total_copy_ratio IS NOT NULL AND hold_ratio.total_copy_ratio < circ_matchpoint.total_copy_hold_ratio THEN
146             result.fail_part := 'config.circ_matrix_test.total_copy_hold_ratio';
147             result.success := FALSE;
148             done := TRUE;
149             RETURN NEXT result;
150         END IF;
151     END IF;
152
153     -- Fail if the available copy-hold ratio is too low
154     IF circ_matchpoint.available_copy_hold_ratio IS NOT NULL THEN
155         IF hold_ratio.hold_count IS NULL THEN
156             SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item);
157         END IF;
158         IF hold_ratio.available_copy_ratio IS NOT NULL AND hold_ratio.available_copy_ratio < circ_matchpoint.available_copy_hold_ratio THEN
159             result.fail_part := 'config.circ_matrix_test.available_copy_hold_ratio';
160             result.success := FALSE;
161             done := TRUE;
162             RETURN NEXT result;
163         END IF;
164     END IF;
165
166     -- Fail if the user has too many items with specific circ_modifiers checked out
167     FOR out_by_circ_mod IN SELECT * FROM config.circ_matrix_circ_mod_test WHERE matchpoint = circ_matchpoint.id LOOP
168         SELECT  INTO items_out COUNT(*)
169           FROM  action.circulation circ
170             JOIN asset.copy cp ON (cp.id = circ.target_copy)
171           WHERE circ.usr = match_user
172                AND circ.circ_lib IN ( SELECT * FROM unnest(context_org_list) )
173             AND circ.checkin_time IS NULL
174             AND (circ.stop_fines IN ('MAXFINES','LONGOVERDUE') OR circ.stop_fines IS NULL)
175             AND cp.circ_modifier IN (SELECT circ_mod FROM config.circ_matrix_circ_mod_test_map WHERE circ_mod_test = out_by_circ_mod.id);
176         IF items_out >= out_by_circ_mod.items_out THEN
177             result.fail_part := 'config.circ_matrix_circ_mod_test';
178             result.success := FALSE;
179             done := TRUE;
180             RETURN NEXT result;
181         END IF;
182     END LOOP;
183
184     -- If we passed everything, return the successful matchpoint
185     IF NOT done THEN
186         RETURN NEXT result;
187     END IF;
188
189     RETURN;
190 END;
191 $func$ LANGUAGE plpgsql;