]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/sql/Pg/040.schema.asset.sql
Merge branch 'master' of git.evergreen-ils.org:Evergreen into template-toolkit-opac
[Evergreen.git] / Open-ILS / src / sql / Pg / 040.schema.asset.sql
1 /*
2  * Copyright (C) 2004-2008  Georgia Public Library Service
3  * Copyright (C) 2007-2008  Equinox Software, Inc.
4  * Mike Rylander <miker@esilibrary.com> 
5  *
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.
10  *
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.
15  *
16  */
17
18 DROP SCHEMA IF EXISTS asset CASCADE;
19
20 BEGIN;
21
22 CREATE SCHEMA asset;
23
24 CREATE TABLE asset.copy_location (
25         id              SERIAL  PRIMARY KEY,
26         name            TEXT    NOT NULL,
27         owning_lib      INT     NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
28         holdable        BOOL    NOT NULL DEFAULT TRUE,
29         hold_verify     BOOL    NOT NULL DEFAULT FALSE,
30         opac_visible    BOOL    NOT NULL DEFAULT TRUE,
31         circulate       BOOL    NOT NULL DEFAULT TRUE,
32         label_prefix    TEXT,
33         label_suffix    TEXT,
34         CONSTRAINT acl_name_once_per_lib UNIQUE (name, owning_lib)
35 );
36
37 CREATE TABLE asset.copy_location_order
38 (
39         id              SERIAL           PRIMARY KEY,
40         location        INT              NOT NULL
41                                              REFERENCES asset.copy_location
42                                              ON DELETE CASCADE
43                                              DEFERRABLE INITIALLY DEFERRED,
44         org             INT              NOT NULL
45                                              REFERENCES actor.org_unit
46                                              ON DELETE CASCADE
47                                              DEFERRABLE INITIALLY DEFERRED,
48         position        INT              NOT NULL DEFAULT 0,
49         CONSTRAINT acplo_once_per_org UNIQUE ( location, org )
50 );
51
52 CREATE TABLE asset.copy (
53         id              BIGSERIAL                       PRIMARY KEY,
54         circ_lib        INT                             NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
55         creator         BIGINT                          NOT NULL,
56         call_number     BIGINT                          NOT NULL,
57         editor          BIGINT                          NOT NULL,
58         create_date     TIMESTAMP WITH TIME ZONE        DEFAULT NOW(),
59         edit_date       TIMESTAMP WITH TIME ZONE        DEFAULT NOW(),
60         copy_number     INT,
61         status          INT                             NOT NULL DEFAULT 0 REFERENCES config.copy_status (id) DEFERRABLE INITIALLY DEFERRED,
62         location        INT                             NOT NULL DEFAULT 1 REFERENCES asset.copy_location (id) DEFERRABLE INITIALLY DEFERRED,
63         loan_duration   INT                             NOT NULL CHECK ( loan_duration IN (1,2,3) ),
64         fine_level      INT                             NOT NULL CHECK ( fine_level IN (1,2,3) ),
65         age_protect     INT,
66         circulate       BOOL                            NOT NULL DEFAULT TRUE,
67         deposit         BOOL                            NOT NULL DEFAULT FALSE,
68         ref             BOOL                            NOT NULL DEFAULT FALSE,
69         holdable        BOOL                            NOT NULL DEFAULT TRUE,
70         deposit_amount  NUMERIC(6,2)                    NOT NULL DEFAULT 0.00,
71         price           NUMERIC(8,2),
72         barcode         TEXT                            NOT NULL,
73         circ_modifier   TEXT,
74         circ_as_type    TEXT,
75         dummy_title     TEXT,
76         dummy_author    TEXT,
77         alert_message   TEXT,
78         opac_visible    BOOL                            NOT NULL DEFAULT TRUE,
79         deleted         BOOL                            NOT NULL DEFAULT FALSE,
80         floating                BOOL                            NOT NULL DEFAULT FALSE,
81         dummy_isbn      TEXT,
82         status_changed_time TIMESTAMP WITH TIME ZONE,
83         mint_condition      BOOL        NOT NULL DEFAULT TRUE,
84     cost    NUMERIC(8,2)
85 );
86 CREATE UNIQUE INDEX copy_barcode_key ON asset.copy (barcode) WHERE deleted = FALSE OR deleted IS FALSE;
87 CREATE INDEX cp_cn_idx ON asset.copy (call_number);
88 CREATE INDEX cp_avail_cn_idx ON asset.copy (call_number);
89 CREATE INDEX cp_creator_idx  ON asset.copy ( creator );
90 CREATE INDEX cp_editor_idx   ON asset.copy ( editor );
91 CREATE INDEX cp_create_date  ON asset.copy (create_date);
92 CREATE RULE protect_copy_delete AS ON DELETE TO asset.copy DO INSTEAD UPDATE asset.copy SET deleted = TRUE WHERE OLD.id = asset.copy.id;
93
94 CREATE TABLE asset.copy_part_map (
95     id          SERIAL  PRIMARY KEY,
96     target_copy BIGINT  NOT NULL, -- points o asset.copy
97     part        INT     NOT NULL REFERENCES biblio.monograph_part (id) ON DELETE CASCADE
98 );
99 CREATE UNIQUE INDEX copy_part_map_cp_part_idx ON asset.copy_part_map (target_copy, part);
100
101 CREATE TABLE asset.opac_visible_copies (
102   id        BIGSERIAL primary key,
103   copy_id   BIGINT, -- copy id
104   record    BIGINT,
105   circ_lib  INTEGER
106 );
107 COMMENT ON TABLE asset.opac_visible_copies IS $$
108 Materialized view of copies that are visible in the OPAC, used by
109 search.query_parser_fts() to speed up OPAC visibility checks on large
110 databases.  Contents are maintained by a set of triggers.
111 $$;
112 CREATE INDEX opac_visible_copies_idx1 on asset.opac_visible_copies (record, circ_lib);
113 CREATE INDEX opac_visible_copies_copy_id_idx on asset.opac_visible_copies (copy_id);
114 CREATE UNIQUE INDEX opac_visible_copies_once_per_record_idx on asset.opac_visible_copies (copy_id, record);
115
116 CREATE OR REPLACE FUNCTION asset.acp_status_changed()
117 RETURNS TRIGGER AS $$
118 BEGIN
119     IF NEW.status <> OLD.status THEN
120         NEW.status_changed_time := now();
121     END IF;
122     RETURN NEW;
123 END;
124 $$ LANGUAGE plpgsql;
125
126 CREATE TRIGGER acp_status_changed_trig
127     BEFORE UPDATE ON asset.copy
128     FOR EACH ROW EXECUTE PROCEDURE asset.acp_status_changed();
129
130 CREATE TABLE asset.stat_cat_sip_fields (
131     field   CHAR(2) PRIMARY KEY,
132     name    TEXT    NOT NULL,
133     one_only BOOL    NOT NULL DEFAULT FALSE
134 );
135 COMMENT ON TABLE asset.stat_cat_sip_fields IS $$
136 Asset Statistical Category SIP Fields
137
138 Contains the list of valid SIP Field identifiers for
139 Statistical Categories.
140 $$;
141
142 CREATE TABLE asset.stat_cat_entry_transparency_map (
143         id                      BIGSERIAL       PRIMARY KEY,
144         stat_cat                INT             NOT NULL, -- needs ON DELETE CASCADE
145         stat_cat_entry          INT             NOT NULL, -- needs ON DELETE CASCADE
146         owning_transparency     INT             NOT NULL, -- needs ON DELETE CASCADE
147         CONSTRAINT scte_once_per_trans UNIQUE (owning_transparency,stat_cat)
148 );
149
150 CREATE TABLE asset.stat_cat (
151         id              SERIAL  PRIMARY KEY,
152         owner           INT     NOT NULL,
153         opac_visible    BOOL    NOT NULL DEFAULT FALSE,
154         name            TEXT    NOT NULL,
155         required        BOOL    NOT NULL DEFAULT FALSE,
156     sip_field   CHAR(2) REFERENCES asset.stat_cat_sip_fields(field) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
157     sip_format  TEXT,
158         CONSTRAINT sc_once_per_owner UNIQUE (owner,name)
159 );
160
161 CREATE TABLE asset.stat_cat_entry (
162         id              SERIAL  PRIMARY KEY,
163         stat_cat        INT     NOT NULL,
164         owner           INT     NOT NULL,
165         value           TEXT    NOT NULL,
166         CONSTRAINT sce_once_per_owner UNIQUE (stat_cat,owner,value)
167 );
168
169 CREATE TABLE asset.stat_cat_entry_copy_map (
170         id              BIGSERIAL       PRIMARY KEY,
171         stat_cat        INT             NOT NULL,
172         stat_cat_entry  INT             NOT NULL,
173         owning_copy     BIGINT          NOT NULL,
174         CONSTRAINT sce_once_per_copy UNIQUE (owning_copy,stat_cat)
175 );
176 CREATE INDEX scecm_owning_copy_idx ON asset.stat_cat_entry_copy_map(owning_copy);
177
178 CREATE FUNCTION asset.stat_cat_check() RETURNS trigger AS $func$
179 DECLARE
180     sipfield asset.stat_cat_sip_fields%ROWTYPE;
181     use_count INT;
182 BEGIN
183     IF NEW.sip_field IS NOT NULL THEN
184         SELECT INTO sipfield * FROM asset.stat_cat_sip_fields WHERE field = NEW.sip_field;
185         IF sipfield.one_only THEN
186             SELECT INTO use_count count(id) FROM asset.stat_cat WHERE sip_field = NEW.sip_field AND id != NEW.id;
187             IF use_count > 0 THEN
188                 RAISE EXCEPTION 'Sip field cannot be used twice';
189             END IF;
190         END IF;
191     END IF;
192     RETURN NEW;
193 END;
194 $func$ LANGUAGE PLPGSQL;
195
196 CREATE TRIGGER asset_stat_cat_sip_update_trigger
197     BEFORE INSERT OR UPDATE ON asset.stat_cat FOR EACH ROW
198     EXECUTE PROCEDURE asset.stat_cat_check();
199
200 CREATE TABLE asset.copy_note (
201         id              BIGSERIAL                       PRIMARY KEY,
202         owning_copy     BIGINT                          NOT NULL,
203         creator         BIGINT                          NOT NULL,
204         create_date     TIMESTAMP WITH TIME ZONE        DEFAULT NOW(),
205         pub             BOOL                            NOT NULL DEFAULT FALSE,
206         title           TEXT                            NOT NULL,
207         value           TEXT                            NOT NULL
208 );
209 CREATE INDEX asset_copy_note_creator_idx ON asset.copy_note ( creator );
210 CREATE INDEX asset_copy_note_owning_copy_idx ON asset.copy_note ( owning_copy );
211
212 CREATE TABLE asset.uri (
213     id  SERIAL  PRIMARY KEY,
214     href    TEXT    NOT NULL,
215     label   TEXT,
216     use_restriction TEXT,
217     active  BOOL    NOT NULL DEFAULT TRUE
218 );
219
220 CREATE TABLE asset.call_number_class (
221     id             bigserial     PRIMARY KEY,
222     name           TEXT          NOT NULL,
223     normalizer     TEXT          NOT NULL DEFAULT 'asset.normalize_generic',
224     field          TEXT          NOT NULL DEFAULT '050ab,055ab,060ab,070ab,080ab,082ab,086ab,088ab,090,092,096,098,099'
225 );
226 COMMENT ON TABLE asset.call_number_class IS $$
227 Defines the call number normalization database functions in the "normalizer"
228 column and the tag/subfield combinations to use to lookup the call number in
229 the "field" column for a given classification scheme. Tag/subfield combinations
230 are delimited by commas.
231 $$;
232
233 CREATE OR REPLACE FUNCTION asset.label_normalizer() RETURNS TRIGGER AS $func$
234 DECLARE
235     sortkey        TEXT := '';
236 BEGIN
237     sortkey := NEW.label_sortkey;
238
239     IF NEW.label_class IS NULL THEN
240             NEW.label_class := COALESCE(
241             (
242                 SELECT substring(value from E'\\d+')::integer
243                 FROM actor.org_unit_setting
244                 WHERE name = 'cat.default_classification_scheme'
245                 AND org_unit = NEW.owning_lib
246             ), 1
247         );
248     END IF;
249
250     EXECUTE 'SELECT ' || acnc.normalizer || '(' || 
251        quote_literal( NEW.label ) || ')'
252        FROM asset.call_number_class acnc
253        WHERE acnc.id = NEW.label_class
254        INTO sortkey;
255     NEW.label_sortkey = sortkey;
256     RETURN NEW;
257 END;
258 $func$ LANGUAGE PLPGSQL;
259
260 CREATE OR REPLACE FUNCTION asset.label_normalizer_generic(TEXT) RETURNS TEXT AS $func$
261     # Created after looking at the Koha C4::ClassSortRoutine::Generic module,
262     # thus could probably be considered a derived work, although nothing was
263     # directly copied - but to err on the safe side of providing attribution:
264     # Copyright (C) 2007 LibLime
265     # Copyright (C) 2011 Equinox Software, Inc (Steve Callendar)
266     # Licensed under the GPL v2 or later
267
268     use strict;
269     use warnings;
270
271     # Converts the callnumber to uppercase
272     # Strips spaces from start and end of the call number
273     # Converts anything other than letters, digits, and periods into spaces
274     # Collapses multiple spaces into a single underscore
275     my $callnum = uc(shift);
276     $callnum =~ s/^\s//g;
277     $callnum =~ s/\s$//g;
278     # NOTE: this previously used underscores, but this caused sorting issues
279     # for the "before" half of page 0 on CN browse, sorting CNs containing a
280     # decimal before "whole number" CNs
281     $callnum =~ s/[^A-Z0-9_.]/ /g;
282     $callnum =~ s/ {2,}/ /g;
283
284     return $callnum;
285 $func$ LANGUAGE PLPERLU;
286
287 CREATE OR REPLACE FUNCTION asset.label_normalizer_dewey(TEXT) RETURNS TEXT AS $func$
288     # Derived from the Koha C4::ClassSortRoutine::Dewey module
289     # Copyright (C) 2007 LibLime
290     # Licensed under the GPL v2 or later
291
292     use strict;
293     use warnings;
294
295     my $init = uc(shift);
296     $init =~ s/^\s+//;
297     $init =~ s/\s+$//;
298     $init =~ s!/!!g;
299     $init =~ s/^([\p{IsAlpha}]+)/$1 /;
300     my @tokens = split /\.|\s+/, $init;
301     my $digit_group_count = 0;
302     for (my $i = 0; $i <= $#tokens; $i++) {
303         if ($tokens[$i] =~ /^\d+$/) {
304             $digit_group_count++;
305             if (2 == $digit_group_count) {
306                 $tokens[$i] = sprintf("%-15.15s", $tokens[$i]);
307                 $tokens[$i] =~ tr/ /0/;
308             }
309         }
310     }
311     my $key = join("_", @tokens);
312     $key =~ s/[^\p{IsAlnum}_]//g;
313
314     return $key;
315
316 $func$ LANGUAGE PLPERLU;
317
318 CREATE OR REPLACE FUNCTION asset.label_normalizer_lc(TEXT) RETURNS TEXT AS $func$
319     use strict;
320     use warnings;
321
322     # Library::CallNumber::LC is currently hosted at http://code.google.com/p/library-callnumber-lc/
323     # The author hopes to upload it to CPAN some day, which would make our lives easier
324     use Library::CallNumber::LC;
325
326     my $callnum = Library::CallNumber::LC->new(shift);
327     return $callnum->normalize();
328
329 $func$ LANGUAGE PLPERLU;
330
331 INSERT INTO asset.call_number_class (name, normalizer, field) VALUES 
332     ('Generic', 'asset.label_normalizer_generic', '050ab,055ab,060ab,070ab,080ab,082ab,086ab,088ab,090,092,096,098,099'),
333     ('Dewey (DDC)', 'asset.label_normalizer_dewey', '080ab,082ab,092abef'),
334     ('Library of Congress (LC)', 'asset.label_normalizer_lc', '050ab,055ab,090abef')
335 ;
336
337 CREATE OR REPLACE FUNCTION asset.normalize_affix_sortkey () RETURNS TRIGGER AS $$
338 BEGIN
339     NEW.label_sortkey := REGEXP_REPLACE(
340         evergreen.lpad_number_substrings(
341             naco_normalize(NEW.label),
342             '0',
343             10
344         ),
345         E'\\s+',
346         '',
347         'g'
348     );
349     RETURN NEW;
350 END;
351 $$ LANGUAGE PLPGSQL;
352
353 CREATE TABLE asset.call_number_prefix (
354         id                      SERIAL   PRIMARY KEY,
355         owning_lib          INT                 NOT NULL REFERENCES actor.org_unit (id),
356         label               TEXT                NOT NULL, -- i18n
357         label_sortkey   TEXT
358 );
359 CREATE TRIGGER prefix_normalize_tgr BEFORE INSERT OR UPDATE ON asset.call_number_prefix FOR EACH ROW EXECUTE PROCEDURE asset.normalize_affix_sortkey();
360 CREATE UNIQUE INDEX asset_call_number_prefix_once_per_lib ON asset.call_number_prefix (label, owning_lib);
361 CREATE INDEX asset_call_number_prefix_sortkey_idx ON asset.call_number_prefix (label_sortkey);
362
363 CREATE TABLE asset.call_number_suffix (
364         id                      SERIAL   PRIMARY KEY,
365         owning_lib          INT                 NOT NULL REFERENCES actor.org_unit (id),
366         label               TEXT                NOT NULL, -- i18n
367         label_sortkey   TEXT
368 );
369 CREATE TRIGGER suffix_normalize_tgr BEFORE INSERT OR UPDATE ON asset.call_number_suffix FOR EACH ROW EXECUTE PROCEDURE asset.normalize_affix_sortkey();
370 CREATE UNIQUE INDEX asset_call_number_suffix_once_per_lib ON asset.call_number_suffix (label, owning_lib);
371 CREATE INDEX asset_call_number_suffix_sortkey_idx ON asset.call_number_suffix (label_sortkey);
372
373 CREATE TABLE asset.call_number (
374         id              bigserial PRIMARY KEY,
375         creator         BIGINT                          NOT NULL,
376         create_date     TIMESTAMP WITH TIME ZONE        DEFAULT NOW(),
377         editor          BIGINT                          NOT NULL,
378         edit_date       TIMESTAMP WITH TIME ZONE        DEFAULT NOW(),
379         record          bigint                          NOT NULL,
380         owning_lib      INT                                 NOT NULL,
381         label           TEXT                            NOT NULL,
382         deleted         BOOL                            NOT NULL DEFAULT FALSE,
383         prefix          INT                                 NOT NULL DEFAULT -1 REFERENCES asset.call_number_prefix(id) DEFERRABLE INITIALLY DEFERRED,
384         suffix          INT                                 NOT NULL DEFAULT -1 REFERENCES asset.call_number_suffix(id) DEFERRABLE INITIALLY DEFERRED,
385         label_class     BIGINT                          NOT NULL
386                                                         REFERENCES asset.call_number_class(id)
387                                                         DEFERRABLE INITIALLY DEFERRED,
388         label_sortkey   TEXT
389 );
390 CREATE INDEX asset_call_number_record_idx ON asset.call_number (record);
391 CREATE INDEX asset_call_number_creator_idx ON asset.call_number (creator);
392 CREATE INDEX asset_call_number_editor_idx ON asset.call_number (editor);
393 CREATE INDEX asset_call_number_dewey_idx ON asset.call_number (public.call_number_dewey(label));
394 CREATE INDEX asset_call_number_upper_label_id_owning_lib_idx ON asset.call_number (oils_text_as_bytea(label),id,owning_lib);
395 CREATE INDEX asset_call_number_label_sortkey ON asset.call_number(oils_text_as_bytea(label_sortkey));
396 CREATE UNIQUE INDEX asset_call_number_label_once_per_lib ON asset.call_number (record, owning_lib, label, prefix, suffix) WHERE deleted = FALSE OR deleted IS FALSE;
397 CREATE INDEX asset_call_number_label_sortkey_browse ON asset.call_number(oils_text_as_bytea(label_sortkey), oils_text_as_bytea(label), id, owning_lib) WHERE deleted IS FALSE OR deleted = FALSE;
398 CREATE RULE protect_cn_delete AS ON DELETE TO asset.call_number DO INSTEAD UPDATE asset.call_number SET deleted = TRUE WHERE OLD.id = asset.call_number.id;
399 CREATE TRIGGER asset_label_sortkey_trigger
400     BEFORE UPDATE OR INSERT ON asset.call_number
401     FOR EACH ROW EXECUTE PROCEDURE asset.label_normalizer();
402
403 CREATE TABLE asset.uri_call_number_map (
404     id          BIGSERIAL   PRIMARY KEY,
405     uri         INT         NOT NULL REFERENCES asset.uri (id),
406     call_number INT         NOT NULL REFERENCES asset.call_number (id),
407     CONSTRAINT uri_cn_once UNIQUE (uri,call_number)
408 );
409 CREATE INDEX asset_uri_call_number_map_cn_idx ON asset.uri_call_number_map (call_number);
410
411 CREATE TABLE asset.call_number_note (
412         id              BIGSERIAL                       PRIMARY KEY,
413         call_number     BIGINT                          NOT NULL,
414         creator         BIGINT                          NOT NULL,
415         create_date     TIMESTAMP WITH TIME ZONE        DEFAULT NOW(),
416         pub             BOOL                            NOT NULL DEFAULT FALSE,
417         title           TEXT                            NOT NULL,
418         value           TEXT                            NOT NULL
419 );
420 CREATE INDEX asset_call_number_note_creator_idx ON asset.call_number_note ( creator );
421
422 CREATE TABLE asset.copy_template (
423         id             SERIAL   PRIMARY KEY,
424         owning_lib     INT      NOT NULL
425                                 REFERENCES actor.org_unit (id)
426                                 DEFERRABLE INITIALLY DEFERRED,
427         creator        BIGINT   NOT NULL
428                                 REFERENCES actor.usr (id)
429                                 DEFERRABLE INITIALLY DEFERRED,
430         editor         BIGINT   NOT NULL
431                                 REFERENCES actor.usr (id)
432                                 DEFERRABLE INITIALLY DEFERRED,
433         create_date    TIMESTAMP WITH TIME ZONE    DEFAULT NOW(),
434         edit_date      TIMESTAMP WITH TIME ZONE    DEFAULT NOW(),
435         name           TEXT     NOT NULL,
436         -- columns above this point are attributes of the template itself
437         -- columns after this point are attributes of the copy this template modifies/creates
438         circ_lib       INT      REFERENCES actor.org_unit (id)
439                                 DEFERRABLE INITIALLY DEFERRED,
440         status         INT      REFERENCES config.copy_status (id)
441                                 DEFERRABLE INITIALLY DEFERRED,
442         location       INT      REFERENCES asset.copy_location (id)
443                                 DEFERRABLE INITIALLY DEFERRED,
444         loan_duration  INT      CONSTRAINT valid_loan_duration CHECK (
445                                     loan_duration IS NULL OR loan_duration IN (1,2,3)),
446         fine_level     INT      CONSTRAINT valid_fine_level CHECK (
447                                     fine_level IS NULL OR loan_duration IN (1,2,3)),
448         age_protect    INT,
449         circulate      BOOL,
450         deposit        BOOL,
451         ref            BOOL,
452         holdable       BOOL,
453         deposit_amount NUMERIC(6,2),
454         price          NUMERIC(8,2),
455         circ_modifier  TEXT,
456         circ_as_type   TEXT,
457         alert_message  TEXT,
458         opac_visible   BOOL,
459         floating       BOOL,
460         mint_condition BOOL
461 );
462
463 CREATE OR REPLACE FUNCTION asset.opac_ou_record_copy_count (org INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
464 DECLARE
465     ans RECORD;
466     trans INT;
467 BEGIN
468     SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
469
470     FOR ans IN SELECT u.id, t.depth FROM actor.org_unit_ancestors(org) AS u JOIN actor.org_unit_type t ON (u.ou_type = t.id) LOOP
471         RETURN QUERY
472         SELECT  ans.depth,
473                 ans.id,
474                 COUNT( av.id ),
475                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
476                 COUNT( av.id ),
477                 trans
478           FROM  
479                 actor.org_unit_descendants(ans.id) d
480                 JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
481                 JOIN asset.copy cp ON (cp.id = av.copy_id)
482           GROUP BY 1,2,6;
483
484         IF NOT FOUND THEN
485             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
486         END IF;
487
488     END LOOP;
489
490     RETURN;
491 END;
492 $f$ LANGUAGE PLPGSQL;
493
494 CREATE OR REPLACE FUNCTION asset.opac_lasso_record_copy_count (i_lasso INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
495 DECLARE
496     ans RECORD;
497     trans INT;
498 BEGIN
499     SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
500
501     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
502         RETURN QUERY
503         SELECT  -1,
504                 ans.id,
505                 COUNT( av.id ),
506                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
507                 COUNT( av.id ),
508                 trans
509           FROM
510                 actor.org_unit_descendants(ans.id) d
511                 JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
512                 JOIN asset.copy cp ON (cp.id = av.copy_id)
513           GROUP BY 1,2,6;
514
515         IF NOT FOUND THEN
516             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
517         END IF;
518
519     END LOOP;   
520                 
521     RETURN;     
522 END;            
523 $f$ LANGUAGE PLPGSQL;
524
525 CREATE OR REPLACE FUNCTION asset.staff_ou_record_copy_count (org INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
526 DECLARE         
527     ans RECORD; 
528     trans INT;
529 BEGIN           
530     SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
531
532     FOR ans IN SELECT u.id, t.depth FROM actor.org_unit_ancestors(org) AS u JOIN actor.org_unit_type t ON (u.ou_type = t.id) LOOP
533         RETURN QUERY
534         SELECT  ans.depth,
535                 ans.id,
536                 COUNT( cp.id ),
537                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
538                 COUNT( cp.id ),
539                 trans
540           FROM
541                 actor.org_unit_descendants(ans.id) d
542                 JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted)
543                 JOIN asset.call_number cn ON (cn.record = rid AND cn.id = cp.call_number AND NOT cn.deleted)
544           GROUP BY 1,2,6;
545
546         IF NOT FOUND THEN
547             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
548         END IF;
549
550     END LOOP;
551
552     RETURN;
553 END;
554 $f$ LANGUAGE PLPGSQL;
555
556 CREATE OR REPLACE FUNCTION asset.staff_lasso_record_copy_count (i_lasso INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
557 DECLARE
558     ans RECORD;
559     trans INT;
560 BEGIN
561     SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
562
563     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
564         RETURN QUERY
565         SELECT  -1,
566                 ans.id,
567                 COUNT( cp.id ),
568                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
569                 COUNT( cp.id ),
570                 trans
571           FROM
572                 actor.org_unit_descendants(ans.id) d
573                 JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted)
574                 JOIN asset.call_number cn ON (cn.record = rid AND cn.id = cp.call_number AND NOT cn.deleted)
575           GROUP BY 1,2,6;
576
577         IF NOT FOUND THEN
578             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
579         END IF;
580
581     END LOOP;
582
583     RETURN;
584 END;
585 $f$ LANGUAGE PLPGSQL;
586
587 CREATE OR REPLACE FUNCTION asset.record_copy_count ( place INT, rid BIGINT, staff BOOL) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
588 BEGIN
589     IF staff IS TRUE THEN
590         IF place > 0 THEN
591             RETURN QUERY SELECT * FROM asset.staff_ou_record_copy_count( place, rid );
592         ELSE
593             RETURN QUERY SELECT * FROM asset.staff_lasso_record_copy_count( -place, rid );
594         END IF;
595     ELSE
596         IF place > 0 THEN
597             RETURN QUERY SELECT * FROM asset.opac_ou_record_copy_count( place, rid );
598         ELSE
599             RETURN QUERY SELECT * FROM asset.opac_lasso_record_copy_count( -place, rid );
600         END IF;
601     END IF;
602
603     RETURN;
604 END;
605 $f$ LANGUAGE PLPGSQL;
606
607 CREATE OR REPLACE FUNCTION asset.opac_ou_metarecord_copy_count (org INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
608 DECLARE
609     ans RECORD;
610     trans INT;
611 BEGIN
612     SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
613
614     FOR ans IN SELECT u.id, t.depth FROM actor.org_unit_ancestors(org) AS u JOIN actor.org_unit_type t ON (u.ou_type = t.id) LOOP
615         RETURN QUERY
616         SELECT  ans.depth,
617                 ans.id,
618                 COUNT( av.id ),
619                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
620                 COUNT( av.id ),
621                 trans
622           FROM  
623                 actor.org_unit_descendants(ans.id) d
624                 JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
625                 JOIN asset.copy cp ON (cp.id = av.copy_id)
626                 JOIN metabib.metarecord_source_map m ON (m.source = av.record)
627           GROUP BY 1,2,6;
628
629         IF NOT FOUND THEN
630             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
631         END IF;
632
633     END LOOP;
634
635     RETURN;
636 END;
637 $f$ LANGUAGE PLPGSQL;
638
639 CREATE OR REPLACE FUNCTION asset.opac_lasso_metarecord_copy_count (i_lasso INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
640 DECLARE
641     ans RECORD;
642     trans INT;
643 BEGIN
644     SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
645
646     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
647         RETURN QUERY
648         SELECT  -1,
649                 ans.id,
650                 COUNT( av.id ),
651                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
652                 COUNT( av.id ),
653                 trans
654           FROM
655                 actor.org_unit_descendants(ans.id) d
656                 JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
657                 JOIN asset.copy cp ON (cp.id = av.copy_id)
658                 JOIN metabib.metarecord_source_map m ON (m.source = av.record)
659           GROUP BY 1,2,6;
660
661         IF NOT FOUND THEN
662             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
663         END IF;
664
665     END LOOP;   
666                 
667     RETURN;     
668 END;            
669 $f$ LANGUAGE PLPGSQL;
670
671 CREATE OR REPLACE FUNCTION asset.staff_ou_metarecord_copy_count (org INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
672 DECLARE         
673     ans RECORD; 
674     trans INT;
675 BEGIN
676     SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
677
678     FOR ans IN SELECT u.id, t.depth FROM actor.org_unit_ancestors(org) AS u JOIN actor.org_unit_type t ON (u.ou_type = t.id) LOOP
679         RETURN QUERY
680         SELECT  ans.depth,
681                 ans.id,
682                 COUNT( cp.id ),
683                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
684                 COUNT( cp.id ),
685                 trans
686           FROM
687                 actor.org_unit_descendants(ans.id) d
688                 JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted)
689                 JOIN asset.call_number cn ON (cn.record = rid AND cn.id = cp.call_number AND NOT cn.deleted)
690                 JOIN metabib.metarecord_source_map m ON (m.source = cn.record)
691           GROUP BY 1,2,6;
692
693         IF NOT FOUND THEN
694             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
695         END IF;
696
697     END LOOP;
698
699     RETURN;
700 END;
701 $f$ LANGUAGE PLPGSQL;
702
703 CREATE OR REPLACE FUNCTION asset.staff_lasso_metarecord_copy_count (i_lasso INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
704 DECLARE
705     ans RECORD;
706     trans INT;
707 BEGIN
708     SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
709
710     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
711         RETURN QUERY
712         SELECT  -1,
713                 ans.id,
714                 COUNT( cp.id ),
715                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
716                 COUNT( cp.id ),
717                 trans
718           FROM
719                 actor.org_unit_descendants(ans.id) d
720                 JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted)
721                 JOIN asset.call_number cn ON (cn.record = rid AND cn.id = cp.call_number AND NOT cn.deleted)
722                 JOIN metabib.metarecord_source_map m ON (m.source = cn.record)
723           GROUP BY 1,2,6;
724
725         IF NOT FOUND THEN
726             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
727         END IF;
728
729     END LOOP;
730
731     RETURN;
732 END;
733 $f$ LANGUAGE PLPGSQL;
734
735 CREATE OR REPLACE FUNCTION asset.metarecord_copy_count ( place INT, rid BIGINT, staff BOOL) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
736 BEGIN
737     IF staff IS TRUE THEN
738         IF place > 0 THEN
739             RETURN QUERY SELECT * FROM asset.staff_ou_metarecord_copy_count( place, rid );
740         ELSE
741             RETURN QUERY SELECT * FROM asset.staff_lasso_metarecord_copy_count( -place, rid );
742         END IF;
743     ELSE
744         IF place > 0 THEN
745             RETURN QUERY SELECT * FROM asset.opac_ou_metarecord_copy_count( place, rid );
746         ELSE
747             RETURN QUERY SELECT * FROM asset.opac_lasso_metarecord_copy_count( -place, rid );
748         END IF;
749     END IF;
750
751     RETURN;
752 END;
753 $f$ LANGUAGE PLPGSQL;
754
755 CREATE OR REPLACE FUNCTION asset.autogenerate_placeholder_barcode ( ) RETURNS TRIGGER AS $f$
756 BEGIN
757         IF NEW.barcode LIKE '@@%' THEN
758                 NEW.barcode := '@@' || NEW.id;
759         END IF;
760         RETURN NEW;
761 END;
762 $f$ LANGUAGE PLPGSQL;
763
764 CREATE TRIGGER autogenerate_placeholder_barcode
765         BEFORE INSERT OR UPDATE ON asset.copy
766         FOR EACH ROW EXECUTE PROCEDURE asset.autogenerate_placeholder_barcode();
767
768 CREATE OR REPLACE FUNCTION evergreen.fake_fkey_tgr () RETURNS TRIGGER AS $F$
769 DECLARE
770     copy_id BIGINT;
771 BEGIN
772     EXECUTE 'SELECT ($1).' || quote_ident(TG_ARGV[0]) INTO copy_id USING NEW;
773     PERFORM * FROM asset.copy WHERE id = copy_id;
774     IF NOT FOUND THEN
775         RAISE EXCEPTION 'Key (%.%=%) does not exist in asset.copy', TG_TABLE_SCHEMA, TG_TABLE_NAME, copy_id;
776     END IF;
777     RETURN NULL;
778 END;
779 $F$ LANGUAGE PLPGSQL;
780
781 COMMIT;
782