]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/040.schema.asset.sql
Reorder some functions to avoid creation failure on an empty db; remove unused (and...
[working/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_entry_transparency_map (
131         id                      BIGSERIAL       PRIMARY KEY,
132         stat_cat                INT             NOT NULL, -- needs ON DELETE CASCADE
133         stat_cat_entry          INT             NOT NULL, -- needs ON DELETE CASCADE
134         owning_transparency     INT             NOT NULL, -- needs ON DELETE CASCADE
135         CONSTRAINT scte_once_per_trans UNIQUE (owning_transparency,stat_cat)
136 );
137
138 CREATE TABLE asset.stat_cat (
139         id              SERIAL  PRIMARY KEY,
140         owner           INT     NOT NULL,
141         opac_visible    BOOL    NOT NULL DEFAULT FALSE,
142         name            TEXT    NOT NULL,
143         required        BOOL    NOT NULL DEFAULT FALSE,
144         CONSTRAINT sc_once_per_owner UNIQUE (owner,name)
145 );
146
147 CREATE TABLE asset.stat_cat_entry (
148         id              SERIAL  PRIMARY KEY,
149         stat_cat        INT     NOT NULL,
150         owner           INT     NOT NULL,
151         value           TEXT    NOT NULL,
152         CONSTRAINT sce_once_per_owner UNIQUE (stat_cat,owner,value)
153 );
154
155 CREATE TABLE asset.stat_cat_entry_copy_map (
156         id              BIGSERIAL       PRIMARY KEY,
157         stat_cat        INT             NOT NULL,
158         stat_cat_entry  INT             NOT NULL,
159         owning_copy     BIGINT          NOT NULL,
160         CONSTRAINT sce_once_per_copy UNIQUE (owning_copy,stat_cat)
161 );
162 CREATE INDEX scecm_owning_copy_idx ON asset.stat_cat_entry_copy_map(owning_copy);
163
164 CREATE TABLE asset.copy_note (
165         id              BIGSERIAL                       PRIMARY KEY,
166         owning_copy     BIGINT                          NOT NULL,
167         creator         BIGINT                          NOT NULL,
168         create_date     TIMESTAMP WITH TIME ZONE        DEFAULT NOW(),
169         pub             BOOL                            NOT NULL DEFAULT FALSE,
170         title           TEXT                            NOT NULL,
171         value           TEXT                            NOT NULL
172 );
173 CREATE INDEX asset_copy_note_creator_idx ON asset.copy_note ( creator );
174 CREATE INDEX asset_copy_note_owning_copy_idx ON asset.copy_note ( owning_copy );
175
176 CREATE TABLE asset.uri (
177     id  SERIAL  PRIMARY KEY,
178     href    TEXT    NOT NULL,
179     label   TEXT,
180     use_restriction TEXT,
181     active  BOOL    NOT NULL DEFAULT TRUE
182 );
183
184 CREATE TABLE asset.call_number_class (
185     id             bigserial     PRIMARY KEY,
186     name           TEXT          NOT NULL,
187     normalizer     TEXT          NOT NULL DEFAULT 'asset.normalize_generic',
188     field          TEXT          NOT NULL DEFAULT '050ab,055ab,060ab,070ab,080ab,082ab,086ab,088ab,090,092,096,098,099'
189 );
190 COMMENT ON TABLE asset.call_number_class IS $$
191 Defines the call number normalization database functions in the "normalizer"
192 column and the tag/subfield combinations to use to lookup the call number in
193 the "field" column for a given classification scheme. Tag/subfield combinations
194 are delimited by commas.
195 $$;
196
197 CREATE OR REPLACE FUNCTION asset.label_normalizer() RETURNS TRIGGER AS $func$
198 DECLARE
199     sortkey        TEXT := '';
200 BEGIN
201     sortkey := NEW.label_sortkey;
202
203     EXECUTE 'SELECT ' || acnc.normalizer || '(' || 
204        quote_literal( NEW.label ) || ')'
205        FROM asset.call_number_class acnc
206        WHERE acnc.id = NEW.label_class
207        INTO sortkey;
208
209     NEW.label_sortkey = sortkey;
210
211     RETURN NEW;
212 END;
213 $func$ LANGUAGE PLPGSQL;
214
215 CREATE OR REPLACE FUNCTION asset.label_normalizer_generic(TEXT) RETURNS TEXT AS $func$
216     # Created after looking at the Koha C4::ClassSortRoutine::Generic module,
217     # thus could probably be considered a derived work, although nothing was
218     # directly copied - but to err on the safe side of providing attribution:
219     # Copyright (C) 2007 LibLime
220     # Copyright (C) 2011 Equinox Software, Inc (Steve Callendar)
221     # Licensed under the GPL v2 or later
222
223     use strict;
224     use warnings;
225
226     # Converts the callnumber to uppercase
227     # Strips spaces from start and end of the call number
228     # Converts anything other than letters, digits, and periods into spaces
229     # Collapses multiple spaces into a single underscore
230     my $callnum = uc(shift);
231     $callnum =~ s/^\s//g;
232     $callnum =~ s/\s$//g;
233     # NOTE: this previously used underscores, but this caused sorting issues
234     # for the "before" half of page 0 on CN browse, sorting CNs containing a
235     # decimal before "whole number" CNs
236     $callnum =~ s/[^A-Z0-9_.]/ /g;
237     $callnum =~ s/ {2,}/ /g;
238
239     return $callnum;
240 $func$ LANGUAGE PLPERLU;
241
242 CREATE OR REPLACE FUNCTION asset.label_normalizer_dewey(TEXT) RETURNS TEXT AS $func$
243     # Derived from the Koha C4::ClassSortRoutine::Dewey module
244     # Copyright (C) 2007 LibLime
245     # Licensed under the GPL v2 or later
246
247     use strict;
248     use warnings;
249
250     my $init = uc(shift);
251     $init =~ s/^\s+//;
252     $init =~ s/\s+$//;
253     $init =~ s!/!!g;
254     $init =~ s/^([\p{IsAlpha}]+)/$1 /;
255     my @tokens = split /\.|\s+/, $init;
256     my $digit_group_count = 0;
257     for (my $i = 0; $i <= $#tokens; $i++) {
258         if ($tokens[$i] =~ /^\d+$/) {
259             $digit_group_count++;
260             if (2 == $digit_group_count) {
261                 $tokens[$i] = sprintf("%-15.15s", $tokens[$i]);
262                 $tokens[$i] =~ tr/ /0/;
263             }
264         }
265     }
266     my $key = join("_", @tokens);
267     $key =~ s/[^\p{IsAlnum}_]//g;
268
269     return $key;
270
271 $func$ LANGUAGE PLPERLU;
272
273 CREATE OR REPLACE FUNCTION asset.label_normalizer_lc(TEXT) RETURNS TEXT AS $func$
274     use strict;
275     use warnings;
276
277     # Library::CallNumber::LC is currently hosted at http://code.google.com/p/library-callnumber-lc/
278     # The author hopes to upload it to CPAN some day, which would make our lives easier
279     use Library::CallNumber::LC;
280
281     my $callnum = Library::CallNumber::LC->new(shift);
282     return $callnum->normalize();
283
284 $func$ LANGUAGE PLPERLU;
285
286 INSERT INTO asset.call_number_class (name, normalizer, field) VALUES 
287     ('Generic', 'asset.label_normalizer_generic', '050ab,055ab,060ab,070ab,080ab,082ab,086ab,088ab,090,092,096,098,099'),
288     ('Dewey (DDC)', 'asset.label_normalizer_dewey', '080ab,082ab,092abef'),
289     ('Library of Congress (LC)', 'asset.label_normalizer_lc', '050ab,055ab,090abef')
290 ;
291
292 CREATE OR REPLACE FUNCTION asset.normalize_affix_sortkey () RETURNS TRIGGER AS $$
293 BEGIN
294     NEW.label_sortkey := REGEXP_REPLACE(
295         evergreen.lpad_number_substrings(
296             naco_normalize(NEW.label),
297             '0',
298             10
299         ),
300         E'\\s+',
301         '',
302         'g'
303     );
304     RETURN NEW;
305 END;
306 $$ LANGUAGE PLPGSQL;
307
308 CREATE TABLE asset.call_number_prefix (
309         id                      SERIAL   PRIMARY KEY,
310         owning_lib          INT                 NOT NULL REFERENCES actor.org_unit (id),
311         label               TEXT                NOT NULL, -- i18n
312         label_sortkey   TEXT
313 );
314 CREATE TRIGGER prefix_normalize_tgr BEFORE INSERT OR UPDATE ON asset.call_number_prefix FOR EACH ROW EXECUTE PROCEDURE asset.normalize_affix_sortkey();
315 CREATE UNIQUE INDEX asset_call_number_prefix_once_per_lib ON asset.call_number_prefix (label, owning_lib);
316 CREATE INDEX asset_call_number_prefix_sortkey_idx ON asset.call_number_prefix (label_sortkey);
317
318 CREATE TABLE asset.call_number_suffix (
319         id                      SERIAL   PRIMARY KEY,
320         owning_lib          INT                 NOT NULL REFERENCES actor.org_unit (id),
321         label               TEXT                NOT NULL, -- i18n
322         label_sortkey   TEXT
323 );
324 CREATE TRIGGER suffix_normalize_tgr BEFORE INSERT OR UPDATE ON asset.call_number_suffix FOR EACH ROW EXECUTE PROCEDURE asset.normalize_affix_sortkey();
325 CREATE UNIQUE INDEX asset_call_number_suffix_once_per_lib ON asset.call_number_suffix (label, owning_lib);
326 CREATE INDEX asset_call_number_suffix_sortkey_idx ON asset.call_number_suffix (label_sortkey);
327
328 CREATE TABLE asset.call_number (
329         id              bigserial PRIMARY KEY,
330         creator         BIGINT                          NOT NULL,
331         create_date     TIMESTAMP WITH TIME ZONE        DEFAULT NOW(),
332         editor          BIGINT                          NOT NULL,
333         edit_date       TIMESTAMP WITH TIME ZONE        DEFAULT NOW(),
334         record          bigint                          NOT NULL,
335         owning_lib      INT                                 NOT NULL,
336         label           TEXT                            NOT NULL,
337         deleted         BOOL                            NOT NULL DEFAULT FALSE,
338         prefix          INT                                 NOT NULL DEFAULT -1 REFERENCES asset.call_number_prefix(id) DEFERRABLE INITIALLY DEFERRED,
339         suffix          INT                                 NOT NULL DEFAULT -1 REFERENCES asset.call_number_suffix(id) DEFERRABLE INITIALLY DEFERRED,
340         label_class     BIGINT                          DEFAULT 1 NOT NULL
341                                                         REFERENCES asset.call_number_class(id)
342                                                         DEFERRABLE INITIALLY DEFERRED,
343         label_sortkey   TEXT
344 );
345 CREATE INDEX asset_call_number_record_idx ON asset.call_number (record);
346 CREATE INDEX asset_call_number_creator_idx ON asset.call_number (creator);
347 CREATE INDEX asset_call_number_editor_idx ON asset.call_number (editor);
348 CREATE INDEX asset_call_number_dewey_idx ON asset.call_number (public.call_number_dewey(label));
349 CREATE INDEX asset_call_number_upper_label_id_owning_lib_idx ON asset.call_number (oils_text_as_bytea(label),id,owning_lib);
350 CREATE INDEX asset_call_number_label_sortkey ON asset.call_number(oils_text_as_bytea(label_sortkey));
351 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;
352 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;
353 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;
354 CREATE TRIGGER asset_label_sortkey_trigger
355     BEFORE UPDATE OR INSERT ON asset.call_number
356     FOR EACH ROW EXECUTE PROCEDURE asset.label_normalizer();
357
358 CREATE TABLE asset.uri_call_number_map (
359     id          BIGSERIAL   PRIMARY KEY,
360     uri         INT         NOT NULL REFERENCES asset.uri (id),
361     call_number INT         NOT NULL REFERENCES asset.call_number (id),
362     CONSTRAINT uri_cn_once UNIQUE (uri,call_number)
363 );
364 CREATE INDEX asset_uri_call_number_map_cn_idx ON asset.uri_call_number_map (call_number);
365
366 CREATE TABLE asset.call_number_note (
367         id              BIGSERIAL                       PRIMARY KEY,
368         call_number     BIGINT                          NOT NULL,
369         creator         BIGINT                          NOT NULL,
370         create_date     TIMESTAMP WITH TIME ZONE        DEFAULT NOW(),
371         pub             BOOL                            NOT NULL DEFAULT FALSE,
372         title           TEXT                            NOT NULL,
373         value           TEXT                            NOT NULL
374 );
375 CREATE INDEX asset_call_number_note_creator_idx ON asset.call_number_note ( creator );
376
377 CREATE TABLE asset.copy_template (
378         id             SERIAL   PRIMARY KEY,
379         owning_lib     INT      NOT NULL
380                                 REFERENCES actor.org_unit (id)
381                                 DEFERRABLE INITIALLY DEFERRED,
382         creator        BIGINT   NOT NULL
383                                 REFERENCES actor.usr (id)
384                                 DEFERRABLE INITIALLY DEFERRED,
385         editor         BIGINT   NOT NULL
386                                 REFERENCES actor.usr (id)
387                                 DEFERRABLE INITIALLY DEFERRED,
388         create_date    TIMESTAMP WITH TIME ZONE    DEFAULT NOW(),
389         edit_date      TIMESTAMP WITH TIME ZONE    DEFAULT NOW(),
390         name           TEXT     NOT NULL,
391         -- columns above this point are attributes of the template itself
392         -- columns after this point are attributes of the copy this template modifies/creates
393         circ_lib       INT      REFERENCES actor.org_unit (id)
394                                 DEFERRABLE INITIALLY DEFERRED,
395         status         INT      REFERENCES config.copy_status (id)
396                                 DEFERRABLE INITIALLY DEFERRED,
397         location       INT      REFERENCES asset.copy_location (id)
398                                 DEFERRABLE INITIALLY DEFERRED,
399         loan_duration  INT      CONSTRAINT valid_loan_duration CHECK (
400                                     loan_duration IS NULL OR loan_duration IN (1,2,3)),
401         fine_level     INT      CONSTRAINT valid_fine_level CHECK (
402                                     fine_level IS NULL OR loan_duration IN (1,2,3)),
403         age_protect    INT,
404         circulate      BOOL,
405         deposit        BOOL,
406         ref            BOOL,
407         holdable       BOOL,
408         deposit_amount NUMERIC(6,2),
409         price          NUMERIC(8,2),
410         circ_modifier  TEXT,
411         circ_as_type   TEXT,
412         alert_message  TEXT,
413         opac_visible   BOOL,
414         floating       BOOL,
415         mint_condition BOOL
416 );
417
418 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$
419 DECLARE
420     ans RECORD;
421     trans INT;
422 BEGIN
423     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;
424
425     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
426         RETURN QUERY
427         SELECT  ans.depth,
428                 ans.id,
429                 COUNT( av.id ),
430                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
431                 COUNT( av.id ),
432                 trans
433           FROM  
434                 actor.org_unit_descendants(ans.id) d
435                 JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
436                 JOIN asset.copy cp ON (cp.id = av.id)
437           GROUP BY 1,2,6;
438
439         IF NOT FOUND THEN
440             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
441         END IF;
442
443     END LOOP;
444
445     RETURN;
446 END;
447 $f$ LANGUAGE PLPGSQL;
448
449 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$
450 DECLARE
451     ans RECORD;
452     trans INT;
453 BEGIN
454     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;
455
456     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
457         RETURN QUERY
458         SELECT  -1,
459                 ans.id,
460                 COUNT( av.id ),
461                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
462                 COUNT( av.id ),
463                 trans
464           FROM
465                 actor.org_unit_descendants(ans.id) d
466                 JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
467                 JOIN asset.copy cp ON (cp.id = av.id)
468           GROUP BY 1,2,6;
469
470         IF NOT FOUND THEN
471             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
472         END IF;
473
474     END LOOP;   
475                 
476     RETURN;     
477 END;            
478 $f$ LANGUAGE PLPGSQL;
479
480 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$
481 DECLARE         
482     ans RECORD; 
483     trans INT;
484 BEGIN           
485     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;
486
487     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
488         RETURN QUERY
489         SELECT  ans.depth,
490                 ans.id,
491                 COUNT( cp.id ),
492                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
493                 COUNT( cp.id ),
494                 trans
495           FROM
496                 actor.org_unit_descendants(ans.id) d
497                 JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted)
498                 JOIN asset.call_number cn ON (cn.record = rid AND cn.id = cp.call_number AND NOT cn.deleted)
499           GROUP BY 1,2,6;
500
501         IF NOT FOUND THEN
502             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
503         END IF;
504
505     END LOOP;
506
507     RETURN;
508 END;
509 $f$ LANGUAGE PLPGSQL;
510
511 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$
512 DECLARE
513     ans RECORD;
514     trans INT;
515 BEGIN
516     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;
517
518     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
519         RETURN QUERY
520         SELECT  -1,
521                 ans.id,
522                 COUNT( cp.id ),
523                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
524                 COUNT( cp.id ),
525                 trans
526           FROM
527                 actor.org_unit_descendants(ans.id) d
528                 JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted)
529                 JOIN asset.call_number cn ON (cn.record = rid AND cn.id = cp.call_number AND NOT cn.deleted)
530           GROUP BY 1,2,6;
531
532         IF NOT FOUND THEN
533             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
534         END IF;
535
536     END LOOP;
537
538     RETURN;
539 END;
540 $f$ LANGUAGE PLPGSQL;
541
542 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$
543 BEGIN
544     IF staff IS TRUE THEN
545         IF place > 0 THEN
546             RETURN QUERY SELECT * FROM asset.staff_ou_record_copy_count( place, rid );
547         ELSE
548             RETURN QUERY SELECT * FROM asset.staff_lasso_record_copy_count( -place, rid );
549         END IF;
550     ELSE
551         IF place > 0 THEN
552             RETURN QUERY SELECT * FROM asset.opac_ou_record_copy_count( place, rid );
553         ELSE
554             RETURN QUERY SELECT * FROM asset.opac_lasso_record_copy_count( -place, rid );
555         END IF;
556     END IF;
557
558     RETURN;
559 END;
560 $f$ LANGUAGE PLPGSQL;
561
562 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$
563 DECLARE
564     ans RECORD;
565     trans INT;
566 BEGIN
567     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;
568
569     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
570         RETURN QUERY
571         SELECT  ans.depth,
572                 ans.id,
573                 COUNT( av.id ),
574                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
575                 COUNT( av.id ),
576                 trans
577           FROM  
578                 actor.org_unit_descendants(ans.id) d
579                 JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
580                 JOIN asset.copy cp ON (cp.id = av.id)
581                 JOIN metabib.metarecord_source_map m ON (m.source = av.record)
582           GROUP BY 1,2,6;
583
584         IF NOT FOUND THEN
585             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
586         END IF;
587
588     END LOOP;
589
590     RETURN;
591 END;
592 $f$ LANGUAGE PLPGSQL;
593
594 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$
595 DECLARE
596     ans RECORD;
597     trans INT;
598 BEGIN
599     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;
600
601     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
602         RETURN QUERY
603         SELECT  -1,
604                 ans.id,
605                 COUNT( av.id ),
606                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
607                 COUNT( av.id ),
608                 trans
609           FROM
610                 actor.org_unit_descendants(ans.id) d
611                 JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
612                 JOIN asset.copy cp ON (cp.id = av.id)
613                 JOIN metabib.metarecord_source_map m ON (m.source = av.record)
614           GROUP BY 1,2,6;
615
616         IF NOT FOUND THEN
617             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
618         END IF;
619
620     END LOOP;   
621                 
622     RETURN;     
623 END;            
624 $f$ LANGUAGE PLPGSQL;
625
626 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$
627 DECLARE         
628     ans RECORD; 
629     trans INT;
630 BEGIN
631     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;
632
633     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
634         RETURN QUERY
635         SELECT  ans.depth,
636                 ans.id,
637                 COUNT( cp.id ),
638                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
639                 COUNT( cp.id ),
640                 trans
641           FROM
642                 actor.org_unit_descendants(ans.id) d
643                 JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted)
644                 JOIN asset.call_number cn ON (cn.record = rid AND cn.id = cp.call_number AND NOT cn.deleted)
645                 JOIN metabib.metarecord_source_map m ON (m.source = cn.record)
646           GROUP BY 1,2,6;
647
648         IF NOT FOUND THEN
649             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
650         END IF;
651
652     END LOOP;
653
654     RETURN;
655 END;
656 $f$ LANGUAGE PLPGSQL;
657
658 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$
659 DECLARE
660     ans RECORD;
661     trans INT;
662 BEGIN
663     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;
664
665     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
666         RETURN QUERY
667         SELECT  -1,
668                 ans.id,
669                 COUNT( cp.id ),
670                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
671                 COUNT( cp.id ),
672                 trans
673           FROM
674                 actor.org_unit_descendants(ans.id) d
675                 JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted)
676                 JOIN asset.call_number cn ON (cn.record = rid AND cn.id = cp.call_number AND NOT cn.deleted)
677                 JOIN metabib.metarecord_source_map m ON (m.source = cn.record)
678           GROUP BY 1,2,6;
679
680         IF NOT FOUND THEN
681             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
682         END IF;
683
684     END LOOP;
685
686     RETURN;
687 END;
688 $f$ LANGUAGE PLPGSQL;
689
690 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$
691 BEGIN
692     IF staff IS TRUE THEN
693         IF place > 0 THEN
694             RETURN QUERY SELECT * FROM asset.staff_ou_metarecord_copy_count( place, rid );
695         ELSE
696             RETURN QUERY SELECT * FROM asset.staff_lasso_metarecord_copy_count( -place, rid );
697         END IF;
698     ELSE
699         IF place > 0 THEN
700             RETURN QUERY SELECT * FROM asset.opac_ou_metarecord_copy_count( place, rid );
701         ELSE
702             RETURN QUERY SELECT * FROM asset.opac_lasso_metarecord_copy_count( -place, rid );
703         END IF;
704     END IF;
705
706     RETURN;
707 END;
708 $f$ LANGUAGE PLPGSQL;
709
710 CREATE OR REPLACE FUNCTION asset.autogenerate_placeholder_barcode ( ) RETURNS TRIGGER AS $f$
711 BEGIN
712         IF NEW.barcode LIKE '@@%' THEN
713                 NEW.barcode := '@@' || NEW.id;
714         END IF;
715         RETURN NEW;
716 END;
717 $f$ LANGUAGE PLPGSQL;
718
719 CREATE TRIGGER autogenerate_placeholder_barcode
720         BEFORE INSERT OR UPDATE ON asset.copy
721         FOR EACH ROW EXECUTE PROCEDURE asset.autogenerate_placeholder_barcode();
722
723 CREATE OR REPLACE FUNCTION evergreen.fake_fkey_tgr () RETURNS TRIGGER AS $F$
724 DECLARE
725     copy_id BIGINT;
726 BEGIN
727     EXECUTE 'SELECT ($1).' || quote_ident(TG_ARGV[0]) INTO copy_id USING NEW;
728     PERFORM * FROM asset.copy WHERE id = copy_id;
729     IF NOT FOUND THEN
730         RAISE EXCEPTION 'Key (%.%=%) does not exist in asset.copy', TG_TABLE_SCHEMA, TG_TABLE_NAME, copy_id;
731     END IF;
732     RETURN NULL;
733 END;
734 $F$ LANGUAGE PLPGSQL;
735
736 COMMIT;
737