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