2 * Copyright (C) 2004-2008 Georgia Public Library Service
3 * Copyright (C) 2007-2008 Equinox Software, Inc.
4 * Mike Rylander <miker@esilibrary.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
18 DROP SCHEMA action CASCADE;
24 CREATE TABLE action.in_house_use (
25 id SERIAL PRIMARY KEY,
26 item BIGINT NOT NULL REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
27 staff INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
28 org_unit INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
29 use_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
31 CREATE INDEX action_in_house_use_staff_idx ON action.in_house_use ( staff );
33 CREATE TABLE action.non_cataloged_circulation (
34 id SERIAL PRIMARY KEY,
35 patron INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
36 staff INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
37 circ_lib INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
38 item_type INT NOT NULL REFERENCES config.non_cataloged_type (id) DEFERRABLE INITIALLY DEFERRED,
39 circ_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
41 CREATE INDEX action_non_cat_circ_patron_idx ON action.non_cataloged_circulation ( patron );
42 CREATE INDEX action_non_cat_circ_staff_idx ON action.non_cataloged_circulation ( staff );
44 CREATE TABLE action.non_cat_in_house_use (
45 id SERIAL PRIMARY KEY,
46 item_type BIGINT NOT NULL REFERENCES config.non_cataloged_type(id) DEFERRABLE INITIALLY DEFERRED,
47 staff INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
48 org_unit INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
49 use_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
51 CREATE INDEX non_cat_in_house_use_staff_idx ON action.non_cat_in_house_use ( staff );
53 CREATE TABLE action.survey (
54 id SERIAL PRIMARY KEY,
55 owner INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
56 start_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
57 end_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() + '10 years'::INTERVAL,
58 usr_summary BOOL NOT NULL DEFAULT FALSE,
59 opac BOOL NOT NULL DEFAULT FALSE,
60 poll BOOL NOT NULL DEFAULT FALSE,
61 required BOOL NOT NULL DEFAULT FALSE,
63 description TEXT NOT NULL
65 CREATE UNIQUE INDEX asv_once_per_owner_idx ON action.survey (owner,name);
67 CREATE TABLE action.survey_question (
68 id SERIAL PRIMARY KEY,
69 survey INT NOT NULL REFERENCES action.survey DEFERRABLE INITIALLY DEFERRED,
70 question TEXT NOT NULL
73 CREATE TABLE action.survey_answer (
74 id SERIAL PRIMARY KEY,
75 question INT NOT NULL REFERENCES action.survey_question DEFERRABLE INITIALLY DEFERRED,
79 CREATE SEQUENCE action.survey_response_group_id_seq;
81 CREATE TABLE action.survey_response (
82 id BIGSERIAL PRIMARY KEY,
83 response_group_id INT,
84 usr INT, -- REFERENCES actor.usr
85 survey INT NOT NULL REFERENCES action.survey DEFERRABLE INITIALLY DEFERRED,
86 question INT NOT NULL REFERENCES action.survey_question DEFERRABLE INITIALLY DEFERRED,
87 answer INT NOT NULL REFERENCES action.survey_answer DEFERRABLE INITIALLY DEFERRED,
88 answer_date TIMESTAMP WITH TIME ZONE,
89 effective_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
91 CREATE INDEX action_survey_response_usr_idx ON action.survey_response ( usr );
93 CREATE OR REPLACE FUNCTION action.survey_response_answer_date_fixup () RETURNS TRIGGER AS '
95 NEW.answer_date := NOW();
99 CREATE TRIGGER action_survey_response_answer_date_fixup_tgr
100 BEFORE INSERT ON action.survey_response
102 EXECUTE PROCEDURE action.survey_response_answer_date_fixup ();
105 CREATE TABLE action.circulation (
106 target_copy BIGINT NOT NULL, -- asset.copy.id
107 circ_lib INT NOT NULL, -- actor.org_unit.id
108 circ_staff INT NOT NULL, -- actor.usr.id
109 checkin_staff INT, -- actor.usr.id
110 checkin_lib INT, -- actor.org_unit.id
111 renewal_remaining INT NOT NULL, -- derived from "circ duration" rule
112 due_date TIMESTAMP WITH TIME ZONE,
113 stop_fines_time TIMESTAMP WITH TIME ZONE,
114 checkin_time TIMESTAMP WITH TIME ZONE,
115 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
116 duration INTERVAL, -- derived from "circ duration" rule
117 fine_interval INTERVAL NOT NULL DEFAULT '1 day'::INTERVAL, -- derived from "circ fine" rule
118 recuring_fine NUMERIC(6,2), -- derived from "circ fine" rule
119 max_fine NUMERIC(6,2), -- derived from "max fine" rule
120 phone_renewal BOOL NOT NULL DEFAULT FALSE,
121 desk_renewal BOOL NOT NULL DEFAULT FALSE,
122 opac_renewal BOOL NOT NULL DEFAULT FALSE,
123 duration_rule TEXT NOT NULL, -- name of "circ duration" rule
124 recuring_fine_rule TEXT NOT NULL, -- name of "circ fine" rule
125 max_fine_rule TEXT NOT NULL, -- name of "max fine" rule
126 stop_fines TEXT CHECK (stop_fines IN (
127 'CHECKIN','CLAIMSRETURNED','LOST','MAXFINES','RENEW','LONGOVERDUE','CLAIMSNEVERCHECKEDOUT')),
128 workstation INT REFERENCES actor.workstation(id)
130 DEFERRABLE INITIALLY DEFERRED,
131 checkin_workstation INT REFERENCES actor.workstation(id)
133 DEFERRABLE INITIALLY DEFERRED,
134 checkin_scan_time TIMESTAMP WITH TIME ZONE
135 ) INHERITS (money.billable_xact);
136 ALTER TABLE action.circulation ADD PRIMARY KEY (id);
137 ALTER TABLE action.circulation
138 ADD COLUMN parent_circ BIGINT
139 REFERENCES action.circulation( id )
140 DEFERRABLE INITIALLY DEFERRED;
141 CREATE INDEX circ_open_xacts_idx ON action.circulation (usr) WHERE xact_finish IS NULL;
142 CREATE INDEX circ_outstanding_idx ON action.circulation (usr) WHERE checkin_time IS NULL;
143 CREATE INDEX circ_checkin_time ON "action".circulation (checkin_time) WHERE checkin_time IS NOT NULL;
144 CREATE INDEX circ_circ_lib_idx ON "action".circulation (circ_lib);
145 CREATE INDEX circ_open_date_idx ON "action".circulation (xact_start) WHERE xact_finish IS NULL;
146 CREATE INDEX circ_all_usr_idx ON action.circulation ( usr );
147 CREATE INDEX circ_circ_staff_idx ON action.circulation ( circ_staff );
148 CREATE INDEX circ_checkin_staff_idx ON action.circulation ( checkin_staff );
149 CREATE UNIQUE INDEX circ_parent_idx ON action.circulation ( parent_circ ) WHERE parent_circ IS NOT NULL;
152 CREATE TRIGGER mat_summary_create_tgr AFTER INSERT ON action.circulation FOR EACH ROW EXECUTE PROCEDURE money.mat_summary_create ('circulation');
153 CREATE TRIGGER mat_summary_change_tgr AFTER UPDATE ON action.circulation FOR EACH ROW EXECUTE PROCEDURE money.mat_summary_update ();
154 CREATE TRIGGER mat_summary_remove_tgr AFTER DELETE ON action.circulation FOR EACH ROW EXECUTE PROCEDURE money.mat_summary_delete ();
157 CREATE TABLE action.aged_circulation (
159 usr_home_ou INT NOT NULL,
160 usr_profile INT NOT NULL,
162 copy_call_number INT NOT NULL,
163 copy_location INT NOT NULL,
164 copy_owning_lib INT NOT NULL,
165 copy_circ_lib INT NOT NULL,
166 copy_bib_record BIGINT NOT NULL,
167 LIKE action.circulation
170 ALTER TABLE action.aged_circulation ADD PRIMARY KEY (id);
171 ALTER TABLE action.aged_circulation DROP COLUMN usr;
172 CREATE INDEX aged_circ_circ_lib_idx ON "action".aged_circulation (circ_lib);
173 CREATE INDEX aged_circ_start_idx ON "action".aged_circulation (xact_start);
174 CREATE INDEX aged_circ_copy_circ_lib_idx ON "action".aged_circulation (copy_circ_lib);
175 CREATE INDEX aged_circ_copy_owning_lib_idx ON "action".aged_circulation (copy_owning_lib);
176 CREATE INDEX aged_circ_copy_location_idx ON "action".aged_circulation (copy_location);
178 CREATE OR REPLACE VIEW action.all_circulation AS
179 SELECT id,usr_post_code, usr_home_ou, usr_profile, usr_birth_year, copy_call_number, copy_location,
180 copy_owning_lib, copy_circ_lib, copy_bib_record, xact_start, xact_finish, target_copy,
181 circ_lib, circ_staff, checkin_staff, checkin_lib, renewal_remaining, due_date,
182 stop_fines_time, checkin_time, create_time, duration, fine_interval, recuring_fine,
183 max_fine, phone_renewal, desk_renewal, opac_renewal, duration_rule, recuring_fine_rule,
184 max_fine_rule, stop_fines, workstation, checkin_workstation, checkin_scan_time, parent_circ
185 FROM action.aged_circulation
187 SELECT DISTINCT circ.id,COALESCE(a.post_code,b.post_code) AS usr_post_code, p.home_ou AS usr_home_ou, p.profile AS usr_profile, EXTRACT(YEAR FROM p.dob)::INT AS usr_birth_year,
188 cp.call_number AS copy_call_number, cp.location AS copy_location, cn.owning_lib AS copy_owning_lib, cp.circ_lib AS copy_circ_lib,
189 cn.record AS copy_bib_record, circ.xact_start, circ.xact_finish, circ.target_copy, circ.circ_lib, circ.circ_staff, circ.checkin_staff,
190 circ.checkin_lib, circ.renewal_remaining, circ.due_date, circ.stop_fines_time, circ.checkin_time, circ.create_time, circ.duration,
191 circ.fine_interval, circ.recuring_fine, circ.max_fine, circ.phone_renewal, circ.desk_renewal, circ.opac_renewal, circ.duration_rule,
192 circ.recuring_fine_rule, circ.max_fine_rule, circ.stop_fines, circ.workstation, circ.checkin_workstation, circ.checkin_scan_time,
194 FROM action.circulation circ
195 JOIN asset.copy cp ON (circ.target_copy = cp.id)
196 JOIN asset.call_number cn ON (cp.call_number = cn.id)
197 JOIN actor.usr p ON (circ.usr = p.id)
198 LEFT JOIN actor.usr_address a ON (p.mailing_address = a.id)
199 LEFT JOIN actor.usr_address b ON (p.billing_address = a.id);
201 CREATE OR REPLACE FUNCTION action.age_circ_on_delete () RETURNS TRIGGER AS $$
206 -- If there are any renewals for this circulation, don't archive or delete
207 -- it yet. We'll do so later, when we archive and delete the renewals.
209 SELECT 'Y' INTO found
210 FROM action.circulation
211 WHERE parent_circ = OLD.id
215 RETURN NULL; -- don't delete
218 -- Archive a copy of the old row to action.aged_circulation
220 INSERT INTO action.aged_circulation
221 (id,usr_post_code, usr_home_ou, usr_profile, usr_birth_year, copy_call_number, copy_location,
222 copy_owning_lib, copy_circ_lib, copy_bib_record, xact_start, xact_finish, target_copy,
223 circ_lib, circ_staff, checkin_staff, checkin_lib, renewal_remaining, due_date,
224 stop_fines_time, checkin_time, create_time, duration, fine_interval, recuring_fine,
225 max_fine, phone_renewal, desk_renewal, opac_renewal, duration_rule, recuring_fine_rule,
226 max_fine_rule, stop_fines, workstation, checkin_workstation, checkin_scan_time, parent_circ)
228 id,usr_post_code, usr_home_ou, usr_profile, usr_birth_year, copy_call_number, copy_location,
229 copy_owning_lib, copy_circ_lib, copy_bib_record, xact_start, xact_finish, target_copy,
230 circ_lib, circ_staff, checkin_staff, checkin_lib, renewal_remaining, due_date,
231 stop_fines_time, checkin_time, create_time, duration, fine_interval, recuring_fine,
232 max_fine, phone_renewal, desk_renewal, opac_renewal, duration_rule, recuring_fine_rule,
233 max_fine_rule, stop_fines, workstation, checkin_workstation, checkin_scan_time, parent_circ
234 FROM action.all_circulation WHERE id = OLD.id;
238 $$ LANGUAGE 'plpgsql';
240 CREATE TRIGGER action_circulation_aging_tgr
241 BEFORE DELETE ON action.circulation
243 EXECUTE PROCEDURE action.age_circ_on_delete ();
246 CREATE OR REPLACE FUNCTION action.age_parent_circ_on_delete () RETURNS TRIGGER AS $$
249 -- Having deleted a renewal, we can delete the original circulation (or a previous
250 -- renewal, if that's what parent_circ is pointing to). That deletion will trigger
251 -- deletion of any prior parents, etc. recursively.
253 IF OLD.parent_circ IS NOT NULL THEN
254 DELETE FROM action.circulation
255 WHERE id = OLD.parent_circ;
260 $$ LANGUAGE 'plpgsql';
262 CREATE TRIGGER age_parent_circ
263 AFTER DELETE ON action.circulation
265 EXECUTE PROCEDURE action.age_parent_circ_on_delete ();
268 CREATE OR REPLACE VIEW action.open_circulation AS
270 FROM action.circulation
271 WHERE checkin_time IS NULL
275 CREATE OR REPLACE VIEW action.billable_circulations AS
277 FROM action.circulation
278 WHERE xact_finish IS NULL;
280 CREATE VIEW stats.fleshed_circulation AS
282 CAST(c.xact_start AS DATE) AS start_date_day,
283 CAST(c.xact_finish AS DATE) AS finish_date_day,
284 DATE_TRUNC('hour', c.xact_start) AS start_date_hour,
285 DATE_TRUNC('hour', c.xact_finish) AS finish_date_hour,
286 cp.call_number_label,
291 FROM "action".circulation c
292 JOIN stats.fleshed_copy cp ON (cp.id = c.target_copy);
295 CREATE OR REPLACE FUNCTION action.circulation_claims_returned () RETURNS TRIGGER AS $$
297 IF OLD.stop_fines IS NULL OR OLD.stop_fines <> NEW.stop_fines THEN
298 IF NEW.stop_fines = 'CLAIMSRETURNED' THEN
299 UPDATE actor.usr SET claims_returned_count = claims_returned_count + 1 WHERE id = NEW.usr;
301 IF NEW.stop_fines = 'LOST' THEN
302 UPDATE asset.copy SET status = 3 WHERE id = NEW.target_copy;
307 $$ LANGUAGE 'plpgsql';
308 CREATE TRIGGER action_circulation_stop_fines_tgr
309 BEFORE UPDATE ON action.circulation
311 EXECUTE PROCEDURE action.circulation_claims_returned ();
313 CREATE TABLE action.hold_request_cancel_cause (
314 id SERIAL PRIMARY KEY,
317 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (1,'Untargeted expiration');
318 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (2,'Hold Shelf expiration');
319 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (3,'Patron via phone');
320 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (4,'Patron in person');
321 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (5,'Staff forced');
322 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (6,'Patron via OPAC');
323 SELECT SETVAL('action.hold_request_cancel_cause_id_seq', 100);
325 CREATE TABLE action.hold_request (
326 id SERIAL PRIMARY KEY,
327 request_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
328 capture_time TIMESTAMP WITH TIME ZONE,
329 fulfillment_time TIMESTAMP WITH TIME ZONE,
330 checkin_time TIMESTAMP WITH TIME ZONE,
331 return_time TIMESTAMP WITH TIME ZONE,
332 prev_check_time TIMESTAMP WITH TIME ZONE,
333 expire_time TIMESTAMP WITH TIME ZONE,
334 cancel_time TIMESTAMP WITH TIME ZONE,
335 cancel_cause INT REFERENCES action.hold_request_cancel_cause (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
337 target BIGINT NOT NULL, -- see hold_type
338 current_copy BIGINT REFERENCES asset.copy (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
339 fulfillment_staff INT REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
340 fulfillment_lib INT REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
341 request_lib INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
342 requestor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
343 usr INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
344 selection_ou INT NOT NULL,
345 selection_depth INT NOT NULL DEFAULT 0,
346 pickup_lib INT NOT NULL REFERENCES actor.org_unit DEFERRABLE INITIALLY DEFERRED,
347 hold_type TEXT NOT NULL CHECK (hold_type IN ('M','T','V','C')),
348 holdable_formats TEXT,
350 email_notify BOOL NOT NULL DEFAULT TRUE,
351 frozen BOOL NOT NULL DEFAULT FALSE,
352 thaw_date TIMESTAMP WITH TIME ZONE,
353 shelf_time TIMESTAMP WITH TIME ZONE,
357 CREATE INDEX hold_request_target_idx ON action.hold_request (target);
358 CREATE INDEX hold_request_usr_idx ON action.hold_request (usr);
359 CREATE INDEX hold_request_pickup_lib_idx ON action.hold_request (pickup_lib);
360 CREATE INDEX hold_request_current_copy_idx ON action.hold_request (current_copy);
361 CREATE INDEX hold_request_prev_check_time_idx ON action.hold_request (prev_check_time);
362 CREATE INDEX hold_request_fulfillment_staff_idx ON action.hold_request ( fulfillment_staff );
363 CREATE INDEX hold_request_requestor_idx ON action.hold_request ( requestor );
366 CREATE TABLE action.hold_request_note (
368 id BIGSERIAL PRIMARY KEY,
369 hold BIGINT NOT NULL REFERENCES action.hold_request (id)
371 DEFERRABLE INITIALLY DEFERRED,
374 slip BOOL NOT NULL DEFAULT FALSE,
375 pub BOOL NOT NULL DEFAULT FALSE,
376 staff BOOL NOT NULL DEFAULT FALSE -- created by staff
379 CREATE INDEX ahrn_hold_idx ON action.hold_request_note (hold);
382 CREATE TABLE action.hold_notification (
383 id SERIAL PRIMARY KEY,
384 hold INT NOT NULL REFERENCES action.hold_request (id)
386 DEFERRABLE INITIALLY DEFERRED,
387 notify_staff INT REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
388 notify_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
389 method TEXT NOT NULL, -- email address or phone number
392 CREATE INDEX ahn_hold_idx ON action.hold_notification (hold);
393 CREATE INDEX ahn_notify_staff_idx ON action.hold_notification ( notify_staff );
395 CREATE TABLE action.hold_copy_map (
396 id SERIAL PRIMARY KEY,
397 hold INT NOT NULL REFERENCES action.hold_request (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
398 target_copy BIGINT NOT NULL REFERENCES asset.copy (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
399 CONSTRAINT copy_once_per_hold UNIQUE (hold,target_copy)
401 -- CREATE INDEX acm_hold_idx ON action.hold_copy_map (hold);
402 CREATE INDEX acm_copy_idx ON action.hold_copy_map (target_copy);
404 CREATE TABLE action.transit_copy (
405 id SERIAL PRIMARY KEY,
406 source_send_time TIMESTAMP WITH TIME ZONE,
407 dest_recv_time TIMESTAMP WITH TIME ZONE,
408 target_copy BIGINT NOT NULL REFERENCES asset.copy (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
409 source INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
410 dest INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
411 prev_hop INT REFERENCES action.transit_copy (id) DEFERRABLE INITIALLY DEFERRED,
412 copy_status INT NOT NULL REFERENCES config.copy_status (id) DEFERRABLE INITIALLY DEFERRED,
413 persistant_transfer BOOL NOT NULL DEFAULT FALSE
415 CREATE INDEX active_transit_dest_idx ON "action".transit_copy (dest);
416 CREATE INDEX active_transit_source_idx ON "action".transit_copy (source);
417 CREATE INDEX active_transit_cp_idx ON "action".transit_copy (target_copy);
420 CREATE TABLE action.hold_transit_copy (
421 hold INT REFERENCES action.hold_request (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED
422 ) INHERITS (action.transit_copy);
423 ALTER TABLE action.hold_transit_copy ADD PRIMARY KEY (id);
424 ALTER TABLE action.hold_transit_copy ADD CONSTRAINT ahtc_tc_fkey FOREIGN KEY (target_copy) REFERENCES asset.copy (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
425 CREATE INDEX active_hold_transit_dest_idx ON "action".hold_transit_copy (dest);
426 CREATE INDEX active_hold_transit_source_idx ON "action".hold_transit_copy (source);
427 CREATE INDEX active_hold_transit_cp_idx ON "action".hold_transit_copy (target_copy);
430 CREATE TABLE action.unfulfilled_hold_list (
431 id BIGSERIAL PRIMARY KEY,
432 current_copy BIGINT NOT NULL,
434 circ_lib INT NOT NULL,
435 fail_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
437 CREATE INDEX uhr_hold_idx ON action.unfulfilled_hold_list (hold);
439 CREATE OR REPLACE VIEW action.unfulfilled_hold_loops AS
443 FROM action.unfulfilled_hold_list u
444 JOIN asset.copy c ON (c.id = u.current_copy)
447 CREATE OR REPLACE VIEW action.unfulfilled_hold_min_loop AS
450 FROM action.unfulfilled_hold_loops
453 CREATE OR REPLACE VIEW action.unfulfilled_hold_innermost_loop AS
455 FROM action.unfulfilled_hold_loops l
456 JOIN action.unfulfilled_hold_min_loop m USING (hold)
457 WHERE l.count = m.min;